+38
-41
gitdiff/patch_header.go
+38
-41
gitdiff/patch_header.go
···
25
25
// not included in the header.
26
26
SHA string
27
27
28
-
// The author details of the patch. Nil if author information is not
29
-
// included in the header.
28
+
// The author details of the patch. If these details are not included in
29
+
// the header, Author is nil and AuthorDate is the zero time.
30
30
Author *PatchIdentity
31
-
AuthorDate *PatchDate
31
+
AuthorDate time.Time
32
32
33
-
// The committer details of the patch. Nil if committer information is not
34
-
// included in the header.
33
+
// The committer details of the patch. If these details are not included in
34
+
// the header, Committer is nil and CommitterDate is the zero time.
35
35
Committer *PatchIdentity
36
-
CommitterDate *PatchDate
36
+
CommitterDate time.Time
37
37
38
38
// The title and body of the commit message describing the changes in the
39
39
// patch. Empty if no message is included in the header.
···
104
104
return PatchIdentity{Name: name, Email: email}, nil
105
105
}
106
106
107
-
// PatchDate is the timestamp when a patch was authored or committed. It
108
-
// contains a raw string version of the date and a parsed version if the date
109
-
// is in a known format.
110
-
type PatchDate struct {
111
-
Parsed time.Time
112
-
Raw string
113
-
}
114
-
115
-
// IsParsed returns true if the PatchDate has a parsed time.
116
-
func (d PatchDate) IsParsed() bool {
117
-
return !d.Parsed.IsZero()
118
-
}
119
-
120
-
// ParsePatchDate parses a patch date string. If s is in a supported format,
121
-
// the PatchDate has both the Raw and Parsed initialized.
122
-
//
123
-
// ParsePatchDate supports the iso, rfc, short, raw, unix, and default formats
124
-
// (with local variants) used by the --date flag in Git.
125
-
func ParsePatchDate(s string) PatchDate {
107
+
// ParsePatchDate parses a patch date string. It returns the parsed time or an
108
+
// error if s has an unknown format. ParsePatchDate supports the iso, rfc,
109
+
// short, raw, unix, and default formats (with local variants) used by the
110
+
// --date flag in Git.
111
+
func ParsePatchDate(s string) (time.Time, error) {
126
112
const (
127
113
isoFormat = "2006-01-02 15:04:05 -0700"
128
114
isoStrictFormat = "2006-01-02T15:04:05-07:00"
···
132
118
defaultLocalFormat = "Mon Jan 2 15:04:05 2006"
133
119
)
134
120
135
-
d := PatchDate{Raw: s}
121
+
if s == "" {
122
+
return time.Time{}, nil
123
+
}
136
124
137
125
for _, fmt := range []string{
138
126
isoFormat,
···
143
131
defaultLocalFormat,
144
132
} {
145
133
if t, err := time.ParseInLocation(fmt, s, time.Local); err == nil {
146
-
d.Parsed = t
147
-
return d
134
+
return t, nil
148
135
}
149
136
}
150
137
151
138
// unix format
152
139
if unix, err := strconv.ParseInt(s, 10, 64); err == nil {
153
-
d.Parsed = time.Unix(unix, 0)
154
-
return d
140
+
return time.Unix(unix, 0), nil
155
141
}
156
142
157
143
// raw format
···
159
145
unix, uerr := strconv.ParseInt(s[:space], 10, 64)
160
146
zone, zerr := time.Parse("-0700", s[space+1:])
161
147
if uerr == nil && zerr == nil {
162
-
d.Parsed = time.Unix(unix, 0).In(zone.Location())
163
-
return d
148
+
return time.Unix(unix, 0).In(zone.Location()), nil
164
149
}
165
150
}
166
151
167
-
return d
152
+
return time.Time{}, fmt.Errorf("unknown date format: %s", s)
168
153
}
169
154
170
155
// ParsePatchHeader parses a preamble string as returned by Parse into a
···
251
236
h.Committer = &u
252
237
253
238
case strings.HasPrefix(line, datePrefix):
254
-
d := ParsePatchDate(strings.TrimSpace(line[len(datePrefix):]))
255
-
h.AuthorDate = &d
239
+
d, err := ParsePatchDate(strings.TrimSpace(line[len(datePrefix):]))
240
+
if err != nil {
241
+
return nil, err
242
+
}
243
+
h.AuthorDate = d
256
244
257
245
case strings.HasPrefix(line, authorDatePrefix):
258
-
d := ParsePatchDate(strings.TrimSpace(line[len(authorDatePrefix):]))
259
-
h.AuthorDate = &d
246
+
d, err := ParsePatchDate(strings.TrimSpace(line[len(authorDatePrefix):]))
247
+
if err != nil {
248
+
return nil, err
249
+
}
250
+
h.AuthorDate = d
260
251
261
252
case strings.HasPrefix(line, commitDatePrefix):
262
-
d := ParsePatchDate(strings.TrimSpace(line[len(commitDatePrefix):]))
263
-
h.CommitterDate = &d
253
+
d, err := ParsePatchDate(strings.TrimSpace(line[len(commitDatePrefix):]))
254
+
if err != nil {
255
+
return nil, err
256
+
}
257
+
h.CommitterDate = d
264
258
}
265
259
}
266
260
if s.Err() != nil {
···
358
352
359
353
date := msg.Header.Get("Date")
360
354
if date != "" {
361
-
d := ParsePatchDate(date)
362
-
h.AuthorDate = &d
355
+
d, err := ParsePatchDate(date)
356
+
if err != nil {
357
+
return nil, err
358
+
}
359
+
h.AuthorDate = d
363
360
}
364
361
365
362
h.Title = msg.Header.Get("Subject")
+41
-77
gitdiff/patch_header_test.go
+41
-77
gitdiff/patch_header_test.go
···
69
69
70
70
tests := map[string]struct {
71
71
Input string
72
-
Output PatchDate
72
+
Output time.Time
73
+
Err interface{}
73
74
}{
74
75
"default": {
75
-
Input: "Thu Apr 9 01:07:06 2020 -0700",
76
-
Output: PatchDate{
77
-
Parsed: expected,
78
-
Raw: "Thu Apr 9 01:07:06 2020 -0700",
79
-
},
76
+
Input: "Thu Apr 9 01:07:06 2020 -0700",
77
+
Output: expected,
80
78
},
81
79
"defaultLocal": {
82
-
Input: "Thu Apr 9 01:07:06 2020",
83
-
Output: PatchDate{
84
-
Parsed: time.Date(2020, 4, 9, 1, 7, 6, 0, time.Local),
85
-
Raw: "Thu Apr 9 01:07:06 2020",
86
-
},
80
+
Input: "Thu Apr 9 01:07:06 2020",
81
+
Output: time.Date(2020, 4, 9, 1, 7, 6, 0, time.Local),
87
82
},
88
83
"iso": {
89
-
Input: "2020-04-09 01:07:06 -0700",
90
-
Output: PatchDate{
91
-
Parsed: expected,
92
-
Raw: "2020-04-09 01:07:06 -0700",
93
-
},
84
+
Input: "2020-04-09 01:07:06 -0700",
85
+
Output: expected,
94
86
},
95
87
"isoStrict": {
96
-
Input: "2020-04-09T01:07:06-07:00",
97
-
Output: PatchDate{
98
-
Parsed: expected,
99
-
Raw: "2020-04-09T01:07:06-07:00",
100
-
},
88
+
Input: "2020-04-09T01:07:06-07:00",
89
+
Output: expected,
101
90
},
102
91
"rfc": {
103
-
Input: "Thu, 9 Apr 2020 01:07:06 -0700",
104
-
Output: PatchDate{
105
-
Parsed: expected,
106
-
Raw: "Thu, 9 Apr 2020 01:07:06 -0700",
107
-
},
92
+
Input: "Thu, 9 Apr 2020 01:07:06 -0700",
93
+
Output: expected,
108
94
},
109
95
"short": {
110
-
Input: "2020-04-09",
111
-
Output: PatchDate{
112
-
Parsed: time.Date(2020, 4, 9, 0, 0, 0, 0, time.Local),
113
-
Raw: "2020-04-09",
114
-
},
96
+
Input: "2020-04-09",
97
+
Output: time.Date(2020, 4, 9, 0, 0, 0, 0, time.Local),
115
98
},
116
99
"raw": {
117
-
Input: "1586419626 -0700",
118
-
Output: PatchDate{
119
-
Parsed: expected,
120
-
Raw: "1586419626 -0700",
121
-
},
100
+
Input: "1586419626 -0700",
101
+
Output: expected,
122
102
},
123
103
"unix": {
124
-
Input: "1586419626",
125
-
Output: PatchDate{
126
-
Parsed: expected,
127
-
Raw: "1586419626",
128
-
},
104
+
Input: "1586419626",
105
+
Output: expected,
129
106
},
130
107
"unknownFormat": {
131
108
Input: "4/9/2020 01:07:06 PDT",
132
-
Output: PatchDate{
133
-
Raw: "4/9/2020 01:07:06 PDT",
134
-
},
109
+
Err: "unknown date format",
135
110
},
136
111
"empty": {
137
-
Input: "",
138
-
Output: PatchDate{},
112
+
Input: "",
139
113
},
140
114
}
141
115
142
116
for name, test := range tests {
143
117
t.Run(name, func(t *testing.T) {
144
-
d := ParsePatchDate(test.Input)
145
-
if test.Output.Raw != d.Raw {
146
-
t.Errorf("incorrect raw date: expected %q, actual %q", test.Output.Raw, d.Raw)
118
+
d, err := ParsePatchDate(test.Input)
119
+
if test.Err != nil {
120
+
assertError(t, test.Err, err, "parsing date")
121
+
return
147
122
}
148
-
if !test.Output.Parsed.Equal(d.Parsed) {
149
-
t.Errorf("incorrect parsed date: expected %v, actual %v", test.Output.Parsed, d.Parsed)
123
+
if err != nil {
124
+
t.Fatalf("unexpected error parsing date: %v", err)
125
+
}
126
+
if !test.Output.Equal(d) {
127
+
t.Errorf("incorrect parsed date: expected %v, actual %v", test.Output, d)
150
128
}
151
129
})
152
130
}
···
158
136
Name: "Morton Haypenny",
159
137
Email: "mhaypenny@example.com",
160
138
}
161
-
expectedDate := &PatchDate{
162
-
Parsed: time.Date(2020, 04, 11, 15, 21, 23, 0, time.FixedZone("PDT", -7*60*60)),
163
-
Raw: "Sat Apr 11 15:21:23 2020 -0700",
164
-
}
139
+
expectedDate := time.Date(2020, 04, 11, 15, 21, 23, 0, time.FixedZone("PDT", -7*60*60))
165
140
expectedTitle := "A sample commit to test header parsing"
166
141
expectedBody := "The medium format shows the body, which\nmay wrap on to multiple lines.\n\nAnother body line."
167
142
···
258
233
Another body line.
259
234
`,
260
235
Header: PatchHeader{
261
-
SHA: expectedSHA,
262
-
Author: expectedIdentity,
263
-
AuthorDate: &PatchDate{
264
-
Parsed: expectedDate.Parsed,
265
-
Raw: "Sat, 11 Apr 2020 15:21:23 -0700",
266
-
},
267
-
Title: "[PATCH] " + expectedTitle,
268
-
Body: expectedBody,
236
+
SHA: expectedSHA,
237
+
Author: expectedIdentity,
238
+
AuthorDate: expectedDate,
239
+
Title: "[PATCH] " + expectedTitle,
240
+
Body: expectedBody,
269
241
},
270
242
},
271
243
"unwrapTitle": {
···
346
318
}
347
319
348
320
assertPatchIdentity(t, "author", exp.Author, act.Author)
349
-
assertPatchDate(t, "author", exp.AuthorDate, act.AuthorDate)
321
+
if !exp.AuthorDate.Equal(act.AuthorDate) {
322
+
t.Errorf("incorrect parsed author date: expected %v, but got %v", exp.AuthorDate, act.AuthorDate)
323
+
}
350
324
351
325
assertPatchIdentity(t, "committer", exp.Committer, act.Committer)
352
-
assertPatchDate(t, "committer", exp.CommitterDate, act.CommitterDate)
326
+
if !exp.CommitterDate.Equal(act.CommitterDate) {
327
+
t.Errorf("incorrect parsed committer date: expected %v, but got %v", exp.CommitterDate, act.CommitterDate)
328
+
}
353
329
354
330
if exp.Title != act.Title {
355
331
t.Errorf("incorrect parsed title:\n expected: %q\n actual: %q", exp.Title, act.Title)
···
372
348
t.Errorf("incorrect parsed %s, expected %+v, bot got %+v", kind, exp, act)
373
349
}
374
350
}
375
-
376
-
func assertPatchDate(t *testing.T, kind string, exp, act *PatchDate) {
377
-
switch {
378
-
case exp == nil && act == nil:
379
-
case exp == nil && act != nil:
380
-
t.Errorf("incorrect parsed %s date: expected nil, but got %+v", kind, act)
381
-
case exp != nil && act == nil:
382
-
t.Errorf("incorrect parsed %s date: expected %+v, but got nil", kind, exp)
383
-
case exp.Raw != act.Raw || !exp.Parsed.Equal(act.Parsed):
384
-
t.Errorf("incorrect parsed %s date, expected %+v, bot got %+v", kind, exp, act)
385
-
}
386
-
}