commit.c (4065B)
1 #include "git/commit.h" 2 3 #include <assert.h> 4 #include <err.h> 5 #include <git2/commit.h> 6 #include <git2/diff.h> 7 #include <git2/oid.h> 8 #include <git2/patch.h> 9 #include <git2/tree.h> 10 #include <git2/types.h> 11 #include <stdlib.h> 12 13 #include "git/internal.h" 14 15 GitCommit* gitcommit_create(const git_oid* oid, git_repository* repo) { 16 assert(oid != NULL); 17 assert(repo != NULL); 18 GitCommit* commit = ecalloc(1, sizeof(GitCommit)); 19 git_commit* gcommit = NULL; 20 if (git_commit_lookup(&gcommit, repo, oid)) { 21 errx(1, "git_commit_lookup"); 22 } 23 24 // Get OID, parent OID. 25 git_oid_tostr(commit->oid, sizeof(commit->oid), git_commit_id(gcommit)); 26 if (git_commit_parentcount(gcommit) > 0) { 27 git_oid_tostr(commit->parentoid, sizeof(commit->parentoid), 28 git_commit_parent_id(gcommit, 0)); 29 } else { 30 commit->parentoid[0] = '\0'; 31 } 32 33 // Set commit summary, message. 34 const char* summary = git_commit_summary(gcommit); 35 commit->summary = summary ? estrdup(summary) : NULL; 36 const char* message = git_commit_message(gcommit); 37 commit->message = message ? estrdup(message) : NULL; 38 39 // Get commit time, tz offset. 40 commit->commit_time = git_commit_time(gcommit); 41 commit->commit_timezone_offset = git_commit_time_offset(gcommit); 42 43 // Get author info. 44 const git_signature* author = git_commit_author(gcommit); 45 commit->author_name = author->name ? estrdup(author->name) : NULL; 46 commit->author_email = author->email ? estrdup(author->email) : NULL; 47 commit->author_time = author->when.time; 48 commit->author_timezone_offset = author->when.offset; 49 50 // Look up commit tree. 51 git_tree* commit_tree = NULL; 52 if (git_tree_lookup(&commit_tree, repo, git_commit_tree_id(gcommit))) { 53 errx(1, "git_tree_lookup"); 54 } 55 56 // Look up parent tree, if there is a parent commit. 57 git_commit* parent = NULL; 58 git_tree* parent_tree = NULL; 59 if (!git_commit_parent(&parent, gcommit, 0)) { 60 if (git_tree_lookup(&parent_tree, repo, git_commit_tree_id(parent))) { 61 errx(1, "git_tree_lookup"); 62 } 63 git_commit_free(parent); 64 parent = NULL; 65 } 66 67 // Compute the diff. 68 git_diff* diff = NULL; 69 git_diff_options opts; 70 git_diff_options_init(&opts, GIT_DIFF_OPTIONS_VERSION); 71 opts.flags |= GIT_DIFF_DISABLE_PATHSPEC_MATCH | GIT_DIFF_IGNORE_SUBMODULES | 72 GIT_DIFF_INCLUDE_TYPECHANGE; 73 if (git_diff_tree_to_tree(&diff, repo, parent_tree, commit_tree, &opts)) { 74 errx(1, "git_diff_tree_to_tree"); 75 } 76 git_tree_free(commit_tree); 77 git_tree_free(parent_tree); 78 79 git_diff_find_options fopts; 80 if (git_diff_find_options_init(&fopts, GIT_DIFF_FIND_OPTIONS_VERSION)) { 81 errx(1, "git_diff_find_init_options"); 82 } 83 84 /* find renames and copies, exact matches (no heuristic) for renames. */ 85 fopts.flags |= GIT_DIFF_FIND_RENAMES | GIT_DIFF_FIND_COPIES | 86 GIT_DIFF_FIND_EXACT_MATCH_ONLY; 87 if (git_diff_find_similar(diff, &fopts)) { 88 errx(1, "git_diff_find_similar"); 89 } 90 91 // Add deltas. 92 commit->addcount = 0; 93 commit->delcount = 0; 94 size_t deltas_len = git_diff_num_deltas(diff); 95 commit->deltas = ecalloc(deltas_len, sizeof(GitDelta*)); 96 size_t deltas_out_count = 0; 97 for (size_t i = 0; i < deltas_len; i++) { 98 git_patch* patch = NULL; 99 if (git_patch_from_diff(&patch, diff, i)) { 100 continue; 101 } 102 103 // Hand ownership of patch to delta. 104 GitDelta* delta = gitdelta_create(patch); 105 if (delta) { 106 commit->deltas[deltas_out_count++] = delta; 107 commit->addcount += delta->addcount; 108 commit->delcount += delta->delcount; 109 } 110 } 111 commit->deltas_len = deltas_out_count; 112 commit->filecount = deltas_len; 113 114 git_diff_free(diff); 115 git_commit_free(gcommit); 116 return commit; 117 } 118 119 void gitcommit_free(GitCommit* commit) { 120 if (!commit) { 121 return; 122 } 123 free(commit->summary); 124 free(commit->message); 125 free(commit->author_name); 126 free(commit->author_email); 127 for (size_t i = 0; i < commit->deltas_len; i++) { 128 gitdelta_free(commit->deltas[i]); 129 commit->deltas[i] = NULL; 130 } 131 free(commit->deltas); 132 commit->deltas = NULL; 133 134 free(commit); 135 }