From f6fa1a38abf7be7d4546b736e6db1ae7229e972f Mon Sep 17 00:00:00 2001 From: David Calavera Date: Fri, 24 Oct 2014 12:12:07 -0700 Subject: [PATCH 1/4] Add Remote#Ls. Allow filtering heads by name. --- remote.go | 72 +++++++++++++++++++++++++++++++++++++++++++++++--- remote_test.go | 43 ++++++++++++++++++++++++++++-- 2 files changed, 109 insertions(+), 6 deletions(-) diff --git a/remote.go b/remote.go index 2ce3be8..139e71b 100644 --- a/remote.go +++ b/remote.go @@ -9,9 +9,11 @@ extern void _go_git_setup_callbacks(git_remote_callbacks *callbacks); */ import "C" import ( - "unsafe" - "runtime" "crypto/x509" + "reflect" + "runtime" + "strings" + "unsafe" ) type TransferProgress struct { @@ -39,6 +41,8 @@ const ( RemoteCompletionDownload RemoteCompletion = C.GIT_REMOTE_COMPLETION_DOWNLOAD RemoteCompletionIndexing = C.GIT_REMOTE_COMPLETION_INDEXING RemoteCompletionError = C.GIT_REMOTE_COMPLETION_ERROR + + RemoteFetchDirection = C.GIT_DIRECTION_FETCH ) type TransportMessageCallback func(str string) int @@ -58,7 +62,7 @@ type RemoteCallbacks struct { } type Remote struct { - ptr *C.git_remote + ptr *C.git_remote callbacks RemoteCallbacks } @@ -95,6 +99,18 @@ type HostkeyCertificate struct { HashSHA1 [20]byte } +type RemoteHead struct { + Id *Oid + Name string +} + +func newRemoteHeadFromC(ptr *C.git_remote_head) RemoteHead { + return RemoteHead{ + Id: newOidFromC(&ptr.oid), + Name: C.GoString(ptr.name), + } +} + func populateRemoteCallbacks(ptr *C.git_remote_callbacks, callbacks *RemoteCallbacks) { C.git_remote_init_callbacks(ptr, C.GIT_REMOTE_CALLBACKS_VERSION) if callbacks == nil { @@ -195,7 +211,6 @@ func certificateCheckCallback(_cert *C.git_cert, _valid C.int, _host *C.char, da return -1 // we don't support anything else atm } - return callbacks.CertificateCheckCallback(&cert, valid, host) } @@ -548,3 +563,52 @@ func (o *Remote) Fetch(refspecs []string, sig *Signature, msg string) error { } return nil } + +func (o *Remote) Ls(filterRefs ...string) ([]RemoteHead, error) { + if ret := C.git_remote_connect(o.ptr, RemoteFetchDirection); ret != 0 { + return nil, MakeGitError(ret) + } + + var refs **C.git_remote_head + var length C.size_t + + if ret := C.git_remote_ls(&refs, &length, o.ptr); ret != 0 { + return nil, MakeGitError(ret) + } + + size := int(length) + + if size == 0 { + return make([]RemoteHead, 0), nil + } + + hdr := reflect.SliceHeader{ + Data: uintptr(unsafe.Pointer(refs)), + Len: size, + Cap: size, + } + + goSlice := *(*[]*C.git_remote_head)(unsafe.Pointer(&hdr)) + + var heads []RemoteHead + + if len(filterRefs) > 0 { + for _, s := range goSlice { + head := newRemoteHeadFromC(s) + + for _, r := range filterRefs { + if strings.Contains(head.Name, r) { + heads = append(heads, head) + break + } + } + } + } else { + heads = make([]RemoteHead, size) + for i, s := range goSlice { + heads[i] = newRemoteHeadFromC(s) + } + } + + return heads, nil +} diff --git a/remote_test.go b/remote_test.go index d1ad1e8..70e53ce 100644 --- a/remote_test.go +++ b/remote_test.go @@ -46,7 +46,6 @@ func TestListRemotes(t *testing.T) { compareStringList(t, expected, actual) } - func assertHostname(cert *Certificate, valid bool, hostname string, t *testing.T) int { if hostname != "github.com" { t.Fatal("Hostname does not match") @@ -65,7 +64,7 @@ func TestCertificateCheck(t *testing.T) { checkFatal(t, err) callbacks := RemoteCallbacks{ - CertificateCheckCallback: func (cert *Certificate, valid bool, hostname string) int { + CertificateCheckCallback: func(cert *Certificate, valid bool, hostname string) int { return assertHostname(cert, valid, hostname, t) }, } @@ -75,3 +74,43 @@ func TestCertificateCheck(t *testing.T) { err = remote.Fetch([]string{}, nil, "") checkFatal(t, err) } + +func TestRemoteLs(t *testing.T) { + repo := createTestRepo(t) + defer os.RemoveAll(repo.Workdir()) + defer repo.Free() + + remote, err := repo.CreateRemote("origin", "https://github.com/libgit2/TestGitRepository") + checkFatal(t, err) + + heads, err := remote.Ls() + checkFatal(t, err) + + if len(heads) == 0 { + t.Error("Expected remote heads") + } +} + +func TestRemoteLsFiltering(t *testing.T) { + repo := createTestRepo(t) + defer os.RemoveAll(repo.Workdir()) + defer repo.Free() + + remote, err := repo.CreateRemote("origin", "https://github.com/libgit2/TestGitRepository") + checkFatal(t, err) + + heads, err := remote.Ls("master") + checkFatal(t, err) + + if len(heads) != 1 { + t.Fatalf("Expected one head for master but I got %d", len(heads)) + } + + if heads[0].Id == nil { + t.Fatalf("Expected head to have an Id, but it's nil") + } + + if heads[0].Name == "" { + t.Fatalf("Expected head to have a name, but it's empty") + } +} -- 2.45.2 From d1b87efd96b30b8e1fc892a8c791e042676f9a38 Mon Sep 17 00:00:00 2001 From: David Calavera Date: Mon, 27 Oct 2014 08:29:42 -0700 Subject: [PATCH 2/4] Add connect methods to Remote. --- remote.go | 25 ++++++++++++++++++++----- remote_test.go | 18 ++++++++++++++++++ 2 files changed, 38 insertions(+), 5 deletions(-) diff --git a/remote.go b/remote.go index 139e71b..a3b4c6c 100644 --- a/remote.go +++ b/remote.go @@ -41,8 +41,8 @@ const ( RemoteCompletionDownload RemoteCompletion = C.GIT_REMOTE_COMPLETION_DOWNLOAD RemoteCompletionIndexing = C.GIT_REMOTE_COMPLETION_INDEXING RemoteCompletionError = C.GIT_REMOTE_COMPLETION_ERROR - - RemoteFetchDirection = C.GIT_DIRECTION_FETCH + RemoteDirectionFetch = C.GIT_DIRECTION_FETCH + RemoteDirectionPush = C.GIT_DIRECTION_PUSH ) type TransportMessageCallback func(str string) int @@ -564,10 +564,25 @@ func (o *Remote) Fetch(refspecs []string, sig *Signature, msg string) error { return nil } -func (o *Remote) Ls(filterRefs ...string) ([]RemoteHead, error) { - if ret := C.git_remote_connect(o.ptr, RemoteFetchDirection); ret != 0 { - return nil, MakeGitError(ret) +func (o *Remote) ConnectFetch() error { + return o.Connect(RemoteDirectionFetch) +} + +func (o *Remote) ConnectPush() error { + return o.Connect(RemoteDirectionPush) +} + +func (o *Remote) Connect(direction C.git_direction) error { + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + if ret := C.git_remote_connect(o.ptr, direction); ret != 0 { + return MakeGitError(ret) } + return nil +} + +func (o *Remote) Ls(filterRefs ...string) ([]RemoteHead, error) { var refs **C.git_remote_head var length C.size_t diff --git a/remote_test.go b/remote_test.go index 70e53ce..6d81df4 100644 --- a/remote_test.go +++ b/remote_test.go @@ -75,6 +75,18 @@ func TestCertificateCheck(t *testing.T) { checkFatal(t, err) } +func TestRemoteConnect(t *testing.T) { + repo := createTestRepo(t) + defer os.RemoveAll(repo.Workdir()) + defer repo.Free() + + remote, err := repo.CreateRemote("origin", "https://github.com/libgit2/TestGitRepository") + checkFatal(t, err) + + err = remote.ConnectFetch() + checkFatal(t, err) +} + func TestRemoteLs(t *testing.T) { repo := createTestRepo(t) defer os.RemoveAll(repo.Workdir()) @@ -83,6 +95,9 @@ func TestRemoteLs(t *testing.T) { remote, err := repo.CreateRemote("origin", "https://github.com/libgit2/TestGitRepository") checkFatal(t, err) + err = remote.ConnectFetch() + checkFatal(t, err) + heads, err := remote.Ls() checkFatal(t, err) @@ -99,6 +114,9 @@ func TestRemoteLsFiltering(t *testing.T) { remote, err := repo.CreateRemote("origin", "https://github.com/libgit2/TestGitRepository") checkFatal(t, err) + err = remote.ConnectFetch() + checkFatal(t, err) + heads, err := remote.Ls("master") checkFatal(t, err) -- 2.45.2 From e969b33b335f0cf797a7c21ac14ddcbdf8aedce3 Mon Sep 17 00:00:00 2001 From: David Calavera Date: Mon, 27 Oct 2014 08:32:50 -0700 Subject: [PATCH 3/4] Make filtering logic more simple. --- remote.go | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/remote.go b/remote.go index a3b4c6c..e360fc0 100644 --- a/remote.go +++ b/remote.go @@ -607,21 +607,18 @@ func (o *Remote) Ls(filterRefs ...string) ([]RemoteHead, error) { var heads []RemoteHead - if len(filterRefs) > 0 { - for _, s := range goSlice { - head := newRemoteHeadFromC(s) + for _, s := range goSlice { + head := newRemoteHeadFromC(s) + if len(filterRefs) > 0 { for _, r := range filterRefs { if strings.Contains(head.Name, r) { heads = append(heads, head) break } } - } - } else { - heads = make([]RemoteHead, size) - for i, s := range goSlice { - heads[i] = newRemoteHeadFromC(s) + } else { + heads = append(heads, head) } } -- 2.45.2 From d722c11f7f6eb85d287da6091cdc84d6f8ebe8fa Mon Sep 17 00:00:00 2001 From: David Calavera Date: Mon, 27 Oct 2014 09:08:05 -0700 Subject: [PATCH 4/4] Hide C.git_direction type. --- remote.go | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/remote.go b/remote.go index e360fc0..edb2b38 100644 --- a/remote.go +++ b/remote.go @@ -36,13 +36,15 @@ func newTransferProgressFromC(c *C.git_transfer_progress) TransferProgress { } type RemoteCompletion uint +type ConnectDirection uint const ( RemoteCompletionDownload RemoteCompletion = C.GIT_REMOTE_COMPLETION_DOWNLOAD RemoteCompletionIndexing = C.GIT_REMOTE_COMPLETION_INDEXING RemoteCompletionError = C.GIT_REMOTE_COMPLETION_ERROR - RemoteDirectionFetch = C.GIT_DIRECTION_FETCH - RemoteDirectionPush = C.GIT_DIRECTION_PUSH + + ConnectDirectionFetch ConnectDirection = C.GIT_DIRECTION_FETCH + ConnectDirectionPush = C.GIT_DIRECTION_PUSH ) type TransportMessageCallback func(str string) int @@ -565,18 +567,18 @@ func (o *Remote) Fetch(refspecs []string, sig *Signature, msg string) error { } func (o *Remote) ConnectFetch() error { - return o.Connect(RemoteDirectionFetch) + return o.Connect(ConnectDirectionFetch) } func (o *Remote) ConnectPush() error { - return o.Connect(RemoteDirectionPush) + return o.Connect(ConnectDirectionPush) } -func (o *Remote) Connect(direction C.git_direction) error { +func (o *Remote) Connect(direction ConnectDirection) error { runtime.LockOSThread() defer runtime.UnlockOSThread() - if ret := C.git_remote_connect(o.ptr, direction); ret != 0 { + if ret := C.git_remote_connect(o.ptr, C.git_direction(direction)); ret != 0 { return MakeGitError(ret) } return nil -- 2.45.2