1
1
use std:: collections:: HashMap ;
2
2
use std:: ffi:: { OsStr , OsString } ;
3
- use std:: fs:: File ;
4
- use std:: io:: { BufReader , BufWriter , Write } ;
3
+ use std:: fmt:: Write as _;
4
+ use std:: fs:: { self , File } ;
5
+ use std:: io:: { self , BufRead , BufReader , BufWriter , Write as _} ;
5
6
use std:: ops:: Not ;
6
7
use std:: path:: PathBuf ;
7
8
use std:: time:: Duration ;
@@ -169,7 +170,8 @@ impl Command {
169
170
| Command :: Toolchain { .. }
170
171
| Command :: Bench { .. }
171
172
| Command :: RustcPull { .. }
172
- | Command :: RustcPush { .. } => { }
173
+ | Command :: RustcPush { .. }
174
+ | Command :: Squash => { }
173
175
}
174
176
// Then run the actual command.
175
177
match self {
@@ -188,6 +190,7 @@ impl Command {
188
190
Command :: Toolchain { flags } => Self :: toolchain ( flags) ,
189
191
Command :: RustcPull { commit } => Self :: rustc_pull ( commit. clone ( ) ) ,
190
192
Command :: RustcPush { github_user, branch } => Self :: rustc_push ( github_user, branch) ,
193
+ Command :: Squash => Self :: squash ( ) ,
191
194
}
192
195
}
193
196
@@ -383,6 +386,72 @@ impl Command {
383
386
Ok ( ( ) )
384
387
}
385
388
389
+ fn squash ( ) -> Result < ( ) > {
390
+ let sh = Shell :: new ( ) ?;
391
+ sh. change_dir ( miri_dir ( ) ?) ;
392
+ // Figure out base wrt latest upstream master.
393
+ // (We can't trust any of the local ones, they can all be outdated.)
394
+ let origin_master = {
395
+ cmd ! ( sh, "git fetch https://github.com/rust-lang/miri/" )
396
+ . quiet ( )
397
+ . ignore_stdout ( )
398
+ . ignore_stderr ( )
399
+ . run ( ) ?;
400
+ cmd ! ( sh, "git rev-parse FETCH_HEAD" ) . read ( ) ?
401
+ } ;
402
+ let base = cmd ! ( sh, "git merge-base HEAD {origin_master}" ) . read ( ) ?;
403
+ // Rebase onto that, setting ourselves as the sequence editor so that we can edit the sequence programmatically.
404
+ // We want to forward the host stdin so apparently we cannot use `cmd!`.
405
+ let mut cmd = process:: Command :: new ( "git" ) ;
406
+ cmd. arg ( "rebase" ) . arg ( & base) . arg ( "--interactive" ) ;
407
+ cmd. env ( "GIT_SEQUENCE_EDITOR" , env:: current_exe ( ) ?) ;
408
+ cmd. env ( "MIRI_SCRIPT_IS_GIT_SEQUENCE_EDITOR" , "1" ) ;
409
+ cmd. current_dir ( sh. current_dir ( ) ) ;
410
+ let result = cmd. status ( ) ?;
411
+ if !result. success ( ) {
412
+ bail ! ( "`git rebase` failed" ) ;
413
+ }
414
+ Ok ( ( ) )
415
+ }
416
+
417
+ pub fn squash_sequence_editor ( ) -> Result < ( ) > {
418
+ let sequence_file = env:: args ( ) . nth ( 1 ) . expect ( "git should pass us a filename" ) ;
419
+ if sequence_file == "fmt" {
420
+ // This is probably us being called as a git hook as part of the rebase. Let's just
421
+ // ignore this. Sadly `git rebase` does not have a flag to skip running hooks.
422
+ return Ok ( ( ) ) ;
423
+ }
424
+ // Read the provided sequence and adjust it.
425
+ let rebase_sequence = {
426
+ let mut rebase_sequence = String :: new ( ) ;
427
+ let file = fs:: File :: open ( & sequence_file) . with_context ( || {
428
+ format ! ( "failed to read rebase sequence from {sequence_file:?}" )
429
+ } ) ?;
430
+ let file = io:: BufReader :: new ( file) ;
431
+ for line in file. lines ( ) {
432
+ let line = line?;
433
+ // The first line is left unchanged.
434
+ if rebase_sequence. is_empty ( ) {
435
+ writeln ! ( rebase_sequence, "{line}" ) . unwrap ( ) ;
436
+ continue ;
437
+ }
438
+ // If this is a "pick" like, make it "squash".
439
+ if let Some ( rest) = line. strip_prefix ( "pick " ) {
440
+ writeln ! ( rebase_sequence, "squash {rest}" ) . unwrap ( ) ;
441
+ continue ;
442
+ }
443
+ // We've reached the end of the relevant part of the sequence, and we can stop.
444
+ break ;
445
+ }
446
+ rebase_sequence
447
+ } ;
448
+ // Write out the adjusted sequence.
449
+ fs:: write ( & sequence_file, rebase_sequence) . with_context ( || {
450
+ format ! ( "failed to write adjusted rebase sequence to {sequence_file:?}" )
451
+ } ) ?;
452
+ Ok ( ( ) )
453
+ }
454
+
386
455
fn bench (
387
456
target : Option < String > ,
388
457
no_install : bool ,
0 commit comments