Add support for creating signed commits and signing commits during a rebase #626

Merged
mbfr merged 25 commits from master into master 2020-08-18 11:25:31 -05:00
7 changed files with 329 additions and 9 deletions
Showing only changes of commit c9e192b7e5 - Show all commits

View File

@ -40,6 +40,62 @@ func (c *Commit) RawMessage() string {
return ret return ret
} }
func (c *Commit) RawHeader() string {
ret := C.GoString(C.git_commit_raw_header(c.cast_ptr))
lhchavez commented 2020-08-18 08:50:58 -05:00 (Migrated from github.com)
Review

one more thing i forgot, can the public functions have a docstring? most of the contents can be copied from the C docs.

one more thing i forgot, can the public functions have a docstring? most of the contents can be copied from the C docs.
mbfr commented 2020-08-18 09:18:00 -05:00 (Migrated from github.com)
Review

Fixed

Fixed
runtime.KeepAlive(c)
return ret
}
// ContentToSign returns the content that will be passed to a signing function for this commit
func (c *Commit) ContentToSign() string {
return c.RawHeader() + "\n" + c.RawMessage()
}
// CommitSigningCb defines a function type that takes some data to sign and returns (signature, signature_field, error)
type CommitSigningCb func(string) (string, string, error)
// WithSignatureUsing creates a new signed commit from this one using the given signing callback
func (c *Commit) WithSignatureUsing(f CommitSigningCb) (*Oid, error) {
signature, signatureField, err := f(c.ContentToSign())
if err != nil {
return nil, err
}
return c.WithSignature(signature, signatureField)
}
// WithSignature creates a new signed commit from the given signature and signature field
func (c *Commit) WithSignature(signature string, signatureField string) (*Oid, error) {
totalCommit := c.ContentToSign()
oid := new(Oid)
var csf *C.char = nil
if signatureField != "" {
csf = C.CString(signatureField)
}
lhchavez commented 2020-08-14 08:41:25 -05:00 (Migrated from github.com)
Review

since C.CString() makes a copy, this needs a defer C.free(unsafe.Pointer(csf)) to avoid a memory leak: https://blog.golang.org/cgo#TOC_2.

since `C.CString()` makes a copy, this needs a `defer C.free(unsafe.Pointer(csf))` to avoid a memory leak: https://blog.golang.org/cgo#TOC_2.
mbfr commented 2020-08-14 09:50:05 -05:00 (Migrated from github.com)
Review

Added

Added
runtime.LockOSThread()
defer runtime.UnlockOSThread()
ret := C.git_commit_create_with_signature(
oid.toC(),
c.Owner().ptr,
C.CString(totalCommit),
C.CString(signature),
csf,
)
lhchavez commented 2020-08-14 08:43:24 -05:00 (Migrated from github.com)
Review

for consistency with Amend() would it be possible to call this variable cerr?

for consistency with [`Amend()`](https://github.com/libgit2/git2go/blob/462ebd83e0ccba9cd93c05ec12dc3d98064e3d5c/commit.go#L160) would it be possible to call this variable `cerr`?
mbfr commented 2020-08-14 09:40:57 -05:00 (Migrated from github.com)
Review

There isn't really a lot of consistency with other functions, sometimes it's called code, sometimes ret, sometimes, ecode, sometimes cerr, etc.

There isn't really a lot of consistency with other functions, sometimes it's called `code`, sometimes `ret`, sometimes, `ecode`, sometimes `cerr`, etc.
runtime.KeepAlive(c)
runtime.KeepAlive(oid)
if ret < 0 {
return nil, MakeGitError(ret)
}
return oid, nil
}
func (c *Commit) ExtractSignature() (string, string, error) { func (c *Commit) ExtractSignature() (string, string, error) {
var c_signed C.git_buf var c_signed C.git_buf

View File

@ -45,7 +45,16 @@ func createBareTestRepo(t *testing.T) *Repository {
return repo return repo
} }
// commitOpts contains any extra options for creating commits in the seed repo
type commitOpts struct {
CommitSigningCb
}
func seedTestRepo(t *testing.T, repo *Repository) (*Oid, *Oid) { func seedTestRepo(t *testing.T, repo *Repository) (*Oid, *Oid) {
return seedTestRepoOpt(t, repo, commitOpts{})
}
func seedTestRepoOpt(t *testing.T, repo *Repository, opts commitOpts) (*Oid, *Oid) {
loc, err := time.LoadLocation("Europe/Berlin") loc, err := time.LoadLocation("Europe/Berlin")
checkFatal(t, err) checkFatal(t, err)
sig := &Signature{ sig := &Signature{
@ -69,6 +78,28 @@ func seedTestRepo(t *testing.T, repo *Repository) (*Oid, *Oid) {
commitId, err := repo.CreateCommit("HEAD", sig, sig, message, tree) commitId, err := repo.CreateCommit("HEAD", sig, sig, message, tree)
checkFatal(t, err) checkFatal(t, err)
if opts.CommitSigningCb != nil {
commit, err := repo.LookupCommit(commitId)
checkFatal(t, err)
signature, signatureField, err := opts.CommitSigningCb(commit.ContentToSign())
checkFatal(t, err)
oid, err := commit.WithSignature(signature, signatureField)
checkFatal(t, err)
newCommit, err := repo.LookupCommit(oid)
checkFatal(t, err)
head, err := repo.Head()
checkFatal(t, err)
_, err = repo.References.Create(
head.Name(),
newCommit.Id(),
true,
"repoint to signed commit",
)
checkFatal(t, err)
}
return commitId, treeId return commitId, treeId
} }

2
go.mod
View File

@ -1,3 +1,5 @@
module github.com/libgit2/git2go/v30 module github.com/libgit2/git2go/v30
go 1.13 go 1.13
require golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de

7
go.sum Normal file
View File

@ -0,0 +1,7 @@
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de h1:ikNHVSjEfnvz6sxdSPCaPt572qowuyMDMJLLm3Db3ig=
golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=

View File

@ -2,6 +2,8 @@ package git
/* /*
#include <git2.h> #include <git2.h>
extern void _go_git_populate_commit_sign_cb(git_rebase_options *opts);
*/ */
import "C" import "C"
import ( import (
@ -69,6 +71,59 @@ func newRebaseOperationFromC(c *C.git_rebase_operation) *RebaseOperation {
return operation return operation
lhchavez commented 2020-08-14 19:04:33 -05:00 (Migrated from github.com)
Review

OK, second try:

		C.git_buf_clear(buf)
		if int(C.git_buf_put(buf, cstr, clen)) != 0 {
			return errors.New("could not set buffer")
		}

git_buf_clear() will remove whatever is in the buffer, and git_buf_put() grows the buffer AND adds the NULL byte at the end.

OK, second try: ```suggestion C.git_buf_clear(buf) if int(C.git_buf_put(buf, cstr, clen)) != 0 { return errors.New("could not set buffer") } ``` [`git_buf_clear()`](https://github.com/libgit2/libgit2/blob/c71321a099373753c22055921c8f538afed5ebb6/src/buffer.c#L152-L163) will remove whatever is in the buffer, and [`git_buf_put()`](https://github.com/libgit2/libgit2/blob/c71321a099373753c22055921c8f538afed5ebb6/src/buffer.c#L223-L238) grows the buffer AND adds the NULL byte at the end.
mbfr commented 2020-08-18 02:51:47 -05:00 (Migrated from github.com)
Review

It seems like those functions aren't defined in the 'buffer.h' header and are also not specified in the documentation, I tried manually adding the extern ... but got an undefined reference error. Are they intended only for internal use?

It seems like those functions aren't defined in the 'buffer.h' header and are also not specified in the [documentation](https://libgit2.org/libgit2/#HEAD/search/git_buf_), I tried manually adding the `extern ...` but got an undefined reference error. Are they intended only for internal use?
lhchavez commented 2020-08-18 08:47:27 -05:00 (Migrated from github.com)
Review

argh, yes u_u they are not marked GIT_EXTERN.

but why is it not possible to use

		if int(C.git_buf_set(buf, cstr, clen)) != 0 {
			return errors.New("could not set buffer")
		}

? git_buf_set resizes the buffer (plus the space for the NULL byte), and adds the trailing NULL byte. That's even independent of whether the original c-string had a NULL pointer or not. (This time around I did test this suggestion locally, so I'm sure this one does definitely work).

argh, yes u_u they are not marked `GIT_EXTERN`. but why is it not possible to use ```suggestion if int(C.git_buf_set(buf, cstr, clen)) != 0 { return errors.New("could not set buffer") } ``` ? [`git_buf_set`](https://github.com/libgit2/libgit2/blob/c71321a099373753c22055921c8f538afed5ebb6/src/buffer.c#L165-L184) resizes the buffer (plus the space for the NULL byte), and adds the trailing NULL byte. That's even independent of whether the original c-string had a NULL pointer or not. (This time around I did test this suggestion locally, so I'm sure this one does definitely work).
mbfr commented 2020-08-18 09:13:14 -05:00 (Migrated from github.com)
Review

I tried this before and although it works in CI (see: github actions on that commit), when running locally I get this error

___go_test_github_com_libgit2_git2go_v30: /build/libgit2/src/libgit2-1.0.1/src/rebase.c:1007: rebase_commit__create: Assertion `git_buf_contains_nul(&commit_signature)' failed.
I tried this before and although it works in CI (see: github actions on that commit), when running locally I get this error ``` ___go_test_github_com_libgit2_git2go_v30: /build/libgit2/src/libgit2-1.0.1/src/rebase.c:1007: rebase_commit__create: Assertion `git_buf_contains_nul(&commit_signature)' failed. ```
lhchavez commented 2020-08-18 10:13:34 -05:00 (Migrated from github.com)
Review

interesting, that means that we need to have at least one CI configuration where assertions are enabled.

okay, what about

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

proof of guarantee: 4149493443/src/cmd/cgo/out.go (L1709-L1716)

interesting, that means that we need to have at least one CI configuration where assertions are enabled. okay, what about ```suggestion // 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") } ``` proof of guarantee: https://github.com/golang/go/blob/4149493443f09c14d9f0fad7030704ed57149b55/src/cmd/cgo/out.go#L1709-L1716
mbfr commented 2020-08-18 10:26:33 -05:00 (Migrated from github.com)
Review

Fixed (and removed the old wrapper)

Fixed (and removed the old wrapper)
} }
//export commitSignCallback
func commitSignCallback(_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.SigningCallback == nil {
return C.GIT_PASSTHROUGH
}
commitContent := C.GoString(_commit_content)
signature, signatureField, err := opts.SigningCallback(commitContent)
if err != nil {
return C.int(-1)
}
fillBuf := func(bufData string, buf *C.git_buf) error {
clen := C.size_t(len(bufData))
cstr := unsafe.Pointer(C.CString(bufData))
// over-assign by a byte (see below)
if int(C.git_buf_grow(buf, clen+1)) != 0 {
return errors.New("could not grow buffer")
}
if int(C.git_buf_set(buf, cstr, clen)) != 0 {
lhchavez commented 2020-08-14 08:58:38 -05:00 (Migrated from github.com)
Review

in order to support callback authors being able to set a specific libgit2 error, we could do something like:

if gitError, ok := err.(*GitError); ok {
    return C.int(gitError.Code)
}
return C.int(-1)
in order to support callback authors being able to set a specific libgit2 error, we could do something like: ```go if gitError, ok := err.(*GitError); ok { return C.int(gitError.Code) } return C.int(-1) ```
mbfr commented 2020-08-14 09:53:17 -05:00 (Migrated from github.com)
Review

Good idea, added

Good idea, added
return errors.New("could not set buffer")
}
// git_buf_set sets 'size' to the 'size' of the buffer to 'clen', but we want it to be clen+1 because after returning it asserts that the buffer ends with a null byte, which Go strings don't
// This avoids having to convert the string to a []byte, then adding a null byte, then doing another copy
lhchavez commented 2020-08-14 08:59:06 -05:00 (Migrated from github.com)
Review

this needs a C.free(...).

this needs a `C.free(...)`.
mbfr commented 2020-08-14 09:53:21 -05:00 (Migrated from github.com)
Review

Fixed

Fixed
buf.size += 1
return nil
}
if signatureField != "" {
err := fillBuf(signatureField, _signature_field)
if err != nil {
return C.int(-1)
}
}
err = fillBuf(signature, _signature)
if err != nil {
return C.int(-1)
}
return C.GIT_OK
}
// 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 {
Version uint Version uint
@ -77,6 +132,7 @@ type RebaseOptions struct {
RewriteNotesRef string RewriteNotesRef string
MergeOptions MergeOptions MergeOptions MergeOptions
CheckoutOptions CheckoutOpts CheckoutOptions CheckoutOpts
SigningCallback CommitSigningCb
} }
// DefaultRebaseOptions returns a RebaseOptions with default values. // DefaultRebaseOptions returns a RebaseOptions with default values.
@ -101,6 +157,7 @@ func rebaseOptionsFromC(opts *C.git_rebase_options) RebaseOptions {
RewriteNotesRef: C.GoString(opts.rewrite_notes_ref), RewriteNotesRef: C.GoString(opts.rewrite_notes_ref),
MergeOptions: mergeOptionsFromC(&opts.merge_options), MergeOptions: mergeOptionsFromC(&opts.merge_options),
CheckoutOptions: checkoutOptionsFromC(&opts.checkout_options), CheckoutOptions: checkoutOptionsFromC(&opts.checkout_options),
// TODO: is it possible to take the callback from C and have it be meaningful in Go? Would we ever want to do it?
} }
} }
@ -108,7 +165,7 @@ func (ro *RebaseOptions) toC() *C.git_rebase_options {
if ro == nil { if ro == nil {
return nil return nil
} }
return &C.git_rebase_options{ opts := &C.git_rebase_options{
version: C.uint(ro.Version), version: C.uint(ro.Version),
quiet: C.int(ro.Quiet), quiet: C.int(ro.Quiet),
inmemory: C.int(ro.InMemory), inmemory: C.int(ro.InMemory),
@ -116,6 +173,13 @@ func (ro *RebaseOptions) toC() *C.git_rebase_options {
merge_options: *ro.MergeOptions.toC(), merge_options: *ro.MergeOptions.toC(),
checkout_options: *ro.CheckoutOptions.toC(), checkout_options: *ro.CheckoutOptions.toC(),
} }
if ro.SigningCallback != nil {
C._go_git_populate_commit_sign_cb(opts)
opts.payload = pointerHandles.Track(*ro)
}
return opts
} }
func mapEmptyStringToNull(ref string) *C.char { func mapEmptyStringToNull(ref string) *C.char {

View File

@ -1,10 +1,15 @@
package git package git
import ( import (
"bytes"
"errors" "errors"
"strconv" "strconv"
"strings"
"testing" "testing"
"time" "time"
"golang.org/x/crypto/openpgp"
"golang.org/x/crypto/openpgp/packet"
) )
// Tests // Tests
@ -48,7 +53,7 @@ func TestRebaseAbort(t *testing.T) {
assertStringList(t, expectedHistory, actualHistory) assertStringList(t, expectedHistory, actualHistory)
// Rebase onto master // Rebase onto master
rebase, err := performRebaseOnto(repo, "master") rebase, err := performRebaseOnto(repo, "master", nil)
checkFatal(t, err) checkFatal(t, err)
defer rebase.Free() defer rebase.Free()
@ -104,7 +109,7 @@ func TestRebaseNoConflicts(t *testing.T) {
} }
// Rebase onto master // Rebase onto master
rebase, err := performRebaseOnto(repo, "master") rebase, err := performRebaseOnto(repo, "master", nil)
checkFatal(t, err) checkFatal(t, err)
defer rebase.Free() defer rebase.Free()
@ -130,11 +135,131 @@ func TestRebaseNoConflicts(t *testing.T) {
actualHistory, err := commitMsgsList(repo) actualHistory, err := commitMsgsList(repo)
checkFatal(t, err) checkFatal(t, err)
assertStringList(t, expectedHistory, actualHistory) assertStringList(t, expectedHistory, actualHistory)
}
func TestRebaseGpgSigned(t *testing.T) {
// TEST DATA
entity, err := openpgp.NewEntity("Namey mcnameface", "test comment", "test@example.com", nil)
checkFatal(t, err)
opts, err := DefaultRebaseOptions()
checkFatal(t, err)
signCommitContent := func(commitContent string) (string, string, error) {
cipherText := new(bytes.Buffer)
err := openpgp.ArmoredDetachSignText(cipherText, entity, strings.NewReader(commitContent), &packet.Config{})
if err != nil {
return "", "", errors.New("error signing payload")
}
return cipherText.String(), "", nil
}
opts.SigningCallback = signCommitContent
commitOpts := commitOpts{
CommitSigningCb: signCommitContent,
}
// Inputs
branchName := "emile"
masterCommit := "something"
emileCommits := []string{
"fou",
"barre",
"ouich",
}
// Outputs
expectedHistory := []string{
"Test rebase, Baby! " + emileCommits[2],
"Test rebase, Baby! " + emileCommits[1],
"Test rebase, Baby! " + emileCommits[0],
"Test rebase, Baby! " + masterCommit,
"This is a commit\n",
}
// TEST
repo := createTestRepo(t)
defer cleanupTestRepo(t, repo)
seedTestRepoOpt(t, repo, commitOpts)
// Try to open existing rebase
_, err = repo.OpenRebase(nil)
if err == nil {
t.Fatal("Did not expect to find a rebase in progress")
}
// Setup a repo with 2 branches and a different tree
err = setupRepoForRebaseOpts(repo, masterCommit, branchName, commitOpts)
checkFatal(t, err)
// Create several commits in emile
for _, commit := range emileCommits {
_, err = commitSomethingOpts(repo, commit, commit, commitOpts)
checkFatal(t, err)
}
// Rebase onto master
rebase, err := performRebaseOnto(repo, "master", &opts)
checkFatal(t, err)
defer rebase.Free()
// Finish the rebase properly
err = rebase.Finish()
checkFatal(t, err)
// Check history is in correct order
actualHistory, err := commitMsgsList(repo)
checkFatal(t, err)
assertStringList(t, expectedHistory, actualHistory)
checkAllCommitsSigned(t, entity, repo)
}
func checkAllCommitsSigned(t *testing.T, entity *openpgp.Entity, repo *Repository) {
head, err := headCommit(repo)
checkFatal(t, err)
defer head.Free()
parent := head.Parent(0)
defer parent.Free()
err = checkCommitSigned(t, entity, parent)
checkFatal(t, err)
for parent.ParentCount() != 0 {
parent = parent.Parent(0)
defer parent.Free()
err = checkCommitSigned(t, entity, parent)
checkFatal(t, err)
}
}
func checkCommitSigned(t *testing.T, entity *openpgp.Entity, commit *Commit) error {
signature, signedData, err := commit.ExtractSignature()
if err != nil {
t.Logf("No signature on commit\n%s", commit.RawHeader()+"\n"+commit.RawMessage())
return err
}
_, err = openpgp.CheckArmoredDetachedSignature(openpgp.EntityList{entity}, strings.NewReader(signedData), bytes.NewBufferString(signature))
if err != nil {
t.Logf("Commit is not signed correctly\n%s", commit.RawHeader()+"\n"+commit.RawMessage())
return err
}
return nil
} }
// Utils // Utils
func setupRepoForRebase(repo *Repository, masterCommit, branchName string) error { func setupRepoForRebase(repo *Repository, masterCommit, branchName string) error {
return setupRepoForRebaseOpts(repo, masterCommit, branchName, commitOpts{})
}
func setupRepoForRebaseOpts(repo *Repository, masterCommit, branchName string, opts commitOpts) error {
// Create a new branch from master // Create a new branch from master
err := createBranch(repo, branchName) err := createBranch(repo, branchName)
if err != nil { if err != nil {
@ -142,7 +267,7 @@ func setupRepoForRebase(repo *Repository, masterCommit, branchName string) error
} }
// Create a commit in master // Create a commit in master
_, err = commitSomething(repo, masterCommit, masterCommit) _, err = commitSomethingOpts(repo, masterCommit, masterCommit, opts)
if err != nil { if err != nil {
return err return err
} }
@ -161,7 +286,7 @@ func setupRepoForRebase(repo *Repository, masterCommit, branchName string) error
return nil return nil
} }
func performRebaseOnto(repo *Repository, branch string) (*Rebase, error) { func performRebaseOnto(repo *Repository, branch string, opts *RebaseOptions) (*Rebase, error) {
master, err := repo.LookupBranch(branch, BranchLocal) master, err := repo.LookupBranch(branch, BranchLocal)
if err != nil { if err != nil {
return nil, err return nil, err
@ -175,7 +300,7 @@ func performRebaseOnto(repo *Repository, branch string) (*Rebase, error) {
defer onto.Free() defer onto.Free()
lhchavez commented 2020-08-14 09:19:21 -05:00 (Migrated from github.com)
Review

maybe add a

t.Helper()

as the first line of this function: https://golang.org/pkg/testing/#T.Helper

maybe add a ```go t.Helper() ``` as the first line of this function: https://golang.org/pkg/testing/#T.Helper
mbfr commented 2020-08-14 10:25:10 -05:00 (Migrated from github.com)
Review

Added, should make logs a bit nicer

Added, should make logs a bit nicer
// Init rebase // Init rebase
rebase, err := repo.InitRebase(nil, nil, onto, nil) rebase, err := repo.InitRebase(nil, nil, onto, opts)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -277,6 +402,10 @@ func headTree(repo *Repository) (*Tree, error) {
} }
func commitSomething(repo *Repository, something, content string) (*Oid, error) { func commitSomething(repo *Repository, something, content string) (*Oid, error) {
return commitSomethingOpts(repo, something, content, commitOpts{})
}
func commitSomethingOpts(repo *Repository, something, content string, commitOpts commitOpts) (*Oid, error) {
headCommit, err := headCommit(repo) headCommit, err := headCommit(repo)
if err != nil { if err != nil {
return nil, err return nil, err
@ -315,14 +444,40 @@ func commitSomething(repo *Repository, something, content string) (*Oid, error)
} }
defer newTree.Free() defer newTree.Free()
if err != nil {
return nil, err
}
commit, err := repo.CreateCommit("HEAD", signature(), signature(), "Test rebase, Baby! "+something, newTree, headCommit) commit, err := repo.CreateCommit("HEAD", signature(), signature(), "Test rebase, Baby! "+something, newTree, headCommit)
if err != nil { if err != nil {
return nil, err return nil, err
} }
if commitOpts.CommitSigningCb != nil {
commit, err := repo.LookupCommit(commit)
if err != nil {
return nil, err
}
oid, err := commit.WithSignatureUsing(commitOpts.CommitSigningCb)
if err != nil {
return nil, err
}
newCommit, err := repo.LookupCommit(oid)
if err != nil {
return nil, err
}
head, err := repo.Head()
if err != nil {
return nil, err
}
_, err = repo.References.Create(
head.Name(),
newCommit.Id(),
true,
"repoint to signed commit",
)
if err != nil {
return nil, err
}
}
opts := &CheckoutOpts{ opts := &CheckoutOpts{
Strategy: CheckoutRemoveUntracked | CheckoutForce, Strategy: CheckoutRemoveUntracked | CheckoutForce,
} }

View File

@ -6,6 +6,11 @@
typedef int (*gogit_submodule_cbk)(git_submodule *sm, const char *name, void *payload); typedef int (*gogit_submodule_cbk)(git_submodule *sm, const char *name, void *payload);
void _go_git_populate_commit_sign_cb(git_rebase_options *opts)
{
opts->signing_cb = (git_commit_signing_cb)commitSignCallback;
}
void _go_git_populate_remote_cb(git_clone_options *opts) void _go_git_populate_remote_cb(git_clone_options *opts)
{ {
opts->remote_cb = (git_remote_create_cb)remoteCreateCallback; opts->remote_cb = (git_remote_create_cb)remoteCreateCallback;