@@ -39,6 +39,11 @@ type PatchHeader struct {
39
39
// patch. Empty if no message is included in the header.
40
40
Title string
41
41
Body string
42
+
43
+ // If the preamble looks like an email, ParsePatchHeader will
44
+ // remove prefixes such as `Re: ` and `[PATCH v3 5/17]` from the
45
+ // Title and place them here.
46
+ SubjectPrefix string
42
47
}
43
48
44
49
// Message returns the commit message for the header. The message consists of
@@ -160,10 +165,14 @@ func ParsePatchDate(s string) (time.Time, error) {
160
165
// formats used by git diff, git log, and git show and the UNIX mailbox format
161
166
// used by git format-patch.
162
167
//
163
- // ParsePatchHeader makes no assumptions about the format of the patch title or
164
- // message other than trimming whitespace and condensing blank lines. In
165
- // particular, it does not remove the extra content that git format-patch adds
166
- // to make emailed patches friendlier, like subject prefixes or commit stats.
168
+ // If ParsePatchHeader detect that it is handling an email, it will
169
+ // remove extra content at the beginning of the title line, such as
170
+ // `[PATCH]` or `Re:` in the same way that `git mailinfo` does.
171
+ // SubjectPrefix will be set to the value of this removed string.
172
+ // (`git mailinfo` is the core part of `git am` that pulls information
173
+ // out of an individual mail.) Unline `git mailinfo`,
174
+ // ParsePatchHeader does not at the moment remove commit states or
175
+ // other extraneous matter after a `---` line.
167
176
func ParsePatchHeader (s string ) (* PatchHeader , error ) {
168
177
r := bufio .NewReader (strings .NewReader (s ))
169
178
@@ -359,7 +368,8 @@ func parseHeaderMail(mailLine string, r io.Reader) (*PatchHeader, error) {
359
368
h .AuthorDate = d
360
369
}
361
370
362
- h .Title = msg .Header .Get ("Subject" )
371
+ subject := msg .Header .Get ("Subject" )
372
+ h .SubjectPrefix , h .Title = parseSubject (subject )
363
373
364
374
s := bufio .NewScanner (msg .Body )
365
375
h .Body = scanMessageBody (s , "" )
@@ -369,3 +379,51 @@ func parseHeaderMail(mailLine string, r io.Reader) (*PatchHeader, error) {
369
379
370
380
return h , nil
371
381
}
382
+
383
+ // Takes an email subject and returns the patch prefix and commit
384
+ // title. i.e., `[PATCH v3 3/5] Implement foo` would return `[PATCH
385
+ // v3 3/5] ` and `Implement foo`
386
+ func parseSubject (s string ) (string , string ) {
387
+ // This is meant to be compatible with
388
+ // https://github.com/git/git/blob/master/mailinfo.c:cleanup_subject().
389
+ // If compatibility with `git am` drifts, go there to see if there
390
+ // are any updates.
391
+
392
+ at := 0
393
+ for at < len (s ) {
394
+ switch s [at ] {
395
+ case 'r' , 'R' :
396
+ // Detect re:, Re:, rE: and RE:
397
+ if at + 2 < len (s ) &&
398
+ (s [at + 1 ] == 'e' || s [at + 1 ] == 'E' ) &&
399
+ s [at + 2 ] == ':' {
400
+ at += 3
401
+ continue
402
+ }
403
+
404
+ case ' ' , '\t' , ':' :
405
+ // Delete whitespace and duplicate ':' characters
406
+ at ++
407
+ continue
408
+
409
+ case '[' :
410
+ // Look for closing parenthesis
411
+ j := at + 1
412
+ for ; j < len (s ); j ++ {
413
+ if s [j ] == ']' {
414
+ break
415
+ }
416
+ }
417
+
418
+ if j < len (s ) {
419
+ at = j + 1
420
+ continue
421
+ }
422
+ }
423
+
424
+ // Only loop if we actually removed something
425
+ break
426
+ }
427
+
428
+ return s [:at ], s [at :]
429
+ }
0 commit comments