diff --git a/godot-git-plugin/src/git_api.cpp b/godot-git-plugin/src/git_api.cpp index 1ccb888..4fd1439 100644 --- a/godot-git-plugin/src/git_api.cpp +++ b/godot-git-plugin/src/git_api.cpp @@ -36,7 +36,7 @@ void GitAPI::_discard_file(String p_file_path) { } void GitAPI::_commit(const String p_msg) { - + if (!can_commit) { godot::Godot::print("Git API: Cannot commit. Check previous errors."); return; @@ -95,7 +95,7 @@ void GitAPI::_commit(const String p_msg) { git_commit_free(fetchhead_commit); git_repository_state_cleanup(repo); } - + git_index_free(repo_index); git_signature_free(default_sign); git_commit_free(parent_commit); @@ -210,8 +210,8 @@ Dictionary GitAPI::_get_modified_files_data() { Dictionary diff; Dictionary index; // Schema is Dictionary wt; // Schema is - diff["index"] = index; - diff["wt"] = wt; + diff[TREE_AREA_STAGED] = index; + diff[TREE_AREA_UNSTAGED] = wt; git_status_options opts = GIT_STATUS_OPTIONS_INIT; opts.show = GIT_STATUS_SHOW_INDEX_AND_WORKDIR; @@ -236,17 +236,17 @@ Dictionary GitAPI::_get_modified_files_data() { } static Dictionary map_changes; - map_changes[GIT_STATUS_WT_NEW] = 0; - map_changes[GIT_STATUS_INDEX_NEW] = 0; - map_changes[GIT_STATUS_WT_MODIFIED] = 1; - map_changes[GIT_STATUS_INDEX_MODIFIED] = 1; - map_changes[GIT_STATUS_WT_RENAMED] = 2; - map_changes[GIT_STATUS_INDEX_RENAMED] = 2; - map_changes[GIT_STATUS_WT_DELETED] = 3; - map_changes[GIT_STATUS_INDEX_DELETED] = 3; - map_changes[GIT_STATUS_WT_TYPECHANGE] = 4; - map_changes[GIT_STATUS_INDEX_TYPECHANGE] = 4; - map_changes[GIT_STATUS_CONFLICTED] = 5; + map_changes[GIT_STATUS_WT_NEW] = CHANGE_TYPE_NEW; + map_changes[GIT_STATUS_INDEX_NEW] = CHANGE_TYPE_NEW; + map_changes[GIT_STATUS_WT_MODIFIED] = CHANGE_TYPE_MODIFIED; + map_changes[GIT_STATUS_INDEX_MODIFIED] = CHANGE_TYPE_MODIFIED; + map_changes[GIT_STATUS_WT_RENAMED] = CHANGE_TYPE_RENAMED; + map_changes[GIT_STATUS_INDEX_RENAMED] = CHANGE_TYPE_RENAMED; + map_changes[GIT_STATUS_WT_DELETED] = CHANGE_TYPE_DELETED; + map_changes[GIT_STATUS_INDEX_DELETED] = CHANGE_TYPE_DELETED; + map_changes[GIT_STATUS_WT_TYPECHANGE] = CHANGE_TYPE_TYPECHANGE; + map_changes[GIT_STATUS_INDEX_TYPECHANGE] = CHANGE_TYPE_TYPECHANGE; + map_changes[GIT_STATUS_CONFLICTED] = CHANGE_TYPE_UNMERGED; const static int git_status_wt = GIT_STATUS_WT_NEW | GIT_STATUS_WT_MODIFIED | GIT_STATUS_WT_DELETED | GIT_STATUS_WT_TYPECHANGE | GIT_STATUS_WT_RENAMED | GIT_STATUS_CONFLICTED; const static int git_status_index = GIT_STATUS_INDEX_NEW | GIT_STATUS_INDEX_MODIFIED | GIT_STATUS_INDEX_DELETED | GIT_STATUS_INDEX_RENAMED | GIT_STATUS_INDEX_TYPECHANGE; @@ -317,6 +317,7 @@ Array GitAPI::_get_previous_commits() { git_commit *commit; const git_signature *sig; git_oid oid; + char commit_id[GIT_OID_HEXSZ + 1]; git_revwalk_new(&walker, repo); git_revwalk_sorting(walker, GIT_SORT_TIME); @@ -324,14 +325,15 @@ Array GitAPI::_get_previous_commits() { for (int i = 0; !git_revwalk_next(&oid, walker) && i <= max_commit_fetch; i++) { - GIT2_CALL_R(git_commit_lookup(&commit, repo, &oid), "Failed to lookup the commit", Array()); + GIT2_CALL_R(git_commit_lookup(&commit, repo, &oid), "Failed to lookup the commit", commits); sig = git_commit_author(commit); - + git_oid_tostr(commit_id, GIT_OID_HEXSZ + 1, git_commit_id(commit)); Dictionary commit_info; commit_info["message"] = String(git_commit_message(commit)); commit_info["author"] = String(sig->name); commit_info["when"] = (int64_t)sig->when.time + (int64_t)(sig->when.offset * 60); // Epoch time in seconds + commit_info["id"] = String(commit_id); commits.push_back(commit_info); git_commit_free(commit); } @@ -467,29 +469,144 @@ bool GitAPI::_checkout_branch(String p_branch_name) { return true; } -Array GitAPI::_get_file_diff(const String file_path) { +Array GitAPI::_get_file_diff(const String identifier, int area) { git_diff_options opts = GIT_DIFF_OPTIONS_INIT; git_diff *diff; + Array diff_contents; - opts.context_lines = 3; + opts.context_lines = 2; opts.interhunk_lines = 0; opts.flags = GIT_DIFF_DISABLE_PATHSPEC_MATCH | GIT_DIFF_INCLUDE_UNTRACKED; - char *pathspec = file_path.alloc_c_string(); + char *pathspec = identifier.alloc_c_string(); opts.pathspec.strings = &pathspec; opts.pathspec.count = 1; - GIT2_CALL_R(git_diff_index_to_workdir(&diff, repo, NULL, &opts), "Could not create diff for index from working directory", Array()); + switch ((TreeArea)area) { + case TREE_AREA_UNSTAGED: { + GIT2_CALL_R(git_diff_index_to_workdir(&diff, repo, NULL, &opts), "Could not create diff for index from working directory", diff_contents); + } break; + case TREE_AREA_STAGED: { + git_object *obj = nullptr; + git_tree *tree = nullptr; - diff_contents.clear(); - GIT2_CALL_R(git_diff_print(diff, GIT_DIFF_FORMAT_PATCH, diff_line_callback_function, &diff_contents), "Call to diff handler provided unsuccessful", Array()); + GIT2_CALL_R(git_revparse_single(&obj, repo, "HEAD^{tree}"), "", diff_contents); + GIT2_CALL_R(git_tree_lookup(&tree, repo, git_object_id(obj)), "", diff_contents); + GIT2_CALL_R(git_diff_tree_to_index(&diff, repo, tree, NULL, &opts), "Could not create diff for tree from index directory", diff_contents); + + git_tree_free(tree); + git_object_free(obj); + } break; + case TREE_AREA_COMMIT: { + opts.pathspec = {}; + git_object *obj = nullptr; + git_commit *commit = nullptr, *parent = nullptr; + git_tree *commit_tree = nullptr, *parent_tree = nullptr; + + GIT2_CALL_R(git_revparse_single(&obj, repo, pathspec), "", diff_contents); + GIT2_CALL_R(git_commit_lookup(&commit, repo, git_object_id(obj)), "", diff_contents); + GIT2_CALL_R(git_commit_parent(&parent, commit, 0), "", diff_contents); + GIT2_CALL_R(git_commit_tree(&commit_tree, commit), "", diff_contents); + GIT2_CALL_R(git_commit_tree(&parent_tree, parent), "", diff_contents); + GIT2_CALL_R(git_diff_tree_to_tree(&diff, repo, parent_tree, commit_tree, &opts), "", diff_contents); + + git_object_free(obj); + git_commit_free(commit); + git_commit_free(parent); + git_tree_free(commit_tree); + git_tree_free(parent_tree); + } break; + } + diff_contents = _parse_diff(diff); git_diff_free(diff); return diff_contents; } +Array GitAPI::_parse_diff(git_diff *diff) { + + /* + diff_files: [ + { + String new_file_path: + String old_file_path: + hunks:[ + { + int old_start: + int old_lines: + int new_start: + int new_lines: + diff_lines: [ + { + int old_line_no: + int new_line_no: + String content: + String status: + } + ] + } + ] + } + ] + */ + Array diff_contents; + for (int i = 0; i < git_diff_num_deltas(diff); i++) { + git_patch *patch; + const git_diff_delta *delta = git_diff_get_delta(diff, i); //file_cb + git_patch_from_diff(&patch, diff, i); + + if (delta->flags & GIT_DIFF_FLAG_BINARY) { + continue; + } + + if (delta->status == GIT_DELTA_UNMODIFIED || delta->status == GIT_DELTA_IGNORED || delta->status == GIT_DELTA_UNTRACKED) { + continue; + } + Dictionary file_diff; + file_diff["new_file"] = String(delta->new_file.path); + file_diff["old_file"] = String(delta->old_file.path); + + Array hunks; + for (int j = 0; j < git_patch_num_hunks(patch); j++) { + const git_diff_hunk *git_hunk; + size_t line_count; + git_patch_get_hunk(&git_hunk, &line_count, patch, j); + + Dictionary hunk; + hunk["old_start"] = git_hunk->old_start; + hunk["new_start"] = git_hunk->new_start; + hunk["old_lines"] = git_hunk->old_lines; + hunk["new_lines"] = git_hunk->new_lines; + + Array diff_lines; + for (int k = 0; k < line_count; k++) { + const git_diff_line *git_diff_line; + git_patch_get_line_in_hunk(&git_diff_line, patch, j, k); //line_cb + char *content = new char[git_diff_line->content_len + 1]; + memcpy(content, git_diff_line->content, git_diff_line->content_len); + content[git_diff_line->content_len] = '\0'; + + Dictionary diff_line; + diff_line["old_line_no"] = git_diff_line->old_lineno; + diff_line["new_line_no"] = git_diff_line->new_lineno; + diff_line["content"] = String(content); + diff_line["status"] = String(git_diff_line->origin); + + diff_lines.push_back(diff_line); + } + + hunk["diff_lines"] = diff_lines; + hunks.push_back(hunk); + } + file_diff["hunks"] = hunks; + diff_contents.push_back(file_diff); + git_patch_free(patch); + } + return diff_contents; +} + String GitAPI::_get_project_name() { return String("project"); diff --git a/godot-git-plugin/src/git_api.h b/godot-git-plugin/src/git_api.h index a1cb873..977f6b9 100644 --- a/godot-git-plugin/src/git_api.h +++ b/godot-git-plugin/src/git_api.h @@ -36,14 +36,13 @@ class GitAPI : public EditorVCSInterface { git_remote_callbacks remote_cbs; bool has_merge = false; - Array diff_contents; git_oid pull_merge_oid; Credentials creds; void _commit(const String p_msg); bool _is_vcs_initialized(); Dictionary _get_modified_files_data(); - Array _get_file_diff(const String file_path); + Array _get_file_diff(const String file_path, int area); String _get_project_name(); String _get_vcs_name(); bool _initialize(const String p_project_root_path); @@ -60,6 +59,7 @@ class GitAPI : public EditorVCSInterface { void _push(); const char *_get_current_branch_name(bool full_ref); void _set_up_credentials(String p_username, String p_password); + Array _parse_diff(git_diff *diff); public: static void _register_methods(); diff --git a/godot-git-plugin/src/git_common.cpp b/godot-git-plugin/src/git_common.cpp index dbba9b8..a8b52e9 100644 --- a/godot-git-plugin/src/git_common.cpp +++ b/godot-git-plugin/src/git_common.cpp @@ -22,44 +22,6 @@ bool check_git2_errors(int error, godot::String message, godot::String function, return true; } -extern "C" int diff_line_callback_function(const git_diff_delta *delta, const git_diff_hunk *hunk, const git_diff_line *line, void *payload) { - - // First we NULL terminate the line text incoming - 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_DEL_EOFNL: - case GIT_DIFF_LINE_DELETION: - prefix = "-"; - break; - - case GIT_DIFF_LINE_ADD_EOFNL: - case GIT_DIFF_LINE_ADDITION: - prefix = "+"; - break; - } - - godot::String content_str = content; - - godot::Dictionary result; - result["content"] = prefix + content_str; - result["status"] = prefix; - result["new_line_number"] = line->new_lineno; - result["line_count"] = line->num_lines; - result["old_line_number"] = line->old_lineno; - result["offset"] = line->content_offset; - - godot::Array *diff_contents = (godot::Array *)payload; - diff_contents->push_back(result); - - return 0; -} - extern "C" int progress_cb(const char *str, int len, void *data) { (void)data; godot::Godot::print("remote: " + godot::String(str).strip_edges()); diff --git a/godot-git-plugin/src/git_common.h b/godot-git-plugin/src/git_common.h index 1291647..2e66620 100644 --- a/godot-git-plugin/src/git_common.h +++ b/godot-git-plugin/src/git_common.h @@ -10,7 +10,6 @@ struct Credentials { char *password; }; -extern "C" int diff_line_callback_function(const git_diff_delta *delta, const git_diff_hunk *hunk, const git_diff_line *line, void *payload); extern "C" int progress_cb(const char *str, int len, void *data); extern "C" int update_cb(const char *refname, const git_oid *a, const git_oid *b, void *data); extern "C" int transfer_progress_cb(const git_indexer_progress *stats, void *payload);