login

<     >

2022-02-26 23:58:51 (UTC-03:00)

Marcel Rodrigues <marcelgmr@gmail.com>

add commit diff

diff --git a/app.lua b/app.lua
index f242f78..7e40b4e 100644
--- a/app.lua
+++ b/app.lua
@@ -63,9 +63,10 @@ local routes = {
         local prev = repo:find_prev(commit:id(), 1)
         local sig = commit:signature()
         local time_str = time_fmt(sig)
+        local diff = repo:diff(commit)
         local env = {
             gname=gname, rname=rname, bname=bname, commit=commit,
-            time_str=time_str, sig=sig, cid=cid, prev=prev,
+            time_str=time_str, sig=sig, cid=cid, prev=prev, diff=diff,
         }
         return lud.template.render_file("view/commit.html", env)
     end},

diff --git a/git.lua b/git.lua
index abb0652..8a8ebe6 100644
--- a/git.lua
+++ b/git.lua
@@ -1,7 +1,9 @@
 local ffi = require "ffi"
 
 ffi.cdef[[
+typedef int64_t git_off_t;
 typedef int64_t git_time_t;
+typedef uint64_t git_object_size_t;
 typedef struct git_repository git_repository;
 typedef struct git_object git_object;
 typedef struct git_commit git_commit;
@@ -40,6 +42,61 @@ typedef struct git_signature {
 	git_time when; /**< time when the action happened */
 } git_signature;
 typedef struct git_diff_options git_diff_options;
+typedef enum {
+	GIT_DIFF_FORMAT_PATCH        = 1u, /**< full git diff */
+	GIT_DIFF_FORMAT_PATCH_HEADER = 2u, /**< just the file headers of patch */
+	GIT_DIFF_FORMAT_RAW          = 3u, /**< like git diff --raw */
+	GIT_DIFF_FORMAT_NAME_ONLY    = 4u, /**< like git diff --name-only */
+	GIT_DIFF_FORMAT_NAME_STATUS  = 5u, /**< like git diff --name-status */
+	GIT_DIFF_FORMAT_PATCH_ID     = 6u  /**< git diff as used by git patch-id */
+} git_diff_format_t;
+typedef enum {
+	GIT_DELTA_UNMODIFIED = 0,  /**< no changes */
+	GIT_DELTA_ADDED = 1,	   /**< entry does not exist in old version */
+	GIT_DELTA_DELETED = 2,	   /**< entry does not exist in new version */
+	GIT_DELTA_MODIFIED = 3,    /**< entry content changed between old and new */
+	GIT_DELTA_RENAMED = 4,     /**< entry was renamed between old and new */
+	GIT_DELTA_COPIED = 5,      /**< entry was copied from another old entry */
+	GIT_DELTA_IGNORED = 6,     /**< entry is ignored item in workdir */
+	GIT_DELTA_UNTRACKED = 7,   /**< entry is untracked item in workdir */
+	GIT_DELTA_TYPECHANGE = 8,  /**< type of entry changed between old and new */
+	GIT_DELTA_UNREADABLE = 9,  /**< entry is unreadable */
+	GIT_DELTA_CONFLICTED = 10  /**< entry in the index is conflicted */
+} git_delta_t;
+typedef struct {
+	git_oid            id;
+	const char        *path;
+	git_object_size_t  size;
+	uint32_t           flags;
+	uint16_t           mode;
+	uint16_t           id_abbrev;
+} git_diff_file;
+typedef struct {
+	git_delta_t   status;
+	uint32_t      flags;	   /**< git_diff_flag_t values */
+	uint16_t      similarity;  /**< for RENAMED and COPIED, value 0-100 */
+	uint16_t      nfiles;	   /**< number of files in this delta */
+	git_diff_file old_file;
+	git_diff_file new_file;
+} git_diff_delta;
+typedef struct {
+	int    old_start;     /**< Starting line number in old_file */
+	int    old_lines;     /**< Number of lines in old_file */
+	int    new_start;     /**< Starting line number in new_file */
+	int    new_lines;     /**< Number of lines in new_file */
+	size_t header_len;    /**< Number of bytes in header text */
+	char   header[128];   /**< Header text, NUL-byte terminated */
+} git_diff_hunk;
+typedef struct {
+	char   origin;       /**< A git_diff_line_t value */
+	int    old_lineno;   /**< Line number in old file or -1 for added line */
+	int    new_lineno;   /**< Line number in new file or -1 for deleted line */
+	int    num_lines;    /**< Number of newline characters in content */
+	size_t content_len;  /**< Number of bytes of data */
+	git_off_t content_offset; /**< Offset in the original file to the content */
+	const char *content; /**< Pointer to diff text, not NUL-byte terminated */
+} git_diff_line;
+typedef int (git_diff_line_cb)(const git_diff_delta *delta, const git_diff_hunk *hunk, const git_diff_line *line, void *payload);
 
 int git_libgit2_init();
 int git_libgit2_shutdown();
@@ -78,6 +135,7 @@ const git_tree_entry * git_tree_entry_byindex(const git_tree *tree, size_t idx);
 git_object_t git_tree_entry_type(const git_tree_entry *entry);
 
 int git_diff_tree_to_tree(git_diff **diff, git_repository *repo, git_tree *old_tree, git_tree *new_tree, const git_diff_options *opts);
+int git_diff_print(git_diff *diff, git_diff_format_t format, git_diff_line_cb print_cb, void *payload);
 ]]
 local C = ffi.load("git2")
 
@@ -133,6 +191,16 @@ function Commit:parent(n)
     end
 end
 
+function Commit:tree()
+    local ptree = ffi.new("git_tree *[1]")
+    local ret = C.git_commit_tree(ptree, self.commit)
+    if ret == 0 then
+        return ptree[0]
+    else
+        return nil
+    end
+end
+
 local Repo = {}
 Repo.__index = Repo
 
@@ -199,6 +267,23 @@ function Repo:find_prev(cid, dist)
     return nil
 end
 
+function Repo:diff(commit)
+    local new_tree = commit:tree()
+    local parent = commit:parent()
+    local old_tree = parent and parent:tree()
+    local pdiff = ffi.new("git_diff *[1]")
+    C.git_diff_tree_to_tree(pdiff, self.repo, old_tree, new_tree, nil)
+    local diff = pdiff[0]
+    local s = ""
+    local function cb(delta, hunk, line, payload)
+        local lua_line = ffi.string(line.content, line.content_len)
+        s = s .. string.char(line.origin) .. lua_line
+        return 0
+    end
+    C.git_diff_print(diff, C.GIT_DIFF_FORMAT_PATCH, cb, nil)
+    return s
+end
+
 local function open(path)
     local self = setmetatable({}, Repo)
     local prepo = ffi.new("git_repository *[1]")

diff --git a/view/commit.html b/view/commit.html
index 5e84bc3..03028d8 100644
--- a/view/commit.html
+++ b/view/commit.html
@@ -18,6 +18,8 @@
   <p>{{$time_str}}</p>
   <p>{{$sig.name}} &lt;{{$sig.email}}&gt;</p>
   <pre>{{$commit:message()}}</pre>
+  <hr>
+  <pre>{{$diff}}</pre>
   <br/>
   % if $prev then
   <a href="/group/{{$gname}}/repo/{{$rname}}/commit/{{$prev:id()}}">&lt</a>