From 11b9888cdfae35bf358464c1d47a741f4c7ff34a Mon Sep 17 00:00:00 2001 From: Yuya Nishihara Date: Sun, 1 Sep 2024 13:44:05 +0900 Subject: [PATCH] refs: retry trivial resolution after merging targets This helps resolve diverged refs by abandoning both sides: D ref = [D] |\ | C C ref = [B - D + C] | | | B | B | B ref = [B - D + A] |/ |/ | A A A A ref = [A - D + A] --- lib/src/refs.rs | 10 ++++++++-- lib/tests/test_refs.rs | 30 ++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 2 deletions(-) diff --git a/lib/src/refs.rs b/lib/src/refs.rs index b14e273bf..cef525595 100644 --- a/lib/src/refs.rs +++ b/lib/src/refs.rs @@ -102,10 +102,16 @@ pub fn merge_ref_targets( ]) .flatten() .simplify(); - if !merge.is_resolved() { + // Suppose left = [A - C + B], base = [B], right = [A], the merge result is + // [A - C + A], which can now be trivially resolved. + if let Some(resolved) = merge.resolve_trivial() { + RefTarget::resolved(resolved.clone()) + } else { merge_ref_targets_non_trivial(index, &mut merge); + // TODO: Maybe better to try resolve_trivial() again, but the result is + // unreliable since merge_ref_targets_non_trivial() is order dependent. + RefTarget::from_merge(merge) } - RefTarget::from_merge(merge) } pub fn merge_remote_refs( diff --git a/lib/tests/test_refs.rs b/lib/tests/test_refs.rs index a60c9d00c..9c77db932 100644 --- a/lib/tests/test_refs.rs +++ b/lib/tests/test_refs.rs @@ -363,6 +363,36 @@ fn test_merge_ref_targets() { target4 ); + // Existing conflict on left, right moves one side of conflict to the other + // side ("A - B + A" - type conflict) + assert_eq!( + merge_ref_targets( + index, + &RefTarget::from_legacy_form( + [commit5.id().clone()], // not an ancestor of commit3, 4 + [commit3.id().clone(), commit4.id().clone()], + ), + &target4, + &target3, + ), + target3 + ); + + // Existing conflict on right, left moves one side of conflict to the other + // side ("A - B + A" - type conflict) + assert_eq!( + merge_ref_targets( + index, + &target4, + &target3, + &RefTarget::from_legacy_form( + [commit5.id().clone()], // not an ancestor of commit3, 4 + [commit3.id().clone(), commit4.id().clone()], + ), + ), + target4 + ); + // Existing conflict on left, right makes unrelated update assert_eq!( merge_ref_targets(