From 66460477b7e3ad8864b2962aefe1fb45b7e04942 Mon Sep 17 00:00:00 2001 From: Martin von Zweigbergk Date: Sat, 15 May 2021 22:16:07 -0700 Subject: [PATCH] cli: add instructions for all diff-editing (aka interactive) commands Now when you do e.g. `jj split`, you'll get a `JJ-INSTRUCTIONS` file as part of the diff you're editing. --- src/commands.rs | 80 +++++++++++++++++++++++++++++++++++++++++++++--- src/diff_edit.rs | 19 +++++++++++- 2 files changed, 93 insertions(+), 6 deletions(-) diff --git a/src/commands.rs b/src/commands.rs index d2846afae..65f24004b 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -749,6 +749,11 @@ fn get_app<'a, 'b>() -> App<'a, 'b> { .subcommand(debug_command) } +fn short_commit_description(commit: &Commit) -> String { + let first_line = commit.description().split('\n').next().unwrap(); + format!("{} ({})", &commit.id().hex()[0..12], first_line) +} + fn cmd_init( ui: &mut Ui, _command: &CommandHelper, @@ -1517,7 +1522,22 @@ fn cmd_squash( let mut_repo = tx.mut_repo(); let new_parent_tree_id; if sub_matches.is_present("interactive") { - new_parent_tree_id = crate::diff_edit::edit_diff(&parent.tree(), &commit.tree())?; + let instructions = format!( + "You are moving changes from: {}\n\ + into its parent: {}\n\n\ + + The left side of the diff shows the contents of the parent commit. The\n\ + right side initially shows the contents of the commit you're moving\n\ + changes from.\n\n\ + + Adjust the right side until the diff shows the changes you want to move\n\ + to the destination. If you don't make any changes, then all the changes\n\ + from the source will be moved into the parent.\n", + short_commit_description(&commit), + short_commit_description(&parent) + ); + new_parent_tree_id = + crate::diff_edit::edit_diff(&parent.tree(), &commit.tree(), &instructions)?; if &new_parent_tree_id == parent.tree().id() { return Err(CommandError::UserError(String::from("No changes selected"))); } @@ -1565,7 +1585,21 @@ fn cmd_unsquash( let parent_base_tree = merge_commit_trees(repo.as_repo_ref(), &parent.parents()); let new_parent_tree_id; if sub_matches.is_present("interactive") { - new_parent_tree_id = crate::diff_edit::edit_diff(&parent_base_tree, &parent.tree())?; + let instructions = format!( + "You are moving changes from: {}\n\ + into its child: {}\n\n\ + + The diff initially shows the parent commit's changes.\n\n\ + + Adjust the right side until it shows the contents you want to keep in\n\ + the parent commit. The changes you edited out will be moved into the\n\ + child commit. If you don't make any changes, then the operation will be\n\ + aborted.\n", + short_commit_description(&parent), + short_commit_description(&commit) + ); + new_parent_tree_id = + crate::diff_edit::edit_diff(&parent_base_tree, &parent.tree(), &instructions)?; if &new_parent_tree_id == parent_base_tree.id() { return Err(CommandError::UserError(String::from("No changes selected"))); } @@ -1624,7 +1658,24 @@ fn cmd_restore( "restore with --interactive and path is not yet supported".to_string(), )); } - tree_id = crate::diff_edit::edit_diff(&source_commit.tree(), &destination_commit.tree())?; + let instructions = format!( + "You are restoring state from: {}\n\ + into: {}\n\n\ + + The left side of the diff shows the contents of the commit you're\n\ + restoring from. The right side initially shows the contents of the\n\ + commit you're restoring into.\n\n\ + + Adjust the right side until it has the changes you wanted from the left\n\ + side. If you don't make any changes, then the operation will be aborted.\n", + short_commit_description(&source_commit), + short_commit_description(&destination_commit) + ); + tree_id = crate::diff_edit::edit_diff( + &source_commit.tree(), + &destination_commit.tree(), + &instructions, + )?; } else if sub_matches.is_present("paths") { let paths = sub_matches.values_of("paths").unwrap(); let mut tree_builder = repo @@ -1674,7 +1725,16 @@ fn cmd_edit( let commit = repo_command.resolve_revision_arg(sub_matches)?; let repo = repo_command.repo(); let base_tree = merge_commit_trees(repo.as_repo_ref(), &commit.parents()); - let tree_id = crate::diff_edit::edit_diff(&base_tree, &commit.tree())?; + let instructions = format!( + "You are editing changes in: {}\n\n\ + + The diff initially shows the commit's changes.\n\n\ + + Adjust the right side until it shows the contents you want. If you\n\ + don't make any changes, then the operation will be aborted.\n", + short_commit_description(&commit) + ); + let tree_id = crate::diff_edit::edit_diff(&base_tree, &commit.tree(), &instructions)?; if &tree_id == commit.tree().id() { ui.write("Nothing changed.\n")?; } else { @@ -1700,7 +1760,17 @@ fn cmd_split( let commit = repo_command.resolve_revision_arg(sub_matches)?; let repo = repo_command.repo(); let base_tree = merge_commit_trees(repo.as_repo_ref(), &commit.parents()); - let tree_id = crate::diff_edit::edit_diff(&base_tree, &commit.tree())?; + let instructions = format!( + "You are splitting a commit in two: {}\n\n\ + + The diff initially shows the changes in the commit you're splitting.\n\n\ + + Adjust the right side until it shows the contents you want for the first\n\ + commit. The remainder will be in the second commit. If you don't make\n\ + any changes, then the operation will be aborted.\n", + short_commit_description(&commit) + ); + let tree_id = crate::diff_edit::edit_diff(&base_tree, &commit.tree(), &instructions)?; if &tree_id == commit.tree().id() { ui.write("Nothing changed.\n")?; } else { diff --git a/src/diff_edit.rs b/src/diff_edit.rs index cc9379691..7afe6f579 100644 --- a/src/diff_edit.rs +++ b/src/diff_edit.rs @@ -12,6 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. +use std::fs::File; +use std::io::Write; use std::path::{Path, PathBuf}; use std::process::Command; use std::sync::Arc; @@ -92,7 +94,11 @@ fn set_readonly_recursively(path: &Path) { std::fs::set_permissions(path, perms).unwrap(); } -pub fn edit_diff(left_tree: &Tree, right_tree: &Tree) -> Result { +pub fn edit_diff( + left_tree: &Tree, + right_tree: &Tree, + instructions: &str, +) -> Result { // First create partial Trees of only the subset of the left and right trees // that affect files changed between them. let store = left_tree.store(); @@ -131,6 +137,14 @@ pub fn edit_diff(left_tree: &Tree, right_tree: &Tree) -> Result Result