Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions options.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package git

import (
"crypto"
"errors"
"fmt"
"regexp"
Expand Down Expand Up @@ -507,6 +508,10 @@ type CommitOptions struct {
// commit will not be signed. The private key must be present and already
// decrypted.
SignKey *openpgp.Entity
// Signer denotes a cryptographic signer to sign the commit with.
// A nil value here means the commit will not be signed.
// Takes precedence over SignKey.
Signer crypto.Signer
// Amend will create a new commit object and replace the commit that HEAD currently
// points to. Cannot be used with All nor Parents.
Amend bool
Expand Down
53 changes: 43 additions & 10 deletions worktree_commit.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@ package git

import (
"bytes"
"crypto"
"crypto/rand"
"errors"
"io"
"path"
"sort"
"strings"
Expand All @@ -14,6 +17,7 @@ import (
"github.com/go-git/go-git/v5/storage"

"github.com/ProtonMail/go-crypto/openpgp"
"github.com/ProtonMail/go-crypto/openpgp/packet"
"github.com/go-git/go-billy/v5"
)

Expand Down Expand Up @@ -125,12 +129,17 @@ func (w *Worktree) buildCommitObject(msg string, opts *CommitOptions, tree plumb
ParentHashes: opts.Parents,
}

if opts.SignKey != nil {
sig, err := w.buildCommitSignature(commit, opts.SignKey)
// Convert SignKey into a Signer if set. Existing Signer should take priority.
signer := opts.Signer
if signer == nil && opts.SignKey != nil {
signer = &gpgSigner{key: opts.SignKey}
}
if signer != nil {
sig, err := w.buildCommitSignature(commit, signer)
if err != nil {
return plumbing.ZeroHash, err
}
commit.PGPSignature = sig
commit.PGPSignature = string(sig)
}

obj := w.r.Storer.NewEncodedObject()
Expand All @@ -140,20 +149,44 @@ func (w *Worktree) buildCommitObject(msg string, opts *CommitOptions, tree plumb
return w.r.Storer.SetEncodedObject(obj)
}

func (w *Worktree) buildCommitSignature(commit *object.Commit, signKey *openpgp.Entity) (string, error) {
type gpgSigner struct {
key *openpgp.Entity
}

func (s *gpgSigner) Public() crypto.PublicKey {
return s.key.PrimaryKey
}

func (s *gpgSigner) Sign(rand io.Reader, digest []byte, opts crypto.SignerOpts) ([]byte, error) {
var cfg *packet.Config
if opts != nil {
cfg = &packet.Config{
DefaultHash: opts.HashFunc(),
}
}

var b bytes.Buffer
if err := openpgp.ArmoredDetachSign(&b, s.key, bytes.NewReader(digest), cfg); err != nil {
return nil, err
}
return b.Bytes(), nil
}

func (w *Worktree) buildCommitSignature(commit *object.Commit, signer crypto.Signer) ([]byte, error) {
encoded := &plumbing.MemoryObject{}
if err := commit.Encode(encoded); err != nil {
return "", err
return nil, err
}
r, err := encoded.Reader()
if err != nil {
return "", err
return nil, err
}
var b bytes.Buffer
if err := openpgp.ArmoredDetachSign(&b, signKey, r, nil); err != nil {
return "", err
b, err := io.ReadAll(r)
if err != nil {
return nil, err
}
return b.String(), nil

return signer.Sign(rand.Reader, b, nil)
}

// buildTreeHelper converts a given index.Index file into multiple git objects
Expand Down
1 change: 0 additions & 1 deletion worktree_commit_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,6 @@ func (s *WorktreeSuite) TestCommitAmend(c *C) {
_, err = w.Commit("foo\n", &CommitOptions{Author: defaultSignature()})
c.Assert(err, IsNil)


amendedHash, err := w.Commit("bar\n", &CommitOptions{Amend: true})
c.Assert(err, IsNil)

Expand Down