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}} <{{$sig.email}}></p> <pre>{{$commit:message()}}</pre> + <hr> + <pre>{{$diff}}</pre> <br/> % if $prev then <a href="/group/{{$gname}}/repo/{{$rname}}/commit/{{$prev:id()}}"><</a>