gitout

A static git page generator
git clone https://git.bracken.jp/gitout.git
Log | Files | Refs | Submodules | README | LICENSE

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 }