diff options
Diffstat (limited to 'vendor/github.com/libgit2/git2go/remote.go')
-rw-r--r-- | vendor/github.com/libgit2/git2go/remote.go | 872 |
1 files changed, 872 insertions, 0 deletions
diff --git a/vendor/github.com/libgit2/git2go/remote.go b/vendor/github.com/libgit2/git2go/remote.go new file mode 100644 index 000000000..b4b1dd727 --- /dev/null +++ b/vendor/github.com/libgit2/git2go/remote.go @@ -0,0 +1,872 @@ +package git + +/* +#include <git2.h> +#include <string.h> + +extern void _go_git_setup_callbacks(git_remote_callbacks *callbacks); + +*/ +import "C" +import ( + "crypto/x509" + "reflect" + "runtime" + "strings" + "unsafe" +) + +type TransferProgress struct { + TotalObjects uint + IndexedObjects uint + ReceivedObjects uint + LocalObjects uint + TotalDeltas uint + ReceivedBytes uint +} + +func newTransferProgressFromC(c *C.git_transfer_progress) TransferProgress { + return TransferProgress{ + TotalObjects: uint(c.total_objects), + IndexedObjects: uint(c.indexed_objects), + ReceivedObjects: uint(c.received_objects), + LocalObjects: uint(c.local_objects), + TotalDeltas: uint(c.total_deltas), + ReceivedBytes: uint(c.received_bytes)} +} + +type RemoteCompletion uint +type ConnectDirection uint + +const ( + RemoteCompletionDownload RemoteCompletion = C.GIT_REMOTE_COMPLETION_DOWNLOAD + RemoteCompletionIndexing RemoteCompletion = C.GIT_REMOTE_COMPLETION_INDEXING + RemoteCompletionError RemoteCompletion = C.GIT_REMOTE_COMPLETION_ERROR + + ConnectDirectionFetch ConnectDirection = C.GIT_DIRECTION_FETCH + ConnectDirectionPush ConnectDirection = C.GIT_DIRECTION_PUSH +) + +type TransportMessageCallback func(str string) ErrorCode +type CompletionCallback func(RemoteCompletion) ErrorCode +type CredentialsCallback func(url string, username_from_url string, allowed_types CredType) (ErrorCode, *Cred) +type TransferProgressCallback func(stats TransferProgress) ErrorCode +type UpdateTipsCallback func(refname string, a *Oid, b *Oid) ErrorCode +type CertificateCheckCallback func(cert *Certificate, valid bool, hostname string) ErrorCode +type PackbuilderProgressCallback func(stage int32, current, total uint32) ErrorCode +type PushTransferProgressCallback func(current, total uint32, bytes uint) ErrorCode +type PushUpdateReferenceCallback func(refname, status string) ErrorCode + +type RemoteCallbacks struct { + SidebandProgressCallback TransportMessageCallback + CompletionCallback + CredentialsCallback + TransferProgressCallback + UpdateTipsCallback + CertificateCheckCallback + PackProgressCallback PackbuilderProgressCallback + PushTransferProgressCallback + PushUpdateReferenceCallback +} + +type FetchPrune uint + +const ( + // Use the setting from the configuration + FetchPruneUnspecified FetchPrune = C.GIT_FETCH_PRUNE_UNSPECIFIED + // Force pruning on + FetchPruneOn FetchPrune = C.GIT_FETCH_PRUNE + // Force pruning off + FetchNoPrune FetchPrune = C.GIT_FETCH_NO_PRUNE +) + +type DownloadTags uint + +const ( + + // Use the setting from the configuration. + DownloadTagsUnspecified DownloadTags = C.GIT_REMOTE_DOWNLOAD_TAGS_UNSPECIFIED + // Ask the server for tags pointing to objects we're already + // downloading. + DownloadTagsAuto DownloadTags = C.GIT_REMOTE_DOWNLOAD_TAGS_AUTO + + // Don't ask for any tags beyond the refspecs. + DownloadTagsNone DownloadTags = C.GIT_REMOTE_DOWNLOAD_TAGS_NONE + + // Ask for the all the tags. + DownloadTagsAll DownloadTags = C.GIT_REMOTE_DOWNLOAD_TAGS_ALL +) + +type FetchOptions struct { + // Callbacks to use for this fetch operation + RemoteCallbacks RemoteCallbacks + // Whether to perform a prune after the fetch + Prune FetchPrune + // Whether to write the results to FETCH_HEAD. Defaults to + // on. Leave this default in order to behave like git. + UpdateFetchhead bool + + // Determines how to behave regarding tags on the remote, such + // as auto-downloading tags for objects we're downloading or + // downloading all of them. + // + // The default is to auto-follow tags. + DownloadTags DownloadTags + + // Headers are extra headers for the fetch operation. + Headers []string +} + +type ProxyType uint + +const ( + // Do not attempt to connect through a proxy + // + // If built against lbicurl, it itself may attempt to connect + // to a proxy if the environment variables specify it. + ProxyTypeNone ProxyType = C.GIT_PROXY_NONE + + // Try to auto-detect the proxy from the git configuration. + ProxyTypeAuto ProxyType = C.GIT_PROXY_AUTO + + // Connect via the URL given in the options + ProxyTypeSpecified ProxyType = C.GIT_PROXY_SPECIFIED +) + +type ProxyOptions struct { + // The type of proxy to use (or none) + Type ProxyType + + // The proxy's URL + Url string +} + +type Remote struct { + ptr *C.git_remote + callbacks RemoteCallbacks + repo *Repository +} + +type CertificateKind uint + +const ( + CertificateX509 CertificateKind = C.GIT_CERT_X509 + CertificateHostkey CertificateKind = C.GIT_CERT_HOSTKEY_LIBSSH2 +) + +// Certificate represents the two possible certificates which libgit2 +// knows it might find. If Kind is CertficateX509 then the X509 field +// will be filled. If Kind is CertificateHostkey then the Hostkey +// field will be fille.d +type Certificate struct { + Kind CertificateKind + X509 *x509.Certificate + Hostkey HostkeyCertificate +} + +type HostkeyKind uint + +const ( + HostkeyMD5 HostkeyKind = C.GIT_CERT_SSH_MD5 + HostkeySHA1 HostkeyKind = C.GIT_CERT_SSH_SHA1 +) + +// Server host key information. If Kind is HostkeyMD5 the MD5 field +// will be filled. If Kind is HostkeySHA1, then HashSHA1 will be +// filled. +type HostkeyCertificate struct { + Kind HostkeyKind + HashMD5 [16]byte + HashSHA1 [20]byte +} + +type PushOptions struct { + // Callbacks to use for this push operation + RemoteCallbacks RemoteCallbacks + + PbParallelism uint + + // Headers are extra headers for the push operation. + Headers []string +} + +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 untrackCalbacksPayload(callbacks *C.git_remote_callbacks) { + if callbacks != nil && callbacks.payload != nil { + pointerHandles.Untrack(callbacks.payload) + } +} + +func populateRemoteCallbacks(ptr *C.git_remote_callbacks, callbacks *RemoteCallbacks) { + C.git_remote_init_callbacks(ptr, C.GIT_REMOTE_CALLBACKS_VERSION) + if callbacks == nil { + return + } + C._go_git_setup_callbacks(ptr) + ptr.payload = pointerHandles.Track(callbacks) +} + +//export sidebandProgressCallback +func sidebandProgressCallback(_str *C.char, _len C.int, data unsafe.Pointer) int { + callbacks := pointerHandles.Get(data).(*RemoteCallbacks) + if callbacks.SidebandProgressCallback == nil { + return 0 + } + str := C.GoStringN(_str, _len) + return int(callbacks.SidebandProgressCallback(str)) +} + +//export completionCallback +func completionCallback(completion_type C.git_remote_completion_type, data unsafe.Pointer) int { + callbacks := pointerHandles.Get(data).(*RemoteCallbacks) + if callbacks.CompletionCallback == nil { + return 0 + } + return int(callbacks.CompletionCallback(RemoteCompletion(completion_type))) +} + +//export credentialsCallback +func credentialsCallback(_cred **C.git_cred, _url *C.char, _username_from_url *C.char, allowed_types uint, data unsafe.Pointer) int { + callbacks, _ := pointerHandles.Get(data).(*RemoteCallbacks) + if callbacks.CredentialsCallback == nil { + return C.GIT_PASSTHROUGH + } + url := C.GoString(_url) + username_from_url := C.GoString(_username_from_url) + ret, cred := callbacks.CredentialsCallback(url, username_from_url, (CredType)(allowed_types)) + if cred != nil { + *_cred = cred.ptr + } + return int(ret) +} + +//export transferProgressCallback +func transferProgressCallback(stats *C.git_transfer_progress, data unsafe.Pointer) int { + callbacks, _ := pointerHandles.Get(data).(*RemoteCallbacks) + if callbacks.TransferProgressCallback == nil { + return 0 + } + return int(callbacks.TransferProgressCallback(newTransferProgressFromC(stats))) +} + +//export updateTipsCallback +func updateTipsCallback(_refname *C.char, _a *C.git_oid, _b *C.git_oid, data unsafe.Pointer) int { + callbacks, _ := pointerHandles.Get(data).(*RemoteCallbacks) + if callbacks.UpdateTipsCallback == nil { + return 0 + } + refname := C.GoString(_refname) + a := newOidFromC(_a) + b := newOidFromC(_b) + return int(callbacks.UpdateTipsCallback(refname, a, b)) +} + +//export certificateCheckCallback +func certificateCheckCallback(_cert *C.git_cert, _valid C.int, _host *C.char, data unsafe.Pointer) int { + callbacks, _ := pointerHandles.Get(data).(*RemoteCallbacks) + // if there's no callback set, we need to make sure we fail if the library didn't consider this cert valid + if callbacks.CertificateCheckCallback == nil { + if _valid == 1 { + return 0 + } else { + return C.GIT_ECERTIFICATE + } + } + host := C.GoString(_host) + valid := _valid != 0 + + var cert Certificate + if _cert.cert_type == C.GIT_CERT_X509 { + cert.Kind = CertificateX509 + ccert := (*C.git_cert_x509)(unsafe.Pointer(_cert)) + x509_certs, err := x509.ParseCertificates(C.GoBytes(ccert.data, C.int(ccert.len))) + if err != nil { + return C.GIT_EUSER + } + + // we assume there's only one, which should hold true for any web server we want to talk to + cert.X509 = x509_certs[0] + } else if _cert.cert_type == C.GIT_CERT_HOSTKEY_LIBSSH2 { + cert.Kind = CertificateHostkey + ccert := (*C.git_cert_hostkey)(unsafe.Pointer(_cert)) + cert.Hostkey.Kind = HostkeyKind(ccert._type) + C.memcpy(unsafe.Pointer(&cert.Hostkey.HashMD5[0]), unsafe.Pointer(&ccert.hash_md5[0]), C.size_t(len(cert.Hostkey.HashMD5))) + C.memcpy(unsafe.Pointer(&cert.Hostkey.HashSHA1[0]), unsafe.Pointer(&ccert.hash_sha1[0]), C.size_t(len(cert.Hostkey.HashSHA1))) + } else { + cstr := C.CString("Unsupported certificate type") + C.giterr_set_str(C.GITERR_NET, cstr) + C.free(unsafe.Pointer(cstr)) + return -1 // we don't support anything else atm + } + + return int(callbacks.CertificateCheckCallback(&cert, valid, host)) +} + +//export packProgressCallback +func packProgressCallback(stage C.int, current, total C.uint, data unsafe.Pointer) int { + callbacks, _ := pointerHandles.Get(data).(*RemoteCallbacks) + + if callbacks.PackProgressCallback == nil { + return 0 + } + + return int(callbacks.PackProgressCallback(int32(stage), uint32(current), uint32(total))) +} + +//export pushTransferProgressCallback +func pushTransferProgressCallback(current, total C.uint, bytes C.size_t, data unsafe.Pointer) int { + callbacks, _ := pointerHandles.Get(data).(*RemoteCallbacks) + if callbacks.PushTransferProgressCallback == nil { + return 0 + } + + return int(callbacks.PushTransferProgressCallback(uint32(current), uint32(total), uint(bytes))) +} + +//export pushUpdateReferenceCallback +func pushUpdateReferenceCallback(refname, status *C.char, data unsafe.Pointer) int { + callbacks, _ := pointerHandles.Get(data).(*RemoteCallbacks) + + if callbacks.PushUpdateReferenceCallback == nil { + return 0 + } + + return int(callbacks.PushUpdateReferenceCallback(C.GoString(refname), C.GoString(status))) +} + +func populateProxyOptions(ptr *C.git_proxy_options, opts *ProxyOptions) { + C.git_proxy_init_options(ptr, C.GIT_PROXY_OPTIONS_VERSION) + if opts == nil { + return + } + + ptr._type = C.git_proxy_t(opts.Type) + ptr.url = C.CString(opts.Url) +} + +func freeProxyOptions(ptr *C.git_proxy_options) { + C.free(unsafe.Pointer(ptr.url)) +} + +func RemoteIsValidName(name string) bool { + cname := C.CString(name) + defer C.free(unsafe.Pointer(cname)) + if C.git_remote_is_valid_name(cname) == 1 { + return true + } + return false +} + +func (r *Remote) Free() { + runtime.SetFinalizer(r, nil) + C.git_remote_free(r.ptr) + r.ptr = nil +} + +type RemoteCollection struct { + repo *Repository +} + +func (c *RemoteCollection) List() ([]string, error) { + var r C.git_strarray + + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + ecode := C.git_remote_list(&r, c.repo.ptr) + runtime.KeepAlive(c.repo) + if ecode < 0 { + return nil, MakeGitError(ecode) + } + defer C.git_strarray_free(&r) + + remotes := makeStringsFromCStrings(r.strings, int(r.count)) + return remotes, nil +} + +func (c *RemoteCollection) Create(name string, url string) (*Remote, error) { + remote := &Remote{repo: c.repo} + + cname := C.CString(name) + defer C.free(unsafe.Pointer(cname)) + curl := C.CString(url) + defer C.free(unsafe.Pointer(curl)) + + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + ret := C.git_remote_create(&remote.ptr, c.repo.ptr, cname, curl) + if ret < 0 { + return nil, MakeGitError(ret) + } + runtime.SetFinalizer(remote, (*Remote).Free) + return remote, nil +} + +func (c *RemoteCollection) Delete(name string) error { + cname := C.CString(name) + defer C.free(unsafe.Pointer(cname)) + + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + ret := C.git_remote_delete(c.repo.ptr, cname) + runtime.KeepAlive(c.repo) + if ret < 0 { + return MakeGitError(ret) + } + return nil +} + +func (c *RemoteCollection) CreateWithFetchspec(name string, url string, fetch string) (*Remote, error) { + remote := &Remote{repo: c.repo} + + cname := C.CString(name) + defer C.free(unsafe.Pointer(cname)) + curl := C.CString(url) + defer C.free(unsafe.Pointer(curl)) + cfetch := C.CString(fetch) + defer C.free(unsafe.Pointer(cfetch)) + + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + ret := C.git_remote_create_with_fetchspec(&remote.ptr, c.repo.ptr, cname, curl, cfetch) + if ret < 0 { + return nil, MakeGitError(ret) + } + runtime.SetFinalizer(remote, (*Remote).Free) + return remote, nil +} + +func (c *RemoteCollection) CreateAnonymous(url string) (*Remote, error) { + remote := &Remote{repo: c.repo} + + curl := C.CString(url) + defer C.free(unsafe.Pointer(curl)) + + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + ret := C.git_remote_create_anonymous(&remote.ptr, c.repo.ptr, curl) + if ret < 0 { + return nil, MakeGitError(ret) + } + runtime.SetFinalizer(remote, (*Remote).Free) + return remote, nil +} + +func (c *RemoteCollection) Lookup(name string) (*Remote, error) { + remote := &Remote{repo: c.repo} + + cname := C.CString(name) + defer C.free(unsafe.Pointer(cname)) + + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + ret := C.git_remote_lookup(&remote.ptr, c.repo.ptr, cname) + if ret < 0 { + return nil, MakeGitError(ret) + } + runtime.SetFinalizer(remote, (*Remote).Free) + return remote, nil +} + +func (o *Remote) Name() string { + s := C.git_remote_name(o.ptr) + runtime.KeepAlive(o) + return C.GoString(s) +} + +func (o *Remote) Url() string { + s := C.git_remote_url(o.ptr) + runtime.KeepAlive(o) + return C.GoString(s) +} + +func (o *Remote) PushUrl() string { + s := C.git_remote_pushurl(o.ptr) + runtime.KeepAlive(o) + return C.GoString(s) +} + +func (c *RemoteCollection) Rename(remote, newname string) ([]string, error) { + cproblems := C.git_strarray{} + defer freeStrarray(&cproblems) + cnewname := C.CString(newname) + defer C.free(unsafe.Pointer(cnewname)) + cremote := C.CString(remote) + defer C.free(unsafe.Pointer(cremote)) + + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + ret := C.git_remote_rename(&cproblems, c.repo.ptr, cremote, cnewname) + runtime.KeepAlive(c.repo) + if ret < 0 { + return []string{}, MakeGitError(ret) + } + + problems := makeStringsFromCStrings(cproblems.strings, int(cproblems.count)) + return problems, nil +} + +func (c *RemoteCollection) SetUrl(remote, url string) error { + curl := C.CString(url) + defer C.free(unsafe.Pointer(curl)) + cremote := C.CString(remote) + defer C.free(unsafe.Pointer(cremote)) + + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + ret := C.git_remote_set_url(c.repo.ptr, cremote, curl) + runtime.KeepAlive(c.repo) + if ret < 0 { + return MakeGitError(ret) + } + return nil +} + +func (c *RemoteCollection) SetPushUrl(remote, url string) error { + curl := C.CString(url) + defer C.free(unsafe.Pointer(curl)) + cremote := C.CString(remote) + defer C.free(unsafe.Pointer(cremote)) + + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + ret := C.git_remote_set_pushurl(c.repo.ptr, cremote, curl) + runtime.KeepAlive(c.repo) + if ret < 0 { + return MakeGitError(ret) + } + return nil +} + +func (c *RemoteCollection) AddFetch(remote, refspec string) error { + crefspec := C.CString(refspec) + defer C.free(unsafe.Pointer(crefspec)) + cremote := C.CString(remote) + defer C.free(unsafe.Pointer(cremote)) + + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + ret := C.git_remote_add_fetch(c.repo.ptr, cremote, crefspec) + runtime.KeepAlive(c.repo) + if ret < 0 { + return MakeGitError(ret) + } + return nil +} + +func sptr(p uintptr) *C.char { + return *(**C.char)(unsafe.Pointer(p)) +} + +func makeStringsFromCStrings(x **C.char, l int) []string { + s := make([]string, l) + i := 0 + for p := uintptr(unsafe.Pointer(x)); i < l; p += unsafe.Sizeof(uintptr(0)) { + s[i] = C.GoString(sptr(p)) + i++ + } + return s +} + +func makeCStringsFromStrings(s []string) **C.char { + l := len(s) + x := (**C.char)(C.malloc(C.size_t(unsafe.Sizeof(unsafe.Pointer(nil)) * uintptr(l)))) + i := 0 + for p := uintptr(unsafe.Pointer(x)); i < l; p += unsafe.Sizeof(uintptr(0)) { + *(**C.char)(unsafe.Pointer(p)) = C.CString(s[i]) + i++ + } + return x +} + +func freeStrarray(arr *C.git_strarray) { + count := int(arr.count) + size := unsafe.Sizeof(unsafe.Pointer(nil)) + + i := 0 + for p := uintptr(unsafe.Pointer(arr.strings)); i < count; p += size { + C.free(unsafe.Pointer(sptr(p))) + i++ + } + + C.free(unsafe.Pointer(arr.strings)) +} + +func (o *Remote) FetchRefspecs() ([]string, error) { + crefspecs := C.git_strarray{} + + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + ret := C.git_remote_get_fetch_refspecs(&crefspecs, o.ptr) + runtime.KeepAlive(o) + if ret < 0 { + return nil, MakeGitError(ret) + } + defer C.git_strarray_free(&crefspecs) + + refspecs := makeStringsFromCStrings(crefspecs.strings, int(crefspecs.count)) + return refspecs, nil +} + +func (c *RemoteCollection) AddPush(remote, refspec string) error { + crefspec := C.CString(refspec) + defer C.free(unsafe.Pointer(crefspec)) + cremote := C.CString(remote) + defer C.free(unsafe.Pointer(cremote)) + + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + ret := C.git_remote_add_push(c.repo.ptr, cremote, crefspec) + runtime.KeepAlive(c.repo) + if ret < 0 { + return MakeGitError(ret) + } + return nil +} + +func (o *Remote) PushRefspecs() ([]string, error) { + crefspecs := C.git_strarray{} + + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + ret := C.git_remote_get_push_refspecs(&crefspecs, o.ptr) + if ret < 0 { + return nil, MakeGitError(ret) + } + defer C.git_strarray_free(&crefspecs) + runtime.KeepAlive(o) + + refspecs := makeStringsFromCStrings(crefspecs.strings, int(crefspecs.count)) + return refspecs, nil +} + +func (o *Remote) RefspecCount() uint { + count := C.git_remote_refspec_count(o.ptr) + runtime.KeepAlive(o) + return uint(count) +} + +func populateFetchOptions(options *C.git_fetch_options, opts *FetchOptions) { + C.git_fetch_init_options(options, C.GIT_FETCH_OPTIONS_VERSION) + if opts == nil { + return + } + populateRemoteCallbacks(&options.callbacks, &opts.RemoteCallbacks) + options.prune = C.git_fetch_prune_t(opts.Prune) + options.update_fetchhead = cbool(opts.UpdateFetchhead) + options.download_tags = C.git_remote_autotag_option_t(opts.DownloadTags) + + options.custom_headers = C.git_strarray{} + options.custom_headers.count = C.size_t(len(opts.Headers)) + options.custom_headers.strings = makeCStringsFromStrings(opts.Headers) +} + +func populatePushOptions(options *C.git_push_options, opts *PushOptions) { + C.git_push_init_options(options, C.GIT_PUSH_OPTIONS_VERSION) + if opts == nil { + return + } + + options.pb_parallelism = C.uint(opts.PbParallelism) + + options.custom_headers = C.git_strarray{} + options.custom_headers.count = C.size_t(len(opts.Headers)) + options.custom_headers.strings = makeCStringsFromStrings(opts.Headers) + + populateRemoteCallbacks(&options.callbacks, &opts.RemoteCallbacks) +} + +// Fetch performs a fetch operation. refspecs specifies which refspecs +// to use for this fetch, use an empty list to use the refspecs from +// the configuration; msg specifies what to use for the reflog +// entries. Leave "" to use defaults. +func (o *Remote) Fetch(refspecs []string, opts *FetchOptions, msg string) error { + var cmsg *C.char = nil + if msg != "" { + cmsg = C.CString(msg) + defer C.free(unsafe.Pointer(cmsg)) + } + + crefspecs := C.git_strarray{} + crefspecs.count = C.size_t(len(refspecs)) + crefspecs.strings = makeCStringsFromStrings(refspecs) + defer freeStrarray(&crefspecs) + + coptions := (*C.git_fetch_options)(C.calloc(1, C.size_t(unsafe.Sizeof(C.git_fetch_options{})))) + defer C.free(unsafe.Pointer(coptions)) + + populateFetchOptions(coptions, opts) + defer untrackCalbacksPayload(&coptions.callbacks) + defer freeStrarray(&coptions.custom_headers) + + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + ret := C.git_remote_fetch(o.ptr, &crefspecs, coptions, cmsg) + runtime.KeepAlive(o) + if ret < 0 { + return MakeGitError(ret) + } + return nil +} + +func (o *Remote) ConnectFetch(callbacks *RemoteCallbacks, proxyOpts *ProxyOptions, headers []string) error { + return o.Connect(ConnectDirectionFetch, callbacks, proxyOpts, headers) +} + +func (o *Remote) ConnectPush(callbacks *RemoteCallbacks, proxyOpts *ProxyOptions, headers []string) error { + return o.Connect(ConnectDirectionPush, callbacks, proxyOpts, headers) +} + +// Connect opens a connection to a remote. +// +// The transport is selected based on the URL. The direction argument +// is due to a limitation of the git protocol (over TCP or SSH) which +// starts up a specific binary which can only do the one or the other. +// +// 'headers' are extra HTTP headers to use in this connection. +func (o *Remote) Connect(direction ConnectDirection, callbacks *RemoteCallbacks, proxyOpts *ProxyOptions, headers []string) error { + var ccallbacks C.git_remote_callbacks + populateRemoteCallbacks(&ccallbacks, callbacks) + + var cproxy C.git_proxy_options + populateProxyOptions(&cproxy, proxyOpts) + defer freeProxyOptions(&cproxy) + + cheaders := C.git_strarray{} + cheaders.count = C.size_t(len(headers)) + cheaders.strings = makeCStringsFromStrings(headers) + defer freeStrarray(&cheaders) + + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + ret := C.git_remote_connect(o.ptr, C.git_direction(direction), &ccallbacks, &cproxy, &cheaders) + runtime.KeepAlive(o) + if ret != 0 { + return MakeGitError(ret) + } + return nil +} + +func (o *Remote) Disconnect() { + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + C.git_remote_disconnect(o.ptr) + runtime.KeepAlive(o) +} + +func (o *Remote) Ls(filterRefs ...string) ([]RemoteHead, error) { + + var refs **C.git_remote_head + var length C.size_t + + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + ret := C.git_remote_ls(&refs, &length, o.ptr) + runtime.KeepAlive(o) + if 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 +} + +func (o *Remote) Push(refspecs []string, opts *PushOptions) error { + crefspecs := C.git_strarray{} + crefspecs.count = C.size_t(len(refspecs)) + crefspecs.strings = makeCStringsFromStrings(refspecs) + defer freeStrarray(&crefspecs) + + coptions := (*C.git_push_options)(C.calloc(1, C.size_t(unsafe.Sizeof(C.git_push_options{})))) + defer C.free(unsafe.Pointer(coptions)) + + populatePushOptions(coptions, opts) + defer untrackCalbacksPayload(&coptions.callbacks) + defer freeStrarray(&coptions.custom_headers) + + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + ret := C.git_remote_push(o.ptr, &crefspecs, coptions) + runtime.KeepAlive(o) + if ret < 0 { + return MakeGitError(ret) + } + return nil +} + +func (o *Remote) PruneRefs() bool { + return C.git_remote_prune_refs(o.ptr) > 0 +} + +func (o *Remote) Prune(callbacks *RemoteCallbacks) error { + var ccallbacks C.git_remote_callbacks + populateRemoteCallbacks(&ccallbacks, callbacks) + + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + ret := C.git_remote_prune(o.ptr, &ccallbacks) + runtime.KeepAlive(o) + if ret < 0 { + return MakeGitError(ret) + } + return nil +} |