ok/jj
1
0
Fork 0
forked from mirrors/jj

cli: teach jj squash to move only changes to specified paths

This commit is contained in:
Martin von Zweigbergk 2022-04-09 10:27:17 -07:00 committed by Martin von Zweigbergk
parent 082ec5ae3b
commit 4be0da3607
3 changed files with 55 additions and 21 deletions

View file

@ -11,10 +11,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
* The new `jj print` command prints the contents of a file in a revision. * The new `jj print` command prints the contents of a file in a revision.
* `jj move` now lets you limit the set of changes to move by specifying paths * `jj move` and `jj squash` now lets you limit the set of changes to move by
on the command line (in addition to the `--interactive` mode). For example, specifying paths on the command line (in addition to the `--interactive`
use `jj move --to @-- foo` to move the changes to file (or directory) `foo` in mode). For example, use `jj move --to @-- foo` to move the changes to file
the working copy to the grandparent commit. (or directory) `foo` in the working copy to the grandparent commit.
### Fixed bugs ### Fixed bugs

View file

@ -1292,6 +1292,9 @@ struct SquashArgs {
/// Interactively choose which parts to squash /// Interactively choose which parts to squash
#[clap(long, short)] #[clap(long, short)]
interactive: bool, interactive: bool,
/// Move only changes to these paths (instead of all paths)
#[clap(conflicts_with = "interactive")]
paths: Vec<String>,
} }
/// Move changes from a revision's parent into the revision /// Move changes from a revision's parent into the revision
@ -3245,10 +3248,8 @@ fn cmd_squash(ui: &mut Ui, command: &CommandHelper, args: &SquashArgs) -> Result
let mut tx = let mut tx =
workspace_command.start_transaction(&format!("squash commit {}", commit.id().hex())); workspace_command.start_transaction(&format!("squash commit {}", commit.id().hex()));
let mut_repo = tx.mut_repo(); let mut_repo = tx.mut_repo();
let new_parent_tree_id; let instructions = format!(
if args.interactive { "\
let instructions = format!(
"\
You are moving changes from: {} You are moving changes from: {}
into its parent: {} into its parent: {}
@ -3260,16 +3261,19 @@ Adjust the right side until the diff shows the changes you want to move
to the destination. If you don't make any changes, then all the changes to the destination. If you don't make any changes, then all the changes
from the source will be moved into the parent. from the source will be moved into the parent.
", ",
short_commit_description(&commit), short_commit_description(&commit),
short_commit_description(parent) short_commit_description(parent)
); );
new_parent_tree_id = let new_parent_tree_id = workspace_command.select_diff(
workspace_command.edit_diff(&parent.tree(), &commit.tree(), &instructions)?; ui,
if &new_parent_tree_id == parent.tree().id() { &parent.tree(),
return Err(CommandError::UserError(String::from("No changes selected"))); &commit.tree(),
} &instructions,
} else { args.interactive,
new_parent_tree_id = commit.tree().id().clone(); &args.paths,
)?;
if &new_parent_tree_id == parent.tree().id() {
return Err(CommandError::UserError(String::from("No changes selected")));
} }
// Abandon the child if the parent now has all the content from the child // Abandon the child if the parent now has all the content from the child
// (always the case in the non-interactive case). // (always the case in the non-interactive case).

View file

@ -122,7 +122,7 @@ fn test_squash() {
} }
#[test] #[test]
fn test_squash_interactive() { fn test_squash_partial() {
let mut test_env = TestEnvironment::default(); let mut test_env = TestEnvironment::default();
test_env.jj_cmd_success(test_env.env_root(), &["init", "repo", "--git"]); test_env.jj_cmd_success(test_env.env_root(), &["init", "repo", "--git"]);
let repo_path = test_env.env_root().join("repo"); let repo_path = test_env.env_root().join("repo");
@ -148,7 +148,8 @@ fn test_squash_interactive() {
o 000000000000 o 000000000000
"###); "###);
// Everything is moved into the parent if no change is made // If we don't make any changes in the diff-editor, the whole change is moved
// into the parent
let edit_script = test_env.set_up_fake_diff_editor(); let edit_script = test_env.set_up_fake_diff_editor();
std::fs::write(&edit_script, "").unwrap(); std::fs::write(&edit_script, "").unwrap();
let stdout = test_env.jj_cmd_success(&repo_path, &["squash", "-r", "b", "-i"]); let stdout = test_env.jj_cmd_success(&repo_path, &["squash", "-r", "b", "-i"]);
@ -166,7 +167,7 @@ fn test_squash_interactive() {
insta::assert_snapshot!(stdout, @"b insta::assert_snapshot!(stdout, @"b
"); ");
// Can squash only some changes // Can squash only some changes in interactive mode
test_env.jj_cmd_success(&repo_path, &["undo"]); test_env.jj_cmd_success(&repo_path, &["undo"]);
std::fs::write(&edit_script, "reset file1").unwrap(); std::fs::write(&edit_script, "reset file1").unwrap();
let stdout = test_env.jj_cmd_success(&repo_path, &["squash", "-r", "b", "-i"]); let stdout = test_env.jj_cmd_success(&repo_path, &["squash", "-r", "b", "-i"]);
@ -193,4 +194,33 @@ fn test_squash_interactive() {
let stdout = test_env.jj_cmd_success(&repo_path, &["print", "file2", "-r", "b"]); let stdout = test_env.jj_cmd_success(&repo_path, &["print", "file2", "-r", "b"]);
insta::assert_snapshot!(stdout, @"b insta::assert_snapshot!(stdout, @"b
"); ");
// Can squash only some changes in non-interactive mode
test_env.jj_cmd_success(&repo_path, &["undo"]);
// Clear the script so we know it won't be used even without -i
std::fs::write(&edit_script, "").unwrap();
let stdout = test_env.jj_cmd_success(&repo_path, &["squash", "-r", "b", "file2"]);
insta::assert_snapshot!(stdout, @r###"
Rebased 1 descendant commits
Working copy now at: a911fa1d0627
"###);
let stdout = test_env.jj_cmd_success(&repo_path, &["log", "-T", template]);
insta::assert_snapshot!(stdout, @r###"
@ a911fa1d0627 c
o fb73ad17899f b
o 70621f4c7a42 a
o 000000000000
"###);
let stdout = test_env.jj_cmd_success(&repo_path, &["print", "file1", "-r", "a"]);
insta::assert_snapshot!(stdout, @"a
");
let stdout = test_env.jj_cmd_success(&repo_path, &["print", "file2", "-r", "a"]);
insta::assert_snapshot!(stdout, @"b
");
let stdout = test_env.jj_cmd_success(&repo_path, &["print", "file1", "-r", "b"]);
insta::assert_snapshot!(stdout, @"b
");
let stdout = test_env.jj_cmd_success(&repo_path, &["print", "file2", "-r", "b"]);
insta::assert_snapshot!(stdout, @"b
");
} }