ctucx.git: nimgit

[nimlang] nim-wrapper for libgit2

commit 300a9f8364b032e7f4a3b6b01a6008428ec3ca17
Author: Leah (ctucx) <leah@ctu.cx>
Date: Tue, 16 Mar 2021 11:34:42 +0100

init
14 files changed, 408 insertions(+), 0 deletions(-)
A
.gitignore
|
4
++++
A
listAllCommits.nim
|
33
+++++++++++++++++++++++++++++++++
A
listAllRefs.nim
|
34
++++++++++++++++++++++++++++++++++
A
nim.cfg
|
1
+
A
nimgit.nim
|
6
++++++
A
nimgit.nimble
|
16
++++++++++++++++
A
nimgit/commit.nim
|
26
++++++++++++++++++++++++++
A
nimgit/free.nim
|
54
++++++++++++++++++++++++++++++++++++++++++++++++++++++
A
nimgit/objects.nim
|
16
++++++++++++++++
A
nimgit/reference.nim
|
69
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A
nimgit/repository.nim
|
20
++++++++++++++++++++
A
nimgit/types.nim
|
91
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A
nimgit/utils.nim
|
9
+++++++++
A
showLastCommit.nim
|
29
+++++++++++++++++++++++++++++
diff --git a/.gitignore b/.gitignore
@@ -0,0 +1,4 @@
+libgit2.so
+listAllCommits
+listAllRefs
+showLastCommit
diff --git a/listAllCommits.nim b/listAllCommits.nim
@@ -0,0 +1,32 @@
+import os, times
+import nimgit
+
+if paramCount() == 0:
+    echo "No git-repo given."
+    quit(QuitFailure)
+
+discard git_libgit2_init()
+
+try:
+    let gitRepository     = openGitRepository(paramStr(1))
+    let gitRevisionWalker = gitRepository.createRevisionWalker("HEAD")
+
+    for gitOid in gitRevisionWalker.walk():
+        let gitCommit = gitRepository.lookupCommit(gitOid)
+
+        let author = gitCommit.getAuthor()
+
+        echo "==================="
+        echo "hash: " & $gitOid
+        echo "author: " & author.name & " <" & author.email & ">"
+        echo "when: " & $author.when.time
+        echo "message: " & gitCommit.getSummary()
+        echo ""
+
+        free(gitCommit)
+
+    free(gitRevisionWalker)
+    free(gitRepository)
+
+except:
+    echo "Error:\n", getCurrentExceptionMsg()+
\ No newline at end of file
diff --git a/listAllRefs.nim b/listAllRefs.nim
@@ -0,0 +1,33 @@
+import os
+import nimgit
+
+if paramCount() == 0:
+    echo "No git-repo given."
+    quit(QuitFailure)
+
+discard git_libgit2_init()
+
+try:
+    let gitRepository = openGitRepository(paramStr(1))
+    let gitRefNames   = gitRepository.getGitReferenceNames()
+
+    for gitRefName in gitRefNames:
+        let gitReference = gitRepository.lookupGitReference(gitRefName)
+
+        case git_reference_type(gitReference):
+            of GIT_REFERENCE_DIRECT:
+                echo gitRefName & ": " & $git_oid_tostr_s(git_reference_target(gitReference))
+
+            of GIT_REFERENCE_SYMBOLIC:
+                echo gitRefName & " => " & $git_reference_symbolic_target(gitReference)
+
+            else:
+                echo "Unexpected reference type"
+                quit(QuitFailure)
+
+        free(gitReference)
+
+    free(gitRepository)
+
+except:
+    echo "Error:\n", getCurrentExceptionMsg()+
\ No newline at end of file
diff --git a/nim.cfg b/nim.cfg
@@ -0,0 +1 @@
+-d:git2Git
diff --git a/nimgit.nim b/nimgit.nim
@@ -0,0 +1,6 @@
+import nimgit2
+import nimgit/[types, free, repository, objects, reference, commit]
+
+export nimgit2
+export types, free, objects, repository, reference, commit
+
diff --git a/nimgit.nimble b/nimgit.nimble
@@ -0,0 +1,16 @@
+# Package
+
+version       = "0.1.0"
+author        = "Leah (ctucx)"
+description   = "libgit2 wrapper for nim"
+license       = "MIT"
+srcDir        = "./"
+installDirs   = @["nimgit"]
+installFiles  = @["nimgit.nim"]
+bin           = @["showLastCommit", "listAllCommits", "listAllFiles", "listAllRefs"]
+
+
+# Dependencies
+
+requires "nim >= 1.4.2"
+requires "nimgit2"
diff --git a/nimgit/commit.nim b/nimgit/commit.nim
@@ -0,0 +1,26 @@
+import nimgit2
+import types, utils
+
+proc lookupCommit* (repo: GitRepository, oid: ptr git_oid): GitCommit =
+    let error = git_commit_lookup(addr result, repo, oid)
+
+    if error != 0:
+        raise newException(CatchableError, "Commit lookup failed: " & $error.getResultCode)
+
+proc getObjectId* (commit: GitCommit): GitObjectId = 
+    result = git_commit_id(commit)
+
+proc getSummary* (commit: GitCommit): string =
+    result = $git_commit_summary(commit)
+
+proc getMessage* (commit: GitCommit): string =
+    result = $git_commit_message(commit)
+
+proc getShortMessage* (commit: GitCommit): string = commit.getSummary()
+
+proc getAuthor* (commit: GitCommit): GitSignature =
+    let author = git_commit_author(commit)
+
+    result.name  = $author.name
+    result.email = $author.email
+    result.when  = parseGitTime(author.when)
diff --git a/nimgit/free.nim b/nimgit/free.nim
@@ -0,0 +1,53 @@
+import nimgit2
+
+type 
+    NimGitTypes = git_clone_options | git_status_options | git_checkout_options |
+                  git_oid | git_diff_options
+
+    GitTypes    = git_repository | git_reference | git_remote | git_tag |
+                  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
+
+
+proc free* [T: NimGitTypes] (point: ptr T) = dealloc(point)
+
+proc free* [T: GitTypes] (point: ptr T) =
+    ## perform a free of a git-managed pointer
+    when T is git_repository:
+        git_repository_free(point)
+    elif T is git_reference:
+        git_reference_free(point)
+    elif T is git_remote:
+        git_remote_free(point)
+    elif T is git_strarray:
+        git_strarray_dispose(point)
+    elif T is git_tag:
+        git_tag_free(point)
+    elif T is git_commit:
+        git_commit_free(point)
+    elif T is git_object:
+        git_object_free(point)
+    elif T is git_tree:
+        git_tree_free(point)
+    elif T is git_tree_entry:
+        git_tree_entry_free(point)
+    elif T is git_revwalk:
+        git_revwalk_free(point)
+    elif T is git_status_list:
+        git_status_list_free(point)
+    elif T is git_annotated_commit:
+        git_annotated_commit_free(point)
+    elif T is git_pathspec:
+        git_pathspec_free(point)
+    elif T is git_pathspec_match_list:
+        git_pathspec_match_list_free(point)
+    elif T is git_diff:
+        git_diff_free(point)
+    elif T is git_buf:
+        git_buf_dispose(point)
+    elif T is git_branch_iterator:
+        git_branch_iterator_free(point)
+    elif T is git_signature:
+        git_signature_free(point)+
\ No newline at end of file
diff --git a/nimgit/objects.nim b/nimgit/objects.nim
@@ -0,0 +1,15 @@
+import nimgit2
+import types
+
+proc initGitObjectId* (): GitObjectId = cast[GitObjectId](sizeof(git_oid).alloc)
+
+proc lookupObject* (repo: GitRepository, name: string): GitObject = 
+    let error = git_revparse_single(addr result, repo, cstring(name))
+
+    if error != 0:
+        raise newException(CatchableError, "Object lookup failed: " & $error.getResultCode)
+
+proc getSha* (obj: GitObjectId): string =
+    result = $git_oid_tostr_s(obj)
+
+proc `$`* (obj: GitObjectId): string = obj.getSha()+
\ No newline at end of file
diff --git a/nimgit/reference.nim b/nimgit/reference.nim
@@ -0,0 +1,69 @@
+import nimgit2
+import types, free, objects
+
+proc getGitReferenceNames* (repo: GitRepository): seq[string] =
+    var gitRefsArr: git_strarray
+
+    let error = git_reference_list(addr gitRefsArr, repo)
+
+    if error != 0:
+        raise newException(CatchableError, "Cannot get reference-names: " & $error.getResultCode)
+
+    result = cstringArrayToSeq(cast[cstringArray](gitRefsArr.strings), gitRefsArr.count)
+    
+    git_strarray_dispose(addr gitRefsArr);
+
+proc lookupGitReference* (repo: GitRepository, refName: string): GitReference =     
+    let error = git_reference_lookup(addr result, repo, cstring(refName))
+
+    if error != 0:
+        raise newException(CatchableError, "Lookup failed: " & $error.getResultCode)
+
+proc createRevisionWalker* (repo: GitRepository, sort: git_sort_t = GIT_SORT_TOPOLOGICAL): GitRevisionWalker =
+    var error: cint
+
+    error = git_revwalk_new(addr result, repo)
+
+    if error != 0:
+        raise newException(CatchableError, "Cannot create RevWalker: " & $error.getResultCode)
+
+    error = git_revwalk_sorting(result, cast[cuint](sort))
+
+    if error != 0:
+        raise newException(CatchableError, "Cannot set sorting: " & $error.getResultCode)
+
+proc createRevisionWalker* (repo: GitRepository, reference: string, sort: git_sort_t = GIT_SORT_TOPOLOGICAL): GitRevisionWalker =
+    var error: cint
+    var gitWalker = repo.createRevisionWalker(sort)
+
+    error = git_revwalk_push_ref(gitWalker, "HEAD")
+
+    if error != 0:
+        raise newException(CatchableError, "Cannot push: " & $error.getResultCode)
+
+    result = gitWalker
+
+proc next* (walker: GitRevisionWalker): GitObjectId =
+    var error: cint
+    var objectId = initGitObjectId()
+
+    error = git_revwalk_next(objectId, walker)
+
+    if error != 0:
+        free(objectId)
+        raise newException(CatchableError, "Cannot get next: " & $error.getResultCode)
+
+    result = objectId
+
+
+
+iterator walk* (walker: GitRevisionWalker): GitObjectId =
+    block:
+        try:
+            while true:
+                yield walker.next()
+        except:
+            if getCurrentExceptionMsg() == "Cannot get next: end of iteration":
+                break
+            else:
+                raise newException(CatchableError, getCurrentExceptionMsg())
diff --git a/nimgit/repository.nim b/nimgit/repository.nim
@@ -0,0 +1,19 @@
+import nimgit2
+import types
+
+proc openGitRepository* (path: string): GitRepository =
+    let error = git_repository_open(addr result, path)
+
+    if error != 0:
+        raise newException(CatchableError, "Cannot open repository: " & $error.getResultCode)
+
+proc getHead* (repo: GitRepository): GitReference = 
+    let error = git_repository_head(addr result, repo)
+
+    if error != 0:
+        raise newException(CatchableError, "Cannot get HEAD: " & $error.getResultCode)
+
+proc getPath* (repo: GitRepository): string =
+    return $git_repository_path(repo)
+
+proc `$`* (repo: GitRepository): string = repo.getPath()+
\ No newline at end of file
diff --git a/nimgit/types.nim b/nimgit/types.nim
@@ -0,0 +1,90 @@
+import times
+import nimgit2
+
+type
+    GitRepository*      = ptr git_repository
+    GitObject*          = ptr git_object
+    GitObjectId*        = ptr git_oid
+    GitCommit*          = ptr git_commit
+    GitReference*       = ptr git_reference
+    GitRevisionWalker* = ptr git_revwalk
+
+    GitTime* = object
+        time*     : Time
+        offset*   : int
+        isSigned* : bool
+
+    GitSignature* = object
+        name*   : string
+        email*  : string
+        `when`* : GitTime
+
+    GitReturnCode* = enum
+        grcApplyFail       = (GIT_EAPPLYFAIL, "patch failed")
+        grcIndexDirty      = (GIT_EINDEXDIRTY, "dirty index")
+        grcMismatch        = (GIT_EMISMATCH, "hash mismatch")
+        grcRetry           = (GIT_RETRY, "retry")
+        grcIterOver        = (GIT_ITEROVER, "end of iteration")
+        grcPassThrough     = (GIT_PASSTHROUGH, "pass-through")
+        grcMergeConflict   = (GIT_EMERGE_CONFLICT, "merge conflict")
+        grcDirectory       = (GIT_EDIRECTORY, "directory")
+        grcUncommitted     = (GIT_EUNCOMMITTED, "uncommitted")
+        grcInvalid         = (GIT_EINVALID, "invalid")
+        grcEndOfFile       = (GIT_EEOF, "end-of-file")
+        grcPeel            = (GIT_EPEEL, "peel")
+        grcApplied         = (GIT_EAPPLIED, "applied")
+        grcCertificate     = (GIT_ECERTIFICATE, "certificate")
+        grcAuthentication  = (GIT_EAUTH, "authentication")
+        grcModified        = (GIT_EMODIFIED, "modified")
+        grcLocked          = (GIT_ELOCKED, "locked")
+        grcConflict        = (GIT_ECONFLICT, "conflict")
+        grcInvalidSpec     = (GIT_EINVALIDSPEC, "invalid spec")
+        grcNonFastForward  = (GIT_ENONFASTFORWARD, "not fast-forward")
+        grcUnmerged        = (GIT_EUNMERGED, "unmerged")
+        grcUnbornBranch    = (GIT_EUNBORNBRANCH, "unborn branch")
+        grcBareRepo        = (GIT_EBAREREPO, "bare repository")
+        grcUser            = (GIT_EUSER, "user-specified")
+        grcBuffer          = (GIT_EBUFS, "buffer overflow")
+        grcAmbiguous       = (GIT_EAMBIGUOUS, "ambiguous match")
+        grcExists          = (GIT_EEXISTS, "object exists")
+        grcNotFound        = (GIT_ENOTFOUND, "not found")
+        grcError           = (GIT_ERROR, "generic error")
+        grcOk              = (GIT_OK, "ok")
+
+    GitErrorClass* = enum
+        gecNone        = (GIT_ERROR_NONE, "none")
+        gecNoMemory    = (GIT_ERROR_NOMEMORY, "no memory")
+        gecOS          = (GIT_ERROR_OS, "os")
+        gecInvalid     = (GIT_ERROR_INVALID, "invalid")
+        gecReference   = (GIT_ERROR_REFERENCE, "reference")
+        gecZlib        = (GIT_ERROR_ZLIB, "zlib")
+        gecRepository  = (GIT_ERROR_REPOSITORY, "repository")
+        gecConfig      = (GIT_ERROR_CONFIG, "config")
+        gecRegEx       = (GIT_ERROR_REGEX, "regex")
+        gecODB         = (GIT_ERROR_ODB, "odb")
+        gecIndex       = (GIT_ERROR_INDEX, "index")
+        gecObject      = (GIT_ERROR_OBJECT, "object")
+        gecNet         = (GIT_ERROR_NET, "network")
+        gecTag         = (GIT_ERROR_TAG, "tag")
+        gecTree        = (GIT_ERROR_TREE, "tree")
+        gecIndexer     = (GIT_ERROR_INDEXER, "indexer")
+        gecSSL         = (GIT_ERROR_SSL, "ssl")
+        gecSubModule   = (GIT_ERROR_SUBMODULE, "submodule")
+        gecThread      = (GIT_ERROR_THREAD, "thread")
+        gecStash       = (GIT_ERROR_STASH, "stash")
+        gecCheckOut    = (GIT_ERROR_CHECKOUT, "check out")
+        gecFetchHead   = (GIT_ERROR_FETCHHEAD, "fetch head")
+        gecMerge       = (GIT_ERROR_MERGE, "merge")
+        gecSSH         = (GIT_ERROR_SSH, "ssh")
+        gecFilter      = (GIT_ERROR_FILTER, "filter")
+        gecRevert      = (GIT_ERROR_REVERT, "revert")
+        gecCallBack    = (GIT_ERROR_CALLBACK, "call back")
+        gecCherryPick  = (GIT_ERROR_CHERRYPICK, "cherry pick")
+        gecDescribe    = (GIT_ERROR_DESCRIBE, "describe")
+        gecReBase      = (GIT_ERROR_REBASE, "re-base")
+        gecFileSystem  = (GIT_ERROR_FILESYSTEM, "filesystem")
+        gecPatch       = (GIT_ERROR_PATCH, "patch")
+        gecWorkTree    = (GIT_ERROR_WORKTREE, "work tree")
+        gecSHA1        = (GIT_ERROR_SHA1, "sha1")
+
+template getResultCode* (code: cint): GitReturnCode = cast[GitReturnCode](code.ord)+
\ No newline at end of file
diff --git a/nimgit/utils.nim b/nimgit/utils.nim
@@ -0,0 +1,8 @@
+import times
+import nimgit2
+import types
+
+proc parseGitTime* (time: git_time): GitTime =
+    result.time     = fromUnix(time.time)
+    result.offset   = time.offset
+    result.isSigned = (time.sign == '+')+
\ No newline at end of file
diff --git a/showLastCommit.nim b/showLastCommit.nim
@@ -0,0 +1,28 @@
+import os, times
+import nimgit
+
+if paramCount() == 0:
+    echo "No git-repo given."
+    quit(QuitFailure)
+
+discard git_libgit2_init()
+
+try:
+    let gitRepository = openGitRepository(paramStr(1))
+    let headCommit    = cast[GitCommit](gitRepository.lookupObject("HEAD^{commit}"))
+
+    echo "Last commit on HEAD in repo: " & $gitRepository
+
+    let commit = cast[GitCommit](headCommit)
+    let author = commit.getAuthor()
+
+    echo "hash: " & $commit.getObjectId()
+    echo "author: " & author.name & " <" & author.email & ">"
+    echo "when: " & $author.when.time
+    echo "message: " & commit.getSummary()
+
+    free(commit);
+    free(gitRepository)
+
+except:
+    echo "Error:\n", getCurrentExceptionMsg()+
\ No newline at end of file