Add branch iterator #66

Merged
jezell merged 9 commits from branch-iterator into master 2014-04-26 13:25:26 -05:00
4 changed files with 137 additions and 81 deletions

View File

@ -20,10 +20,70 @@ const (
)
type Branch struct {
Reference
*Reference
}
func (repo *Repository) CreateBranch(branchName string, target *Commit, force bool, signature *Signature, msg string) (*Reference, error) {
func (r *Reference) Branch() *Branch {
return &Branch{Reference: r}
}
type BranchIterator struct {
ptr *C.git_branch_iterator
repo *Repository
}
type BranchInfo struct {
Branch *Branch
Type BranchType
}
func newBranchIteratorFromC(repo *Repository, ptr *C.git_branch_iterator) *BranchIterator {
i := &BranchIterator{repo: repo, ptr: ptr}
runtime.SetFinalizer(i, (*BranchIterator).Free)
return i
}
func (i *BranchIterator) Next() (*Branch, BranchType, error) {
runtime.LockOSThread()
defer runtime.UnlockOSThread()
var refPtr *C.git_reference
var refType C.git_branch_t
ecode := C.git_branch_next(&refPtr, &refType, i.ptr)
if ecode < 0 {
return nil, BranchLocal, MakeGitError(ecode)
}
branch := newReferenceFromC(refPtr).Branch()
return branch, BranchType(refType), nil
}
func (i *BranchIterator) Free() {
runtime.SetFinalizer(i, nil)
C.git_branch_iterator_free(i.ptr)
}
func (repo *Repository) NewBranchIterator(flags BranchType) (*BranchIterator, error) {
refType := C.git_branch_t(flags)
var ptr *C.git_branch_iterator
runtime.LockOSThread()
defer runtime.UnlockOSThread()
ecode := C.git_branch_iterator_new(&ptr, repo.ptr, refType)
if ecode < 0 {
return nil, MakeGitError(ecode)
}
return newBranchIteratorFromC(repo, ptr), nil
}
func (repo *Repository) CreateBranch(branchName string, target *Commit, force bool, signature *Signature, msg string) (*Branch, error) {
ref := new(Reference)
cBranchName := C.CString(branchName)
@ -47,14 +107,14 @@ func (repo *Repository) CreateBranch(branchName string, target *Commit, force bo
if ret < 0 {
return nil, MakeGitError(ret)
}
return ref, nil
return ref.Branch(), nil
}
func (b *Branch) Delete() error {
runtime.LockOSThread()
defer runtime.UnlockOSThread()
ret := C.git_branch_delete(b.ptr)
ret := C.git_branch_delete(b.Reference.ptr)
if ret < 0 {
return MakeGitError(ret)
}
@ -62,7 +122,7 @@ func (b *Branch) Delete() error {
}
func (b *Branch) Move(newBranchName string, force bool, signature *Signature, msg string) (*Branch, error) {
newBranch := new(Branch)
var ptr *C.git_reference
cNewBranchName := C.CString(newBranchName)
cForce := cbool(force)
@ -80,11 +140,11 @@ func (b *Branch) Move(newBranchName string, force bool, signature *Signature, ms
runtime.LockOSThread()
defer runtime.UnlockOSThread()
ret := C.git_branch_move(&newBranch.ptr, b.ptr, cNewBranchName, cForce, cSignature, cmsg)
ret := C.git_branch_move(&ptr, b.Reference.ptr, cNewBranchName, cForce, cSignature, cmsg)
if ret < 0 {
return nil, MakeGitError(ret)
}
return newBranch, nil
return newReferenceFromC(ptr).Branch(), nil
}
func (b *Branch) IsHead() (bool, error) {
@ -92,7 +152,7 @@ func (b *Branch) IsHead() (bool, error) {
runtime.LockOSThread()
defer runtime.UnlockOSThread()
ret := C.git_branch_is_head(b.ptr)
ret := C.git_branch_is_head(b.Reference.ptr)
switch ret {
case 1:
return true, nil
@ -104,17 +164,18 @@ func (b *Branch) IsHead() (bool, error) {
}
func (repo *Repository) LookupBranch(branchName string, bt BranchType) (*Branch, error) {
branch := new(Branch)
var ptr *C.git_reference
cName := C.CString(branchName)
runtime.LockOSThread()
defer runtime.UnlockOSThread()
ret := C.git_branch_lookup(&branch.ptr, repo.ptr, cName, C.git_branch_t(bt))
ret := C.git_branch_lookup(&ptr, repo.ptr, cName, C.git_branch_t(bt))
if ret < 0 {
return nil, MakeGitError(ret)
}
return branch, nil
return newReferenceFromC(ptr).Branch(), nil
}
func (b *Branch) Name() (string, error) {
@ -124,7 +185,7 @@ func (b *Branch) Name() (string, error) {
runtime.LockOSThread()
defer runtime.UnlockOSThread()
ret := C.git_branch_name(&cName, b.ptr)
ret := C.git_branch_name(&cName, b.Reference.ptr)
if ret < 0 {
return "", MakeGitError(ret)
}
@ -155,24 +216,24 @@ func (b *Branch) SetUpstream(upstreamName string) error {
runtime.LockOSThread()
defer runtime.UnlockOSThread()
ret := C.git_branch_set_upstream(b.ptr, cName)
ret := C.git_branch_set_upstream(b.Reference.ptr, cName)
if ret < 0 {
return MakeGitError(ret)
}
return nil
}
func (b *Branch) Upstream() (*Branch, error) {
upstream := new(Branch)
func (b *Branch) Upstream() (*Reference, error) {
var ptr *C.git_reference
runtime.LockOSThread()
defer runtime.UnlockOSThread()
ret := C.git_branch_upstream(&upstream.ptr, b.ptr)
ret := C.git_branch_upstream(&ptr, b.Reference.ptr)
if ret < 0 {
return nil, MakeGitError(ret)
}
return upstream, nil
return newReferenceFromC(ptr), nil
}
func (repo *Repository) UpstreamName(canonicalBranchName string) (string, error) {

26
branch_test.go Normal file
View File

@ -0,0 +1,26 @@
package git
import (
"testing"
)
func TestBranchIterator(t *testing.T) {
repo := createTestRepo(t)
seedTestRepo(t, repo)
i, err := repo.NewBranchIterator(BranchLocal)
checkFatal(t, err)
b, bt, err := i.Next()
checkFatal(t, err)
if name, _ := b.Name(); name != "master" {
t.Fatalf("expected master")
} else if bt != BranchLocal {
t.Fatalf("expected BranchLocal, not %v", t)
}
b, bt, err = i.Next()
if !IsErrorCode(err, ErrIterOver) {
t.Fatal("expected iterover")
}
}

View File

@ -22,9 +22,8 @@ type Reference struct {
}
func newReferenceFromC(ptr *C.git_reference) *Reference {
ref := &Reference{ptr}
ref := &Reference{ptr: ptr}
runtime.SetFinalizer(ref, (*Reference).Free)
return ref
}
@ -190,6 +189,10 @@ type ReferenceIterator struct {
repo *Repository
}
type ReferenceNameIterator struct {
*ReferenceIterator
}
// NewReferenceIterator creates a new iterator over reference names
func (repo *Repository) NewReferenceIterator() (*ReferenceIterator, error) {
var ptr *C.git_reference_iterator
@ -202,11 +205,28 @@ func (repo *Repository) NewReferenceIterator() (*ReferenceIterator, error) {
return nil, MakeGitError(ret)
}
iter := &ReferenceIterator{repo: repo, ptr: ptr}
iter := &ReferenceIterator{ptr: ptr, repo: repo}
runtime.SetFinalizer(iter, (*ReferenceIterator).Free)
return iter, nil
}
// NewReferenceIterator creates a new branch iterator over reference names
func (repo *Repository) NewReferenceNameIterator() (*ReferenceNameIterator, error) {
var ptr *C.git_reference_iterator
runtime.LockOSThread()
defer runtime.UnlockOSThread()
ret := C.git_reference_iterator_new(&ptr, repo.ptr)
if ret < 0 {
return nil, MakeGitError(ret)
}
iter := &ReferenceIterator{ptr: ptr, repo: repo}
runtime.SetFinalizer(iter, (*ReferenceIterator).Free)
return iter.Names(), nil
}
// NewReferenceIteratorGlob creates an iterator over reference names
// that match the speicified glob. The glob is of the usual fnmatch
// type.
@ -223,14 +243,18 @@ func (repo *Repository) NewReferenceIteratorGlob(glob string) (*ReferenceIterato
return nil, MakeGitError(ret)
}
iter := &ReferenceIterator{repo: repo, ptr: ptr}
iter := &ReferenceIterator{ptr: ptr}
runtime.SetFinalizer(iter, (*ReferenceIterator).Free)
return iter, nil
}
func (i *ReferenceIterator) Names() *ReferenceNameIterator {
return &ReferenceNameIterator{i}
}
// NextName retrieves the next reference name. If the iteration is over,
// the returned error is git.ErrIterOver
func (v *ReferenceIterator) NextName() (string, error) {
func (v *ReferenceNameIterator) Next() (string, error) {
var ptr *C.char
runtime.LockOSThread()
@ -244,23 +268,6 @@ func (v *ReferenceIterator) NextName() (string, error) {
return C.GoString(ptr), nil
}
// Create a channel from the iterator. You can use range on the
// returned channel to iterate over all the references names. The channel
// will be closed in case any error is found.
func (v *ReferenceIterator) NameIter() <-chan string {
ch := make(chan string)
go func() {
defer close(ch)
name, err := v.NextName()
for err == nil {
ch <- name
name, err = v.NextName()
}
}()
return ch
}
// Next retrieves the next reference. If the iterationis over, the
// returned error is git.ErrIterOver
func (v *ReferenceIterator) Next() (*Reference, error) {
@ -273,23 +280,6 @@ func (v *ReferenceIterator) Next() (*Reference, error) {
return newReferenceFromC(ptr), nil
}
// Create a channel from the iterator. You can use range on the
// returned channel to iterate over all the references names. The channel
// will be closed in case any error is found.
func (v *ReferenceIterator) Iter() <-chan *Reference {
ch := make(chan *Reference)
go func() {
defer close(ch)
name, err := v.Next()
for err == nil {
ch <- name
name, err = v.Next()
}
}()
return ch
}
// Free the reference iterator
func (v *ReferenceIterator) Free() {
runtime.SetFinalizer(v, nil)

View File

@ -60,7 +60,7 @@ func TestRefModification(t *testing.T) {
}
func TestIterator(t *testing.T) {
func TestReferenceIterator(t *testing.T) {
repo := createTestRepo(t)
defer os.RemoveAll(repo.Workdir())
@ -106,10 +106,11 @@ func TestIterator(t *testing.T) {
}
// test some manual iteration
name, err := iter.NextName()
nameIter := iter.Names()
name, err := nameIter.Next()
for err == nil {
list = append(list, name)
name, err = iter.NextName()
name, err = nameIter.Next()
}
if !IsErrorCode(err, ErrIterOver) {
t.Fatal("Iteration not over")
@ -135,28 +136,6 @@ func TestIterator(t *testing.T) {
t.Fatalf("Wrong number of references returned %v", count)
}
// test the channel iteration
list = []string{}
iter, err = repo.NewReferenceIterator()
for name := range iter.NameIter() {
list = append(list, name)
}
sort.Strings(list)
compareStringList(t, expected, list)
iter, err = repo.NewReferenceIteratorGlob("refs/heads/t*")
expected = []string{
"refs/heads/three",
"refs/heads/two",
}
list = []string{}
for name := range iter.NameIter() {
list = append(list, name)
}
compareStringList(t, expected, list)
}
func TestUtil(t *testing.T) {