mirror of
https://github.com/martinvonz/jj.git
synced 2024-12-25 21:47:42 +00:00
ef5f97f8d7
The `merge` module now seems like the obvious place for this type.
387 lines
11 KiB
Rust
387 lines
11 KiB
Rust
// Copyright 2021 The Jujutsu Authors
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// https://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
use jj_lib::merge::Merge;
|
|
use jj_lib::op_store::RefTarget;
|
|
use jj_lib::refs::merge_ref_targets;
|
|
use jj_lib::repo::Repo;
|
|
use testutils::{CommitGraphBuilder, TestWorkspace};
|
|
|
|
#[test]
|
|
fn test_merge_ref_targets() {
|
|
let settings = testutils::user_settings();
|
|
let test_workspace = TestWorkspace::init(&settings, false);
|
|
let repo = &test_workspace.repo;
|
|
|
|
// 6 7
|
|
// |/
|
|
// 5
|
|
// | 3 4
|
|
// | |/
|
|
// | 2
|
|
// |/
|
|
// 1
|
|
let mut tx = repo.start_transaction(&settings, "test");
|
|
let mut graph_builder = CommitGraphBuilder::new(&settings, tx.mut_repo());
|
|
let commit1 = graph_builder.initial_commit();
|
|
let commit2 = graph_builder.commit_with_parents(&[&commit1]);
|
|
let commit3 = graph_builder.commit_with_parents(&[&commit2]);
|
|
let commit4 = graph_builder.commit_with_parents(&[&commit2]);
|
|
let commit5 = graph_builder.commit_with_parents(&[&commit1]);
|
|
let commit6 = graph_builder.commit_with_parents(&[&commit5]);
|
|
let commit7 = graph_builder.commit_with_parents(&[&commit5]);
|
|
let repo = tx.commit();
|
|
|
|
let target1 = RefTarget::normal(commit1.id().clone());
|
|
let target2 = RefTarget::normal(commit2.id().clone());
|
|
let target3 = RefTarget::normal(commit3.id().clone());
|
|
let target4 = RefTarget::normal(commit4.id().clone());
|
|
let target5 = RefTarget::normal(commit5.id().clone());
|
|
let target6 = RefTarget::normal(commit6.id().clone());
|
|
let _target7 = RefTarget::normal(commit7.id().clone());
|
|
|
|
let index = repo.index();
|
|
|
|
// Left moved forward
|
|
assert_eq!(
|
|
merge_ref_targets(index, &target3, &target1, &target1),
|
|
target3
|
|
);
|
|
|
|
// Right moved forward
|
|
assert_eq!(
|
|
merge_ref_targets(index, &target1, &target1, &target3),
|
|
target3
|
|
);
|
|
|
|
// Left moved backward
|
|
assert_eq!(
|
|
merge_ref_targets(index, &target1, &target3, &target3),
|
|
target1
|
|
);
|
|
|
|
// Right moved backward
|
|
assert_eq!(
|
|
merge_ref_targets(index, &target3, &target3, &target1),
|
|
target1
|
|
);
|
|
|
|
// Left moved sideways
|
|
assert_eq!(
|
|
merge_ref_targets(index, &target4, &target3, &target3),
|
|
target4
|
|
);
|
|
|
|
// Right moved sideways
|
|
assert_eq!(
|
|
merge_ref_targets(index, &target3, &target3, &target4),
|
|
target4
|
|
);
|
|
|
|
// Both added same target
|
|
assert_eq!(
|
|
merge_ref_targets(index, &target3, RefTarget::absent_ref(), &target3),
|
|
target3
|
|
);
|
|
|
|
// Left added target, right added descendant target
|
|
assert_eq!(
|
|
merge_ref_targets(index, &target2, RefTarget::absent_ref(), &target3),
|
|
target3
|
|
);
|
|
|
|
// Right added target, left added descendant target
|
|
assert_eq!(
|
|
merge_ref_targets(index, &target3, RefTarget::absent_ref(), &target2),
|
|
target3
|
|
);
|
|
|
|
// Both moved forward to same target
|
|
assert_eq!(
|
|
merge_ref_targets(index, &target3, &target1, &target3),
|
|
target3
|
|
);
|
|
|
|
// Both moved forward, left moved further
|
|
assert_eq!(
|
|
merge_ref_targets(index, &target3, &target1, &target2),
|
|
target3
|
|
);
|
|
|
|
// Both moved forward, right moved further
|
|
assert_eq!(
|
|
merge_ref_targets(index, &target2, &target1, &target3),
|
|
target3
|
|
);
|
|
|
|
// Left and right moved forward to divergent targets
|
|
assert_eq!(
|
|
merge_ref_targets(index, &target3, &target1, &target4),
|
|
RefTarget::from_legacy_form(
|
|
[commit1.id().clone()],
|
|
[commit3.id().clone(), commit4.id().clone()]
|
|
)
|
|
);
|
|
|
|
// Left moved back, right moved forward
|
|
assert_eq!(
|
|
merge_ref_targets(index, &target1, &target2, &target3),
|
|
RefTarget::from_legacy_form(
|
|
[commit2.id().clone()],
|
|
[commit1.id().clone(), commit3.id().clone()]
|
|
)
|
|
);
|
|
|
|
// Right moved back, left moved forward
|
|
assert_eq!(
|
|
merge_ref_targets(index, &target3, &target2, &target1),
|
|
RefTarget::from_legacy_form(
|
|
[commit2.id().clone()],
|
|
[commit3.id().clone(), commit1.id().clone()]
|
|
)
|
|
);
|
|
|
|
// Left removed
|
|
assert_eq!(
|
|
merge_ref_targets(index, RefTarget::absent_ref(), &target3, &target3),
|
|
RefTarget::absent()
|
|
);
|
|
|
|
// Right removed
|
|
assert_eq!(
|
|
merge_ref_targets(index, &target3, &target3, RefTarget::absent_ref()),
|
|
RefTarget::absent()
|
|
);
|
|
|
|
// Left removed, right moved forward
|
|
assert_eq!(
|
|
merge_ref_targets(index, RefTarget::absent_ref(), &target1, &target3),
|
|
RefTarget::from_merge(Merge::new(
|
|
vec![Some(commit1.id().clone())],
|
|
vec![None, Some(commit3.id().clone())],
|
|
))
|
|
);
|
|
|
|
// Right removed, left moved forward
|
|
assert_eq!(
|
|
merge_ref_targets(index, &target3, &target1, RefTarget::absent_ref()),
|
|
RefTarget::from_merge(Merge::new(
|
|
vec![Some(commit1.id().clone())],
|
|
vec![Some(commit3.id().clone()), None],
|
|
))
|
|
);
|
|
|
|
// Left became conflicted, right moved forward
|
|
assert_eq!(
|
|
merge_ref_targets(
|
|
index,
|
|
&RefTarget::from_legacy_form(
|
|
[commit2.id().clone()],
|
|
[commit3.id().clone(), commit4.id().clone()]
|
|
),
|
|
&target1,
|
|
&target3
|
|
),
|
|
// TODO: "removes" should have commit 2, just like it does in the next test case
|
|
RefTarget::from_legacy_form(
|
|
[commit1.id().clone()],
|
|
[commit4.id().clone(), commit3.id().clone()]
|
|
)
|
|
);
|
|
|
|
// Right became conflicted, left moved forward
|
|
assert_eq!(
|
|
merge_ref_targets(
|
|
index,
|
|
&target3,
|
|
&target1,
|
|
&RefTarget::from_legacy_form(
|
|
[commit2.id().clone()],
|
|
[commit3.id().clone(), commit4.id().clone()]
|
|
),
|
|
),
|
|
RefTarget::from_legacy_form(
|
|
[commit2.id().clone()],
|
|
[commit3.id().clone(), commit4.id().clone()]
|
|
)
|
|
);
|
|
|
|
// Existing conflict on left, right moves an "add" sideways
|
|
//
|
|
// Under the hood, the conflict is simplified as below:
|
|
// ```
|
|
// 3 4 5 3 4 5 5 4
|
|
// 2 / => 2 3 => 2
|
|
// 3
|
|
// ```
|
|
assert_eq!(
|
|
merge_ref_targets(
|
|
index,
|
|
&RefTarget::from_legacy_form(
|
|
[commit2.id().clone()],
|
|
[commit3.id().clone(), commit4.id().clone()]
|
|
),
|
|
&target3,
|
|
&target5
|
|
),
|
|
RefTarget::from_legacy_form(
|
|
[commit2.id().clone()],
|
|
[commit5.id().clone(), commit4.id().clone()]
|
|
)
|
|
);
|
|
|
|
// Existing conflict on right, left moves an "add" sideways
|
|
//
|
|
// Under the hood, the conflict is simplified as below:
|
|
// ```
|
|
// 5 3 4 5 3 4 5 4
|
|
// \ 2 => 3 2 => 2
|
|
// 3
|
|
// ```
|
|
assert_eq!(
|
|
merge_ref_targets(
|
|
index,
|
|
&target5,
|
|
&target3,
|
|
&RefTarget::from_legacy_form(
|
|
[commit2.id().clone()],
|
|
[commit3.id().clone(), commit4.id().clone()]
|
|
),
|
|
),
|
|
RefTarget::from_legacy_form(
|
|
[commit2.id().clone()],
|
|
[commit5.id().clone(), commit4.id().clone()]
|
|
)
|
|
);
|
|
|
|
// Existing conflict on left, right moves an "add" backwards, past point of
|
|
// divergence
|
|
//
|
|
// Under the hood, the conflict is simplified as below:
|
|
// ```
|
|
// 3 4 1 3 4 1 1 4
|
|
// 2 / => 2 3 => 2
|
|
// 3
|
|
// ```
|
|
assert_eq!(
|
|
merge_ref_targets(
|
|
index,
|
|
&RefTarget::from_legacy_form(
|
|
[commit2.id().clone()],
|
|
[commit3.id().clone(), commit4.id().clone()]
|
|
),
|
|
&target3,
|
|
&target1
|
|
),
|
|
RefTarget::from_legacy_form(
|
|
[commit2.id().clone()],
|
|
[commit1.id().clone(), commit4.id().clone()]
|
|
)
|
|
);
|
|
|
|
// Existing conflict on right, left moves an "add" backwards, past point of
|
|
// divergence
|
|
//
|
|
// Under the hood, the conflict is simplified as below:
|
|
// ```
|
|
// 1 3 4 1 3 4 1 4
|
|
// \ 2 => 3 2 => 2
|
|
// 3
|
|
// ```
|
|
assert_eq!(
|
|
merge_ref_targets(
|
|
index,
|
|
&target1,
|
|
&target3,
|
|
&RefTarget::from_legacy_form(
|
|
[commit2.id().clone()],
|
|
[commit3.id().clone(), commit4.id().clone()]
|
|
),
|
|
),
|
|
RefTarget::from_legacy_form(
|
|
[commit2.id().clone()],
|
|
[commit1.id().clone(), commit4.id().clone()]
|
|
)
|
|
);
|
|
|
|
// Existing conflict on left, right undoes one side of conflict
|
|
assert_eq!(
|
|
merge_ref_targets(
|
|
index,
|
|
&RefTarget::from_legacy_form(
|
|
[commit2.id().clone()],
|
|
[commit3.id().clone(), commit4.id().clone()]
|
|
),
|
|
&target3,
|
|
&target2
|
|
),
|
|
target4
|
|
);
|
|
|
|
// Existing conflict on right, left undoes one side of conflict
|
|
assert_eq!(
|
|
merge_ref_targets(
|
|
index,
|
|
&target2,
|
|
&target3,
|
|
&RefTarget::from_legacy_form(
|
|
[commit2.id().clone()],
|
|
[commit3.id().clone(), commit4.id().clone()]
|
|
),
|
|
),
|
|
target4
|
|
);
|
|
|
|
// Existing conflict on left, right makes unrelated update
|
|
assert_eq!(
|
|
merge_ref_targets(
|
|
index,
|
|
&RefTarget::from_legacy_form(
|
|
[commit2.id().clone()],
|
|
[commit3.id().clone(), commit4.id().clone()]
|
|
),
|
|
&target5,
|
|
&target6
|
|
),
|
|
RefTarget::from_legacy_form(
|
|
[commit2.id().clone(), commit5.id().clone()],
|
|
[
|
|
commit3.id().clone(),
|
|
commit4.id().clone(),
|
|
commit6.id().clone()
|
|
]
|
|
)
|
|
);
|
|
|
|
// Existing conflict on right, left makes unrelated update
|
|
assert_eq!(
|
|
merge_ref_targets(
|
|
index,
|
|
&target6,
|
|
&target5,
|
|
&RefTarget::from_legacy_form(
|
|
[commit2.id().clone()],
|
|
[commit3.id().clone(), commit4.id().clone()]
|
|
),
|
|
),
|
|
RefTarget::from_legacy_form(
|
|
[commit5.id().clone(), commit2.id().clone()],
|
|
[
|
|
commit6.id().clone(),
|
|
commit3.id().clone(),
|
|
commit4.id().clone()
|
|
]
|
|
)
|
|
);
|
|
}
|