diff --git a/CHANGELOG.md b/CHANGELOG.md index e0c0c21288..1187472047 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -69,6 +69,9 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). * `jj commit` and `jj describe` now accept `--author` option allowing to quickly change author of given commit. +* `jj diffedit` now accepts a `--restore-descendants` flag. When used, + descendants of the edited commit will keep their original content. + ### Fixed bugs * Update working copy before reporting changes. This prevents errors during reporting diff --git a/cli/src/commands/diffedit.rs b/cli/src/commands/diffedit.rs index 10d89e5ef1..d20babff46 100644 --- a/cli/src/commands/diffedit.rs +++ b/cli/src/commands/diffedit.rs @@ -38,8 +38,8 @@ use crate::ui::Ui; /// /// Edit the right side of the diff until it looks the way you want. Once you /// close the editor, the revision specified with `-r` or `--to` will be -/// updated. Descendants will be rebased on top as usual, which may result in -/// conflicts. +/// updated. Unless `--restore-descendants` is used, descendants will be +/// rebased on top as usual, which may result in conflicts. /// /// See `jj restore` if you want to move entire files from one revision to /// another. See `jj squash -i` or `jj unsquash -i` if you instead want to move @@ -64,6 +64,14 @@ pub(crate) struct DiffeditArgs { /// Specify diff editor to be used #[arg(long, value_name = "NAME")] tool: Option, + /// Preserve the content (not the diff) when rebasing descendants + /// + /// When rebasing a descendant on top of the rewritten revision, its diff + /// compared to its parent(s) is normally preserved, i.e. the same way that + /// descendants are always rebased. This flag makes it so the content/state + /// is preserved instead of preserving the diff. + #[arg(long)] + restore_descendants: bool, } #[instrument(skip_all)] @@ -119,13 +127,23 @@ don't make any changes, then the operation will be aborted.", .write()?; // rebase_descendants early; otherwise `new_commit` would always have // a conflicted change id at this point. - let num_rebased = tx.repo_mut().rebase_descendants(command.settings())?; + let (num_rewritten, extra_msg) = if args.restore_descendants { + ( + tx.repo_mut().reparent_descendants(command.settings())?, + " (while preserving their content)", + ) + } else { + (tx.repo_mut().rebase_descendants(command.settings())?, "") + }; if let Some(mut formatter) = ui.status_formatter() { write!(formatter, "Created ")?; tx.write_commit_summary(formatter.as_mut(), &new_commit)?; writeln!(formatter)?; - if num_rebased > 0 { - writeln!(formatter, "Rebased {num_rebased} descendant commits")?; + if num_rewritten > 0 { + writeln!( + formatter, + "Rebased {num_rewritten} descendant commits{extra_msg}" + )?; } } tx.finish(ui, format!("edit commit {}", target_commit.id().hex()))?; diff --git a/cli/tests/cli-reference@.md.snap b/cli/tests/cli-reference@.md.snap index 5f62cec802..395c88c739 100644 --- a/cli/tests/cli-reference@.md.snap +++ b/cli/tests/cli-reference@.md.snap @@ -655,7 +655,7 @@ With the `--from` and/or `--to` options, starts a [diff editor] comparing the "f [diff editor]: https://martinvonz.github.io/jj/latest/config/#editing-diffs -Edit the right side of the diff until it looks the way you want. Once you close the editor, the revision specified with `-r` or `--to` will be updated. Descendants will be rebased on top as usual, which may result in conflicts. +Edit the right side of the diff until it looks the way you want. Once you close the editor, the revision specified with `-r` or `--to` will be updated. Unless `--restore-descendants` is used, descendants will be rebased on top as usual, which may result in conflicts. See `jj restore` if you want to move entire files from one revision to another. See `jj squash -i` or `jj unsquash -i` if you instead want to move changes into or out of the parent revision. @@ -673,6 +673,9 @@ See `jj restore` if you want to move entire files from one revision to another. Defaults to @ if --from is specified. * `--tool ` — Specify diff editor to be used +* `--restore-descendants` — Preserve the content (not the diff) when rebasing descendants + + When rebasing a descendant on top of the rewritten revision, its diff compared to its parent(s) is normally preserved, i.e. the same way that descendants are always rebased. This flag makes it so the content/state is preserved instead of preserving the diff. diff --git a/cli/tests/test_diffedit_command.rs b/cli/tests/test_diffedit_command.rs index 0975cd42da..2bd84a6818 100644 --- a/cli/tests/test_diffedit_command.rs +++ b/cli/tests/test_diffedit_command.rs @@ -514,3 +514,42 @@ fn test_diffedit_old_restore_interactive_tests() { +unrelated "###); } + +#[test] +fn test_diffedit_restore_descendants() { + let mut test_env = TestEnvironment::default(); + test_env.jj_cmd_ok(test_env.env_root(), &["git", "init", "repo"]); + let repo_path = test_env.env_root().join("repo"); + + std::fs::write(repo_path.join("file"), "println!(\"foo\")\n").unwrap(); + test_env.jj_cmd_ok(&repo_path, &["new"]); + std::fs::write(repo_path.join("file"), "println!(\"bar\")\n").unwrap(); + test_env.jj_cmd_ok(&repo_path, &["new"]); + std::fs::write(repo_path.join("file"), "println!(\"baz\");\n").unwrap(); + + let edit_script = test_env.set_up_fake_diff_editor(); + + // Add a ";" after the line with "bar". There should be no conflict. + std::fs::write(edit_script, "write file\nprintln!(\"bar\");\n").unwrap(); + let (stdout, stderr) = test_env.jj_cmd_ok( + &repo_path, + &["diffedit", "-r", "@-", "--restore-descendants"], + ); + insta::assert_snapshot!(stdout, @""); + insta::assert_snapshot!(stderr, @r#" + Created rlvkpnrz 62b8c2ce (no description set) + Rebased 1 descendant commits (while preserving their content) + Working copy now at: kkmpptxz 321d1cd1 (no description set) + Parent commit : rlvkpnrz 62b8c2ce (no description set) + "#); + let stdout = test_env.jj_cmd_success(&repo_path, &["diff", "--git"]); + insta::assert_snapshot!(stdout, @r#" + diff --git a/file b/file + index 1a598a8fc9..7b6a85ab5a 100644 + --- a/file + +++ b/file + @@ -1,1 +1,1 @@ + -println!("bar"); + +println!("baz"); + "#); +} diff --git a/cli/tests/test_global_opts.rs b/cli/tests/test_global_opts.rs index b275f7109f..a0ca70a593 100644 --- a/cli/tests/test_global_opts.rs +++ b/cli/tests/test_global_opts.rs @@ -602,6 +602,7 @@ fn test_help() { --from Show changes from this revision --to Edit changes in this revision --tool Specify diff editor to be used + --restore-descendants Preserve the content (not the diff) when rebasing descendants -h, --help Print help (see more with '--help') Global Options: