From 8ff5e4371117994a7d9061128837f8c73317c527 Mon Sep 17 00:00:00 2001 From: lhchavez Date: Sat, 18 Aug 2018 00:46:30 +0000 Subject: [PATCH 1/3] Add support for CreateCommitFromIds This change adds support for CreateCommitFromIds from libgit2. --- repository.go | 62 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/repository.go b/repository.go index d8de97a..a430751 100644 --- a/repository.go +++ b/repository.go @@ -3,6 +3,8 @@ package git /* #include #include +#include +#include */ import "C" import ( @@ -389,6 +391,66 @@ func (v *Repository) CreateCommit( return oid, nil } +func (v *Repository) CreateCommitFromIds( + refname string, author, committer *Signature, + message string, tree *Oid, parents ...*Oid) (*Oid, error) { + + oid := new(Oid) + + var cref *C.char + if refname == "" { + cref = nil + } else { + cref = C.CString(refname) + defer C.free(unsafe.Pointer(cref)) + } + + cmsg := C.CString(message) + defer C.free(unsafe.Pointer(cmsg)) + + var parentsarg **C.git_oid = nil + + nparents := len(parents) + if nparents > 0 { + parentsarg = (**C.git_oid)(C.malloc(C.size_t(unsafe.Sizeof(uintptr(0)) * uintptr(nparents)))) + defer C.free(unsafe.Pointer(parentsarg)) + parentsptr := uintptr(unsafe.Pointer(parentsarg)) + for _, v := range parents { + *(**C.git_oid)(unsafe.Pointer(parentsptr)) = v.toC() + parentsptr += unsafe.Sizeof(uintptr(0)) + } + } + + authorSig, err := author.toC() + if err != nil { + return nil, err + } + defer C.git_signature_free(authorSig) + + committerSig, err := committer.toC() + if err != nil { + return nil, err + } + defer C.git_signature_free(committerSig) + + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + ret := C.git_commit_create_from_ids( + oid.toC(), v.ptr, cref, + authorSig, committerSig, + nil, cmsg, tree.toC(), C.size_t(nparents), parentsarg) + + runtime.KeepAlive(v) + runtime.KeepAlive(oid) + runtime.KeepAlive(parents) + if ret < 0 { + return nil, MakeGitError(ret) + } + + return oid, nil +} + func (v *Odb) Free() { runtime.SetFinalizer(v, nil) C.git_odb_free(v.ptr) -- 2.45.2 From f505e39c9eac64264d6341cc925c5fa7f67b56cf Mon Sep 17 00:00:00 2001 From: lhchavez Date: Sat, 5 Jan 2019 20:13:01 +0000 Subject: [PATCH 2/3] Add a test and some comments as to the ugly pointer arithmetic --- repository.go | 11 +++++++++-- repository_test.go | 42 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 2 deletions(-) create mode 100644 repository_test.go diff --git a/repository.go b/repository.go index a430751..30673a7 100644 --- a/repository.go +++ b/repository.go @@ -412,12 +412,19 @@ func (v *Repository) CreateCommitFromIds( nparents := len(parents) if nparents > 0 { - parentsarg = (**C.git_oid)(C.malloc(C.size_t(unsafe.Sizeof(uintptr(0)) * uintptr(nparents)))) + // All this awful pointer arithmetic is needed to avoid passing a Go + // pointer to Go pointer into C. Other methods (like CreateCommits) are + // fine without this workaround because they are just passing Go pointers + // to C pointers, but arrays-of-pointers-to-git_oid are a bit special since + // both the array and the objects are allocated from Go. + var emptyOidPtr *C.git_oid + sizeofOidPtr := unsafe.Sizeof(emptyOidPtr) + parentsarg = (**C.git_oid)(C.calloc(C.size_t(uintptr(nparents)), C.size_t(sizeofOidPtr))) defer C.free(unsafe.Pointer(parentsarg)) parentsptr := uintptr(unsafe.Pointer(parentsarg)) for _, v := range parents { *(**C.git_oid)(unsafe.Pointer(parentsptr)) = v.toC() - parentsptr += unsafe.Sizeof(uintptr(0)) + parentsptr += sizeofOidPtr } } diff --git a/repository_test.go b/repository_test.go new file mode 100644 index 0000000..1950c69 --- /dev/null +++ b/repository_test.go @@ -0,0 +1,42 @@ +package git + +import ( + "testing" + "time" +) + +func TestCreateCommitFromIds(t *testing.T) { + t.Parallel() + repo := createTestRepo(t) + defer cleanupTestRepo(t, repo) + + loc, err := time.LoadLocation("Europe/Berlin") + checkFatal(t, err) + sig := &Signature{ + Name: "Rand Om Hacker", + Email: "random@hacker.com", + When: time.Date(2013, 03, 06, 14, 30, 0, 0, loc), + } + + idx, err := repo.Index() + checkFatal(t, err) + err = idx.AddByPath("README") + checkFatal(t, err) + err = idx.Write() + checkFatal(t, err) + treeId, err := idx.WriteTree() + checkFatal(t, err) + + message := "This is a commit\n" + tree, err := repo.LookupTree(treeId) + checkFatal(t, err) + expectedCommitId, err := repo.CreateCommit("HEAD", sig, sig, message, tree) + checkFatal(t, err) + + commitId, err := repo.CreateCommitFromIds("", sig, sig, message, treeId) + checkFatal(t, err) + + if !expectedCommitId.Equal(commitId) { + t.Errorf("mismatched commit ids, expected %v, got %v", expectedCommitId.String(), commitId.String()) + } +} -- 2.45.2 From 35518c78df9ae727651212512bfaa1a8dae02585 Mon Sep 17 00:00:00 2001 From: lhchavez Date: Tue, 8 Jan 2019 02:50:42 +0000 Subject: [PATCH 3/3] Keeping the tree alive --- repository.go | 1 + 1 file changed, 1 insertion(+) diff --git a/repository.go b/repository.go index 30673a7..2a4e9c8 100644 --- a/repository.go +++ b/repository.go @@ -450,6 +450,7 @@ func (v *Repository) CreateCommitFromIds( runtime.KeepAlive(v) runtime.KeepAlive(oid) + runtime.KeepAlive(tree) runtime.KeepAlive(parents) if ret < 0 { return nil, MakeGitError(ret) -- 2.45.2