diff --git a/.github/workflows/backport.yml b/.github/workflows/backport.yml index 17f6ed409..9f18fac45 100644 --- a/.github/workflows/backport.yml +++ b/.github/workflows/backport.yml @@ -12,7 +12,7 @@ jobs: strategy: fail-fast: false matrix: - branch: [ 'release-1.0', 'release-0.28', 'release-0.27' ] + branch: [ 'release-1.1', 'release-1.0', 'release-0.28', 'release-0.27' ] runs-on: ubuntu-20.04 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d5d7034c4..50e5e14c5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -62,8 +62,7 @@ jobs: fail-fast: false matrix: libgit2: - - 'v1.1.0' - - 'v1.2.0' + - '109b4c887ffb63962c7017a66fc4a1f48becb48e' # v1.2.0 with a fixed symbol name: Go (system-wide, dynamic) runs-on: ubuntu-20.04 diff --git a/Build_bundled_static.go b/Build_bundled_static.go index 85ed7393c..09ed0f5b3 100644 --- a/Build_bundled_static.go +++ b/Build_bundled_static.go @@ -1,3 +1,4 @@ +//go:build static && !system_libgit2 // +build static,!system_libgit2 package git @@ -9,8 +10,8 @@ package git #cgo CFLAGS: -DLIBGIT2_STATIC #include -#if LIBGIT2_VER_MAJOR != 1 || LIBGIT2_VER_MINOR < 1 || LIBGIT2_VER_MINOR > 2 -# error "Invalid libgit2 version; this git2go supports libgit2 between v1.1.0 and v1.2.0" +#if LIBGIT2_VER_MAJOR != 1 || LIBGIT2_VER_MINOR < 2 || LIBGIT2_VER_MINOR > 2 +# error "Invalid libgit2 version; this git2go supports libgit2 between v1.2.0 and v1.2.0" #endif */ import "C" diff --git a/Build_system_dynamic.go b/Build_system_dynamic.go index 348cdc82f..950018856 100644 --- a/Build_system_dynamic.go +++ b/Build_system_dynamic.go @@ -1,3 +1,4 @@ +//go:build !static // +build !static package git @@ -7,8 +8,8 @@ package git #cgo CFLAGS: -DLIBGIT2_DYNAMIC #include -#if LIBGIT2_VER_MAJOR != 1 || LIBGIT2_VER_MINOR < 1 || LIBGIT2_VER_MINOR > 2 -# error "Invalid libgit2 version; this git2go supports libgit2 between v1.1.0 and v1.2.0" +#if LIBGIT2_VER_MAJOR != 1 || LIBGIT2_VER_MINOR < 2 || LIBGIT2_VER_MINOR > 2 +# error "Invalid libgit2 version; this git2go supports libgit2 between v1.2.0 and v1.2.0" #endif */ import "C" diff --git a/Build_system_static.go b/Build_system_static.go index c1a829221..309369d90 100644 --- a/Build_system_static.go +++ b/Build_system_static.go @@ -1,3 +1,4 @@ +//go:build static && system_libgit2 // +build static,system_libgit2 package git @@ -7,8 +8,8 @@ package git #cgo CFLAGS: -DLIBGIT2_STATIC #include -#if LIBGIT2_VER_MAJOR != 1 || LIBGIT2_VER_MINOR < 1 || LIBGIT2_VER_MINOR > 2 -# error "Invalid libgit2 version; this git2go supports libgit2 between v1.1.0 and v1.2.0" +#if LIBGIT2_VER_MAJOR != 1 || LIBGIT2_VER_MINOR < 2 || LIBGIT2_VER_MINOR > 2 +# error "Invalid libgit2 version; this git2go supports libgit2 between v1.2.0 and v1.2.0" #endif */ import "C" diff --git a/README.md b/README.md index d05c4c1bd..7cc36eb26 100644 --- a/README.md +++ b/README.md @@ -10,20 +10,21 @@ Due to the fact that Go 1.11 module versions have semantic meaning and don't nec | libgit2 | git2go | |---------|---------------| -| main | (will be v32) | +| main | (will be v33) | +| 1.2 | v32 | | 1.1 | v31 | | 1.0 | v30 | | 0.99 | v29 | | 0.28 | v28 | | 0.27 | v27 | -You can import them in your project with the version's major number as a suffix. For example, if you have libgit2 v1.1 installed, you'd import git2go v31 with: +You can import them in your project with the version's major number as a suffix. For example, if you have libgit2 v1.2 installed, you'd import git2go v32 with: ```sh -go get github.com/libgit2/git2go/v31 +go get github.com/libgit2/git2go/v32 ``` ```go -import "github.com/libgit2/git2go/v31" +import "github.com/libgit2/git2go/v32" ``` which will ensure there are no sudden changes to the API. @@ -44,10 +45,10 @@ This project wraps the functionality provided by libgit2. If you're using a vers ### Versioned branch, dynamic linking -When linking dynamically against a released version of libgit2, install it via your system's package manager. CGo will take care of finding its pkg-config file and set up the linking. Import via Go modules, e.g. to work against libgit2 v1.1 +When linking dynamically against a released version of libgit2, install it via your system's package manager. CGo will take care of finding its pkg-config file and set up the linking. Import via Go modules, e.g. to work against libgit2 v1.2 ```go -import "github.com/libgit2/git2go/v31" +import "github.com/libgit2/git2go/v32" ``` ### Versioned branch, static linking @@ -77,7 +78,7 @@ In order to let Go pass the correct flags to `pkg-config`, `-tags static` needs One thing to take into account is that since Go expects the `pkg-config` file to be within the same directory where `make install-static` was called, so the `go.mod` file may need to have a [`replace` directive](https://github.com/golang/go/wiki/Modules#when-should-i-use-the-replace-directive) so that the correct setup is achieved. So if `git2go` is checked out at `$GOPATH/src/github.com/libgit2/git2go` and your project at `$GOPATH/src/github.com/my/project`, the `go.mod` file of `github.com/my/project` might need to have a line like - replace github.com/libgit2/git2go/v31 ../../libgit2/git2go + replace github.com/libgit2/git2go/v32 ../../libgit2/git2go Parallelism and network operations ---------------------------------- diff --git a/checkout.go b/checkout.go index ebf7c310c..89841a8a0 100644 --- a/checkout.go +++ b/checkout.go @@ -7,7 +7,6 @@ extern void _go_git_populate_checkout_callbacks(git_checkout_options *opts); */ import "C" import ( - "errors" "os" "runtime" "unsafe" @@ -49,8 +48,8 @@ const ( CheckoutUpdateSubmodulesIfChanged CheckoutStrategy = C.GIT_CHECKOUT_UPDATE_SUBMODULES_IF_CHANGED // Recursively checkout submodules if HEAD moved in super repo (NOT IMPLEMENTED) ) -type CheckoutNotifyCallback func(why CheckoutNotifyType, path string, baseline, target, workdir DiffFile) ErrorCode -type CheckoutProgressCallback func(path string, completed, total uint) ErrorCode +type CheckoutNotifyCallback func(why CheckoutNotifyType, path string, baseline, target, workdir DiffFile) error +type CheckoutProgressCallback func(path string, completed, total uint) type CheckoutOptions struct { Strategy CheckoutStrategy // Default will be a dry run @@ -116,9 +115,9 @@ func checkoutNotifyCallback( 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()) + err := data.options.NotifyCallback(CheckoutNotifyType(why), path, baseline, target, workdir) + if err != nil { + *data.errorTarget = err return C.int(ErrorCodeUser) } return C.int(ErrorCodeOK) diff --git a/cherrypick.go b/cherrypick.go index 5ba57a5e0..a0ee0f00c 100644 --- a/cherrypick.go +++ b/cherrypick.go @@ -9,18 +9,16 @@ import ( ) type CherrypickOptions struct { - Version uint - Mainline uint - MergeOpts MergeOptions - CheckoutOpts CheckoutOptions + Mainline uint + MergeOptions MergeOptions + CheckoutOptions CheckoutOptions } func cherrypickOptionsFromC(c *C.git_cherrypick_options) CherrypickOptions { opts := CherrypickOptions{ - Version: uint(c.version), - Mainline: uint(c.mainline), - MergeOpts: mergeOptionsFromC(&c.merge_opts), - CheckoutOpts: checkoutOptionsFromC(&c.checkout_opts), + Mainline: uint(c.mainline), + MergeOptions: mergeOptionsFromC(&c.merge_opts), + CheckoutOptions: checkoutOptionsFromC(&c.checkout_opts), } return opts } @@ -31,8 +29,8 @@ func populateCherrypickOptions(copts *C.git_cherrypick_options, opts *Cherrypick return nil } copts.mainline = C.uint(opts.Mainline) - populateMergeOptions(&copts.merge_opts, &opts.MergeOpts) - populateCheckoutOptions(&copts.checkout_opts, &opts.CheckoutOpts, errorTarget) + populateMergeOptions(&copts.merge_opts, &opts.MergeOptions) + populateCheckoutOptions(&copts.checkout_opts, &opts.CheckoutOptions, errorTarget) return copts } @@ -82,7 +80,7 @@ func (r *Repository) CherrypickCommit(pick, our *Commit, opts CherrypickOptions) runtime.LockOSThread() defer runtime.UnlockOSThread() - cOpts := populateMergeOptions(&C.git_merge_options{}, &opts.MergeOpts) + cOpts := populateMergeOptions(&C.git_merge_options{}, &opts.MergeOptions) defer freeMergeOptions(cOpts) var ptr *C.git_index diff --git a/clone.go b/clone.go index b02a43e19..234efa014 100644 --- a/clone.go +++ b/clone.go @@ -7,16 +7,15 @@ extern void _go_git_populate_clone_callbacks(git_clone_options *opts); */ import "C" import ( - "errors" "runtime" "unsafe" ) -type RemoteCreateCallback func(repo *Repository, name, url string) (*Remote, ErrorCode) +type RemoteCreateCallback func(repo *Repository, name, url string) (*Remote, error) type CloneOptions struct { - *CheckoutOpts - *FetchOptions + CheckoutOptions CheckoutOptions + FetchOptions FetchOptions Bare bool CheckoutBranch string RemoteCreateCallback RemoteCreateCallback @@ -71,9 +70,10 @@ func remoteCreateCallback( panic("invalid remote create callback") } - remote, ret := data.options.RemoteCreateCallback(repo, name, url) - if ret < 0 { - *data.errorTarget = errors.New(ErrorCode(ret).String()) + remote, err := data.options.RemoteCreateCallback(repo, name, url) + + if err != nil { + *data.errorTarget = err return C.int(ErrorCodeUser) } if remote == nil { @@ -100,8 +100,8 @@ func populateCloneOptions(copts *C.git_clone_options, opts *CloneOptions, errorT if opts == nil { return nil } - populateCheckoutOptions(&copts.checkout_opts, opts.CheckoutOpts, errorTarget) - populateFetchOptions(&copts.fetch_opts, opts.FetchOptions, errorTarget) + populateCheckoutOptions(&copts.checkout_opts, &opts.CheckoutOptions, errorTarget) + populateFetchOptions(&copts.fetch_opts, &opts.FetchOptions, errorTarget) copts.bare = cbool(opts.Bare) if opts.RemoteCreateCallback != nil { diff --git a/clone_test.go b/clone_test.go index acfbbcbcf..8814dd002 100644 --- a/clone_test.go +++ b/clone_test.go @@ -49,15 +49,9 @@ func TestCloneWithCallback(t *testing.T) { opts := CloneOptions{ Bare: true, - RemoteCreateCallback: func(r *Repository, name, url string) (*Remote, ErrorCode) { + RemoteCreateCallback: func(r *Repository, name, url string) (*Remote, error) { testPayload += 1 - - remote, err := r.Remotes.Create(REMOTENAME, url) - if err != nil { - return nil, ErrorCodeGeneric - } - - return remote, ErrorCodeOK + return r.Remotes.Create(REMOTENAME, url) }, } diff --git a/commit.go b/commit.go index 0869ef2d9..9a09fe775 100644 --- a/commit.go +++ b/commit.go @@ -37,10 +37,14 @@ func (c *Commit) Message() string { return ret } -func (c *Commit) MessageEncoding() string { - ret := C.GoString(C.git_commit_message_encoding(c.cast_ptr)) +func (c *Commit) MessageEncoding() MessageEncoding { + ptr := C.git_commit_message_encoding(c.cast_ptr) + if ptr == nil { + return MessageEncodingUTF8 + } + ret := C.GoString(ptr) runtime.KeepAlive(c) - return ret + return MessageEncoding(ret) } func (c *Commit) RawMessage() string { @@ -64,6 +68,19 @@ func (c *Commit) ContentToSign() string { // CommitSigningCallback defines a function type that takes some data to sign and returns (signature, signature_field, error) type CommitSigningCallback func(string) (signature, signatureField string, err error) +// CommitCreateCallback defines a function type that is called when another +// function is going to create commits (for example, Rebase) to allow callers +// to override the commit creation behavior. For example, users may wish to +// sign commits by providing this information to Repository.CreateCommitBuffer, +// signing that buffer, then calling Repository.CreateCommitWithSignature. +type CommitCreateCallback func( + author, committer *Signature, + messageEncoding MessageEncoding, + message string, + tree *Tree, + parents ...*Commit, +) (oid *Oid, err error) + // WithSignatureUsing creates a new signed commit from this one using the given signing callback func (c *Commit) WithSignatureUsing(f CommitSigningCallback) (*Oid, error) { signature, signatureField, err := f(c.ContentToSign()) diff --git a/deprecated.go b/deprecated.go index 587fd0e67..01253eecb 100644 --- a/deprecated.go +++ b/deprecated.go @@ -230,7 +230,27 @@ func SubmoduleVisitor(csub unsafe.Pointer, name *C.char, handle unsafe.Pointer) if !ok { panic("invalid submodule visitor callback") } - return (C.int)(callback(sub, C.GoString(name))) + err := callback(sub, C.GoString(name)) + if err != nil { + return C.int(ErrorCodeUser) + } + return C.int(ErrorCodeOK) +} + +// reference.go + +// Deprecated: ReferenceIsValidName is a deprecated alias of ReferenceNameIsValid. +func ReferenceIsValidName(name string) bool { + valid, _ := ReferenceNameIsValid(name) + return valid +} + +// remote.go + +// Deprecated: RemoteIsValidName is a deprecated alias of RemoteNameIsValid. +func RemoteIsValidName(name string) bool { + valid, _ := RemoteNameIsValid(name) + return valid } // tree.go @@ -239,9 +259,13 @@ func SubmoduleVisitor(csub unsafe.Pointer, name *C.char, handle unsafe.Pointer) func CallbackGitTreeWalk(_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 { + callback, ok := pointerHandles.Get(ptr).(TreeWalkCallback) + if !ok { panic("invalid treewalk callback") } + err := callback(root, newTreeEntry(entry)) + if err != nil { + return C.int(ErrorCodeUser) + } + return C.int(ErrorCodeOK) } diff --git a/git.go b/git.go index 62cf5d954..383c49261 100644 --- a/git.go +++ b/git.go @@ -227,7 +227,7 @@ func NewOid(s string) (*Oid, error) { } if len(slice) != 20 { - return nil, &GitError{"Invalid Oid", ErrorClassNone, ErrGeneric} + return nil, &GitError{"invalid oid", ErrorClassNone, ErrorCodeGeneric} } copy(o[:], slice[:20]) diff --git a/go.mod b/go.mod index df3b1fb87..db1ea199d 100644 --- a/go.mod +++ b/go.mod @@ -1,4 +1,4 @@ -module github.com/libgit2/git2go/v31 +module github.com/libgit2/git2go/v32 go 1.13 diff --git a/index.go b/index.go index 48c922c6e..dcb2780bb 100644 --- a/index.go +++ b/index.go @@ -10,13 +10,12 @@ 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 IndexMatchedPathCallback func(string, string) error type indexMatchedPathCallbackData struct { callback IndexMatchedPathCallback errorTarget *error @@ -343,9 +342,9 @@ func indexMatchedPathCallback(cPath, cMatchedPathspec *C.char, payload unsafe.Po panic("invalid matched path callback") } - ret := data.callback(C.GoString(cPath), C.GoString(cMatchedPathspec)) - if ret < 0 { - *data.errorTarget = errors.New(ErrorCode(ret).String()) + err := data.callback(C.GoString(cPath), C.GoString(cMatchedPathspec)) + if err != nil { + *data.errorTarget = err return C.int(ErrorCodeUser) } diff --git a/index_test.go b/index_test.go index 5fa3f9f3d..aea5c1981 100644 --- a/index_test.go +++ b/index_test.go @@ -223,9 +223,9 @@ func TestIndexAddAllCallback(t *testing.T) { checkFatal(t, err) cbPath := "" - err = idx.AddAll([]string{}, IndexAddDefault, func(p, mP string) int { + err = idx.AddAll([]string{}, IndexAddDefault, func(p, mP string) error { cbPath = p - return 0 + return nil }) checkFatal(t, err) if cbPath != "README" { diff --git a/indexer_test.go b/indexer_test.go index 70b9f761b..1566f9750 100644 --- a/indexer_test.go +++ b/indexer_test.go @@ -33,9 +33,9 @@ func TestIndexerOutOfOrder(t *testing.T) { defer os.RemoveAll(tmpPath) var finalStats TransferProgress - idx, err := NewIndexer(tmpPath, nil, func(stats TransferProgress) ErrorCode { + idx, err := NewIndexer(tmpPath, nil, func(stats TransferProgress) error { finalStats = stats - return ErrorCodeOK + return nil }) checkFatal(t, err) defer idx.Free() diff --git a/merge.go b/merge.go index a8e7d6ece..6e7d581e7 100644 --- a/merge.go +++ b/merge.go @@ -138,7 +138,6 @@ const ( ) type MergeOptions struct { - Version uint TreeFlags MergeTreeFlag RenameThreshold uint @@ -151,7 +150,6 @@ type MergeOptions struct { func mergeOptionsFromC(opts *C.git_merge_options) MergeOptions { return MergeOptions{ - Version: uint(opts.version), TreeFlags: MergeTreeFlag(opts.flags), RenameThreshold: uint(opts.rename_threshold), TargetLimit: uint(opts.target_limit), diff --git a/odb_test.go b/odb_test.go index ed5c24c55..26848511f 100644 --- a/odb_test.go +++ b/odb_test.go @@ -167,9 +167,9 @@ func TestOdbWritepack(t *testing.T) { checkFatal(t, err) var finalStats TransferProgress - writepack, err := odb.NewWritePack(func(stats TransferProgress) ErrorCode { + writepack, err := odb.NewWritePack(func(stats TransferProgress) error { finalStats = stats - return ErrorCodeOK + return nil }) checkFatal(t, err) defer writepack.Free() diff --git a/rebase.go b/rebase.go index 98c3e90f5..58c0b6f95 100644 --- a/rebase.go +++ b/rebase.go @@ -9,6 +9,7 @@ import "C" import ( "errors" "fmt" + "reflect" "runtime" "unsafe" ) @@ -71,76 +72,140 @@ func newRebaseOperationFromC(c *C.git_rebase_operation) *RebaseOperation { return operation } -//export commitSigningCallback -func commitSigningCallback(errorMessage **C.char, _signature *C.git_buf, _signature_field *C.git_buf, _commit_content *C.char, handle unsafe.Pointer) C.int { +//export commitCreateCallback +func commitCreateCallback( + errorMessage **C.char, + _out *C.git_oid, + _author, _committer *C.git_signature, + _message_encoding, _message *C.char, + _tree *C.git_tree, + _parent_count C.size_t, + _parents **C.git_commit, + handle unsafe.Pointer, +) C.int { data, ok := pointerHandles.Get(handle).(*rebaseOptionsData) if !ok { panic("invalid sign payload") } - if data.options.CommitSigningCallback == nil { + if data.options.CommitCreateCallback == nil && data.options.CommitSigningCallback == nil { return C.int(ErrorCodePassthrough) } - commitContent := C.GoString(_commit_content) + messageEncoding := MessageEncodingUTF8 + if _message_encoding != nil { + messageEncoding = MessageEncoding(C.GoString(_message_encoding)) + } + tree := &Tree{ + Object: Object{ + ptr: (*C.git_object)(_tree), + repo: data.repo, + }, + cast_ptr: _tree, + } - signature, signatureField, err := data.options.CommitSigningCallback(commitContent) - if err != nil { - if data.errorTarget != nil { - *data.errorTarget = err + var goParents []*C.git_commit + if _parent_count > 0 { + hdr := reflect.SliceHeader{ + Data: uintptr(unsafe.Pointer(_parents)), + Len: int(_parent_count), + Cap: int(_parent_count), } - return setCallbackError(errorMessage, err) + goParents = *(*[]*C.git_commit)(unsafe.Pointer(&hdr)) } - fillBuf := func(bufData string, buf *C.git_buf) error { - clen := C.size_t(len(bufData)) - cstr := unsafe.Pointer(C.CString(bufData)) - defer C.free(cstr) - - // libgit2 requires the contents of the buffer to be NULL-terminated. - // C.CString() guarantees that the returned buffer will be - // NULL-terminated, so we can safely copy the terminator. - if int(C.git_buf_set(buf, cstr, clen+1)) != 0 { - return errors.New("could not set buffer") + parents := make([]*Commit, int(_parent_count)) + for i, p := range goParents { + parents[i] = &Commit{ + Object: Object{ + ptr: (*C.git_object)(p), + repo: data.repo, + }, + cast_ptr: p, } - - return nil } - if signatureField != "" { - err := fillBuf(signatureField, _signature_field) + if data.options.CommitCreateCallback != nil { + oid, err := data.options.CommitCreateCallback( + newSignatureFromC(_author), + newSignatureFromC(_committer), + messageEncoding, + C.GoString(_message), + tree, + parents..., + ) + if err != nil { + if data.errorTarget != nil { + *data.errorTarget = err + } + return setCallbackError(errorMessage, err) + } + if oid == nil { + return C.int(ErrorCodePassthrough) + } + *_out = *oid.toC() + } else if data.options.CommitSigningCallback != nil { + commitContent, err := data.repo.CreateCommitBuffer( + newSignatureFromC(_author), + newSignatureFromC(_committer), + messageEncoding, + C.GoString(_message), + tree, + parents..., + ) if err != nil { if data.errorTarget != nil { *data.errorTarget = err } return setCallbackError(errorMessage, err) } - } - err = fillBuf(signature, _signature) - if err != nil { - if data.errorTarget != nil { - *data.errorTarget = err + signature, signatureField, err := data.options.CommitSigningCallback(string(commitContent)) + if err != nil { + if data.errorTarget != nil { + *data.errorTarget = err + } + return setCallbackError(errorMessage, err) + } + + oid, err := data.repo.CreateCommitWithSignature(string(commitContent), signature, signatureField) + if err != nil { + if data.errorTarget != nil { + *data.errorTarget = err + } + return setCallbackError(errorMessage, err) } - return setCallbackError(errorMessage, err) + *_out = *oid.toC() } return C.int(ErrorCodeOK) } -// RebaseOptions are used to tell the rebase machinery how to operate +// RebaseOptions are used to tell the rebase machinery how to operate. type RebaseOptions struct { - Version uint - Quiet int - InMemory int - RewriteNotesRef string - MergeOptions MergeOptions - CheckoutOptions CheckoutOptions + Quiet int + InMemory int + RewriteNotesRef string + MergeOptions MergeOptions + CheckoutOptions CheckoutOptions + // CommitCreateCallback is an optional callback that allows users to override + // commit creation when rebasing. If specified, users can create + // their own commit and provide the commit ID, which may be useful for + // signing commits or otherwise customizing the commit creation. If this + // callback returns a nil Oid, then the rebase will continue to create the + // commit. + CommitCreateCallback CommitCreateCallback + // Deprecated: CommitSigningCallback is an optional callback that will be + // called with the commit content, allowing a signature to be added to the + // rebase commit. This field is only used when rebasing. This callback is + // not invoked if a CommitCreateCallback is specified. CommitCreateCallback + // should be used instead of this. CommitSigningCallback CommitSigningCallback } type rebaseOptionsData struct { options *RebaseOptions + repo *Repository errorTarget *error } @@ -160,7 +225,6 @@ func DefaultRebaseOptions() (RebaseOptions, error) { func rebaseOptionsFromC(opts *C.git_rebase_options) RebaseOptions { return RebaseOptions{ - Version: uint(opts.version), Quiet: int(opts.quiet), InMemory: int(opts.inmemory), RewriteNotesRef: C.GoString(opts.rewrite_notes_ref), @@ -169,7 +233,7 @@ func rebaseOptionsFromC(opts *C.git_rebase_options) RebaseOptions { } } -func populateRebaseOptions(copts *C.git_rebase_options, opts *RebaseOptions, errorTarget *error) *C.git_rebase_options { +func populateRebaseOptions(copts *C.git_rebase_options, opts *RebaseOptions, repo *Repository, errorTarget *error) *C.git_rebase_options { C.git_rebase_options_init(copts, C.GIT_REBASE_OPTIONS_VERSION) if opts == nil { return nil @@ -181,9 +245,10 @@ func populateRebaseOptions(copts *C.git_rebase_options, opts *RebaseOptions, err populateMergeOptions(&copts.merge_options, &opts.MergeOptions) populateCheckoutOptions(&copts.checkout_options, &opts.CheckoutOptions, errorTarget) - if opts.CommitSigningCallback != nil { + if opts.CommitCreateCallback != nil || opts.CommitSigningCallback != nil { data := &rebaseOptionsData{ options: opts, + repo: repo, errorTarget: errorTarget, } C._go_git_populate_rebase_callbacks(copts) @@ -239,7 +304,7 @@ func (r *Repository) InitRebase(branch *AnnotatedCommit, upstream *AnnotatedComm var ptr *C.git_rebase var err error - cOpts := populateRebaseOptions(&C.git_rebase_options{}, opts, &err) + cOpts := populateRebaseOptions(&C.git_rebase_options{}, opts, r, &err) ret := C.git_rebase_init(&ptr, r.ptr, branch.ptr, upstream.ptr, onto.ptr, cOpts) runtime.KeepAlive(branch) runtime.KeepAlive(upstream) @@ -263,7 +328,7 @@ func (r *Repository) OpenRebase(opts *RebaseOptions) (*Rebase, error) { var ptr *C.git_rebase var err error - cOpts := populateRebaseOptions(&C.git_rebase_options{}, opts, &err) + cOpts := populateRebaseOptions(&C.git_rebase_options{}, opts, r, &err) ret := C.git_rebase_open(&ptr, r.ptr, cOpts) runtime.KeepAlive(r) if ret == C.int(ErrorCodeUser) && err != nil { diff --git a/reference.go b/reference.go index ae49c2191..46e9b48a6 100644 --- a/reference.go +++ b/reference.go @@ -476,7 +476,7 @@ func (v *ReferenceIterator) Free() { C.git_reference_iterator_free(v.ptr) } -// ReferenceIsValidName returns whether the reference name is well-formed. +// ReferenceNameIsValid returns whether the reference name is well-formed. // // Valid reference names must follow one of two patterns: // @@ -486,11 +486,19 @@ func (v *ReferenceIterator) Free() { // 2. Names prefixed with "refs/" can be almost anything. You must avoid // the characters '~', '^', ':', ' \ ', '?', '[', and '*', and the sequences // ".." and " @ {" which have special meaning to revparse. -func ReferenceIsValidName(name string) bool { +func ReferenceNameIsValid(name string) (bool, error) { cname := C.CString(name) defer C.free(unsafe.Pointer(cname)) - return C.git_reference_is_valid_name(cname) == 1 + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + var valid C.int + ret := C.git_reference_name_is_valid(&valid, cname) + if ret < 0 { + return false, MakeGitError(ret) + } + return valid == 1, nil } const ( diff --git a/reference_test.go b/reference_test.go index 285adb59a..46c9e7ff0 100644 --- a/reference_test.go +++ b/reference_test.go @@ -214,12 +214,16 @@ func TestIsNote(t *testing.T) { } } -func TestReferenceIsValidName(t *testing.T) { +func TestReferenceNameIsValid(t *testing.T) { t.Parallel() - if !ReferenceIsValidName("HEAD") { + valid, err := ReferenceNameIsValid("HEAD") + checkFatal(t, err) + if !valid { t.Errorf("HEAD should be a valid reference name") } - if ReferenceIsValidName("HEAD1") { + valid, err = ReferenceNameIsValid("HEAD1") + checkFatal(t, err) + if valid { t.Errorf("HEAD1 should not be a valid reference name") } } diff --git a/remote.go b/remote.go index 275d4d97e..2696e2094 100644 --- a/remote.go +++ b/remote.go @@ -69,15 +69,15 @@ const ( ConnectDirectionPush ConnectDirection = C.GIT_DIRECTION_PUSH ) -type TransportMessageCallback func(str string) ErrorCode -type CompletionCallback func(RemoteCompletion) ErrorCode +type TransportMessageCallback func(str string) error +type CompletionCallback func(RemoteCompletion) error type CredentialsCallback func(url string, username_from_url string, allowed_types CredentialType) (*Credential, error) -type TransferProgressCallback func(stats TransferProgress) ErrorCode -type UpdateTipsCallback func(refname string, a *Oid, b *Oid) ErrorCode -type CertificateCheckCallback func(cert *Certificate, valid bool, hostname string) ErrorCode -type PackbuilderProgressCallback func(stage int32, current, total uint32) ErrorCode -type PushTransferProgressCallback func(current, total uint32, bytes uint) ErrorCode -type PushUpdateReferenceCallback func(refname, status string) ErrorCode +type TransferProgressCallback func(stats TransferProgress) error +type UpdateTipsCallback func(refname string, a *Oid, b *Oid) error +type CertificateCheckCallback func(cert *Certificate, valid bool, hostname string) error +type PackbuilderProgressCallback func(stage int32, current, total uint32) error +type PushTransferProgressCallback func(current, total uint32, bytes uint) error +type PushUpdateReferenceCallback func(refname, status string) error type RemoteCallbacks struct { SidebandProgressCallback TransportMessageCallback @@ -329,10 +329,8 @@ func sidebandProgressCallback(errorMessage **C.char, _str *C.char, _len C.int, h if data.callbacks.SidebandProgressCallback == nil { return C.int(ErrorCodeOK) } - str := C.GoStringN(_str, _len) - ret := data.callbacks.SidebandProgressCallback(str) - if ret < 0 { - err := errors.New(ErrorCode(ret).String()) + err := data.callbacks.SidebandProgressCallback(C.GoStringN(_str, _len)) + if err != nil { if data.errorTarget != nil { *data.errorTarget = err } @@ -342,14 +340,13 @@ func sidebandProgressCallback(errorMessage **C.char, _str *C.char, _len C.int, h } //export completionCallback -func completionCallback(errorMessage **C.char, completion_type C.git_remote_completion_type, handle unsafe.Pointer) C.int { +func completionCallback(errorMessage **C.char, completionType C.git_remote_completion_type, handle unsafe.Pointer) C.int { data := pointerHandles.Get(handle).(*remoteCallbacksData) if data.callbacks.CompletionCallback == nil { return C.int(ErrorCodeOK) } - ret := data.callbacks.CompletionCallback(RemoteCompletion(completion_type)) - if ret < 0 { - err := errors.New(ErrorCode(ret).String()) + err := data.callbacks.CompletionCallback(RemoteCompletion(completionType)) + if err != nil { if data.errorTarget != nil { *data.errorTarget = err } @@ -396,9 +393,8 @@ func transferProgressCallback(errorMessage **C.char, stats *C.git_transfer_progr if data.callbacks.TransferProgressCallback == nil { return C.int(ErrorCodeOK) } - ret := data.callbacks.TransferProgressCallback(newTransferProgressFromC(stats)) - if ret < 0 { - err := errors.New(ErrorCode(ret).String()) + err := data.callbacks.TransferProgressCallback(newTransferProgressFromC(stats)) + if err != nil { if data.errorTarget != nil { *data.errorTarget = err } @@ -422,9 +418,8 @@ func updateTipsCallback( refname := C.GoString(_refname) a := newOidFromC(_a) b := newOidFromC(_b) - ret := data.callbacks.UpdateTipsCallback(refname, a, b) - if ret < 0 { - err := errors.New(ErrorCode(ret).String()) + err := data.callbacks.UpdateTipsCallback(refname, a, b) + if err != nil { if data.errorTarget != nil { *data.errorTarget = err } @@ -489,9 +484,8 @@ func certificateCheckCallback( return setCallbackError(errorMessage, err) } - ret := data.callbacks.CertificateCheckCallback(&cert, valid, host) - if ret < 0 { - err := errors.New(ErrorCode(ret).String()) + err := data.callbacks.CertificateCheckCallback(&cert, valid, host) + if err != nil { if data.errorTarget != nil { *data.errorTarget = err } @@ -507,9 +501,8 @@ func packProgressCallback(errorMessage **C.char, stage C.int, current, total C.u return C.int(ErrorCodeOK) } - ret := data.callbacks.PackProgressCallback(int32(stage), uint32(current), uint32(total)) - if ret < 0 { - err := errors.New(ErrorCode(ret).String()) + err := data.callbacks.PackProgressCallback(int32(stage), uint32(current), uint32(total)) + if err != nil { if data.errorTarget != nil { *data.errorTarget = err } @@ -525,9 +518,8 @@ func pushTransferProgressCallback(errorMessage **C.char, current, total C.uint, return C.int(ErrorCodeOK) } - ret := data.callbacks.PushTransferProgressCallback(uint32(current), uint32(total), uint(bytes)) - if ret < 0 { - err := errors.New(ErrorCode(ret).String()) + err := data.callbacks.PushTransferProgressCallback(uint32(current), uint32(total), uint(bytes)) + if err != nil { if data.errorTarget != nil { *data.errorTarget = err } @@ -543,9 +535,8 @@ func pushUpdateReferenceCallback(errorMessage **C.char, refname, status *C.char, return C.int(ErrorCodeOK) } - ret := data.callbacks.PushUpdateReferenceCallback(C.GoString(refname), C.GoString(status)) - if ret < 0 { - err := errors.New(ErrorCode(ret).String()) + err := data.callbacks.PushUpdateReferenceCallback(C.GoString(refname), C.GoString(status)) + if err != nil { if data.errorTarget != nil { *data.errorTarget = err } @@ -573,12 +564,20 @@ func freeProxyOptions(copts *C.git_proxy_options) { C.free(unsafe.Pointer(copts.url)) } -// RemoteIsValidName returns whether the remote name is well-formed. -func RemoteIsValidName(name string) bool { +// RemoteNameIsValid returns whether the remote name is well-formed. +func RemoteNameIsValid(name string) (bool, error) { cname := C.CString(name) defer C.free(unsafe.Pointer(cname)) - return C.git_remote_is_valid_name(cname) == 1 + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + var valid C.int + ret := C.git_remote_name_is_valid(&valid, cname) + if ret < 0 { + return false, MakeGitError(ret) + } + return valid == 1, nil } // free releases the resources of the Remote. diff --git a/remote_test.go b/remote_test.go index 9660a3f18..05395b3a7 100644 --- a/remote_test.go +++ b/remote_test.go @@ -38,13 +38,13 @@ func TestListRemotes(t *testing.T) { compareStringList(t, expected, actual) } -func assertHostname(cert *Certificate, valid bool, hostname string, t *testing.T) ErrorCode { +func assertHostname(cert *Certificate, valid bool, hostname string, t *testing.T) error { if hostname != "github.com" { - t.Fatal("Hostname does not match") - return ErrorCodeUser + t.Fatal("hostname does not match") + return errors.New("hostname does not match") } - return ErrorCodeOK + return nil } func TestCertificateCheck(t *testing.T) { @@ -58,7 +58,7 @@ func TestCertificateCheck(t *testing.T) { options := FetchOptions{ RemoteCallbacks: RemoteCallbacks{ - CertificateCheckCallback: func(cert *Certificate, valid bool, hostname string) ErrorCode { + CertificateCheckCallback: func(cert *Certificate, valid bool, hostname string) error { return assertHostname(cert, valid, hostname, t) }, }, @@ -479,14 +479,13 @@ func TestRemoteSSH(t *testing.T) { certificateCheckCallbackCalled := false fetchOpts := FetchOptions{ RemoteCallbacks: RemoteCallbacks{ - CertificateCheckCallback: func(cert *Certificate, valid bool, hostname string) ErrorCode { + CertificateCheckCallback: func(cert *Certificate, valid bool, hostname string) error { hostkeyFingerprint := fmt.Sprintf("%x", cert.Hostkey.HashMD5[:]) if hostkeyFingerprint != publicKeyFingerprint { - t.Logf("server hostkey %q, want %q", hostkeyFingerprint, publicKeyFingerprint) - return ErrorCodeAuth + return fmt.Errorf("server hostkey %q, want %q", hostkeyFingerprint, publicKeyFingerprint) } certificateCheckCallbackCalled = true - return ErrorCodeOK + return nil }, CredentialsCallback: func(url, username string, allowedTypes CredentialType) (*Credential, error) { if allowedTypes&(CredentialTypeSSHKey|CredentialTypeSSHCustom|CredentialTypeSSHMemory) != 0 { diff --git a/revert.go b/revert.go index 085df169c..76ddf532e 100644 --- a/revert.go +++ b/revert.go @@ -10,9 +10,9 @@ import ( // RevertOptions contains options for performing a revert type RevertOptions struct { - Mainline uint - MergeOpts MergeOptions - CheckoutOpts CheckoutOptions + Mainline uint + MergeOptions MergeOptions + CheckoutOptions CheckoutOptions } func populateRevertOptions(copts *C.git_revert_options, opts *RevertOptions, errorTarget *error) *C.git_revert_options { @@ -21,16 +21,16 @@ func populateRevertOptions(copts *C.git_revert_options, opts *RevertOptions, err return nil } copts.mainline = C.uint(opts.Mainline) - populateMergeOptions(&copts.merge_opts, &opts.MergeOpts) - populateCheckoutOptions(&copts.checkout_opts, &opts.CheckoutOpts, errorTarget) + populateMergeOptions(&copts.merge_opts, &opts.MergeOptions) + populateCheckoutOptions(&copts.checkout_opts, &opts.CheckoutOptions, errorTarget) return copts } func revertOptionsFromC(copts *C.git_revert_options) RevertOptions { return RevertOptions{ - Mainline: uint(copts.mainline), - MergeOpts: mergeOptionsFromC(&copts.merge_opts), - CheckoutOpts: checkoutOptionsFromC(&copts.checkout_opts), + Mainline: uint(copts.mainline), + MergeOptions: mergeOptionsFromC(&copts.merge_opts), + CheckoutOptions: checkoutOptionsFromC(&copts.checkout_opts), } } diff --git a/revert_test.go b/revert_test.go index fcf8e43a6..1104866ae 100644 --- a/revert_test.go +++ b/revert_test.go @@ -60,11 +60,11 @@ func TestRevertCommit(t *testing.T) { revertOptions, err := DefaultRevertOptions() checkFatal(t, err) - index, err := repo.RevertCommit(commit, commit, 0, &revertOptions.MergeOpts) + index, err := repo.RevertCommit(commit, commit, 0, &revertOptions.MergeOptions) checkFatal(t, err) defer index.Free() - err = repo.CheckoutIndex(index, &revertOptions.CheckoutOpts) + err = repo.CheckoutIndex(index, &revertOptions.CheckoutOptions) checkFatal(t, err) actualReadmeContents := readReadme(t, repo) diff --git a/submodule.go b/submodule.go index 673cf5fb0..34d50318e 100644 --- a/submodule.go +++ b/submodule.go @@ -8,15 +8,14 @@ extern int _go_git_visit_submodule(git_repository *repo, void *fct); import "C" import ( - "errors" "runtime" "unsafe" ) // SubmoduleUpdateOptions type SubmoduleUpdateOptions struct { - *CheckoutOpts - *FetchOptions + CheckoutOptions CheckoutOptions + FetchOptions FetchOptions } // Submodule @@ -111,7 +110,7 @@ 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 SubmoduleCallback func(sub *Submodule, name string) error type submoduleCallbackData struct { callback SubmoduleCallback errorTarget *error @@ -126,9 +125,9 @@ func submoduleCallback(csub unsafe.Pointer, name *C.char, handle unsafe.Pointer) panic("invalid submodule visitor callback") } - ret := data.callback(sub, C.GoString(name)) - if ret < 0 { - *data.errorTarget = errors.New(ErrorCode(ret).String()) + err := data.callback(sub, C.GoString(name)) + if err != nil { + *data.errorTarget = err return C.int(ErrorCodeUser) } @@ -391,8 +390,8 @@ func populateSubmoduleUpdateOptions(copts *C.git_submodule_update_options, opts return nil } - populateCheckoutOptions(&copts.checkout_opts, opts.CheckoutOpts, errorTarget) - populateFetchOptions(&copts.fetch_opts, opts.FetchOptions, errorTarget) + populateCheckoutOptions(&copts.checkout_opts, &opts.CheckoutOptions, errorTarget) + populateFetchOptions(&copts.fetch_opts, &opts.FetchOptions, errorTarget) return copts } diff --git a/submodule_test.go b/submodule_test.go index fa2e98cd6..09ddae5ec 100644 --- a/submodule_test.go +++ b/submodule_test.go @@ -15,9 +15,9 @@ func TestSubmoduleForeach(t *testing.T) { checkFatal(t, err) i := 0 - err = repo.Submodules.Foreach(func(sub *Submodule, name string) int { + err = repo.Submodules.Foreach(func(sub *Submodule, name string) error { i++ - return 0 + return nil }) checkFatal(t, err) diff --git a/tree.go b/tree.go index 14fe7e449..b1aeaa7e2 100644 --- a/tree.go +++ b/tree.go @@ -8,7 +8,6 @@ extern int _go_git_treewalk(git_tree *tree, git_treewalk_mode mode, void *ptr); import "C" import ( - "errors" "runtime" "unsafe" ) @@ -121,7 +120,7 @@ func (t *Tree) EntryCount() uint64 { return uint64(num) } -type TreeWalkCallback func(string, *TreeEntry) int +type TreeWalkCallback func(string, *TreeEntry) error type treeWalkCallbackData struct { callback TreeWalkCallback errorTarget *error @@ -134,9 +133,9 @@ func treeWalkCallback(_root *C.char, entry *C.git_tree_entry, ptr unsafe.Pointer panic("invalid treewalk callback") } - ret := data.callback(C.GoString(_root), newTreeEntry(entry)) - if ret < 0 { - *data.errorTarget = errors.New(ErrorCode(ret).String()) + err := data.callback(C.GoString(_root), newTreeEntry(entry)) + if err != nil { + *data.errorTarget = err return C.int(ErrorCodeUser) } diff --git a/vendor/libgit2 b/vendor/libgit2 index 7f4fa1786..109b4c887 160000 --- a/vendor/libgit2 +++ b/vendor/libgit2 @@ -1 +1 @@ -Subproject commit 7f4fa178629d559c037a1f72f79f79af9c1ef8ce +Subproject commit 109b4c887ffb63962c7017a66fc4a1f48becb48e diff --git a/wrapper.c b/wrapper.c index 6f65f7154..e999136dd 100644 --- a/wrapper.c +++ b/wrapper.c @@ -116,18 +116,28 @@ void _go_git_populate_apply_callbacks(git_apply_options *options) options->hunk_cb = (git_apply_hunk_cb)&hunkApplyCallback; } -static int commit_signing_callback( - git_buf *signature, - git_buf *signature_field, - const char *commit_contents, +static int commit_create_callback( + git_oid *out, + const git_signature *author, + const git_signature *committer, + const char *message_encoding, + const char *message, + const git_tree *tree, + size_t parent_count, + const git_commit *parents[], void *payload) { char *error_message = NULL; - const int ret = commitSigningCallback( + const int ret = commitCreateCallback( &error_message, - signature, - signature_field, - (char *)commit_contents, + out, + (git_signature *)author, + (git_signature *)committer, + (char *)message_encoding, + (char *)message, + (git_tree *)tree, + parent_count, + (git_commit **)parents, payload ); return set_callback_error(error_message, ret); @@ -135,7 +145,7 @@ static int commit_signing_callback( void _go_git_populate_rebase_callbacks(git_rebase_options *opts) { - opts->signing_cb = commit_signing_callback; + opts->commit_create_cb = commit_create_callback; } void _go_git_populate_clone_callbacks(git_clone_options *opts)