summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--repository.go70
-rw-r--r--repository_test.go42
2 files changed, 112 insertions, 0 deletions
diff --git a/repository.go b/repository.go
index 74924b7..3e0d20f 100644
--- a/repository.go
+++ b/repository.go
@@ -3,6 +3,8 @@ package git
/*
#include <git2.h>
#include <git2/sys/repository.h>
+#include <git2/sys/commit.h>
+#include <string.h>
*/
import "C"
import (
@@ -389,6 +391,74 @@ 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 {
+ // 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 += sizeofOidPtr
+ }
+ }
+
+ 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(tree)
+ 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)
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: "[email protected]",
+ 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())
+ }
+}