gitout

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

commit.c (8211B)


      1 #include "writer/gopher/commit.h"
      2 
      3 #include <err.h>
      4 #include <limits.h>
      5 #include <stdbool.h>
      6 #include <stdint.h>
      7 #include <stdio.h>
      8 #include <stdlib.h>
      9 #include <string.h>
     10 
     11 #include "format.h"
     12 #include "git/delta.h"
     13 #include "utils.h"
     14 #include "writer/gopher/page.h"
     15 
     16 struct GopherCommit {
     17   FILE* out;
     18   GopherPage* page;
     19 };
     20 
     21 static void gopher_commit_write_summary(GopherCommit* commit,
     22                                         const GitCommit* git_commit);
     23 static void gopher_commit_write_diffstat(GopherCommit* commit,
     24                                          const GitCommit* git_commit);
     25 static void gopher_commit_write_diffstat_row(GopherCommit* commit,
     26                                              const GitDelta* delta);
     27 static void gopher_commit_write_diff_content(GopherCommit* commit,
     28                                              const GitCommit* git_commit);
     29 static void gopher_commit_write_diff_delta(GopherCommit* commit,
     30                                            const GitDelta* delta);
     31 static void gopher_commit_write_diff_hunk(GopherCommit* commit,
     32                                           const GitHunk* hunk);
     33 
     34 GopherCommit* gopher_commit_create(const GitRepo* repo,
     35                                    const char* oid,
     36                                    const char* title) {
     37   GopherCommit* commit = ecalloc(1, sizeof(GopherCommit));
     38   char filename[PATH_MAX];
     39   if (snprintf(filename, sizeof(filename), "%s.gph", oid) < 0) {
     40     err(1, "snprintf");
     41   }
     42   char path[PATH_MAX];
     43   path_concat(path, sizeof(path), "commit", filename);
     44   commit->out = efopen(path, "w");
     45   commit->page = gopher_page_create(commit->out, repo, title, "");
     46   return commit;
     47 }
     48 
     49 void gopher_commit_free(GopherCommit* commit) {
     50   if (!commit) {
     51     return;
     52   }
     53   fclose(commit->out);
     54   commit->out = NULL;
     55   gopher_page_free(commit->page);
     56   commit->page = NULL;
     57   free(commit);
     58 }
     59 
     60 void gopher_commit_begin(GopherCommit* commit) {
     61   gopher_page_begin(commit->page);
     62 }
     63 
     64 void gopher_commit_add_commit(GopherCommit* commit,
     65                               const GitCommit* git_commit) {
     66   static const uint64_t kDiffMaxFiles = 1000;
     67   static const uint64_t kDiffMaxDeltas = 1000;
     68   static const uint64_t kDiffMaxDeltaLines = 100000;
     69   FILE* out = commit->out;
     70 
     71   gopher_commit_write_summary(commit, git_commit);
     72 
     73   size_t deltas_len = gitcommit_delta_count(git_commit);
     74   if (deltas_len == 0) {
     75     return;
     76   }
     77   size_t addcount = gitcommit_addcount(git_commit);
     78   size_t delcount = gitcommit_delcount(git_commit);
     79   size_t filecount = gitcommit_filecount(git_commit);
     80   if (filecount > kDiffMaxFiles || deltas_len > kDiffMaxDeltas ||
     81       addcount > kDiffMaxDeltaLines || delcount > kDiffMaxDeltaLines) {
     82     fprintf(out, "Diff is too large, output suppressed.</pre>\n");
     83     return;
     84   }
     85 
     86   gopher_commit_write_diffstat(commit, git_commit);
     87   gopher_commit_write_diff_content(commit, git_commit);
     88 }
     89 
     90 void gopher_commit_end(GopherCommit* commit) {
     91   gopher_page_end(commit->page);
     92 }
     93 
     94 void gopher_commit_write_summary(GopherCommit* commit,
     95                                  const GitCommit* git_commit) {
     96   FILE* out = commit->out;
     97   const char* oid = gitcommit_oid(git_commit);
     98   fprintf(out, "[1|commit %s|../commit/%s.gph|server|port]\n", oid, oid);
     99 
    100   const char* parentoid = gitcommit_parentoid(git_commit);
    101   if (parentoid[0] != '\0') {
    102     fprintf(out, "[1|parent %s|../commit/%s.gph|server|port]\n", parentoid,
    103             parentoid);
    104   }
    105 
    106   fprintf(out, "[h|Author: ");
    107   print_gopher_link(commit->out, gitcommit_author_name(git_commit));
    108   fprintf(out, " <");
    109   print_gopher_link(commit->out, gitcommit_author_email(git_commit));
    110   fprintf(out, ">|URL:mailto:");
    111   print_gopher_link(commit->out, gitcommit_author_email(git_commit));
    112   fprintf(out, "|server|port]\n");
    113   fprintf(out, "Date:   ");
    114   print_time(out, gitcommit_author_time(git_commit),
    115              gitcommit_author_timezone_offset(git_commit));
    116   fprintf(out, "\n");
    117 
    118   const char* message = gitcommit_message(git_commit);
    119   if (message) {
    120     fprintf(out, "\n");
    121     print_gopher_text(out, message, true);
    122     fprintf(out, "\n");
    123   }
    124 }
    125 
    126 void gopher_commit_write_diffstat(GopherCommit* commit,
    127                                   const GitCommit* git_commit) {
    128   fprintf(commit->out, "Diffstat:\n");
    129   size_t delta_count = gitcommit_delta_count(git_commit);
    130   for (size_t i = 0; i < delta_count; i++) {
    131     gopher_commit_write_diffstat_row(commit, gitcommit_delta(git_commit, i));
    132   }
    133 }
    134 
    135 void gopher_commit_write_diffstat_row(GopherCommit* commit,
    136                                       const GitDelta* delta) {
    137   static const size_t kGraphWidth = 30;
    138   FILE* out = commit->out;
    139 
    140   fprintf(out, "  %c ", gitdelta_status(delta));
    141   char filename[PATH_MAX];
    142   const char* old_file_path = gitdelta_old_file_path(delta);
    143   const char* new_file_path = gitdelta_new_file_path(delta);
    144   if (strcmp(old_file_path, new_file_path) == 0) {
    145     snprintf(filename, sizeof(filename), "%s", old_file_path);
    146   } else {
    147     snprintf(filename, sizeof(filename), "%s -> %s", old_file_path,
    148              new_file_path);
    149   }
    150   print_gopher_link_padded(out, filename, 35, ' ');
    151 
    152   size_t changed = gitdelta_lines_added(delta) + gitdelta_lines_deleted(delta);
    153   fprintf(out, " | ");
    154   fprintf(out, "%7zu ", changed);
    155   char* added_graph = gitdelta_added_graph(delta, kGraphWidth);
    156   fprintf(out, "%s", added_graph);
    157   free(added_graph);
    158   char* deleted_graph = gitdelta_deleted_graph(delta, kGraphWidth);
    159   fprintf(out, "%s", deleted_graph);
    160   free(deleted_graph);
    161   fprintf(out, "\n");
    162 }
    163 
    164 void gopher_commit_write_diff_content(GopherCommit* commit,
    165                                       const GitCommit* git_commit) {
    166   FILE* out = commit->out;
    167   size_t addcount = gitcommit_addcount(git_commit);
    168   size_t delcount = gitcommit_delcount(git_commit);
    169   size_t filecount = gitcommit_filecount(git_commit);
    170   fprintf(out, "\n%zu file%s changed, %zu insertion%s(+), %zu deletion%s(-)\n",
    171           filecount, filecount == 1 ? "" : "s",  //
    172           addcount, addcount == 1 ? "" : "s",    //
    173           delcount, delcount == 1 ? "" : "s");
    174   fprintf(commit->out, "---\n");
    175 
    176   size_t delta_count = gitcommit_delta_count(git_commit);
    177   for (size_t i = 0; i < delta_count; i++) {
    178     gopher_commit_write_diff_delta(commit, gitcommit_delta(git_commit, i));
    179   }
    180 }
    181 
    182 void gopher_commit_write_diff_delta(GopherCommit* commit,
    183                                     const GitDelta* delta) {
    184   FILE* out = commit->out;
    185   fprintf(out, "[1|diff --git a/");
    186   print_gopher_link(out, gitdelta_old_file_path(delta));
    187   fprintf(out, " b/");
    188   print_gopher_link(out, gitdelta_new_file_path(delta));
    189   fprintf(out, "|../file/");
    190   print_gopher_link(out, gitdelta_new_file_path(delta));
    191   fprintf(out, ".gph|server|port]\n");
    192 
    193   if (gitdelta_is_binary(delta)) {
    194     fprintf(out, "Binary files differ.\n");
    195   } else {
    196     size_t hunk_count = gitdelta_hunk_count(delta);
    197     for (size_t i = 0; i < hunk_count; i++) {
    198       gopher_commit_write_diff_hunk(commit, gitdelta_hunk(delta, i));
    199     }
    200   }
    201 }
    202 
    203 void gopher_commit_write_diff_hunk(GopherCommit* commit, const GitHunk* hunk) {
    204   FILE* out = commit->out;
    205 
    206   // Output header. e.g. @@ -0,0 +1,3 @@
    207   const char* header = githunk_header(hunk);
    208   print_gopher_text(out, header, false);
    209   fprintf(out, "\n");
    210 
    211   // Iterate over lines in hunk.
    212   size_t line_count = githunk_line_count(hunk);
    213   for (size_t i = 0; i < line_count; i++) {
    214     const GitHunkLine* line = githunk_line(hunk, i);
    215 
    216     const char* content = githunkline_content(line);
    217     size_t content_len = githunkline_content_len(line);
    218     int old_lineno = githunkline_old_lineno(line);
    219     int new_lineno = githunkline_new_lineno(line);
    220     if (old_lineno == -1) {
    221       // Added line. Prefix with +.
    222       fprintf(out, "+");
    223       print_gopher_text_len(out, content, content_len, false);
    224       fprintf(out, "\n");
    225     } else if (new_lineno == -1) {
    226       // Removed line. Prefix with -.
    227       fprintf(out, "-");
    228       print_gopher_text_len(out, content, content_len, false);
    229       fprintf(out, "\n");
    230     } else {
    231       // Unchanged line. Prefix with ' '. No link.
    232       fprintf(out, " ");
    233       print_gopher_text_len(out, content, content_len, false);
    234       fprintf(out, "\n");
    235     }
    236   }
    237 }