commit.c (5865B)
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 #include "utils.h" 15 16 struct GitCommit { 17 char* oid; 18 char* parentoid; 19 const char* summary; 20 const char* message; 21 time_t commit_time; 22 int commit_timezone_offset; 23 const char* author_name; 24 const char* author_email; 25 time_t author_time; 26 int author_timezone_offset; 27 GitDelta** deltas; 28 size_t deltas_len; 29 size_t addcount; 30 size_t delcount; 31 size_t filecount; 32 33 git_commit* commit; 34 git_diff* diff; 35 }; 36 37 GitCommit* gitcommit_create(const git_oid* oid, git_repository* repo) { 38 GitCommit* commit = ecalloc(1, sizeof(GitCommit)); 39 if (git_commit_lookup(&commit->commit, repo, oid)) { 40 errx(1, "git_commit_lookup"); 41 } 42 43 // Get OID, parent OID. 44 commit->oid = ecalloc(GIT_OID_SHA1_HEXSIZE + 1, sizeof(char)); 45 git_oid_tostr(commit->oid, GIT_OID_SHA1_HEXSIZE + 1, 46 git_commit_id(commit->commit)); 47 commit->parentoid = ecalloc(GIT_OID_SHA1_HEXSIZE + 1, sizeof(char)); 48 git_oid_tostr(commit->parentoid, GIT_OID_SHA1_HEXSIZE + 1, 49 git_commit_parent_id(commit->commit, 0)); 50 51 // Set commit summary, message. 52 commit->summary = git_commit_summary(commit->commit); 53 commit->message = git_commit_message(commit->commit); 54 55 // Get commit time, tz offset. 56 commit->commit_time = git_commit_time(commit->commit); 57 commit->commit_timezone_offset = git_commit_time_offset(commit->commit); 58 59 // Get author info. 60 const git_signature* author = git_commit_author(commit->commit); 61 commit->author_name = author->name; 62 commit->author_email = author->email; 63 commit->author_time = author->when.time; 64 commit->author_timezone_offset = author->when.offset; 65 66 // Look up commit tree. 67 git_tree* commit_tree = NULL; 68 if (git_tree_lookup(&commit_tree, repo, git_commit_tree_id(commit->commit))) { 69 errx(1, "git_tree_lookup"); 70 } 71 72 // Look up parent tree, if there is a parent commit. 73 git_commit* parent = NULL; 74 git_tree* parent_tree = NULL; 75 if (!git_commit_parent(&parent, commit->commit, 0)) { 76 if (git_tree_lookup(&parent_tree, repo, git_commit_tree_id(parent))) { 77 errx(1, "git_tree_lookup"); 78 } 79 git_commit_free(parent); 80 parent = NULL; 81 } 82 83 // Compute the diff. 84 git_diff* diff = NULL; 85 git_diff_options opts; 86 git_diff_options_init(&opts, GIT_DIFF_OPTIONS_VERSION); 87 opts.flags |= GIT_DIFF_DISABLE_PATHSPEC_MATCH | GIT_DIFF_IGNORE_SUBMODULES | 88 GIT_DIFF_INCLUDE_TYPECHANGE; 89 if (git_diff_tree_to_tree(&diff, repo, parent_tree, commit_tree, &opts)) { 90 errx(1, "git_diff_tree_to_tree"); 91 } 92 git_tree_free(commit_tree); 93 git_tree_free(parent_tree); 94 95 git_diff_free(commit->diff); 96 commit->diff = diff; 97 98 git_diff_find_options fopts; 99 if (git_diff_find_options_init(&fopts, GIT_DIFF_FIND_OPTIONS_VERSION)) { 100 errx(1, "git_diff_find_init_options"); 101 } 102 103 /* find renames and copies, exact matches (no heuristic) for renames. */ 104 fopts.flags |= GIT_DIFF_FIND_RENAMES | GIT_DIFF_FIND_COPIES | 105 GIT_DIFF_FIND_EXACT_MATCH_ONLY; 106 if (git_diff_find_similar(commit->diff, &fopts)) { 107 errx(1, "git_diff_find_similar"); 108 } 109 110 // Add deltas. 111 commit->addcount = 0; 112 commit->delcount = 0; 113 size_t deltas_len = git_diff_num_deltas(commit->diff); 114 commit->deltas = ecalloc(deltas_len, sizeof(GitDelta*)); 115 size_t deltas_out_count = 0; 116 for (size_t i = 0; i < deltas_len; i++) { 117 git_patch* patch = NULL; 118 if (git_patch_from_diff(&patch, commit->diff, i)) { 119 continue; 120 } 121 122 // Hand ownership of patch to delta. 123 GitDelta* delta = gitdelta_create(patch); 124 if (delta) { 125 commit->deltas[deltas_out_count++] = delta; 126 commit->addcount += gitdelta_lines_added(delta); 127 commit->delcount += gitdelta_lines_deleted(delta); 128 } 129 } 130 commit->deltas_len = deltas_out_count; 131 commit->filecount = deltas_len; 132 return commit; 133 } 134 135 void gitcommit_free(GitCommit* commit) { 136 if (!commit) { 137 return; 138 } 139 free(commit->oid); 140 free(commit->parentoid); 141 for (size_t i = 0; i < commit->deltas_len; i++) { 142 gitdelta_free(commit->deltas[i]); 143 commit->deltas[i] = NULL; 144 } 145 free(commit->deltas); 146 commit->deltas = NULL; 147 148 git_diff_free(commit->diff); 149 commit->diff = NULL; 150 git_commit_free(commit->commit); 151 commit->commit = NULL; 152 free(commit); 153 } 154 155 const char* gitcommit_oid(const GitCommit* commit) { 156 return commit->oid; 157 } 158 159 const char* gitcommit_parentoid(const GitCommit* commit) { 160 return commit->parentoid; 161 } 162 163 const char* gitcommit_summary(const GitCommit* commit) { 164 return commit->summary; 165 } 166 167 const char* gitcommit_message(const GitCommit* commit) { 168 return commit->message; 169 } 170 171 time_t gitcommit_commit_time(const GitCommit* commit) { 172 return commit->commit_time; 173 } 174 175 int gitcommit_commit_timezone_offset(const GitCommit* commit) { 176 return commit->commit_timezone_offset; 177 } 178 179 const char* gitcommit_author_name(const GitCommit* commit) { 180 return commit->author_name; 181 } 182 183 const char* gitcommit_author_email(const GitCommit* commit) { 184 return commit->author_email; 185 } 186 187 time_t gitcommit_author_time(const GitCommit* commit) { 188 return commit->author_time; 189 } 190 191 int gitcommit_author_timezone_offset(const GitCommit* commit) { 192 return commit->author_timezone_offset; 193 } 194 195 GitDelta* gitcommit_delta(const GitCommit* commit, size_t index) { 196 assert(index < commit->deltas_len); 197 return commit->deltas[index]; 198 } 199 200 size_t gitcommit_delta_count(const GitCommit* commit) { 201 return commit->deltas_len; 202 } 203 204 size_t gitcommit_addcount(const GitCommit* commit) { 205 return commit->addcount; 206 } 207 208 size_t gitcommit_delcount(const GitCommit* commit) { 209 return commit->delcount; 210 } 211 212 size_t gitcommit_filecount(const GitCommit* commit) { 213 return commit->filecount; 214 }