aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAditya Sirish <aditya@saky.in>2023-09-27 11:54:31 -0400
committerAditya Sirish <aditya@saky.in>2023-09-27 16:28:43 -0400
commitcbfd0c5005523b131449c6fdd7fa0c0bc84012a9 (patch)
tree0559b309115cec0e6536661dca8c7ae938cd6357
parent809f9df1b76258a311a20c76d346e86aca0a08f8 (diff)
downloadgo-git-cbfd0c5005523b131449c6fdd7fa0c0bc84012a9.tar.gz
plumbing/object: Support mergetag in merge commits
When a merge commit is created from merging a signed tag, the tag object is embedded in the commit object. This commit adds support for this tag data when encoding and decoding a commit object. Signed-off-by: Aditya Sirish <aditya@saky.in>
-rw-r--r--plumbing/object/commit.go38
-rw-r--r--plumbing/object/commit_test.go45
2 files changed, 83 insertions, 0 deletions
diff --git a/plumbing/object/commit.go b/plumbing/object/commit.go
index 8a0f35c..508e93c 100644
--- a/plumbing/object/commit.go
+++ b/plumbing/object/commit.go
@@ -20,6 +20,11 @@ const (
beginpgp string = "-----BEGIN PGP SIGNATURE-----"
endpgp string = "-----END PGP SIGNATURE-----"
headerpgp string = "gpgsig"
+
+ // https://github.com/git/git/blob/bcb6cae2966cc407ca1afc77413b3ef11103c175/Documentation/gitformat-signature.txt#L153
+ // When a merge commit is created from a signed tag, the tag is embedded in
+ // the commit with the "mergetag" header.
+ headermergetag string = "mergetag"
)
// Hash represents the hash of an object
@@ -38,6 +43,9 @@ type Commit struct {
// Committer is the one performing the commit, might be different from
// Author.
Committer Signature
+ // MergeTag is the embedded tag object when a merge commit is created by
+ // merging a signed tag.
+ MergeTag string
// PGPSignature is the PGP signature of the commit.
PGPSignature string
// Message is the commit message, contains arbitrary text.
@@ -184,6 +192,7 @@ func (c *Commit) Decode(o plumbing.EncodedObject) (err error) {
defer sync.PutBufioReader(r)
var message bool
+ var mergetag bool
var pgpsig bool
var msgbuf bytes.Buffer
for {
@@ -192,6 +201,16 @@ func (c *Commit) Decode(o plumbing.EncodedObject) (err error) {
return err
}
+ if mergetag {
+ if len(line) > 0 && line[0] == ' ' {
+ line = bytes.TrimLeft(line, " ")
+ c.MergeTag += string(line)
+ continue
+ } else {
+ mergetag = false
+ }
+ }
+
if pgpsig {
if len(line) > 0 && line[0] == ' ' {
line = bytes.TrimLeft(line, " ")
@@ -225,6 +244,9 @@ func (c *Commit) Decode(o plumbing.EncodedObject) (err error) {
c.Author.Decode(data)
case "committer":
c.Committer.Decode(data)
+ case headermergetag:
+ c.MergeTag += string(data) + "\n"
+ mergetag = true
case headerpgp:
c.PGPSignature += string(data) + "\n"
pgpsig = true
@@ -286,6 +308,22 @@ func (c *Commit) encode(o plumbing.EncodedObject, includeSig bool) (err error) {
return err
}
+ if c.MergeTag != "" {
+ if _, err = fmt.Fprint(w, "\n"+headermergetag+" "); err != nil {
+ return err
+ }
+
+ // Split tag information lines and re-write with a left padding and
+ // newline. Use join for this so it's clear that a newline should not be
+ // added after this section. The newline will be added either as part of
+ // the PGP signature or the commit message.
+ mergetag := strings.TrimSuffix(c.MergeTag, "\n")
+ lines := strings.Split(mergetag, "\n")
+ if _, err = fmt.Fprint(w, strings.Join(lines, "\n ")); err != nil {
+ return err
+ }
+ }
+
if c.PGPSignature != "" && includeSig {
if _, err = fmt.Fprint(w, "\n"+headerpgp+" "); err != nil {
return err
diff --git a/plumbing/object/commit_test.go b/plumbing/object/commit_test.go
index 4b0f6b4..f1a3447 100644
--- a/plumbing/object/commit_test.go
+++ b/plumbing/object/commit_test.go
@@ -3,6 +3,7 @@ package object
import (
"bytes"
"context"
+ "fmt"
"io"
"strings"
"time"
@@ -197,6 +198,27 @@ func (s *SuiteCommit) TestPatchContext_ToNil(c *C) {
}
func (s *SuiteCommit) TestCommitEncodeDecodeIdempotent(c *C) {
+ pgpsignature := `-----BEGIN PGP SIGNATURE-----
+
+iQEcBAABAgAGBQJTZbQlAAoJEF0+sviABDDrZbQH/09PfE51KPVPlanr6q1v4/Ut
+LQxfojUWiLQdg2ESJItkcuweYg+kc3HCyFejeDIBw9dpXt00rY26p05qrpnG+85b
+hM1/PswpPLuBSr+oCIDj5GMC2r2iEKsfv2fJbNW8iWAXVLoWZRF8B0MfqX/YTMbm
+ecorc4iXzQu7tupRihslbNkfvfciMnSDeSvzCpWAHl7h8Wj6hhqePmLm9lAYqnKp
+8S5B/1SSQuEAjRZgI4IexpZoeKGVDptPHxLLS38fozsyi0QyDyzEgJxcJQVMXxVi
+RUysgqjcpT8+iQM1PblGfHR4XAhuOqN5Fx06PSaFZhqvWFezJ28/CLyX5q+oIVk=
+=EFTF
+-----END PGP SIGNATURE-----
+`
+
+ tag := fmt.Sprintf(`object f000000000000000000000000000000000000000
+type commit
+tag change
+tagger Foo <foo@example.local> 1695827841 -0400
+
+change
+%s
+`, pgpsignature)
+
ts, err := time.Parse(time.RFC3339, "2006-01-02T15:04:05-07:00")
c.Assert(err, IsNil)
commits := []*Commit{
@@ -219,6 +241,29 @@ func (s *SuiteCommit) TestCommitEncodeDecodeIdempotent(c *C) {
plumbing.NewHash("f000000000000000000000000000000000000007"),
},
},
+ {
+ Author: Signature{Name: "Foo", Email: "foo@example.local", When: ts},
+ Committer: Signature{Name: "Bar", Email: "bar@example.local", When: ts},
+ Message: "Testing mergetag\n\nHere, commit is not signed",
+ TreeHash: plumbing.NewHash("f000000000000000000000000000000000000001"),
+ ParentHashes: []plumbing.Hash{
+ plumbing.NewHash("f000000000000000000000000000000000000002"),
+ plumbing.NewHash("f000000000000000000000000000000000000003"),
+ },
+ MergeTag: tag,
+ },
+ {
+ Author: Signature{Name: "Foo", Email: "foo@example.local", When: ts},
+ Committer: Signature{Name: "Bar", Email: "bar@example.local", When: ts},
+ Message: "Testing mergetag\n\nHere, commit is also signed",
+ TreeHash: plumbing.NewHash("f000000000000000000000000000000000000001"),
+ ParentHashes: []plumbing.Hash{
+ plumbing.NewHash("f000000000000000000000000000000000000002"),
+ plumbing.NewHash("f000000000000000000000000000000000000003"),
+ },
+ MergeTag: tag,
+ PGPSignature: pgpsignature,
+ },
}
for _, commit := range commits {
obj := &plumbing.MemoryObject{}