restore: add --restore-descendants flag

This commit is contained in:
Samuel Tardieu 2024-09-24 10:59:47 +02:00
parent e8deb08f78
commit cf7847d784
4 changed files with 100 additions and 7 deletions

View file

@ -69,11 +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.
* `jj abandon` now accepts a `--restore-descendants` flag. When used,
descendants of the abandoned commits will keep their original content.
* `jj diffedit`, `jj abandon`, and `jj restore` now accept a `--restore-descendants`
flag. When used, descendants of the edited or deleted commits will keep their original
content.
### Fixed bugs

View file

@ -70,6 +70,9 @@ pub(crate) struct RestoreArgs {
/// the user might not even realize something went wrong.
#[arg(long, short, hide = true)]
revision: Option<RevisionArg>,
/// Preserve the content (not the diff) when rebasing descendants
#[arg(long)]
restore_descendants: bool,
}
#[instrument(skip_all)]
@ -116,13 +119,23 @@ pub(crate) fn cmd_restore(
.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_rebased, 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")?;
writeln!(
formatter,
"Rebased {num_rebased} descendant commits{extra_msg}"
)?;
}
}
tx.finish(ui, format!("restore into commit {}", to_commit.id().hex()))?;

View file

@ -1784,6 +1784,7 @@ See `jj diffedit` if you'd like to restore portions of files rather than entire
This undoes the changes that can be seen with `jj diff -r REVISION`. If `REVISION` only has a single parent, this option is equivalent to `jj restore --to REVISION --from REVISION-`.
The default behavior of `jj restore` is equivalent to `jj restore --changes-in @`.
* `--restore-descendants` — Preserve the content (not the diff) when rebasing descendants

View file

@ -261,6 +261,87 @@ fn test_restore_conflicted_merge() {
"###);
}
#[test]
fn test_restore_restore_descendants() {
let 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");
create_commit(&test_env, &repo_path, "base", &[], &[("file", "base\n")]);
create_commit(&test_env, &repo_path, "a", &["base"], &[("file", "a\n")]);
create_commit(
&test_env,
&repo_path,
"b",
&["base"],
&[("file", "b\n"), ("file2", "b\n")],
);
create_commit(
&test_env,
&repo_path,
"ab",
&["a", "b"],
&[("file", "ab\n")],
);
// Test the setup
insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r#"
@ ab
b
a
base
"#);
insta::assert_snapshot!(
std::fs::read_to_string(repo_path.join("file")).unwrap(), @r#"
ab
"#);
// Commit "b" was not supposed to modify "file", restore it from its parent
// while preserving its child commit content.
let (stdout, stderr) = test_env.jj_cmd_ok(
&repo_path,
&["restore", "-c", "b", "file", "--restore-descendants"],
);
insta::assert_snapshot!(stdout, @"");
insta::assert_snapshot!(stderr, @r#"
Created royxmykx 3fd5aa05 b | b
Rebased 1 descendant commits (while preserving their content)
Working copy now at: vruxwmqv bf5491a0 ab | ab
Parent commit : zsuskuln aa493daf a | a
Parent commit : royxmykx 3fd5aa05 b | b
"#);
// Check that "a", "b", and "ab" have their expected content by diffing them.
// "ab" must have kept its content.
insta::assert_snapshot!(test_env.jj_cmd_success(&repo_path, &["diff", "--from=a", "--to=ab", "--git"]), @r#"
diff --git a/file b/file
index 7898192261..81bf396956 100644
--- a/file
+++ b/file
@@ -1,1 +1,1 @@
-a
+ab
diff --git a/file2 b/file2
new file mode 100644
index 0000000000..6178079822
--- /dev/null
+++ b/file2
@@ -1,0 +1,1 @@
+b
"#);
insta::assert_snapshot!(test_env.jj_cmd_success(&repo_path, &["diff", "--from=b", "--to=ab", "--git"]), @r#"
diff --git a/file b/file
index df967b96a5..81bf396956 100644
--- a/file
+++ b/file
@@ -1,1 +1,1 @@
-base
+ab
"#);
}
fn create_commit(
test_env: &TestEnvironment,
repo_path: &Path,