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) 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]);

View file

@ -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
}) { }) {