From 4be0da36070ddcada93abbd9484aa5e0fb897359 Mon Sep 17 00:00:00 2001 From: Martin von Zweigbergk Date: Sat, 9 Apr 2022 10:27:17 -0700 Subject: [PATCH] cli: teach `jj squash` to move only changes to specified paths --- CHANGELOG.md | 8 ++++---- src/commands.rs | 32 ++++++++++++++++++-------------- tests/test_squash_command.rs | 36 +++++++++++++++++++++++++++++++++--- 3 files changed, 55 insertions(+), 21 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0633a6468..fa2fc76e1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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. -* `jj move` now lets you limit the set of changes to move by specifying paths - on the command line (in addition to the `--interactive` mode). For example, - use `jj move --to @-- foo` to move the changes to file (or directory) `foo` in - the working copy to the grandparent commit. +* `jj move` and `jj squash` now lets you limit the set of changes to move by + specifying paths on the command line (in addition to the `--interactive` + mode). For example, use `jj move --to @-- foo` to move the changes to file + (or directory) `foo` in the working copy to the grandparent commit. ### Fixed bugs diff --git a/src/commands.rs b/src/commands.rs index 3bd10d5e2..7381255c5 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -1292,6 +1292,9 @@ struct SquashArgs { /// Interactively choose which parts to squash #[clap(long, short)] interactive: bool, + /// Move only changes to these paths (instead of all paths) + #[clap(conflicts_with = "interactive")] + paths: Vec, } /// 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 = workspace_command.start_transaction(&format!("squash commit {}", commit.id().hex())); let mut_repo = tx.mut_repo(); - let new_parent_tree_id; - if args.interactive { - let instructions = format!( - "\ + let instructions = format!( + "\ You are moving changes from: {} 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 from the source will be moved into the parent. ", - short_commit_description(&commit), - short_commit_description(parent) - ); - new_parent_tree_id = - workspace_command.edit_diff(&parent.tree(), &commit.tree(), &instructions)?; - if &new_parent_tree_id == parent.tree().id() { - return Err(CommandError::UserError(String::from("No changes selected"))); - } - } else { - new_parent_tree_id = commit.tree().id().clone(); + short_commit_description(&commit), + short_commit_description(parent) + ); + let new_parent_tree_id = workspace_command.select_diff( + ui, + &parent.tree(), + &commit.tree(), + &instructions, + args.interactive, + &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 // (always the case in the non-interactive case). diff --git a/tests/test_squash_command.rs b/tests/test_squash_command.rs index d0e264826..193439c17 100644 --- a/tests/test_squash_command.rs +++ b/tests/test_squash_command.rs @@ -122,7 +122,7 @@ fn test_squash() { } #[test] -fn test_squash_interactive() { +fn test_squash_partial() { let mut test_env = TestEnvironment::default(); test_env.jj_cmd_success(test_env.env_root(), &["init", "repo", "--git"]); let repo_path = test_env.env_root().join("repo"); @@ -148,7 +148,8 @@ fn test_squash_interactive() { 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(); std::fs::write(&edit_script, "").unwrap(); 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 "); - // Can squash only some changes + // Can squash only some changes in interactive mode test_env.jj_cmd_success(&repo_path, &["undo"]); std::fs::write(&edit_script, "reset file1").unwrap(); 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"]); 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 +"); }