gout

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

commit.c (3990B)


      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   }
     65 
     66   // Compute the diff.
     67   git_diff* diff = NULL;
     68   git_diff_options opts;
     69   git_diff_options_init(&opts, GIT_DIFF_OPTIONS_VERSION);
     70   opts.flags |= GIT_DIFF_DISABLE_PATHSPEC_MATCH | GIT_DIFF_IGNORE_SUBMODULES |
     71                 GIT_DIFF_INCLUDE_TYPECHANGE;
     72   if (git_diff_tree_to_tree(&diff, repo, parent_tree, commit_tree, &opts)) {
     73     errx(1, "git_diff_tree_to_tree");
     74   }
     75   git_tree_free(commit_tree);
     76   git_tree_free(parent_tree);
     77 
     78   git_diff_find_options fopts;
     79   if (git_diff_find_options_init(&fopts, GIT_DIFF_FIND_OPTIONS_VERSION)) {
     80     errx(1, "git_diff_find_init_options");
     81   }
     82 
     83   /* find renames and copies, exact matches (no heuristic) for renames. */
     84   fopts.flags |= GIT_DIFF_FIND_RENAMES | GIT_DIFF_FIND_COPIES |
     85                  GIT_DIFF_FIND_EXACT_MATCH_ONLY;
     86   if (git_diff_find_similar(diff, &fopts)) {
     87     errx(1, "git_diff_find_similar");
     88   }
     89 
     90   // Add deltas.
     91   commit->addcount = 0;
     92   commit->delcount = 0;
     93   size_t deltas_len = git_diff_num_deltas(diff);
     94   commit->deltas = ecalloc(deltas_len, sizeof(GitDelta*));
     95   size_t deltas_out_count = 0;
     96   for (size_t i = 0; i < deltas_len; i++) {
     97     git_patch* patch = NULL;
     98     if (git_patch_from_diff(&patch, diff, i)) {
     99       continue;
    100     }
    101 
    102     // Hand ownership of patch to delta.
    103     GitDelta* delta = gitdelta_create(patch);
    104     if (delta) {
    105       commit->deltas[deltas_out_count++] = delta;
    106       commit->addcount += delta->addcount;
    107       commit->delcount += delta->delcount;
    108     }
    109   }
    110   commit->deltas_len = deltas_out_count;
    111   commit->filecount = deltas_len;
    112 
    113   git_diff_free(diff);
    114   git_commit_free(gcommit);
    115   return commit;
    116 }
    117 
    118 void gitcommit_free(GitCommit* commit) {
    119   if (!commit) {
    120     return;
    121   }
    122   free(commit->summary);
    123   free(commit->message);
    124   free(commit->author_name);
    125   free(commit->author_email);
    126   for (size_t i = 0; i < commit->deltas_len; i++) {
    127     gitdelta_free(commit->deltas[i]);
    128   }
    129   free(commit->deltas);
    130   free(commit);
    131 }