diff --git a/index_test.go b/index_test.go index cbcdaa5..fe6fb87 100644 --- a/index_test.go +++ b/index_test.go @@ -7,7 +7,7 @@ import ( "io/ioutil" ) -func TestCreateRepoAndStage(t *testing.T) { +func createTestRepo(t *testing.T) *Repository { // figure out where we can create the test repo path, err := ioutil.TempDir("", "git2go") checkFatal(t, err) @@ -17,11 +17,17 @@ func TestCreateRepoAndStage(t *testing.T) { tmpfile := "README" err = ioutil.WriteFile(path + "/" + tmpfile, []byte("foo\n"), 0644) checkFatal(t, err) - defer os.RemoveAll(path) + + return repo +} + +func TestCreateRepoAndStage(t *testing.T) { + repo := createTestRepo(t) + defer os.RemoveAll(repo.Workdir()) idx, err := repo.Index() checkFatal(t, err) - err = idx.AddByPath(tmpfile) + err = idx.AddByPath("README") checkFatal(t, err) treeId, err := idx.WriteTree() checkFatal(t, err) @@ -42,5 +48,5 @@ func checkFatal(t *testing.T, err error) { t.Fatal() } - t.Fatalf("Fail at %v:%v", file, line) + t.Fatalf("Fail at %v:%v; %v", file, line, err) } diff --git a/reference.go b/reference.go new file mode 100644 index 0000000..820d166 --- /dev/null +++ b/reference.go @@ -0,0 +1,113 @@ +package git + +/* +#cgo pkg-config: libgit2 +#include +#include +*/ +import "C" +import ( + "runtime" + "unsafe" +) + +var ( + SYMBOLIC = C.GIT_REF_SYMBOLIC + OID = C.GIT_REF_OID +) + +type Reference struct { + ptr *C.git_reference +} + +func newReferenceFromC(ptr *C.git_reference) *Reference { + ref := &Reference{ptr} + runtime.SetFinalizer(ref, (*Reference).Free) + + return ref +} + +func (v *Reference) SetSymbolicTarget(target string) (*Reference, error) { + var ptr *C.git_reference + ctarget := C.CString(target) + defer C.free(unsafe.Pointer(ctarget)) + + ret := C.git_reference_symbolic_set_target(&ptr, v.ptr, ctarget) + if ret < 0 { + return nil, LastError() + } + + return newReferenceFromC(ptr), nil +} + +func (v *Reference) SetTarget(target *Oid) (*Reference, error) { + var ptr *C.git_reference + + ret := C.git_reference_set_target(&ptr, v.ptr, target.toC()) + if ret < 0 { + return nil, LastError() + } + + return newReferenceFromC(ptr), nil +} + +func (v *Reference) Resolve() (*Reference, error) { + var ptr *C.git_reference + + ret := C.git_reference_resolve(&ptr, v.ptr) + if ret < 0 { + return nil, LastError() + } + + return newReferenceFromC(ptr), nil +} + +func (v *Reference) Rename(name string, force bool) (*Reference, error) { + var ptr *C.git_reference + cname := C.CString(name) + defer C.free(unsafe.Pointer(cname)) + + ret := C.git_reference_rename(&ptr, v.ptr, cname, cbool(force)) + + if ret < 0 { + return nil, LastError() + } + + return newReferenceFromC(ptr), nil +} + +func (v *Reference) Target() *Oid { + return newOidFromC(C.git_reference_target(v.ptr)) +} + +func (v *Reference) SymbolicTarget() string { + cstr := C.git_reference_symbolic_target(v.ptr) + if cstr == nil { + return "" + } + + return C.GoString(cstr) +} + +func (v *Reference) Delete() error { + ret := C.git_reference_delete(v.ptr) + + if ret < 0 { + return LastError() + } + + return nil +} + +func (v *Reference) Name() string { + return C.GoString(C.git_reference_name(v.ptr)) +} + +func (v *Reference) Type() int { + return int(C.git_reference_type(v.ptr)) +} + +func (v *Reference) Free() { + runtime.SetFinalizer(v, nil) + C.git_reference_free(v.ptr) +} diff --git a/reference_test.go b/reference_test.go new file mode 100644 index 0000000..ba32d81 --- /dev/null +++ b/reference_test.go @@ -0,0 +1,74 @@ +package git + +import ( + "os" + "runtime" + "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: "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) + 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) + + _, err = repo.CreateReference("refs/tags/tree", treeId, true) + checkFatal(t, err) + + tag, err := repo.LookupReference("refs/tags/tree") + checkFatal(t, err) + checkRefType(t, tag, OID) + + ref, err := repo.LookupReference("HEAD") + checkFatal(t, err) + checkRefType(t, ref, SYMBOLIC) + + ref, err = ref.Resolve() + checkFatal(t, err) + checkRefType(t, ref, OID) + + if commitId.String() != ref.Target().String() { + t.Fatalf("Wrong ref target") + } + + _, err = tag.Rename("refs/tags/renamed", false) + checkFatal(t, err) + tag, err = repo.LookupReference("refs/tags/renamed") + checkFatal(t, err) + checkRefType(t, ref, OID) + +} + +func checkRefType(t *testing.T, ref *Reference, kind int) { + if ref.Type() == kind { + return + } + + // The failure happens at wherever we were called, not here + _, file, line, ok := runtime.Caller(1) + if !ok { + t.Fatal() + } + + t.Fatalf("Wrong ref type at %v:%v; have %v, expected %v", file, line, ref.Type(), kind) +} diff --git a/repository.go b/repository.go index 4cc8c5d..51a2718 100644 --- a/repository.go +++ b/repository.go @@ -103,6 +103,47 @@ func (v *Repository) LookupBlob(o *Oid) (*Blob, error) { return blob, nil } +func (v *Repository) LookupReference(name string) (*Reference, error) { + cname := C.CString(name) + defer C.free(unsafe.Pointer(cname)) + var ptr *C.git_reference + + ecode := C.git_reference_lookup(&ptr, v.ptr, cname) + if ecode < 0 { + return nil, LastError() + } + + return newReferenceFromC(ptr), nil +} + +func (v *Repository) CreateReference(name string, oid *Oid, force bool) (*Reference, error) { + cname := C.CString(name) + defer C.free(unsafe.Pointer(cname)) + var ptr *C.git_reference + + ecode := C.git_reference_create(&ptr, v.ptr, cname, oid.toC(), cbool(force)) + if ecode < 0 { + return nil, LastError() + } + + return newReferenceFromC(ptr), nil +} + +func (v *Repository) CreateSymbolicReference(name, target string, force bool) (*Reference, error) { + cname := C.CString(name) + defer C.free(unsafe.Pointer(cname)) + ctarget := C.CString(target) + defer C.free(unsafe.Pointer(ctarget)) + var ptr *C.git_reference + + ecode := C.git_reference_symbolic_create(&ptr, v.ptr, cname, ctarget, cbool(force)) + if ecode < 0 { + return nil, LastError() + } + + return newReferenceFromC(ptr), nil +} + func (v *Repository) Walk() (*RevWalk, error) { walk := new(RevWalk) ecode := C.git_revwalk_new(&walk.ptr, v.ptr) @@ -176,6 +217,10 @@ func (repo *Repository) Path() string { return C.GoString(C.git_repository_path(repo.ptr)) } +func (repo *Repository) Workdir() string { + return C.GoString(C.git_repository_workdir(repo.ptr)) +} + func (v *Repository) TreeBuilder() (*TreeBuilder, error) { bld := new(TreeBuilder) if ret := C.git_treebuilder_create(&bld.ptr, nil); ret < 0 {