This repository has no description
0

Configure Feed

Select the types of activity you want to include in your feed.

knotmirror/xrpc/gitea: parse mergetag commit header

Signed-off-by: Seongmin Lee <git@boltless.me>

author
Seongmin Lee
committer
Tangled
date (Jul 21, 2026, 5:47 PM +0300) commit 22c5bff0 parent b9b7e63f change-id kwvttpkk
+98 -67
+49 -65
knotmirror/xrpc/gitea/batch.go
··· 228 228 ExtraHeaders: make(map[string][]byte), 229 229 } 230 230 231 - payloadSB := new(strings.Builder) 232 - signatureSB := new(strings.Builder) 233 - messageSB := new(strings.Builder) 234 - firstLine := true 235 - message := false 236 - pgpsig := false 237 - 238 - bufReader, ok := reader.(*bufio.Reader) 239 - if !ok { 240 - bufReader = bufio.NewReader(reader) 241 - } 242 - 243 - readLoop: 231 + bufReader := bufio.NewReader(reader) 232 + inHeader := true 233 + var payloadSB, messageSB bytes.Buffer 234 + var headerKey string 235 + var headerValue []byte 244 236 for { 245 237 line, err := bufReader.ReadBytes('\n') 246 - if err != nil { 247 - if err == io.EOF { 248 - if message { 249 - _, _ = messageSB.Write(line) 250 - } 251 - _, _ = payloadSB.Write(line) 252 - break readLoop 253 - } 238 + if err != nil && err != io.EOF { 254 239 return nil, err 255 240 } 256 - if pgpsig { 257 - if len(line) > 0 && line[0] == ' ' { 258 - _, _ = signatureSB.Write(line[1:]) 259 - continue 260 - } 261 - pgpsig = false 241 + if len(line) == 0 { 242 + break 262 243 } 263 244 264 - if !message { 265 - // This is probably not correct but is copied from go-gits interpretation... 266 - trimmed := bytes.TrimSpace(line) 267 - if len(trimmed) == 0 { 268 - message = true 269 - _, _ = payloadSB.Write(line) 270 - continue 245 + if inHeader { 246 + inHeader = !(len(line) == 1 && line[0] == '\n') // a bare newline ends the header block 247 + k, v, _ := bytes.Cut(line, []byte{' '}) 248 + if len(k) != 0 || !inHeader { 249 + if headerKey != "" { 250 + assignCommitHeader(commit, headerKey, headerValue) 251 + } 252 + headerKey = string(k) // also resets headerValue via the assignment below 253 + headerValue = v 254 + } else { 255 + headerValue = append(headerValue, v...) 271 256 } 272 - 273 - k, data, _ := bytes.Cut(line, []byte{' '}) 274 - 275 - switch string(k) { 276 - case "tree": 277 - commit.TreeHash = plumbing.NewHash(string(data)) 278 - _, _ = payloadSB.Write(line) 279 - case "parent": 280 - commit.ParentHashes = append(commit.ParentHashes, plumbing.NewHash(string(data))) 257 + if headerKey != "gpgsig" && headerKey != "gpgsig-sha256" { 281 258 _, _ = payloadSB.Write(line) 282 - case "author": 283 - commit.Author.Decode(data) 284 - _, _ = payloadSB.Write(line) 285 - case "committer": 286 - commit.Committer.Decode(data) 287 - _, _ = payloadSB.Write(line) 288 - case "gpgsig": 289 - fallthrough 290 - case "gpgsig-sha256": // FIXME: no intertop, so only 1 exists at present. 291 - _, _ = signatureSB.Write(data) 292 - _ = signatureSB.WriteByte('\n') 293 - pgpsig = true 294 - default: 295 - commit.ExtraHeaders[string(k)] = bytes.TrimSpace(data) 296 - // If the first line is not any of the known headers, then it is probably the prefix added when git cat-file is called with --batch, and that is not part of the payload 297 - if !firstLine { 298 - // Every subsequent header field is added to the payload 299 - _, _ = payloadSB.Write(line) 300 - } 301 259 } 302 260 } else { 303 261 _, _ = messageSB.Write(line) 304 262 _, _ = payloadSB.Write(line) 305 263 } 306 264 307 - firstLine = false 265 + if err == io.EOF { 266 + break 267 + } 308 268 } 269 + 309 270 commit.Message = messageSB.String() 310 271 // TODO: pass raw payload so we can verify it without reconstructing the payload 311 - commit.PGPSignature = signatureSB.String() 312 - 272 + // if commit.Signature != nil { 273 + // commit.Signature.Payload = payloadSB.String() 274 + // } 313 275 return commit, nil 276 + } 277 + 278 + func assignCommitHeader(commit *object.Commit, headerKey string, headerValue []byte) { 279 + value := bytes.TrimSuffix(headerValue, []byte{'\n'}) 280 + switch headerKey { 281 + case "tree": 282 + commit.TreeHash = plumbing.NewHash(string(value)) 283 + case "parent": 284 + commit.ParentHashes = append(commit.ParentHashes, plumbing.NewHash(string(value))) 285 + case "author": 286 + commit.Author.Decode(value) 287 + case "committer": 288 + commit.Committer.Decode(value) 289 + case "gpgsig", "gpgsig-sha256": 290 + // if there are duplicate "gpgsig" and "gpgsig-sha256" headers, then the signature must have already been invalid 291 + // so we don't need to handle duplicate headers here 292 + commit.PGPSignature = string(value) 293 + case "mergetag": 294 + commit.MergeTag = string(value) 295 + default: 296 + commit.ExtraHeaders[headerKey] = value 297 + } 314 298 } 315 299 316 300 // ParseCatFileTreeLine reads an entry from a tree in a cat-file --batch stream
+49 -2
knotmirror/xrpc/gitea/commit_test.go
··· 19 19 author silverwind <me@silverwind.io> 1563741793 +0200 20 20 committer silverwind <me@silverwind.io> 1563741793 +0200 21 21 gpgsig -----BEGIN PGP SIGNATURE----- 22 + ` + " " + ` 22 23 iQIzBAABCAAdFiEEWPb2jX6FS2mqyJRQLmK0HJOGlEMFAl00zmEACgkQLmK0HJOG 23 24 lEMDFBAAhQKKqLD1VICygJMEB8t1gBmNLgvziOLfpX4KPWdPtBk3v/QJ7OrfMrVK 24 25 xlC4ZZyx6yMm1Q7GzmuWykmZQJ9HMaHJ49KAbh5MMjjV/+OoQw9coIdo8nagRUld ··· 61 62 mfeFhT57UbE4qukTDIQ0Y0WM40UYRTakRaDY7ubhXgLgx09Cnp9XTVMsHgT6j9/i 62 63 1pxsB104XLWjQHTjr1JtiaBQEwFh9r2OKTcpvaLcbNtYpo7CzOs= 63 64 =FRsO 64 - -----END PGP SIGNATURE----- 65 - `, commitFromReader.PGPSignature) 65 + -----END PGP SIGNATURE-----`, commitFromReader.PGPSignature) 66 66 assert.Equal(t, `tree f1a6cb52b2d16773290cefe49ad0684b50a4f930 67 67 parent 37991dec2c8e592043f47155ce4808d4580f9123 68 68 author silverwind <me@silverwind.io> 1563741793 +0200 ··· 71 71 empty commit`, tcommit.Payload()) 72 72 assert.Equal(t, "silverwind <me@silverwind.io>", commitFromReader.Author.String()) 73 73 } 74 + 75 + func TestCommitFromReaderMergeTag(t *testing.T) { 76 + // Built with explicit "\n" concatenation, not a backtick literal: the blank 77 + // mergetag continuation lines are " \n" (space + newline) and gofmt/editors 78 + // strip trailing whitespace from raw literals, which would corrupt the input. 79 + commitString := "tree 635dfb8e1e9d4d75855cc23eb28d35533f55b42f\n" + 80 + "parent c1fa0bb633e4a6b11e83ffc57fa5abe8ebb87891\n" + 81 + "parent 8f80b5b227ef9ea422080487715c841856339aed\n" + 82 + "author Linus Torvalds <torvalds@linux-foundation.org> 1778539129 -0700\n" + 83 + "committer Linus Torvalds <torvalds@linux-foundation.org> 1778539129 -0700\n" + 84 + "mergetag object 8f80b5b227ef9ea422080487715c841856339aed\n" + 85 + " type commit\n" + 86 + " tag linux_kselftest-kunit-fixes-7.1-rc4\n" + 87 + " tagger Shuah Khan <skhan@linuxfoundation.org> 1778535878 -0600\n" + 88 + " \n" + // blank continuation line inside the mergetag: the bug trigger 89 + " linux_kselftest-kunit-fixes-7.1-rc4\n" + 90 + " \n" + 91 + " Fix to decouple KUNIT_DEBUGFS and KUNIT_ALL_TESTS options.\n" + 92 + " -----BEGIN PGP SIGNATURE-----\n" + 93 + " \n" + 94 + " iQIzBAABCgAdFiEEPZKym/RZuOCGeA/kCwJExA0NQxwFAmoCUf0ACgkQCwJExA0N\n" + 95 + " =QdSk\n" + 96 + " -----END PGP SIGNATURE-----\n" + 97 + "\n" + // real header/message separator: truly empty line 98 + "Merge tag 'linux_kselftest-kunit-fixes-7.1-rc4' of git://example\n" + 99 + "\n" + 100 + "Pull kunit fixes from Shuah Khan" 101 + 102 + sha := plumbing.NewHash("50897c955902c93ae71c38698abb910525ebdc89") 103 + 104 + c, err := ReadCommit(sha, strings.NewReader(commitString)) 105 + require.NoError(t, err) 106 + require.NotNil(t, c) 107 + 108 + // Message must be only the merge message, not polluted with the tag body/signature. 109 + assert.Equal(t, "Merge tag 'linux_kselftest-kunit-fixes-7.1-rc4' of git://example\n\nPull kunit fixes from Shuah Khan", c.Message) 110 + 111 + // mergetag is captured, and this commit itself is not gpg-signed. 112 + assert.Contains(t, c.MergeTag, "type commit\n") 113 + assert.Contains(t, c.MergeTag, "-----END PGP SIGNATURE-----") 114 + assert.Empty(t, c.PGPSignature) 115 + 116 + // The broken parser dumped stray continuation lines into ExtraHeaders[""]. 117 + assert.NotContains(t, c.ExtraHeaders, "") 118 + 119 + assert.Equal(t, "Linus Torvalds <torvalds@linux-foundation.org>", c.Author.String()) 120 + }