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

cli: make "duplicate" evaluate source revisions all at once

There's a subtle behavior change that an empty revset is no longer rejected
individually, but I think that's good for "jj duplicate".

cmd_duplicate() was the last caller of index.topo_order().
This commit is contained in:
Yuya Nishihara 2024-03-15 17:35:52 +09:00
parent a1b3b3c547
commit 4e3c772428
2 changed files with 24 additions and 21 deletions

View file

@ -14,14 +14,13 @@
use std::io::Write;
use indexmap::{IndexMap, IndexSet};
use indexmap::IndexMap;
use jj_lib::backend::CommitId;
use jj_lib::commit::Commit;
use jj_lib::repo::Repo;
use tracing::instrument;
use crate::cli_util::{
resolve_multiple_nonempty_revsets, short_commit_hash, CommandHelper, RevisionArg,
};
use crate::cli_util::{short_commit_hash, CommandHelper, RevisionArg};
use crate::command_error::{user_error, CommandError};
use crate::ui::Ui;
@ -43,12 +42,15 @@ pub(crate) fn cmd_duplicate(
args: &DuplicateArgs,
) -> Result<(), CommandError> {
let mut workspace_command = command.workspace_helper(ui)?;
let to_duplicate: IndexSet<Commit> =
resolve_multiple_nonempty_revsets(&args.revisions, &workspace_command)?;
if to_duplicate
.iter()
.any(|commit| commit.id() == workspace_command.repo().store().root_commit_id())
{
let to_duplicate: Vec<CommitId> = {
let expression = workspace_command.parse_union_revsets(&args.revisions)?;
let revset = workspace_command.evaluate_revset(expression)?;
revset.iter().collect() // in reverse topological order
};
if to_duplicate.is_empty() {
return Err(user_error("No revisions to duplicate"));
}
if to_duplicate.last() == Some(workspace_command.repo().store().root_commit_id()) {
return Err(user_error("Cannot duplicate the root commit"));
}
let mut duplicated_old_to_new: IndexMap<Commit, Commit> = IndexMap::new();
@ -58,14 +60,10 @@ pub(crate) fn cmd_duplicate(
let store = base_repo.store();
let mut_repo = tx.mut_repo();
for original_commit_id in base_repo
.index()
.topo_order(&mut to_duplicate.iter().map(|c| c.id()))
.into_iter()
{
for original_commit_id in to_duplicate.iter().rev() {
// Topological order ensures that any parents of `original_commit` are
// either not in `to_duplicate` or were already duplicated.
let original_commit = store.get_commit(&original_commit_id).unwrap();
let original_commit = store.get_commit(original_commit_id).unwrap();
let new_parents = original_commit
.parents()
.iter()

View file

@ -47,18 +47,23 @@ fn test_duplicate() {
000000000000
"###);
let stderr = test_env.jj_cmd_failure(&repo_path, &["duplicate", "root()"]);
let stderr = test_env.jj_cmd_failure(&repo_path, &["duplicate", "all()"]);
insta::assert_snapshot!(stderr, @r###"
Error: Cannot duplicate the root commit
"###);
let stderr = test_env.jj_cmd_failure(&repo_path, &["duplicate", "none()"]);
insta::assert_snapshot!(stderr, @r###"
Error: No revisions to duplicate
"###);
let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["duplicate", "a"]);
insta::assert_snapshot!(stdout, @"");
insta::assert_snapshot!(stderr, @r###"
Duplicated 2443ea76b0b1 as znkkpsqq 2f6dc5a1 a
Duplicated 2443ea76b0b1 as kpqxywon f5b1e687 a
"###);
insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r###"
2f6dc5a1ffc2 a
f5b1e68729d6 a
@ 17a00fc21654 c
d370aee184ba b
@ -74,10 +79,10 @@ fn test_duplicate() {
let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["duplicate" /* duplicates `c` */]);
insta::assert_snapshot!(stdout, @"");
insta::assert_snapshot!(stderr, @r###"
Duplicated 17a00fc21654 as wqnwkozp 1dd099ea c
Duplicated 17a00fc21654 as lylxulpl ef3b0f3d c
"###);
insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r###"
1dd099ea963c c
ef3b0f3d1046 c
@ 17a00fc21654 c