refs: run non-trivial merge of ref targets without destructuring Merge object

This commit is contained in:
Yuya Nishihara 2023-11-06 17:26:41 +09:00
parent 93601541cb
commit 92dfe59ade
2 changed files with 31 additions and 21 deletions

View file

@ -206,6 +206,15 @@ impl<T> Merge<T> {
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.
pub fn num_sides(&self) -> usize {
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]
fn test_pad_to() {
let mut x = c(&[], &[1]);

View file

@ -93,19 +93,16 @@ pub fn merge_ref_targets(
return resolved.clone();
}
let merge = Merge::new(
let mut merge = Merge::new(
vec![base.as_merge().clone()],
vec![left.as_merge().clone(), right.as_merge().clone()],
)
.flatten()
.simplify();
if merge.is_resolved() {
RefTarget::from_merge(merge)
} else {
let merge = merge_ref_targets_non_trivial(index, merge);
RefTarget::from_merge(merge)
if !merge.is_resolved() {
merge_ref_targets_non_trivial(index, &mut merge);
}
RefTarget::from_merge(merge)
}
pub fn merge_remote_refs(
@ -127,27 +124,20 @@ pub fn merge_remote_refs(
RemoteRef { target, state }
}
fn merge_ref_targets_non_trivial(
index: &dyn Index,
conflict: Merge<Option<CommitId>>,
) -> 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);
fn merge_ref_targets_non_trivial(index: &dyn Index, conflict: &mut Merge<Option<CommitId>>) {
while let Some((remove_index, add_index)) = find_pair_to_remove(index, conflict) {
conflict.swap_remove(remove_index, add_index);
}
Merge::new(removes, adds)
}
fn find_pair_to_remove(
index: &dyn Index,
removes: &[Option<CommitId>],
adds: &[Option<CommitId>],
conflict: &Merge<Option<CommitId>>,
) -> Option<(usize, usize)> {
// 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.
for (add_index1, add1) in adds.iter().enumerate() {
for (add_index2, add2) in adds.iter().enumerate().skip(add_index1 + 1) {
for (add_index1, add1) in conflict.adds().enumerate() {
for (add_index2, add2) in conflict.adds().enumerate().skip(add_index1 + 1) {
// TODO: Instead of relying on the list order, maybe ((add1, add2), remove)
// combination should be somehow weighted?
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),
_ => 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),
None => true, // Absent ref can be considered a root
}) {