mirror of
https://github.com/martinvonz/jj.git
synced 2024-12-26 05:57:10 +00:00
git: add test for concurrent change in git repo between exports
If you update a branch using regular `git` (or some Git-based tool) between two `jj git export`, we will overwrite that change if you had also changed the branch in jj land. There's a similar problem if you delete the branch in jj land. Let's have a test for that. I'm going to make us not overwrite it soon. This patch adds a test for those cases, plus many other cases in consistent way. Since the new test covers some cases tested by existing tests, I removed those tests.
This commit is contained in:
parent
25008b63a4
commit
9b59461242
1 changed files with 140 additions and 36 deletions
|
@ -434,36 +434,6 @@ fn test_export_refs_no_detach() {
|
|||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_export_refs_no_op() {
|
||||
// Nothing changes on the git side if nothing changed on the jj side
|
||||
let test_data = GitRepoData::create();
|
||||
let git_repo = test_data.git_repo;
|
||||
let commit1 = empty_git_commit(&git_repo, "refs/heads/main", &[]);
|
||||
git_repo.set_head("refs/heads/main").unwrap();
|
||||
|
||||
let mut tx = test_data
|
||||
.repo
|
||||
.start_transaction(&test_data.settings, "test");
|
||||
let mut_repo = tx.mut_repo();
|
||||
git::import_refs(mut_repo, &git_repo).unwrap();
|
||||
mut_repo.rebase_descendants(&test_data.settings).unwrap();
|
||||
|
||||
assert_eq!(git::export_refs(mut_repo, &git_repo), Ok(vec![]));
|
||||
// The export should be a no-op since nothing changed on the jj side since last
|
||||
// export
|
||||
assert_eq!(git::export_refs(mut_repo, &git_repo), Ok(vec![]));
|
||||
assert_eq!(
|
||||
mut_repo.get_git_ref("refs/heads/main"),
|
||||
Some(RefTarget::Normal(jj_id(&commit1)))
|
||||
);
|
||||
assert_eq!(git_repo.head().unwrap().name(), Some("refs/heads/main"));
|
||||
assert_eq!(
|
||||
git_repo.find_reference("refs/heads/main").unwrap().target(),
|
||||
Some(commit1.id())
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_export_refs_branch_changed() {
|
||||
// We can export a change to a branch
|
||||
|
@ -473,9 +443,6 @@ fn test_export_refs_branch_changed() {
|
|||
git_repo
|
||||
.reference("refs/heads/feature", commit.id(), false, "test")
|
||||
.unwrap();
|
||||
git_repo
|
||||
.reference("refs/heads/delete-me", commit.id(), false, "test")
|
||||
.unwrap();
|
||||
git_repo.set_head("refs/heads/feature").unwrap();
|
||||
|
||||
let mut tx = test_data
|
||||
|
@ -493,13 +460,11 @@ fn test_export_refs_branch_changed() {
|
|||
"main".to_string(),
|
||||
RefTarget::Normal(new_commit.id().clone()),
|
||||
);
|
||||
mut_repo.remove_local_branch("delete-me");
|
||||
assert_eq!(git::export_refs(mut_repo, &git_repo), Ok(vec![]));
|
||||
assert_eq!(
|
||||
mut_repo.get_git_ref("refs/heads/main"),
|
||||
Some(RefTarget::Normal(new_commit.id().clone()))
|
||||
);
|
||||
assert_eq!(mut_repo.get_git_ref("refs/heads/delete-me"), None);
|
||||
assert_eq!(
|
||||
git_repo
|
||||
.find_reference("refs/heads/main")
|
||||
|
@ -509,7 +474,6 @@ fn test_export_refs_branch_changed() {
|
|||
.id(),
|
||||
git_id(&new_commit)
|
||||
);
|
||||
assert!(git_repo.find_reference("refs/heads/delete-me").is_err());
|
||||
// HEAD should be unchanged since its target branch didn't change
|
||||
assert_eq!(git_repo.head().unwrap().name(), Some("refs/heads/feature"));
|
||||
}
|
||||
|
@ -753,6 +717,146 @@ fn test_export_partial_failure() {
|
|||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_export_reexport_transitions() {
|
||||
// Test exporting after making changes on the jj side, or the git side, or both
|
||||
let test_data = GitRepoData::create();
|
||||
let git_repo = test_data.git_repo;
|
||||
let mut tx = test_data
|
||||
.repo
|
||||
.start_transaction(&test_data.settings, "test");
|
||||
let mut_repo = tx.mut_repo();
|
||||
let commit_a =
|
||||
create_random_commit(&test_data.settings, &test_data.repo).write_to_repo(mut_repo);
|
||||
let commit_b =
|
||||
create_random_commit(&test_data.settings, &test_data.repo).write_to_repo(mut_repo);
|
||||
let commit_c =
|
||||
create_random_commit(&test_data.settings, &test_data.repo).write_to_repo(mut_repo);
|
||||
// Create a few branches whose names indicate how they change in jj in git. The
|
||||
// first letter represents the branch's target in the last export. The second
|
||||
// letter represents the branch's target in jj. The third letter represents the
|
||||
// branch's target in git. "X" means that the branch doesn't exist. "A", "B", or
|
||||
// "C" means that the branch points to that commit.
|
||||
//
|
||||
// AAB: Branch modified in git
|
||||
// AAX: Branch deleted in git
|
||||
// ABA: Branch modified in jj
|
||||
// ABB: Branch modified in both jj and git, pointing to same target
|
||||
// ABC: Branch modified in both jj and git, pointing to different targets
|
||||
// ABX: Branch modified in jj, deleted in git
|
||||
// AXA: Branch deleted in jj
|
||||
// AXB: Branch deleted in jj, modified in git
|
||||
// AXX: Branch deleted in both jj and git
|
||||
// XAA: Branch added in both jj and git, pointing to same target
|
||||
// XAB: Branch added in both jj and git, pointing to different targets
|
||||
// XAX: Branch added in jj
|
||||
// XXA: Branch added in git
|
||||
|
||||
// Create initial state and export it
|
||||
for branch in [
|
||||
"AAB", "AAX", "ABA", "ABB", "ABC", "ABX", "AXA", "AXB", "AXX",
|
||||
] {
|
||||
mut_repo.set_local_branch(branch.to_string(), RefTarget::Normal(commit_a.id().clone()));
|
||||
}
|
||||
assert_eq!(git::export_refs(mut_repo, &git_repo), Ok(vec![]));
|
||||
|
||||
// Make changes on the jj side
|
||||
for branch in ["AXA", "AXB", "AXX"] {
|
||||
mut_repo.remove_local_branch(branch);
|
||||
}
|
||||
for branch in ["XAA", "XAB", "XAX"] {
|
||||
mut_repo.set_local_branch(branch.to_string(), RefTarget::Normal(commit_a.id().clone()));
|
||||
}
|
||||
for branch in ["ABA", "ABB", "ABC", "ABX"] {
|
||||
mut_repo.set_local_branch(branch.to_string(), RefTarget::Normal(commit_b.id().clone()));
|
||||
}
|
||||
|
||||
// Make changes on the git side
|
||||
for branch in ["AAX", "ABX", "AXX"] {
|
||||
git_repo
|
||||
.find_reference(&format!("refs/heads/{branch}"))
|
||||
.unwrap()
|
||||
.delete()
|
||||
.unwrap();
|
||||
}
|
||||
for branch in ["XAA", "XXA"] {
|
||||
git_repo
|
||||
.reference(&format!("refs/heads/{branch}"), git_id(&commit_a), true, "")
|
||||
.unwrap();
|
||||
}
|
||||
for branch in ["AAB", "ABB", "AXB", "XAB"] {
|
||||
git_repo
|
||||
.reference(&format!("refs/heads/{branch}"), git_id(&commit_b), true, "")
|
||||
.unwrap();
|
||||
}
|
||||
let branch = "ABC";
|
||||
git_repo
|
||||
.reference(&format!("refs/heads/{branch}"), git_id(&commit_c), true, "")
|
||||
.unwrap();
|
||||
|
||||
// TODO: The branches that we made conflicting changes to should have failed to
|
||||
// export. They should have been unchanged in git and in
|
||||
// mut_repo.view().git_refs().
|
||||
assert_eq!(git::export_refs(mut_repo, &git_repo), Ok(vec![]));
|
||||
// TODO: AXB should have remained at B from git
|
||||
for branch in ["AAX", "AXA", "AXB", "AXX"] {
|
||||
assert!(
|
||||
git_repo
|
||||
.find_reference(&format!("refs/heads/{branch}"))
|
||||
.is_err(),
|
||||
"{branch} should not exist"
|
||||
);
|
||||
}
|
||||
// TODO: XAB should have remained at B from git
|
||||
for branch in ["XAA", "XAB", "XAX", "XXA"] {
|
||||
assert_eq!(
|
||||
git_repo
|
||||
.find_reference(&format!("refs/heads/{branch}"))
|
||||
.unwrap()
|
||||
.target(),
|
||||
Some(git_id(&commit_a)),
|
||||
"{branch} should point to commit A"
|
||||
);
|
||||
}
|
||||
// TODO: ABX should have remained deleted from git
|
||||
for branch in ["AAB", "ABA", "AAB", "ABB", "ABX"] {
|
||||
assert_eq!(
|
||||
git_repo
|
||||
.find_reference(&format!("refs/heads/{branch}"))
|
||||
.unwrap()
|
||||
.target(),
|
||||
Some(git_id(&commit_b)),
|
||||
"{branch} should point to commit B"
|
||||
);
|
||||
}
|
||||
// TODO: ABC should have remained at C from git
|
||||
let branch = "ABC";
|
||||
assert_eq!(
|
||||
git_repo
|
||||
.find_reference(&format!("refs/heads/{branch}"))
|
||||
.unwrap()
|
||||
.target(),
|
||||
Some(git_id(&commit_b)),
|
||||
"{branch} should point to commit B",
|
||||
);
|
||||
// TODO: ABC should remain at A, ABX should remain at A, AXB should remain at A,
|
||||
// XAB should remain missing.
|
||||
assert_eq!(
|
||||
*mut_repo.view().git_refs(),
|
||||
btreemap! {
|
||||
"refs/heads/AAX".to_string() => RefTarget::Normal(commit_a.id().clone()),
|
||||
"refs/heads/AAB".to_string() => RefTarget::Normal(commit_a.id().clone()),
|
||||
"refs/heads/ABA".to_string() => RefTarget::Normal(commit_b.id().clone()),
|
||||
"refs/heads/ABB".to_string() => RefTarget::Normal(commit_b.id().clone()),
|
||||
"refs/heads/ABC".to_string() => RefTarget::Normal(commit_b.id().clone()),
|
||||
"refs/heads/ABX".to_string() => RefTarget::Normal(commit_b.id().clone()),
|
||||
"refs/heads/XAA".to_string() => RefTarget::Normal(commit_a.id().clone()),
|
||||
"refs/heads/XAB".to_string() => RefTarget::Normal(commit_a.id().clone()),
|
||||
"refs/heads/XAX".to_string() => RefTarget::Normal(commit_a.id().clone()),
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_init() {
|
||||
let settings = testutils::user_settings();
|
||||
|
|
Loading…
Reference in a new issue