package git /* #include extern void _go_git_populate_remote_cb(git_clone_options *opts); */ import "C" import ( "runtime" "unsafe" ) type RemoteCreateCallback func(repo *Repository, name, url string) (*Remote, ErrorCode) type CloneOptions struct { *CheckoutOpts *FetchOptions Bare bool CheckoutBranch string RemoteCreateCallback RemoteCreateCallback } func Clone(url string, path string, options *CloneOptions) (*Repository, error) { curl := C.CString(url) defer C.free(unsafe.Pointer(curl)) cpath := C.CString(path) defer C.free(unsafe.Pointer(cpath)) copts := (*C.git_clone_options)(C.calloc(1, C.size_t(unsafe.Sizeof(C.git_clone_options{})))) populateCloneOptions(copts, options) defer freeCloneOptions(copts) if len(options.CheckoutBranch) != 0 { copts.checkout_branch = C.CString(options.CheckoutBranch) } runtime.LockOSThread() defer runtime.UnlockOSThread() var ptr *C.git_repository ret := C.git_clone(&ptr, curl, cpath, copts) freeCheckoutOpts(&copts.checkout_opts) if ret < 0 { return nil, MakeGitError(ret) } return newRepositoryFromC(ptr), nil } //export remoteCreateCallback func remoteCreateCallback(cremote unsafe.Pointer, crepo unsafe.Pointer, cname, curl *C.char, payload unsafe.Pointer) C.int { name := C.GoString(cname) url := C.GoString(curl) repo := newRepositoryFromC((*C.git_repository)(crepo)) // We don't own this repository, so make sure we don't try to free it runtime.SetFinalizer(repo, nil) if opts, ok := pointerHandles.Get(payload).(CloneOptions); ok { remote, err := opts.RemoteCreateCallback(repo, name, url) // clear finalizer as the calling C function will // free the remote itself runtime.SetFinalizer(remote, nil) if err == ErrOk && remote != nil { cptr := (**C.git_remote)(cremote) *cptr = remote.ptr } else if err == ErrOk && remote == nil { panic("no remote created by callback") } return C.int(err) } else { panic("invalid remote create callback") } } func populateCloneOptions(ptr *C.git_clone_options, opts *CloneOptions) { C.git_clone_init_options(ptr, C.GIT_CLONE_OPTIONS_VERSION) if opts == nil { return } populateCheckoutOpts(&ptr.checkout_opts, opts.CheckoutOpts) populateFetchOptions(&ptr.fetch_opts, opts.FetchOptions) ptr.bare = cbool(opts.Bare) if opts.RemoteCreateCallback != nil { // Go v1.1 does not allow to assign a C function pointer C._go_git_populate_remote_cb(ptr) ptr.remote_cb_payload = pointerHandles.Track(*opts) } } func freeCloneOptions(ptr *C.git_clone_options) { if ptr == nil { return } freeCheckoutOpts(&ptr.checkout_opts) if ptr.remote_cb_payload != nil { pointerHandles.Untrack(ptr.remote_cb_payload) } C.free(unsafe.Pointer(ptr.checkout_branch)) C.free(unsafe.Pointer(ptr)) }