gout

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

delta.c (5868B)


      1 #include "git/delta.h"
      2 
      3 #include <assert.h>
      4 #include <git2/diff.h>
      5 #include <git2/patch.h>
      6 #include <stdlib.h>
      7 #include <string.h>
      8 
      9 #include "git/internal.h"
     10 #include "utils.h"
     11 
     12 /* HunkLines */
     13 static GitHunkLine* githunkline_create(git_patch* patch,
     14                                        size_t hunk_id,
     15                                        size_t line_id);
     16 static void githunkline_free(GitHunkLine* hunk_line);
     17 
     18 /* Hunks */
     19 static GitHunk* githunk_create(git_patch* patch, size_t id);
     20 static void githunk_free(GitHunk* hunk);
     21 
     22 /* Deltas */
     23 static char status_for_delta(const git_diff_delta* delta);
     24 static char* gitdelta_graph(const GitDelta* delta,
     25                             size_t length,
     26                             char character,
     27                             size_t max_width);
     28 
     29 static GitHunkLine* githunkline_create(git_patch* patch,
     30                                        size_t hunk_id,
     31                                        size_t line_id) {
     32   assert(patch != NULL);
     33   const git_diff_line* line;
     34   if (git_patch_get_line_in_hunk(&line, patch, hunk_id, line_id)) {
     35     return NULL;
     36   }
     37 
     38   GitHunkLine* line_out = ecalloc(1, sizeof(GitHunkLine));
     39   line_out->id = line_id;
     40   line_out->old_lineno = line->old_lineno;
     41   line_out->new_lineno = line->new_lineno;
     42   line_out->content_len = line->content_len;
     43   line_out->content = ecalloc(line->content_len + 1, sizeof(char));
     44   memcpy(line_out->content, line->content, line->content_len);
     45   return line_out;
     46 }
     47 
     48 static void githunkline_free(GitHunkLine* hunk_line) {
     49   if (!hunk_line) {
     50     return;
     51   }
     52   free(hunk_line->content);
     53   free(hunk_line);
     54 }
     55 
     56 static GitHunk* githunk_create(git_patch* patch, size_t hunk_id) {
     57   assert(patch != NULL);
     58   const git_diff_hunk* hunk;
     59   size_t line_count;
     60   if (git_patch_get_hunk(&hunk, &line_count, patch, hunk_id)) {
     61     return NULL;
     62   }
     63 
     64   GitHunk* hunk_out = ecalloc(1, sizeof(GitHunk));
     65   hunk_out->id = hunk_id;
     66   hunk_out->header = estrdup(hunk->header);
     67   hunk_out->lines = ecalloc(line_count, sizeof(GitHunkLine*));
     68   size_t lines_out_count = 0;
     69   for (size_t i = 0; i < line_count; i++) {
     70     GitHunkLine* hunk_line = githunkline_create(patch, hunk_id, i);
     71     if (hunk_line) {
     72       hunk_out->lines[lines_out_count++] = hunk_line;
     73     }
     74   }
     75   hunk_out->lines_len = lines_out_count;
     76   return hunk_out;
     77 }
     78 
     79 static void githunk_free(GitHunk* hunk) {
     80   if (!hunk) {
     81     return;
     82   }
     83   free(hunk->header);
     84   for (size_t i = 0; i < hunk->lines_len; i++) {
     85     githunkline_free(hunk->lines[i]);
     86     hunk->lines[i] = NULL;
     87   }
     88   free(hunk->lines);
     89   hunk->lines = NULL;
     90   free(hunk);
     91 }
     92 
     93 static char status_for_delta(const git_diff_delta* delta) {
     94   assert(delta != NULL);
     95   switch (delta->status) {
     96     case GIT_DELTA_ADDED:
     97       return 'A';
     98     case GIT_DELTA_COPIED:
     99       return 'C';
    100     case GIT_DELTA_DELETED:
    101       return 'D';
    102     case GIT_DELTA_MODIFIED:
    103       return 'M';
    104     case GIT_DELTA_RENAMED:
    105       return 'R';
    106     case GIT_DELTA_TYPECHANGE:
    107       return 'T';
    108     case GIT_DELTA_CONFLICTED:
    109     case GIT_DELTA_IGNORED:
    110     case GIT_DELTA_UNMODIFIED:
    111     case GIT_DELTA_UNREADABLE:
    112     case GIT_DELTA_UNTRACKED:
    113       return ' ';
    114   }
    115   return ' ';
    116 }
    117 
    118 GitDelta* gitdelta_create(git_patch* patch) {
    119   assert(patch != NULL);
    120   GitDelta* delta = ecalloc(1, sizeof(GitDelta));
    121 
    122   const git_diff_delta* git_delta = git_patch_get_delta(patch);
    123   delta->status = status_for_delta(git_delta);
    124   delta->is_binary = git_delta->flags & GIT_DIFF_FLAG_BINARY;
    125   delta->old_file_path =
    126       git_delta->old_file.path ? estrdup(git_delta->old_file.path) : NULL;
    127   delta->new_file_path =
    128       git_delta->new_file.path ? estrdup(git_delta->new_file.path) : NULL;
    129   delta->addcount = 0;
    130   delta->delcount = 0;
    131 
    132   /* Skip stats for binary data. */
    133   if (delta->is_binary) {
    134     git_patch_free(patch);
    135     return delta;
    136   }
    137 
    138   /* Populate hunks array. */
    139   size_t hunk_count = git_patch_num_hunks(patch);
    140   delta->hunks = ecalloc(hunk_count, sizeof(GitHunk*));
    141   size_t hunks_out_count = 0;
    142   for (size_t i = 0; i < hunk_count; i++) {
    143     const git_diff_hunk* hunk;
    144     size_t line_count;
    145     if (!git_patch_get_hunk(&hunk, &line_count, patch, i)) {
    146       GitHunk* hunk = githunk_create(patch, i);
    147       if (hunk) {
    148         delta->hunks[hunks_out_count++] = hunk;
    149       }
    150     }
    151   }
    152   delta->hunks_len = hunks_out_count;
    153 
    154   /* Increment added/deleted line counts. */
    155   for (size_t i = 0; i < delta->hunks_len; i++) {
    156     GitHunk* hunk = delta->hunks[i];
    157     for (size_t j = 0; j < hunk->lines_len; j++) {
    158       GitHunkLine* line = hunk->lines[j];
    159       if (line->old_lineno == -1) {
    160         delta->addcount++;
    161       } else if (line->new_lineno == -1) {
    162         delta->delcount++;
    163       }
    164     }
    165   }
    166   git_patch_free(patch);
    167   return delta;
    168 }
    169 
    170 void gitdelta_free(GitDelta* delta) {
    171   if (!delta) {
    172     return;
    173   }
    174   free(delta->old_file_path);
    175   free(delta->new_file_path);
    176   for (size_t i = 0; i < delta->hunks_len; i++) {
    177     githunk_free(delta->hunks[i]);
    178     delta->hunks[i] = NULL;
    179   }
    180   free(delta->hunks);
    181   delta->hunks = NULL;
    182   free(delta);
    183 }
    184 
    185 static char* gitdelta_graph(const GitDelta* delta,
    186                             size_t length,
    187                             char c,
    188                             size_t max_width) {
    189   assert(delta != NULL);
    190   size_t changed = delta->addcount + delta->delcount;
    191   if (changed > max_width && length > 0) {
    192     length = (length * max_width) / changed;
    193     if (length == 0) {
    194       length = 1;
    195     }
    196   }
    197   char* graph = ecalloc(length + 1, sizeof(char));
    198   memset(graph, c, length);
    199   return graph;
    200 }
    201 
    202 char* gitdelta_added_graph(const GitDelta* delta, size_t max_width) {
    203   assert(delta != NULL);
    204   return gitdelta_graph(delta, delta->addcount, '+', max_width);
    205 }
    206 
    207 char* gitdelta_deleted_graph(const GitDelta* delta, size_t max_width) {
    208   assert(delta != NULL);
    209   return gitdelta_graph(delta, delta->delcount, '-', max_width);
    210 }