diff --git a/remote.go b/remote.go index 2ce3be8..edb2b38 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 { @@ -34,11 +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 + + ConnectDirectionFetch ConnectDirection = C.GIT_DIRECTION_FETCH + ConnectDirectionPush = C.GIT_DIRECTION_PUSH ) type TransportMessageCallback func(str string) int @@ -58,7 +64,7 @@ type RemoteCallbacks struct { } type Remote struct { - ptr *C.git_remote + ptr *C.git_remote callbacks RemoteCallbacks } @@ -95,6 +101,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 +213,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 +565,64 @@ func (o *Remote) Fetch(refspecs []string, sig *Signature, msg string) error { } return nil } + +func (o *Remote) ConnectFetch() error { + return o.Connect(ConnectDirectionFetch) +} + +func (o *Remote) ConnectPush() error { + return o.Connect(ConnectDirectionPush) +} + +func (o *Remote) Connect(direction ConnectDirection) error { + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + if ret := C.git_remote_connect(o.ptr, C.git_direction(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 + + 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 + + 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 = append(heads, head) + } + } + + return heads, nil +} diff --git a/remote_test.go b/remote_test.go index d1ad1e8..6d81df4 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,61 @@ func TestCertificateCheck(t *testing.T) { err = remote.Fetch([]string{}, nil, "") 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()) + defer repo.Free() + + 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) + + 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) + + err = remote.ConnectFetch() + 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") + } +}