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 *RemoteCallbacks Bare bool CheckoutBranch string RemoteCreateCallback RemoteCreateCallback } func Clone(url string, path string, options *CloneOptions) (*Repository, error) { repo := new(Repository) 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() ret := C.git_clone(&repo.ptr, curl, cpath, copts) if ret < 0 { return nil, MakeGitError(ret) } runtime.SetFinalizer(repo, (*Repository).Free) return repo, 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 := Repository{(*C.git_repository)(crepo)} if opts, ok := pointerHandles.Get(payload).(CloneOptions); ok { remote, err := opts.RemoteCreateCallback(repo, name, url) if err == ErrOk && remote != nil { // clear finalizer as the calling C function will // free the remote itself runtime.SetFinalizer(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) populateRemoteCallbacks(&ptr.remote_callbacks, opts.RemoteCallbacks) 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)) }