summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorlhchavez <[email protected]>2020-12-05 13:13:59 -0800
committerGitHub <[email protected]>2020-12-05 13:13:59 -0800
commit5d8eaf7e65c404a0d10d3705697dd99369630dda (patch)
tree85e2f17a8c3ee1fe3ec6a6e680237907ec8dc638
parent137c05e802d5e11a5ab54809bc8be8f61ccece21 (diff)
Refactor all callbacks (#700)
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`.
-rw-r--r--checkout.go99
-rw-r--r--cherrypick.go18
-rw-r--r--clone.go79
-rw-r--r--diff.go394
-rw-r--r--diff_test.go31
-rw-r--r--git.go11
-rw-r--r--index.go55
-rw-r--r--merge.go21
-rw-r--r--odb.go35
-rw-r--r--packbuilder.go35
-rw-r--r--patch.go13
-rw-r--r--rebase.go90
-rw-r--r--remote.go137
-rw-r--r--remote_test.go2
-rw-r--r--reset.go9
-rw-r--r--revert.go36
-rw-r--r--stash.go120
-rw-r--r--submodule.go58
-rw-r--r--tag.go36
-rw-r--r--tree.go42
-rw-r--r--wrapper.c404
21 files changed, 1150 insertions, 575 deletions
diff --git a/checkout.go b/checkout.go
index fa6609a..3a60eb8 100644
--- a/checkout.go
+++ b/checkout.go
@@ -3,10 +3,11 @@ package git
/*
#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 (
+ "errors"
"os"
"runtime"
"unsafe"
@@ -75,10 +76,10 @@ func checkoutOptionsFromC(c *C.git_checkout_options) CheckoutOptions {
NotifyFlags: CheckoutNotifyType(c.notify_flags),
}
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 {
- opts.ProgressCallback = pointerHandles.Get(c.progress_payload).(*CheckoutOptions).ProgressCallback
+ opts.ProgressCallback = pointerHandles.Get(c.progress_payload).(*checkoutCallbackData).options.ProgressCallback
}
if c.target_directory != nil {
opts.TargetDirectory = C.GoString(c.target_directory)
@@ -86,19 +87,26 @@ func checkoutOptionsFromC(c *C.git_checkout_options) CheckoutOptions {
return opts
}
-func (opts *CheckoutOptions) toC() *C.git_checkout_options {
+func (opts *CheckoutOptions) toC(errorTarget *error) *C.git_checkout_options {
if opts == nil {
return nil
}
- c := C.git_checkout_options{}
- populateCheckoutOptions(&c, opts)
- return &c
+ return populateCheckoutOptions(&C.git_checkout_options{}, opts, errorTarget)
+}
+
+type checkoutCallbackData struct {
+ options *CheckoutOptions
+ errorTarget *error
}
//export checkoutNotifyCallback
-func checkoutNotifyCallback(why C.git_checkout_notify_t, cpath *C.char, cbaseline, ctarget, cworkdir, data unsafe.Pointer) int {
- if data == nil {
- return 0
+func checkoutNotifyCallback(
+ why C.git_checkout_notify_t,
+ cpath *C.char,
+ cbaseline, ctarget, cworkdir, handle unsafe.Pointer,
+) C.int {
+ if handle == nil {
+ return C.int(ErrorCodeOK)
}
path := C.GoString(cpath)
var baseline, target, workdir DiffFile
@@ -111,26 +119,35 @@ func checkoutNotifyCallback(why C.git_checkout_notify_t, cpath *C.char, cbaselin
if cworkdir != nil {
workdir = diffFileFromC((*C.git_diff_file)(cworkdir))
}
- opts := pointerHandles.Get(data).(*CheckoutOptions)
- if opts.NotifyCallback == nil {
- return 0
+ data := pointerHandles.Get(handle).(*checkoutCallbackData)
+ if data.options.NotifyCallback == nil {
+ return C.int(ErrorCodeOK)
+ }
+ 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 int(opts.NotifyCallback(CheckoutNotifyType(why), path, baseline, target, workdir))
+ return C.int(ErrorCodeOK)
}
//export checkoutProgressCallback
-func checkoutProgressCallback(path *C.char, completed_steps, total_steps C.size_t, data unsafe.Pointer) int {
- opts := pointerHandles.Get(data).(*CheckoutOptions)
- if opts.ProgressCallback == nil {
- return 0
+func checkoutProgressCallback(
+ path *C.char,
+ completed_steps, total_steps C.size_t,
+ 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
// C-struct. Returns a pointer to ptr, or nil if opts is nil, in order
// 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 {
return nil
}
@@ -142,14 +159,18 @@ func populateCheckoutOptions(ptr *C.git_checkout_options, opts *CheckoutOptions)
ptr.file_mode = C.uint(opts.FileMode.Perm())
ptr.notify_flags = C.uint(opts.NotifyFlags)
if opts.NotifyCallback != nil || opts.ProgressCallback != nil {
- C._go_git_populate_checkout_cb(ptr)
- }
- payload := pointerHandles.Track(opts)
- if opts.NotifyCallback != nil {
- ptr.notify_payload = payload
- }
- if opts.ProgressCallback != nil {
- ptr.progress_payload = payload
+ C._go_git_populate_checkout_callbacks(ptr)
+ data := &checkoutCallbackData{
+ options: opts,
+ errorTarget: errorTarget,
+ }
+ payload := pointerHandles.Track(data)
+ if opts.NotifyCallback != nil {
+ ptr.notify_payload = payload
+ }
+ if opts.ProgressCallback != nil {
+ ptr.progress_payload = payload
+ }
}
if 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 {
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()
defer runtime.UnlockOSThread()
- cOpts := opts.toC()
+ var err error
+ cOpts := opts.toC(&err)
defer freeCheckoutOptions(cOpts)
ret := C.git_checkout_head(v.ptr, cOpts)
runtime.KeepAlive(v)
+
+ if ret == C.int(ErrorCodeUser) && err != nil {
+ return err
+ }
if ret < 0 {
return MakeGitError(ret)
}
@@ -209,11 +237,15 @@ func (v *Repository) CheckoutIndex(index *Index, opts *CheckoutOptions) error {
runtime.LockOSThread()
defer runtime.UnlockOSThread()
- cOpts := opts.toC()
+ var err error
+ cOpts := opts.toC(&err)
defer freeCheckoutOptions(cOpts)
ret := C.git_checkout_index(v.ptr, iptr, cOpts)
runtime.KeepAlive(v)
+ if ret == C.int(ErrorCodeUser) && err != nil {
+ return err
+ }
if ret < 0 {
return MakeGitError(ret)
}
@@ -225,11 +257,16 @@ func (v *Repository) CheckoutTree(tree *Tree, opts *CheckoutOptions) error {
runtime.LockOSThread()
defer runtime.UnlockOSThread()
- cOpts := opts.toC()
+ var err error
+ cOpts := opts.toC(&err)
defer freeCheckoutOptions(cOpts)
ret := C.git_checkout_tree(v.ptr, tree.ptr, cOpts)
runtime.KeepAlive(v)
+ runtime.KeepAlive(tree)
+ if ret == C.int(ErrorCodeUser) && err != nil {
+ return err
+ }
if ret < 0 {
return MakeGitError(ret)
}
diff --git a/cherrypick.go b/cherrypick.go
index 7ef1666..b5d6c70 100644
--- a/cherrypick.go
+++ b/cherrypick.go
@@ -25,7 +25,7 @@ func cherrypickOptionsFromC(c *C.git_cherrypick_options) CherrypickOptions {
return opts
}
-func (opts *CherrypickOptions) toC() *C.git_cherrypick_options {
+func (opts *CherrypickOptions) toC(errorTarget *error) *C.git_cherrypick_options {
if opts == nil {
return nil
}
@@ -33,7 +33,7 @@ func (opts *CherrypickOptions) toC() *C.git_cherrypick_options {
c.version = C.uint(opts.Version)
c.mainline = C.uint(opts.Mainline)
c.merge_opts = *opts.MergeOpts.toC()
- c.checkout_opts = *opts.CheckoutOpts.toC()
+ c.checkout_opts = *opts.CheckoutOpts.toC(errorTarget)
return &c
}
@@ -41,6 +41,7 @@ func freeCherrypickOpts(ptr *C.git_cherrypick_options) {
if ptr == nil {
return
}
+ freeMergeOptions(&ptr.merge_opts)
freeCheckoutOptions(&ptr.checkout_opts)
}
@@ -62,14 +63,18 @@ func (v *Repository) Cherrypick(commit *Commit, opts CherrypickOptions) error {
runtime.LockOSThread()
defer runtime.UnlockOSThread()
- cOpts := opts.toC()
+ var err error
+ cOpts := opts.toC(&err)
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(commit)
- if ecode < 0 {
- return MakeGitError(ecode)
+ if ret == C.int(ErrorCodeUser) && err != nil {
+ return err
+ }
+ if ret < 0 {
+ return MakeGitError(ret)
}
return nil
}
@@ -79,6 +84,7 @@ func (r *Repository) CherrypickCommit(pick, our *Commit, opts CherrypickOptions)
defer runtime.UnlockOSThread()
cOpts := opts.MergeOpts.toC()
+ defer freeMergeOptions(cOpts)
var ptr *C.git_index
ret := C.git_cherrypick_commit(&ptr, r.ptr, pick.cast_ptr, our.cast_ptr, C.uint(opts.Mainline), cOpts)
diff --git a/clone.go b/clone.go
index 26251c8..8166b08 100644
--- a/clone.go
+++ b/clone.go
@@ -3,10 +3,11 @@ package git
/*
#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 (
+ "errors"
"runtime"
"unsafe"
)
@@ -28,20 +29,23 @@ func Clone(url string, path string, options *CloneOptions) (*Repository, error)
cpath := C.CString(path)
defer C.free(unsafe.Pointer(cpath))
- copts := (*C.git_clone_options)(C.calloc(1, C.size_t(unsafe.Sizeof(C.git_clone_options{}))))
- populateCloneOptions(copts, options)
- defer freeCloneOptions(copts)
+ var err error
+ cOptions := populateCloneOptions(&C.git_clone_options{}, options, &err)
+ defer freeCloneOptions(cOptions)
if len(options.CheckoutBranch) != 0 {
- copts.checkout_branch = C.CString(options.CheckoutBranch)
+ cOptions.checkout_branch = C.CString(options.CheckoutBranch)
}
runtime.LockOSThread()
defer runtime.UnlockOSThread()
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 {
return nil, MakeGitError(ret)
}
@@ -50,47 +54,69 @@ func Clone(url string, path string, options *CloneOptions) (*Repository, error)
}
//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)
url := C.GoString(curl)
repo := newRepositoryFromC((*C.git_repository)(crepo))
// We don't own this repository, so make sure we don't try to free it
runtime.SetFinalizer(repo, nil)
- if opts, ok := pointerHandles.Get(payload).(CloneOptions); ok {
- remote, errorCode := opts.RemoteCreateCallback(repo, name, url)
- // clear finalizer as the calling C function will
- // free the remote itself
- runtime.SetFinalizer(remote, nil)
+ data, ok := pointerHandles.Get(payload).(*cloneCallbackData)
+ if !ok {
+ panic("invalid remote create callback")
+ }
- if errorCode == ErrorCodeOK && remote != nil {
- cptr := (**C.git_remote)(cremote)
- *cptr = remote.ptr
- } else if errorCode == ErrorCodeOK && remote == nil {
- panic("no remote created by callback")
- }
+ remote, ret := data.options.RemoteCreateCallback(repo, name, url)
+ // clear finalizer as the calling C function will
+ // free the remote itself
+ runtime.SetFinalizer(remote, nil)
- return C.int(errorCode)
- } else {
- panic("invalid remote create callback")
+ if ret < 0 {
+ *data.errorTarget = errors.New(ErrorCode(ret).String())
+ return C.int(ErrorCodeUser)
+ }
+
+ if remote == nil {
+ panic("no remote created by callback")
}
+
+ cptr := (**C.git_remote)(cremote)
+ *cptr = remote.ptr
+
+ return C.int(ErrorCodeOK)
+}
+
+type cloneCallbackData struct {
+ options *CloneOptions
+ errorTarget *error
}
-func populateCloneOptions(ptr *C.git_clone_options, opts *CloneOptions) {
+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)
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)
ptr.bare = cbool(opts.Bare)
if opts.RemoteCreateCallback != nil {
+ data := &cloneCallbackData{
+ options: opts,
+ errorTarget: errorTarget,
+ }
// Go v1.1 does not allow to assign a C function pointer
- C._go_git_populate_remote_cb(ptr)
- ptr.remote_cb_payload = pointerHandles.Track(*opts)
+ C._go_git_populate_clone_callbacks(ptr)
+ ptr.remote_cb_payload = pointerHandles.Track(data)
}
+
+ return ptr
}
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))
}
diff --git a/diff.go b/diff.go
index e50cd70..dd793e4 100644
--- a/diff.go
+++ b/diff.go
@@ -3,7 +3,7 @@ package git
/*
#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 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);
@@ -294,11 +294,11 @@ func (diff *Diff) Stats() (*DiffStats, error) {
return stats, nil
}
-type diffForEachData struct {
- FileCallback DiffForEachFileCallback
- HunkCallback DiffForEachHunkCallback
- LineCallback DiffForEachLineCallback
- Error error
+type diffForEachCallbackData struct {
+ fileCallback DiffForEachFileCallback
+ hunkCallback DiffForEachHunkCallback
+ lineCallback DiffForEachLineCallback
+ errorTarget *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)
}
- data := &diffForEachData{
- FileCallback: cbFile,
+ var err error
+ data := &diffForEachCallbackData{
+ fileCallback: cbFile,
+ errorTarget: &err,
}
handle := pointerHandles.Track(data)
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)
- if ecode < 0 {
- return data.Error
+ if ret == C.int(ErrorCodeUser) && err != nil {
+ return err
+ }
+ if ret < 0 {
+ return MakeGitError(ret)
}
+
return nil
}
-//export diffForEachFileCb
-func diffForEachFileCb(delta *C.git_diff_delta, progress C.float, handle unsafe.Pointer) int {
+//export diffForEachFileCallback
+func diffForEachFileCallback(delta *C.git_diff_delta, progress C.float, handle unsafe.Pointer) C.int {
payload := pointerHandles.Get(handle)
- data, ok := payload.(*diffForEachData)
+ data, ok := payload.(*diffForEachCallbackData)
if !ok {
panic("could not retrieve data for handle")
}
- data.HunkCallback = nil
- if data.FileCallback != nil {
- cb, err := data.FileCallback(diffDeltaFromC(delta), float64(progress))
+ data.hunkCallback = nil
+ if data.fileCallback != nil {
+ cb, err := data.fileCallback(diffDeltaFromC(delta), float64(progress))
if err != nil {
- data.Error = err
- return -1
+ *data.errorTarget = err
+ return C.int(ErrorCodeUser)
}
- data.HunkCallback = cb
+ data.hunkCallback = cb
}
- return 0
+ return C.int(ErrorCodeOK)
}
type DiffForEachHunkCallback func(DiffHunk) (DiffForEachLineCallback, error)
-//export diffForEachHunkCb
-func diffForEachHunkCb(delta *C.git_diff_delta, hunk *C.git_diff_hunk, handle unsafe.Pointer) int {
+//export diffForEachHunkCallback
+func diffForEachHunkCallback(delta *C.git_diff_delta, hunk *C.git_diff_hunk, handle unsafe.Pointer) C.int {
payload := pointerHandles.Get(handle)
- data, ok := payload.(*diffForEachData)
+ data, ok := payload.(*diffForEachCallbackData)
if !ok {
panic("could not retrieve data for handle")
}
- data.LineCallback = nil
- if data.HunkCallback != nil {
- cb, err := data.HunkCallback(diffHunkFromC(hunk))
+ data.lineCallback = nil
+ if data.hunkCallback != nil {
+ cb, err := data.hunkCallback(diffHunkFromC(hunk))
if err != nil {
- data.Error = err
- return -1
+ *data.errorTarget = err
+ return C.int(ErrorCodeUser)
}
- data.LineCallback = cb
+ data.lineCallback = cb
}
- return 0
+ return C.int(ErrorCodeOK)
}
type DiffForEachLineCallback func(DiffLine) error
-//export diffForEachLineCb
-func diffForEachLineCb(delta *C.git_diff_delta, hunk *C.git_diff_hunk, line *C.git_diff_line, handle unsafe.Pointer) int {
+//export diffForEachLineCallback
+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)
- data, ok := payload.(*diffForEachData)
+ data, ok := payload.(*diffForEachCallbackData)
if !ok {
panic("could not retrieve data for handle")
}
- err := data.LineCallback(diffLineFromC(line))
+ err := data.lineCallback(diffLineFromC(line))
if err != nil {
- data.Error = err
- return -1
+ *data.errorTarget = err
+ return C.int(ErrorCodeUser)
}
- return 0
+ return C.int(ErrorCodeOK)
}
func (diff *Diff) Patch(deltaIndex int) (*Patch, error) {
@@ -586,93 +595,98 @@ var (
ErrDeltaSkip = errors.New("Skip delta")
)
-type diffNotifyData struct {
- Callback DiffNotifyCallback
- Repository *Repository
- Error error
+type diffNotifyCallbackData struct {
+ callback DiffNotifyCallback
+ repository *Repository
+ errorTarget *error
}
-//export diffNotifyCb
-func diffNotifyCb(_diff_so_far unsafe.Pointer, delta_to_add *C.git_diff_delta, matched_pathspec *C.char, handle unsafe.Pointer) int {
+//export diffNotifyCallback
+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)
payload := pointerHandles.Get(handle)
- data, ok := payload.(*diffNotifyData)
+ data, ok := payload.(*diffNotifyCallbackData)
if !ok {
panic("could not retrieve data for handle")
}
- if data != nil {
- // We are not taking ownership of this diff pointer, so no finalizer is set.
- diff := &Diff{
- ptr: diff_so_far,
- repo: data.Repository,
- runFinalizer: false,
- }
+ if data == nil {
+ return C.int(ErrorCodeOK)
+ }
- err := data.Callback(diff, diffDeltaFromC(delta_to_add), C.GoString(matched_pathspec))
+ // We are not taking ownership of this diff pointer, so no finalizer is set.
+ diff := &Diff{
+ ptr: diff_so_far,
+ repo: data.repository,
+ runFinalizer: false,
+ }
- // Since the callback could theoretically keep a reference to the diff
- // (which could be freed by libgit2 if an error occurs later during the
- // diffing process), this converts a use-after-free (terrible!) into a nil
- // dereference ("just" pretty bad).
- diff.ptr = nil
+ err := data.callback(diff, diffDeltaFromC(delta_to_add), C.GoString(matched_pathspec))
- if err == ErrDeltaSkip {
- return 1
- } else if err != nil {
- data.Error = err
- return -1
- } else {
- return 0
- }
+ // Since the callback could theoretically keep a reference to the diff
+ // (which could be freed by libgit2 if an error occurs later during the
+ // diffing process), this converts a use-after-free (terrible!) into a nil
+ // dereference ("just" pretty bad).
+ diff.ptr = nil
+
+ if err == ErrDeltaSkip {
+ return 1
}
- return 0
+ if err != nil {
+ *data.errorTarget = err
+ return C.int(ErrorCodeUser)
+ }
+
+ return C.int(ErrorCodeOK)
}
-func diffOptionsToC(opts *DiffOptions, repo *Repository) (copts *C.git_diff_options) {
- cpathspec := C.git_strarray{}
- if opts != nil {
- notifyData := &diffNotifyData{
- Callback: opts.NotifyCallback,
- Repository: repo,
- }
+func (opts *DiffOptions) toC(repo *Repository, errorTarget *error) *C.git_diff_options {
+ if opts == nil {
+ return nil
+ }
- if opts.Pathspec != nil {
- cpathspec.count = C.size_t(len(opts.Pathspec))
- cpathspec.strings = makeCStringsFromStrings(opts.Pathspec)
- }
+ cpathspec := C.git_strarray{}
+ if opts.Pathspec != nil {
+ cpathspec.count = C.size_t(len(opts.Pathspec))
+ cpathspec.strings = makeCStringsFromStrings(opts.Pathspec)
+ }
- copts = &C.git_diff_options{
- version: C.GIT_DIFF_OPTIONS_VERSION,
- flags: C.uint32_t(opts.Flags),
- ignore_submodules: C.git_submodule_ignore_t(opts.IgnoreSubmodules),
- pathspec: cpathspec,
- context_lines: C.uint32_t(opts.ContextLines),
- interhunk_lines: C.uint32_t(opts.InterhunkLines),
- id_abbrev: C.uint16_t(opts.IdAbbrev),
- max_size: C.git_off_t(opts.MaxSize),
- old_prefix: C.CString(opts.OldPrefix),
- new_prefix: C.CString(opts.NewPrefix),
- }
+ copts := &C.git_diff_options{
+ version: C.GIT_DIFF_OPTIONS_VERSION,
+ flags: C.uint32_t(opts.Flags),
+ ignore_submodules: C.git_submodule_ignore_t(opts.IgnoreSubmodules),
+ pathspec: cpathspec,
+ context_lines: C.uint32_t(opts.ContextLines),
+ interhunk_lines: C.uint32_t(opts.InterhunkLines),
+ id_abbrev: C.uint16_t(opts.IdAbbrev),
+ max_size: C.git_off_t(opts.MaxSize),
+ old_prefix: C.CString(opts.OldPrefix),
+ new_prefix: C.CString(opts.NewPrefix),
+ }
- if opts.NotifyCallback != nil {
- C._go_git_setup_diff_notify_callbacks(copts)
- copts.payload = pointerHandles.Track(notifyData)
+ if opts.NotifyCallback != nil {
+ notifyData := &diffNotifyCallbackData{
+ callback: opts.NotifyCallback,
+ repository: repo,
+ errorTarget: errorTarget,
}
+ C._go_git_setup_diff_notify_callbacks(copts)
+ copts.payload = pointerHandles.Track(notifyData)
}
- return
+ return copts
}
func freeDiffOptions(copts *C.git_diff_options) {
- if copts != nil {
- cpathspec := copts.pathspec
- freeStrarray(&cpathspec)
- C.free(unsafe.Pointer(copts.old_prefix))
- C.free(unsafe.Pointer(copts.new_prefix))
- if copts.payload != nil {
- pointerHandles.Untrack(copts.payload)
- }
+ if copts == nil {
+ return
+ }
+ cpathspec := copts.pathspec
+ freeStrarray(&cpathspec)
+ C.free(unsafe.Pointer(copts.old_prefix))
+ C.free(unsafe.Pointer(copts.new_prefix))
+ if copts.payload != nil {
+ pointerHandles.Untrack(copts.payload)
}
}
@@ -688,17 +702,21 @@ func (v *Repository) DiffTreeToTree(oldTree, newTree *Tree, opts *DiffOptions) (
newPtr = newTree.cast_ptr
}
- copts := diffOptionsToC(opts, v)
+ var err error
+ copts := opts.toC(v, &err)
defer freeDiffOptions(copts)
runtime.LockOSThread()
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(newTree)
- if ecode < 0 {
- return nil, MakeGitError(ecode)
+ if ret == C.int(ErrorCodeUser) && err != nil {
+ return nil, err
+ }
+ if ret < 0 {
+ return nil, MakeGitError(ret)
}
return newDiffFromC(diffPtr, v), nil
}
@@ -711,17 +729,22 @@ func (v *Repository) DiffTreeToWorkdir(oldTree *Tree, opts *DiffOptions) (*Diff,
oldPtr = oldTree.cast_ptr
}
- copts := diffOptionsToC(opts, v)
+ var err error
+ copts := opts.toC(v, &err)
defer freeDiffOptions(copts)
runtime.LockOSThread()
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)
- if ecode < 0 {
- return nil, MakeGitError(ecode)
+ if ret == C.int(ErrorCodeUser) && err != nil {
+ return nil, err
+ }
+ if ret < 0 {
+ return nil, MakeGitError(ret)
}
+
return newDiffFromC(diffPtr, v), nil
}
@@ -738,18 +761,23 @@ func (v *Repository) DiffTreeToIndex(oldTree *Tree, index *Index, opts *DiffOpti
indexPtr = index.ptr
}
- copts := diffOptionsToC(opts, v)
+ var err error
+ copts := opts.toC(v, &err)
defer freeDiffOptions(copts)
runtime.LockOSThread()
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(index)
- if ecode < 0 {
- return nil, MakeGitError(ecode)
+ if ret == C.int(ErrorCodeUser) && err != nil {
+ return nil, err
+ }
+ if ret < 0 {
+ return nil, MakeGitError(ret)
}
+
return newDiffFromC(diffPtr, v), nil
}
@@ -761,17 +789,22 @@ func (v *Repository) DiffTreeToWorkdirWithIndex(oldTree *Tree, opts *DiffOptions
oldPtr = oldTree.cast_ptr
}
- copts := diffOptionsToC(opts, v)
+ var err error
+ copts := opts.toC(v, &err)
defer freeDiffOptions(copts)
runtime.LockOSThread()
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)
- if ecode < 0 {
- return nil, MakeGitError(ecode)
+ if ret == C.int(ErrorCodeUser) && err != nil {
+ return nil, err
}
+ if ret < 0 {
+ return nil, MakeGitError(ret)
+ }
+
return newDiffFromC(diffPtr, v), nil
}
@@ -783,25 +816,32 @@ func (v *Repository) DiffIndexToWorkdir(index *Index, opts *DiffOptions) (*Diff,
indexPtr = index.ptr
}
- copts := diffOptionsToC(opts, v)
+ var err error
+ copts := opts.toC(v, &err)
defer freeDiffOptions(copts)
runtime.LockOSThread()
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)
- if ecode < 0 {
- return nil, MakeGitError(ecode)
+ if ret == C.int(ErrorCodeUser) && err != nil {
+ return nil, err
}
+ if ret < 0 {
+ return nil, MakeGitError(ret)
+ }
+
return newDiffFromC(diffPtr, v), nil
}
// 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.
func DiffBlobs(oldBlob *Blob, oldAsPath string, newBlob *Blob, newAsPath string, opts *DiffOptions, fileCallback DiffForEachFileCallback, detail DiffDetail) error {
- data := &diffForEachData{
- FileCallback: fileCallback,
+ var err error
+ data := &diffForEachCallbackData{
+ fileCallback: fileCallback,
+ errorTarget: &err,
}
intHunks := C.int(0)
@@ -833,17 +873,20 @@ func DiffBlobs(oldBlob *Blob, oldAsPath string, newBlob *Blob, newAsPath string,
newBlobPath := C.CString(newAsPath)
defer C.free(unsafe.Pointer(newBlobPath))
- copts := diffOptionsToC(opts, repo)
+ copts := opts.toC(repo, &err)
defer freeDiffOptions(copts)
runtime.LockOSThread()
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(newBlob)
- if ecode < 0 {
- return MakeGitError(ecode)
+ if ret == C.int(ErrorCodeUser) && err != nil {
+ return err
+ }
+ if ret < 0 {
+ return MakeGitError(ret)
}
return nil
@@ -864,56 +907,59 @@ type ApplyOptions struct {
Flags uint
}
+type applyCallbackData struct {
+ options *ApplyOptions
+ errorTarget *error
+}
+
//export hunkApplyCallback
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 {
panic("invalid apply options payload")
}
- if opts.ApplyHunkCallback == nil {
- return 0
+ if data.options.ApplyHunkCallback == nil {
+ return C.int(ErrorCodeOK)
}
hunk := diffHunkFromC(_hunk)
- apply, err := opts.ApplyHunkCallback(&hunk)
+ apply, err := data.options.ApplyHunkCallback(&hunk)
if err != nil {
- if gitError, ok := err.(*GitError); ok {
- return C.int(gitError.Code)
- }
- return -1
- } else if apply {
- return 0
- } else {
+ *data.errorTarget = err
+ return C.int(ErrorCodeUser)
+ }
+
+ if !apply {
return 1
}
+ return C.int(ErrorCodeOK)
}
//export deltaApplyCallback
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 {
panic("invalid apply options payload")
}
- if opts.ApplyDeltaCallback == nil {
- return 0
+ if data.options.ApplyDeltaCallback == nil {
+ return C.int(ErrorCodeOK)
}
delta := diffDeltaFromC(_delta)
- apply, err := opts.ApplyDeltaCallback(&delta)
+ apply, err := data.options.ApplyDeltaCallback(&delta)
if err != nil {
- if gitError, ok := err.(*GitError); ok {
- return C.int(gitError.Code)
- }
- return -1
- } else if apply {
- return 0
- } else {
+ *data.errorTarget = err
+ return C.int(ErrorCodeUser)
+ }
+
+ if !apply {
return 1
}
+ return C.int(ErrorCodeOK)
}
// 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)
if int(ecode) != 0 {
-
return nil, MakeGitError(ecode)
}
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 {
return nil
}
@@ -943,13 +988,26 @@ func (a *ApplyOptions) toC() *C.git_apply_options {
}
if a.ApplyDeltaCallback != nil || a.ApplyHunkCallback != nil {
- C._go_git_populate_apply_cb(opts)
- opts.payload = pointerHandles.Track(a)
+ data := &applyCallbackData{
+ options: a,
+ errorTarget: errorTarget,
+ }
+ C._go_git_populate_apply_callbacks(opts)
+ opts.payload = pointerHandles.Track(data)
}
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 {
return &ApplyOptions{
Flags: uint(opts.flags),
@@ -979,13 +1037,19 @@ func (v *Repository) ApplyDiff(diff *Diff, location ApplyLocation, opts *ApplyOp
runtime.LockOSThread()
defer runtime.UnlockOSThread()
- cOpts := opts.toC()
- ecode := C.git_apply(v.ptr, diff.ptr, C.git_apply_location_t(location), cOpts)
+ var err error
+ 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(diff)
runtime.KeepAlive(cOpts)
- if ecode < 0 {
- return MakeGitError(ecode)
+ if ret == C.int(ErrorCodeUser) && err != nil {
+ return err
+ }
+ if ret < 0 {
+ return MakeGitError(ret)
}
return nil
@@ -996,14 +1060,20 @@ func (v *Repository) ApplyToTree(diff *Diff, tree *Tree, opts *ApplyOptions) (*I
runtime.LockOSThread()
defer runtime.UnlockOSThread()
+ var err error
+ cOpts := opts.toC(&err)
+ defer freeApplyOptions(cOpts)
+
var indexPtr *C.git_index
- cOpts := opts.toC()
- ecode := C.git_apply_to_tree(&indexPtr, v.ptr, tree.cast_ptr, diff.ptr, cOpts)
+ ret := C.git_apply_to_tree(&indexPtr, v.ptr, tree.cast_ptr, diff.ptr, cOpts)
runtime.KeepAlive(diff)
runtime.KeepAlive(tree)
runtime.KeepAlive(cOpts)
- if ecode != 0 {
- return nil, MakeGitError(ecode)
+ if ret == C.int(ErrorCodeUser) && err != nil {
+ return nil, err
+ }
+ if ret < 0 {
+ return nil, MakeGitError(ret)
}
return newIndexFromC(indexPtr, v), nil
diff --git a/diff_test.go b/diff_test.go
index 3a1e65e..1e3cd5c 100644
--- a/diff_test.go
+++ b/diff_test.go
@@ -173,9 +173,8 @@ func TestDiffTreeToTree(t *testing.T) {
}, DiffDetailLines)
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) {
@@ -486,13 +485,15 @@ func TestApplyToTree(t *testing.T) {
diffAC, err := repo.DiffTreeToTree(treeA, treeC, nil)
checkFatal(t, err)
+ errMessageDropped := errors.New("message dropped")
+
for _, tc := range []struct {
name string
tree *Tree
diff *Diff
applyHunkCallback ApplyHunkCallback
applyDeltaCallback ApplyDeltaCallback
- error error
+ err error
expectedDiff *Diff
}{
{
@@ -505,7 +506,7 @@ func TestApplyToTree(t *testing.T) {
name: "applying a conflicting patch errors",
tree: treeB,
diff: diffAC,
- error: &GitError{
+ err: &GitError{
Message: "hunk at line 1 did not apply",
Code: ErrorCodeApplyFail,
Class: ErrorClassPatch,
@@ -529,12 +530,8 @@ func TestApplyToTree(t *testing.T) {
name: "hunk callback erroring fails the call",
tree: treeA,
diff: diffAB,
- applyHunkCallback: func(*DiffHunk) (bool, error) { return true, errors.New("message dropped") },
- error: &GitError{
- Message: "Generic",
- Code: ErrorCodeGeneric,
- Class: ErrorClassInvalid,
- },
+ applyHunkCallback: func(*DiffHunk) (bool, error) { return true, errMessageDropped },
+ err: errMessageDropped,
},
{
name: "delta callback returning false does not apply",
@@ -546,12 +543,8 @@ func TestApplyToTree(t *testing.T) {
name: "delta callback erroring fails the call",
tree: treeA,
diff: diffAB,
- applyDeltaCallback: func(*DiffDelta) (bool, error) { return true, errors.New("message dropped") },
- error: &GitError{
- Message: "Generic",
- Code: ErrorCodeGeneric,
- Class: ErrorClassInvalid,
- },
+ applyDeltaCallback: func(*DiffDelta) (bool, error) { return true, errMessageDropped },
+ err: errMessageDropped,
},
} {
t.Run(tc.name, func(t *testing.T) {
@@ -562,9 +555,9 @@ func TestApplyToTree(t *testing.T) {
opts.ApplyDeltaCallback = tc.applyDeltaCallback
index, err := repo.ApplyToTree(tc.diff, tc.tree, opts)
- if tc.error != nil {
- if !reflect.DeepEqual(err, tc.error) {
- t.Fatalf("expected error %q but got %q", tc.error, err)
+ if tc.err != nil {
+ if !reflect.DeepEqual(tc.err, err) {
+ t.Fatalf("expected error %q but got %q", tc.err, err)
}
return
diff --git a/git.go b/git.go
index e13c7e8..cdcf6bb 100644
--- a/git.go
+++ b/git.go
@@ -330,6 +330,17 @@ func ucbool(b bool) C.uint {
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) {
ceildirs := C.CString(strings.Join(ceiling_dirs, string(C.GIT_PATH_LIST_SEPARATOR)))
defer C.free(unsafe.Pointer(ceildirs))
diff --git a/index.go b/index.go
index 61d5d82..f1f2e9d 100644
--- a/index.go
+++ b/index.go
@@ -10,12 +10,17 @@ extern int _go_git_index_remove_all(git_index*, const git_strarray*, void*);
*/
import "C"
import (
+ "errors"
"fmt"
"runtime"
"unsafe"
)
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.
type IndexAddOption uint
@@ -227,12 +232,17 @@ func (v *Index) AddAll(pathspecs []string, flags IndexAddOption, callback IndexM
cpathspecs.strings = makeCStringsFromStrings(pathspecs)
defer freeStrarray(&cpathspecs)
+ var err error
+ data := indexMatchedPathCallbackData{
+ callback: callback,
+ errorTarget: &err,
+ }
runtime.LockOSThread()
defer runtime.UnlockOSThread()
var handle unsafe.Pointer
if callback != nil {
- handle = pointerHandles.Track(callback)
+ handle = pointerHandles.Track(&data)
defer pointerHandles.Untrack(handle)
}
@@ -243,9 +253,13 @@ func (v *Index) AddAll(pathspecs []string, flags IndexAddOption, callback IndexM
handle,
)
runtime.KeepAlive(v)
+ if ret == C.int(ErrorCodeUser) && err != nil {
+ return err
+ }
if ret < 0 {
return MakeGitError(ret)
}
+
return nil
}
@@ -255,12 +269,17 @@ func (v *Index) UpdateAll(pathspecs []string, callback IndexMatchedPathCallback)
cpathspecs.strings = makeCStringsFromStrings(pathspecs)
defer freeStrarray(&cpathspecs)
+ var err error
+ data := indexMatchedPathCallbackData{
+ callback: callback,
+ errorTarget: &err,
+ }
runtime.LockOSThread()
defer runtime.UnlockOSThread()
var handle unsafe.Pointer
if callback != nil {
- handle = pointerHandles.Track(callback)
+ handle = pointerHandles.Track(&data)
defer pointerHandles.Untrack(handle)
}
@@ -270,9 +289,13 @@ func (v *Index) UpdateAll(pathspecs []string, callback IndexMatchedPathCallback)
handle,
)
runtime.KeepAlive(v)
+ if ret == C.int(ErrorCodeUser) && err != nil {
+ return err
+ }
if ret < 0 {
return MakeGitError(ret)
}
+
return nil
}
@@ -282,12 +305,17 @@ func (v *Index) RemoveAll(pathspecs []string, callback IndexMatchedPathCallback)
cpathspecs.strings = makeCStringsFromStrings(pathspecs)
defer freeStrarray(&cpathspecs)
+ var err error
+ data := indexMatchedPathCallbackData{
+ callback: callback,
+ errorTarget: &err,
+ }
runtime.LockOSThread()
defer runtime.UnlockOSThread()
var handle unsafe.Pointer
if callback != nil {
- handle = pointerHandles.Track(callback)
+ handle = pointerHandles.Track(&data)
defer pointerHandles.Untrack(handle)
}
@@ -297,19 +325,30 @@ func (v *Index) RemoveAll(pathspecs []string, callback IndexMatchedPathCallback)
handle,
)
runtime.KeepAlive(v)
+ if ret == C.int(ErrorCodeUser) && err != nil {
+ return err
+ }
if ret < 0 {
return MakeGitError(ret)
}
+
return nil
}
//export indexMatchedPathCallback
-func indexMatchedPathCallback(cPath, cMatchedPathspec *C.char, payload unsafe.Pointer) int {
- if callback, ok := pointerHandles.Get(payload).(IndexMatchedPathCallback); ok {
- return callback(C.GoString(cPath), C.GoString(cMatchedPathspec))
- } else {
+func indexMatchedPathCallback(cPath, cMatchedPathspec *C.char, payload unsafe.Pointer) C.int {
+ data, ok := pointerHandles.Get(payload).(*indexMatchedPathCallbackData)
+ if !ok {
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 {
@@ -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))
if centry == nil {
- return nil, MakeGitError(C.GIT_ENOTFOUND)
+ return nil, MakeGitError(C.int(ErrorCodeNotFound))
}
ret := newIndexEntryFromC(centry)
runtime.KeepAlive(v)
diff --git a/merge.go b/merge.go
index 00d0462..3fe76bb 100644
--- a/merge.go
+++ b/merge.go
@@ -186,6 +186,9 @@ func (mo *MergeOptions) toC() *C.git_merge_options {
}
}
+func freeMergeOptions(opts *C.git_merge_options) {
+}
+
type MergeFileFavor int
const (
@@ -199,8 +202,10 @@ func (r *Repository) Merge(theirHeads []*AnnotatedCommit, mergeOptions *MergeOpt
runtime.LockOSThread()
defer runtime.UnlockOSThread()
+ var err error
cMergeOpts := mergeOptions.toC()
- cCheckoutOptions := checkoutOptions.toC()
+ defer freeMergeOptions(cMergeOpts)
+ cCheckoutOptions := checkoutOptions.toC(&err)
defer freeCheckoutOptions(cCheckoutOptions)
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
}
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)
- if err < 0 {
- return MakeGitError(err)
+ if ret == C.int(ErrorCodeUser) && err != nil {
+ return err
+ }
+ if ret < 0 {
+ return MakeGitError(ret)
}
return nil
}
@@ -262,6 +270,7 @@ func (r *Repository) MergeCommits(ours *Commit, theirs *Commit, options *MergeOp
defer runtime.UnlockOSThread()
copts := options.toC()
+ defer freeMergeOptions(copts)
var ptr *C.git_index
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()
copts := options.toC()
+ defer freeMergeOptions(copts)
var ancestor_ptr *C.git_tree
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) {
+ if c == nil {
+ return
+ }
C.free(unsafe.Pointer(c.ancestor_label))
C.free(unsafe.Pointer(c.our_label))
C.free(unsafe.Pointer(c.their_label))
diff --git a/odb.go b/odb.go
index d313e3b..2d052ac 100644
--- a/odb.go
+++ b/odb.go
@@ -175,35 +175,33 @@ func (v *Odb) Read(oid *Oid) (obj *OdbObject, err error) {
}
type OdbForEachCallback func(id *Oid) error
-
-type foreachData struct {
- callback OdbForEachCallback
- err error
+type odbForEachCallbackData struct {
+ callback OdbForEachCallback
+ errorTarget *error
}
-//export odbForEachCb
-func odbForEachCb(id *C.git_oid, handle unsafe.Pointer) int {
- data, ok := pointerHandles.Get(handle).(*foreachData)
-
+//export odbForEachCallback
+func odbForEachCallback(id *C.git_oid, handle unsafe.Pointer) C.int {
+ data, ok := pointerHandles.Get(handle).(*odbForEachCallbackData)
if !ok {
panic("could not retrieve handle")
}
err := data.callback(newOidFromC(id))
if err != nil {
- data.err = err
- return C.GIT_EUSER
+ *data.errorTarget = err
+ return C.int(ErrorCodeUser)
}
- return 0
+ return C.int(ErrorCodeOK)
}
func (v *Odb) ForEach(callback OdbForEachCallback) error {
- data := foreachData{
- callback: callback,
- err: nil,
+ var err error
+ data := odbForEachCallbackData{
+ callback: callback,
+ errorTarget: &err,
}
-
runtime.LockOSThread()
defer runtime.UnlockOSThread()
@@ -212,9 +210,10 @@ func (v *Odb) ForEach(callback OdbForEachCallback) error {
ret := C._go_git_odb_foreach(v.ptr, handle)
runtime.KeepAlive(v)
- if ret == C.GIT_EUSER {
- return data.err
- } else if ret < 0 {
+ if ret == C.int(ErrorCodeUser) && err != nil {
+ return err
+ }
+ if ret < 0 {
return MakeGitError(ret)
}
diff --git a/packbuilder.go b/packbuilder.go
index 576e5ca..5d3a933 100644
--- a/packbuilder.go
+++ b/packbuilder.go
@@ -133,15 +133,15 @@ func (pb *Packbuilder) Written() uint32 {
}
type PackbuilderForeachCallback func([]byte) error
-type packbuilderCbData struct {
- callback PackbuilderForeachCallback
- err error
+type packbuilderCallbackData struct {
+ callback PackbuilderForeachCallback
+ errorTarget *error
}
-//export packbuilderForEachCb
-func packbuilderForEachCb(buf unsafe.Pointer, size C.size_t, handle unsafe.Pointer) int {
+//export packbuilderForEachCallback
+func packbuilderForEachCallback(buf unsafe.Pointer, size C.size_t, handle unsafe.Pointer) C.int {
payload := pointerHandles.Get(handle)
- data, ok := payload.(*packbuilderCbData)
+ data, ok := payload.(*packbuilderCallbackData)
if !ok {
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)
if err != nil {
- data.err = err
- return C.GIT_EUSER
+ *data.errorTarget = err
+ return C.int(ErrorCodeUser)
}
- return 0
+ return C.int(ErrorCodeOK)
}
// ForEach repeatedly calls the callback with new packfile data until
// there is no more data or the callback returns an error
func (pb *Packbuilder) ForEach(callback PackbuilderForeachCallback) error {
- data := packbuilderCbData{
- callback: callback,
- err: nil,
+ var err error
+ data := packbuilderCallbackData{
+ callback: callback,
+ errorTarget: &err,
}
handle := pointerHandles.Track(&data)
defer pointerHandles.Untrack(handle)
@@ -170,13 +171,13 @@ func (pb *Packbuilder) ForEach(callback PackbuilderForeachCallback) error {
runtime.LockOSThread()
defer runtime.UnlockOSThread()
- err := C._go_git_packbuilder_foreach(pb.ptr, handle)
+ ret := C._go_git_packbuilder_foreach(pb.ptr, handle)
runtime.KeepAlive(pb)
- if err == C.GIT_EUSER {
- return data.err
+ if ret == C.int(ErrorCodeUser) && err != nil {
+ return err
}
- if err < 0 {
- return MakeGitError(err)
+ if ret < 0 {
+ return MakeGitError(ret)
}
return nil
diff --git a/patch.go b/patch.go
index 4c6648e..6441751 100644
--- a/patch.go
+++ b/patch.go
@@ -77,17 +77,22 @@ func (v *Repository) PatchFromBuffers(oldPath, newPath string, oldBuf, newBuf []
cNewPath := C.CString(newPath)
defer C.free(unsafe.Pointer(cNewPath))
- copts := diffOptionsToC(opts, v)
+ var err error
+ copts := opts.toC(v, &err)
defer freeDiffOptions(copts)
runtime.LockOSThread()
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(newBuf)
- if ecode < 0 {
- return nil, MakeGitError(ecode)
+ if ret == C.int(ErrorCodeUser) && err != nil {
+ return nil, err
+ }
+ if ret < 0 {
+ return nil, MakeGitError(ret)
}
+
return newPatchFromC(patchPtr), nil
}
diff --git a/rebase.go b/rebase.go
index f3746c1..4c621ab 100644
--- a/rebase.go
+++ b/rebase.go
@@ -3,7 +3,7 @@ package git
/*
#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 (
@@ -71,25 +71,22 @@ func newRebaseOperationFromC(c *C.git_rebase_operation) *RebaseOperation {
return operation
}
-//export commitSignCallback
-func commitSignCallback(_signature *C.git_buf, _signature_field *C.git_buf, _commit_content *C.char, _payload unsafe.Pointer) C.int {
+//export commitSigningCallback
+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)
if !ok {
panic("invalid sign payload")
}
if opts.CommitSigningCallback == nil {
- return C.GIT_PASSTHROUGH
+ return C.int(ErrorCodePassthrough)
}
commitContent := C.GoString(_commit_content)
signature, signatureField, err := opts.CommitSigningCallback(commitContent)
if err != nil {
- if gitError, ok := err.(*GitError); ok {
- return C.int(gitError.Code)
- }
- return C.int(-1)
+ return setCallbackError(errorMessage, err)
}
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 != "" {
err := fillBuf(signatureField, _signature_field)
if err != nil {
- return C.int(-1)
+ return setCallbackError(errorMessage, err)
}
}
err = fillBuf(signature, _signature)
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
@@ -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 {
return nil
}
- opts := &C.git_rebase_options{
+
+ cOptions := &C.git_rebase_options{
version: C.uint(ro.Version),
quiet: C.int(ro.Quiet),
inmemory: C.int(ro.InMemory),
rewrite_notes_ref: mapEmptyStringToNull(ro.RewriteNotesRef),
merge_options: *ro.MergeOptions.toC(),
- checkout_options: *ro.CheckoutOptions.toC(),
+ checkout_options: *ro.CheckoutOptions.toC(errorTarget),
}
if ro.CommitSigningCallback != nil {
- C._go_git_populate_commit_sign_cb(opts)
- opts.payload = pointerHandles.Track(ro)
+ C._go_git_populate_rebase_callbacks(cOptions)
+ 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 {
@@ -188,8 +198,9 @@ func mapEmptyStringToNull(ref string) *C.char {
// Rebase is the struct representing a Rebase object.
type Rebase struct {
- ptr *C.git_rebase
- r *Repository
+ ptr *C.git_rebase
+ r *Repository
+ options *C.git_rebase_options
}
// 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
- 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(upstream)
runtime.KeepAlive(onto)
- if err < 0 {
- return nil, MakeGitError(err)
+ if ret == C.int(ErrorCodeUser) && err != nil {
+ 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.
@@ -227,13 +245,20 @@ func (r *Repository) OpenRebase(opts *RebaseOptions) (*Rebase, error) {
defer runtime.UnlockOSThread()
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)
- if err < 0 {
- return nil, MakeGitError(err)
+ if ret == C.int(ErrorCodeUser) && err != nil {
+ 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.
@@ -344,13 +369,14 @@ func (rebase *Rebase) Abort() error {
}
// Free frees the Rebase object.
-func (rebase *Rebase) Free() {
- runtime.SetFinalizer(rebase, nil)
- C.git_rebase_free(rebase.ptr)
+func (r *Rebase) Free() {
+ runtime.SetFinalizer(r, nil)
+ C.git_rebase_free(r.ptr)
+ freeRebaseOptions(r.options)
}
-func newRebaseFromC(ptr *C.git_rebase) *Rebase {
- rebase := &Rebase{ptr: ptr}
+func newRebaseFromC(ptr *C.git_rebase, opts *C.git_rebase_options) *Rebase {
+ rebase := &Rebase{ptr: ptr, options: opts}
runtime.SetFinalizer(rebase, (*Rebase).Free)
return rebase
}
diff --git a/remote.go b/remote.go
index 72e26ce..795f9af 100644
--- a/remote.go
+++ b/remote.go
@@ -6,12 +6,13 @@ package git
#include <git2.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 (
"crypto/x509"
+ "errors"
"reflect"
"runtime"
"strings"
@@ -219,43 +220,55 @@ func populateRemoteCallbacks(ptr *C.git_remote_callbacks, callbacks *RemoteCallb
if callbacks == nil {
return
}
- C._go_git_setup_callbacks(ptr)
+ C._go_git_populate_remote_callbacks(ptr)
ptr.payload = pointerHandles.Track(callbacks)
}
//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)
if callbacks.SidebandProgressCallback == nil {
- return 0
+ return C.int(ErrorCodeOK)
}
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
-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)
if callbacks.CompletionCallback == nil {
- return 0
+ return C.int(ErrorCodeOK)
+ }
+ ret := callbacks.CompletionCallback(RemoteCompletion(completion_type))
+ if ret < 0 {
+ return setCallbackError(errorMessage, errors.New(ErrorCode(ret).String()))
}
- return int(callbacks.CompletionCallback(RemoteCompletion(completion_type)))
+ return C.int(ErrorCodeOK)
}
//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)
if callbacks.CredentialsCallback == nil {
- return C.GIT_PASSTHROUGH
+ return C.int(ErrorCodePassthrough)
}
url := C.GoString(_url)
username_from_url := C.GoString(_username_from_url)
cred, err := callbacks.CredentialsCallback(url, username_from_url, (CredentialType)(allowed_types))
if err != nil {
- if gitError, ok := err.(*GitError); ok {
- return int(gitError.Code)
- }
- return C.GIT_EUSER
+ return setCallbackError(errorMessage, err)
}
if cred != nil {
*_cred = cred.ptr
@@ -264,41 +277,61 @@ func credentialsCallback(_cred **C.git_credential, _url *C.char, _username_from_
cred.ptr = nil
runtime.SetFinalizer(cred, nil)
}
- return 0
+ return C.int(ErrorCodeOK)
}
//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)
if callbacks.TransferProgressCallback == nil {
- return 0
+ return C.int(ErrorCodeOK)
+ }
+ ret := callbacks.TransferProgressCallback(newTransferProgressFromC(stats))
+ if ret < 0 {
+ return setCallbackError(errorMessage, errors.New(ErrorCode(ret).String()))
}
- return int(callbacks.TransferProgressCallback(newTransferProgressFromC(stats)))
+ return C.int(ErrorCodeOK)
}
//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)
if callbacks.UpdateTipsCallback == nil {
- return 0
+ return C.int(ErrorCodeOK)
}
refname := C.GoString(_refname)
a := newOidFromC(_a)
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
-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)
// if there's no callback set, we need to make sure we fail if the library didn't consider this cert valid
if callbacks.CertificateCheckCallback == nil {
- if _valid == 1 {
- return 0
- } else {
- return C.GIT_ECERTIFICATE
+ if _valid == 0 {
+ return C.int(ErrorCodeCertificate)
}
+ return C.int(ErrorCodeOK)
}
+
host := C.GoString(_host)
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))
x509_certs, err := x509.ParseCertificates(C.GoBytes(ccert.data, C.int(ccert.len)))
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
@@ -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.HashSHA256[0]), unsafe.Pointer(&ccert.hash_sha256[0]), C.size_t(len(cert.Hostkey.HashSHA256)))
} else {
- cstr := C.CString("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 setCallbackError(errorMessage, errors.New("unsupported certificate type"))
}
- 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
-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)
-
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
-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)
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
-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)
-
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) {
@@ -373,6 +420,10 @@ func populateProxyOptions(ptr *C.git_proxy_options, opts *ProxyOptions) {
}
func freeProxyOptions(ptr *C.git_proxy_options) {
+ if ptr == nil {
+ return
+ }
+
C.free(unsafe.Pointer(ptr.url))
}
diff --git a/remote_test.go b/remote_test.go
index 7e16856..4cc3298 100644
--- a/remote_test.go
+++ b/remote_test.go
@@ -30,7 +30,7 @@ func assertHostname(cert *Certificate, valid bool, hostname string, t *testing.T
return ErrorCodeUser
}
- return 0
+ return ErrorCodeOK
}
func TestCertificateCheck(t *testing.T) {
diff --git a/reset.go b/reset.go
index b3ecff4..155b58b 100644
--- a/reset.go
+++ b/reset.go
@@ -17,8 +17,15 @@ const (
func (r *Repository) ResetToCommit(commit *Commit, resetType ResetType, opts *CheckoutOptions) error {
runtime.LockOSThread()
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 {
return MakeGitError(ret)
}
diff --git a/revert.go b/revert.go
index 2c18bc9..f318ade 100644
--- a/revert.go
+++ b/revert.go
@@ -15,12 +15,15 @@ type RevertOptions struct {
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{
version: C.GIT_REVERT_OPTIONS_VERSION,
mainline: C.uint(opts.Mainline),
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) {
+ if opts != nil {
+ return
+ }
+ freeMergeOptions(&opts.merge_opts)
freeCheckoutOptions(&opts.checkout_opts)
}
@@ -57,19 +64,19 @@ func (r *Repository) Revert(commit *Commit, revertOptions *RevertOptions) error
runtime.LockOSThread()
defer runtime.UnlockOSThread()
- var cOpts *C.git_revert_options
-
- if revertOptions != nil {
- cOpts = revertOptions.toC()
- defer freeRevertOptions(cOpts)
- }
+ var err error
+ cOpts := revertOptions.toC(&err)
+ 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(commit)
- if ecode < 0 {
- return MakeGitError(ecode)
+ if ret == C.int(ErrorCodeUser) && err != nil {
+ return err
+ }
+ if ret < 0 {
+ return MakeGitError(ret)
}
return nil
@@ -81,11 +88,8 @@ func (r *Repository) RevertCommit(revertCommit *Commit, ourCommit *Commit, mainl
runtime.LockOSThread()
defer runtime.UnlockOSThread()
- var cOpts *C.git_merge_options
-
- if mergeOptions != nil {
- cOpts = mergeOptions.toC()
- }
+ cOpts := mergeOptions.toC()
+ defer freeMergeOptions(cOpts)
var index *C.git_index
diff --git a/stash.go b/stash.go
index 7e0ed8f..620e70f 100644
--- a/stash.go
+++ b/stash.go
@@ -3,7 +3,7 @@ package git
/*
#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);
*/
import "C"
@@ -113,28 +113,28 @@ const (
// StashApplyProgressCallback is the apply operation notification callback.
type StashApplyProgressCallback func(progress StashApplyProgress) error
-
-type stashApplyProgressData struct {
- Callback StashApplyProgressCallback
- Error error
+type stashApplyProgressCallbackData struct {
+ callback StashApplyProgressCallback
+ errorTarget *error
}
-//export stashApplyProgressCb
-func stashApplyProgressCb(progress C.git_stash_apply_progress_t, handle unsafe.Pointer) int {
+//export stashApplyProgressCallback
+func stashApplyProgressCallback(progress C.git_stash_apply_progress_t, handle unsafe.Pointer) C.int {
payload := pointerHandles.Get(handle)
- data, ok := payload.(*stashApplyProgressData)
+ data, ok := payload.(*stashApplyProgressCallbackData)
if !ok {
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))
- if err != nil {
- data.Error = err
- return C.GIT_EUSER
- }
+ err := data.callback(StashApplyProgress(progress))
+ if err != nil {
+ *data.errorTarget = err
+ return C.int(ErrorCodeUser)
}
- return 0
+ return C.int(ErrorCodeOK)
}
// StashApplyOptions represents options to control the apply operation.
@@ -161,38 +161,34 @@ func DefaultStashApplyOptions() (StashApplyOptions, error) {
}, nil
}
-func (opts *StashApplyOptions) toC() (
- optsC *C.git_stash_apply_options, progressData *stashApplyProgressData) {
-
- if opts != nil {
- progressData = &stashApplyProgressData{
- Callback: opts.ProgressCallback,
- }
-
- optsC = &C.git_stash_apply_options{
- version: C.GIT_STASH_APPLY_OPTIONS_VERSION,
- flags: C.uint32_t(opts.Flags),
- }
- populateCheckoutOptions(&optsC.checkout_options, &opts.CheckoutOptions)
- if opts.ProgressCallback != nil {
- C._go_git_setup_stash_apply_progress_callbacks(optsC)
- optsC.progress_payload = pointerHandles.Track(progressData)
- }
+func (opts *StashApplyOptions) toC(errorTarget *error) *C.git_stash_apply_options {
+ if opts == nil {
+ return nil
}
- 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)
+ optsC := &C.git_stash_apply_options{
+ version: C.GIT_STASH_APPLY_OPTIONS_VERSION,
+ flags: C.uint32_t(opts.Flags),
+ }
+ populateCheckoutOptions(&optsC.checkout_options, &opts.CheckoutOptions, errorTarget)
+ if opts.ProgressCallback != nil {
+ progressData := &stashApplyProgressCallbackData{
+ callback: opts.ProgressCallback,
+ errorTarget: errorTarget,
+ }
+ C._go_git_populate_stash_apply_callbacks(optsC)
+ optsC.progress_payload = pointerHandles.Track(progressData)
}
+ return optsC
}
func freeStashApplyOptions(optsC *C.git_stash_apply_options) {
- if optsC != nil {
- freeCheckoutOptions(&optsC.checkout_options)
+ if optsC == nil {
+ 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.
@@ -220,8 +216,8 @@ func freeStashApplyOptions(optsC *C.git_stash_apply_options) {
//
// Error codes can be interogated with IsErrorCode(err, ErrorCodeNotFound).
func (c *StashCollection) Apply(index int, opts StashApplyOptions) error {
- optsC, progressData := opts.toC()
- defer untrackStashApplyOptionsCallback(optsC)
+ var err error
+ optsC := opts.toC(&err)
defer freeStashApplyOptions(optsC)
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)
runtime.KeepAlive(c)
- if ret == C.GIT_EUSER {
- return progressData.Error
+ if ret == C.int(ErrorCodeUser) && err != nil {
+ return err
}
if ret < 0 {
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'
// is the commit id of the stash.
type StashCallback func(index int, message string, id *Oid) error
-
type stashCallbackData struct {
- Callback StashCallback
- Error error
+ callback StashCallback
+ errorTarget *error
}
-//export stashForeachCb
-func stashForeachCb(index C.size_t, message *C.char, id *C.git_oid, handle unsafe.Pointer) int {
+//export stashForeachCallback
+func stashForeachCallback(index C.size_t, message *C.char, id *C.git_oid, handle unsafe.Pointer) C.int {
payload := pointerHandles.Get(handle)
data, ok := payload.(*stashCallbackData)
if !ok {
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 {
- data.Error = err
- return C.GIT_EUSER
+ *data.errorTarget = err
+ return C.int(ErrorCodeUser)
}
- return 0
+ return C.int(ErrorCodeOK)
}
// 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.
func (c *StashCollection) Foreach(callback StashCallback) error {
+ var err error
data := stashCallbackData{
- Callback: callback,
+ callback: callback,
+ errorTarget: &err,
}
-
handle := pointerHandles.Track(&data)
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)
runtime.KeepAlive(c)
- if ret == C.GIT_EUSER {
- return data.Error
+ if ret == C.int(ErrorCodeUser) && err != nil {
+ return err
}
if ret < 0 {
return MakeGitError(ret)
@@ -323,8 +319,8 @@ func (c *StashCollection) Drop(index int) error {
// Returns error code ErrorCodeNotFound if there's no stashed
// state for the given index.
func (c *StashCollection) Pop(index int, opts StashApplyOptions) error {
- optsC, progressData := opts.toC()
- defer untrackStashApplyOptionsCallback(optsC)
+ var err error
+ optsC := opts.toC(&err)
defer freeStashApplyOptions(optsC)
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)
runtime.KeepAlive(c)
- if ret == C.GIT_EUSER {
- return progressData.Error
+ if ret == C.int(ErrorCodeUser) && err != nil {
+ return err
}
if ret < 0 {
return MakeGitError(ret)
diff --git a/submodule.go b/submodule.go
index 1b5ec60..01474f6 100644
--- a/submodule.go
+++ b/submodule.go
@@ -6,7 +6,9 @@ package git
extern int _go_git_visit_submodule(git_repository *repo, void *fct);
*/
import "C"
+
import (
+ "errors"
"runtime"
"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.
type SubmoduleCallback func(sub *Submodule, name string) int
+type submoduleCallbackData struct {
+ callback SubmoduleCallback
+ errorTarget *error
+}
//export submoduleCallback
func submoduleCallback(csub unsafe.Pointer, name *C.char, handle unsafe.Pointer) C.int {
sub := &Submodule{(*C.git_submodule)(csub), nil}
- if callback, ok := pointerHandles.Get(handle).(SubmoduleCallback); ok {
- return (C.int)(callback(sub, C.GoString(name)))
- } else {
+ data, ok := pointerHandles.Get(handle).(submoduleCallbackData)
+ if !ok {
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)
+ }
+
+ return C.int(ErrorCodeOK)
}
-func (c *SubmoduleCollection) Foreach(cbk SubmoduleCallback) error {
+func (c *SubmoduleCollection) Foreach(callback SubmoduleCallback) error {
+ var err error
+ data := submoduleCallbackData{
+ callback: callback,
+ errorTarget: &err,
+ }
runtime.LockOSThread()
defer runtime.UnlockOSThread()
- handle := pointerHandles.Track(cbk)
+ handle := pointerHandles.Track(data)
defer pointerHandles.Untrack(handle)
ret := C._go_git_visit_submodule(c.repo.ptr, handle)
runtime.KeepAlive(c)
+ if ret == C.int(ErrorCodeUser) && err != nil {
+ return err
+ }
if ret < 0 {
return MakeGitError(ret)
}
+
return nil
}
@@ -342,17 +364,18 @@ func (sub *Submodule) Open() (*Repository, error) {
}
func (sub *Submodule) Update(init bool, opts *SubmoduleUpdateOptions) error {
- var copts C.git_submodule_update_options
- err := populateSubmoduleUpdateOptions(&copts, opts)
- if err != nil {
- return err
- }
+ var err error
+ cOpts := populateSubmoduleUpdateOptions(&C.git_submodule_update_options{}, opts, &err)
+ defer freeSubmoduleUpdateOptions(cOpts)
runtime.LockOSThread()
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)
+ if ret == C.int(ErrorCodeUser) && err != nil {
+ return err
+ }
if ret < 0 {
return MakeGitError(ret)
}
@@ -360,15 +383,22 @@ func (sub *Submodule) Update(init bool, opts *SubmoduleUpdateOptions) error {
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)
if opts == nil {
return nil
}
- populateCheckoutOptions(&ptr.checkout_opts, opts.CheckoutOpts)
+ populateCheckoutOptions(&ptr.checkout_opts, opts.CheckoutOpts, errorTarget)
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)
}
diff --git a/tag.go b/tag.go
index 20b9ba2..dd8477a 100644
--- a/tag.go
+++ b/tag.go
@@ -197,48 +197,48 @@ func (c *TagsCollection) ListWithMatch(pattern string) ([]string, error) {
// so repo.LookupTag() will return an error for these tags. Use
// repo.References.Lookup() instead.
type TagForeachCallback func(name string, id *Oid) error
-type tagForeachData struct {
- callback TagForeachCallback
- err error
+type tagForeachCallbackData struct {
+ callback TagForeachCallback
+ errorTarget *error
}
-//export gitTagForeachCb
-func gitTagForeachCb(name *C.char, id *C.git_oid, handle unsafe.Pointer) int {
+//export tagForeachCallback
+func tagForeachCallback(name *C.char, id *C.git_oid, handle unsafe.Pointer) C.int {
payload := pointerHandles.Get(handle)
- data, ok := payload.(*tagForeachData)
+ data, ok := payload.(*tagForeachCallbackData)
if !ok {
panic("could not retrieve tag foreach CB handle")
}
err := data.callback(C.GoString(name), newOidFromC(id))
if err != nil {
- data.err = err
- return C.GIT_EUSER
+ *data.errorTarget = err
+ return C.int(ErrorCodeUser)
}
- return 0
+ return C.int(ErrorCodeOK)
}
// Foreach calls the callback for each tag in the repository.
func (c *TagsCollection) Foreach(callback TagForeachCallback) error {
- data := tagForeachData{
- callback: callback,
- err: nil,
+ var err error
+ data := tagForeachCallbackData{
+ callback: callback,
+ errorTarget: &err,
}
-
handle := pointerHandles.Track(&data)
defer pointerHandles.Untrack(handle)
runtime.LockOSThread()
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)
- if err == C.GIT_EUSER {
- return data.err
+ if ret == C.int(ErrorCodeUser) && err != nil {
+ return err
}
- if err < 0 {
- return MakeGitError(err)
+ if ret < 0 {
+ return MakeGitError(ret)
}
return nil
diff --git a/tree.go b/tree.go
index cf85f2e..38c5bdc 100644
--- a/tree.go
+++ b/tree.go
@@ -8,6 +8,7 @@ extern int _go_git_treewalk(git_tree *tree, git_treewalk_mode mode, void *ptr);
import "C"
import (
+ "errors"
"runtime"
"unsafe"
)
@@ -120,33 +121,46 @@ func (t *Tree) EntryCount() uint64 {
}
type TreeWalkCallback func(string, *TreeEntry) int
+type treeWalkCallbackData struct {
+ callback TreeWalkCallback
+ errorTarget *error
+}
//export treeWalkCallback
func treeWalkCallback(_root *C.char, entry *C.git_tree_entry, ptr unsafe.Pointer) C.int {
- root := C.GoString(_root)
-
- if callback, ok := pointerHandles.Get(ptr).(TreeWalkCallback); ok {
- return C.int(callback(root, newTreeEntry(entry)))
- } else {
+ data, ok := pointerHandles.Get(ptr).(*treeWalkCallbackData)
+ if !ok {
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 {
+ var err error
+ data := treeWalkCallbackData{
+ callback: callback,
+ errorTarget: &err,
+ }
runtime.LockOSThread()
defer runtime.UnlockOSThread()
- ptr := pointerHandles.Track(callback)
- defer pointerHandles.Untrack(ptr)
+ handle := pointerHandles.Track(&data)
+ defer pointerHandles.Untrack(handle)
- err := C._go_git_treewalk(
- t.cast_ptr,
- C.GIT_TREEWALK_PRE,
- ptr,
- )
+ ret := C._go_git_treewalk(t.cast_ptr, C.GIT_TREEWALK_PRE, handle)
runtime.KeepAlive(t)
- if err < 0 {
- return MakeGitError(err)
+ if ret == C.int(ErrorCodeUser) && err != nil {
+ return err
+ }
+ if ret < 0 {
+ return MakeGitError(ret)
}
return nil
diff --git a/wrapper.c b/wrapper.c
index 6dea2f3..cee8d00 100644
--- a/wrapper.c
+++ b/wrapper.c
@@ -1,36 +1,149 @@
#include "_cgo_export.h"
+
#include <git2.h>
#include <git2/sys/odb_backend.h>
#include <git2/sys/refdb_backend.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);
+// }
+
+/**
+ * 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)
+{
+ if (error_message != NULL) {
+ if (ret < 0)
+ git_error_set_str(GIT_ERROR_CALLBACK, error_message);
+ free(error_message);
+ }
+ return ret;
+}
+
+void _go_git_populate_apply_callbacks(git_apply_options *options)
+{
+ options->delta_cb = (git_apply_delta_cb)&deltaApplyCallback;
+ options->hunk_cb = (git_apply_hunk_cb)&hunkApplyCallback;
+}
-void _go_git_populate_apply_cb(git_apply_options *options)
+static int commit_signing_callback(
+ git_buf *signature,
+ git_buf *signature_field,
+ const char *commit_contents,
+ void *payload)
{
- options->delta_cb = (git_apply_delta_cb)deltaApplyCallback;
- options->hunk_cb = (git_apply_hunk_cb)hunkApplyCallback;
+ 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_commit_sign_cb(git_rebase_options *opts)
+void _go_git_populate_rebase_callbacks(git_rebase_options *opts)
{
- opts->signing_cb = (git_commit_signing_cb)commitSignCallback;
+ opts->signing_cb = commit_signing_callback;
}
-void _go_git_populate_remote_cb(git_clone_options *opts)
+void _go_git_populate_clone_callbacks(git_clone_options *opts)
{
- opts->remote_cb = (git_remote_create_cb)remoteCreateCallback;
+ opts->remote_cb = (git_remote_create_cb)&remoteCreateCallback;
}
-void _go_git_populate_checkout_cb(git_checkout_options *opts)
+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;
+ 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)
{
- 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)
@@ -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)
{
- 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)
{
- 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)
{
- if (backend->free)
- backend->free(backend);
-
- return;
+ if (!backend->free)
+ return;
+ backend->free(backend);
}
void _go_git_refdb_backend_free(git_refdb_backend *backend)
{
- if (backend->free)
- backend->free(backend);
-
- return;
+ if (!backend->free)
+ return;
+ backend->free(backend);
}
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_line_cb lcb = NULL;
- if (eachFile) {
- fcb = (git_diff_file_cb)&diffForEachFileCb;
- }
-
- if (eachHunk) {
- hcb = (git_diff_hunk_cb)&diffForEachHunkCb;
- }
-
- if (eachLine) {
- lcb = (git_diff_line_cb)&diffForEachLineCb;
- }
+ if (eachFile)
+ fcb = (git_diff_file_cb)&diffForEachFileCallback;
+ if (eachHunk)
+ hcb = (git_diff_hunk_cb)&diffForEachHunkCallback;
+ if (eachLine)
+ lcb = (git_diff_line_cb)&diffForEachLineCallback;
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_hunk_cb hcb = NULL;
git_diff_line_cb lcb = NULL;
- if (eachFile) {
- fcb = (git_diff_file_cb)&diffForEachFileCb;
- }
+ if (eachFile)
+ fcb = (git_diff_file_cb)&diffForEachFileCallback;
+ if (eachHunk)
+ hcb = (git_diff_hunk_cb)&diffForEachHunkCallback;
+ if (eachLine)
+ lcb = (git_diff_line_cb)&diffForEachLineCallback;
- if (eachHunk) {
- hcb = (git_diff_hunk_cb)&diffForEachHunkCb;
- }
+ return git_diff_blobs(old, old_path, new, new_path, opts, fcb, NULL, hcb, lcb, payload);
+}
- if (eachLine) {
- lcb = (git_diff_line_cb)&diffForEachLineCb;
- }
+void _go_git_setup_diff_notify_callbacks(git_diff_options *opts)
+{
+ opts->notify_cb = (git_diff_notify_cb)&diffNotifyCallback;
+}
- return git_diff_blobs(old, old_path, new, new_path, opts, fcb, NULL, hcb, lcb, payload);
+static int sideband_progress_callback(const char *str, int len, void *payload)
+{
+ char *error_message = NULL;
+ const int ret = sidebandProgressCallback(&error_message, (char *)str, len, payload);
+ return set_callback_error(error_message, ret);
+}
+
+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);
}
-void _go_git_setup_diff_notify_callbacks(git_diff_options *opts) {
- opts->notify_cb = (git_diff_notify_cb)diffNotifyCb;
+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);
}
-void _go_git_setup_callbacks(git_remote_callbacks *callbacks) {
- 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);
- typedef int (*push_update_reference_cb)(const char *refname, const char *status, void *data);
+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);
+}
- 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;
+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);
}
-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;
+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;
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) {
- git_index_matched_path_cb cb = callback ? (git_index_matched_path_cb) &indexMatchedPathCallback : NULL;
+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;
return git_index_update_all(index, pathspec, cb, 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;
+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;
return git_index_remove_all(index, pathspec, cb, callback);
}
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 ours = 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);
}
-void _go_git_setup_stash_apply_progress_callbacks(git_stash_apply_options *opts) {
- opts->progress_cb = (git_stash_apply_progress_cb)stashApplyProgressCb;
+void _go_git_populate_stash_apply_callbacks(git_stash_apply_options *opts)
+{
+ opts->progress_cb = (git_stash_apply_progress_cb)&stashApplyProgressCallback;
}
-int _go_git_stash_foreach(git_repository *repo, void *payload) {
- return git_stash_foreach(repo, (git_stash_cb)&stashForeachCb, payload);
+int _go_git_stash_foreach(git_repository *repo, void *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)
@@ -192,16 +432,21 @@ void _go_git_writestream_free(git_writestream *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;
}
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);
}
@@ -216,12 +461,15 @@ void _go_git_odb_writepack_free(git_odb_writepack *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;
- indexer_options.progress_cb = (git_transfer_progress_cb)transferProgressCallback;
+ indexer_options.progress_cb = transfer_progress_callback;
indexer_options.progress_cb_payload = progress_cb_payload;
return git_indexer_new(out, path, mode, odb, &indexer_options);
}
-
-/* EOF */