mirror of
https://github.com/godotengine/godot-git-plugin.git
synced 2026-01-03 10:09:43 +03:00
Add diff loading with useability fixes
This commit is contained in:
17
demo/.gitattributes
vendored
Normal file
17
demo/.gitattributes
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
# Set the default behavior, in case people don't have core.autocrlf set.
|
||||
* text=auto
|
||||
|
||||
#Explicitly declare text files you want to always be normalized and converted
|
||||
#to native line endings on checkout.
|
||||
*.cpp text
|
||||
*.c text
|
||||
*.h text
|
||||
*.gd text
|
||||
*.cs text
|
||||
|
||||
#Declare files that will always have CRLF line endings on checkout.
|
||||
*.sln text eol=crlf
|
||||
|
||||
#Denote all files that are truly binary and should not be modified.
|
||||
*.png binary
|
||||
*.jpg binary
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -7,7 +7,10 @@ extends Node
|
||||
# Called when the node enters the scene tree for the first time.
|
||||
func _ready():
|
||||
pass # Replace with function body.
|
||||
# This is a test change for the VCS addon
|
||||
|
||||
func _some_function():
|
||||
pass
|
||||
|
||||
# Called every frame. 'delta' is the elapsed time since the previous frame.
|
||||
#func _process(delta):
|
||||
# pass
|
||||
|
||||
@@ -21,6 +21,7 @@ _global_script_class_icons={
|
||||
[application]
|
||||
|
||||
config/name="demo"
|
||||
run/main_scene="res://demo.tscn"
|
||||
config/icon="res://icon.png"
|
||||
|
||||
[gdnative]
|
||||
|
||||
@@ -129,13 +129,11 @@
|
||||
<ItemGroup>
|
||||
<ClCompile Include="src\gdlibrary.cpp" />
|
||||
<ClCompile Include="src\git_api.cpp" />
|
||||
<ClCompile Include="src\git_callbacks.cpp" />
|
||||
<ClCompile Include="src\git_common.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="src\allocation_defs.h" />
|
||||
<ClInclude Include="src\git_api.h" />
|
||||
<ClInclude Include="src\git_callbacks.h" />
|
||||
<ClInclude Include="src\git_common.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
|
||||
@@ -21,9 +21,6 @@
|
||||
<ClCompile Include="src\git_api.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\git_callbacks.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\git_common.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
@@ -35,9 +32,6 @@
|
||||
<ClInclude Include="src\git_common.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="src\git_callbacks.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="src\allocation_defs.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
namespace godot {
|
||||
|
||||
bool GitAPI::is_initialized = false;
|
||||
GitAPI *GitAPI::singleton = NULL;
|
||||
|
||||
void GitAPI::_register_methods() {
|
||||
|
||||
@@ -13,6 +13,7 @@ void GitAPI::_register_methods() {
|
||||
register_method("_get_initialization_settings_panel_container", &GitAPI::_get_initialization_settings_panel_container);
|
||||
register_method("_get_is_vcs_intialized", &GitAPI::_get_is_vcs_intialized);
|
||||
register_method("_get_modified_files_data", &GitAPI::_get_modified_files_data);
|
||||
register_method("_get_file_diff", &GitAPI::_get_file_diff);
|
||||
register_method("_get_project_name", &GitAPI::_get_project_name);
|
||||
register_method("_get_vcs_name", &GitAPI::_get_vcs_name);
|
||||
register_method("_initialize", &GitAPI::_initialize);
|
||||
@@ -31,42 +32,41 @@ void GitAPI::_commit(const String p_msg) {
|
||||
git_signature *default_sign;
|
||||
git_oid tree_id, parent_id, commit_id;
|
||||
git_tree *tree;
|
||||
git_index *repo_index;
|
||||
git_commit *parent;
|
||||
git_index *index;
|
||||
|
||||
git_signature_default(&default_sign, repo);
|
||||
|
||||
git_reference_name_to_id(&parent_id, repo, "HEAD");
|
||||
|
||||
/* Get the index and write it to a tree */
|
||||
git_repository_index(&index, repo);
|
||||
git_repository_index(&repo_index, repo);
|
||||
for (int i = 0; i < staged_files.size(); i++) {
|
||||
|
||||
git_index_add_bypath(index, ((String)staged_files[i]).alloc_c_string());
|
||||
git_index_add_bypath(repo_index, ((String)staged_files[i]).alloc_c_string());
|
||||
}
|
||||
git_index_write_tree(&tree_id, index);
|
||||
git_index_write_tree(&tree_id, repo_index);
|
||||
git_tree_lookup(&tree, repo, &tree_id);
|
||||
|
||||
/* Get HEAD as a commit object to use as the parent of the commit */
|
||||
GIT2_CALL(git_reference_name_to_id(&parent_id, repo, "HEAD"), "Could not get HEAD reference for parent ID", NULL);
|
||||
git_commit_lookup(&parent, repo, &parent_id);
|
||||
|
||||
/* Do the commit */
|
||||
git_commit_create_v(
|
||||
&commit_id,
|
||||
repo,
|
||||
"HEAD", /* The commit will update the position of HEAD */
|
||||
default_sign,
|
||||
default_sign,
|
||||
NULL, /* UTF-8 encoding */
|
||||
p_msg.alloc_c_string(),
|
||||
tree, /* The tree from the index */
|
||||
1, /* Only one parent */
|
||||
parent /* No need to make a list with create_v */
|
||||
&commit_id,
|
||||
repo,
|
||||
"HEAD",
|
||||
default_sign,
|
||||
default_sign,
|
||||
NULL,
|
||||
p_msg.alloc_c_string(),
|
||||
tree,
|
||||
1,
|
||||
parent
|
||||
);
|
||||
|
||||
staged_files.clear();
|
||||
|
||||
git_index_write(repo_index);
|
||||
git_index_free(repo_index);
|
||||
git_signature_free(default_sign);
|
||||
git_index_free(index);
|
||||
git_commit_free(parent);
|
||||
git_tree_free(tree);
|
||||
}
|
||||
@@ -81,10 +81,13 @@ void GitAPI::_stage_file(const String p_file_path) {
|
||||
|
||||
void GitAPI::_unstage_file(const String p_file_path) {
|
||||
|
||||
staged_files.erase(p_file_path);
|
||||
if (staged_files.find(p_file_path) != -1) {
|
||||
|
||||
staged_files.erase(p_file_path);
|
||||
}
|
||||
}
|
||||
|
||||
void GitAPI::create_gitignore() {
|
||||
void GitAPI::create_gitignore_and_gitattributes() {
|
||||
|
||||
File *file = File::_new();
|
||||
|
||||
@@ -101,37 +104,61 @@ void GitAPI::create_gitignore() {
|
||||
);
|
||||
file->close();
|
||||
}
|
||||
|
||||
if (!file->file_exists("res://.gitattributes")) {
|
||||
|
||||
file->open("res://.gitattributes", File::ModeFlags::WRITE);
|
||||
file->store_string(
|
||||
"# Set the default behavior, in case people don't have core.autocrlf set.\n"
|
||||
"* text=auto\n\n"
|
||||
|
||||
"#Explicitly declare text files you want to always be normalized and converted\n"
|
||||
"#to native line endings on checkout.\n"
|
||||
"*.cpp text\n"
|
||||
"*.c text\n"
|
||||
"*.h text\n"
|
||||
"*.gd text\n"
|
||||
"*.cs text\n\n"
|
||||
|
||||
"#Declare files that will always have CRLF line endings on checkout.\n"
|
||||
"*.sln text eol=crlf\n\n"
|
||||
|
||||
"#Denote all files that are truly binary and should not be modified.\n"
|
||||
"*.png binary\n"
|
||||
"*.jpg binary\n");
|
||||
file->close();
|
||||
}
|
||||
}
|
||||
|
||||
void GitAPI::create_initial_commit() {
|
||||
|
||||
git_signature *sig;
|
||||
git_index *index;
|
||||
git_oid tree_id, commit_id;
|
||||
git_index *repo_index;
|
||||
git_tree *tree;
|
||||
|
||||
GIT2_CALL(git_signature_default(&sig, repo), "Unable to create a commit signature. Perhaps 'user.name' and 'user.email' are not set", NULL);
|
||||
GIT2_CALL(git_repository_index(&index, repo), "Could not create intial commit", NULL);
|
||||
GIT2_CALL(git_index_write_tree(&tree_id, index), "Could not create intial commit", NULL);
|
||||
|
||||
git_index_free(index);
|
||||
GIT2_CALL(git_repository_index(&repo_index, repo), "Could not get repository index", NULL);
|
||||
GIT2_CALL(git_index_write_tree(&tree_id, repo_index), "Could not create intial commit", NULL);
|
||||
|
||||
GIT2_CALL(git_tree_lookup(&tree, repo, &tree_id), "Could not create intial commit", NULL);
|
||||
GIT2_CALL(
|
||||
git_commit_create_v(
|
||||
&commit_id,
|
||||
repo,
|
||||
"HEAD",
|
||||
sig,
|
||||
sig,
|
||||
NULL,
|
||||
"Initial commit",
|
||||
tree,
|
||||
0
|
||||
&commit_id,
|
||||
repo,
|
||||
"HEAD",
|
||||
sig,
|
||||
sig,
|
||||
NULL,
|
||||
"Initial commit",
|
||||
tree,
|
||||
0
|
||||
),
|
||||
"Could not create the initial commit",
|
||||
NULL);
|
||||
|
||||
git_index_write(repo_index);
|
||||
git_index_free(repo_index);
|
||||
git_tree_free(tree);
|
||||
git_signature_free(sig);
|
||||
}
|
||||
@@ -153,12 +180,11 @@ bool GitAPI::_get_is_vcs_intialized() {
|
||||
Dictionary GitAPI::_get_modified_files_data() {
|
||||
|
||||
git_status_options opts = GIT_STATUS_OPTIONS_INIT;
|
||||
opts.flags += GIT_STATUS_OPT_EXCLUDE_SUBMODULES;
|
||||
opts.flags += GIT_STATUS_OPT_INCLUDE_UNTRACKED;
|
||||
opts.flags += GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS;
|
||||
opts.flags += GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX;
|
||||
git_status_list *statuses = NULL;
|
||||
opts.show = GIT_STATUS_SHOW_INDEX_AND_WORKDIR;
|
||||
opts.flags = GIT_STATUS_OPT_EXCLUDE_SUBMODULES;
|
||||
opts.flags |= GIT_STATUS_OPT_INCLUDE_UNTRACKED | GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX | GIT_STATUS_OPT_SORT_CASE_SENSITIVELY;
|
||||
|
||||
git_status_list *statuses = NULL;
|
||||
GIT2_CALL(git_status_list_new(&statuses, repo, &opts), "Could not get status information from repository", NULL);
|
||||
|
||||
Dictionary diff; // Schema is <file_path, status>
|
||||
@@ -209,6 +235,28 @@ Dictionary GitAPI::_get_modified_files_data() {
|
||||
return diff;
|
||||
}
|
||||
|
||||
String GitAPI::_get_file_diff(const String file_path) {
|
||||
|
||||
git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
|
||||
git_diff *diff;
|
||||
char *pathspec = file_path.alloc_c_string();
|
||||
|
||||
opts.context_lines = 0;
|
||||
opts.interhunk_lines = 0;
|
||||
opts.flags = GIT_DIFF_DISABLE_PATHSPEC_MATCH | GIT_DIFF_INCLUDE_UNTRACKED;
|
||||
opts.pathspec.strings = &pathspec;
|
||||
opts.pathspec.count = 1;
|
||||
|
||||
git_diff_index_to_workdir(&diff, repo, NULL, &opts);
|
||||
|
||||
diff_content_container = "";
|
||||
git_diff_print(diff, GIT_DIFF_FORMAT_PATCH, diff_line_callback_function, NULL);
|
||||
|
||||
git_diff_free(diff);
|
||||
|
||||
return diff_content_container;
|
||||
}
|
||||
|
||||
String GitAPI::_get_project_name() {
|
||||
|
||||
return String("project");
|
||||
@@ -223,6 +271,8 @@ bool GitAPI::_initialize(const String p_project_root_path) {
|
||||
|
||||
ERR_FAIL_COND_V(p_project_root_path == "", false);
|
||||
|
||||
singleton = this;
|
||||
|
||||
int init = git_libgit2_init();
|
||||
if (init > 1) {
|
||||
|
||||
@@ -237,7 +287,7 @@ bool GitAPI::_initialize(const String p_project_root_path) {
|
||||
GIT2_CALL(git_repository_init(&repo, p_project_root_path.alloc_c_string(), 0), "Could not initialize repository", NULL);
|
||||
if (git_repository_head_unborn(repo) == 1) {
|
||||
|
||||
create_gitignore();
|
||||
create_gitignore_and_gitattributes();
|
||||
create_initial_commit();
|
||||
}
|
||||
|
||||
@@ -250,6 +300,7 @@ bool GitAPI::_initialize(const String p_project_root_path) {
|
||||
bool GitAPI::_shut_down() {
|
||||
|
||||
git_repository_free(repo);
|
||||
|
||||
GIT2_CALL(git_libgit2_shutdown(), "Could not shutdown Git Addon", NULL);
|
||||
|
||||
return true;
|
||||
|
||||
@@ -9,11 +9,10 @@
|
||||
#include <Directory.hpp>
|
||||
#include <File.hpp>
|
||||
|
||||
#include <git2.h>
|
||||
|
||||
#include <git_common.h>
|
||||
#include <allocation_defs.h>
|
||||
#include <git_callbacks.h>
|
||||
|
||||
#include <git2.h>
|
||||
|
||||
namespace godot {
|
||||
|
||||
@@ -21,20 +20,21 @@ class GitAPI : public EditorVCSInterface {
|
||||
|
||||
GODOT_CLASS(GitAPI, EditorVCSInterface)
|
||||
|
||||
static bool is_initialized;
|
||||
static GitAPI *singleton;
|
||||
|
||||
Array staged_files;
|
||||
|
||||
|
||||
PanelContainer *init_settings_panel_container;
|
||||
Button *init_settings_button;
|
||||
|
||||
git_repository *repo;
|
||||
|
||||
|
||||
void _commit(const String p_msg);
|
||||
Control *_get_commit_dock_panel_container();
|
||||
Control *_get_initialization_settings_panel_container();
|
||||
bool _get_is_vcs_intialized();
|
||||
Dictionary _get_modified_files_data();
|
||||
String _get_file_diff(const String file_path);
|
||||
String _get_project_name();
|
||||
String _get_vcs_name();
|
||||
bool _initialize(const String p_project_root_path);
|
||||
@@ -45,7 +45,13 @@ class GitAPI : public EditorVCSInterface {
|
||||
public:
|
||||
static void _register_methods();
|
||||
|
||||
void create_gitignore();
|
||||
static GitAPI *get_singleton() { return singleton; }
|
||||
|
||||
bool is_initialized;
|
||||
|
||||
String diff_content_container;
|
||||
|
||||
void create_gitignore_and_gitattributes();
|
||||
void create_initial_commit();
|
||||
|
||||
void _init();
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
#include <git_callbacks.h>
|
||||
|
||||
int status_callback(const char *p_path, const char *p_matched_pathspec, void *p_payload) {
|
||||
|
||||
StatusPayload p = *(StatusPayload *)(p_payload);
|
||||
int ret;
|
||||
unsigned int status;
|
||||
|
||||
if (git_status_file(&status, p.repo, p_path)) {
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((status & GIT_STATUS_WT_MODIFIED) || (status & GIT_STATUS_WT_NEW)) {
|
||||
|
||||
godot::Godot::print("Staged '%s'", p_path);
|
||||
ret = 0;
|
||||
} else {
|
||||
|
||||
ret = 1;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
#ifndef GIT_CALLBACKS_H
|
||||
#define GIT_CALLBACKS_H
|
||||
|
||||
#include <Godot.hpp>
|
||||
#include <git2.h>
|
||||
|
||||
#include <git_common.h>
|
||||
|
||||
int status_callback(const char *p_path, const char *p_matched_pathspec, void *p_payload);
|
||||
|
||||
#endif // !GIT_CALLBACKS_H
|
||||
@@ -1,4 +1,5 @@
|
||||
#include <git_common.h>
|
||||
#include <git_api.h>
|
||||
|
||||
void check_git2_errors(int error, const char *message, const char *extra) {
|
||||
|
||||
@@ -24,3 +25,25 @@ void check_git2_errors(int error, const char *message, const char *extra) {
|
||||
printf("Git API: %s [%d]%s%s\n", message, error, lg2spacer, lg2msg);
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" int diff_line_callback_function(const git_diff_delta *delta, const git_diff_hunk *hunk, const git_diff_line *line, void *payload) {
|
||||
|
||||
// NULL terminate the line text
|
||||
char *content = new char[line->content_len + 1];
|
||||
memcpy(content, line->content, line->content_len);
|
||||
static int i = 0;
|
||||
content[line->content_len] = '\0';
|
||||
|
||||
godot::String prefix;
|
||||
switch (line->origin) {
|
||||
|
||||
case GIT_DIFF_LINE_ADD_EOFNL:
|
||||
case GIT_DIFF_LINE_ADDITION: prefix = "+"; break;
|
||||
|
||||
case GIT_DIFF_LINE_DEL_EOFNL:
|
||||
case GIT_DIFF_LINE_DELETION: prefix = "-"; break;
|
||||
}
|
||||
godot::GitAPI::get_singleton()->diff_content_container += "\n" + prefix + godot::String(content);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -7,11 +7,8 @@
|
||||
|
||||
void check_git2_errors(int error, const char *message, const char *extra);
|
||||
|
||||
extern "C" int diff_line_callback_function(const git_diff_delta *delta, const git_diff_hunk *hunk, const git_diff_line *line, void *payload);
|
||||
|
||||
#define GIT2_CALL(function_call, m_error_msg, m_additional_msg) check_git2_errors(function_call, m_error_msg, m_additional_msg);
|
||||
|
||||
struct StatusPayload {
|
||||
|
||||
git_repository *repo;
|
||||
};
|
||||
|
||||
#endif // !GIT_COMMON_H
|
||||
|
||||
Reference in New Issue
Block a user