diff --git a/lib/src/repo.rs b/lib/src/repo.rs index 101c57024..d22802383 100644 --- a/lib/src/repo.rs +++ b/lib/src/repo.rs @@ -303,9 +303,9 @@ impl RepoAtHead { } pub struct UnresolvedHeadRepo { - repo_loader: RepoLoader, - locked_op_heads: LockedOpHeads, - op_heads: Vec, + pub repo_loader: RepoLoader, + pub locked_op_heads: LockedOpHeads, + pub op_heads: Vec, } impl UnresolvedHeadRepo { diff --git a/src/commands.rs b/src/commands.rs index 44d691614..27ce62131 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -218,8 +218,27 @@ jj init --git-repo=."; ui, "Concurrent modification detected, resolving automatically.", )?; - // TODO: Tell the user how many commits were rebased. - unresolved.resolve(ui.settings()) + let base_repo = repo_loader.load_at(&unresolved.op_heads[0]); + // TODO: It may be helpful to print each operation we're merging here + let mut workspace_command = self.for_loaded_repo(ui, workspace, base_repo)?; + let mut tx = + workspace_command.start_transaction("resolve concurrent operations"); + for other_op_head in unresolved.op_heads.into_iter().skip(1) { + tx.merge_operation(other_op_head); + let num_rebased = tx.mut_repo().rebase_descendants(ui.settings()); + if num_rebased > 0 { + writeln!( + ui, + "Rebased {} descendant commits onto commits rewritten by other \ + operation", + num_rebased + )?; + } + } + let merged_repo = tx.write().leave_unpublished(); + unresolved.locked_op_heads.finish(merged_repo.operation()); + workspace_command.repo = merged_repo; + return Ok(workspace_command); } } } else { diff --git a/tests/test_concurrent_operations.rs b/tests/test_concurrent_operations.rs index f26bf6f5e..01f9dbc18 100644 --- a/tests/test_concurrent_operations.rs +++ b/tests/test_concurrent_operations.rs @@ -15,7 +15,7 @@ use jujutsu::testutils::TestEnvironment; #[test] -fn test_concurrent_operations() { +fn test_concurrent_operation_divergence() { 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"); @@ -39,3 +39,31 @@ fn test_concurrent_operations() { o "###); } + +#[test] +fn test_concurrent_operations_auto_rebase() { + 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"); + + std::fs::write(repo_path.join("file"), "contents").unwrap(); + test_env.jj_cmd_success(&repo_path, &["describe", "-m", "initial"]); + let stdout = test_env.jj_cmd_success(&repo_path, &["op", "log"]); + let op_id_hex = stdout[2..14].to_string(); + + test_env.jj_cmd_success(&repo_path, &["describe", "-m", "rewritten"]); + test_env.jj_cmd_success( + &repo_path, + &["new", "--at-op", &op_id_hex, "-m", "new child"], + ); + + // We should be informed about the concurrent modification + let stdout = test_env.jj_cmd_success(&repo_path, &["log", "-T", "commit_id \" \" description"]); + insta::assert_snapshot!(stdout, @r###" + Concurrent modification detected, resolving automatically. + Rebased 1 descendant commits onto commits rewritten by other operation + o 4eeb7d76372418118a91c34f09e5e3936f0deeb5 new child + @ 14176aeadc0259b2150fc7374969e74b1552a498 rewritten + o 0000000000000000000000000000000000000000 + "###); +}