libgit2 v1.2.0 #major

This commit introduces libgit2 v1.2.0 to git2go, which brings a large
number of [bugfixes and
features](https://github.com/libgit2/libgit2/releases/tag/v1.2.0).

This also marks the start of the v32 release.
This commit is contained in:
lhchavez 2021-09-03 06:40:31 -07:00
parent b78bde3d74
commit 018647fd48
15 changed files with 214 additions and 81 deletions

View File

@ -12,7 +12,7 @@ jobs:
strategy: strategy:
fail-fast: false fail-fast: false
matrix: 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 runs-on: ubuntu-20.04

View File

@ -62,8 +62,7 @@ jobs:
fail-fast: false fail-fast: false
matrix: matrix:
libgit2: libgit2:
- 'v1.1.0' - '109b4c887ffb63962c7017a66fc4a1f48becb48e' # v1.2.0 with a fixed symbol
- 'v1.2.0'
name: Go (system-wide, dynamic) name: Go (system-wide, dynamic)
runs-on: ubuntu-20.04 runs-on: ubuntu-20.04

View File

@ -1,3 +1,4 @@
//go:build static && !system_libgit2
// +build static,!system_libgit2 // +build static,!system_libgit2
package git package git
@ -9,8 +10,8 @@ package git
#cgo CFLAGS: -DLIBGIT2_STATIC #cgo CFLAGS: -DLIBGIT2_STATIC
#include <git2.h> #include <git2.h>
#if LIBGIT2_VER_MAJOR != 1 || LIBGIT2_VER_MINOR < 1 || LIBGIT2_VER_MINOR > 2 #if LIBGIT2_VER_MAJOR != 1 || LIBGIT2_VER_MINOR < 2 || LIBGIT2_VER_MINOR > 2
# error "Invalid libgit2 version; this git2go supports libgit2 between v1.1.0 and v1.2.0" # error "Invalid libgit2 version; this git2go supports libgit2 between v1.2.0 and v1.2.0"
#endif #endif
*/ */
import "C" import "C"

View File

@ -1,3 +1,4 @@
//go:build !static
// +build !static // +build !static
package git package git
@ -7,8 +8,8 @@ package git
#cgo CFLAGS: -DLIBGIT2_DYNAMIC #cgo CFLAGS: -DLIBGIT2_DYNAMIC
#include <git2.h> #include <git2.h>
#if LIBGIT2_VER_MAJOR != 1 || LIBGIT2_VER_MINOR < 1 || LIBGIT2_VER_MINOR > 2 #if LIBGIT2_VER_MAJOR != 1 || LIBGIT2_VER_MINOR < 2 || LIBGIT2_VER_MINOR > 2
# error "Invalid libgit2 version; this git2go supports libgit2 between v1.1.0 and v1.2.0" # error "Invalid libgit2 version; this git2go supports libgit2 between v1.2.0 and v1.2.0"
#endif #endif
*/ */
import "C" import "C"

View File

@ -1,3 +1,4 @@
//go:build static && system_libgit2
// +build static,system_libgit2 // +build static,system_libgit2
package git package git
@ -7,8 +8,8 @@ package git
#cgo CFLAGS: -DLIBGIT2_STATIC #cgo CFLAGS: -DLIBGIT2_STATIC
#include <git2.h> #include <git2.h>
#if LIBGIT2_VER_MAJOR != 1 || LIBGIT2_VER_MINOR < 1 || LIBGIT2_VER_MINOR > 2 #if LIBGIT2_VER_MAJOR != 1 || LIBGIT2_VER_MINOR < 2 || LIBGIT2_VER_MINOR > 2
# error "Invalid libgit2 version; this git2go supports libgit2 between v1.1.0 and v1.2.0" # error "Invalid libgit2 version; this git2go supports libgit2 between v1.2.0 and v1.2.0"
#endif #endif
*/ */
import "C" import "C"

View File

@ -10,20 +10,21 @@ Due to the fact that Go 1.11 module versions have semantic meaning and don't nec
| libgit2 | git2go | | libgit2 | git2go |
|---------|---------------| |---------|---------------|
| main | (will be v32) | | main | (will be v33) |
| 1.2 | v32 |
| 1.1 | v31 | | 1.1 | v31 |
| 1.0 | v30 | | 1.0 | v30 |
| 0.99 | v29 | | 0.99 | v29 |
| 0.28 | v28 | | 0.28 | v28 |
| 0.27 | v27 | | 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 ```sh
go get github.com/libgit2/git2go/v31 go get github.com/libgit2/git2go/v32
``` ```
```go ```go
import "github.com/libgit2/git2go/v31" import "github.com/libgit2/git2go/v32"
``` ```
which will ensure there are no sudden changes to the API. 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 ### 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 ```go
import "github.com/libgit2/git2go/v31" import "github.com/libgit2/git2go/v32"
``` ```
### Versioned branch, static linking ### 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 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 Parallelism and network operations
---------------------------------- ----------------------------------

View File

@ -37,10 +37,14 @@ func (c *Commit) Message() string {
return ret return ret
} }
func (c *Commit) MessageEncoding() string { func (c *Commit) MessageEncoding() MessageEncoding {
ret := C.GoString(C.git_commit_message_encoding(c.cast_ptr)) ptr := C.git_commit_message_encoding(c.cast_ptr)
if ptr == nil {
return MessageEncodingUTF8
}
ret := C.GoString(ptr)
runtime.KeepAlive(c) runtime.KeepAlive(c)
return ret return MessageEncoding(ret)
} }
func (c *Commit) RawMessage() string { 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) // 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) 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 // WithSignatureUsing creates a new signed commit from this one using the given signing callback
func (c *Commit) WithSignatureUsing(f CommitSigningCallback) (*Oid, error) { func (c *Commit) WithSignatureUsing(f CommitSigningCallback) (*Oid, error) {
signature, signatureField, err := f(c.ContentToSign()) signature, signatureField, err := f(c.ContentToSign())

View File

@ -237,6 +237,22 @@ func SubmoduleVisitor(csub unsafe.Pointer, name *C.char, handle unsafe.Pointer)
return C.int(ErrorCodeOK) 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 // tree.go
// Deprecated: CallbackGitTreeWalk is not used. // Deprecated: CallbackGitTreeWalk is not used.

2
go.mod
View File

@ -1,4 +1,4 @@
module github.com/libgit2/git2go/v31 module github.com/libgit2/git2go/v32
go 1.13 go 1.13

125
rebase.go
View File

@ -9,6 +9,7 @@ import "C"
import ( import (
"errors" "errors"
"fmt" "fmt"
"reflect"
"runtime" "runtime"
"unsafe" "unsafe"
) )
@ -71,20 +72,87 @@ func newRebaseOperationFromC(c *C.git_rebase_operation) *RebaseOperation {
return operation return operation
} }
//export commitSigningCallback //export commitCreateCallback
func commitSigningCallback(errorMessage **C.char, _signature *C.git_buf, _signature_field *C.git_buf, _commit_content *C.char, handle unsafe.Pointer) C.int { 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) data, ok := pointerHandles.Get(handle).(*rebaseOptionsData)
if !ok { if !ok {
panic("invalid sign payload") panic("invalid sign payload")
} }
if data.options.CommitSigningCallback == nil { if data.options.CommitCreateCallback == nil && data.options.CommitSigningCallback == nil {
return C.int(ErrorCodePassthrough) 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) 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),
}
goParents = *(*[]*C.git_commit)(unsafe.Pointer(&hdr))
}
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,
}
}
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 err != nil {
if data.errorTarget != nil { if data.errorTarget != nil {
*data.errorTarget = err *data.errorTarget = err
@ -92,54 +160,52 @@ func commitSigningCallback(errorMessage **C.char, _signature *C.git_buf, _signat
return setCallbackError(errorMessage, err) return setCallbackError(errorMessage, err)
} }
fillBuf := func(bufData string, buf *C.git_buf) error { signature, signatureField, err := data.options.CommitSigningCallback(string(commitContent))
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")
}
return nil
}
if signatureField != "" {
err := fillBuf(signatureField, _signature_field)
if err != nil { if err != nil {
if data.errorTarget != nil { if data.errorTarget != nil {
*data.errorTarget = err *data.errorTarget = err
} }
return setCallbackError(errorMessage, err) return setCallbackError(errorMessage, err)
} }
}
err = fillBuf(signature, _signature) oid, err := data.repo.CreateCommitWithSignature(string(commitContent), signature, signatureField)
if err != nil { if err != nil {
if data.errorTarget != nil { if data.errorTarget != nil {
*data.errorTarget = err *data.errorTarget = err
} }
return setCallbackError(errorMessage, err) return setCallbackError(errorMessage, err)
} }
*_out = *oid.toC()
}
return C.int(ErrorCodeOK) 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 { type RebaseOptions struct {
Quiet int Quiet int
InMemory int InMemory int
RewriteNotesRef string RewriteNotesRef string
MergeOptions MergeOptions MergeOptions MergeOptions
CheckoutOptions CheckoutOptions 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 CommitSigningCallback CommitSigningCallback
} }
type rebaseOptionsData struct { type rebaseOptionsData struct {
options *RebaseOptions options *RebaseOptions
repo *Repository
errorTarget *error errorTarget *error
} }
@ -167,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) C.git_rebase_options_init(copts, C.GIT_REBASE_OPTIONS_VERSION)
if opts == nil { if opts == nil {
return nil return nil
@ -179,9 +245,10 @@ func populateRebaseOptions(copts *C.git_rebase_options, opts *RebaseOptions, err
populateMergeOptions(&copts.merge_options, &opts.MergeOptions) populateMergeOptions(&copts.merge_options, &opts.MergeOptions)
populateCheckoutOptions(&copts.checkout_options, &opts.CheckoutOptions, errorTarget) populateCheckoutOptions(&copts.checkout_options, &opts.CheckoutOptions, errorTarget)
if opts.CommitSigningCallback != nil { if opts.CommitCreateCallback != nil || opts.CommitSigningCallback != nil {
data := &rebaseOptionsData{ data := &rebaseOptionsData{
options: opts, options: opts,
repo: repo,
errorTarget: errorTarget, errorTarget: errorTarget,
} }
C._go_git_populate_rebase_callbacks(copts) C._go_git_populate_rebase_callbacks(copts)
@ -237,7 +304,7 @@ func (r *Repository) InitRebase(branch *AnnotatedCommit, upstream *AnnotatedComm
var ptr *C.git_rebase var ptr *C.git_rebase
var err error 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) ret := C.git_rebase_init(&ptr, r.ptr, branch.ptr, upstream.ptr, onto.ptr, cOpts)
runtime.KeepAlive(branch) runtime.KeepAlive(branch)
runtime.KeepAlive(upstream) runtime.KeepAlive(upstream)
@ -261,7 +328,7 @@ func (r *Repository) OpenRebase(opts *RebaseOptions) (*Rebase, error) {
var ptr *C.git_rebase var ptr *C.git_rebase
var err error 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) ret := C.git_rebase_open(&ptr, r.ptr, cOpts)
runtime.KeepAlive(r) runtime.KeepAlive(r)
if ret == C.int(ErrorCodeUser) && err != nil { if ret == C.int(ErrorCodeUser) && err != nil {

View File

@ -476,7 +476,7 @@ func (v *ReferenceIterator) Free() {
C.git_reference_iterator_free(v.ptr) 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: // 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 // 2. Names prefixed with "refs/" can be almost anything. You must avoid
// the characters '~', '^', ':', ' \ ', '?', '[', and '*', and the sequences // the characters '~', '^', ':', ' \ ', '?', '[', and '*', and the sequences
// ".." and " @ {" which have special meaning to revparse. // ".." and " @ {" which have special meaning to revparse.
func ReferenceIsValidName(name string) bool { func ReferenceNameIsValid(name string) (bool, error) {
cname := C.CString(name) cname := C.CString(name)
defer C.free(unsafe.Pointer(cname)) 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 ( const (

View File

@ -214,12 +214,16 @@ func TestIsNote(t *testing.T) {
} }
} }
func TestReferenceIsValidName(t *testing.T) { func TestReferenceNameIsValid(t *testing.T) {
t.Parallel() t.Parallel()
if !ReferenceIsValidName("HEAD") { valid, err := ReferenceNameIsValid("HEAD")
checkFatal(t, err)
if !valid {
t.Errorf("HEAD should be a valid reference name") 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") t.Errorf("HEAD1 should not be a valid reference name")
} }
} }

View File

@ -564,12 +564,20 @@ func freeProxyOptions(copts *C.git_proxy_options) {
C.free(unsafe.Pointer(copts.url)) C.free(unsafe.Pointer(copts.url))
} }
// RemoteIsValidName returns whether the remote name is well-formed. // RemoteNameIsValid returns whether the remote name is well-formed.
func RemoteIsValidName(name string) bool { func RemoteNameIsValid(name string) (bool, error) {
cname := C.CString(name) cname := C.CString(name)
defer C.free(unsafe.Pointer(cname)) 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. // free releases the resources of the Remote.

2
vendor/libgit2 vendored

@ -1 +1 @@
Subproject commit 7f4fa178629d559c037a1f72f79f79af9c1ef8ce Subproject commit 109b4c887ffb63962c7017a66fc4a1f48becb48e

View File

@ -116,18 +116,28 @@ void _go_git_populate_apply_callbacks(git_apply_options *options)
options->hunk_cb = (git_apply_hunk_cb)&hunkApplyCallback; options->hunk_cb = (git_apply_hunk_cb)&hunkApplyCallback;
} }
static int commit_signing_callback( static int commit_create_callback(
git_buf *signature, git_oid *out,
git_buf *signature_field, const git_signature *author,
const char *commit_contents, 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) void *payload)
{ {
char *error_message = NULL; char *error_message = NULL;
const int ret = commitSigningCallback( const int ret = commitCreateCallback(
&error_message, &error_message,
signature, out,
signature_field, (git_signature *)author,
(char *)commit_contents, (git_signature *)committer,
(char *)message_encoding,
(char *)message,
(git_tree *)tree,
parent_count,
(git_commit **)parents,
payload payload
); );
return set_callback_error(error_message, ret); 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) 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) void _go_git_populate_clone_callbacks(git_clone_options *opts)