@@ -12,7 +12,7 @@ import (
12
12
"os/signal"
13
13
"path/filepath"
14
14
"regexp"
15
- "sort "
15
+ "strconv "
16
16
"strings"
17
17
"text/tabwriter"
18
18
"time"
@@ -57,6 +57,7 @@ type options struct {
57
57
logLevel string
58
58
centralRef string
59
59
fetchMode string
60
+ history int
60
61
61
62
dryRun bool
62
63
githubLogin string
@@ -80,6 +81,7 @@ func (o *options) Bind(fs *flag.FlagSet) {
80
81
fs .StringVar (& o .logLevel , "log-level" , logrus .InfoLevel .String (), "Logging level." )
81
82
fs .StringVar (& o .centralRef , "central-ref" , "origin/master" , "Git ref for the central branch that will be updated, used as the base for determining what commits need to be cherry-picked." )
82
83
fs .StringVar (& o .fetchMode , "fetch-mode" , string (ssh ), "Method to use for fetching from git remotes." )
84
+ fs .IntVar (& o .history , "history" , 1 , "How many commits back to start searching for missing vendor commits." )
83
85
84
86
fs .BoolVar (& o .dryRun , "dry-run" , true , "Whether to actually create the pull request with github client" )
85
87
fs .StringVar (& o .githubLogin , "github-login" , githubLogin , "The GitHub username to use." )
@@ -165,7 +167,7 @@ func main() {
165
167
logrus .WithError (err ).Fatal ("could not unmarshal input commits" )
166
168
}
167
169
} else {
168
- commits , err = detectNewCommits (ctx , logger .WithField ("phase" , "detect" ), opts .stagingDir , opts .centralRef , fetchMode (opts .fetchMode ))
170
+ commits , err = detectNewCommits (ctx , logger .WithField ("phase" , "detect" ), opts .stagingDir , opts .centralRef , fetchMode (opts .fetchMode ), opts . history )
169
171
if err != nil {
170
172
logger .WithError (err ).Fatal ("failed to detect commits" )
171
173
}
@@ -266,7 +268,7 @@ type commit struct {
266
268
var repoRegex = regexp .MustCompile (`Upstream-repository: ([^ ]+)\n` )
267
269
var commitRegex = regexp .MustCompile (`Upstream-commit: ([a-f0-9]+)\n` )
268
270
269
- func detectNewCommits (ctx context.Context , logger * logrus.Entry , stagingDir , centralRef string , mode fetchMode ) ([]commit , error ) {
271
+ func detectNewCommits (ctx context.Context , logger * logrus.Entry , stagingDir , centralRef string , mode fetchMode , history int ) ([]commit , error ) {
270
272
lastCommits := map [string ]string {}
271
273
if err := fs .WalkDir (os .DirFS (stagingDir ), "." , func (path string , d fs.DirEntry , err error ) error {
272
274
if err != nil {
@@ -284,11 +286,12 @@ func detectNewCommits(ctx context.Context, logger *logrus.Entry, stagingDir, cen
284
286
output , err := runCommand (logger , exec .CommandContext (ctx ,
285
287
"git" , "log" ,
286
288
centralRef ,
287
- "-n" , "1" ,
289
+ "-n" , strconv . Itoa ( history ) ,
288
290
"--grep" , "Upstream-repository: " + path ,
289
291
"--grep" , "Upstream-commit" ,
290
292
"--all-match" ,
291
293
"--pretty=%B" ,
294
+ "--reverse" ,
292
295
"--" ,
293
296
filepath .Join (stagingDir , path ),
294
297
))
@@ -315,7 +318,7 @@ func detectNewCommits(ctx context.Context, logger *logrus.Entry, stagingDir, cen
315
318
return nil , fmt .Errorf ("failed to walk %s: %w" , stagingDir , err )
316
319
}
317
320
318
- var commits [] commit
321
+ commits := map [ string ][] commit {}
319
322
for repo , lastCommit := range lastCommits {
320
323
var remote string
321
324
switch mode {
@@ -335,6 +338,7 @@ func detectNewCommits(ctx context.Context, logger *logrus.Entry, stagingDir, cen
335
338
output , err := runCommand (logger , exec .CommandContext (ctx ,
336
339
"git" , "log" ,
337
340
"--pretty=%H" ,
341
+ "--no-merges" ,
338
342
lastCommit + "...FETCH_HEAD" ,
339
343
))
340
344
if err != nil {
@@ -365,7 +369,10 @@ func detectNewCommits(ctx context.Context, logger *logrus.Entry, stagingDir, cen
365
369
if err != nil {
366
370
return nil , fmt .Errorf ("invalid time %s: %w" , parts [1 ], err )
367
371
}
368
- commits = append (commits , commit {
372
+ if _ , ok := commits [repo ]; ! ok {
373
+ commits [repo ] = []commit {}
374
+ }
375
+ commits [repo ] = append (commits [repo ], commit {
369
376
Hash : parts [0 ],
370
377
Date : committedTime ,
371
378
Author : parts [2 ],
@@ -375,10 +382,43 @@ func detectNewCommits(ctx context.Context, logger *logrus.Entry, stagingDir, cen
375
382
}
376
383
}
377
384
}
378
- sort .Slice (commits , func (i , j int ) bool {
379
- return commits [i ].Date .Before (commits [j ].Date )
380
- })
381
- return commits , nil
385
+ // we would like to intertwine the commits from each upstream repository by date, while
386
+ // keeping the order of commits from any one repository in the order they were committed in
387
+ var orderedCommits []commit
388
+ indices := map [string ]int {}
389
+ for repo := range commits {
390
+ indices [repo ] = 0
391
+ }
392
+ for {
393
+ // find which repo's commit stack we should pop off to get the next earliest commit
394
+ nextTime := time .Now ()
395
+ var nextRepo string
396
+ for repo , index := range indices {
397
+ if commits [repo ][index ].Date .Before (nextTime ) {
398
+ nextTime = commits [repo ][index ].Date
399
+ nextRepo = repo
400
+ }
401
+ }
402
+
403
+ // pop the commit, add it to our list and do housekeeping for our index records
404
+ orderedCommits = append (orderedCommits , commits [nextRepo ][indices [nextRepo ]])
405
+ if indices [nextRepo ] == len (commits [nextRepo ])- 1 {
406
+ delete (indices , nextRepo )
407
+ } else {
408
+ indices [nextRepo ] += 1
409
+ }
410
+
411
+ if len (indices ) == 0 {
412
+ break
413
+ }
414
+ }
415
+
416
+ // our ordered list is descending, but we need to cherry-pick from the oldest first
417
+ var reversedCommits []commit
418
+ for i := range orderedCommits {
419
+ reversedCommits = append (reversedCommits , orderedCommits [len (orderedCommits )- i - 1 ])
420
+ }
421
+ return reversedCommits , nil
382
422
}
383
423
384
424
func isCommitMissing (ctx context.Context , logger * logrus.Entry , stagingDir string , c commit ) (bool , error ) {
0 commit comments