@@ -355,51 +355,48 @@ impl RepositoryClient<'_> {
355
355
start : & str ,
356
356
path : & str ,
357
357
) -> anyhow:: Result < FullCommitData > {
358
- self . start_new_request ( ) ?;
359
- self . client . get ( true ) ?;
360
- self . client . url ( & format ! (
361
- "https://api.github.com/repos/{repo}/commits/{start}" ,
362
- repo = self . repo
363
- ) ) ?;
364
- let resolved_start = self
365
- . client
366
- . without_body ( )
367
- . send_with_response :: < CommitData > ( ) ?
368
- . sha ;
369
- for page in 1 ..20 {
358
+ const MAX_COMMITS : usize = 200 ;
359
+ const BORS_EMAIL : & str =
"[email protected] " ;
360
+
361
+ let mut commit = start. to_string ( ) ;
362
+ let mut scanned_commits = 0 ;
363
+ for _ in 0 ..MAX_COMMITS {
364
+ scanned_commits += 1 ;
365
+
370
366
self . start_new_request ( ) ?;
371
367
self . client . get ( true ) ?;
372
368
self . client . url ( & format ! (
373
- "https://api.github.com/repos/{repo}/commits?sha={resolved_start}&per_page=100&page={page}&author=bors " ,
369
+ "https://api.github.com/repos/{repo}/commits/{commit} " ,
374
370
repo = self . repo
375
371
) ) ?;
376
- let commits = self
372
+
373
+ let commit_data = self
377
374
. client
378
375
. without_body ( )
379
- . send_with_response :: < Vec < CommitData > > ( ) ?;
380
- for commit in commits {
381
- self . start_new_request ( ) ?;
382
- self . client . get ( true ) ?;
383
- self . client . url ( & format ! (
384
- "https://api.github.com/repos/{repo}/commits/{sha}" ,
385
- repo = self . repo,
386
- sha = commit. sha,
387
- ) ) ?;
388
- let commit = self
389
- . client
390
- . without_body ( )
391
- . send_with_response :: < FullCommitData > ( ) ?;
392
- if commit. files . iter ( ) . any ( |f| f. filename == path) {
393
- return Ok ( commit) ;
394
- }
376
+ . send_with_response :: < FullCommitData > ( ) ?;
377
+
378
+ // We pick the *first* parent commit to continue walking through the commit graph. In
379
+ // a merge commit, the first parent is always the merge base (i.e. the master branch),
380
+ // while the second parent is always the branch being merged in.
381
+ //
382
+ // This is important because we only want bors merge commits for branches merged into
383
+ // Rust's master branch, not bors merge commits in subtrees being pulled in.
384
+ let Some ( parent) = & commit_data. parents . first ( ) else {
385
+ break ;
386
+ } ;
387
+ commit. clone_from ( & parent. sha ) ;
388
+
389
+ if commit_data. commit . author . email != BORS_EMAIL {
390
+ continue ;
391
+ }
392
+ if commit_data. files . iter ( ) . any ( |f| f. filename == path) {
393
+ return Ok ( commit_data) ;
395
394
}
396
395
}
397
396
398
397
anyhow:: bail!(
399
- "Failed to find bors commit touching {:?} in start={} ancestors (scanned {} commits)" ,
400
- path,
401
- start,
402
- 20 * 100 ,
398
+ "Failed to find bors commit touching {path:?} in \
399
+ start={start} ancestors (scanned {scanned_commits} commits)"
403
400
) ;
404
401
}
405
402
@@ -511,15 +508,21 @@ pub(crate) struct CreateTag<'a> {
511
508
512
509
#[ derive( serde:: Deserialize ) ]
513
510
pub ( crate ) struct FullCommitData {
514
- #[ allow( unused) ]
511
+ #[ cfg_attr ( not ( test ) , allow( unused) ) ]
515
512
pub ( crate ) sha : String ,
516
513
pub ( crate ) parents : Vec < CommitParent > ,
514
+ pub ( crate ) commit : CommitCommit ,
517
515
pub ( crate ) files : Vec < CommitFile > ,
518
516
}
519
517
520
518
#[ derive( serde:: Deserialize ) ]
521
- pub ( crate ) struct CommitData {
522
- pub ( crate ) sha : String ,
519
+ pub ( crate ) struct CommitCommit {
520
+ pub ( crate ) author : CommitAuthor ,
521
+ }
522
+
523
+ #[ derive( serde:: Deserialize ) ]
524
+ pub ( crate ) struct CommitAuthor {
525
+ pub ( crate ) email : String ,
523
526
}
524
527
525
528
#[ derive( serde:: Deserialize ) ]
0 commit comments