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:
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

View File

@ -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

View File

@ -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 <git2.h>
#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"

View File

@ -1,3 +1,4 @@
//go:build !static
// +build !static
package git
@ -7,8 +8,8 @@ package git
#cgo CFLAGS: -DLIBGIT2_DYNAMIC
#include <git2.h>
#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"

View File

@ -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 <git2.h>
#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"

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 |
|---------|---------------|
| 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
----------------------------------

View File

@ -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())

View File

@ -237,6 +237,22 @@ func SubmoduleVisitor(csub unsafe.Pointer, name *C.char, handle unsafe.Pointer)
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
// 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

151
rebase.go
View File

@ -9,6 +9,7 @@ import "C"
import (
"errors"
"fmt"
"reflect"
"runtime"
"unsafe"
)
@ -71,75 +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)
signature, signatureField, err := data.options.CommitSigningCallback(commitContent)
if err != nil {
if data.errorTarget != nil {
*data.errorTarget = err
}
return setCallbackError(errorMessage, err)
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,
}
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")
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 nil
goParents = *(*[]*C.git_commit)(unsafe.Pointer(&hdr))
}
if signatureField != "" {
err := fillBuf(signatureField, _signature_field)
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)
}
}
err = fillBuf(signature, _signature)
if err != nil {
if data.errorTarget != nil {
*data.errorTarget = err
if oid == nil {
return C.int(ErrorCodePassthrough)
}
return setCallbackError(errorMessage, err)
*_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)
}
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)
}
*_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 {
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
}
@ -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)
if opts == nil {
return nil
@ -179,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)
@ -237,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)
@ -261,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 {

View File

@ -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 (

View File

@ -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")
}
}

View File

@ -564,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.

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;
}
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)