Skip to content

Commit 146cb08

Browse files
chore: do rust more idiomatically, clean up language still referencing the non-rollback behaviour
1 parent fa8c852 commit 146cb08

File tree

4 files changed

+49
-48
lines changed

4 files changed

+49
-48
lines changed

src/cmd/rebase.rs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -96,12 +96,15 @@ fn make() -> clap::Command {
9696
.long_help(
9797
"Execute the given shell command after each patch is successfully \
9898
applied during the rebase operation. If the command fails (exits with \
99-
non-zero status), the rebase will halt.\n\
99+
non-zero status), the entire transaction is rolled back and no patches \
100+
remain applied.\n\
100101
\n\
101102
This option may be specified multiple times to run multiple commands \
102103
in sequence after each patch.\n\
103104
\n\
104-
This is similar to `git rebase --exec`.",
105+
This is similar to `git rebase --exec`, but note that stgit rolls back \
106+
the entire operation on failure rather than leaving you at the failing \
107+
point.",
105108
)
106109
.action(clap::ArgAction::Append)
107110
.value_name("cmd")
@@ -256,9 +259,9 @@ fn run(matches: &ArgMatches) -> Result<()> {
256259
} else if !matches.get_flag("nopush") {
257260
stack.check_head_top_mismatch()?;
258261
let check_merged = matches.get_flag("merged");
259-
let exec_cmds: Vec<String> = matches
262+
let exec_cmds: Vec<&str> = matches
260263
.get_many::<String>("exec")
261-
.map(|vals| vals.cloned().collect())
264+
.map(|vals| vals.map(String::as_str).collect())
262265
.unwrap_or_default();
263266

264267
stack

src/stack/transaction/mod.rs

Lines changed: 30 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -969,49 +969,36 @@ impl<'repo> StackTransaction<'repo> {
969969
where
970970
P: AsRef<PatchName>,
971971
{
972-
let stupid = self.stack.repo.stupid();
973-
stupid.with_temp_index(|stupid_temp| {
974-
let mut temp_index_tree_id: Option<gix::ObjectId> = None;
975-
976-
let merged = if check_merged {
977-
Some(self.check_merged(patchnames, stupid_temp, &mut temp_index_tree_id)?)
978-
} else {
979-
None
980-
};
981-
982-
for (i, patchname) in patchnames.iter().enumerate() {
983-
let patchname = patchname.as_ref();
984-
let is_last = i + 1 == patchnames.len();
985-
let already_merged = merged
986-
.as_ref()
987-
.is_some_and(|merged| merged.contains(&patchname));
988-
self.push_patch(
989-
patchname,
990-
already_merged,
991-
is_last,
992-
stupid_temp,
993-
&mut temp_index_tree_id,
994-
)?;
995-
}
996-
997-
Ok(())
998-
})
972+
self.push_patches_impl(patchnames, check_merged, &[])
999973
}
1000974

1001975
/// Push unapplied patches, running exec commands after each successful push.
1002976
///
1003-
/// This is similar to `push_patches`, but after each patch is pushed successfully,
1004-
/// all provided exec commands are run in sequence. If any exec command fails, the
1005-
/// entire transaction is rolled back (no patches remain applied).
977+
/// After each patch is successfully pushed, all provided exec commands are run
978+
/// in sequence. If any exec command fails, the entire transaction is rolled back
979+
/// (no patches remain applied).
1006980
///
1007981
/// This supports the `stg rebase --exec` functionality. Note that this differs from
1008-
/// `git rebase --exec` which leaves you at the failing point; stgit's behavior is
1009-
/// safer and consistent with how stgit transactions work.
982+
/// `git rebase --exec` which leaves you at the failing point; stgit's rollback
983+
/// behavior is safer and consistent with how stgit transactions work.
1010984
pub(crate) fn push_patches_with_exec<P>(
1011985
&mut self,
1012986
patchnames: &[P],
1013987
check_merged: bool,
1014-
exec_cmds: &[String],
988+
exec_cmds: &[&str],
989+
) -> Result<()>
990+
where
991+
P: AsRef<PatchName>,
992+
{
993+
self.push_patches_impl(patchnames, check_merged, exec_cmds)
994+
}
995+
996+
/// Core implementation for pushing patches with optional exec commands.
997+
fn push_patches_impl<P>(
998+
&mut self,
999+
patchnames: &[P],
1000+
check_merged: bool,
1001+
exec_cmds: &[&str],
10151002
) -> Result<()>
10161003
where
10171004
P: AsRef<PatchName>,
@@ -1028,22 +1015,26 @@ impl<'repo> StackTransaction<'repo> {
10281015

10291016
for (i, patchname) in patchnames.iter().enumerate() {
10301017
let patchname = patchname.as_ref();
1031-
let is_last = i + 1 == patchnames.len() && exec_cmds.is_empty();
1018+
// When exec commands are provided, we can't optimize the final checkout
1019+
// because the commands may modify the working tree. Only treat the last
1020+
// patch as "last" (enabling checkout optimization) when there are no
1021+
// exec commands.
1022+
let should_optimize_final_checkout =
1023+
i + 1 == patchnames.len() && exec_cmds.is_empty();
10321024
let already_merged = merged
10331025
.as_ref()
10341026
.is_some_and(|merged| merged.contains(&patchname));
10351027
self.push_patch(
10361028
patchname,
10371029
already_merged,
1038-
is_last,
1030+
should_optimize_final_checkout,
10391031
stupid_temp,
10401032
&mut temp_index_tree_id,
10411033
)?;
10421034

1043-
// Run exec commands after each successful push
1044-
for exec_cmd in exec_cmds {
1045-
self.ui.print_exec(exec_cmd)?;
1046-
stupid.exec_cmd(exec_cmd)?;
1035+
for cmd in exec_cmds {
1036+
self.ui.print_exec(cmd)?;
1037+
stupid.exec_cmd(cmd)?;
10471038
}
10481039
}
10491040

src/stupid/context.rs

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1335,14 +1335,21 @@ impl StupidContext<'_, '_> {
13351335
.stdout(Stdio::inherit())
13361336
.stderr(Stdio::inherit())
13371337
.status()
1338-
.with_context(|| format!("could not execute `{cmd}`"))?;
1338+
.with_context(|| format!("could not execute command: {cmd}"))?;
13391339

13401340
if status.success() {
13411341
Ok(())
13421342
} else if let Some(code) = status.code() {
1343-
Err(anyhow!("`{cmd}` exited with code {code}"))
1343+
Err(anyhow!("command exited with code {code}: {cmd}"))
13441344
} else {
1345-
Err(anyhow!("`{cmd}` failed"))
1345+
#[cfg(unix)]
1346+
{
1347+
use std::os::unix::process::ExitStatusExt;
1348+
if let Some(signal) = status.signal() {
1349+
return Err(anyhow!("command killed by signal {signal}: {cmd}"));
1350+
}
1351+
}
1352+
Err(anyhow!("command failed: {cmd}"))
13461353
}
13471354
}
13481355

t/t2206-rebase-exec.sh

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,10 @@ test_expect_success 'Rebase with multiple --exec options' '
3838
test $(wc -l <exec2.log) = 3
3939
'
4040

41-
test_expect_success 'Rebase with --exec halts on command failure and rolls back' '
41+
test_expect_success 'Rebase with --exec rolls back on command failure' '
4242
rm -f exec.log out &&
4343
test_must_fail stg rebase --exec "echo EXEC >>exec.log && false" master >out 2>&1 &&
44-
grep -q "exited with code" out &&
44+
grep -q "command exited with code" out &&
4545
test_line_count = 1 exec.log &&
4646
test "$(stg series --applied -c)" = "0"
4747
'

0 commit comments

Comments
 (0)