@@ -39,40 +39,47 @@ pub struct Outcome<'a> {
3939 /// Use [`has_unresolved_conflicts()`](Outcome::has_unresolved_conflicts()) to see if any action is needed
4040 /// before using [`tree`](Outcome::tree).
4141 pub conflicts : Vec < Conflict > ,
42- /// `true` if `conflicts` contains only a single *unresolved* conflict in the last slot, but possibly more resolved ones.
42+ /// `true` if `conflicts` contains only a single [*unresolved* conflict](ResolutionFailure) in the last slot, but
43+ /// possibly more [resolved ones](Resolution) before that.
4344 /// This also makes this outcome a very partial merge that cannot be completed.
4445 /// Only set if [`fail_on_conflict`](Options::fail_on_conflict) is `true`.
4546 pub failed_on_first_unresolved_conflict : bool ,
4647}
4748
4849/// Determine what should be considered an unresolved conflict.
50+ #[ derive( Default , Debug , Copy , Clone , Eq , PartialEq , Ord , PartialOrd , Hash ) ]
51+ pub struct TreatAsUnresolved {
52+ /// Determine which content merges should be considered unresolved.
53+ pub content_merge : treat_as_unresolved:: ContentMerge ,
54+ /// Determine which tree merges should be considered unresolved.
55+ pub tree_merge : treat_as_unresolved:: TreeMerge ,
56+ }
57+
4958///
50- /// Note that no matter which variant, [conflicts](Conflict) with
51- /// [resolution failure](`ResolutionFailure`) will always be unresolved.
52- ///
53- /// Also, when one side was modified but the other side renamed it, this will not
54- /// be considered a conflict, even if a non-conflicting merge happened.
55- #[ derive( Debug , Copy , Clone , Eq , PartialEq , Ord , PartialOrd , Hash ) ]
56- pub enum TreatAsUnresolved {
57- /// Only consider content merges with conflict markers as unresolved.
58- ///
59- /// Auto-resolved tree conflicts will *not* be considered unresolved.
60- ConflictMarkers ,
61- /// Consider content merges with conflict markers as unresolved, and content
62- /// merges where conflicts where auto-resolved in any way, like choosing
63- /// *ours*, *theirs* or by their *union*.
64- ///
65- /// Auto-resolved tree conflicts will *not* be considered unresolved.
66- ConflictMarkersAndAutoResolved ,
67- /// Whenever there were conflicting renames, or conflict markers, it is unresolved.
68- /// Note that auto-resolved content merges will *not* be considered unresolved.
69- ///
70- /// Also note that files that were changed in one and renamed in another will
71- /// be moved into place, which will be considered resolved.
72- Renames ,
73- /// Similar to [`Self::Renames`], but auto-resolved content-merges will
74- /// also be considered unresolved.
75- RenamesAndAutoResolvedContent ,
59+ pub mod treat_as_unresolved {
60+ /// Which kind of content merges should be considered unresolved?
61+ #[ derive( Default , Debug , Copy , Clone , Eq , PartialEq , Ord , PartialOrd , Hash ) ]
62+ pub enum ContentMerge {
63+ /// Content merges that still show conflict markers.
64+ #[ default]
65+ Markers ,
66+ /// Content merges who would have conflicted if it wasn't for a
67+ /// [resolution strategy](crate::blob::builtin_driver::text::Conflict::ResolveWithOurs).
68+ ForcedResolution ,
69+ }
70+
71+ /// Which kind of tree merges should be considered unresolved?
72+ #[ derive( Default , Debug , Copy , Clone , Eq , PartialEq , Ord , PartialOrd , Hash ) ]
73+ pub enum TreeMerge {
74+ /// All failed renames.
75+ Undecidable ,
76+ /// All failed renames, and the ones where a tree item was renamed to avoid a clash.
77+ #[ default]
78+ EvasiveRenames ,
79+ /// All of `EvasiveRenames`, and tree merges that would have conflicted but which were resolved
80+ /// with a [resolution strategy](super::ResolveWith).
81+ ForcedResolution ,
82+ }
7683}
7784
7885impl Outcome < ' _ > {
@@ -174,35 +181,36 @@ impl Conflict {
174181 /// Return `true` if this instance is considered unresolved based on the criterion specified by `how`.
175182 pub fn is_unresolved ( & self , how : TreatAsUnresolved ) -> bool {
176183 use crate :: blob;
177- let content_merge_matches = |info : & ContentMerge | match how {
178- TreatAsUnresolved :: ConflictMarkers | TreatAsUnresolved :: Renames => {
179- matches ! ( info. resolution, blob:: Resolution :: Conflict )
180- }
181- TreatAsUnresolved :: RenamesAndAutoResolvedContent | TreatAsUnresolved :: ConflictMarkersAndAutoResolved => {
184+ let content_merge_matches = |info : & ContentMerge | match how. content_merge {
185+ treat_as_unresolved:: ContentMerge :: Markers => matches ! ( info. resolution, blob:: Resolution :: Conflict ) ,
186+ treat_as_unresolved:: ContentMerge :: ForcedResolution => {
182187 matches ! (
183188 info. resolution,
184189 blob:: Resolution :: Conflict | blob:: Resolution :: CompleteWithAutoResolvedConflict
185190 )
186191 }
187192 } ;
188- match how {
189- TreatAsUnresolved :: ConflictMarkers | TreatAsUnresolved :: ConflictMarkersAndAutoResolved => {
193+ match how. tree_merge {
194+ treat_as_unresolved :: TreeMerge :: Undecidable => {
190195 self . resolution . is_err ( ) || self . content_merge ( ) . map_or ( false , |info| content_merge_matches ( & info) )
191196 }
192- TreatAsUnresolved :: Renames | TreatAsUnresolved :: RenamesAndAutoResolvedContent => match & self . resolution {
193- Ok ( success) => match success {
194- Resolution :: SourceLocationAffectedByRename { .. } => false ,
195- Resolution :: OursModifiedTheirsRenamedAndChangedThenRename {
196- merged_blob,
197- final_location,
198- ..
199- } => final_location. is_some ( ) || merged_blob. as_ref ( ) . map_or ( false , content_merge_matches) ,
200- Resolution :: OursModifiedTheirsModifiedThenBlobContentMerge { merged_blob } => {
201- content_merge_matches ( merged_blob)
202- }
203- } ,
204- Err ( _failure) => true ,
205- } ,
197+ treat_as_unresolved:: TreeMerge :: EvasiveRenames | treat_as_unresolved:: TreeMerge :: ForcedResolution => {
198+ match & self . resolution {
199+ Ok ( success) => match success {
200+ Resolution :: SourceLocationAffectedByRename { .. } => false ,
201+ Resolution :: Forced ( _) => how. tree_merge == treat_as_unresolved:: TreeMerge :: ForcedResolution ,
202+ Resolution :: OursModifiedTheirsRenamedAndChangedThenRename {
203+ merged_blob,
204+ final_location,
205+ ..
206+ } => final_location. is_some ( ) || merged_blob. as_ref ( ) . map_or ( false , content_merge_matches) ,
207+ Resolution :: OursModifiedTheirsModifiedThenBlobContentMerge { merged_blob } => {
208+ content_merge_matches ( merged_blob)
209+ }
210+ } ,
211+ Err ( _failure) => true ,
212+ }
213+ }
206214 }
207215 }
208216
@@ -237,13 +245,8 @@ impl Conflict {
237245
238246 /// Return information about the content merge if it was performed.
239247 pub fn content_merge ( & self ) -> Option < ContentMerge > {
240- match & self . resolution {
241- Ok ( success) => match success {
242- Resolution :: SourceLocationAffectedByRename { .. } => None ,
243- Resolution :: OursModifiedTheirsRenamedAndChangedThenRename { merged_blob, .. } => * merged_blob,
244- Resolution :: OursModifiedTheirsModifiedThenBlobContentMerge { merged_blob } => Some ( * merged_blob) ,
245- } ,
246- Err ( failure) => match failure {
248+ fn failure_merged_blob ( failure : & ResolutionFailure ) -> Option < ContentMerge > {
249+ match failure {
247250 ResolutionFailure :: OursRenamedTheirsRenamedDifferently { merged_blob } => * merged_blob,
248251 ResolutionFailure :: Unknown
249252 | ResolutionFailure :: OursModifiedTheirsDeleted
@@ -253,7 +256,16 @@ impl Conflict {
253256 }
254257 | ResolutionFailure :: OursAddedTheirsAddedTypeMismatch { .. }
255258 | ResolutionFailure :: OursDeletedTheirsRenamed => None ,
259+ }
260+ }
261+ match & self . resolution {
262+ Ok ( success) => match success {
263+ Resolution :: Forced ( failure) => failure_merged_blob ( failure) ,
264+ Resolution :: SourceLocationAffectedByRename { .. } => None ,
265+ Resolution :: OursModifiedTheirsRenamedAndChangedThenRename { merged_blob, .. } => * merged_blob,
266+ Resolution :: OursModifiedTheirsModifiedThenBlobContentMerge { merged_blob } => Some ( * merged_blob) ,
256267 } ,
268+ Err ( failure) => failure_merged_blob ( failure) ,
257269 }
258270 }
259271}
@@ -291,6 +303,9 @@ pub enum Resolution {
291303 /// The outcome of the content merge.
292304 merged_blob : ContentMerge ,
293305 } ,
306+ /// This is a resolution failure was forcefully turned into a usable resolution, i.e. [making a choice](ResolveWith)
307+ /// is turned into a valid resolution.
308+ Forced ( ResolutionFailure ) ,
294309}
295310
296311/// Describes of a conflict involving *our* change and *their* failed to be resolved.
@@ -358,13 +373,26 @@ pub struct Options {
358373 /// If `None`, when symlinks clash *ours* will be chosen and a conflict will occur.
359374 /// Otherwise, the same logic applies as for the merge of binary resources.
360375 pub symlink_conflicts : Option < crate :: blob:: builtin_driver:: binary:: ResolveWith > ,
361- /// If `true`, instead of issuing a conflict with [`ResolutionFailure`], do nothing and keep the base/ancestor
362- /// version. This is useful when one wants to avoid any kind of merge-conflict to have *some*, *lossy* resolution.
363- pub allow_lossy_resolution : bool ,
376+ /// If `None`, tree irreconcilable tree conflicts will result in [resolution failures](ResolutionFailure).
377+ /// Otherwise, one can choose a side. Note that it's still possible to determine that auto-resolution happened
378+ /// despite this choice, which allows carry forward the conflicting information, possibly for later resolution.
379+ /// If `Some(…)`, irreconcilable conflicts are reconciled by making a choice. This mlso means that [`Conflict::entries()`]
380+ /// won't be set as the conflict was officially resolved.
381+ pub tree_conflicts : Option < ResolveWith > ,
382+ }
383+
384+ /// Decide how to resolve tree-related conflicts, but only those that have [no way of being correct](ResolutionFailure).
385+ #[ derive( Copy , Clone , Debug , Eq , PartialEq , Ord , PartialOrd , Hash ) ]
386+ pub enum ResolveWith {
387+ /// On irreconcilable conflict, choose neither *our* nor *their* state, but keep the common *ancestor* state instead.
388+ Ancestor ,
389+ /// On irreconcilable conflict, choose *our* side
390+ Ours ,
364391}
365392
366393pub ( super ) mod function;
367394mod utils;
395+ ///
368396pub mod apply_index_entries {
369397
370398 pub ( super ) mod function {
@@ -398,6 +426,7 @@ pub mod apply_index_entries {
398426 for conflict in conflicts. iter ( ) . filter ( |c| c. is_unresolved ( how) ) {
399427 let ( renamed_path, current_path) : ( Option < & BStr > , & BStr ) = match & conflict. resolution {
400428 Ok ( success) => match success {
429+ Resolution :: Forced ( _) => continue ,
401430 Resolution :: SourceLocationAffectedByRename { final_location } => {
402431 ( Some ( final_location. as_bstr ( ) ) , final_location. as_bstr ( ) )
403432 }
0 commit comments