diff --git a/rust/hg-core/src/copy_tracing.rs b/rust/hg-core/src/copy_tracing.rs --- a/rust/hg-core/src/copy_tracing.rs +++ b/rust/hg-core/src/copy_tracing.rs @@ -9,6 +9,7 @@ use std::cmp::Ordering; use std::collections::HashMap; +use std::collections::HashSet; use std::convert::TryInto; pub type PathCopies = HashMap; @@ -22,6 +23,9 @@ /// the copy source, (Set to None in case of deletion of the associated /// key) path: Option, + /// a set of previous `CopySource.rev` value directly or indirectly + /// overwritten by this one. + overwritten: HashSet, } impl CopySource { @@ -29,7 +33,11 @@ /// /// Use this when no previous copy source existed. fn new(rev: Revision, path: Option) -> Self { - Self { rev, path } + Self { + rev, + path, + overwritten: HashSet::new(), + } } /// create a new CopySource from merging two others @@ -37,9 +45,15 @@ /// Use this when merging two InternalPathCopies requires active merging of /// some entries. fn new_from_merge(rev: Revision, winner: &Self, loser: &Self) -> Self { + let mut overwritten = HashSet::new(); + overwritten.extend(winner.overwritten.iter().copied()); + overwritten.extend(loser.overwritten.iter().copied()); + overwritten.insert(winner.rev); + overwritten.insert(loser.rev); Self { rev, path: winner.path, + overwritten: overwritten, } } @@ -47,6 +61,7 @@ /// /// Use this when recording copy information from parent → child edges fn overwrite(&mut self, rev: Revision, path: Option) { + self.overwritten.insert(self.rev); self.rev = rev; self.path = path; } @@ -55,9 +70,14 @@ /// /// Use this when recording copy information from parent → child edges fn mark_delete(&mut self, rev: Revision) { + self.overwritten.insert(self.rev); self.rev = rev; self.path = None; } + + fn is_overwritten_by(&self, other: &Self) -> bool { + other.overwritten.contains(&self.rev) + } } /// maps CopyDestination to Copy Source (+ a "timestamp" for the operation) @@ -834,8 +854,12 @@ if src_major.path.is_none() { // We cannot get different copy information for both p1 and p2 // from the same revision. Unless this was a - // deletion - (MergePick::Any, false) + // deletion. + // + // However the deletion might come over different data on each + // branch. + let need_over = src_major.overwritten != src_minor.overwritten; + (MergePick::Any, need_over) } else { unreachable!(); } @@ -852,10 +876,11 @@ // we have the same value, but from other source; if src_major.rev == src_minor.rev { // If the two entry are identical, they are both valid + debug_assert!(src_minor.overwritten == src_minor.overwritten); (MergePick::Any, false) - } else if oracle.is_overwrite(src_major.rev, src_minor.rev) { + } else if src_major.is_overwritten_by(src_minor) { (MergePick::Minor, false) - } else if oracle.is_overwrite(src_minor.rev, src_major.rev) { + } else if src_minor.is_overwritten_by(src_major) { (MergePick::Major, false) } else { (MergePick::Any, true) @@ -884,7 +909,7 @@ // salvaged by the merge, unconditionnaly preserve the // major side. (MergePick::Major, true) - } else if oracle.is_overwrite(src_minor.rev, src_major.rev) { + } else if src_minor.is_overwritten_by(src_major) { // The information from the minor version are strictly older than // the major version if action == MergeCase::Merged { @@ -898,7 +923,7 @@ // No activity on the minor branch, pick the newer one. (MergePick::Major, false) } - } else if oracle.is_overwrite(src_major.rev, src_minor.rev) { + } else if src_major.is_overwritten_by(src_minor) { if action == MergeCase::Merged { // If the file was actively merged, its means some non-copy // activity happened on the other branch. It