Skip to content

Commit ac0afe4

Browse files
committed
add ./miri squash
1 parent 37b677a commit ac0afe4

File tree

3 files changed

+84
-10
lines changed

3 files changed

+84
-10
lines changed

CONTRIBUTING.md

+4-6
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,10 @@ When you get a review, please take care of the requested changes in new commits.
1919
existing commits. Generally avoid force-pushing. The only time you should force push is when there
2020
is a conflict with the master branch (in that case you should rebase across master, not merge), and
2121
all the way at the end of the review process when the reviewer tells you that the PR is done and you
22-
should squash the commits. For the latter case, use `git rebase --keep-base ...` to squash without
23-
changing the base commit your PR branches off of. Use your own judgment and the reviewer's guidance
24-
to decide whether the PR should be squashed into a single commit or multiple logically separate
25-
commits. (All this is to work around the fact that Github is quite bad at dealing with force pushes
26-
and does not support `git range-diff`. Maybe one day Github will be good at git and then life can
27-
become easier.)
22+
should squash the commits. If you are unsure how to use `git rebase` to squash commits, use `./miri
23+
squash` which automates the process but leaves little room for customization. (All this is to work
24+
around the fact that Github is quite bad at dealing with force pushes and does not support `git
25+
range-diff`. Maybe one day Github will be good at git and then life can become easier.)
2826

2927
Most PRs bounce back and forth between the reviewer and the author several times, so it is good to
3028
keep track of who is expected to take the next step. We are using the `S-waiting-for-review` and

miri-script/src/commands.rs

+72-3
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
use std::collections::HashMap;
22
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 _};
56
use std::ops::Not;
67
use std::path::PathBuf;
78
use 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,

miri-script/src/main.rs

+8-1
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,8 @@ pub enum Command {
133133
#[arg(default_value = "miri-sync")]
134134
branch: String,
135135
},
136+
/// Squash the commits of the current feature branch into one.
137+
Squash,
136138
}
137139

138140
impl Command {
@@ -154,7 +156,7 @@ impl Command {
154156
flags.extend(remainder);
155157
Ok(())
156158
}
157-
Self::Bench { .. } | Self::RustcPull { .. } | Self::RustcPush { .. } =>
159+
Self::Bench { .. } | Self::RustcPull { .. } | Self::RustcPush { .. } | Self::Squash =>
158160
bail!("unexpected \"--\" found in arguments"),
159161
}
160162
}
@@ -170,6 +172,11 @@ pub struct Cli {
170172
}
171173

172174
fn main() -> Result<()> {
175+
// If we are invoked as the git sequence editor, jump to that logic.
176+
if !std::env::var_os("MIRI_SCRIPT_IS_GIT_SEQUENCE_EDITOR").unwrap_or_default().is_empty() {
177+
return Command::squash_sequence_editor();
178+
}
179+
173180
// Split the arguments into the part before the `--` and the part after.
174181
// The `--` itself ends up in the second part.
175182
let miri_args: Vec<_> = std::env::args().take_while(|x| *x != "--").collect();

0 commit comments

Comments
 (0)