ctucx.git: nimgit

[nimlang] nim-wrapper for libgit2

commit ca7f01e88a829023e92d047d7a4537dea2c13a28
parent 0c4ecd37247855fd7e3402cd17d3ec955970684f
Author: Leah (ctucx) <leah@ctu.cx>
Date: Tue, 23 Mar 2021 20:13:56 +0100

add diff-procs
5 files changed, 170 insertions(+), 4 deletions(-)
M
nimgit.nim
|
4
++--
A
nimgit/diff.nim
|
109
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
M
nimgit/free.nim
|
5
++++-
M
nimgit/types.nim
|
12
++++++++++++
M
showLastCommit.nim
|
44
+++++++++++++++++++++++++++++++++++++++++++-
diff --git a/nimgit.nim b/nimgit.nim
@@ -1,5 +1,5 @@
 import nimgit2
-import nimgit/[types, free, repository, config, objects, oid, tag, blob, tree, treeEntry, reference, revisionWalker, branch, commit]
+import nimgit/[types, free, repository, config, objects, oid, tag, blob, tree, treeEntry, reference, revisionWalker, branch, commit, diff]
 
 export nimgit2
-export types, free, repository, config, objects, oid, tag, blob, tree, treeEntry, reference, revisionWalker, branch, commit
+export types, free, repository, config, objects, oid, tag, blob, tree, treeEntry, reference, revisionWalker, branch, commit, diff
diff --git a/nimgit/diff.nim b/nimgit/diff.nim
@@ -0,0 +1,108 @@
+import nimgit2
+import types, free
+
+proc initDiffOptions* (): GitDiffOptions =
+    result = cast[GitDiffOptions](sizeof(git_diff_options).alloc)
+    discard git_diff_init_options(result, GIT_DIFF_OPTIONS_VERSION)
+
+proc GitDiffFindOptions* (): GitDiffFindOptions =
+    result = cast[GitDiffFindOptions](sizeof(git_diff_find_options).alloc)
+    discard git_diff_find_init_options(result, GIT_DIFF_FIND_OPTIONS_VERSION)
+
+
+proc diffTrees* (repo: GitRepository, newTree: GitTree, oldTree: GitTree, options: GitDiffOptions): GitDiff =
+    let error = git_diff_tree_to_tree(addr result, repo, newTree, oldTree, options)
+
+    if error != 0:
+        free(result)
+        raise newException(CatchableError, "Cannot diff Trees: " & $error.getResultCode)
+
+proc findSimilar* (diff: GitDiff, options: GitDiffFindOptions) =
+    let error = git_diff_find_similar(diff, options)
+
+    if error != 0:
+        raise newException(CatchableError, "Cannot diff find: " & $error.getResultCode)
+
+proc stats* (diff: GitDiff): GitDiffStats =
+    var diffStats : ptr git_diff_stats
+    let error     = git_diff_get_stats(addr diffStats, diff)
+
+    if error != 0:
+        raise newException(CatchableError, "Cannot get diff-stats: " & $error.getResultCode)
+
+    result.filesChanged = cast[int](git_diff_stats_files_changed(diffStats))
+    result.deletions    = cast[int](git_diff_stats_deletions(diffStats))
+    result.insertions   = cast[int](git_diff_stats_insertions(diffStats))
+
+    git_diff_stats_free(diffStats)
+
+proc len* (delta: GitDiff): int = cast[int](git_diff_num_deltas(delta))
+
+proc delta* (delta: GitDiff, id: int): GitDiffDelta = git_diff_get_delta(delta, uint(id))
+
+iterator deltas* (diff: GitDiff): (int, GitDiffDelta) =
+    var counter : int
+    let diffLen = diff.len
+
+    while counter < diffLen:
+        yield (counter, diff.delta(counter))
+        inc(counter)
+
+proc statusChar* (delta: GitDiffDelta): string =
+    case delta.status:
+        of GIT_DELTA_ADDED:
+            result = "A"
+        of GIT_DELTA_COPIED:
+            result = "C"
+        of GIT_DELTA_DELETED:
+            result = "D"
+        of GIT_DELTA_MODIFIED:
+            result = "M"
+        of GIT_DELTA_RENAMED:
+            result = "R"
+        of GIT_DELTA_TYPECHANGE:
+            result = "T"
+        else:
+            result = " "
+
+
+proc patch* (diff: GitDiff, id: int): GitPatch =
+    let error = git_patch_from_diff(addr result, diff, uint(id))
+
+    if error != 0:
+        raise newException(CatchableError, "Cannot get patch: " & $error.getResultCode)
+
+
+proc hunksLen* (patch: GitPatch): int = cast[int](git_patch_num_hunks(patch))
+
+proc hunk* (patch: GitPatch, id: int): GitDiffHunk =
+    var linesCount : uint
+    let error      = git_patch_get_hunk(addr result, addr linesCount, patch, uint(id))
+
+    if error != 0:
+        raise newException(CatchableError, "Cannot get diff-hunk: " & $error.getResultCode)
+
+iterator hunks* (patch: GitPatch): (int, GitDiffHunk) =
+    var counter : int
+    let hunkLen = patch.hunksLen
+
+    while counter < hunkLen:
+        yield (counter, patch.hunk(counter))
+        inc(counter)
+
+
+proc linesLen* (patch: GitPatch, id: int): int = cast[int](git_patch_num_lines_in_hunk(patch, uint(id)))
+
+proc line* (patch: GitPatch, hunk: int, line: int): GitDiffLine =
+    let error = git_patch_get_line_in_hunk(addr result, patch, uint(hunk), uint(line))
+
+    if error != 0:
+        raise newException(CatchableError, "Cannot get diff-line: " & $error.getResultCode)
+
+iterator lines* (patch: GitPatch, hunkId: int): (int, GitDiffLine) =
+    var counter  : int
+    let lineLen = patch.linesLen(hunkId)
+
+    while counter < lineLen:
+        yield (counter, patch.line(hunkId, counter))
+        inc(counter)+
\ No newline at end of file
diff --git a/nimgit/free.nim b/nimgit/free.nim
@@ -8,7 +8,7 @@ type
                   git_strarray | git_object | git_commit | git_status_list |
                   git_annotated_commit | git_tree_entry | git_revwalk | git_buf |
                   git_pathspec | git_tree | git_diff | git_pathspec_match_list |
-                  git_branch_iterator | git_signature | git_blob
+                  git_branch_iterator | git_signature | git_blob | git_patch
 
 
 proc free* [T: NimGitTypes] (point: ptr T) = dealloc(point)

@@ -55,3 +55,5 @@ proc free* [T: GitTypes] (point: ptr T) =
         git_signature_free(point)
     elif T is git_blob:
         git_blob_free(point)
+    elif T is git_patch:
+        git_patch_free(point)+
\ No newline at end of file
diff --git a/nimgit/types.nim b/nimgit/types.nim
@@ -16,6 +16,18 @@ type
     GitBuffer*          = ptr git_buf
     GitTag*             = ptr git_tag
     GitBlob*            = ptr git_blob
+    GitDiff*            = ptr git_diff
+    GitDiffOptions*     = ptr git_diff_options
+    GitDiffFindOptions* = ptr git_diff_find_options
+    GitDiffDelta*       = ptr git_diff_delta
+    GitPatch*           = ptr git_patch
+    GitDiffHunk*        = ptr git_diff_hunk
+    GitDiffLine*        = ptr git_diff_line
+
+    GitDiffStats* = object
+        filesChanged* : int
+        insertions*   : int
+        deletions*    : int
 
     GitObjectKind* = enum
         # we have to add 2 here to satisfy nim; discriminants.low must be zero
diff --git a/showLastCommit.nim b/showLastCommit.nim
@@ -1,4 +1,4 @@
-import os, times
+import os, times, bitops
 import nimgit
 
 if paramCount() == 0:

@@ -38,6 +38,48 @@ try:
 
     echo ""
 
+    if commit.hasParents:
+        let parent     = gitRepository.lookupCommit(commit.parentIds[0])
+        let parentTree = gitRepository.lookupTree(parent.treeId)
+
+        let diffopts   = initDiffOptions()
+        diffopts.flags = cast[uint32](GIT_DIFF_DISABLE_PATHSPEC_MATCH) or cast[uint32](GIT_DIFF_IGNORE_SUBMODULES) or cast[uint32](GIT_DIFF_INCLUDE_TYPECHANGE)
+
+        let findopts   = GitDiffFindOptions()
+        findopts.flags = cast[uint32](GIT_DIFF_FIND_RENAMES) or cast[uint32](GIT_DIFF_FIND_COPIES) or cast[uint32](GIT_DIFF_FIND_EXACT_MATCH_ONLY)
+
+        let diff       = gitRepository.diffTrees(tree, parentTree, diffopts)
+        diff.findSimilar(findopts)
+
+        free(parentTree)
+        free(parent)
+
+        echo diff.len
+        echo diff.stats
+
+        for deltaIndex, delta in diff.deltas:
+            echo delta.statusChar & " " & $delta.old_file.path & " " & $delta.new_file.path
+
+            let patch = diff.patch(deltaIndex)
+
+            for hunkIndex, hunk in patch.hunks():
+
+                echo $hunk.header
+
+                for lineIndex, line in patch.lines(hunkIndex):
+                    var status: string
+
+                    if line.old_lineno == -1:
+                        status = "+"
+                    elif line.new_lineno == -1:
+                        status = "-"
+                    else:
+                        status = " "
+
+                    echo status & $line.content
+
+            free(patch)
+
     free(tree)
     free(commit)
     free(config)