summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVicent Martí <[email protected]>2013-06-13 10:15:36 -0700
committerVicent Martí <[email protected]>2013-06-13 10:15:36 -0700
commit62f65d071d0671fb53aaca54a2d59a636267c2b0 (patch)
treee03dd9af8fb0e7287abfc0597c1a325013ee0073
parent01d1a5c5d5fede6f054e50a1154ff747e3879cf8 (diff)
parent5766c4accf913bb4a98189177261e1db939397e2 (diff)
Merge pull request #13 from libgit2/polymorphism-take-2
My take on polymorphism
-rw-r--r--blob.go12
-rw-r--r--commit.go24
-rw-r--r--git_test.go47
-rw-r--r--index_test.go15
-rw-r--r--object.go84
-rw-r--r--object_test.go68
-rw-r--r--odb.go9
-rw-r--r--reference_test.go24
-rw-r--r--repository.go56
-rw-r--r--tree.go30
10 files changed, 259 insertions, 110 deletions
diff --git a/blob.go b/blob.go
index 73e3ab3..cc26c92 100644
--- a/blob.go
+++ b/blob.go
@@ -7,24 +7,18 @@ package git
*/
import "C"
import (
- "runtime"
"unsafe"
)
type Blob struct {
- ptr *C.git_object
+ gitObject
}
-func (v *Blob) Free() {
- runtime.SetFinalizer(v, nil)
- C.git_object_free(v.ptr)
-}
-
-func (v *Blob) Size() int64 {
+func (v Blob) Size() int64 {
return int64(C.git_blob_rawsize(v.ptr))
}
-func (v *Blob) Contents() []byte {
+func (v Blob) Contents() []byte {
size := C.int(C.git_blob_rawsize(v.ptr))
buffer := unsafe.Pointer(C.git_blob_rawcontent(v.ptr))
return C.GoBytes(buffer, size)
diff --git a/commit.go b/commit.go
index 06d1a22..cacaa33 100644
--- a/commit.go
+++ b/commit.go
@@ -9,46 +9,40 @@ extern int _go_git_treewalk(git_tree *tree, git_treewalk_mode mode, void *ptr);
import "C"
import (
- "runtime"
"unsafe"
"time"
)
// Commit
type Commit struct {
- ptr *C.git_commit
+ gitObject
}
-func (c *Commit) Id() *Oid {
- return newOidFromC(C.git_commit_id(c.ptr))
-}
-
-func (c *Commit) Message() string {
+func (c Commit) Message() string {
return C.GoString(C.git_commit_message(c.ptr))
}
-func (c *Commit) Tree() (*Tree, error) {
- tree := new(Tree)
+func (c Commit) Tree() (*Tree, error) {
+ var ptr *C.git_object
- err := C.git_commit_tree(&tree.ptr, c.ptr)
+ err := C.git_commit_tree(&ptr, c.ptr)
if err < 0 {
return nil, LastError()
}
- runtime.SetFinalizer(tree, (*Tree).Free)
- return tree, nil
+ return allocObject(ptr).(*Tree), nil
}
-func (c *Commit) TreeId() *Oid {
+func (c Commit) TreeId() *Oid {
return newOidFromC(C.git_commit_tree_id(c.ptr))
}
-func (c *Commit) Author() *Signature {
+func (c Commit) Author() *Signature {
ptr := C.git_commit_author(c.ptr)
return newSignatureFromC(ptr)
}
-func (c *Commit) Committer() *Signature {
+func (c Commit) Committer() *Signature {
ptr := C.git_commit_committer(c.ptr)
return newSignatureFromC(ptr)
}
diff --git a/git_test.go b/git_test.go
new file mode 100644
index 0000000..52aea1d
--- /dev/null
+++ b/git_test.go
@@ -0,0 +1,47 @@
+package git
+
+import (
+ "testing"
+ "io/ioutil"
+ "time"
+)
+
+func createTestRepo(t *testing.T) *Repository {
+ // figure out where we can create the test repo
+ path, err := ioutil.TempDir("", "git2go")
+ checkFatal(t, err)
+ repo, err := InitRepository(path, false)
+ checkFatal(t, err)
+
+ tmpfile := "README"
+ err = ioutil.WriteFile(path + "/" + tmpfile, []byte("foo\n"), 0644)
+ checkFatal(t, err)
+
+ return repo
+}
+
+func seedTestRepo(t *testing.T, repo *Repository) (*Oid, *Oid) {
+ 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)
+ treeId, err := idx.WriteTree()
+ checkFatal(t, err)
+
+ message := "This is a commit\n"
+ tree, err := repo.LookupTree(treeId)
+ checkFatal(t, err)
+ commitId, err := repo.CreateCommit("HEAD", sig, sig, message, tree)
+ checkFatal(t, err)
+
+ return commitId, treeId
+}
+
diff --git a/index_test.go b/index_test.go
index fe6fb87..9828d0f 100644
--- a/index_test.go
+++ b/index_test.go
@@ -4,23 +4,8 @@ import (
"os"
"runtime"
"testing"
- "io/ioutil"
)
-func createTestRepo(t *testing.T) *Repository {
- // figure out where we can create the test repo
- path, err := ioutil.TempDir("", "git2go")
- checkFatal(t, err)
- repo, err := InitRepository(path, false)
- checkFatal(t, err)
-
- tmpfile := "README"
- err = ioutil.WriteFile(path + "/" + tmpfile, []byte("foo\n"), 0644)
- checkFatal(t, err)
-
- return repo
-}
-
func TestCreateRepoAndStage(t *testing.T) {
repo := createTestRepo(t)
defer os.RemoveAll(repo.Workdir())
diff --git a/object.go b/object.go
new file mode 100644
index 0000000..c6cd8a8
--- /dev/null
+++ b/object.go
@@ -0,0 +1,84 @@
+package git
+
+/*
+#cgo pkg-config: libgit2
+#include <git2.h>
+#include <git2/errors.h>
+*/
+import "C"
+import "runtime"
+
+type ObjectType int
+
+var (
+ OBJ_ANY ObjectType = C.GIT_OBJ_ANY
+ OBJ_BAD ObjectType = C.GIT_OBJ_BAD
+ OBJ_COMMIT ObjectType = C.GIT_OBJ_COMMIT
+ OBJ_TREE ObjectType = C.GIT_OBJ_TREE
+ OBJ_BLOB ObjectType = C.GIT_OBJ_BLOB
+ OBJ_TAG ObjectType = C.GIT_OBJ_TAG
+)
+
+type Object interface {
+ Free()
+ Id() *Oid
+ Type() ObjectType
+}
+
+type gitObject struct {
+ ptr *C.git_object
+}
+
+func (t ObjectType) String() (string) {
+ switch (t) {
+ case OBJ_ANY:
+ return "Any"
+ case OBJ_BAD:
+ return "Bad"
+ case OBJ_COMMIT:
+ return "Commit"
+ case OBJ_TREE:
+ return "Tree"
+ case OBJ_BLOB:
+ return "Blob"
+ case OBJ_TAG:
+ return "tag"
+ }
+ // Never reached
+ return ""
+}
+
+func (o gitObject) Id() *Oid {
+ return newOidFromC(C.git_commit_id(o.ptr))
+}
+
+func (o gitObject) Type() ObjectType {
+ return ObjectType(C.git_object_type(o.ptr))
+}
+
+func (o *gitObject) Free() {
+ runtime.SetFinalizer(o, nil)
+ C.git_commit_free(o.ptr)
+}
+
+func allocObject(cobj *C.git_object) Object {
+
+ switch ObjectType(C.git_object_type(cobj)) {
+ case OBJ_COMMIT:
+ commit := &Commit{gitObject{cobj}}
+ runtime.SetFinalizer(commit, (*Commit).Free)
+ return commit
+
+ case OBJ_TREE:
+ tree := &Tree{gitObject{cobj}}
+ runtime.SetFinalizer(tree, (*Tree).Free)
+ return tree
+
+ case OBJ_BLOB:
+ blob := &Blob{gitObject{cobj}}
+ runtime.SetFinalizer(blob, (*Blob).Free)
+ return blob
+ }
+
+ return nil
+}
diff --git a/object_test.go b/object_test.go
new file mode 100644
index 0000000..82dce98
--- /dev/null
+++ b/object_test.go
@@ -0,0 +1,68 @@
+package git
+
+import (
+ "os"
+ "testing"
+)
+
+func TestObjectPoymorphism(t *testing.T) {
+ repo := createTestRepo(t)
+ defer os.RemoveAll(repo.Workdir())
+ commitId, treeId := seedTestRepo(t, repo)
+
+ var obj Object
+
+ commit, err := repo.LookupCommit(commitId)
+ checkFatal(t, err)
+
+ obj = commit
+ if obj.Type() != OBJ_COMMIT {
+ t.Fatalf("Wrong object type, expected commit, have %v", obj.Type())
+ }
+
+ tree, err := repo.LookupTree(treeId)
+ checkFatal(t, err)
+
+ obj = tree
+ if obj.Type() != OBJ_TREE {
+ t.Fatalf("Wrong object type, expected tree, have %v", obj.Type())
+ }
+
+ tree2, ok := obj.(*Tree)
+ if !ok {
+ t.Fatalf("Converting back to *Tree is not ok")
+ }
+
+ if tree2.EntryByName("README") == nil {
+ t.Fatalf("Tree did not have expected \"README\" entry")
+ }
+
+ _, ok = obj.(*Commit)
+ if ok {
+ t.Fatalf("*Tree is somehow the same as *Commit")
+ }
+
+ obj, err = repo.Lookup(tree.Id())
+ checkFatal(t, err)
+
+ _, ok = obj.(*Tree)
+ if !ok {
+ t.Fatalf("Lookup creates the wrong type")
+ }
+
+ if obj.Type() != OBJ_TREE {
+ t.Fatalf("Type() doesn't agree with dynamic type")
+ }
+
+ obj, err = repo.RevparseSingle("HEAD")
+ checkFatal(t, err)
+ if obj.Type() != OBJ_COMMIT || obj.Id().String() != commit.Id().String() {
+ t.Fatalf("Failed to parse the right revision")
+ }
+
+ obj, err = repo.RevparseSingle("HEAD^{tree}")
+ checkFatal(t, err)
+ if obj.Type() != OBJ_TREE || obj.Id().String() != tree.Id().String() {
+ t.Fatalf("Failed to parse the right revision")
+ }
+}
diff --git a/odb.go b/odb.go
index f6eb591..9d7d02e 100644
--- a/odb.go
+++ b/odb.go
@@ -14,15 +14,6 @@ import (
"runtime"
)
-var (
- OBJ_ANY = C.GIT_OBJ_ANY
- OBJ_BAD = C.GIT_OBJ_BAD
- OBJ_COMMIT = C.GIT_OBJ_COMMIT
- OBJ_TREE = C.GIT_OBJ_TREE
- OBJ_BLOB = C.GIT_OBJ_BLOB
- OBJ_TAG = C.GIT_OBJ_TAG
-)
-
type Odb struct {
ptr *C.git_odb
}
diff --git a/reference_test.go b/reference_test.go
index a03f638..6ac4ece 100644
--- a/reference_test.go
+++ b/reference_test.go
@@ -5,35 +5,15 @@ import (
"runtime"
"sort"
"testing"
- "time"
)
func TestRefModification(t *testing.T) {
repo := createTestRepo(t)
defer os.RemoveAll(repo.Workdir())
- 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)
- treeId, err := idx.WriteTree()
- checkFatal(t, err)
-
- message := "This is a commit\n"
- tree, err := repo.LookupTree(treeId)
- checkFatal(t, err)
- commitId, err := repo.CreateCommit("HEAD", sig, sig, message, tree)
- checkFatal(t, err)
+ commitId, treeId := seedTestRepo(t, repo)
- _, err = repo.CreateReference("refs/tags/tree", treeId, true)
+ _, err := repo.CreateReference("refs/tags/tree", treeId, true)
checkFatal(t, err)
tag, err := repo.LookupReference("refs/tags/tree")
diff --git a/repository.go b/repository.go
index b9a10ad..3a44000 100644
--- a/repository.go
+++ b/repository.go
@@ -72,35 +72,45 @@ func (v *Repository) Index() (*Index, error) {
return newIndexFromC(ptr), nil
}
-func (v *Repository) LookupTree(oid *Oid) (*Tree, error) {
- tree := new(Tree)
- ret := C.git_tree_lookup(&tree.ptr, v.ptr, oid.toC())
+func (v *Repository) lookupType(oid *Oid, t ObjectType) (Object, error) {
+ var ptr *C.git_object
+ ret := C.git_object_lookup(&ptr, v.ptr, oid.toC(), C.git_otype(t))
if ret < 0 {
return nil, LastError()
}
- return tree, nil
+ return allocObject(ptr), nil
}
-func (v *Repository) LookupCommit(o *Oid) (*Commit, error) {
- commit := new(Commit)
- ecode := C.git_commit_lookup(&commit.ptr, v.ptr, o.toC())
- if ecode < 0 {
- return nil, LastError()
+func (v *Repository) Lookup(oid *Oid) (Object, error) {
+ return v.lookupType(oid, OBJ_ANY)
+}
+
+func (v *Repository) LookupTree(oid *Oid) (*Tree, error) {
+ obj, err := v.lookupType(oid, OBJ_TREE)
+ if err != nil {
+ return nil, err
}
- return commit, nil
+ return obj.(*Tree), nil
}
-func (v *Repository) LookupBlob(o *Oid) (*Blob, error) {
- blob := new(Blob)
- ecode := C.git_blob_lookup(&blob.ptr, v.ptr, o.toC())
- if ecode < 0 {
- return nil, LastError()
+func (v *Repository) LookupCommit(oid *Oid) (*Commit, error) {
+ obj, err := v.lookupType(oid, OBJ_COMMIT)
+ if err != nil {
+ return nil, err
+ }
+
+ return obj.(*Commit), nil
+}
+
+func (v *Repository) LookupBlob(oid *Oid) (*Blob, error) {
+ obj, err := v.lookupType(oid, OBJ_BLOB)
+ if err != nil {
+ return nil, err
}
- runtime.SetFinalizer(blob, (*Blob).Free)
- return blob, nil
+ return obj.(*Blob), nil
}
func (v *Repository) LookupReference(name string) (*Reference, error) {
@@ -245,3 +255,15 @@ func (v *Repository) TreeBuilder() (*TreeBuilder, error) {
return bld, nil
}
+func (v *Repository) RevparseSingle(spec string) (Object, error) {
+ cspec := C.CString(spec)
+ defer C.free(unsafe.Pointer(cspec))
+
+ var ptr *C.git_object
+ ecode := C.git_revparse_single(&ptr, v.ptr, cspec)
+ if ecode < 0 {
+ return nil, LastError()
+ }
+
+ return allocObject(ptr), nil
+}
diff --git a/tree.go b/tree.go
index dc82929..9a9fd63 100644
--- a/tree.go
+++ b/tree.go
@@ -14,40 +14,24 @@ import (
)
type Tree struct {
- ptr *C.git_tree
+ gitObject
}
type TreeEntry struct {
Name string
Id *Oid
- Type int
+ Type ObjectType
}
func newTreeEntry(entry *C.git_tree_entry) *TreeEntry {
return &TreeEntry{
C.GoString(C.git_tree_entry_name(entry)),
newOidFromC(C.git_tree_entry_id(entry)),
- int(C.git_tree_entry_type(entry)),
+ ObjectType(C.git_tree_entry_type(entry)),
}
}
-func (t *Tree) Free() {
- runtime.SetFinalizer(t, nil)
- C.git_tree_free(t.ptr)
-}
-
-func TreeLookup(repo *Repository, oid *Oid) (*Tree, error) {
- tree := new(Tree)
- err := C.git_tree_lookup(&tree.ptr, repo.ptr, oid.toC())
- if err < 0 {
- return nil, LastError()
- }
-
- runtime.SetFinalizer(tree, (*Tree).Free)
- return tree, nil
-}
-
-func (t *Tree) EntryByName(filename string) *TreeEntry {
+func (t Tree) EntryByName(filename string) *TreeEntry {
cname := C.CString(filename)
defer C.free(unsafe.Pointer(cname))
@@ -59,7 +43,7 @@ func (t *Tree) EntryByName(filename string) *TreeEntry {
return newTreeEntry(entry)
}
-func (t *Tree) EntryByIndex(index uint64) *TreeEntry {
+func (t Tree) EntryByIndex(index uint64) *TreeEntry {
entry := C.git_tree_entry_byindex(t.ptr, C.size_t(index))
if entry == nil {
return nil
@@ -68,7 +52,7 @@ func (t *Tree) EntryByIndex(index uint64) *TreeEntry {
return newTreeEntry(entry)
}
-func (t *Tree) EntryCount() uint64 {
+func (t Tree) EntryCount() uint64 {
num := C.git_tree_entrycount(t.ptr)
return uint64(num)
}
@@ -84,7 +68,7 @@ func CallbackGitTreeWalk(_root unsafe.Pointer, _entry unsafe.Pointer, ptr unsafe
return C.int(callback(root, newTreeEntry(entry)))
}
-func (t *Tree) Walk(callback TreeWalkCallback) error {
+func (t Tree) Walk(callback TreeWalkCallback) error {
err := C._go_git_treewalk(
t.ptr,
C.GIT_TREEWALK_PRE,