Skip to content

Commit ad9a3a5

Browse files
authored
Merge pull request #1633 from baloo/baloo/release-5.x/jj-signed-commits
plumbing: support commits extra headers, support jujutsu signed commit [5.x]
2 parents c12263d + f2c3467 commit ad9a3a5

2 files changed

Lines changed: 180 additions & 0 deletions

File tree

plumbing/object/commit.go

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,10 +62,55 @@ type Commit struct {
6262
ParentHashes []plumbing.Hash
6363
// Encoding is the encoding of the commit.
6464
Encoding MessageEncoding
65+
// List of extra headers of the commit
66+
ExtraHeaders []ExtraHeader
6567

6668
s storer.EncodedObjectStorer
6769
}
6870

71+
// ExtraHeader holds any non-standard header
72+
type ExtraHeader struct {
73+
// Header name
74+
Key string
75+
// Value of the header
76+
Value string
77+
}
78+
79+
// Implement fmt.Formatter for ExtraHeader
80+
func (h ExtraHeader) Format(f fmt.State, verb rune) {
81+
switch verb {
82+
case 'v':
83+
fmt.Fprintf(f, "ExtraHeader{Key: %v, Value: %v}", h.Key, h.Value)
84+
default:
85+
fmt.Fprintf(f, "%s", h.Key)
86+
if len(h.Value) > 0 {
87+
fmt.Fprint(f, " ")
88+
// Content may be spread on multiple lines, if so we need to
89+
// prepend each of them with a space for "continuation".
90+
value := strings.TrimSuffix(h.Value, "\n")
91+
lines := strings.Split(value, "\n")
92+
fmt.Fprint(f, strings.Join(lines, "\n "))
93+
}
94+
}
95+
}
96+
97+
// Parse an extra header and indicate whether it may be continue on the next line
98+
func parseExtraHeader(line []byte) (ExtraHeader, bool) {
99+
split := bytes.SplitN(line, []byte{' '}, 2)
100+
101+
out := ExtraHeader {
102+
Key: string(bytes.TrimRight(split[0], "\n")),
103+
Value: "",
104+
}
105+
106+
if len(split) == 2 {
107+
out.Value += string(split[1])
108+
return out, true
109+
} else {
110+
return out, false
111+
}
112+
}
113+
69114
// GetCommit gets a commit from an object storer and decodes it.
70115
func GetCommit(s storer.EncodedObjectStorer, h plumbing.Hash) (*Commit, error) {
71116
o, err := s.EncodedObject(plumbing.CommitObject, h)
@@ -204,6 +249,7 @@ func (c *Commit) Decode(o plumbing.EncodedObject) (err error) {
204249
var mergetag bool
205250
var pgpsig bool
206251
var msgbuf bytes.Buffer
252+
var extraheader *ExtraHeader = nil
207253
for {
208254
line, err := r.ReadBytes('\n')
209255
if err != nil && err != io.EOF {
@@ -230,7 +276,19 @@ func (c *Commit) Decode(o plumbing.EncodedObject) (err error) {
230276
}
231277
}
232278

279+
if extraheader != nil {
280+
if len(line) > 0 && line[0] == ' ' {
281+
extraheader.Value += string(line[1:])
282+
continue
283+
} else {
284+
extraheader.Value = strings.TrimRight(extraheader.Value, "\n")
285+
c.ExtraHeaders = append(c.ExtraHeaders, *extraheader)
286+
extraheader = nil
287+
}
288+
}
289+
233290
if !message {
291+
original_line := line
234292
line = bytes.TrimSpace(line)
235293
if len(line) == 0 {
236294
message = true
@@ -261,6 +319,13 @@ func (c *Commit) Decode(o plumbing.EncodedObject) (err error) {
261319
case headerpgp:
262320
c.PGPSignature += string(data) + "\n"
263321
pgpsig = true
322+
default:
323+
h, maybecontinued := parseExtraHeader(original_line)
324+
if maybecontinued {
325+
extraheader = &h
326+
} else {
327+
c.ExtraHeaders = append(c.ExtraHeaders, h)
328+
}
264329
}
265330
} else {
266331
msgbuf.Write(line)
@@ -341,6 +406,13 @@ func (c *Commit) encode(o plumbing.EncodedObject, includeSig bool) (err error) {
341406
}
342407
}
343408

409+
for _, header := range c.ExtraHeaders {
410+
411+
if _, err = fmt.Fprintf(w, "\n%s", header); err != nil {
412+
return err
413+
}
414+
}
415+
344416
if c.PGPSignature != "" && includeSig {
345417
if _, err = fmt.Fprint(w, "\n"+headerpgp+" "); err != nil {
346418
return err

plumbing/object/commit_test.go

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -553,6 +553,114 @@ func (s *SuiteCommit) TestEncodeWithoutSignature(c *C) {
553553
"Merge branch 'master' of github.com:tyba/git-fixture\n")
554554
}
555555

556+
func (s *SuiteCommit) TestEncodeWithoutSignatureJujutsu(c *C) {
557+
object := &plumbing.MemoryObject{}
558+
object.SetType(plumbing.CommitObject)
559+
object.Write([]byte(`tree 4b825dc642cb6eb9a060e54bf8d69288fbee4904
560+
author John Doe <john.doe@example.com> 1755280730 -0700
561+
committer John Doe <john.doe@example.com> 1755280730 -0700
562+
change-id wxmuynokkzxmuwxwvnnpnptoyuypknwv
563+
gpgsig -----BEGIN PGP SIGNATURE-----
564+
565+
iHUEABMIAB0WIQSZpnSpGKbQbDaLe5iiNQl48cTY5gUCaJ91XQAKCRCiNQl48cTY
566+
5vCYAP9Sf1yV9oUviRIxEA+4rsGIx0hI6kqFajJ/3TtBjyCTggD+PFnKOxdXeFL2
567+
GLwcCzFIsmQmkLxuLypsg+vueDSLpsM=
568+
=VucY
569+
-----END PGP SIGNATURE-----
570+
571+
initial commit
572+
573+
Change-Id: I6a6a696432d51cbff02d53234ccaca6b151afc34
574+
`))
575+
576+
commit, err := DecodeCommit(s.Storer, object)
577+
c.Assert(err, IsNil)
578+
579+
// Similar to TestString since no signature
580+
encoded := &plumbing.MemoryObject{}
581+
err = commit.EncodeWithoutSignature(encoded)
582+
er, err := encoded.Reader()
583+
c.Assert(err, IsNil)
584+
payload, err := io.ReadAll(er)
585+
c.Assert(err, IsNil)
586+
587+
c.Assert(string(payload), Equals, `tree 4b825dc642cb6eb9a060e54bf8d69288fbee4904
588+
author John Doe <john.doe@example.com> 1755280730 -0700
589+
committer John Doe <john.doe@example.com> 1755280730 -0700
590+
change-id wxmuynokkzxmuwxwvnnpnptoyuypknwv
591+
592+
initial commit
593+
594+
Change-Id: I6a6a696432d51cbff02d53234ccaca6b151afc34
595+
`)
596+
}
597+
598+
func (s *SuiteCommit) TestEncodeExtraHeaders(c *C) {
599+
object := &plumbing.MemoryObject{}
600+
object.SetType(plumbing.CommitObject)
601+
object.Write([]byte(`tree 4b825dc642cb6eb9a060e54bf8d69288fbee4904
602+
author John Doe <john.doe@example.com> 1755280730 -0700
603+
committer John Doe <john.doe@example.com> 1755280730 -0700
604+
continuedheader to be
605+
continued
606+
continuedheader to be
607+
continued
608+
on
609+
more than
610+
a single line
611+
simpleflag
612+
value no key
613+
614+
initial commit
615+
`))
616+
617+
commit, err := DecodeCommit(s.Storer, object)
618+
c.Assert(err, IsNil)
619+
620+
c.Assert(commit.ExtraHeaders, DeepEquals, []ExtraHeader{
621+
ExtraHeader {
622+
Key: "continuedheader",
623+
Value: "to be\ncontinued",
624+
},
625+
ExtraHeader {
626+
Key: "continuedheader",
627+
Value: "to be\ncontinued\non\nmore than\na single line",
628+
},
629+
ExtraHeader {
630+
Key: "simpleflag",
631+
Value: "",
632+
},
633+
ExtraHeader {
634+
Key: "",
635+
Value: "value no key",
636+
},
637+
})
638+
639+
// Similar to TestString since no signature
640+
encoded := &plumbing.MemoryObject{}
641+
err = commit.EncodeWithoutSignature(encoded)
642+
er, err := encoded.Reader()
643+
c.Assert(err, IsNil)
644+
payload, err := io.ReadAll(er)
645+
c.Assert(err, IsNil)
646+
647+
c.Assert(string(payload), Equals, `tree 4b825dc642cb6eb9a060e54bf8d69288fbee4904
648+
author John Doe <john.doe@example.com> 1755280730 -0700
649+
committer John Doe <john.doe@example.com> 1755280730 -0700
650+
continuedheader to be
651+
continued
652+
continuedheader to be
653+
continued
654+
on
655+
more than
656+
a single line
657+
simpleflag
658+
value no key
659+
660+
initial commit
661+
`)
662+
}
663+
556664
func (s *SuiteCommit) TestLess(c *C) {
557665
when1 := time.Now()
558666
when2 := when1.Add(time.Hour)

0 commit comments

Comments
 (0)