Refactor all callbacks (#700) (#703)

This change is a preparation for another change that makes all callback
types return a Go error instead of an error code / an integer. That is
going to make make things a lot more idiomatic.

The reason this change is split is threefold:

a) This change is mostly mechanical and should contain no semantic
   changes.
b) This change is backwards-compatible (in the Go API compatibility
   sense of the word), and thus can be backported to all other releases.
c) It makes the other change a bit smaller and more focused on just one
   thing.

Concretely, this change makes all callbacks populate a Go error when
they fail. If the callback is invoked from the same stack as the
function to which it was passed (e.g. for `Tree.Walk`), it will preserve
the error object directly into a struct that also holds the callback
function. Otherwise if the callback is pased to one func and will be
invoked when run from another one (e.g. for `Repository.InitRebase`),
the error string is saved into the libgit2 thread-local storage and then
re-created as a `GitError`.

(cherry picked from commit 5d8eaf7e65)

Co-authored-by: lhchavez <lhchavez@lhchavez.com>
This commit is contained in:
github-actions[bot] 2020-12-05 16:23:04 -08:00 committed by GitHub
parent a54915d90b
commit b7d6ab837c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 1161 additions and 586 deletions

View File

@ -3,10 +3,11 @@ package git
/* /*
#include <git2.h> #include <git2.h>
extern void _go_git_populate_checkout_cb(git_checkout_options *opts); extern void _go_git_populate_checkout_callbacks(git_checkout_options *opts);
*/ */
import "C" import "C"
import ( import (
"errors"
"os" "os"
"runtime" "runtime"
"unsafe" "unsafe"
@ -75,10 +76,10 @@ func checkoutOptionsFromC(c *C.git_checkout_options) CheckoutOptions {
NotifyFlags: CheckoutNotifyType(c.notify_flags), NotifyFlags: CheckoutNotifyType(c.notify_flags),
} }
if c.notify_payload != nil { if c.notify_payload != nil {
opts.NotifyCallback = pointerHandles.Get(c.notify_payload).(*CheckoutOptions).NotifyCallback opts.NotifyCallback = pointerHandles.Get(c.notify_payload).(*checkoutCallbackData).options.NotifyCallback
} }
if c.progress_payload != nil { if c.progress_payload != nil {
opts.ProgressCallback = pointerHandles.Get(c.progress_payload).(*CheckoutOptions).ProgressCallback opts.ProgressCallback = pointerHandles.Get(c.progress_payload).(*checkoutCallbackData).options.ProgressCallback
} }
if c.target_directory != nil { if c.target_directory != nil {
opts.TargetDirectory = C.GoString(c.target_directory) opts.TargetDirectory = C.GoString(c.target_directory)
@ -86,19 +87,26 @@ func checkoutOptionsFromC(c *C.git_checkout_options) CheckoutOptions {
return opts return opts
} }
func (opts *CheckoutOptions) toC() *C.git_checkout_options { func (opts *CheckoutOptions) toC(errorTarget *error) *C.git_checkout_options {
if opts == nil { if opts == nil {
return nil return nil
} }
c := C.git_checkout_options{} return populateCheckoutOptions(&C.git_checkout_options{}, opts, errorTarget)
populateCheckoutOptions(&c, opts) }
return &c
type checkoutCallbackData struct {
options *CheckoutOptions
errorTarget *error
} }
//export checkoutNotifyCallback //export checkoutNotifyCallback
func checkoutNotifyCallback(why C.git_checkout_notify_t, cpath *C.char, cbaseline, ctarget, cworkdir, data unsafe.Pointer) int { func checkoutNotifyCallback(
if data == nil { why C.git_checkout_notify_t,
return 0 cpath *C.char,
cbaseline, ctarget, cworkdir, handle unsafe.Pointer,
) C.int {
if handle == nil {
return C.int(ErrorCodeOK)
} }
path := C.GoString(cpath) path := C.GoString(cpath)
var baseline, target, workdir DiffFile var baseline, target, workdir DiffFile
@ -111,26 +119,35 @@ func checkoutNotifyCallback(why C.git_checkout_notify_t, cpath *C.char, cbaselin
if cworkdir != nil { if cworkdir != nil {
workdir = diffFileFromC((*C.git_diff_file)(cworkdir)) workdir = diffFileFromC((*C.git_diff_file)(cworkdir))
} }
opts := pointerHandles.Get(data).(*CheckoutOptions) data := pointerHandles.Get(handle).(*checkoutCallbackData)
if opts.NotifyCallback == nil { if data.options.NotifyCallback == nil {
return 0 return C.int(ErrorCodeOK)
} }
return int(opts.NotifyCallback(CheckoutNotifyType(why), path, baseline, target, workdir)) ret := data.options.NotifyCallback(CheckoutNotifyType(why), path, baseline, target, workdir)
if ret < 0 {
*data.errorTarget = errors.New(ErrorCode(ret).String())
return C.int(ErrorCodeUser)
}
return C.int(ErrorCodeOK)
} }
//export checkoutProgressCallback //export checkoutProgressCallback
func checkoutProgressCallback(path *C.char, completed_steps, total_steps C.size_t, data unsafe.Pointer) int { func checkoutProgressCallback(
opts := pointerHandles.Get(data).(*CheckoutOptions) path *C.char,
if opts.ProgressCallback == nil { completed_steps, total_steps C.size_t,
return 0 handle unsafe.Pointer,
) {
data := pointerHandles.Get(handle).(*checkoutCallbackData)
if data.options.ProgressCallback == nil {
return
} }
return int(opts.ProgressCallback(C.GoString(path), uint(completed_steps), uint(total_steps))) data.options.ProgressCallback(C.GoString(path), uint(completed_steps), uint(total_steps))
} }
// Convert the CheckoutOptions struct to the corresponding // Convert the CheckoutOptions struct to the corresponding
// C-struct. Returns a pointer to ptr, or nil if opts is nil, in order // C-struct. Returns a pointer to ptr, or nil if opts is nil, in order
// to help with what to pass. // to help with what to pass.
func populateCheckoutOptions(ptr *C.git_checkout_options, opts *CheckoutOptions) *C.git_checkout_options { func populateCheckoutOptions(ptr *C.git_checkout_options, opts *CheckoutOptions, errorTarget *error) *C.git_checkout_options {
if opts == nil { if opts == nil {
return nil return nil
} }
@ -142,15 +159,19 @@ func populateCheckoutOptions(ptr *C.git_checkout_options, opts *CheckoutOptions)
ptr.file_mode = C.uint(opts.FileMode.Perm()) ptr.file_mode = C.uint(opts.FileMode.Perm())
ptr.notify_flags = C.uint(opts.NotifyFlags) ptr.notify_flags = C.uint(opts.NotifyFlags)
if opts.NotifyCallback != nil || opts.ProgressCallback != nil { if opts.NotifyCallback != nil || opts.ProgressCallback != nil {
C._go_git_populate_checkout_cb(ptr) C._go_git_populate_checkout_callbacks(ptr)
data := &checkoutCallbackData{
options: opts,
errorTarget: errorTarget,
} }
payload := pointerHandles.Track(opts) payload := pointerHandles.Track(data)
if opts.NotifyCallback != nil { if opts.NotifyCallback != nil {
ptr.notify_payload = payload ptr.notify_payload = payload
} }
if opts.ProgressCallback != nil { if opts.ProgressCallback != nil {
ptr.progress_payload = payload ptr.progress_payload = payload
} }
}
if opts.TargetDirectory != "" { if opts.TargetDirectory != "" {
ptr.target_directory = C.CString(opts.TargetDirectory) ptr.target_directory = C.CString(opts.TargetDirectory)
} }
@ -176,6 +197,8 @@ func freeCheckoutOptions(ptr *C.git_checkout_options) {
} }
if ptr.notify_payload != nil { if ptr.notify_payload != nil {
pointerHandles.Untrack(ptr.notify_payload) pointerHandles.Untrack(ptr.notify_payload)
} else if ptr.progress_payload != nil {
pointerHandles.Untrack(ptr.progress_payload)
} }
} }
@ -185,11 +208,16 @@ func (v *Repository) CheckoutHead(opts *CheckoutOptions) error {
runtime.LockOSThread() runtime.LockOSThread()
defer runtime.UnlockOSThread() defer runtime.UnlockOSThread()
cOpts := opts.toC() var err error
cOpts := opts.toC(&err)
defer freeCheckoutOptions(cOpts) defer freeCheckoutOptions(cOpts)
ret := C.git_checkout_head(v.ptr, cOpts) ret := C.git_checkout_head(v.ptr, cOpts)
runtime.KeepAlive(v) runtime.KeepAlive(v)
if ret == C.int(ErrorCodeUser) && err != nil {
return err
}
if ret < 0 { if ret < 0 {
return MakeGitError(ret) return MakeGitError(ret)
} }
@ -209,11 +237,15 @@ func (v *Repository) CheckoutIndex(index *Index, opts *CheckoutOptions) error {
runtime.LockOSThread() runtime.LockOSThread()
defer runtime.UnlockOSThread() defer runtime.UnlockOSThread()
cOpts := opts.toC() var err error
cOpts := opts.toC(&err)
defer freeCheckoutOptions(cOpts) defer freeCheckoutOptions(cOpts)
ret := C.git_checkout_index(v.ptr, iptr, cOpts) ret := C.git_checkout_index(v.ptr, iptr, cOpts)
runtime.KeepAlive(v) runtime.KeepAlive(v)
if ret == C.int(ErrorCodeUser) && err != nil {
return err
}
if ret < 0 { if ret < 0 {
return MakeGitError(ret) return MakeGitError(ret)
} }
@ -225,11 +257,16 @@ func (v *Repository) CheckoutTree(tree *Tree, opts *CheckoutOptions) error {
runtime.LockOSThread() runtime.LockOSThread()
defer runtime.UnlockOSThread() defer runtime.UnlockOSThread()
cOpts := opts.toC() var err error
cOpts := opts.toC(&err)
defer freeCheckoutOptions(cOpts) defer freeCheckoutOptions(cOpts)
ret := C.git_checkout_tree(v.ptr, tree.ptr, cOpts) ret := C.git_checkout_tree(v.ptr, tree.ptr, cOpts)
runtime.KeepAlive(v) runtime.KeepAlive(v)
runtime.KeepAlive(tree)
if ret == C.int(ErrorCodeUser) && err != nil {
return err
}
if ret < 0 { if ret < 0 {
return MakeGitError(ret) return MakeGitError(ret)
} }

View File

@ -25,7 +25,7 @@ func cherrypickOptionsFromC(c *C.git_cherrypick_options) CherrypickOptions {
return opts return opts
} }
func (opts *CherrypickOptions) toC() *C.git_cherrypick_options { func (opts *CherrypickOptions) toC(errorTarget *error) *C.git_cherrypick_options {
if opts == nil { if opts == nil {
return nil return nil
} }
@ -33,7 +33,7 @@ func (opts *CherrypickOptions) toC() *C.git_cherrypick_options {
c.version = C.uint(opts.Version) c.version = C.uint(opts.Version)
c.mainline = C.uint(opts.Mainline) c.mainline = C.uint(opts.Mainline)
c.merge_opts = *opts.MergeOpts.toC() c.merge_opts = *opts.MergeOpts.toC()
c.checkout_opts = *opts.CheckoutOpts.toC() c.checkout_opts = *opts.CheckoutOpts.toC(errorTarget)
return &c return &c
} }
@ -41,6 +41,7 @@ func freeCherrypickOpts(ptr *C.git_cherrypick_options) {
if ptr == nil { if ptr == nil {
return return
} }
freeMergeOptions(&ptr.merge_opts)
freeCheckoutOptions(&ptr.checkout_opts) freeCheckoutOptions(&ptr.checkout_opts)
} }
@ -62,14 +63,18 @@ func (v *Repository) Cherrypick(commit *Commit, opts CherrypickOptions) error {
runtime.LockOSThread() runtime.LockOSThread()
defer runtime.UnlockOSThread() defer runtime.UnlockOSThread()
cOpts := opts.toC() var err error
cOpts := opts.toC(&err)
defer freeCherrypickOpts(cOpts) defer freeCherrypickOpts(cOpts)
ecode := C.git_cherrypick(v.ptr, commit.cast_ptr, cOpts) ret := C.git_cherrypick(v.ptr, commit.cast_ptr, cOpts)
runtime.KeepAlive(v) runtime.KeepAlive(v)
runtime.KeepAlive(commit) runtime.KeepAlive(commit)
if ecode < 0 { if ret == C.int(ErrorCodeUser) && err != nil {
return MakeGitError(ecode) return err
}
if ret < 0 {
return MakeGitError(ret)
} }
return nil return nil
} }
@ -79,6 +84,7 @@ func (r *Repository) CherrypickCommit(pick, our *Commit, opts CherrypickOptions)
defer runtime.UnlockOSThread() defer runtime.UnlockOSThread()
cOpts := opts.MergeOpts.toC() cOpts := opts.MergeOpts.toC()
defer freeMergeOptions(cOpts)
var ptr *C.git_index var ptr *C.git_index
ret := C.git_cherrypick_commit(&ptr, r.ptr, pick.cast_ptr, our.cast_ptr, C.uint(opts.Mainline), cOpts) ret := C.git_cherrypick_commit(&ptr, r.ptr, pick.cast_ptr, our.cast_ptr, C.uint(opts.Mainline), cOpts)

View File

@ -3,10 +3,11 @@ package git
/* /*
#include <git2.h> #include <git2.h>
extern void _go_git_populate_remote_cb(git_clone_options *opts); extern void _go_git_populate_clone_callbacks(git_clone_options *opts);
*/ */
import "C" import "C"
import ( import (
"errors"
"runtime" "runtime"
"unsafe" "unsafe"
) )
@ -28,20 +29,23 @@ func Clone(url string, path string, options *CloneOptions) (*Repository, error)
cpath := C.CString(path) cpath := C.CString(path)
defer C.free(unsafe.Pointer(cpath)) defer C.free(unsafe.Pointer(cpath))
copts := (*C.git_clone_options)(C.calloc(1, C.size_t(unsafe.Sizeof(C.git_clone_options{})))) var err error
populateCloneOptions(copts, options) cOptions := populateCloneOptions(&C.git_clone_options{}, options, &err)
defer freeCloneOptions(copts) defer freeCloneOptions(cOptions)
if len(options.CheckoutBranch) != 0 { if len(options.CheckoutBranch) != 0 {
copts.checkout_branch = C.CString(options.CheckoutBranch) cOptions.checkout_branch = C.CString(options.CheckoutBranch)
} }
runtime.LockOSThread() runtime.LockOSThread()
defer runtime.UnlockOSThread() defer runtime.UnlockOSThread()
var ptr *C.git_repository var ptr *C.git_repository
ret := C.git_clone(&ptr, curl, cpath, copts) ret := C.git_clone(&ptr, curl, cpath, cOptions)
if ret == C.int(ErrorCodeUser) && err != nil {
return nil, err
}
if ret < 0 { if ret < 0 {
return nil, MakeGitError(ret) return nil, MakeGitError(ret)
} }
@ -50,47 +54,69 @@ func Clone(url string, path string, options *CloneOptions) (*Repository, error)
} }
//export remoteCreateCallback //export remoteCreateCallback
func remoteCreateCallback(cremote unsafe.Pointer, crepo unsafe.Pointer, cname, curl *C.char, payload unsafe.Pointer) C.int { func remoteCreateCallback(
cremote unsafe.Pointer,
crepo unsafe.Pointer,
cname, curl *C.char,
payload unsafe.Pointer,
) C.int {
name := C.GoString(cname) name := C.GoString(cname)
url := C.GoString(curl) url := C.GoString(curl)
repo := newRepositoryFromC((*C.git_repository)(crepo)) repo := newRepositoryFromC((*C.git_repository)(crepo))
// We don't own this repository, so make sure we don't try to free it // We don't own this repository, so make sure we don't try to free it
runtime.SetFinalizer(repo, nil) runtime.SetFinalizer(repo, nil)
if opts, ok := pointerHandles.Get(payload).(CloneOptions); ok { data, ok := pointerHandles.Get(payload).(*cloneCallbackData)
remote, errorCode := opts.RemoteCreateCallback(repo, name, url) if !ok {
panic("invalid remote create callback")
}
remote, ret := data.options.RemoteCreateCallback(repo, name, url)
// clear finalizer as the calling C function will // clear finalizer as the calling C function will
// free the remote itself // free the remote itself
runtime.SetFinalizer(remote, nil) runtime.SetFinalizer(remote, nil)
if errorCode == ErrorCodeOK && remote != nil { if ret < 0 {
cptr := (**C.git_remote)(cremote) *data.errorTarget = errors.New(ErrorCode(ret).String())
*cptr = remote.ptr return C.int(ErrorCodeUser)
} else if errorCode == ErrorCodeOK && remote == nil { }
if remote == nil {
panic("no remote created by callback") panic("no remote created by callback")
} }
return C.int(errorCode) cptr := (**C.git_remote)(cremote)
} else { *cptr = remote.ptr
panic("invalid remote create callback")
} return C.int(ErrorCodeOK)
} }
func populateCloneOptions(ptr *C.git_clone_options, opts *CloneOptions) { type cloneCallbackData struct {
options *CloneOptions
errorTarget *error
}
func populateCloneOptions(ptr *C.git_clone_options, opts *CloneOptions, errorTarget *error) *C.git_clone_options {
C.git_clone_options_init(ptr, C.GIT_CLONE_OPTIONS_VERSION) C.git_clone_options_init(ptr, C.GIT_CLONE_OPTIONS_VERSION)
if opts == nil { if opts == nil {
return return nil
} }
populateCheckoutOptions(&ptr.checkout_opts, opts.CheckoutOpts) populateCheckoutOptions(&ptr.checkout_opts, opts.CheckoutOpts, errorTarget)
populateFetchOptions(&ptr.fetch_opts, opts.FetchOptions) populateFetchOptions(&ptr.fetch_opts, opts.FetchOptions)
ptr.bare = cbool(opts.Bare) ptr.bare = cbool(opts.Bare)
if opts.RemoteCreateCallback != nil { if opts.RemoteCreateCallback != nil {
// Go v1.1 does not allow to assign a C function pointer data := &cloneCallbackData{
C._go_git_populate_remote_cb(ptr) options: opts,
ptr.remote_cb_payload = pointerHandles.Track(*opts) errorTarget: errorTarget,
} }
// Go v1.1 does not allow to assign a C function pointer
C._go_git_populate_clone_callbacks(ptr)
ptr.remote_cb_payload = pointerHandles.Track(data)
}
return ptr
} }
func freeCloneOptions(ptr *C.git_clone_options) { func freeCloneOptions(ptr *C.git_clone_options) {
@ -105,5 +131,4 @@ func freeCloneOptions(ptr *C.git_clone_options) {
} }
C.free(unsafe.Pointer(ptr.checkout_branch)) C.free(unsafe.Pointer(ptr.checkout_branch))
C.free(unsafe.Pointer(ptr))
} }

320
diff.go
View File

@ -3,7 +3,7 @@ package git
/* /*
#include <git2.h> #include <git2.h>
extern void _go_git_populate_apply_cb(git_apply_options *options); extern void _go_git_populate_apply_callbacks(git_apply_options *options);
extern int _go_git_diff_foreach(git_diff *diff, int eachFile, int eachHunk, int eachLine, void *payload); extern int _go_git_diff_foreach(git_diff *diff, int eachFile, int eachHunk, int eachLine, void *payload);
extern void _go_git_setup_diff_notify_callbacks(git_diff_options* opts); extern void _go_git_setup_diff_notify_callbacks(git_diff_options* opts);
extern int _go_git_diff_blobs(git_blob *old, const char *old_path, git_blob *new, const char *new_path, git_diff_options *opts, int eachFile, int eachHunk, int eachLine, void *payload); extern int _go_git_diff_blobs(git_blob *old, const char *old_path, git_blob *new, const char *new_path, git_diff_options *opts, int eachFile, int eachHunk, int eachLine, void *payload);
@ -294,11 +294,11 @@ func (diff *Diff) Stats() (*DiffStats, error) {
return stats, nil return stats, nil
} }
type diffForEachData struct { type diffForEachCallbackData struct {
FileCallback DiffForEachFileCallback fileCallback DiffForEachFileCallback
HunkCallback DiffForEachHunkCallback hunkCallback DiffForEachHunkCallback
LineCallback DiffForEachLineCallback lineCallback DiffForEachLineCallback
Error error errorTarget *error
} }
type DiffForEachFileCallback func(delta DiffDelta, progress float64) (DiffForEachHunkCallback, error) type DiffForEachFileCallback func(delta DiffDelta, progress float64) (DiffForEachHunkCallback, error)
@ -326,82 +326,91 @@ func (diff *Diff) ForEach(cbFile DiffForEachFileCallback, detail DiffDetail) err
intLines = C.int(1) intLines = C.int(1)
} }
data := &diffForEachData{ var err error
FileCallback: cbFile, data := &diffForEachCallbackData{
fileCallback: cbFile,
errorTarget: &err,
} }
handle := pointerHandles.Track(data) handle := pointerHandles.Track(data)
defer pointerHandles.Untrack(handle) defer pointerHandles.Untrack(handle)
ecode := C._go_git_diff_foreach(diff.ptr, 1, intHunks, intLines, handle) runtime.LockOSThread()
defer runtime.UnlockOSThread()
ret := C._go_git_diff_foreach(diff.ptr, 1, intHunks, intLines, handle)
runtime.KeepAlive(diff) runtime.KeepAlive(diff)
if ecode < 0 { if ret == C.int(ErrorCodeUser) && err != nil {
return data.Error return err
} }
if ret < 0 {
return MakeGitError(ret)
}
return nil return nil
} }
//export diffForEachFileCb //export diffForEachFileCallback
func diffForEachFileCb(delta *C.git_diff_delta, progress C.float, handle unsafe.Pointer) int { func diffForEachFileCallback(delta *C.git_diff_delta, progress C.float, handle unsafe.Pointer) C.int {
payload := pointerHandles.Get(handle) payload := pointerHandles.Get(handle)
data, ok := payload.(*diffForEachData) data, ok := payload.(*diffForEachCallbackData)
if !ok { if !ok {
panic("could not retrieve data for handle") panic("could not retrieve data for handle")
} }
data.HunkCallback = nil data.hunkCallback = nil
if data.FileCallback != nil { if data.fileCallback != nil {
cb, err := data.FileCallback(diffDeltaFromC(delta), float64(progress)) cb, err := data.fileCallback(diffDeltaFromC(delta), float64(progress))
if err != nil { if err != nil {
data.Error = err *data.errorTarget = err
return -1 return C.int(ErrorCodeUser)
} }
data.HunkCallback = cb data.hunkCallback = cb
} }
return 0 return C.int(ErrorCodeOK)
} }
type DiffForEachHunkCallback func(DiffHunk) (DiffForEachLineCallback, error) type DiffForEachHunkCallback func(DiffHunk) (DiffForEachLineCallback, error)
//export diffForEachHunkCb //export diffForEachHunkCallback
func diffForEachHunkCb(delta *C.git_diff_delta, hunk *C.git_diff_hunk, handle unsafe.Pointer) int { func diffForEachHunkCallback(delta *C.git_diff_delta, hunk *C.git_diff_hunk, handle unsafe.Pointer) C.int {
payload := pointerHandles.Get(handle) payload := pointerHandles.Get(handle)
data, ok := payload.(*diffForEachData) data, ok := payload.(*diffForEachCallbackData)
if !ok { if !ok {
panic("could not retrieve data for handle") panic("could not retrieve data for handle")
} }
data.LineCallback = nil data.lineCallback = nil
if data.HunkCallback != nil { if data.hunkCallback != nil {
cb, err := data.HunkCallback(diffHunkFromC(hunk)) cb, err := data.hunkCallback(diffHunkFromC(hunk))
if err != nil { if err != nil {
data.Error = err *data.errorTarget = err
return -1 return C.int(ErrorCodeUser)
} }
data.LineCallback = cb data.lineCallback = cb
} }
return 0 return C.int(ErrorCodeOK)
} }
type DiffForEachLineCallback func(DiffLine) error type DiffForEachLineCallback func(DiffLine) error
//export diffForEachLineCb //export diffForEachLineCallback
func diffForEachLineCb(delta *C.git_diff_delta, hunk *C.git_diff_hunk, line *C.git_diff_line, handle unsafe.Pointer) int { func diffForEachLineCallback(delta *C.git_diff_delta, hunk *C.git_diff_hunk, line *C.git_diff_line, handle unsafe.Pointer) C.int {
payload := pointerHandles.Get(handle) payload := pointerHandles.Get(handle)
data, ok := payload.(*diffForEachData) data, ok := payload.(*diffForEachCallbackData)
if !ok { if !ok {
panic("could not retrieve data for handle") panic("could not retrieve data for handle")
} }
err := data.LineCallback(diffLineFromC(line)) err := data.lineCallback(diffLineFromC(line))
if err != nil { if err != nil {
data.Error = err *data.errorTarget = err
return -1 return C.int(ErrorCodeUser)
} }
return 0 return C.int(ErrorCodeOK)
} }
func (diff *Diff) Patch(deltaIndex int) (*Patch, error) { func (diff *Diff) Patch(deltaIndex int) (*Patch, error) {
@ -586,31 +595,34 @@ var (
ErrDeltaSkip = errors.New("Skip delta") ErrDeltaSkip = errors.New("Skip delta")
) )
type diffNotifyData struct { type diffNotifyCallbackData struct {
Callback DiffNotifyCallback callback DiffNotifyCallback
Repository *Repository repository *Repository
Error error errorTarget *error
} }
//export diffNotifyCb //export diffNotifyCallback
func diffNotifyCb(_diff_so_far unsafe.Pointer, delta_to_add *C.git_diff_delta, matched_pathspec *C.char, handle unsafe.Pointer) int { func diffNotifyCallback(_diff_so_far unsafe.Pointer, delta_to_add *C.git_diff_delta, matched_pathspec *C.char, handle unsafe.Pointer) C.int {
diff_so_far := (*C.git_diff)(_diff_so_far) diff_so_far := (*C.git_diff)(_diff_so_far)
payload := pointerHandles.Get(handle) payload := pointerHandles.Get(handle)
data, ok := payload.(*diffNotifyData) data, ok := payload.(*diffNotifyCallbackData)
if !ok { if !ok {
panic("could not retrieve data for handle") panic("could not retrieve data for handle")
} }
if data != nil { if data == nil {
return C.int(ErrorCodeOK)
}
// We are not taking ownership of this diff pointer, so no finalizer is set. // We are not taking ownership of this diff pointer, so no finalizer is set.
diff := &Diff{ diff := &Diff{
ptr: diff_so_far, ptr: diff_so_far,
repo: data.Repository, repo: data.repository,
runFinalizer: false, runFinalizer: false,
} }
err := data.Callback(diff, diffDeltaFromC(delta_to_add), C.GoString(matched_pathspec)) err := data.callback(diff, diffDeltaFromC(delta_to_add), C.GoString(matched_pathspec))
// Since the callback could theoretically keep a reference to the diff // Since the callback could theoretically keep a reference to the diff
// (which could be freed by libgit2 if an error occurs later during the // (which could be freed by libgit2 if an error occurs later during the
@ -620,30 +632,27 @@ func diffNotifyCb(_diff_so_far unsafe.Pointer, delta_to_add *C.git_diff_delta, m
if err == ErrDeltaSkip { if err == ErrDeltaSkip {
return 1 return 1
} else if err != nil {
data.Error = err
return -1
} else {
return 0
} }
} if err != nil {
return 0 *data.errorTarget = err
return C.int(ErrorCodeUser)
}
return C.int(ErrorCodeOK)
}
func (opts *DiffOptions) toC(repo *Repository, errorTarget *error) *C.git_diff_options {
if opts == nil {
return nil
} }
func diffOptionsToC(opts *DiffOptions, repo *Repository) (copts *C.git_diff_options) {
cpathspec := C.git_strarray{} cpathspec := C.git_strarray{}
if opts != nil {
notifyData := &diffNotifyData{
Callback: opts.NotifyCallback,
Repository: repo,
}
if opts.Pathspec != nil { if opts.Pathspec != nil {
cpathspec.count = C.size_t(len(opts.Pathspec)) cpathspec.count = C.size_t(len(opts.Pathspec))
cpathspec.strings = makeCStringsFromStrings(opts.Pathspec) cpathspec.strings = makeCStringsFromStrings(opts.Pathspec)
} }
copts = &C.git_diff_options{ copts := &C.git_diff_options{
version: C.GIT_DIFF_OPTIONS_VERSION, version: C.GIT_DIFF_OPTIONS_VERSION,
flags: C.uint32_t(opts.Flags), flags: C.uint32_t(opts.Flags),
ignore_submodules: C.git_submodule_ignore_t(opts.IgnoreSubmodules), ignore_submodules: C.git_submodule_ignore_t(opts.IgnoreSubmodules),
@ -657,15 +666,21 @@ func diffOptionsToC(opts *DiffOptions, repo *Repository) (copts *C.git_diff_opti
} }
if opts.NotifyCallback != nil { if opts.NotifyCallback != nil {
notifyData := &diffNotifyCallbackData{
callback: opts.NotifyCallback,
repository: repo,
errorTarget: errorTarget,
}
C._go_git_setup_diff_notify_callbacks(copts) C._go_git_setup_diff_notify_callbacks(copts)
copts.payload = pointerHandles.Track(notifyData) copts.payload = pointerHandles.Track(notifyData)
} }
} return copts
return
} }
func freeDiffOptions(copts *C.git_diff_options) { func freeDiffOptions(copts *C.git_diff_options) {
if copts != nil { if copts == nil {
return
}
cpathspec := copts.pathspec cpathspec := copts.pathspec
freeStrarray(&cpathspec) freeStrarray(&cpathspec)
C.free(unsafe.Pointer(copts.old_prefix)) C.free(unsafe.Pointer(copts.old_prefix))
@ -674,7 +689,6 @@ func freeDiffOptions(copts *C.git_diff_options) {
pointerHandles.Untrack(copts.payload) pointerHandles.Untrack(copts.payload)
} }
} }
}
func (v *Repository) DiffTreeToTree(oldTree, newTree *Tree, opts *DiffOptions) (*Diff, error) { func (v *Repository) DiffTreeToTree(oldTree, newTree *Tree, opts *DiffOptions) (*Diff, error) {
var diffPtr *C.git_diff var diffPtr *C.git_diff
@ -688,17 +702,21 @@ func (v *Repository) DiffTreeToTree(oldTree, newTree *Tree, opts *DiffOptions) (
newPtr = newTree.cast_ptr newPtr = newTree.cast_ptr
} }
copts := diffOptionsToC(opts, v) var err error
copts := opts.toC(v, &err)
defer freeDiffOptions(copts) defer freeDiffOptions(copts)
runtime.LockOSThread() runtime.LockOSThread()
defer runtime.UnlockOSThread() defer runtime.UnlockOSThread()
ecode := C.git_diff_tree_to_tree(&diffPtr, v.ptr, oldPtr, newPtr, copts) ret := C.git_diff_tree_to_tree(&diffPtr, v.ptr, oldPtr, newPtr, copts)
runtime.KeepAlive(oldTree) runtime.KeepAlive(oldTree)
runtime.KeepAlive(newTree) runtime.KeepAlive(newTree)
if ecode < 0 { if ret == C.int(ErrorCodeUser) && err != nil {
return nil, MakeGitError(ecode) return nil, err
}
if ret < 0 {
return nil, MakeGitError(ret)
} }
return newDiffFromC(diffPtr, v), nil return newDiffFromC(diffPtr, v), nil
} }
@ -711,17 +729,22 @@ func (v *Repository) DiffTreeToWorkdir(oldTree *Tree, opts *DiffOptions) (*Diff,
oldPtr = oldTree.cast_ptr oldPtr = oldTree.cast_ptr
} }
copts := diffOptionsToC(opts, v) var err error
copts := opts.toC(v, &err)
defer freeDiffOptions(copts) defer freeDiffOptions(copts)
runtime.LockOSThread() runtime.LockOSThread()
defer runtime.UnlockOSThread() defer runtime.UnlockOSThread()
ecode := C.git_diff_tree_to_workdir(&diffPtr, v.ptr, oldPtr, copts) ret := C.git_diff_tree_to_workdir(&diffPtr, v.ptr, oldPtr, copts)
runtime.KeepAlive(oldTree) runtime.KeepAlive(oldTree)
if ecode < 0 { if ret == C.int(ErrorCodeUser) && err != nil {
return nil, MakeGitError(ecode) return nil, err
} }
if ret < 0 {
return nil, MakeGitError(ret)
}
return newDiffFromC(diffPtr, v), nil return newDiffFromC(diffPtr, v), nil
} }
@ -738,18 +761,23 @@ func (v *Repository) DiffTreeToIndex(oldTree *Tree, index *Index, opts *DiffOpti
indexPtr = index.ptr indexPtr = index.ptr
} }
copts := diffOptionsToC(opts, v) var err error
copts := opts.toC(v, &err)
defer freeDiffOptions(copts) defer freeDiffOptions(copts)
runtime.LockOSThread() runtime.LockOSThread()
defer runtime.UnlockOSThread() defer runtime.UnlockOSThread()
ecode := C.git_diff_tree_to_index(&diffPtr, v.ptr, oldPtr, indexPtr, copts) ret := C.git_diff_tree_to_index(&diffPtr, v.ptr, oldPtr, indexPtr, copts)
runtime.KeepAlive(oldTree) runtime.KeepAlive(oldTree)
runtime.KeepAlive(index) runtime.KeepAlive(index)
if ecode < 0 { if ret == C.int(ErrorCodeUser) && err != nil {
return nil, MakeGitError(ecode) return nil, err
} }
if ret < 0 {
return nil, MakeGitError(ret)
}
return newDiffFromC(diffPtr, v), nil return newDiffFromC(diffPtr, v), nil
} }
@ -761,17 +789,22 @@ func (v *Repository) DiffTreeToWorkdirWithIndex(oldTree *Tree, opts *DiffOptions
oldPtr = oldTree.cast_ptr oldPtr = oldTree.cast_ptr
} }
copts := diffOptionsToC(opts, v) var err error
copts := opts.toC(v, &err)
defer freeDiffOptions(copts) defer freeDiffOptions(copts)
runtime.LockOSThread() runtime.LockOSThread()
defer runtime.UnlockOSThread() defer runtime.UnlockOSThread()
ecode := C.git_diff_tree_to_workdir_with_index(&diffPtr, v.ptr, oldPtr, copts) ret := C.git_diff_tree_to_workdir_with_index(&diffPtr, v.ptr, oldPtr, copts)
runtime.KeepAlive(oldTree) runtime.KeepAlive(oldTree)
if ecode < 0 { if ret == C.int(ErrorCodeUser) && err != nil {
return nil, MakeGitError(ecode) return nil, err
} }
if ret < 0 {
return nil, MakeGitError(ret)
}
return newDiffFromC(diffPtr, v), nil return newDiffFromC(diffPtr, v), nil
} }
@ -783,25 +816,32 @@ func (v *Repository) DiffIndexToWorkdir(index *Index, opts *DiffOptions) (*Diff,
indexPtr = index.ptr indexPtr = index.ptr
} }
copts := diffOptionsToC(opts, v) var err error
copts := opts.toC(v, &err)
defer freeDiffOptions(copts) defer freeDiffOptions(copts)
runtime.LockOSThread() runtime.LockOSThread()
defer runtime.UnlockOSThread() defer runtime.UnlockOSThread()
ecode := C.git_diff_index_to_workdir(&diffPtr, v.ptr, indexPtr, copts) ret := C.git_diff_index_to_workdir(&diffPtr, v.ptr, indexPtr, copts)
runtime.KeepAlive(index) runtime.KeepAlive(index)
if ecode < 0 { if ret == C.int(ErrorCodeUser) && err != nil {
return nil, MakeGitError(ecode) return nil, err
} }
if ret < 0 {
return nil, MakeGitError(ret)
}
return newDiffFromC(diffPtr, v), nil return newDiffFromC(diffPtr, v), nil
} }
// DiffBlobs performs a diff between two arbitrary blobs. You can pass // DiffBlobs performs a diff between two arbitrary blobs. You can pass
// whatever file names you'd like for them to appear as in the diff. // whatever file names you'd like for them to appear as in the diff.
func DiffBlobs(oldBlob *Blob, oldAsPath string, newBlob *Blob, newAsPath string, opts *DiffOptions, fileCallback DiffForEachFileCallback, detail DiffDetail) error { func DiffBlobs(oldBlob *Blob, oldAsPath string, newBlob *Blob, newAsPath string, opts *DiffOptions, fileCallback DiffForEachFileCallback, detail DiffDetail) error {
data := &diffForEachData{ var err error
FileCallback: fileCallback, data := &diffForEachCallbackData{
fileCallback: fileCallback,
errorTarget: &err,
} }
intHunks := C.int(0) intHunks := C.int(0)
@ -833,17 +873,20 @@ func DiffBlobs(oldBlob *Blob, oldAsPath string, newBlob *Blob, newAsPath string,
newBlobPath := C.CString(newAsPath) newBlobPath := C.CString(newAsPath)
defer C.free(unsafe.Pointer(newBlobPath)) defer C.free(unsafe.Pointer(newBlobPath))
copts := diffOptionsToC(opts, repo) copts := opts.toC(repo, &err)
defer freeDiffOptions(copts) defer freeDiffOptions(copts)
runtime.LockOSThread() runtime.LockOSThread()
defer runtime.UnlockOSThread() defer runtime.UnlockOSThread()
ecode := C._go_git_diff_blobs(oldBlobPtr, oldBlobPath, newBlobPtr, newBlobPath, copts, 1, intHunks, intLines, handle) ret := C._go_git_diff_blobs(oldBlobPtr, oldBlobPath, newBlobPtr, newBlobPath, copts, 1, intHunks, intLines, handle)
runtime.KeepAlive(oldBlob) runtime.KeepAlive(oldBlob)
runtime.KeepAlive(newBlob) runtime.KeepAlive(newBlob)
if ecode < 0 { if ret == C.int(ErrorCodeUser) && err != nil {
return MakeGitError(ecode) return err
}
if ret < 0 {
return MakeGitError(ret)
} }
return nil return nil
@ -864,56 +907,59 @@ type ApplyOptions struct {
Flags uint Flags uint
} }
type applyCallbackData struct {
options *ApplyOptions
errorTarget *error
}
//export hunkApplyCallback //export hunkApplyCallback
func hunkApplyCallback(_hunk *C.git_diff_hunk, _payload unsafe.Pointer) C.int { func hunkApplyCallback(_hunk *C.git_diff_hunk, _payload unsafe.Pointer) C.int {
opts, ok := pointerHandles.Get(_payload).(*ApplyOptions) data, ok := pointerHandles.Get(_payload).(*applyCallbackData)
if !ok { if !ok {
panic("invalid apply options payload") panic("invalid apply options payload")
} }
if opts.ApplyHunkCallback == nil { if data.options.ApplyHunkCallback == nil {
return 0 return C.int(ErrorCodeOK)
} }
hunk := diffHunkFromC(_hunk) hunk := diffHunkFromC(_hunk)
apply, err := opts.ApplyHunkCallback(&hunk) apply, err := data.options.ApplyHunkCallback(&hunk)
if err != nil { if err != nil {
if gitError, ok := err.(*GitError); ok { *data.errorTarget = err
return C.int(gitError.Code) return C.int(ErrorCodeUser)
} }
return -1
} else if apply { if !apply {
return 0
} else {
return 1 return 1
} }
return C.int(ErrorCodeOK)
} }
//export deltaApplyCallback //export deltaApplyCallback
func deltaApplyCallback(_delta *C.git_diff_delta, _payload unsafe.Pointer) C.int { func deltaApplyCallback(_delta *C.git_diff_delta, _payload unsafe.Pointer) C.int {
opts, ok := pointerHandles.Get(_payload).(*ApplyOptions) data, ok := pointerHandles.Get(_payload).(*applyCallbackData)
if !ok { if !ok {
panic("invalid apply options payload") panic("invalid apply options payload")
} }
if opts.ApplyDeltaCallback == nil { if data.options.ApplyDeltaCallback == nil {
return 0 return C.int(ErrorCodeOK)
} }
delta := diffDeltaFromC(_delta) delta := diffDeltaFromC(_delta)
apply, err := opts.ApplyDeltaCallback(&delta) apply, err := data.options.ApplyDeltaCallback(&delta)
if err != nil { if err != nil {
if gitError, ok := err.(*GitError); ok { *data.errorTarget = err
return C.int(gitError.Code) return C.int(ErrorCodeUser)
} }
return -1
} else if apply { if !apply {
return 0
} else {
return 1 return 1
} }
return C.int(ErrorCodeOK)
} }
// DefaultApplyOptions returns default options for applying diffs or patches. // DefaultApplyOptions returns default options for applying diffs or patches.
@ -925,14 +971,13 @@ func DefaultApplyOptions() (*ApplyOptions, error) {
ecode := C.git_apply_options_init(&opts, C.GIT_APPLY_OPTIONS_VERSION) ecode := C.git_apply_options_init(&opts, C.GIT_APPLY_OPTIONS_VERSION)
if int(ecode) != 0 { if int(ecode) != 0 {
return nil, MakeGitError(ecode) return nil, MakeGitError(ecode)
} }
return applyOptionsFromC(&opts), nil return applyOptionsFromC(&opts), nil
} }
func (a *ApplyOptions) toC() *C.git_apply_options { func (a *ApplyOptions) toC(errorTarget *error) *C.git_apply_options {
if a == nil { if a == nil {
return nil return nil
} }
@ -943,13 +988,26 @@ func (a *ApplyOptions) toC() *C.git_apply_options {
} }
if a.ApplyDeltaCallback != nil || a.ApplyHunkCallback != nil { if a.ApplyDeltaCallback != nil || a.ApplyHunkCallback != nil {
C._go_git_populate_apply_cb(opts) data := &applyCallbackData{
opts.payload = pointerHandles.Track(a) options: a,
errorTarget: errorTarget,
}
C._go_git_populate_apply_callbacks(opts)
opts.payload = pointerHandles.Track(data)
} }
return opts return opts
} }
func freeApplyOptions(opts *C.git_apply_options) {
if opts == nil {
return
}
if opts.payload != nil {
pointerHandles.Untrack(opts.payload)
}
}
func applyOptionsFromC(opts *C.git_apply_options) *ApplyOptions { func applyOptionsFromC(opts *C.git_apply_options) *ApplyOptions {
return &ApplyOptions{ return &ApplyOptions{
Flags: uint(opts.flags), Flags: uint(opts.flags),
@ -979,13 +1037,19 @@ func (v *Repository) ApplyDiff(diff *Diff, location ApplyLocation, opts *ApplyOp
runtime.LockOSThread() runtime.LockOSThread()
defer runtime.UnlockOSThread() defer runtime.UnlockOSThread()
cOpts := opts.toC() var err error
ecode := C.git_apply(v.ptr, diff.ptr, C.git_apply_location_t(location), cOpts) cOpts := opts.toC(&err)
defer freeApplyOptions(cOpts)
ret := C.git_apply(v.ptr, diff.ptr, C.git_apply_location_t(location), cOpts)
runtime.KeepAlive(v) runtime.KeepAlive(v)
runtime.KeepAlive(diff) runtime.KeepAlive(diff)
runtime.KeepAlive(cOpts) runtime.KeepAlive(cOpts)
if ecode < 0 { if ret == C.int(ErrorCodeUser) && err != nil {
return MakeGitError(ecode) return err
}
if ret < 0 {
return MakeGitError(ret)
} }
return nil return nil
@ -996,14 +1060,20 @@ func (v *Repository) ApplyToTree(diff *Diff, tree *Tree, opts *ApplyOptions) (*I
runtime.LockOSThread() runtime.LockOSThread()
defer runtime.UnlockOSThread() defer runtime.UnlockOSThread()
var err error
cOpts := opts.toC(&err)
defer freeApplyOptions(cOpts)
var indexPtr *C.git_index var indexPtr *C.git_index
cOpts := opts.toC() ret := C.git_apply_to_tree(&indexPtr, v.ptr, tree.cast_ptr, diff.ptr, cOpts)
ecode := C.git_apply_to_tree(&indexPtr, v.ptr, tree.cast_ptr, diff.ptr, cOpts)
runtime.KeepAlive(diff) runtime.KeepAlive(diff)
runtime.KeepAlive(tree) runtime.KeepAlive(tree)
runtime.KeepAlive(cOpts) runtime.KeepAlive(cOpts)
if ecode != 0 { if ret == C.int(ErrorCodeUser) && err != nil {
return nil, MakeGitError(ecode) return nil, err
}
if ret < 0 {
return nil, MakeGitError(ret)
} }
return newIndexFromC(indexPtr, v), nil return newIndexFromC(indexPtr, v), nil

View File

@ -173,9 +173,8 @@ func TestDiffTreeToTree(t *testing.T) {
}, DiffDetailLines) }, DiffDetailLines)
if err != errTest { if err != errTest {
t.Fatal("Expected custom error to be returned") t.Fatalf("Expected custom error to be returned, got %v, want %v", err, errTest)
} }
} }
func createTestTrees(t *testing.T, repo *Repository) (originalTree *Tree, newTree *Tree) { func createTestTrees(t *testing.T, repo *Repository) (originalTree *Tree, newTree *Tree) {
@ -486,13 +485,15 @@ func TestApplyToTree(t *testing.T) {
diffAC, err := repo.DiffTreeToTree(treeA, treeC, nil) diffAC, err := repo.DiffTreeToTree(treeA, treeC, nil)
checkFatal(t, err) checkFatal(t, err)
errMessageDropped := errors.New("message dropped")
for _, tc := range []struct { for _, tc := range []struct {
name string name string
tree *Tree tree *Tree
diff *Diff diff *Diff
applyHunkCallback ApplyHunkCallback applyHunkCallback ApplyHunkCallback
applyDeltaCallback ApplyDeltaCallback applyDeltaCallback ApplyDeltaCallback
error error err error
expectedDiff *Diff expectedDiff *Diff
}{ }{
{ {
@ -505,7 +506,7 @@ func TestApplyToTree(t *testing.T) {
name: "applying a conflicting patch errors", name: "applying a conflicting patch errors",
tree: treeB, tree: treeB,
diff: diffAC, diff: diffAC,
error: &GitError{ err: &GitError{
Message: "hunk at line 1 did not apply", Message: "hunk at line 1 did not apply",
Code: ErrorCodeApplyFail, Code: ErrorCodeApplyFail,
Class: ErrorClassPatch, Class: ErrorClassPatch,
@ -529,12 +530,8 @@ func TestApplyToTree(t *testing.T) {
name: "hunk callback erroring fails the call", name: "hunk callback erroring fails the call",
tree: treeA, tree: treeA,
diff: diffAB, diff: diffAB,
applyHunkCallback: func(*DiffHunk) (bool, error) { return true, errors.New("message dropped") }, applyHunkCallback: func(*DiffHunk) (bool, error) { return true, errMessageDropped },
error: &GitError{ err: errMessageDropped,
Message: "Generic",
Code: ErrorCodeGeneric,
Class: ErrorClassInvalid,
},
}, },
{ {
name: "delta callback returning false does not apply", name: "delta callback returning false does not apply",
@ -546,12 +543,8 @@ func TestApplyToTree(t *testing.T) {
name: "delta callback erroring fails the call", name: "delta callback erroring fails the call",
tree: treeA, tree: treeA,
diff: diffAB, diff: diffAB,
applyDeltaCallback: func(*DiffDelta) (bool, error) { return true, errors.New("message dropped") }, applyDeltaCallback: func(*DiffDelta) (bool, error) { return true, errMessageDropped },
error: &GitError{ err: errMessageDropped,
Message: "Generic",
Code: ErrorCodeGeneric,
Class: ErrorClassInvalid,
},
}, },
} { } {
t.Run(tc.name, func(t *testing.T) { t.Run(tc.name, func(t *testing.T) {
@ -562,9 +555,9 @@ func TestApplyToTree(t *testing.T) {
opts.ApplyDeltaCallback = tc.applyDeltaCallback opts.ApplyDeltaCallback = tc.applyDeltaCallback
index, err := repo.ApplyToTree(tc.diff, tc.tree, opts) index, err := repo.ApplyToTree(tc.diff, tc.tree, opts)
if tc.error != nil { if tc.err != nil {
if !reflect.DeepEqual(err, tc.error) { if !reflect.DeepEqual(tc.err, err) {
t.Fatalf("expected error %q but got %q", tc.error, err) t.Fatalf("expected error %q but got %q", tc.err, err)
} }
return return

11
git.go
View File

@ -330,6 +330,17 @@ func ucbool(b bool) C.uint {
return C.uint(0) return C.uint(0)
} }
func setCallbackError(errorMessage **C.char, err error) C.int {
if err != nil {
*errorMessage = C.CString(err.Error())
if gitError, ok := err.(*GitError); ok {
return C.int(gitError.Code)
}
return C.int(ErrorCodeUser)
}
return C.int(ErrorCodeOK)
}
func Discover(start string, across_fs bool, ceiling_dirs []string) (string, error) { func Discover(start string, across_fs bool, ceiling_dirs []string) (string, error) {
ceildirs := C.CString(strings.Join(ceiling_dirs, string(C.GIT_PATH_LIST_SEPARATOR))) ceildirs := C.CString(strings.Join(ceiling_dirs, string(C.GIT_PATH_LIST_SEPARATOR)))
defer C.free(unsafe.Pointer(ceildirs)) defer C.free(unsafe.Pointer(ceildirs))

View File

@ -10,12 +10,17 @@ extern int _go_git_index_remove_all(git_index*, const git_strarray*, void*);
*/ */
import "C" import "C"
import ( import (
"errors"
"fmt" "fmt"
"runtime" "runtime"
"unsafe" "unsafe"
) )
type IndexMatchedPathCallback func(string, string) int type IndexMatchedPathCallback func(string, string) int
type indexMatchedPathCallbackData struct {
callback IndexMatchedPathCallback
errorTarget *error
}
// IndexAddOption is a set of flags for APIs that add files matching pathspec. // IndexAddOption is a set of flags for APIs that add files matching pathspec.
type IndexAddOption uint type IndexAddOption uint
@ -227,12 +232,17 @@ func (v *Index) AddAll(pathspecs []string, flags IndexAddOption, callback IndexM
cpathspecs.strings = makeCStringsFromStrings(pathspecs) cpathspecs.strings = makeCStringsFromStrings(pathspecs)
defer freeStrarray(&cpathspecs) defer freeStrarray(&cpathspecs)
var err error
data := indexMatchedPathCallbackData{
callback: callback,
errorTarget: &err,
}
runtime.LockOSThread() runtime.LockOSThread()
defer runtime.UnlockOSThread() defer runtime.UnlockOSThread()
var handle unsafe.Pointer var handle unsafe.Pointer
if callback != nil { if callback != nil {
handle = pointerHandles.Track(callback) handle = pointerHandles.Track(&data)
defer pointerHandles.Untrack(handle) defer pointerHandles.Untrack(handle)
} }
@ -243,9 +253,13 @@ func (v *Index) AddAll(pathspecs []string, flags IndexAddOption, callback IndexM
handle, handle,
) )
runtime.KeepAlive(v) runtime.KeepAlive(v)
if ret == C.int(ErrorCodeUser) && err != nil {
return err
}
if ret < 0 { if ret < 0 {
return MakeGitError(ret) return MakeGitError(ret)
} }
return nil return nil
} }
@ -255,12 +269,17 @@ func (v *Index) UpdateAll(pathspecs []string, callback IndexMatchedPathCallback)
cpathspecs.strings = makeCStringsFromStrings(pathspecs) cpathspecs.strings = makeCStringsFromStrings(pathspecs)
defer freeStrarray(&cpathspecs) defer freeStrarray(&cpathspecs)
var err error
data := indexMatchedPathCallbackData{
callback: callback,
errorTarget: &err,
}
runtime.LockOSThread() runtime.LockOSThread()
defer runtime.UnlockOSThread() defer runtime.UnlockOSThread()
var handle unsafe.Pointer var handle unsafe.Pointer
if callback != nil { if callback != nil {
handle = pointerHandles.Track(callback) handle = pointerHandles.Track(&data)
defer pointerHandles.Untrack(handle) defer pointerHandles.Untrack(handle)
} }
@ -270,9 +289,13 @@ func (v *Index) UpdateAll(pathspecs []string, callback IndexMatchedPathCallback)
handle, handle,
) )
runtime.KeepAlive(v) runtime.KeepAlive(v)
if ret == C.int(ErrorCodeUser) && err != nil {
return err
}
if ret < 0 { if ret < 0 {
return MakeGitError(ret) return MakeGitError(ret)
} }
return nil return nil
} }
@ -282,12 +305,17 @@ func (v *Index) RemoveAll(pathspecs []string, callback IndexMatchedPathCallback)
cpathspecs.strings = makeCStringsFromStrings(pathspecs) cpathspecs.strings = makeCStringsFromStrings(pathspecs)
defer freeStrarray(&cpathspecs) defer freeStrarray(&cpathspecs)
var err error
data := indexMatchedPathCallbackData{
callback: callback,
errorTarget: &err,
}
runtime.LockOSThread() runtime.LockOSThread()
defer runtime.UnlockOSThread() defer runtime.UnlockOSThread()
var handle unsafe.Pointer var handle unsafe.Pointer
if callback != nil { if callback != nil {
handle = pointerHandles.Track(callback) handle = pointerHandles.Track(&data)
defer pointerHandles.Untrack(handle) defer pointerHandles.Untrack(handle)
} }
@ -297,19 +325,30 @@ func (v *Index) RemoveAll(pathspecs []string, callback IndexMatchedPathCallback)
handle, handle,
) )
runtime.KeepAlive(v) runtime.KeepAlive(v)
if ret == C.int(ErrorCodeUser) && err != nil {
return err
}
if ret < 0 { if ret < 0 {
return MakeGitError(ret) return MakeGitError(ret)
} }
return nil return nil
} }
//export indexMatchedPathCallback //export indexMatchedPathCallback
func indexMatchedPathCallback(cPath, cMatchedPathspec *C.char, payload unsafe.Pointer) int { func indexMatchedPathCallback(cPath, cMatchedPathspec *C.char, payload unsafe.Pointer) C.int {
if callback, ok := pointerHandles.Get(payload).(IndexMatchedPathCallback); ok { data, ok := pointerHandles.Get(payload).(*indexMatchedPathCallbackData)
return callback(C.GoString(cPath), C.GoString(cMatchedPathspec)) if !ok {
} else {
panic("invalid matched path callback") panic("invalid matched path callback")
} }
ret := data.callback(C.GoString(cPath), C.GoString(cMatchedPathspec))
if ret < 0 {
*data.errorTarget = errors.New(ErrorCode(ret).String())
return C.int(ErrorCodeUser)
}
return C.int(ErrorCodeOK)
} }
func (v *Index) RemoveByPath(path string) error { func (v *Index) RemoveByPath(path string) error {
@ -435,7 +474,7 @@ func (v *Index) EntryByPath(path string, stage int) (*IndexEntry, error) {
centry := C.git_index_get_bypath(v.ptr, cpath, C.int(stage)) centry := C.git_index_get_bypath(v.ptr, cpath, C.int(stage))
if centry == nil { if centry == nil {
return nil, MakeGitError(C.GIT_ENOTFOUND) return nil, MakeGitError(C.int(ErrorCodeNotFound))
} }
ret := newIndexEntryFromC(centry) ret := newIndexEntryFromC(centry)
runtime.KeepAlive(v) runtime.KeepAlive(v)

View File

@ -186,6 +186,9 @@ func (mo *MergeOptions) toC() *C.git_merge_options {
} }
} }
func freeMergeOptions(opts *C.git_merge_options) {
}
type MergeFileFavor int type MergeFileFavor int
const ( const (
@ -199,8 +202,10 @@ func (r *Repository) Merge(theirHeads []*AnnotatedCommit, mergeOptions *MergeOpt
runtime.LockOSThread() runtime.LockOSThread()
defer runtime.UnlockOSThread() defer runtime.UnlockOSThread()
var err error
cMergeOpts := mergeOptions.toC() cMergeOpts := mergeOptions.toC()
cCheckoutOptions := checkoutOptions.toC() defer freeMergeOptions(cMergeOpts)
cCheckoutOptions := checkoutOptions.toC(&err)
defer freeCheckoutOptions(cCheckoutOptions) defer freeCheckoutOptions(cCheckoutOptions)
gmerge_head_array := make([]*C.git_annotated_commit, len(theirHeads)) gmerge_head_array := make([]*C.git_annotated_commit, len(theirHeads))
@ -208,10 +213,13 @@ func (r *Repository) Merge(theirHeads []*AnnotatedCommit, mergeOptions *MergeOpt
gmerge_head_array[i] = theirHeads[i].ptr gmerge_head_array[i] = theirHeads[i].ptr
} }
ptr := unsafe.Pointer(&gmerge_head_array[0]) ptr := unsafe.Pointer(&gmerge_head_array[0])
err := C.git_merge(r.ptr, (**C.git_annotated_commit)(ptr), C.size_t(len(theirHeads)), cMergeOpts, cCheckoutOptions) ret := C.git_merge(r.ptr, (**C.git_annotated_commit)(ptr), C.size_t(len(theirHeads)), cMergeOpts, cCheckoutOptions)
runtime.KeepAlive(theirHeads) runtime.KeepAlive(theirHeads)
if err < 0 { if ret == C.int(ErrorCodeUser) && err != nil {
return MakeGitError(err) return err
}
if ret < 0 {
return MakeGitError(ret)
} }
return nil return nil
} }
@ -262,6 +270,7 @@ func (r *Repository) MergeCommits(ours *Commit, theirs *Commit, options *MergeOp
defer runtime.UnlockOSThread() defer runtime.UnlockOSThread()
copts := options.toC() copts := options.toC()
defer freeMergeOptions(copts)
var ptr *C.git_index var ptr *C.git_index
ret := C.git_merge_commits(&ptr, r.ptr, ours.cast_ptr, theirs.cast_ptr, copts) ret := C.git_merge_commits(&ptr, r.ptr, ours.cast_ptr, theirs.cast_ptr, copts)
@ -279,6 +288,7 @@ func (r *Repository) MergeTrees(ancestor *Tree, ours *Tree, theirs *Tree, option
defer runtime.UnlockOSThread() defer runtime.UnlockOSThread()
copts := options.toC() copts := options.toC()
defer freeMergeOptions(copts)
var ancestor_ptr *C.git_tree var ancestor_ptr *C.git_tree
if ancestor != nil { if ancestor != nil {
@ -446,6 +456,9 @@ func populateCMergeFileOptions(c *C.git_merge_file_options, options MergeFileOpt
} }
func freeCMergeFileOptions(c *C.git_merge_file_options) { func freeCMergeFileOptions(c *C.git_merge_file_options) {
if c == nil {
return
}
C.free(unsafe.Pointer(c.ancestor_label)) C.free(unsafe.Pointer(c.ancestor_label))
C.free(unsafe.Pointer(c.our_label)) C.free(unsafe.Pointer(c.our_label))
C.free(unsafe.Pointer(c.their_label)) C.free(unsafe.Pointer(c.their_label))

31
odb.go
View File

@ -175,35 +175,33 @@ func (v *Odb) Read(oid *Oid) (obj *OdbObject, err error) {
} }
type OdbForEachCallback func(id *Oid) error type OdbForEachCallback func(id *Oid) error
type odbForEachCallbackData struct {
type foreachData struct {
callback OdbForEachCallback callback OdbForEachCallback
err error errorTarget *error
} }
//export odbForEachCb //export odbForEachCallback
func odbForEachCb(id *C.git_oid, handle unsafe.Pointer) int { func odbForEachCallback(id *C.git_oid, handle unsafe.Pointer) C.int {
data, ok := pointerHandles.Get(handle).(*foreachData) data, ok := pointerHandles.Get(handle).(*odbForEachCallbackData)
if !ok { if !ok {
panic("could not retrieve handle") panic("could not retrieve handle")
} }
err := data.callback(newOidFromC(id)) err := data.callback(newOidFromC(id))
if err != nil { if err != nil {
data.err = err *data.errorTarget = err
return C.GIT_EUSER return C.int(ErrorCodeUser)
} }
return 0 return C.int(ErrorCodeOK)
} }
func (v *Odb) ForEach(callback OdbForEachCallback) error { func (v *Odb) ForEach(callback OdbForEachCallback) error {
data := foreachData{ var err error
data := odbForEachCallbackData{
callback: callback, callback: callback,
err: nil, errorTarget: &err,
} }
runtime.LockOSThread() runtime.LockOSThread()
defer runtime.UnlockOSThread() defer runtime.UnlockOSThread()
@ -212,9 +210,10 @@ func (v *Odb) ForEach(callback OdbForEachCallback) error {
ret := C._go_git_odb_foreach(v.ptr, handle) ret := C._go_git_odb_foreach(v.ptr, handle)
runtime.KeepAlive(v) runtime.KeepAlive(v)
if ret == C.GIT_EUSER { if ret == C.int(ErrorCodeUser) && err != nil {
return data.err return err
} else if ret < 0 { }
if ret < 0 {
return MakeGitError(ret) return MakeGitError(ret)
} }

View File

@ -133,15 +133,15 @@ func (pb *Packbuilder) Written() uint32 {
} }
type PackbuilderForeachCallback func([]byte) error type PackbuilderForeachCallback func([]byte) error
type packbuilderCbData struct { type packbuilderCallbackData struct {
callback PackbuilderForeachCallback callback PackbuilderForeachCallback
err error errorTarget *error
} }
//export packbuilderForEachCb //export packbuilderForEachCallback
func packbuilderForEachCb(buf unsafe.Pointer, size C.size_t, handle unsafe.Pointer) int { func packbuilderForEachCallback(buf unsafe.Pointer, size C.size_t, handle unsafe.Pointer) C.int {
payload := pointerHandles.Get(handle) payload := pointerHandles.Get(handle)
data, ok := payload.(*packbuilderCbData) data, ok := payload.(*packbuilderCallbackData)
if !ok { if !ok {
panic("could not get packbuilder CB data") panic("could not get packbuilder CB data")
} }
@ -150,19 +150,20 @@ func packbuilderForEachCb(buf unsafe.Pointer, size C.size_t, handle unsafe.Point
err := data.callback(slice) err := data.callback(slice)
if err != nil { if err != nil {
data.err = err *data.errorTarget = err
return C.GIT_EUSER return C.int(ErrorCodeUser)
} }
return 0 return C.int(ErrorCodeOK)
} }
// ForEach repeatedly calls the callback with new packfile data until // ForEach repeatedly calls the callback with new packfile data until
// there is no more data or the callback returns an error // there is no more data or the callback returns an error
func (pb *Packbuilder) ForEach(callback PackbuilderForeachCallback) error { func (pb *Packbuilder) ForEach(callback PackbuilderForeachCallback) error {
data := packbuilderCbData{ var err error
data := packbuilderCallbackData{
callback: callback, callback: callback,
err: nil, errorTarget: &err,
} }
handle := pointerHandles.Track(&data) handle := pointerHandles.Track(&data)
defer pointerHandles.Untrack(handle) defer pointerHandles.Untrack(handle)
@ -170,13 +171,13 @@ func (pb *Packbuilder) ForEach(callback PackbuilderForeachCallback) error {
runtime.LockOSThread() runtime.LockOSThread()
defer runtime.UnlockOSThread() defer runtime.UnlockOSThread()
err := C._go_git_packbuilder_foreach(pb.ptr, handle) ret := C._go_git_packbuilder_foreach(pb.ptr, handle)
runtime.KeepAlive(pb) runtime.KeepAlive(pb)
if err == C.GIT_EUSER { if ret == C.int(ErrorCodeUser) && err != nil {
return data.err return err
} }
if err < 0 { if ret < 0 {
return MakeGitError(err) return MakeGitError(ret)
} }
return nil return nil

View File

@ -77,17 +77,22 @@ func (v *Repository) PatchFromBuffers(oldPath, newPath string, oldBuf, newBuf []
cNewPath := C.CString(newPath) cNewPath := C.CString(newPath)
defer C.free(unsafe.Pointer(cNewPath)) defer C.free(unsafe.Pointer(cNewPath))
copts := diffOptionsToC(opts, v) var err error
copts := opts.toC(v, &err)
defer freeDiffOptions(copts) defer freeDiffOptions(copts)
runtime.LockOSThread() runtime.LockOSThread()
defer runtime.UnlockOSThread() defer runtime.UnlockOSThread()
ecode := C.git_patch_from_buffers(&patchPtr, oldPtr, C.size_t(len(oldBuf)), cOldPath, newPtr, C.size_t(len(newBuf)), cNewPath, copts) ret := C.git_patch_from_buffers(&patchPtr, oldPtr, C.size_t(len(oldBuf)), cOldPath, newPtr, C.size_t(len(newBuf)), cNewPath, copts)
runtime.KeepAlive(oldBuf) runtime.KeepAlive(oldBuf)
runtime.KeepAlive(newBuf) runtime.KeepAlive(newBuf)
if ecode < 0 { if ret == C.int(ErrorCodeUser) && err != nil {
return nil, MakeGitError(ecode) return nil, err
} }
if ret < 0 {
return nil, MakeGitError(ret)
}
return newPatchFromC(patchPtr), nil return newPatchFromC(patchPtr), nil
} }

View File

@ -3,7 +3,7 @@ package git
/* /*
#include <git2.h> #include <git2.h>
extern void _go_git_populate_commit_sign_cb(git_rebase_options *opts); extern void _go_git_populate_rebase_callbacks(git_rebase_options *opts);
*/ */
import "C" import "C"
import ( import (
@ -71,25 +71,22 @@ func newRebaseOperationFromC(c *C.git_rebase_operation) *RebaseOperation {
return operation return operation
} }
//export commitSignCallback //export commitSigningCallback
func commitSignCallback(_signature *C.git_buf, _signature_field *C.git_buf, _commit_content *C.char, _payload unsafe.Pointer) C.int { func commitSigningCallback(errorMessage **C.char, _signature *C.git_buf, _signature_field *C.git_buf, _commit_content *C.char, _payload unsafe.Pointer) C.int {
opts, ok := pointerHandles.Get(_payload).(*RebaseOptions) opts, ok := pointerHandles.Get(_payload).(*RebaseOptions)
if !ok { if !ok {
panic("invalid sign payload") panic("invalid sign payload")
} }
if opts.CommitSigningCallback == nil { if opts.CommitSigningCallback == nil {
return C.GIT_PASSTHROUGH return C.int(ErrorCodePassthrough)
} }
commitContent := C.GoString(_commit_content) commitContent := C.GoString(_commit_content)
signature, signatureField, err := opts.CommitSigningCallback(commitContent) signature, signatureField, err := opts.CommitSigningCallback(commitContent)
if err != nil { if err != nil {
if gitError, ok := err.(*GitError); ok { return setCallbackError(errorMessage, err)
return C.int(gitError.Code)
}
return C.int(-1)
} }
fillBuf := func(bufData string, buf *C.git_buf) error { fillBuf := func(bufData string, buf *C.git_buf) error {
@ -110,16 +107,16 @@ func commitSignCallback(_signature *C.git_buf, _signature_field *C.git_buf, _com
if signatureField != "" { if signatureField != "" {
err := fillBuf(signatureField, _signature_field) err := fillBuf(signatureField, _signature_field)
if err != nil { if err != nil {
return C.int(-1) return setCallbackError(errorMessage, err)
} }
} }
err = fillBuf(signature, _signature) err = fillBuf(signature, _signature)
if err != nil { if err != nil {
return C.int(-1) return setCallbackError(errorMessage, err)
} }
return C.GIT_OK return C.int(ErrorCodeOK)
} }
// RebaseOptions are used to tell the rebase machinery how to operate // RebaseOptions are used to tell the rebase machinery how to operate
@ -158,25 +155,38 @@ func rebaseOptionsFromC(opts *C.git_rebase_options) RebaseOptions {
} }
} }
func (ro *RebaseOptions) toC() *C.git_rebase_options { func (ro *RebaseOptions) toC(errorTarget *error) *C.git_rebase_options {
if ro == nil { if ro == nil {
return nil return nil
} }
opts := &C.git_rebase_options{
cOptions := &C.git_rebase_options{
version: C.uint(ro.Version), version: C.uint(ro.Version),
quiet: C.int(ro.Quiet), quiet: C.int(ro.Quiet),
inmemory: C.int(ro.InMemory), inmemory: C.int(ro.InMemory),
rewrite_notes_ref: mapEmptyStringToNull(ro.RewriteNotesRef), rewrite_notes_ref: mapEmptyStringToNull(ro.RewriteNotesRef),
merge_options: *ro.MergeOptions.toC(), merge_options: *ro.MergeOptions.toC(),
checkout_options: *ro.CheckoutOptions.toC(), checkout_options: *ro.CheckoutOptions.toC(errorTarget),
} }
if ro.CommitSigningCallback != nil { if ro.CommitSigningCallback != nil {
C._go_git_populate_commit_sign_cb(opts) C._go_git_populate_rebase_callbacks(cOptions)
opts.payload = pointerHandles.Track(ro) cOptions.payload = pointerHandles.Track(ro)
} }
return opts return cOptions
}
func freeRebaseOptions(opts *C.git_rebase_options) {
if opts == nil {
return
}
C.free(unsafe.Pointer(opts.rewrite_notes_ref))
freeMergeOptions(&opts.merge_options)
freeCheckoutOptions(&opts.checkout_options)
if opts.payload != nil {
pointerHandles.Untrack(opts.payload)
}
} }
func mapEmptyStringToNull(ref string) *C.char { func mapEmptyStringToNull(ref string) *C.char {
@ -190,6 +200,7 @@ func mapEmptyStringToNull(ref string) *C.char {
type Rebase struct { type Rebase struct {
ptr *C.git_rebase ptr *C.git_rebase
r *Repository r *Repository
options *C.git_rebase_options
} }
// InitRebase initializes a rebase operation to rebase the changes in branch relative to upstream onto another branch. // InitRebase initializes a rebase operation to rebase the changes in branch relative to upstream onto another branch.
@ -210,15 +221,22 @@ func (r *Repository) InitRebase(branch *AnnotatedCommit, upstream *AnnotatedComm
} }
var ptr *C.git_rebase var ptr *C.git_rebase
err := C.git_rebase_init(&ptr, r.ptr, branch.ptr, upstream.ptr, onto.ptr, opts.toC()) var err error
cOpts := opts.toC(&err)
ret := C.git_rebase_init(&ptr, r.ptr, branch.ptr, upstream.ptr, onto.ptr, cOpts)
runtime.KeepAlive(branch) runtime.KeepAlive(branch)
runtime.KeepAlive(upstream) runtime.KeepAlive(upstream)
runtime.KeepAlive(onto) runtime.KeepAlive(onto)
if err < 0 { if ret == C.int(ErrorCodeUser) && err != nil {
return nil, MakeGitError(err) freeRebaseOptions(cOpts)
return nil, err
}
if ret < 0 {
freeRebaseOptions(cOpts)
return nil, MakeGitError(ret)
} }
return newRebaseFromC(ptr), nil return newRebaseFromC(ptr, cOpts), nil
} }
// OpenRebase opens an existing rebase that was previously started by either an invocation of InitRebase or by another client. // OpenRebase opens an existing rebase that was previously started by either an invocation of InitRebase or by another client.
@ -227,13 +245,20 @@ func (r *Repository) OpenRebase(opts *RebaseOptions) (*Rebase, error) {
defer runtime.UnlockOSThread() defer runtime.UnlockOSThread()
var ptr *C.git_rebase var ptr *C.git_rebase
err := C.git_rebase_open(&ptr, r.ptr, opts.toC()) var err error
cOpts := opts.toC(&err)
ret := C.git_rebase_open(&ptr, r.ptr, cOpts)
runtime.KeepAlive(r) runtime.KeepAlive(r)
if err < 0 { if ret == C.int(ErrorCodeUser) && err != nil {
return nil, MakeGitError(err) freeRebaseOptions(cOpts)
return nil, err
}
if ret < 0 {
freeRebaseOptions(cOpts)
return nil, MakeGitError(ret)
} }
return newRebaseFromC(ptr), nil return newRebaseFromC(ptr, cOpts), nil
} }
// OperationAt gets the rebase operation specified by the given index. // OperationAt gets the rebase operation specified by the given index.
@ -344,13 +369,14 @@ func (rebase *Rebase) Abort() error {
} }
// Free frees the Rebase object. // Free frees the Rebase object.
func (rebase *Rebase) Free() { func (r *Rebase) Free() {
runtime.SetFinalizer(rebase, nil) runtime.SetFinalizer(r, nil)
C.git_rebase_free(rebase.ptr) C.git_rebase_free(r.ptr)
freeRebaseOptions(r.options)
} }
func newRebaseFromC(ptr *C.git_rebase) *Rebase { func newRebaseFromC(ptr *C.git_rebase, opts *C.git_rebase_options) *Rebase {
rebase := &Rebase{ptr: ptr} rebase := &Rebase{ptr: ptr, options: opts}
runtime.SetFinalizer(rebase, (*Rebase).Free) runtime.SetFinalizer(rebase, (*Rebase).Free)
return rebase return rebase
} }

137
remote.go
View File

@ -6,12 +6,13 @@ package git
#include <git2.h> #include <git2.h>
#include <git2/sys/cred.h> #include <git2/sys/cred.h>
extern void _go_git_setup_callbacks(git_remote_callbacks *callbacks); extern void _go_git_populate_remote_callbacks(git_remote_callbacks *callbacks);
*/ */
import "C" import "C"
import ( import (
"crypto/x509" "crypto/x509"
"errors"
"reflect" "reflect"
"runtime" "runtime"
"strings" "strings"
@ -219,43 +220,55 @@ func populateRemoteCallbacks(ptr *C.git_remote_callbacks, callbacks *RemoteCallb
if callbacks == nil { if callbacks == nil {
return return
} }
C._go_git_setup_callbacks(ptr) C._go_git_populate_remote_callbacks(ptr)
ptr.payload = pointerHandles.Track(callbacks) ptr.payload = pointerHandles.Track(callbacks)
} }
//export sidebandProgressCallback //export sidebandProgressCallback
func sidebandProgressCallback(_str *C.char, _len C.int, data unsafe.Pointer) int { func sidebandProgressCallback(errorMessage **C.char, _str *C.char, _len C.int, data unsafe.Pointer) C.int {
callbacks := pointerHandles.Get(data).(*RemoteCallbacks) callbacks := pointerHandles.Get(data).(*RemoteCallbacks)
if callbacks.SidebandProgressCallback == nil { if callbacks.SidebandProgressCallback == nil {
return 0 return C.int(ErrorCodeOK)
} }
str := C.GoStringN(_str, _len) str := C.GoStringN(_str, _len)
return int(callbacks.SidebandProgressCallback(str)) ret := callbacks.SidebandProgressCallback(str)
if ret < 0 {
return setCallbackError(errorMessage, errors.New(ErrorCode(ret).String()))
}
return C.int(ErrorCodeOK)
} }
//export completionCallback //export completionCallback
func completionCallback(completion_type C.git_remote_completion_type, data unsafe.Pointer) int { func completionCallback(errorMessage **C.char, completion_type C.git_remote_completion_type, data unsafe.Pointer) C.int {
callbacks := pointerHandles.Get(data).(*RemoteCallbacks) callbacks := pointerHandles.Get(data).(*RemoteCallbacks)
if callbacks.CompletionCallback == nil { if callbacks.CompletionCallback == nil {
return 0 return C.int(ErrorCodeOK)
} }
return int(callbacks.CompletionCallback(RemoteCompletion(completion_type))) ret := callbacks.CompletionCallback(RemoteCompletion(completion_type))
if ret < 0 {
return setCallbackError(errorMessage, errors.New(ErrorCode(ret).String()))
}
return C.int(ErrorCodeOK)
} }
//export credentialsCallback //export credentialsCallback
func credentialsCallback(_cred **C.git_credential, _url *C.char, _username_from_url *C.char, allowed_types uint, data unsafe.Pointer) int { func credentialsCallback(
errorMessage **C.char,
_cred **C.git_credential,
_url *C.char,
_username_from_url *C.char,
allowed_types uint,
data unsafe.Pointer,
) C.int {
callbacks, _ := pointerHandles.Get(data).(*RemoteCallbacks) callbacks, _ := pointerHandles.Get(data).(*RemoteCallbacks)
if callbacks.CredentialsCallback == nil { if callbacks.CredentialsCallback == nil {
return C.GIT_PASSTHROUGH return C.int(ErrorCodePassthrough)
} }
url := C.GoString(_url) url := C.GoString(_url)
username_from_url := C.GoString(_username_from_url) username_from_url := C.GoString(_username_from_url)
cred, err := callbacks.CredentialsCallback(url, username_from_url, (CredentialType)(allowed_types)) cred, err := callbacks.CredentialsCallback(url, username_from_url, (CredentialType)(allowed_types))
if err != nil { if err != nil {
if gitError, ok := err.(*GitError); ok { return setCallbackError(errorMessage, err)
return int(gitError.Code)
}
return C.GIT_EUSER
} }
if cred != nil { if cred != nil {
*_cred = cred.ptr *_cred = cred.ptr
@ -264,41 +277,61 @@ func credentialsCallback(_cred **C.git_credential, _url *C.char, _username_from_
cred.ptr = nil cred.ptr = nil
runtime.SetFinalizer(cred, nil) runtime.SetFinalizer(cred, nil)
} }
return 0 return C.int(ErrorCodeOK)
} }
//export transferProgressCallback //export transferProgressCallback
func transferProgressCallback(stats *C.git_transfer_progress, data unsafe.Pointer) int { func transferProgressCallback(errorMessage **C.char, stats *C.git_transfer_progress, data unsafe.Pointer) C.int {
callbacks, _ := pointerHandles.Get(data).(*RemoteCallbacks) callbacks, _ := pointerHandles.Get(data).(*RemoteCallbacks)
if callbacks.TransferProgressCallback == nil { if callbacks.TransferProgressCallback == nil {
return 0 return C.int(ErrorCodeOK)
} }
return int(callbacks.TransferProgressCallback(newTransferProgressFromC(stats))) ret := callbacks.TransferProgressCallback(newTransferProgressFromC(stats))
if ret < 0 {
return setCallbackError(errorMessage, errors.New(ErrorCode(ret).String()))
}
return C.int(ErrorCodeOK)
} }
//export updateTipsCallback //export updateTipsCallback
func updateTipsCallback(_refname *C.char, _a *C.git_oid, _b *C.git_oid, data unsafe.Pointer) int { func updateTipsCallback(
errorMessage **C.char,
_refname *C.char,
_a *C.git_oid,
_b *C.git_oid,
data unsafe.Pointer,
) C.int {
callbacks, _ := pointerHandles.Get(data).(*RemoteCallbacks) callbacks, _ := pointerHandles.Get(data).(*RemoteCallbacks)
if callbacks.UpdateTipsCallback == nil { if callbacks.UpdateTipsCallback == nil {
return 0 return C.int(ErrorCodeOK)
} }
refname := C.GoString(_refname) refname := C.GoString(_refname)
a := newOidFromC(_a) a := newOidFromC(_a)
b := newOidFromC(_b) b := newOidFromC(_b)
return int(callbacks.UpdateTipsCallback(refname, a, b)) ret := callbacks.UpdateTipsCallback(refname, a, b)
if ret < 0 {
return setCallbackError(errorMessage, errors.New(ErrorCode(ret).String()))
}
return C.int(ErrorCodeOK)
} }
//export certificateCheckCallback //export certificateCheckCallback
func certificateCheckCallback(_cert *C.git_cert, _valid C.int, _host *C.char, data unsafe.Pointer) int { func certificateCheckCallback(
errorMessage **C.char,
_cert *C.git_cert,
_valid C.int,
_host *C.char,
data unsafe.Pointer,
) C.int {
callbacks, _ := pointerHandles.Get(data).(*RemoteCallbacks) 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 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 callbacks.CertificateCheckCallback == nil {
if _valid == 1 { if _valid == 0 {
return 0 return C.int(ErrorCodeCertificate)
} else {
return C.GIT_ECERTIFICATE
} }
return C.int(ErrorCodeOK)
} }
host := C.GoString(_host) host := C.GoString(_host)
valid := _valid != 0 valid := _valid != 0
@ -308,7 +341,10 @@ func certificateCheckCallback(_cert *C.git_cert, _valid C.int, _host *C.char, da
ccert := (*C.git_cert_x509)(unsafe.Pointer(_cert)) ccert := (*C.git_cert_x509)(unsafe.Pointer(_cert))
x509_certs, err := x509.ParseCertificates(C.GoBytes(ccert.data, C.int(ccert.len))) x509_certs, err := x509.ParseCertificates(C.GoBytes(ccert.data, C.int(ccert.len)))
if err != nil { if err != nil {
return C.GIT_EUSER return setCallbackError(errorMessage, err)
}
if len(x509_certs) < 1 {
return setCallbackError(errorMessage, errors.New("empty certificate list"))
} }
// we assume there's only one, which should hold true for any web server we want to talk to // we assume there's only one, which should hold true for any web server we want to talk to
@ -321,45 +357,56 @@ func certificateCheckCallback(_cert *C.git_cert, _valid C.int, _host *C.char, da
C.memcpy(unsafe.Pointer(&cert.Hostkey.HashSHA1[0]), unsafe.Pointer(&ccert.hash_sha1[0]), C.size_t(len(cert.Hostkey.HashSHA1))) C.memcpy(unsafe.Pointer(&cert.Hostkey.HashSHA1[0]), unsafe.Pointer(&ccert.hash_sha1[0]), C.size_t(len(cert.Hostkey.HashSHA1)))
C.memcpy(unsafe.Pointer(&cert.Hostkey.HashSHA256[0]), unsafe.Pointer(&ccert.hash_sha256[0]), C.size_t(len(cert.Hostkey.HashSHA256))) C.memcpy(unsafe.Pointer(&cert.Hostkey.HashSHA256[0]), unsafe.Pointer(&ccert.hash_sha256[0]), C.size_t(len(cert.Hostkey.HashSHA256)))
} else { } else {
cstr := C.CString("Unsupported certificate type") return setCallbackError(errorMessage, errors.New("unsupported certificate type"))
C.git_error_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)) ret := callbacks.CertificateCheckCallback(&cert, valid, host)
if ret < 0 {
return setCallbackError(errorMessage, errors.New(ErrorCode(ret).String()))
}
return C.int(ErrorCodeOK)
} }
//export packProgressCallback //export packProgressCallback
func packProgressCallback(stage C.int, current, total C.uint, data unsafe.Pointer) int { func packProgressCallback(errorMessage **C.char, stage C.int, current, total C.uint, data unsafe.Pointer) C.int {
callbacks, _ := pointerHandles.Get(data).(*RemoteCallbacks) callbacks, _ := pointerHandles.Get(data).(*RemoteCallbacks)
if callbacks.PackProgressCallback == nil { if callbacks.PackProgressCallback == nil {
return 0 return C.int(ErrorCodeOK)
} }
return int(callbacks.PackProgressCallback(int32(stage), uint32(current), uint32(total))) ret := callbacks.PackProgressCallback(int32(stage), uint32(current), uint32(total))
if ret < 0 {
return setCallbackError(errorMessage, errors.New(ErrorCode(ret).String()))
}
return C.int(ErrorCodeOK)
} }
//export pushTransferProgressCallback //export pushTransferProgressCallback
func pushTransferProgressCallback(current, total C.uint, bytes C.size_t, data unsafe.Pointer) int { func pushTransferProgressCallback(errorMessage **C.char, current, total C.uint, bytes C.size_t, data unsafe.Pointer) C.int {
callbacks, _ := pointerHandles.Get(data).(*RemoteCallbacks) callbacks, _ := pointerHandles.Get(data).(*RemoteCallbacks)
if callbacks.PushTransferProgressCallback == nil { if callbacks.PushTransferProgressCallback == nil {
return 0 return C.int(ErrorCodeOK)
} }
return int(callbacks.PushTransferProgressCallback(uint32(current), uint32(total), uint(bytes))) ret := callbacks.PushTransferProgressCallback(uint32(current), uint32(total), uint(bytes))
if ret < 0 {
return setCallbackError(errorMessage, errors.New(ErrorCode(ret).String()))
}
return C.int(ErrorCodeOK)
} }
//export pushUpdateReferenceCallback //export pushUpdateReferenceCallback
func pushUpdateReferenceCallback(refname, status *C.char, data unsafe.Pointer) int { func pushUpdateReferenceCallback(errorMessage **C.char, refname, status *C.char, data unsafe.Pointer) C.int {
callbacks, _ := pointerHandles.Get(data).(*RemoteCallbacks) callbacks, _ := pointerHandles.Get(data).(*RemoteCallbacks)
if callbacks.PushUpdateReferenceCallback == nil { if callbacks.PushUpdateReferenceCallback == nil {
return 0 return C.int(ErrorCodeOK)
} }
return int(callbacks.PushUpdateReferenceCallback(C.GoString(refname), C.GoString(status))) ret := callbacks.PushUpdateReferenceCallback(C.GoString(refname), C.GoString(status))
if ret < 0 {
return setCallbackError(errorMessage, errors.New(ErrorCode(ret).String()))
}
return C.int(ErrorCodeOK)
} }
func populateProxyOptions(ptr *C.git_proxy_options, opts *ProxyOptions) { func populateProxyOptions(ptr *C.git_proxy_options, opts *ProxyOptions) {
@ -373,6 +420,10 @@ func populateProxyOptions(ptr *C.git_proxy_options, opts *ProxyOptions) {
} }
func freeProxyOptions(ptr *C.git_proxy_options) { func freeProxyOptions(ptr *C.git_proxy_options) {
if ptr == nil {
return
}
C.free(unsafe.Pointer(ptr.url)) C.free(unsafe.Pointer(ptr.url))
} }

View File

@ -30,7 +30,7 @@ func assertHostname(cert *Certificate, valid bool, hostname string, t *testing.T
return ErrorCodeUser return ErrorCodeUser
} }
return 0 return ErrorCodeOK
} }
func TestCertificateCheck(t *testing.T) { func TestCertificateCheck(t *testing.T) {

View File

@ -17,8 +17,15 @@ const (
func (r *Repository) ResetToCommit(commit *Commit, resetType ResetType, opts *CheckoutOptions) error { func (r *Repository) ResetToCommit(commit *Commit, resetType ResetType, opts *CheckoutOptions) error {
runtime.LockOSThread() runtime.LockOSThread()
defer runtime.UnlockOSThread() defer runtime.UnlockOSThread()
ret := C.git_reset(r.ptr, commit.ptr, C.git_reset_t(resetType), opts.toC())
var err error
cOpts := opts.toC(&err)
defer freeCheckoutOptions(cOpts)
ret := C.git_reset(r.ptr, commit.ptr, C.git_reset_t(resetType), cOpts)
if ret == C.int(ErrorCodeUser) && err != nil {
return err
}
if ret < 0 { if ret < 0 {
return MakeGitError(ret) return MakeGitError(ret)
} }

View File

@ -15,12 +15,15 @@ type RevertOptions struct {
CheckoutOpts CheckoutOptions CheckoutOpts CheckoutOptions
} }
func (opts *RevertOptions) toC() *C.git_revert_options { func (opts *RevertOptions) toC(errorTarget *error) *C.git_revert_options {
if opts == nil {
return nil
}
return &C.git_revert_options{ return &C.git_revert_options{
version: C.GIT_REVERT_OPTIONS_VERSION, version: C.GIT_REVERT_OPTIONS_VERSION,
mainline: C.uint(opts.Mainline), mainline: C.uint(opts.Mainline),
merge_opts: *opts.MergeOpts.toC(), merge_opts: *opts.MergeOpts.toC(),
checkout_opts: *opts.CheckoutOpts.toC(), checkout_opts: *opts.CheckoutOpts.toC(errorTarget),
} }
} }
@ -33,6 +36,10 @@ func revertOptionsFromC(opts *C.git_revert_options) RevertOptions {
} }
func freeRevertOptions(opts *C.git_revert_options) { func freeRevertOptions(opts *C.git_revert_options) {
if opts != nil {
return
}
freeMergeOptions(&opts.merge_opts)
freeCheckoutOptions(&opts.checkout_opts) freeCheckoutOptions(&opts.checkout_opts)
} }
@ -57,19 +64,19 @@ func (r *Repository) Revert(commit *Commit, revertOptions *RevertOptions) error
runtime.LockOSThread() runtime.LockOSThread()
defer runtime.UnlockOSThread() defer runtime.UnlockOSThread()
var cOpts *C.git_revert_options var err error
cOpts := revertOptions.toC(&err)
if revertOptions != nil {
cOpts = revertOptions.toC()
defer freeRevertOptions(cOpts) defer freeRevertOptions(cOpts)
}
ecode := C.git_revert(r.ptr, commit.cast_ptr, cOpts) ret := C.git_revert(r.ptr, commit.cast_ptr, cOpts)
runtime.KeepAlive(r) runtime.KeepAlive(r)
runtime.KeepAlive(commit) runtime.KeepAlive(commit)
if ecode < 0 { if ret == C.int(ErrorCodeUser) && err != nil {
return MakeGitError(ecode) return err
}
if ret < 0 {
return MakeGitError(ret)
} }
return nil return nil
@ -81,11 +88,8 @@ func (r *Repository) RevertCommit(revertCommit *Commit, ourCommit *Commit, mainl
runtime.LockOSThread() runtime.LockOSThread()
defer runtime.UnlockOSThread() defer runtime.UnlockOSThread()
var cOpts *C.git_merge_options cOpts := mergeOptions.toC()
defer freeMergeOptions(cOpts)
if mergeOptions != nil {
cOpts = mergeOptions.toC()
}
var index *C.git_index var index *C.git_index

108
stash.go
View File

@ -3,7 +3,7 @@ package git
/* /*
#include <git2.h> #include <git2.h>
extern void _go_git_setup_stash_apply_progress_callbacks(git_stash_apply_options *opts); extern void _go_git_populate_stash_apply_callbacks(git_stash_apply_options *opts);
extern int _go_git_stash_foreach(git_repository *repo, void *payload); extern int _go_git_stash_foreach(git_repository *repo, void *payload);
*/ */
import "C" import "C"
@ -113,28 +113,28 @@ const (
// StashApplyProgressCallback is the apply operation notification callback. // StashApplyProgressCallback is the apply operation notification callback.
type StashApplyProgressCallback func(progress StashApplyProgress) error type StashApplyProgressCallback func(progress StashApplyProgress) error
type stashApplyProgressCallbackData struct {
type stashApplyProgressData struct { callback StashApplyProgressCallback
Callback StashApplyProgressCallback errorTarget *error
Error error
} }
//export stashApplyProgressCb //export stashApplyProgressCallback
func stashApplyProgressCb(progress C.git_stash_apply_progress_t, handle unsafe.Pointer) int { func stashApplyProgressCallback(progress C.git_stash_apply_progress_t, handle unsafe.Pointer) C.int {
payload := pointerHandles.Get(handle) payload := pointerHandles.Get(handle)
data, ok := payload.(*stashApplyProgressData) data, ok := payload.(*stashApplyProgressCallbackData)
if !ok { if !ok {
panic("could not retrieve data for handle") panic("could not retrieve data for handle")
} }
if data == nil || data.callback == nil {
return C.int(ErrorCodeOK)
}
if data != nil { err := data.callback(StashApplyProgress(progress))
err := data.Callback(StashApplyProgress(progress))
if err != nil { if err != nil {
data.Error = err *data.errorTarget = err
return C.GIT_EUSER return C.int(ErrorCodeUser)
} }
} return C.int(ErrorCodeOK)
return 0
} }
// StashApplyOptions represents options to control the apply operation. // StashApplyOptions represents options to control the apply operation.
@ -161,38 +161,34 @@ func DefaultStashApplyOptions() (StashApplyOptions, error) {
}, nil }, nil
} }
func (opts *StashApplyOptions) toC() ( func (opts *StashApplyOptions) toC(errorTarget *error) *C.git_stash_apply_options {
optsC *C.git_stash_apply_options, progressData *stashApplyProgressData) { if opts == nil {
return nil
if opts != nil {
progressData = &stashApplyProgressData{
Callback: opts.ProgressCallback,
} }
optsC := &C.git_stash_apply_options{
optsC = &C.git_stash_apply_options{
version: C.GIT_STASH_APPLY_OPTIONS_VERSION, version: C.GIT_STASH_APPLY_OPTIONS_VERSION,
flags: C.uint32_t(opts.Flags), flags: C.uint32_t(opts.Flags),
} }
populateCheckoutOptions(&optsC.checkout_options, &opts.CheckoutOptions) populateCheckoutOptions(&optsC.checkout_options, &opts.CheckoutOptions, errorTarget)
if opts.ProgressCallback != nil { if opts.ProgressCallback != nil {
C._go_git_setup_stash_apply_progress_callbacks(optsC) progressData := &stashApplyProgressCallbackData{
callback: opts.ProgressCallback,
errorTarget: errorTarget,
}
C._go_git_populate_stash_apply_callbacks(optsC)
optsC.progress_payload = pointerHandles.Track(progressData) optsC.progress_payload = pointerHandles.Track(progressData)
} }
} return optsC
return
}
// should be called after every call to toC() as deferred.
func untrackStashApplyOptionsCallback(optsC *C.git_stash_apply_options) {
if optsC != nil && optsC.progress_payload != nil {
pointerHandles.Untrack(optsC.progress_payload)
}
} }
func freeStashApplyOptions(optsC *C.git_stash_apply_options) { func freeStashApplyOptions(optsC *C.git_stash_apply_options) {
if optsC != nil { if optsC == nil {
freeCheckoutOptions(&optsC.checkout_options) return
} }
if optsC.progress_payload != nil {
pointerHandles.Untrack(optsC.progress_payload)
}
freeCheckoutOptions(&optsC.checkout_options)
} }
// Apply applies a single stashed state from the stash list. // Apply applies a single stashed state from the stash list.
@ -220,8 +216,8 @@ func freeStashApplyOptions(optsC *C.git_stash_apply_options) {
// //
// Error codes can be interogated with IsErrorCode(err, ErrorCodeNotFound). // Error codes can be interogated with IsErrorCode(err, ErrorCodeNotFound).
func (c *StashCollection) Apply(index int, opts StashApplyOptions) error { func (c *StashCollection) Apply(index int, opts StashApplyOptions) error {
optsC, progressData := opts.toC() var err error
defer untrackStashApplyOptionsCallback(optsC) optsC := opts.toC(&err)
defer freeStashApplyOptions(optsC) defer freeStashApplyOptions(optsC)
runtime.LockOSThread() runtime.LockOSThread()
@ -229,8 +225,8 @@ func (c *StashCollection) Apply(index int, opts StashApplyOptions) error {
ret := C.git_stash_apply(c.repo.ptr, C.size_t(index), optsC) ret := C.git_stash_apply(c.repo.ptr, C.size_t(index), optsC)
runtime.KeepAlive(c) runtime.KeepAlive(c)
if ret == C.GIT_EUSER { if ret == C.int(ErrorCodeUser) && err != nil {
return progressData.Error return err
} }
if ret < 0 { if ret < 0 {
return MakeGitError(ret) return MakeGitError(ret)
@ -245,26 +241,25 @@ func (c *StashCollection) Apply(index int, opts StashApplyOptions) error {
// 'message' is the message used when creating the stash and 'id' // 'message' is the message used when creating the stash and 'id'
// is the commit id of the stash. // is the commit id of the stash.
type StashCallback func(index int, message string, id *Oid) error type StashCallback func(index int, message string, id *Oid) error
type stashCallbackData struct { type stashCallbackData struct {
Callback StashCallback callback StashCallback
Error error errorTarget *error
} }
//export stashForeachCb //export stashForeachCallback
func stashForeachCb(index C.size_t, message *C.char, id *C.git_oid, handle unsafe.Pointer) int { func stashForeachCallback(index C.size_t, message *C.char, id *C.git_oid, handle unsafe.Pointer) C.int {
payload := pointerHandles.Get(handle) payload := pointerHandles.Get(handle)
data, ok := payload.(*stashCallbackData) data, ok := payload.(*stashCallbackData)
if !ok { if !ok {
panic("could not retrieve data for handle") panic("could not retrieve data for handle")
} }
err := data.Callback(int(index), C.GoString(message), newOidFromC(id)) err := data.callback(int(index), C.GoString(message), newOidFromC(id))
if err != nil { if err != nil {
data.Error = err *data.errorTarget = err
return C.GIT_EUSER return C.int(ErrorCodeUser)
} }
return 0 return C.int(ErrorCodeOK)
} }
// Foreach loops over all the stashed states and calls the callback // Foreach loops over all the stashed states and calls the callback
@ -272,10 +267,11 @@ func stashForeachCb(index C.size_t, message *C.char, id *C.git_oid, handle unsaf
// //
// If callback returns an error, this will stop looping. // If callback returns an error, this will stop looping.
func (c *StashCollection) Foreach(callback StashCallback) error { func (c *StashCollection) Foreach(callback StashCallback) error {
var err error
data := stashCallbackData{ data := stashCallbackData{
Callback: callback, callback: callback,
errorTarget: &err,
} }
handle := pointerHandles.Track(&data) handle := pointerHandles.Track(&data)
defer pointerHandles.Untrack(handle) defer pointerHandles.Untrack(handle)
@ -284,8 +280,8 @@ func (c *StashCollection) Foreach(callback StashCallback) error {
ret := C._go_git_stash_foreach(c.repo.ptr, handle) ret := C._go_git_stash_foreach(c.repo.ptr, handle)
runtime.KeepAlive(c) runtime.KeepAlive(c)
if ret == C.GIT_EUSER { if ret == C.int(ErrorCodeUser) && err != nil {
return data.Error return err
} }
if ret < 0 { if ret < 0 {
return MakeGitError(ret) return MakeGitError(ret)
@ -323,8 +319,8 @@ func (c *StashCollection) Drop(index int) error {
// Returns error code ErrorCodeNotFound if there's no stashed // Returns error code ErrorCodeNotFound if there's no stashed
// state for the given index. // state for the given index.
func (c *StashCollection) Pop(index int, opts StashApplyOptions) error { func (c *StashCollection) Pop(index int, opts StashApplyOptions) error {
optsC, progressData := opts.toC() var err error
defer untrackStashApplyOptionsCallback(optsC) optsC := opts.toC(&err)
defer freeStashApplyOptions(optsC) defer freeStashApplyOptions(optsC)
runtime.LockOSThread() runtime.LockOSThread()
@ -332,8 +328,8 @@ func (c *StashCollection) Pop(index int, opts StashApplyOptions) error {
ret := C.git_stash_pop(c.repo.ptr, C.size_t(index), optsC) ret := C.git_stash_pop(c.repo.ptr, C.size_t(index), optsC)
runtime.KeepAlive(c) runtime.KeepAlive(c)
if ret == C.GIT_EUSER { if ret == C.int(ErrorCodeUser) && err != nil {
return progressData.Error return err
} }
if ret < 0 { if ret < 0 {
return MakeGitError(ret) return MakeGitError(ret)

View File

@ -6,7 +6,9 @@ package git
extern int _go_git_visit_submodule(git_repository *repo, void *fct); extern int _go_git_visit_submodule(git_repository *repo, void *fct);
*/ */
import "C" import "C"
import ( import (
"errors"
"runtime" "runtime"
"unsafe" "unsafe"
) )
@ -108,30 +110,50 @@ func (c *SubmoduleCollection) Lookup(name string) (*Submodule, error) {
// SubmoduleCallback is a function that is called for every submodule found in SubmoduleCollection.Foreach. // SubmoduleCallback is a function that is called for every submodule found in SubmoduleCollection.Foreach.
type SubmoduleCallback func(sub *Submodule, name string) int type SubmoduleCallback func(sub *Submodule, name string) int
type submoduleCallbackData struct {
callback SubmoduleCallback
errorTarget *error
}
//export submoduleCallback //export submoduleCallback
func submoduleCallback(csub unsafe.Pointer, name *C.char, handle unsafe.Pointer) C.int { func submoduleCallback(csub unsafe.Pointer, name *C.char, handle unsafe.Pointer) C.int {
sub := &Submodule{(*C.git_submodule)(csub), nil} sub := &Submodule{(*C.git_submodule)(csub), nil}
if callback, ok := pointerHandles.Get(handle).(SubmoduleCallback); ok { data, ok := pointerHandles.Get(handle).(submoduleCallbackData)
return (C.int)(callback(sub, C.GoString(name))) if !ok {
} else {
panic("invalid submodule visitor callback") panic("invalid submodule visitor callback")
} }
ret := data.callback(sub, C.GoString(name))
if ret < 0 {
*data.errorTarget = errors.New(ErrorCode(ret).String())
return C.int(ErrorCodeUser)
} }
func (c *SubmoduleCollection) Foreach(cbk SubmoduleCallback) error { return C.int(ErrorCodeOK)
}
func (c *SubmoduleCollection) Foreach(callback SubmoduleCallback) error {
var err error
data := submoduleCallbackData{
callback: callback,
errorTarget: &err,
}
runtime.LockOSThread() runtime.LockOSThread()
defer runtime.UnlockOSThread() defer runtime.UnlockOSThread()
handle := pointerHandles.Track(cbk) handle := pointerHandles.Track(data)
defer pointerHandles.Untrack(handle) defer pointerHandles.Untrack(handle)
ret := C._go_git_visit_submodule(c.repo.ptr, handle) ret := C._go_git_visit_submodule(c.repo.ptr, handle)
runtime.KeepAlive(c) runtime.KeepAlive(c)
if ret == C.int(ErrorCodeUser) && err != nil {
return err
}
if ret < 0 { if ret < 0 {
return MakeGitError(ret) return MakeGitError(ret)
} }
return nil return nil
} }
@ -342,17 +364,18 @@ func (sub *Submodule) Open() (*Repository, error) {
} }
func (sub *Submodule) Update(init bool, opts *SubmoduleUpdateOptions) error { func (sub *Submodule) Update(init bool, opts *SubmoduleUpdateOptions) error {
var copts C.git_submodule_update_options var err error
err := populateSubmoduleUpdateOptions(&copts, opts) cOpts := populateSubmoduleUpdateOptions(&C.git_submodule_update_options{}, opts, &err)
if err != nil { defer freeSubmoduleUpdateOptions(cOpts)
return err
}
runtime.LockOSThread() runtime.LockOSThread()
defer runtime.UnlockOSThread() defer runtime.UnlockOSThread()
ret := C.git_submodule_update(sub.ptr, cbool(init), &copts) ret := C.git_submodule_update(sub.ptr, cbool(init), cOpts)
runtime.KeepAlive(sub) runtime.KeepAlive(sub)
if ret == C.int(ErrorCodeUser) && err != nil {
return err
}
if ret < 0 { if ret < 0 {
return MakeGitError(ret) return MakeGitError(ret)
} }
@ -360,15 +383,22 @@ func (sub *Submodule) Update(init bool, opts *SubmoduleUpdateOptions) error {
return nil return nil
} }
func populateSubmoduleUpdateOptions(ptr *C.git_submodule_update_options, opts *SubmoduleUpdateOptions) error { func populateSubmoduleUpdateOptions(ptr *C.git_submodule_update_options, opts *SubmoduleUpdateOptions, errorTarget *error) *C.git_submodule_update_options {
C.git_submodule_update_options_init(ptr, C.GIT_SUBMODULE_UPDATE_OPTIONS_VERSION) C.git_submodule_update_options_init(ptr, C.GIT_SUBMODULE_UPDATE_OPTIONS_VERSION)
if opts == nil { if opts == nil {
return nil return nil
} }
populateCheckoutOptions(&ptr.checkout_opts, opts.CheckoutOpts) populateCheckoutOptions(&ptr.checkout_opts, opts.CheckoutOpts, errorTarget)
populateFetchOptions(&ptr.fetch_opts, opts.FetchOptions) populateFetchOptions(&ptr.fetch_opts, opts.FetchOptions)
return nil return ptr
}
func freeSubmoduleUpdateOptions(ptr *C.git_submodule_update_options) {
if ptr == nil {
return
}
freeCheckoutOptions(&ptr.checkout_opts)
} }

32
tag.go
View File

@ -197,48 +197,48 @@ func (c *TagsCollection) ListWithMatch(pattern string) ([]string, error) {
// so repo.LookupTag() will return an error for these tags. Use // so repo.LookupTag() will return an error for these tags. Use
// repo.References.Lookup() instead. // repo.References.Lookup() instead.
type TagForeachCallback func(name string, id *Oid) error type TagForeachCallback func(name string, id *Oid) error
type tagForeachData struct { type tagForeachCallbackData struct {
callback TagForeachCallback callback TagForeachCallback
err error errorTarget *error
} }
//export gitTagForeachCb //export tagForeachCallback
func gitTagForeachCb(name *C.char, id *C.git_oid, handle unsafe.Pointer) int { func tagForeachCallback(name *C.char, id *C.git_oid, handle unsafe.Pointer) C.int {
payload := pointerHandles.Get(handle) payload := pointerHandles.Get(handle)
data, ok := payload.(*tagForeachData) data, ok := payload.(*tagForeachCallbackData)
if !ok { if !ok {
panic("could not retrieve tag foreach CB handle") panic("could not retrieve tag foreach CB handle")
} }
err := data.callback(C.GoString(name), newOidFromC(id)) err := data.callback(C.GoString(name), newOidFromC(id))
if err != nil { if err != nil {
data.err = err *data.errorTarget = err
return C.GIT_EUSER return C.int(ErrorCodeUser)
} }
return 0 return C.int(ErrorCodeOK)
} }
// Foreach calls the callback for each tag in the repository. // Foreach calls the callback for each tag in the repository.
func (c *TagsCollection) Foreach(callback TagForeachCallback) error { func (c *TagsCollection) Foreach(callback TagForeachCallback) error {
data := tagForeachData{ var err error
data := tagForeachCallbackData{
callback: callback, callback: callback,
err: nil, errorTarget: &err,
} }
handle := pointerHandles.Track(&data) handle := pointerHandles.Track(&data)
defer pointerHandles.Untrack(handle) defer pointerHandles.Untrack(handle)
runtime.LockOSThread() runtime.LockOSThread()
defer runtime.UnlockOSThread() defer runtime.UnlockOSThread()
err := C._go_git_tag_foreach(c.repo.ptr, handle) ret := C._go_git_tag_foreach(c.repo.ptr, handle)
runtime.KeepAlive(c) runtime.KeepAlive(c)
if err == C.GIT_EUSER { if ret == C.int(ErrorCodeUser) && err != nil {
return data.err return err
} }
if err < 0 { if ret < 0 {
return MakeGitError(err) return MakeGitError(ret)
} }
return nil return nil

42
tree.go
View File

@ -8,6 +8,7 @@ extern int _go_git_treewalk(git_tree *tree, git_treewalk_mode mode, void *ptr);
import "C" import "C"
import ( import (
"errors"
"runtime" "runtime"
"unsafe" "unsafe"
) )
@ -120,33 +121,46 @@ func (t *Tree) EntryCount() uint64 {
} }
type TreeWalkCallback func(string, *TreeEntry) int type TreeWalkCallback func(string, *TreeEntry) int
type treeWalkCallbackData struct {
callback TreeWalkCallback
errorTarget *error
}
//export treeWalkCallback //export treeWalkCallback
func treeWalkCallback(_root *C.char, entry *C.git_tree_entry, ptr unsafe.Pointer) C.int { func treeWalkCallback(_root *C.char, entry *C.git_tree_entry, ptr unsafe.Pointer) C.int {
root := C.GoString(_root) data, ok := pointerHandles.Get(ptr).(*treeWalkCallbackData)
if !ok {
if callback, ok := pointerHandles.Get(ptr).(TreeWalkCallback); ok {
return C.int(callback(root, newTreeEntry(entry)))
} else {
panic("invalid treewalk callback") panic("invalid treewalk callback")
} }
ret := data.callback(C.GoString(_root), newTreeEntry(entry))
if ret < 0 {
*data.errorTarget = errors.New(ErrorCode(ret).String())
return C.int(ErrorCodeUser)
}
return C.int(ErrorCodeOK)
} }
func (t *Tree) Walk(callback TreeWalkCallback) error { func (t *Tree) Walk(callback TreeWalkCallback) error {
var err error
data := treeWalkCallbackData{
callback: callback,
errorTarget: &err,
}
runtime.LockOSThread() runtime.LockOSThread()
defer runtime.UnlockOSThread() defer runtime.UnlockOSThread()
ptr := pointerHandles.Track(callback) handle := pointerHandles.Track(&data)
defer pointerHandles.Untrack(ptr) defer pointerHandles.Untrack(handle)
err := C._go_git_treewalk( ret := C._go_git_treewalk(t.cast_ptr, C.GIT_TREEWALK_PRE, handle)
t.cast_ptr,
C.GIT_TREEWALK_PRE,
ptr,
)
runtime.KeepAlive(t) runtime.KeepAlive(t)
if err < 0 { if ret == C.int(ErrorCodeUser) && err != nil {
return MakeGitError(err) return err
}
if ret < 0 {
return MakeGitError(ret)
} }
return nil return nil

398
wrapper.c
View File

@ -1,36 +1,149 @@
#include "_cgo_export.h" #include "_cgo_export.h"
#include <git2.h> #include <git2.h>
#include <git2/sys/odb_backend.h> #include <git2/sys/odb_backend.h>
#include <git2/sys/refdb_backend.h> #include <git2/sys/refdb_backend.h>
#include <git2/sys/cred.h> #include <git2/sys/cred.h>
typedef int (*gogit_submodule_cbk)(git_submodule *sm, const char *name, void *payload); // There are two ways in which to declare a callback:
//
// * If there is a guarantee that the callback will always be called within the
// same stack (e.g. by passing the callback directly into a function / into a
// struct that goes into a function), the following pattern is preferred,
// which preserves the error object as-is:
//
// // myfile.go
// type FooCallback func(...) (..., error)
// type FooCallbackData struct {
// callback FooCallback
// errorTarget *error
// }
//
// //export fooCallback
// func fooCallback(..., handle unsafe.Pointer) C.int {
// payload := pointerHandles.Get(handle)
// data := payload.(*fooCallbackData)
// ...
// err := data.callback(...)
// if err != nil {
// *data.errorTarget = err
// return C.int(ErrorCodeUser)
// }
// return C.int(ErrorCodeOK)
// }
//
// func MyFunction(... callback FooCallback) error {
// var err error
// data := fooCallbackData{
// callback: callback,
// errorTarget: &err,
// }
// handle := pointerHandles.Track(&data)
// defer pointerHandles.Untrack(handle)
//
// runtime.LockOSThread()
// defer runtime.UnlockOSThread()
//
// ret := C._go_git_my_function(..., handle)
// if ret == C.int(ErrorCodeUser) && err != nil {
// return err
// }
// if ret < 0 {
// return MakeGitError(ret)
// }
// return nil
// }
//
// // wrapper.c
// int _go_git_my_function(..., void *payload)
// {
// return git_my_function(..., (git_foo_cb)&fooCallback, payload);
// }
//
// * Otherwise, if the same callback can be invoked from multiple functions or
// from different stacks (e.g. when passing the callback to an object), a
// different pattern should be used instead, which has the downside of losing
// the original error object and converting it to a GitError:
//
// // myfile.go
// type FooCallback func(...) (..., error)
//
// //export fooCallback
// func fooCallback(errorMessage **C.char, ..., handle unsafe.Pointer) C.int {
// callback := pointerHandles.Get(data).(*FooCallback)
// ...
// err := callback(...)
// if err != nil {
// return setCallbackError(errorMessage, err)
// }
// return C.int(ErrorCodeOK)
// }
//
// // wrapper.c
// static int foo_callback(...)
// {
// char *error_message = NULL;
// const int ret = fooCallback(&error_message, ...);
// return set_callback_error(error_message, ret);
// }
void _go_git_populate_apply_cb(git_apply_options *options) /**
* Sets the thread-local error to the provided string. This needs to happen in
* C because Go might change Goroutines _just_ before returning, which would
* lose the contents of the error message.
*/
static int set_callback_error(char *error_message, int ret)
{ {
options->delta_cb = (git_apply_delta_cb)deltaApplyCallback; if (error_message != NULL) {
options->hunk_cb = (git_apply_hunk_cb)hunkApplyCallback; if (ret < 0)
git_error_set_str(GIT_ERROR_CALLBACK, error_message);
free(error_message);
}
return ret;
} }
void _go_git_populate_commit_sign_cb(git_rebase_options *opts) void _go_git_populate_apply_callbacks(git_apply_options *options)
{ {
opts->signing_cb = (git_commit_signing_cb)commitSignCallback; options->delta_cb = (git_apply_delta_cb)&deltaApplyCallback;
options->hunk_cb = (git_apply_hunk_cb)&hunkApplyCallback;
} }
void _go_git_populate_remote_cb(git_clone_options *opts) static int commit_signing_callback(
git_buf *signature,
git_buf *signature_field,
const char *commit_contents,
void *payload)
{ {
opts->remote_cb = (git_remote_create_cb)remoteCreateCallback; char *error_message = NULL;
const int ret = commitSigningCallback(
&error_message,
signature,
signature_field,
(char *)commit_contents,
payload
);
return set_callback_error(error_message, ret);
} }
void _go_git_populate_checkout_cb(git_checkout_options *opts) void _go_git_populate_rebase_callbacks(git_rebase_options *opts)
{ {
opts->notify_cb = (git_checkout_notify_cb)checkoutNotifyCallback; opts->signing_cb = commit_signing_callback;
opts->progress_cb = (git_checkout_progress_cb)checkoutProgressCallback; }
void _go_git_populate_clone_callbacks(git_clone_options *opts)
{
opts->remote_cb = (git_remote_create_cb)&remoteCreateCallback;
}
void _go_git_populate_checkout_callbacks(git_checkout_options *opts)
{
opts->notify_cb = (git_checkout_notify_cb)&checkoutNotifyCallback;
opts->progress_cb = (git_checkout_progress_cb)&checkoutProgressCallback;
} }
int _go_git_visit_submodule(git_repository *repo, void *fct) int _go_git_visit_submodule(git_repository *repo, void *fct)
{ {
return git_submodule_foreach(repo, (gogit_submodule_cbk)&submoduleCallback, fct); return git_submodule_foreach(repo, (git_submodule_cb)&submoduleCallback, fct);
} }
int _go_git_treewalk(git_tree *tree, git_treewalk_mode mode, void *ptr) int _go_git_treewalk(git_tree *tree, git_treewalk_mode mode, void *ptr)
@ -40,28 +153,26 @@ int _go_git_treewalk(git_tree *tree, git_treewalk_mode mode, void *ptr)
int _go_git_packbuilder_foreach(git_packbuilder *pb, void *payload) int _go_git_packbuilder_foreach(git_packbuilder *pb, void *payload)
{ {
return git_packbuilder_foreach(pb, (git_packbuilder_foreach_cb)&packbuilderForEachCb, payload); return git_packbuilder_foreach(pb, (git_packbuilder_foreach_cb)&packbuilderForEachCallback, payload);
} }
int _go_git_odb_foreach(git_odb *db, void *payload) int _go_git_odb_foreach(git_odb *db, void *payload)
{ {
return git_odb_foreach(db, (git_odb_foreach_cb)&odbForEachCb, payload); return git_odb_foreach(db, (git_odb_foreach_cb)&odbForEachCallback, payload);
} }
void _go_git_odb_backend_free(git_odb_backend *backend) void _go_git_odb_backend_free(git_odb_backend *backend)
{ {
if (backend->free) if (!backend->free)
backend->free(backend);
return; return;
backend->free(backend);
} }
void _go_git_refdb_backend_free(git_refdb_backend *backend) void _go_git_refdb_backend_free(git_refdb_backend *backend)
{ {
if (backend->free) if (!backend->free)
backend->free(backend);
return; return;
backend->free(backend);
} }
int _go_git_diff_foreach(git_diff *diff, int eachFile, int eachHunk, int eachLine, void *payload) int _go_git_diff_foreach(git_diff *diff, int eachFile, int eachHunk, int eachLine, void *payload)
@ -70,83 +181,210 @@ int _go_git_diff_foreach(git_diff *diff, int eachFile, int eachHunk, int eachLin
git_diff_hunk_cb hcb = NULL; git_diff_hunk_cb hcb = NULL;
git_diff_line_cb lcb = NULL; git_diff_line_cb lcb = NULL;
if (eachFile) { if (eachFile)
fcb = (git_diff_file_cb)&diffForEachFileCb; fcb = (git_diff_file_cb)&diffForEachFileCallback;
} if (eachHunk)
hcb = (git_diff_hunk_cb)&diffForEachHunkCallback;
if (eachHunk) { if (eachLine)
hcb = (git_diff_hunk_cb)&diffForEachHunkCb; lcb = (git_diff_line_cb)&diffForEachLineCallback;
}
if (eachLine) {
lcb = (git_diff_line_cb)&diffForEachLineCb;
}
return git_diff_foreach(diff, fcb, NULL, hcb, lcb, payload); return git_diff_foreach(diff, fcb, NULL, hcb, lcb, payload);
} }
int _go_git_diff_blobs(git_blob *old, const char *old_path, git_blob *new, const char *new_path, git_diff_options *opts, int eachFile, int eachHunk, int eachLine, void *payload) int _go_git_diff_blobs(
git_blob *old,
const char *old_path,
git_blob *new,
const char *new_path,
git_diff_options *opts,
int eachFile,
int eachHunk,
int eachLine,
void *payload)
{ {
git_diff_file_cb fcb = NULL; git_diff_file_cb fcb = NULL;
git_diff_hunk_cb hcb = NULL; git_diff_hunk_cb hcb = NULL;
git_diff_line_cb lcb = NULL; git_diff_line_cb lcb = NULL;
if (eachFile) { if (eachFile)
fcb = (git_diff_file_cb)&diffForEachFileCb; fcb = (git_diff_file_cb)&diffForEachFileCallback;
} if (eachHunk)
hcb = (git_diff_hunk_cb)&diffForEachHunkCallback;
if (eachHunk) { if (eachLine)
hcb = (git_diff_hunk_cb)&diffForEachHunkCb; lcb = (git_diff_line_cb)&diffForEachLineCallback;
}
if (eachLine) {
lcb = (git_diff_line_cb)&diffForEachLineCb;
}
return git_diff_blobs(old, old_path, new, new_path, opts, fcb, NULL, hcb, lcb, payload); return git_diff_blobs(old, old_path, new, new_path, opts, fcb, NULL, hcb, lcb, payload);
} }
void _go_git_setup_diff_notify_callbacks(git_diff_options *opts) { void _go_git_setup_diff_notify_callbacks(git_diff_options *opts)
opts->notify_cb = (git_diff_notify_cb)diffNotifyCb; {
opts->notify_cb = (git_diff_notify_cb)&diffNotifyCallback;
} }
void _go_git_setup_callbacks(git_remote_callbacks *callbacks) { static int sideband_progress_callback(const char *str, int len, void *payload)
typedef int (*completion_cb)(git_remote_completion_type type, void *data); {
typedef int (*update_tips_cb)(const char *refname, const git_oid *a, const git_oid *b, void *data); char *error_message = NULL;
typedef int (*push_update_reference_cb)(const char *refname, const char *status, void *data); const int ret = sidebandProgressCallback(&error_message, (char *)str, len, payload);
return set_callback_error(error_message, ret);
callbacks->sideband_progress = (git_transport_message_cb)sidebandProgressCallback;
callbacks->completion = (completion_cb)completionCallback;
callbacks->credentials = (git_credential_acquire_cb)credentialsCallback;
callbacks->transfer_progress = (git_transfer_progress_cb)transferProgressCallback;
callbacks->update_tips = (update_tips_cb)updateTipsCallback;
callbacks->certificate_check = (git_transport_certificate_check_cb) certificateCheckCallback;
callbacks->pack_progress = (git_packbuilder_progress) packProgressCallback;
callbacks->push_transfer_progress = (git_push_transfer_progress) pushTransferProgressCallback;
callbacks->push_update_reference = (push_update_reference_cb) pushUpdateReferenceCallback;
} }
int _go_git_index_add_all(git_index *index, const git_strarray *pathspec, unsigned int flags, void *callback) { static int completion_callback(git_remote_completion_type completion_type, void *data)
{
char *error_message = NULL;
const int ret = completionCallback(&error_message, completion_type, data);
return set_callback_error(error_message, ret);
}
static int credentials_callback(
git_credential **cred,
const char *url,
const char *username_from_url,
unsigned int allowed_types,
void *data)
{
char *error_message = NULL;
const int ret = credentialsCallback(
&error_message,
cred,
(char *)url,
(char *)username_from_url,
allowed_types,
data
);
return set_callback_error(error_message, ret);
}
static int transfer_progress_callback(const git_transfer_progress *stats, void *data)
{
char *error_message = NULL;
const int ret = transferProgressCallback(
&error_message,
(git_transfer_progress *)stats,
data
);
return set_callback_error(error_message, ret);
}
static int update_tips_callback(const char *refname, const git_oid *a, const git_oid *b, void *data)
{
char *error_message = NULL;
const int ret = updateTipsCallback(
&error_message,
(char *)refname,
(git_oid *)a,
(git_oid *)b,
data
);
return set_callback_error(error_message, ret);
}
static int certificate_check_callback(git_cert *cert, int valid, const char *host, void *data)
{
char *error_message = NULL;
const int ret = certificateCheckCallback(
&error_message,
cert,
valid,
(char *)host,
data
);
return set_callback_error(error_message, ret);
}
static int pack_progress_callback(int stage, unsigned int current, unsigned int total, void *data)
{
char *error_message = NULL;
const int ret = packProgressCallback(
&error_message,
stage,
current,
total,
data
);
return set_callback_error(error_message, ret);
}
static int push_transfer_progress_callback(
unsigned int current,
unsigned int total,
size_t bytes,
void *data)
{
char *error_message = NULL;
const int ret = pushTransferProgressCallback(
&error_message,
current,
total,
bytes,
data
);
return set_callback_error(error_message, ret);
}
static int push_update_reference_callback(const char *refname, const char *status, void *data)
{
char *error_message = NULL;
const int ret = pushUpdateReferenceCallback(
&error_message,
(char *)refname,
(char *)status,
data
);
return set_callback_error(error_message, ret);
}
void _go_git_populate_remote_callbacks(git_remote_callbacks *callbacks)
{
callbacks->sideband_progress = sideband_progress_callback;
callbacks->completion = completion_callback;
callbacks->credentials = credentials_callback;
callbacks->transfer_progress = transfer_progress_callback;
callbacks->update_tips = update_tips_callback;
callbacks->certificate_check = certificate_check_callback;
callbacks->pack_progress = pack_progress_callback;
callbacks->push_transfer_progress = push_transfer_progress_callback;
callbacks->push_update_reference = push_update_reference_callback;
}
int _go_git_index_add_all(git_index *index, const git_strarray *pathspec, unsigned int flags, void *callback)
{
git_index_matched_path_cb cb = callback ? (git_index_matched_path_cb)&indexMatchedPathCallback : NULL; git_index_matched_path_cb cb = callback ? (git_index_matched_path_cb)&indexMatchedPathCallback : NULL;
return git_index_add_all(index, pathspec, flags, cb, callback); return git_index_add_all(index, pathspec, flags, cb, callback);
} }
int _go_git_index_update_all(git_index *index, const git_strarray *pathspec, void *callback) { int _go_git_index_update_all(git_index *index, const git_strarray *pathspec, void *callback)
{
git_index_matched_path_cb cb = callback ? (git_index_matched_path_cb)&indexMatchedPathCallback : NULL; git_index_matched_path_cb cb = callback ? (git_index_matched_path_cb)&indexMatchedPathCallback : NULL;
return git_index_update_all(index, pathspec, cb, callback); return git_index_update_all(index, pathspec, cb, callback);
} }
int _go_git_index_remove_all(git_index *index, const git_strarray *pathspec, void *callback) { int _go_git_index_remove_all(git_index *index, const git_strarray *pathspec, void *callback)
{
git_index_matched_path_cb cb = callback ? (git_index_matched_path_cb)&indexMatchedPathCallback : NULL; git_index_matched_path_cb cb = callback ? (git_index_matched_path_cb)&indexMatchedPathCallback : NULL;
return git_index_remove_all(index, pathspec, cb, callback); return git_index_remove_all(index, pathspec, cb, callback);
} }
int _go_git_tag_foreach(git_repository *repo, void *payload) int _go_git_tag_foreach(git_repository *repo, void *payload)
{ {
return git_tag_foreach(repo, (git_tag_foreach_cb)&gitTagForeachCb, payload); return git_tag_foreach(repo, (git_tag_foreach_cb)&tagForeachCallback, payload);
} }
int _go_git_merge_file(git_merge_file_result* out, char* ancestorContents, size_t ancestorLen, char* ancestorPath, unsigned int ancestorMode, char* oursContents, size_t oursLen, char* oursPath, unsigned int oursMode, char* theirsContents, size_t theirsLen, char* theirsPath, unsigned int theirsMode, git_merge_file_options* copts) { int _go_git_merge_file(
git_merge_file_result* out,
char* ancestorContents,
size_t ancestorLen,
char* ancestorPath,
unsigned int ancestorMode,
char* oursContents,
size_t oursLen,
char* oursPath,
unsigned int oursMode,
char* theirsContents,
size_t theirsLen,
char* theirsPath,
unsigned int theirsMode,
git_merge_file_options* copts)
{
git_merge_file_input ancestor = GIT_MERGE_FILE_INPUT_INIT; git_merge_file_input ancestor = GIT_MERGE_FILE_INPUT_INIT;
git_merge_file_input ours = GIT_MERGE_FILE_INPUT_INIT; git_merge_file_input ours = GIT_MERGE_FILE_INPUT_INIT;
git_merge_file_input theirs = GIT_MERGE_FILE_INPUT_INIT; git_merge_file_input theirs = GIT_MERGE_FILE_INPUT_INIT;
@ -169,12 +407,14 @@ int _go_git_merge_file(git_merge_file_result* out, char* ancestorContents, size_
return git_merge_file(out, &ancestor, &ours, &theirs, copts); return git_merge_file(out, &ancestor, &ours, &theirs, copts);
} }
void _go_git_setup_stash_apply_progress_callbacks(git_stash_apply_options *opts) { void _go_git_populate_stash_apply_callbacks(git_stash_apply_options *opts)
opts->progress_cb = (git_stash_apply_progress_cb)stashApplyProgressCb; {
opts->progress_cb = (git_stash_apply_progress_cb)&stashApplyProgressCallback;
} }
int _go_git_stash_foreach(git_repository *repo, void *payload) { int _go_git_stash_foreach(git_repository *repo, void *payload)
return git_stash_foreach(repo, (git_stash_cb)&stashForeachCb, payload); {
return git_stash_foreach(repo, (git_stash_cb)&stashForeachCallback, payload);
} }
int _go_git_writestream_write(git_writestream *stream, const char *buffer, size_t len) int _go_git_writestream_write(git_writestream *stream, const char *buffer, size_t len)
@ -192,16 +432,21 @@ void _go_git_writestream_free(git_writestream *stream)
stream->free(stream); stream->free(stream);
} }
git_credential_t _go_git_credential_credtype(git_credential *cred) { git_credential_t _go_git_credential_credtype(git_credential *cred)
{
return cred->credtype; return cred->credtype;
} }
int _go_git_odb_write_pack(git_odb_writepack **out, git_odb *db, void *progress_payload) int _go_git_odb_write_pack(git_odb_writepack **out, git_odb *db, void *progress_payload)
{ {
return git_odb_write_pack(out, db, (git_transfer_progress_cb)transferProgressCallback, progress_payload); return git_odb_write_pack(out, db, transfer_progress_callback, progress_payload);
} }
int _go_git_odb_writepack_append(git_odb_writepack *writepack, const void *data, size_t size, git_transfer_progress *stats) int _go_git_odb_writepack_append(
git_odb_writepack *writepack,
const void *data,
size_t size,
git_transfer_progress *stats)
{ {
return writepack->append(writepack, data, size, stats); return writepack->append(writepack, data, size, stats);
} }
@ -216,12 +461,15 @@ void _go_git_odb_writepack_free(git_odb_writepack *writepack)
writepack->free(writepack); writepack->free(writepack);
} }
int _go_git_indexer_new(git_indexer **out, const char *path, unsigned int mode, git_odb *odb, void *progress_cb_payload) int _go_git_indexer_new(
git_indexer **out,
const char *path,
unsigned int mode,
git_odb *odb,
void *progress_cb_payload)
{ {
git_indexer_options indexer_options = GIT_INDEXER_OPTIONS_INIT; git_indexer_options indexer_options = GIT_INDEXER_OPTIONS_INIT;
indexer_options.progress_cb = (git_transfer_progress_cb)transferProgressCallback; indexer_options.progress_cb = transfer_progress_callback;
indexer_options.progress_cb_payload = progress_cb_payload; indexer_options.progress_cb_payload = progress_cb_payload;
return git_indexer_new(out, path, mode, odb, &indexer_options); return git_indexer_new(out, path, mode, odb, &indexer_options);
} }
/* EOF */