gout

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

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 }