diff --git a/demo/.gitattributes b/demo/.gitattributes new file mode 100644 index 0000000..939be60 --- /dev/null +++ b/demo/.gitattributes @@ -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 diff --git a/demo/bin/win64/libgitapi.dll b/demo/bin/win64/libgitapi.dll index b04ca2a..004298d 100644 Binary files a/demo/bin/win64/libgitapi.dll and b/demo/bin/win64/libgitapi.dll differ diff --git a/demo/bin/win64/libgitapi.exp b/demo/bin/win64/libgitapi.exp index 6fa6383..e3495f5 100644 Binary files a/demo/bin/win64/libgitapi.exp and b/demo/bin/win64/libgitapi.exp differ diff --git a/demo/bin/win64/libgitapi.lib b/demo/bin/win64/libgitapi.lib index 6dfa3d0..fe8ba5f 100644 Binary files a/demo/bin/win64/libgitapi.lib and b/demo/bin/win64/libgitapi.lib differ diff --git a/demo/new_script.gd b/demo/new_script.gd index 0cfed86..dd2f28f 100644 --- a/demo/new_script.gd +++ b/demo/new_script.gd @@ -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 diff --git a/demo/project.godot b/demo/project.godot index 6510017..6d981cc 100644 --- a/demo/project.godot +++ b/demo/project.godot @@ -21,6 +21,7 @@ _global_script_class_icons={ [application] config/name="demo" +run/main_scene="res://demo.tscn" config/icon="res://icon.png" [gdnative] diff --git a/godot-git-plugin/godot-git-plugin.vcxproj b/godot-git-plugin/godot-git-plugin.vcxproj index 9148296..bf2ab66 100644 --- a/godot-git-plugin/godot-git-plugin.vcxproj +++ b/godot-git-plugin/godot-git-plugin.vcxproj @@ -129,13 +129,11 @@ - - diff --git a/godot-git-plugin/godot-git-plugin.vcxproj.filters b/godot-git-plugin/godot-git-plugin.vcxproj.filters index 391df48..c294095 100644 --- a/godot-git-plugin/godot-git-plugin.vcxproj.filters +++ b/godot-git-plugin/godot-git-plugin.vcxproj.filters @@ -21,9 +21,6 @@ Source Files - - Source Files - Source Files @@ -35,9 +32,6 @@ Header Files - - Header Files - Header Files diff --git a/godot-git-plugin/src/git_api.cpp b/godot-git-plugin/src/git_api.cpp index 4f83022..afc5b73 100644 --- a/godot-git-plugin/src/git_api.cpp +++ b/godot-git-plugin/src/git_api.cpp @@ -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 @@ -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; diff --git a/godot-git-plugin/src/git_api.h b/godot-git-plugin/src/git_api.h index 2c5f921..6ac723b 100644 --- a/godot-git-plugin/src/git_api.h +++ b/godot-git-plugin/src/git_api.h @@ -9,11 +9,10 @@ #include #include -#include - #include #include -#include + +#include 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(); diff --git a/godot-git-plugin/src/git_callbacks.cpp b/godot-git-plugin/src/git_callbacks.cpp deleted file mode 100644 index df76c67..0000000 --- a/godot-git-plugin/src/git_callbacks.cpp +++ /dev/null @@ -1,24 +0,0 @@ -#include - -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; -} diff --git a/godot-git-plugin/src/git_callbacks.h b/godot-git-plugin/src/git_callbacks.h deleted file mode 100644 index 47820ef..0000000 --- a/godot-git-plugin/src/git_callbacks.h +++ /dev/null @@ -1,11 +0,0 @@ -#ifndef GIT_CALLBACKS_H -#define GIT_CALLBACKS_H - -#include -#include - -#include - -int status_callback(const char *p_path, const char *p_matched_pathspec, void *p_payload); - -#endif // !GIT_CALLBACKS_H diff --git a/godot-git-plugin/src/git_common.cpp b/godot-git-plugin/src/git_common.cpp index 00e9581..e2c389e 100644 --- a/godot-git-plugin/src/git_common.cpp +++ b/godot-git-plugin/src/git_common.cpp @@ -1,4 +1,5 @@ #include +#include 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; +} diff --git a/godot-git-plugin/src/git_common.h b/godot-git-plugin/src/git_common.h index cad7eed..7c15cd1 100644 --- a/godot-git-plugin/src/git_common.h +++ b/godot-git-plugin/src/git_common.h @@ -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