cmd: Add --to and --from arguments to jj touchup

This allows touching up a commit while seeing the differences between it and
another arbitrary commit.
This commit is contained in:
Ilya Grigoriev 2022-12-07 22:01:58 -08:00
parent b474fc6ab5
commit f71ca25ebe
3 changed files with 82 additions and 25 deletions

View File

@ -537,19 +537,34 @@ struct RestoreArgs {
paths: Vec<String>,
}
/// Touch up the content changes in a revision
/// Touch up the content changes in a revision with a diff editor
///
/// With the `-r` option, which is the default, starts a diff editor (`meld` by
/// default) on the changes in the revision.
///
/// With the `--from` and/or `--to` options, starts a diff editor comparing the
/// "from" revision to the "to" revision.
///
/// Starts a diff editor (`meld` by default) on the changes in the revision.
/// Edit the right side of the diff until it looks the way you want. Once you
/// close the editor, the revision will be updated. Descendants will be rebased
/// on top as usual, which may result in conflicts. See `jj squash -i` or `jj
/// unsquash -i` if you instead want to move changes into or out of the parent
/// revision.
/// 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.
///
/// 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.
#[derive(clap::Args, Clone, Debug)]
struct TouchupArgs {
/// The revision to touch up
#[arg(long, short, default_value = "@")]
revision: RevisionArg,
/// The revision to touch up. Defaults to @ if --to/--from are not
/// specified.
#[arg(long, short)]
revision: Option<RevisionArg>,
/// Show changes from this revision. Defaults to @ if --to is specified.
#[arg(long, conflicts_with = "revision")]
from: Option<RevisionArg>,
/// Edit changes in this revision. Defaults to @ if --from is specified.
#[arg(long, conflicts_with = "revision")]
to: Option<RevisionArg>,
}
/// Split a revision in two
@ -2408,27 +2423,47 @@ fn cmd_touchup(
args: &TouchupArgs,
) -> Result<(), CommandError> {
let mut workspace_command = command.workspace_helper(ui)?;
let commit = workspace_command.resolve_single_rev(&args.revision)?;
workspace_command.check_rewriteable(&commit)?;
let base_tree = merge_commit_trees(workspace_command.repo().as_repo_ref(), &commit.parents());
let (target_commit, base_commits, diff_description);
if args.from.is_some() || args.to.is_some() {
target_commit = workspace_command.resolve_single_rev(args.to.as_deref().unwrap_or("@"))?;
base_commits =
vec![workspace_command.resolve_single_rev(args.from.as_deref().unwrap_or("@"))?];
diff_description = format!(
"The diff initially shows the commit's changes relative to:\n{}",
short_commit_description(&base_commits[0])
);
} else {
target_commit =
workspace_command.resolve_single_rev(args.revision.as_deref().unwrap_or("@"))?;
base_commits = target_commit.parents();
diff_description = "The diff initially shows the commit's changes.".to_string();
};
workspace_command.check_rewriteable(&target_commit)?;
let instructions = format!(
"\
You are editing changes in: {}
The diff initially shows the commit's changes.
{diff_description}
Adjust the right side until it shows the contents you want. If you
don't make any changes, then the operation will be aborted.",
short_commit_description(&commit)
short_commit_description(&target_commit),
);
let tree_id = workspace_command.edit_diff(ui, &base_tree, &commit.tree(), &instructions)?;
if &tree_id == commit.tree_id() {
let base_tree = merge_commit_trees(
workspace_command.repo().as_repo_ref(),
base_commits.as_slice(),
);
let tree_id =
workspace_command.edit_diff(ui, &base_tree, &target_commit.tree(), &instructions)?;
if &tree_id == target_commit.tree_id() {
ui.write("Nothing changed.\n")?;
} else {
let mut tx =
workspace_command.start_transaction(&format!("edit commit {}", commit.id().hex()));
let mut tx = workspace_command
.start_transaction(&format!("edit commit {}", target_commit.id().hex()));
let mut_repo = tx.mut_repo();
let new_commit = CommitBuilder::for_rewrite_from(ui.settings(), &commit)
let new_commit = CommitBuilder::for_rewrite_from(ui.settings(), &target_commit)
.set_tree(tree_id)
.write_to_repo(mut_repo);
ui.write("Created ")?;

View File

@ -263,12 +263,14 @@ fn test_help() {
let stdout = test_env.jj_cmd_success(test_env.env_root(), &["touchup", "-h"]);
insta::assert_snapshot!(stdout, @r###"
Touch up the content changes in a revision
Touch up the content changes in a revision with a diff editor
Usage: jj touchup [OPTIONS]
Options:
-r, --revision <REVISION> The revision to touch up [default: @]
-r, --revision <REVISION> The revision to touch up. Defaults to @ if --to/--from are not specified
--from <FROM> Show changes from this revision. Defaults to @ if --to is specified
--to <TO> Edit changes in this revision. Defaults to @ if --from is specified
-h, --help Print help information (use `--help` for more detail)
Global Options:

View File

@ -25,6 +25,7 @@ fn test_touchup() {
let repo_path = test_env.env_root().join("repo");
std::fs::write(repo_path.join("file1"), "a\n").unwrap();
test_env.jj_cmd_success(&repo_path, &["new"]);
std::fs::write(repo_path.join("file2"), "a\n").unwrap();
std::fs::write(repo_path.join("file3"), "a\n").unwrap();
test_env.jj_cmd_success(&repo_path, &["new"]);
@ -66,8 +67,8 @@ fn test_touchup() {
std::fs::write(&edit_script, "reset file2").unwrap();
let stdout = test_env.jj_cmd_success(&repo_path, &["touchup"]);
insta::assert_snapshot!(stdout, @r###"
Created 8c79910b5033 (no description set)
Working copy now at: 8c79910b5033 (no description set)
Created 1930da4a57e9 (no description set)
Working copy now at: 1930da4a57e9 (no description set)
Added 0 files, modified 1 files, removed 0 files
"###);
let stdout = test_env.jj_cmd_success(&repo_path, &["diff", "-s"]);
@ -80,15 +81,34 @@ fn test_touchup() {
std::fs::write(&edit_script, "write file3\nmodified\n").unwrap();
let stdout = test_env.jj_cmd_success(&repo_path, &["touchup", "-r", "@-"]);
insta::assert_snapshot!(stdout, @r###"
Created 472de2debaff (no description set)
Created c03ae96780b6 (no description set)
Rebased 1 descendant commits
Working copy now at: 6d19dc1ea106 (no description set)
Working copy now at: 2a4dc204a6ab (no description set)
Added 0 files, modified 1 files, removed 0 files
"###);
let contents = String::from_utf8(std::fs::read(repo_path.join("file3")).unwrap()).unwrap();
insta::assert_snapshot!(contents, @r###"
modified
"###);
// Test touchup --from @--
test_env.jj_cmd_success(&repo_path, &["undo"]);
std::fs::write(
&edit_script,
"files-before file1\0files-after JJ-INSTRUCTIONS file2 file3\0reset file2",
)
.unwrap();
let stdout = test_env.jj_cmd_success(&repo_path, &["touchup", "--from", "@--"]);
insta::assert_snapshot!(stdout, @r###"
Created 15f2c966d508 (no description set)
Working copy now at: 15f2c966d508 (no description set)
Added 0 files, modified 0 files, removed 1 files
"###);
let stdout = test_env.jj_cmd_success(&repo_path, &["diff", "-s"]);
insta::assert_snapshot!(stdout, @r###"
R file1
R file2
"###);
}
#[test]