Skip to content

Commit 89508db

Browse files
committed
squash: support --restore-descendants
Sometimes it is convenient to be able to make changes to a commit in the past without affecting descendants -- I already have a workflow that tracks the commits of all children, makes the changes, and then restores manually, but that performs a lot more rebases than necessary, which adds up when history is long. Supporting `--restore-descendants` for `jj squash` helps this workflow greatly.
1 parent 744a008 commit 89508db

5 files changed

Lines changed: 634 additions & 18 deletions

File tree

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -298,6 +298,10 @@ Thanks to the people who made this release happen!
298298
* `JJ_PAGER` can now override the `ui.pager` config, matching `JJ_EDITOR` for
299299
callers that need a jj-specific environment override.
300300

301+
* `jj squash` now accepts a `--restore-descendants` flag, matching `jj abandon`,
302+
`jj diffedit`, and `jj restore`. When used, descendants of the squashed
303+
commits keep their original content rather than being 3-way merged.
304+
301305
### Fixed bugs
302306

303307
* Improving consistency with `git` handling of `.gitignore`, including `/`

cli/src/commands/squash.rs

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,10 @@ pub(crate) struct SquashArgs {
176176
/// The source revision will not be abandoned
177177
#[arg(long, short)]
178178
keep_emptied: bool,
179+
180+
/// Preserve the content (not the diff) when rebasing descendants
181+
#[arg(long)]
182+
restore_descendants: bool,
179183
}
180184

181185
#[instrument(skip_all)]
@@ -293,7 +297,11 @@ pub(crate) async fn cmd_squash(
293297
.collect(),
294298
);
295299
}
296-
let new_commit = rewriter.rebase().await?.write().await?;
300+
let new_commit = if args.restore_descendants {
301+
rewriter.reparent().write().await?
302+
} else {
303+
rewriter.rebase().await?.write().await?
304+
};
297305
rewritten.insert(old_commit_id, new_commit);
298306
num_rebased += 1;
299307
Ok(())
@@ -307,6 +315,12 @@ pub(crate) async fn cmd_squash(
307315
commit
308316
};
309317

318+
let descendants_msg_suffix = if args.restore_descendants {
319+
" (while preserving their content)"
320+
} else {
321+
""
322+
};
323+
310324
let fileset_expression = tx
311325
.base_workspace_helper()
312326
.parse_file_patterns(ui, &args.paths)?;
@@ -332,6 +346,7 @@ pub(crate) async fn cmd_squash(
332346
&source_commits,
333347
&destination,
334348
args.keep_emptied,
349+
args.restore_descendants,
335350
)
336351
.await?
337352
{
@@ -391,15 +406,22 @@ pub(crate) async fn cmd_squash(
391406
);
392407
}
393408
let commit = commit_builder.write(tx.repo_mut()).await?;
394-
let num_rebased = tx.repo_mut().rebase_descendants().await?;
409+
let num_rebased = if args.restore_descendants {
410+
tx.repo_mut().reparent_descendants().await?
411+
} else {
412+
tx.repo_mut().rebase_descendants().await?
413+
};
395414
if let Some(mut formatter) = ui.status_formatter() {
396415
if insert_destination_commit {
397416
write!(formatter, "Created new commit ")?;
398417
tx.write_commit_summary(formatter.as_mut(), &commit)?;
399418
writeln!(formatter)?;
400419
}
401420
if num_rebased > 0 {
402-
writeln!(formatter, "Rebased {num_rebased} descendant commits.")?;
421+
writeln!(
422+
formatter,
423+
"Rebased {num_rebased} descendant commits{descendants_msg_suffix}."
424+
)?;
403425
}
404426
}
405427
} else {
@@ -414,7 +436,10 @@ pub(crate) async fn cmd_squash(
414436
writeln!(formatter)?;
415437
}
416438
if num_rebased > 0 {
417-
writeln!(formatter, "Rebased {num_rebased} descendant commits.")?;
439+
writeln!(
440+
formatter,
441+
"Rebased {num_rebased} descendant commits{descendants_msg_suffix}."
442+
)?;
418443
}
419444
}
420445

cli/tests/cli-reference@.md.snap

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3278,6 +3278,7 @@ An alternative squashing UI is available via the `-o`, `-A`, and `-B` options. U
32783278
* `-i`, `--interactive` — Interactively choose which parts to squash
32793279
* `--tool <NAME>` — Specify diff editor to be used (implies --interactive)
32803280
* `-k`, `--keep-emptied` — The source revision will not be abandoned
3281+
* `--restore-descendants` — Preserve the content (not the diff) when rebasing descendants
32813282

32823283

32833284

0 commit comments

Comments
 (0)