diff options
| -rw-r--r-- | note.go | 99 | ||||
| -rw-r--r-- | note_test.go | 113 | ||||
| -rw-r--r-- | remote.go | 24 | ||||
| -rw-r--r-- | remote_test.go | 83 | ||||
| -rw-r--r-- | repository.go | 100 |
5 files changed, 413 insertions, 6 deletions
@@ -0,0 +1,99 @@ +package git + +/* +#include <git2.h> +*/ +import "C" + +import ( + "runtime" + "unsafe" +) + +// Note +type Note struct { + ptr *C.git_note +} + +// Free frees a git_note object +func (n *Note) Free() error { + if n.ptr == nil { + return ErrInvalid + } + runtime.SetFinalizer(n, nil) + C.git_note_free(n.ptr) + n.ptr = nil + return nil +} + +// Author returns the signature of the note author +func (n *Note) Author() *Signature { + ptr := C.git_note_author(n.ptr) + return newSignatureFromC(ptr) +} + +// Id returns the note object's id +func (n *Note) Id() *Oid { + ptr := C.git_note_id(n.ptr) + return newOidFromC(ptr) +} + +// Committer returns the signature of the note committer +func (n *Note) Committer() *Signature { + ptr := C.git_note_committer(n.ptr) + return newSignatureFromC(ptr) +} + +// Message returns the note message +func (n *Note) Message() string { + return C.GoString(C.git_note_message(n.ptr)) +} + +// NoteIterator +type NoteIterator struct { + ptr *C.git_note_iterator +} + +// NewNoteIterator creates a new iterator for notes +func (repo *Repository) NewNoteIterator(ref string) (*NoteIterator, error) { + var cref *C.char + if ref == "" { + cref = nil + } else { + cref = C.CString(ref) + defer C.free(unsafe.Pointer(cref)) + } + + var ptr *C.git_note_iterator + + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + if ret := C.git_note_iterator_new(&ptr, repo.ptr, cref); ret < 0 { + return nil, MakeGitError(ret) + } + + iter := &NoteIterator{ptr: ptr} + runtime.SetFinalizer(iter, (*NoteIterator).Free) + return iter, nil +} + +// Free frees the note interator +func (v *NoteIterator) Free() { + runtime.SetFinalizer(v, nil) + C.git_note_iterator_free(v.ptr) +} + +// Next returns the current item (note id & annotated id) and advances the +// iterator internally to the next item +func (it *NoteIterator) Next() (noteId, annotatedId *Oid, err error) { + noteId, annotatedId = new(Oid), new(Oid) + + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + if ret := C.git_note_next(noteId.toC(), annotatedId.toC(), it.ptr); ret < 0 { + err = MakeGitError(ret) + } + return +} diff --git a/note_test.go b/note_test.go new file mode 100644 index 0000000..f5e9c01 --- /dev/null +++ b/note_test.go @@ -0,0 +1,113 @@ +package git + +import ( + "fmt" + "os" + "reflect" + "testing" + "time" +) + +func TestCreateNote(t *testing.T) { + repo := createTestRepo(t) + defer os.RemoveAll(repo.Workdir()) + + commitId, _ := seedTestRepo(t, repo) + + commit, err := repo.LookupCommit(commitId) + checkFatal(t, err) + + note, noteId := createTestNote(t, repo, commit) + + compareStrings(t, "I am a note\n", note.Message()) + compareStrings(t, noteId.String(), note.Id().String()) + compareStrings(t, "alice", note.Author().Name) + compareStrings(t, "[email protected]", note.Author().Email) + compareStrings(t, "alice", note.Committer().Name) + compareStrings(t, "[email protected]", note.Committer().Email) +} + +func TestNoteIterator(t *testing.T) { + repo := createTestRepo(t) + defer os.RemoveAll(repo.Workdir()) + seedTestRepo(t, repo) + + notes := make([]*Note, 5) + for i := range notes { + commitId, _ := updateReadme(t, repo, fmt.Sprintf("README v%d\n", i+1)) + commit, err := repo.LookupCommit(commitId) + checkFatal(t, err) + + note, _ := createTestNote(t, repo, commit) + notes[i] = note + } + + iter, err := repo.NewNoteIterator("") + checkFatal(t, err) + for { + noteId, commitId, err := iter.Next() + if err != nil { + if !IsErrorCode(err, ErrIterOver) { + checkFatal(t, err) + } + break + } + + note, err := repo.ReadNote("", commitId) + checkFatal(t, err) + + if !reflect.DeepEqual(note.Id(), noteId) { + t.Errorf("expected note oid '%v', actual '%v'", note.Id(), noteId) + } + } +} + +func TestRemoveNote(t *testing.T) { + repo := createTestRepo(t) + defer os.RemoveAll(repo.Workdir()) + + commitId, _ := seedTestRepo(t, repo) + + commit, err := repo.LookupCommit(commitId) + checkFatal(t, err) + + note, _ := createTestNote(t, repo, commit) + + _, err = repo.ReadNote("", commit.Id()) + checkFatal(t, err) + + err = repo.RemoveNote("", note.Author(), note.Committer(), commitId) + checkFatal(t, err) + + _, err = repo.ReadNote("", commit.Id()) + if err == nil { + t.Fatal("note remove failed") + } +} + +func TestDefaultNoteRef(t *testing.T) { + repo := createTestRepo(t) + defer os.RemoveAll(repo.Workdir()) + + ref, err := repo.DefaultNoteRef() + checkFatal(t, err) + + compareStrings(t, "refs/notes/commits", ref) +} + +func createTestNote(t *testing.T, repo *Repository, commit *Commit) (*Note, *Oid) { + loc, err := time.LoadLocation("Europe/Berlin") + sig := &Signature{ + Name: "alice", + Email: "[email protected]", + When: time.Date(2015, 01, 05, 13, 0, 0, 0, loc), + } + + noteId, err := repo.CreateNote("", sig, sig, commit.Id(), "I am a note\n", false) + checkFatal(t, err) + + note, err := repo.ReadNote("", commit.Id()) + checkFatal(t, err) + + return note, noteId +} @@ -69,7 +69,6 @@ type RemoteCallbacks struct { PushUpdateReferenceCallback } - type Remote struct { ptr *C.git_remote callbacks RemoteCallbacks @@ -259,8 +258,6 @@ func pushUpdateReferenceCallback(refname, status *C.char, data unsafe.Pointer) i return int(callbacks.PushUpdateReferenceCallback(C.GoString(refname), C.GoString(status))) } - - func RemoteIsValidName(name string) bool { cname := C.CString(name) defer C.free(unsafe.Pointer(cname)) @@ -330,7 +327,7 @@ func (repo *Repository) CreateRemote(name string, url string) (*Remote, error) { func (repo *Repository) DeleteRemote(name string) error { cname := C.CString(name) defer C.free(unsafe.Pointer(cname)) - + runtime.LockOSThread() defer runtime.UnlockOSThread() @@ -722,8 +719,8 @@ func (o *Remote) Push(refspecs []string, opts *PushOptions, sig *Signature, msg crefspecs.strings = makeCStringsFromStrings(refspecs) defer freeStrarray(&crefspecs) - runtime.LockOSThread() - defer runtime.UnlockOSThread() + runtime.LockOSThread() + defer runtime.UnlockOSThread() ret := C.git_remote_push(o.ptr, &crefspecs, &copts, csig, cmsg) if ret < 0 { @@ -731,3 +728,18 @@ func (o *Remote) Push(refspecs []string, opts *PushOptions, sig *Signature, msg } return nil } + +func (o *Remote) PruneRefs() bool { + return C.git_remote_prune_refs(o.ptr) > 0 +} + +func (o *Remote) Prune() error { + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + ret := C.git_remote_prune(o.ptr) + if ret < 0 { + return MakeGitError(ret) + } + return nil +} diff --git a/remote_test.go b/remote_test.go index 631a6cd..54a66ed 100644 --- a/remote_test.go +++ b/remote_test.go @@ -1,8 +1,10 @@ package git import ( + "fmt" "os" "testing" + "time" ) func TestRefspecs(t *testing.T) { @@ -132,3 +134,84 @@ func TestRemoteLsFiltering(t *testing.T) { t.Fatalf("Expected head to have a name, but it's empty") } } + +func TestRemotePruneRefs(t *testing.T) { + repo := createTestRepo(t) + defer os.RemoveAll(repo.Workdir()) + defer repo.Free() + + config, err := repo.Config() + checkFatal(t, err) + defer config.Free() + + err = config.SetBool("remote.origin.prune", true) + checkFatal(t, err) + + _, err = repo.CreateRemote("origin", "https://github.com/libgit2/TestGitRepository") + checkFatal(t, err) + + remote, err := repo.LookupRemote("origin") + checkFatal(t, err) + + if !remote.PruneRefs() { + t.Fatal("Expected remote to be configured to prune references") + } +} + +func TestRemotePrune(t *testing.T) { + remoteRepo := createTestRepo(t) + defer os.RemoveAll(remoteRepo.Workdir()) + defer remoteRepo.Free() + + head, _ := seedTestRepo(t, remoteRepo) + commit, err := remoteRepo.LookupCommit(head) + checkFatal(t, err) + defer commit.Free() + + sig := &Signature{ + Name: "Rand Om Hacker", + Email: "[email protected]", + When: time.Now(), + } + + remoteRef, err := remoteRepo.CreateBranch("test-prune", commit, true, sig, "branch test-prune") + checkFatal(t, err) + + repo := createTestRepo(t) + defer os.RemoveAll(repo.Workdir()) + defer repo.Free() + + config, err := repo.Config() + checkFatal(t, err) + defer config.Free() + + remoteUrl := fmt.Sprintf("file://%s", remoteRepo.Workdir()) + remote, err := repo.CreateRemote("origin", remoteUrl) + checkFatal(t, err) + + err = remote.Fetch([]string{"test-prune"}, sig, "") + checkFatal(t, err) + + _, err = repo.CreateReference("refs/remotes/origin/test-prune", head, true, sig, "remote reference") + checkFatal(t, err) + + err = remoteRef.Delete() + checkFatal(t, err) + + err = config.SetBool("remote.origin.prune", true) + checkFatal(t, err) + + rr, err := repo.LookupRemote("origin") + checkFatal(t, err) + + err = rr.ConnectFetch() + checkFatal(t, err) + + err = rr.Prune() + checkFatal(t, err) + + _, err = repo.LookupReference("refs/remotes/origin/test-prune") + if err == nil { + t.Fatal("Expected error getting a pruned reference") + } +} diff --git a/repository.go b/repository.go index 222ede3..7760c3a 100644 --- a/repository.go +++ b/repository.go @@ -530,3 +530,103 @@ func (v *Repository) DwimReference(name string) (*Reference, error) { return newReferenceFromC(ptr, v), nil } + +// CreateNote adds a note for an object +func (v *Repository) CreateNote( + ref string, author, committer *Signature, id *Oid, + note string, force bool) (*Oid, error) { + + oid := new(Oid) + + var cref *C.char + if ref == "" { + cref = nil + } else { + cref = C.CString(ref) + defer C.free(unsafe.Pointer(cref)) + } + + authorSig := author.toC() + defer C.git_signature_free(authorSig) + + committerSig := committer.toC() + defer C.git_signature_free(committerSig) + + cnote := C.CString(note) + defer C.free(unsafe.Pointer(cnote)) + + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + ret := C.git_note_create( + oid.toC(), v.ptr, cref, authorSig, + committerSig, id.toC(), cnote, cbool(force)) + + if ret < 0 { + return nil, MakeGitError(ret) + } + return oid, nil +} + +// ReadNote reads the note for an object +func (v *Repository) ReadNote(ref string, id *Oid) (*Note, error) { + var cref *C.char + if ref == "" { + cref = nil + } else { + cref = C.CString(ref) + defer C.free(unsafe.Pointer(cref)) + } + + note := new(Note) + + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + if ret := C.git_note_read(¬e.ptr, v.ptr, cref, id.toC()); ret < 0 { + return nil, MakeGitError(ret) + } + + runtime.SetFinalizer(note, (*Note).Free) + return note, nil +} + +// RemoveNote removes the note for an object +func (v *Repository) RemoveNote(ref string, author, committer *Signature, id *Oid) error { + var cref *C.char + if ref == "" { + cref = nil + } else { + cref = C.CString(ref) + defer C.free(unsafe.Pointer(cref)) + } + + authorSig := author.toC() + defer C.git_signature_free(authorSig) + + committerSig := committer.toC() + defer C.git_signature_free(committerSig) + + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + ret := C.git_note_remove(v.ptr, cref, authorSig, committerSig, id.toC()) + if ret < 0 { + return MakeGitError(ret) + } + return nil +} + +// DefaultNoteRef returns the default notes reference for a repository +func (v *Repository) DefaultNoteRef() (string, error) { + var ptr *C.char + + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + if ret := C.git_note_default_ref(&ptr, v.ptr); ret < 0 { + return "", MakeGitError(ret) + } + + return C.GoString(ptr), nil +} |
