forked from mirrors/jj
cli: make jj new
create a merge commit if more than one arg given
`jj merge` just creates an empty change, which is practically the same as `jj new`. The main difference is that the former requires more than one argument and the latter requires at most one argument. It seems cleaner to generalize them and make them aliases. This patch starts doing that by making `jj new` accept more than one argument. Instead of having `jj merge` be exactly an alias for `jj new`, we may want to make it a thin wrapper that just checks that more than one argument was given. That would probably be less confusing to users who run `jj merge` without arguments to see what it does. We should probably make `jj checkout` also be an alias for `jj new`, but that will have to wait until we have removed support for open commits (since `jj checkout` still has logic for dealing with open commits).
This commit is contained in:
parent
0d1c23bf02
commit
a1a980d395
3 changed files with 63 additions and 16 deletions
|
@ -143,6 +143,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
|
||||
* `jj merge` now outputs the id of the newly created commit.
|
||||
|
||||
* `jj new` can now be used for creating merge commits. If you pass more than
|
||||
one argument to it, the new commit will have all of them as parents.
|
||||
|
||||
### Fixed bugs
|
||||
|
||||
* When rebasing a conflict where one side modified a file and the other side
|
||||
|
|
|
@ -1395,13 +1395,17 @@ struct EditArgs {
|
|||
|
||||
/// Create a new, empty change and edit it in the working copy
|
||||
///
|
||||
/// Note that you can create a merge commit by specifying multiple revisions as
|
||||
/// argument. For example, `jj new main @` will create a new commit with the
|
||||
/// `main` branch and the working copy as parents.
|
||||
///
|
||||
/// For more information, see
|
||||
/// https://github.com/martinvonz/jj/blob/main/docs/working-copy.md.
|
||||
#[derive(clap::Args, Clone, Debug)]
|
||||
struct NewArgs {
|
||||
/// Parent of the new change
|
||||
/// Parent(s) of the new change
|
||||
#[clap(default_value = "@")]
|
||||
revision: String,
|
||||
revisions: Vec<String>,
|
||||
/// The change description to use
|
||||
#[clap(long, short, default_value = "")]
|
||||
message: String,
|
||||
|
@ -3477,18 +3481,26 @@ fn cmd_edit(ui: &mut Ui, command: &CommandHelper, args: &EditArgs) -> Result<(),
|
|||
|
||||
fn cmd_new(ui: &mut Ui, command: &CommandHelper, args: &NewArgs) -> Result<(), CommandError> {
|
||||
let mut workspace_command = command.workspace_helper(ui)?;
|
||||
let parent = workspace_command.resolve_single_rev(&args.revision)?;
|
||||
let commit_builder = CommitBuilder::for_open_commit(
|
||||
ui.settings(),
|
||||
parent.id().clone(),
|
||||
parent.tree_id().clone(),
|
||||
)
|
||||
.set_description(args.message.clone());
|
||||
let mut commits = vec![];
|
||||
let mut parent_ids = vec![];
|
||||
assert!(
|
||||
!args.revisions.is_empty(),
|
||||
"expected a non-empty list from clap"
|
||||
);
|
||||
for revision_arg in &args.revisions {
|
||||
let commit = workspace_command.resolve_single_rev(revision_arg)?;
|
||||
parent_ids.push(commit.id().clone());
|
||||
commits.push(commit);
|
||||
}
|
||||
let mut tx = workspace_command.start_transaction("new empty commit");
|
||||
let mut_repo = tx.mut_repo();
|
||||
let new_commit = commit_builder.write_to_repo(mut_repo);
|
||||
let merged_tree = merge_commit_trees(workspace_command.repo().as_repo_ref(), &commits);
|
||||
let new_commit = CommitBuilder::for_new_commit(ui.settings(), merged_tree.id().clone())
|
||||
.set_parents(parent_ids)
|
||||
.set_description(args.message.clone())
|
||||
.set_open(true)
|
||||
.write_to_repo(tx.mut_repo());
|
||||
let workspace_id = workspace_command.workspace_id();
|
||||
mut_repo.edit(workspace_id, &new_commit);
|
||||
tx.mut_repo().edit(workspace_id, &new_commit);
|
||||
workspace_command.finish_transaction(ui, tx)?;
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -12,6 +12,8 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use std::path::Path;
|
||||
|
||||
use crate::common::TestEnvironment;
|
||||
|
||||
pub mod common;
|
||||
|
@ -25,8 +27,7 @@ fn test_new() {
|
|||
test_env.jj_cmd_success(&repo_path, &["describe", "-m", "add a file"]);
|
||||
test_env.jj_cmd_success(&repo_path, &["new", "-m", "a new commit"]);
|
||||
|
||||
let stdout = test_env.jj_cmd_success(&repo_path, &["log", "-T", "commit_id \" \" description"]);
|
||||
insta::assert_snapshot!(stdout, @r###"
|
||||
insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r###"
|
||||
@ 88436dbcdbedc2b8a6ebd0687981906d09ccc68f a new commit
|
||||
o 51e9c5819117991e4a6dc5a4a744283fc74f0746 add a file
|
||||
o 0000000000000000000000000000000000000000 (no description set)
|
||||
|
@ -34,8 +35,7 @@ fn test_new() {
|
|||
|
||||
// Start a new change off of a specific commit (the root commit in this case).
|
||||
test_env.jj_cmd_success(&repo_path, &["new", "-m", "off of root", "root"]);
|
||||
let stdout = test_env.jj_cmd_success(&repo_path, &["log", "-T", "commit_id \" \" description"]);
|
||||
insta::assert_snapshot!(stdout, @r###"
|
||||
insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r###"
|
||||
@ d8c0a3e1570f1f5b08113a3427b3160900c3d48e off of root
|
||||
| o 88436dbcdbedc2b8a6ebd0687981906d09ccc68f a new commit
|
||||
| o 51e9c5819117991e4a6dc5a4a744283fc74f0746 add a file
|
||||
|
@ -43,3 +43,35 @@ fn test_new() {
|
|||
o 0000000000000000000000000000000000000000 (no description set)
|
||||
"###);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_new_merge() {
|
||||
let 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");
|
||||
|
||||
test_env.jj_cmd_success(&repo_path, &["branch", "create", "main"]);
|
||||
test_env.jj_cmd_success(&repo_path, &["describe", "-m", "add file1"]);
|
||||
std::fs::write(repo_path.join("file1"), "a").unwrap();
|
||||
test_env.jj_cmd_success(&repo_path, &["new", "root", "-m", "add file2"]);
|
||||
std::fs::write(repo_path.join("file2"), "b").unwrap();
|
||||
|
||||
// Create a merge commit
|
||||
test_env.jj_cmd_success(&repo_path, &["new", "main", "@"]);
|
||||
insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r###"
|
||||
@ 5b37ef8ee8cd934dfe1e70adff66cd0679f5a573 (no description set)
|
||||
|\
|
||||
o | 99814c62bec5c13d2053435b3d6bbeb1900cb57e add file2
|
||||
| o fe37af248a068697c6dcd7ebd17f5aac2205e7cb add file1
|
||||
|/
|
||||
o 0000000000000000000000000000000000000000 (no description set)
|
||||
"###);
|
||||
let stdout = test_env.jj_cmd_success(&repo_path, &["print", "file1"]);
|
||||
insta::assert_snapshot!(stdout, @"a");
|
||||
let stdout = test_env.jj_cmd_success(&repo_path, &["print", "file2"]);
|
||||
insta::assert_snapshot!(stdout, @"b");
|
||||
}
|
||||
|
||||
fn get_log_output(test_env: &TestEnvironment, repo_path: &Path) -> String {
|
||||
test_env.jj_cmd_success(repo_path, &["log", "-T", "commit_id \" \" description"])
|
||||
}
|
Loading…
Reference in a new issue