@@ -364,27 +364,29 @@ func (cl *GerritCL) Branch() string { return cl.branch }
364
364
func (cl * GerritCL ) updateBranch () {
365
365
for i := len (cl .Metas ) - 1 ; i >= 0 ; i -- {
366
366
mc := cl .Metas [i ]
367
- branch , _ := lineValue (mc .Commit .Msg , "Branch:" )
367
+ branch := lineValue (mc .Commit .Msg , "Branch:" )
368
368
if branch != "" {
369
369
cl .branch = strings .TrimPrefix (branch , "refs/heads/" )
370
370
return
371
371
}
372
372
}
373
373
}
374
374
375
- // lineValue extracts a value from an RFC 822-style "key: value" series of lines.
375
+ // lineValueOK extracts a value from an RFC 822-style "key: value" series of lines.
376
376
// If all is,
377
377
// foo: bar
378
378
// bar: baz
379
379
// lineValue(all, "foo:") returns "bar". It trims any whitespace.
380
380
// The prefix is case sensitive and must include the colon.
381
- func lineValue (all , prefix string ) (value , rest string ) {
381
+ // The ok value reports whether a line with such a prefix is found, even if its
382
+ // value is empty. If ok is true, the rest value contains the subsequent lines.
383
+ func lineValueOK (all , prefix string ) (value , rest string , ok bool ) {
382
384
orig := all
383
385
consumed := 0
384
386
for {
385
387
i := strings .Index (all , prefix )
386
388
if i == - 1 {
387
- return "" , ""
389
+ return "" , "" , false
388
390
}
389
391
if i > 0 && all [i - 1 ] != '\n' && all [i - 1 ] != '\r' {
390
392
all = all [i + len (prefix ):]
@@ -399,17 +401,26 @@ func lineValue(all, prefix string) (value, rest string) {
399
401
} else {
400
402
consumed = len (orig )
401
403
}
402
- return strings .TrimSpace (val ), orig [consumed :]
404
+ return strings .TrimSpace (val ), orig [consumed :], true
403
405
}
404
406
}
405
407
408
+ func lineValue (all , prefix string ) string {
409
+ value , _ , _ := lineValueOK (all , prefix )
410
+ return value
411
+ }
412
+
413
+ func lineValueRest (all , prefix string ) (value , rest string ) {
414
+ value , rest , _ = lineValueOK (all , prefix )
415
+ return
416
+ }
417
+
406
418
// WorkInProgress reports whether the CL has its Work-in-progress bit set, per
407
419
// https://gerrit-review.googlesource.com/Documentation/intro-user.html#wip
408
420
func (cl * GerritCL ) WorkInProgress () bool {
409
421
var wip bool
410
422
for _ , m := range cl .Metas {
411
- v , _ := lineValue (m .Commit .Msg , "Work-in-progress:" )
412
- switch v {
423
+ switch lineValue (m .Commit .Msg , "Work-in-progress:" ) {
413
424
case "true" :
414
425
wip = true
415
426
case "false" :
@@ -438,8 +449,7 @@ func (cl *GerritCL) Footer(key string) string {
438
449
panic ("Footer key does not end in colon" )
439
450
}
440
451
// TODO: git footers are treated as multimaps. Account for this.
441
- v , _ := lineValue (cl .Commit .Msg , key )
442
- return v
452
+ return lineValue (cl .Commit .Msg , key )
443
453
}
444
454
445
455
// OwnerID returns the ID of the CL’s owner. It will return -1 on error.
@@ -1292,7 +1302,6 @@ func (gp *GerritProject) check() error {
1292
1302
type GerritMeta struct {
1293
1303
// Commit points up to the git commit for this Gerrit NoteDB meta commit.
1294
1304
Commit * GitCommit
1295
-
1296
1305
// CL is the Gerrit CL this metadata is for.
1297
1306
CL * GerritCL
1298
1307
@@ -1324,17 +1333,39 @@ func (m *GerritMeta) Footer() string {
1324
1333
return m .Commit .Msg [i + 2 :]
1325
1334
}
1326
1335
1327
- // Hashtags returns the current set of hashtags.
1336
+ // Hashtags returns the set of hashtags on m's CL as of the time of m .
1328
1337
func (m * GerritMeta ) Hashtags () GerritHashtags {
1329
- tags , _ := lineValue (m .Footer (), "Hashtags: " )
1330
- return GerritHashtags (tags )
1338
+ // If this GerritMeta set hashtags, use it.
1339
+ tags , _ , ok := lineValueOK (m .Footer (), "Hashtags: " )
1340
+ if ok {
1341
+ return GerritHashtags (tags )
1342
+ }
1343
+
1344
+ // Otherwise, look at older metas (from most recent to oldest)
1345
+ // to find most recent value. Ignore anything that's newer
1346
+ // than m.
1347
+ sawThisMeta := false // whether we've seen 'm'
1348
+ metas := m .CL .Metas
1349
+ for i := len (metas ) - 1 ; i >= 0 ; i -- {
1350
+ mp := metas [i ]
1351
+ if mp .Commit .Hash == m .Commit .Hash {
1352
+ sawThisMeta = true
1353
+ continue
1354
+ }
1355
+ if ! sawThisMeta {
1356
+ continue
1357
+ }
1358
+ if tags , _ , ok := lineValueOK (mp .Footer (), "Hashtags: " ); ok {
1359
+ return GerritHashtags (tags )
1360
+ }
1361
+ }
1362
+ return ""
1331
1363
}
1332
1364
1333
1365
// ActionTag returns the Gerrit "Tag" value from the meta commit.
1334
1366
// These are of the form "autogenerated:gerrit:setHashtag".
1335
1367
func (m * GerritMeta ) ActionTag () string {
1336
- v , _ := lineValue (m .Footer (), "Tag: " )
1337
- return v
1368
+ return lineValue (m .Footer (), "Tag: " )
1338
1369
}
1339
1370
1340
1371
// HashtagEdits returns the hashtags added and removed by this meta commit,
@@ -1354,7 +1385,7 @@ func (m *GerritMeta) HashtagEdits() (added, removed GerritHashtags, ok bool) {
1354
1385
// Hashtag added: bar
1355
1386
// Hashtags added: foo, bar
1356
1387
for len (msg ) > 0 {
1357
- value , rest := lineValue (msg , "Hash" )
1388
+ value , rest := lineValueRest (msg , "Hash" )
1358
1389
msg = rest
1359
1390
colon := strings .IndexByte (value , ':' )
1360
1391
if colon != - 1 {
@@ -1419,8 +1450,7 @@ func (m *GerritMeta) LabelVotes() map[string]map[string]int8 {
1419
1450
isNew := strings .Contains (footer , "\n Tag: autogenerated:gerrit:newPatchSet\n " )
1420
1451
email := mc .Commit .Author .Email ()
1421
1452
if isNew {
1422
- commit , _ := lineValue (footer , "Commit: " )
1423
- if commit != "" {
1453
+ if commit := lineValue (footer , "Commit: " ); commit != "" {
1424
1454
// TODO: implement Gerrit's vote copying. For example,
1425
1455
// label.Label-Name.copyAllScoresIfNoChange defaults to true (as it is with Go's server)
1426
1456
// https://gerrit-review.googlesource.com/Documentation/config-labels.html#label_copyAllScoresIfNoChange
@@ -1447,7 +1477,7 @@ func (m *GerritMeta) LabelVotes() map[string]map[string]int8 {
1447
1477
remain := footer
1448
1478
for len (remain ) > 0 {
1449
1479
var labelEqVal string
1450
- labelEqVal , remain = lineValue (remain , "Label: " )
1480
+ labelEqVal , remain = lineValueRest (remain , "Label: " )
1451
1481
if labelEqVal != "" {
1452
1482
label , value , whose := parseGerritLabelValue (labelEqVal )
1453
1483
if label != "" {
0 commit comments