mirror of
https://github.com/martinvonz/jj.git
synced 2025-02-10 22:39:32 +00:00
refs: run non-trivial merge of ref targets without destructuring Merge object
This commit is contained in:
parent
93601541cb
commit
92dfe59ade
2 changed files with 31 additions and 21 deletions
|
@ -206,6 +206,15 @@ impl<T> Merge<T> {
|
||||||
self.values.get(index * 2)
|
self.values.get(index * 2)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Removes the specified "removed"/"added" values. The removed slots are
|
||||||
|
/// replaced by the last "removed"/"added" values.
|
||||||
|
pub fn swap_remove(&mut self, remove_index: usize, add_index: usize) -> (T, T) {
|
||||||
|
// Swap with the last "added" and "removed" values in order.
|
||||||
|
let add = self.values.swap_remove(add_index * 2);
|
||||||
|
let remove = self.values.swap_remove(remove_index * 2 + 1);
|
||||||
|
(remove, add)
|
||||||
|
}
|
||||||
|
|
||||||
/// The number of positive terms in the conflict.
|
/// The number of positive terms in the conflict.
|
||||||
pub fn num_sides(&self) -> usize {
|
pub fn num_sides(&self) -> usize {
|
||||||
self.values.len() / 2 + 1
|
self.values.len() / 2 + 1
|
||||||
|
@ -815,6 +824,17 @@ mod tests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_swap_remove() {
|
||||||
|
let mut x = c(&[1, 3, 5], &[0, 2, 4, 6]);
|
||||||
|
assert_eq!(x.swap_remove(0, 1), (1, 2));
|
||||||
|
assert_eq!(x, c(&[5, 3], &[0, 6, 4]));
|
||||||
|
assert_eq!(x.swap_remove(1, 0), (3, 0));
|
||||||
|
assert_eq!(x, c(&[5], &[4, 6]));
|
||||||
|
assert_eq!(x.swap_remove(0, 1), (5, 6));
|
||||||
|
assert_eq!(x, c(&[], &[4]));
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_pad_to() {
|
fn test_pad_to() {
|
||||||
let mut x = c(&[], &[1]);
|
let mut x = c(&[], &[1]);
|
||||||
|
|
|
@ -93,19 +93,16 @@ pub fn merge_ref_targets(
|
||||||
return resolved.clone();
|
return resolved.clone();
|
||||||
}
|
}
|
||||||
|
|
||||||
let merge = Merge::new(
|
let mut merge = Merge::new(
|
||||||
vec![base.as_merge().clone()],
|
vec![base.as_merge().clone()],
|
||||||
vec![left.as_merge().clone(), right.as_merge().clone()],
|
vec![left.as_merge().clone(), right.as_merge().clone()],
|
||||||
)
|
)
|
||||||
.flatten()
|
.flatten()
|
||||||
.simplify();
|
.simplify();
|
||||||
|
if !merge.is_resolved() {
|
||||||
if merge.is_resolved() {
|
merge_ref_targets_non_trivial(index, &mut merge);
|
||||||
RefTarget::from_merge(merge)
|
|
||||||
} else {
|
|
||||||
let merge = merge_ref_targets_non_trivial(index, merge);
|
|
||||||
RefTarget::from_merge(merge)
|
|
||||||
}
|
}
|
||||||
|
RefTarget::from_merge(merge)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn merge_remote_refs(
|
pub fn merge_remote_refs(
|
||||||
|
@ -127,27 +124,20 @@ pub fn merge_remote_refs(
|
||||||
RemoteRef { target, state }
|
RemoteRef { target, state }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn merge_ref_targets_non_trivial(
|
fn merge_ref_targets_non_trivial(index: &dyn Index, conflict: &mut Merge<Option<CommitId>>) {
|
||||||
index: &dyn Index,
|
while let Some((remove_index, add_index)) = find_pair_to_remove(index, conflict) {
|
||||||
conflict: Merge<Option<CommitId>>,
|
conflict.swap_remove(remove_index, add_index);
|
||||||
) -> Merge<Option<CommitId>> {
|
|
||||||
let (mut removes, mut adds) = conflict.take();
|
|
||||||
while let Some((remove_index, add_index)) = find_pair_to_remove(index, &removes, &adds) {
|
|
||||||
removes.swap_remove(remove_index);
|
|
||||||
adds.swap_remove(add_index);
|
|
||||||
}
|
}
|
||||||
Merge::new(removes, adds)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn find_pair_to_remove(
|
fn find_pair_to_remove(
|
||||||
index: &dyn Index,
|
index: &dyn Index,
|
||||||
removes: &[Option<CommitId>],
|
conflict: &Merge<Option<CommitId>>,
|
||||||
adds: &[Option<CommitId>],
|
|
||||||
) -> Option<(usize, usize)> {
|
) -> Option<(usize, usize)> {
|
||||||
// If a "remove" is an ancestor of two different "adds" and one of the
|
// If a "remove" is an ancestor of two different "adds" and one of the
|
||||||
// "adds" is an ancestor of the other, then pick the descendant.
|
// "adds" is an ancestor of the other, then pick the descendant.
|
||||||
for (add_index1, add1) in adds.iter().enumerate() {
|
for (add_index1, add1) in conflict.adds().enumerate() {
|
||||||
for (add_index2, add2) in adds.iter().enumerate().skip(add_index1 + 1) {
|
for (add_index2, add2) in conflict.adds().enumerate().skip(add_index1 + 1) {
|
||||||
// TODO: Instead of relying on the list order, maybe ((add1, add2), remove)
|
// TODO: Instead of relying on the list order, maybe ((add1, add2), remove)
|
||||||
// combination should be somehow weighted?
|
// combination should be somehow weighted?
|
||||||
let (add_index, add_id) = match (add1, add2) {
|
let (add_index, add_id) = match (add1, add2) {
|
||||||
|
@ -156,7 +146,7 @@ fn find_pair_to_remove(
|
||||||
(Some(id1), Some(id2)) if index.is_ancestor(id2, id1) => (add_index2, id2),
|
(Some(id1), Some(id2)) if index.is_ancestor(id2, id1) => (add_index2, id2),
|
||||||
_ => continue,
|
_ => continue,
|
||||||
};
|
};
|
||||||
if let Some(remove_index) = removes.iter().position(|remove| match remove {
|
if let Some(remove_index) = conflict.removes().position(|remove| match remove {
|
||||||
Some(id) => index.is_ancestor(id, add_id),
|
Some(id) => index.is_ancestor(id, add_id),
|
||||||
None => true, // Absent ref can be considered a root
|
None => true, // Absent ref can be considered a root
|
||||||
}) {
|
}) {
|
||||||
|
|
Loading…
Reference in a new issue