Skip to content

Commit b35104c

Browse files
authored
Merge pull request #10 from git-lfs/ttaylorr/multi-line-headers
commit.go: support multi-line header continuations
2 parents f9ae4a7 + 69666fc commit b35104c

File tree

3 files changed

+133
-6
lines changed

3 files changed

+133
-6
lines changed

commit.go

Lines changed: 31 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,18 @@ func (c *Commit) Decode(from io.Reader, size int64) (n int, err error) {
105105
continue
106106
}
107107

108-
if fields := strings.Fields(text); len(fields) > 0 && !finishedHeaders {
108+
if fields := strings.Fields(text); !finishedHeaders {
109+
if len(fields) == 0 {
110+
// Executing in this block means that we got a
111+
// whitespace-only line, while parsing a header.
112+
//
113+
// Append it to the last-parsed header, and
114+
// continue.
115+
c.ExtraHeaders[len(c.ExtraHeaders)-1].V +=
116+
fmt.Sprintf("\n%s", text[1:])
117+
continue
118+
}
119+
109120
switch fields[0] {
110121
case "tree":
111122
id, err := hex.DecodeString(fields[1])
@@ -132,10 +143,23 @@ func (c *Commit) Decode(from io.Reader, size int64) (n int, err error) {
132143
c.Committer = ""
133144
}
134145
default:
135-
c.ExtraHeaders = append(c.ExtraHeaders, &ExtraHeader{
136-
K: fields[0],
137-
V: strings.Join(fields[1:], " "),
138-
})
146+
if strings.HasPrefix(s.Text(), " ") {
147+
idx := len(c.ExtraHeaders) - 1
148+
hdr := c.ExtraHeaders[idx]
149+
150+
// Append the line of text (removing the
151+
// leading space) to the last header
152+
// that we parsed, adding a newline
153+
// between the two.
154+
hdr.V = strings.Join(append(
155+
[]string{hdr.V}, s.Text()[1:],
156+
), "\n")
157+
} else {
158+
c.ExtraHeaders = append(c.ExtraHeaders, &ExtraHeader{
159+
K: fields[0],
160+
V: strings.Join(fields[1:], " "),
161+
})
162+
}
139163
}
140164
} else {
141165
messageParts = append(messageParts, s.Text())
@@ -177,7 +201,8 @@ func (c *Commit) Encode(to io.Writer) (n int, err error) {
177201
n = n + n2
178202

179203
for _, hdr := range c.ExtraHeaders {
180-
n3, err := fmt.Fprintf(to, "%s %s\n", hdr.K, hdr.V)
204+
n3, err := fmt.Fprintf(to, "%s %s\n",
205+
hdr.K, strings.Replace(hdr.V, "\n", "\n ", -1))
181206
if err != nil {
182207
return n, err
183208
}

commit_test.go

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"time"
1111

1212
"github.com/stretchr/testify/assert"
13+
"github.com/stretchr/testify/require"
1314
)
1415

1516
func TestCommitReturnsCorrectObjectType(t *testing.T) {
@@ -20,6 +21,8 @@ func TestCommitEncoding(t *testing.T) {
2021
author := &Signature{Name: "John Doe", Email: "john@example.com", When: time.Now()}
2122
committer := &Signature{Name: "Jane Doe", Email: "jane@example.com", When: time.Now()}
2223

24+
sig := "-----BEGIN PGP SIGNATURE-----\n<signature>\n-----END PGP SIGNATURE-----"
25+
2326
c := &Commit{
2427
Author: author.String(),
2528
Committer: committer.String(),
@@ -29,6 +32,7 @@ func TestCommitEncoding(t *testing.T) {
2932
TreeID: []byte("cccccccccccccccccccc"),
3033
ExtraHeaders: []*ExtraHeader{
3134
{"foo", "bar"},
35+
{"gpgsig", sig},
3236
},
3337
Message: "initial commit",
3438
}
@@ -44,6 +48,9 @@ func TestCommitEncoding(t *testing.T) {
4448
assertLine(t, buf, "author %s", author.String())
4549
assertLine(t, buf, "committer %s", committer.String())
4650
assertLine(t, buf, "foo bar")
51+
assertLine(t, buf, "gpgsig -----BEGIN PGP SIGNATURE-----")
52+
assertLine(t, buf, " <signature>")
53+
assertLine(t, buf, " -----END PGP SIGNATURE-----")
4754
assertLine(t, buf, "")
4855
assertLine(t, buf, "initial commit")
4956

@@ -164,6 +171,41 @@ func TestCommitDecodingWithWhitespace(t *testing.T) {
164171
assert.Equal(t, "tree <- initial commit", commit.Message)
165172
}
166173

174+
func TestCommitDecodingMultilineHeader(t *testing.T) {
175+
author := &Signature{Name: "", Email: "john@example.com", When: time.Now()}
176+
committer := &Signature{Name: "", Email: "jane@example.com", When: time.Now()}
177+
178+
treeId := []byte("cccccccccccccccccccc")
179+
180+
from := new(bytes.Buffer)
181+
182+
fmt.Fprintf(from, "author %s\n", author)
183+
fmt.Fprintf(from, "committer %s\n", committer)
184+
fmt.Fprintf(from, "tree %s\n", hex.EncodeToString(treeId))
185+
fmt.Fprintf(from, "gpgsig -----BEGIN PGP SIGNATURE-----\n")
186+
fmt.Fprintf(from, " <signature>\n")
187+
fmt.Fprintf(from, " -----END PGP SIGNATURE-----\n")
188+
fmt.Fprintf(from, "\ninitial commit\n")
189+
190+
flen := from.Len()
191+
192+
commit := new(Commit)
193+
n, err := commit.Decode(from, int64(flen))
194+
195+
require.Nil(t, err)
196+
require.Equal(t, flen, n)
197+
require.Len(t, commit.ExtraHeaders, 1)
198+
199+
hdr := commit.ExtraHeaders[0]
200+
201+
assert.Equal(t, "gpgsig", hdr.K)
202+
assert.EqualValues(t, []string{
203+
"-----BEGIN PGP SIGNATURE-----",
204+
"<signature>",
205+
"-----END PGP SIGNATURE-----"},
206+
strings.Split(hdr.V, "\n"))
207+
}
208+
167209
func assertLine(t *testing.T, buf *bytes.Buffer, wanted string, args ...interface{}) {
168210
got, err := buf.ReadString('\n')
169211
if err == io.EOF {

object_db_test.go

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,44 @@ import (
1515
"github.com/stretchr/testify/require"
1616
)
1717

18+
const roundTripCommitSha string = `561ed224a6bd39232d902ad8023c0ebe44fbf6c5`
19+
const roundTripCommit string = `tree f2ebdf9c967f69d57b370901f9344596ec47e51c
20+
parent fe8fbf7de1cd9f08ae642e502bf5de94e523cc08
21+
author brian m. carlson <bk2204@github.com> 1543506816 +0000
22+
committer brian m. carlson <bk2204@github.com> 1543506816 +0000
23+
gpgsig -----BEGIN PGP SIGNATURE-----
24+
Version: GnuPG/MacGPG2 v2.2.9 (Darwin)
25+
26+
iQIGBAABCgAwFiEETbktHYzuflTwZxNFLQybwS+Cs6EFAlwAC4cSHGJrMjIwNEBn
27+
aXRodWIuY29tAAoJEC0Mm8EvgrOhiRMN/2rTxkBb5BeQQeq7rPiIW8+29FzuvPeD
28+
/DhxlRKwKut9h4qhtxNQszTezxhP4PLOkuMvUax2pGXCQ8cjkSswagmycev+AB4d
29+
s0loG4SrEwvH8nAdr6qfNx4ZproRJ8QaEJqyN9SqF7PCWrUAoJKehdgA38WtYFws
30+
ON+nIwzDIvgpoNI+DzgWrx16SOTp87xt8RaJOVK9JNZQk8zBh7rR2viS9CWLysmz
31+
wOh3j4XI1TZ5IFJfpCxZzUDFgb6K3wpAX6Vux5F1f3cN5MsJn6WUJCmYCvwofeeZ
32+
6LMqKgry7EA12l7Tv/JtmMeh+rbT5WLdMIsjascUaHRhpJDNqqHCKMEj1zh3QZNY
33+
Hycdcs24JouVAtPwg07f1ncPU3aE624LnNRA9A6Ih6SkkKE4tgMVA5qkObDfwzLE
34+
lWyBj2QKySaIdSlU2EcoH3UK33v/ofrRr3+bUkDgxdqeV/RkBVvfpeMwFVSFWseE
35+
bCcotryLCZF7vBQU+pKC+EaZxQV9L5+McGzcDYxUmqrhwtR+azRBYFOw+lOT4sYD
36+
FxdLFWCtmDhKPX5Ajci2gmyfgCwdIeDhSuOf2iQQGRpE6y7aka4AlaE=
37+
=UyqL
38+
-----END PGP SIGNATURE-----
39+
40+
pack/set: ignore packs without indices
41+
42+
When we look for packs to read, we look for a pack file, and then an
43+
index, and fail if either one is missing. When Git looks for packs to
44+
read, it looks only for indices and then checks if the pack is present.
45+
46+
The Git approach handles the case when there is an extra pack that lacks
47+
an index, while our approach does not. Consequently, we can get various
48+
errors (showing up so far only on Windows) when an index is missing.
49+
50+
If the index file cannot be read for any reason, simply skip the entire
51+
pack altogether and continue on. This leaves us no more or less
52+
functional than Git in terms of discovering objects and makes our error
53+
handling more robust.
54+
`
55+
1856
func TestDecodeObject(t *testing.T) {
1957
sha := "af5626b4a114abcb82d63db7c8082c3c4756e51b"
2058
contents := "Hello, world!\n"
@@ -223,6 +261,28 @@ func TestWriteCommit(t *testing.T) {
223261
assert.NotNil(t, s.(*memoryStorer).fs[hex.EncodeToString(sha)])
224262
}
225263

264+
func TestWriteCommitWithGPGSignature(t *testing.T) {
265+
b, err := NewMemoryBackend(nil)
266+
require.NoError(t, err)
267+
268+
odb, err := FromBackend(b)
269+
require.NoError(t, err)
270+
271+
commit := new(Commit)
272+
_, err = commit.Decode(
273+
strings.NewReader(roundTripCommit), int64(len(roundTripCommit)))
274+
require.NoError(t, err)
275+
276+
buf := new(bytes.Buffer)
277+
commit.Encode(buf)
278+
assert.Equal(t, roundTripCommit, buf.String())
279+
280+
sha, err := odb.WriteCommit(commit)
281+
282+
assert.Nil(t, err)
283+
assert.Equal(t, roundTripCommitSha, hex.EncodeToString(sha))
284+
}
285+
226286
func TestDecodeTag(t *testing.T) {
227287
const sha = "7639ba293cd2c457070e8446ecdea56682af0f48"
228288
tagShaHex, err := hex.DecodeString(sha)

0 commit comments

Comments
 (0)