Skip to content

Commit

Permalink
Work queue optimization to make time complexity less than quadratic
Browse files Browse the repository at this point in the history
  • Loading branch information
ilyagr committed Jan 16, 2023
1 parent 7b52f4e commit e6aca27
Show file tree
Hide file tree
Showing 2 changed files with 29 additions and 20 deletions.
41 changes: 25 additions & 16 deletions src/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2073,29 +2073,38 @@ fn cmd_duplicate(
let mut tx = workspace_command
.start_transaction(&format!("duplicating {} commit(s)", to_duplicate.len()));
let mut_repo = tx.mut_repo();

let mut work_stack = vec![];
// TODO fix `target/debug/jj duplicate 6dd8-:` fails, probably because it
// diverges and then the branches merge.
while !to_duplicate.is_empty() {
let mut original_commit = None;
while let Some(next_commit_if_work_stack_empty) = to_duplicate.first() {
// Find a commit whose parents we either don't plan to duplicate or have already
// duplicated.
// This reimplements the heads() revset function, but we will optimize it
// shortly.
for commit in to_duplicate.iter() {
if !commit
// duplicated. The optimization of following a chain of parents makes this linear in the
// number of commits to duplicate.
let mut counter = 0;
let original_commit = loop {
if counter > to_duplicate.len() {
panic!(
"Found a cycle: every commit has a parent that is also in the set of commits \
to duplicate"
);
}
counter += 1;
let candidate = work_stack
.pop()
.unwrap_or_else(|| next_commit_if_work_stack_empty.clone());
if let Some(parent_in_set) = candidate
.parents()
.iter()
.any(|parent| to_duplicate.contains(parent))
.into_iter()
.find(|parent| to_duplicate.contains(parent))
{
original_commit = Some(commit.clone());
break;
work_stack.push(candidate);
work_stack.push(parent_in_set);
} else {
break candidate;
}
}
};

let original_commit = original_commit.expect(
"Found a cycle: every commit has a parent that is also in the set of commits to \
duplicate",
);
let new_parents = original_commit
.parents()
.iter()
Expand Down
8 changes: 4 additions & 4 deletions tests/test_duplicate_command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -202,17 +202,17 @@ fn test_duplicate_many() {
let stdout = test_env.jj_cmd_success(&repo_path, &["duplicate", "a:"]);
insta::assert_snapshot!(stdout, @r###"
Duplicated 2443ea76b0b1 as c6f7f8c4512e a
Duplicated 1394f625cbbd as aa2687406dd2 b
Duplicated c0cb3a0b73e7 as 08844614d525 c
Duplicated ebd06dba20ec as e26642da6fa0 d
Duplicated 1394f625cbbd as aa2687406dd2 b
Duplicated 921dde6e55c0 as 3e94fa8469f7 e
"###);
insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r###"
o 3e94fa8469f7 e
|\
o | aa2687406dd2 b
| o e26642da6fa0 d
| o 08844614d525 c
o | e26642da6fa0 d
o | 08844614d525 c
| o aa2687406dd2 b
|/
o c6f7f8c4512e a
| @ 921dde6e55c0 e
Expand Down

0 comments on commit e6aca27

Please sign in to comment.