11use std:: collections:: HashMap ;
22use 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 _} ;
56use std:: ops:: Not ;
67use std:: path:: PathBuf ;
78use std:: time:: Duration ;
@@ -169,7 +170,8 @@ impl Command {
169170 | Command :: Toolchain { .. }
170171 | Command :: Bench { .. }
171172 | Command :: RustcPull { .. }
172- | Command :: RustcPush { .. } => { }
173+ | Command :: RustcPush { .. }
174+ | Command :: Squash => { }
173175 }
174176 // Then run the actual command.
175177 match self {
@@ -188,6 +190,7 @@ impl Command {
188190 Command :: Toolchain { flags } => Self :: toolchain ( flags) ,
189191 Command :: RustcPull { commit } => Self :: rustc_pull ( commit. clone ( ) ) ,
190192 Command :: RustcPush { github_user, branch } => Self :: rustc_push ( github_user, branch) ,
193+ Command :: Squash => Self :: squash ( ) ,
191194 }
192195 }
193196
@@ -383,6 +386,72 @@ impl Command {
383386 Ok ( ( ) )
384387 }
385388
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+
386455 fn bench (
387456 target : Option < String > ,
388457 no_install : bool ,
0 commit comments