Skip to content

Commit 3900942

Browse files
feat(move): add --dry-run to support testing in-memory rebases
This is useful to finding the exact commit that created a conflict for an out of date stack: ``` TEST_COMMAND='git move -s "current($OLD_COMMIT)" -d $BRANCHLESS_TEST_COMMIT --in-memory --dry-run' git test run \ $MERGE_BASE..main \ --exec "$TEST_COMMAND" --jobs 8 \ --strategy worktree --search binary ``` This was able to search through 372 commits in only 40 iterations, finding the exact commit where the conflict was created.
1 parent d707b11 commit 3900942

File tree

14 files changed

+167
-7
lines changed

14 files changed

+167
-7
lines changed

git-branchless-lib/src/core/rewrite/execute.rs

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -498,6 +498,7 @@ mod in_memory {
498498
preserve_timestamps,
499499
force_in_memory: _,
500500
force_on_disk: _,
501+
dry_run: _,
501502
resolve_merge_conflicts: _, // May be needed once we can resolve merge conflicts in memory.
502503
check_out_commit_options: _, // Caller is responsible for checking out to new HEAD.
503504
} = options;
@@ -909,6 +910,7 @@ mod in_memory {
909910
preserve_timestamps: _,
910911
force_in_memory: _,
911912
force_on_disk: _,
913+
dry_run: _,
912914
resolve_merge_conflicts: _,
913915
check_out_commit_options,
914916
} = options;
@@ -994,6 +996,7 @@ mod on_disk {
994996
preserve_timestamps,
995997
force_in_memory: _,
996998
force_on_disk: _,
999+
dry_run: _,
9971000
resolve_merge_conflicts: _,
9981001
check_out_commit_options: _, // Checkout happens after rebase has concluded.
9991002
} = options;
@@ -1170,6 +1173,7 @@ mod on_disk {
11701173
preserve_timestamps: _,
11711174
force_in_memory: _,
11721175
force_on_disk: _,
1176+
dry_run: _,
11731177
resolve_merge_conflicts: _,
11741178
check_out_commit_options: _, // Checkout happens after rebase has concluded.
11751179
} = options;
@@ -1210,6 +1214,10 @@ pub struct ExecuteRebasePlanOptions {
12101214
/// Force an on-disk rebase (as opposed to an in-memory rebase).
12111215
pub force_on_disk: bool,
12121216

1217+
/// When attempting an in-memory rebase, only report success or failure,
1218+
/// discarding the resulting changes.
1219+
pub dry_run: bool,
1220+
12131221
/// Whether or not an attempt should be made to resolve merge conflicts,
12141222
/// rather than failing-fast.
12151223
pub resolve_merge_conflicts: bool,
@@ -1228,7 +1236,10 @@ pub enum ExecuteRebasePlanResult {
12281236
rewritten_oids: Option<HashMap<NonZeroOid, MaybeZeroOid>>,
12291237
},
12301238

1231-
/// The rebase operation encounter a failure to merge, and it was not
1239+
/// The rebase operation is viable, but was not executed to completion.
1240+
WouldSucceed,
1241+
1242+
/// The rebase operation encountered a failure to merge, and it was not
12321243
/// requested to try to resolve it.
12331244
DeclinedToMerge {
12341245
/// Information about the merge failure that occurred.
@@ -1259,6 +1270,7 @@ pub fn execute_rebase_plan(
12591270
preserve_timestamps: _,
12601271
force_in_memory,
12611272
force_on_disk,
1273+
dry_run,
12621274
resolve_merge_conflicts,
12631275
check_out_commit_options: _,
12641276
} = options;
@@ -1277,6 +1289,14 @@ pub fn execute_rebase_plan(
12771289
rewritten_oids,
12781290
new_head_oid,
12791291
} => {
1292+
if *dry_run {
1293+
writeln!(
1294+
effects.get_output_stream(),
1295+
"In-memory rebase would succeed."
1296+
)?;
1297+
return Ok(ExecuteRebasePlanResult::WouldSucceed);
1298+
}
1299+
12801300
// Ignore the return code, as it probably indicates that the
12811301
// checkout failed (which might happen if the user has changes
12821302
// which don't merge cleanly). The user can resolve that

git-branchless-lib/tests/test_rewrite_plan.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -759,6 +759,7 @@ fn create_and_execute_plan(
759759
preserve_timestamps: false,
760760
force_in_memory: false,
761761
force_on_disk: false,
762+
dry_run: false,
762763
resolve_merge_conflicts: true,
763764
check_out_commit_options: CheckOutCommitOptions {
764765
additional_args: Default::default(),

git-branchless-move/src/lib.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ pub fn r#move(
7474
move_options: &MoveOptions,
7575
fixup: bool,
7676
insert: bool,
77+
dry_run: bool,
7778
) -> EyreExitOr<()> {
7879
let sources_provided = !sources.is_empty();
7980
let bases_provided = !bases.is_empty();
@@ -484,6 +485,7 @@ pub fn r#move(
484485
preserve_timestamps: get_restack_preserve_timestamps(&repo)?,
485486
force_in_memory,
486487
force_on_disk,
488+
dry_run,
487489
resolve_merge_conflicts,
488490
check_out_commit_options: Default::default(),
489491
};
@@ -505,6 +507,15 @@ pub fn r#move(
505507
match result {
506508
ExecuteRebasePlanResult::Succeeded { rewritten_oids: _ } => Ok(Ok(())),
507509

510+
ExecuteRebasePlanResult::WouldSucceed if dry_run => {
511+
writeln!(effects.get_output_stream(), "(This was a dry-run; no commits were moved. Re-run without --dry-run to actually move commits.)")?;
512+
Ok(Ok(()))
513+
}
514+
515+
ExecuteRebasePlanResult::WouldSucceed => {
516+
unreachable!("WouldSucceed should only apply to dry runs")
517+
}
518+
508519
ExecuteRebasePlanResult::DeclinedToMerge { failed_merge_info } => {
509520
failed_merge_info.describe(effects, &repo, MergeConflictRemediation::Retry)?;
510521
Ok(Err(ExitCode(1)))

git-branchless-opts/src/lib.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -550,6 +550,10 @@ pub enum Command {
550550
/// Only supported if the moved subtree has a single head.
551551
#[clap(action, short = 'I', long = "insert")]
552552
insert: bool,
553+
554+
/// Test whether an in-memory rebase would succeed.
555+
#[clap(action, long = "dry-run", conflicts_with = "force_on_disk")]
556+
dry_run: bool,
553557
},
554558

555559
/// Move to a later commit in the current stack.

git-branchless-record/src/lib.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -554,6 +554,7 @@ To proceed anyways, run: git move -f -s 'siblings(.)",
554554
preserve_timestamps: get_restack_preserve_timestamps(&repo)?,
555555
force_in_memory: true,
556556
force_on_disk: false,
557+
dry_run: false,
557558
resolve_merge_conflicts: false,
558559
check_out_commit_options: Default::default(),
559560
};
@@ -566,7 +567,8 @@ To proceed anyways, run: git move -f -s 'siblings(.)",
566567
&execute_options,
567568
)?;
568569
match result {
569-
ExecuteRebasePlanResult::Succeeded { rewritten_oids: _ } => Ok(Ok(())),
570+
ExecuteRebasePlanResult::Succeeded { rewritten_oids: _ }
571+
| ExecuteRebasePlanResult::WouldSucceed => Ok(Ok(())),
570572
ExecuteRebasePlanResult::DeclinedToMerge { failed_merge_info } => {
571573
failed_merge_info.describe(effects, &repo, MergeConflictRemediation::Insert)?;
572574
Ok(Ok(()))

git-branchless-reword/src/lib.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -289,6 +289,7 @@ pub fn reword(
289289
preserve_timestamps: get_restack_preserve_timestamps(&repo)?,
290290
force_in_memory: true,
291291
force_on_disk: false,
292+
dry_run: false,
292293
resolve_merge_conflicts: false,
293294
check_out_commit_options: CheckOutCommitOptions {
294295
additional_args: Default::default(),
@@ -315,7 +316,8 @@ pub fn reword(
315316
}
316317
ExecuteRebasePlanResult::Succeeded {
317318
rewritten_oids: None,
318-
} => Ok(Ok(())),
319+
}
320+
| ExecuteRebasePlanResult::WouldSucceed => Ok(Ok(())),
319321
ExecuteRebasePlanResult::DeclinedToMerge {
320322
failed_merge_info: _,
321323
} => {

git-branchless-submit/src/phabricator.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -354,6 +354,7 @@ impl Forge for PhabricatorForge<'_> {
354354
preserve_timestamps: true,
355355
force_in_memory: true,
356356
force_on_disk: false,
357+
dry_run: false,
357358
resolve_merge_conflicts: false,
358359
check_out_commit_options: CheckOutCommitOptions {
359360
render_smartlog: false,
@@ -485,7 +486,8 @@ Differential Revision: https://phabricator.example.com/D000$(git rev-list --coun
485486
} => rewritten_oids,
486487
ExecuteRebasePlanResult::Succeeded {
487488
rewritten_oids: None,
488-
} => {
489+
}
490+
| ExecuteRebasePlanResult::WouldSucceed => {
489491
warn!("No rewritten commit OIDs were produced by rebase plan execution");
490492
Default::default()
491493
}
@@ -602,6 +604,7 @@ Differential Revision: https://phabricator.example.com/D000$(git rev-list --coun
602604
preserve_timestamps: true,
603605
force_in_memory: true,
604606
force_on_disk: false,
607+
dry_run: false,
605608
resolve_merge_conflicts: false,
606609
check_out_commit_options: CheckOutCommitOptions {
607610
render_smartlog: false,

git-branchless-test/src/lib.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -411,6 +411,7 @@ BUG: Expected resolved_interactive ({resolved_interactive:?}) to match interacti
411411
preserve_timestamps: get_restack_preserve_timestamps(repo)?,
412412
force_in_memory,
413413
force_on_disk: *force_on_disk,
414+
dry_run: false,
414415
resolve_merge_conflicts: *resolve_merge_conflicts,
415416
check_out_commit_options: CheckOutCommitOptions {
416417
render_smartlog: false,
@@ -726,14 +727,16 @@ fn set_abort_trap(
726727
preserve_timestamps: true,
727728
force_in_memory: false,
728729
force_on_disk: true,
730+
dry_run: false,
729731
resolve_merge_conflicts: false,
730732
check_out_commit_options: CheckOutCommitOptions {
731733
render_smartlog: false,
732734
..Default::default()
733735
},
734736
},
735737
)? {
736-
ExecuteRebasePlanResult::Succeeded { rewritten_oids: _ } => {
738+
ExecuteRebasePlanResult::Succeeded { rewritten_oids: _ }
739+
| ExecuteRebasePlanResult::WouldSucceed => {
737740
// Do nothing.
738741
}
739742
ExecuteRebasePlanResult::DeclinedToMerge { failed_merge_info } => {
@@ -2091,6 +2094,7 @@ fn apply_fixes(
20912094
execute_options,
20922095
)? {
20932096
ExecuteRebasePlanResult::Succeeded { rewritten_oids } => rewritten_oids,
2097+
ExecuteRebasePlanResult::WouldSucceed => return Ok(Ok(())),
20942098
ExecuteRebasePlanResult::DeclinedToMerge { failed_merge_info } => {
20952099
writeln!(effects.get_output_stream(), "BUG: encountered merge conflicts during git test fix, but we should not be applying any patches: {failed_merge_info:?}")?;
20962100
return Ok(Err(ExitCode(1)));

git-branchless/src/commands/amend.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,7 @@ pub fn amend(
294294
event_tx_id,
295295
force_in_memory: move_options.force_in_memory,
296296
force_on_disk: move_options.force_on_disk,
297+
dry_run: false,
297298
preserve_timestamps: get_restack_preserve_timestamps(&repo)?,
298299
resolve_merge_conflicts: move_options.resolve_merge_conflicts,
299300
check_out_commit_options: CheckOutCommitOptions {
@@ -313,7 +314,8 @@ pub fn amend(
313314
)? {
314315
ExecuteRebasePlanResult::Succeeded {
315316
rewritten_oids: None,
316-
} => {}
317+
}
318+
| ExecuteRebasePlanResult::WouldSucceed => {}
317319

318320
ExecuteRebasePlanResult::Succeeded {
319321
rewritten_oids: Some(rewritten_oids),

git-branchless/src/commands/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ fn command_main(ctx: CommandContext, opts: Opts) -> EyreExitOr<()> {
9696
move_options,
9797
fixup,
9898
insert,
99+
dry_run,
99100
} => git_branchless_move::r#move(
100101
&effects,
101102
&git_run_info,
@@ -107,6 +108,7 @@ fn command_main(ctx: CommandContext, opts: Opts) -> EyreExitOr<()> {
107108
&move_options,
108109
fixup,
109110
insert,
111+
dry_run,
110112
)?,
111113

112114
Command::Next {

0 commit comments

Comments
 (0)