@@ -43,31 +43,25 @@ pub struct Outcome<'a> {
4343     pub  failed_on_first_unresolved_conflict :  bool , 
4444} 
4545
46+ /// Determine what should be considered an unresolved conflict. 
47+ /// 
48+ /// Note that no matter which variant, [conflicts](Conflict) with [resolution failure](`ResolutionFailure`) 
49+ /// will always be unresolved. 
50+ /// 
51+ /// However, those whose resolution was 
52+ #[ derive( Debug ,  Copy ,  Clone ,  Eq ,  PartialEq ,  Ord ,  PartialOrd ,  Hash ) ]  
53+ pub  enum  UnresolvedConflict  { 
54+     /// Only consider content merges with conflict markers as unresolved. 
55+      ConflictMarkers , 
56+     /// Whenever there was any rename, or conflict markers, it is unresolved. 
57+      Renames , 
58+ } 
59+ 
4660impl  Outcome < ' _ >  { 
4761    /// Return `true` if there is any conflict that would still need to be resolved as they would yield undesirable trees. 
48-      /// Note that this interpretation of conflicts and their resolution, see 
49-      /// [`has_unresolved_conflicts_strict`](Self::has_unresolved_conflicts_strict). 
50-      pub  fn  has_unresolved_conflicts ( & self )  -> bool  { 
51-         self . conflicts . iter ( ) . any ( |c| { 
52-             c. resolution . is_err ( ) 
53-                 || c. content_merge ( ) . map_or ( false ,  |info| { 
54-                     matches ! ( info. resolution,  crate :: blob:: Resolution :: Conflict ) 
55-                 } ) 
56-         } ) 
57-     } 
58- 
59-     /// Return `true` only if there was any (even resolved) conflict in the tree structure, or if there are still conflict markers. 
60-      pub  fn  has_unresolved_conflicts_strict ( & self )  -> bool  { 
61-         self . conflicts . iter ( ) . any ( |c| match  & c. resolution  { 
62-             Ok ( success)  => match  success { 
63-                 Resolution :: OursAddedTheirsAddedTypeMismatch  {  .. } 
64-                 | Resolution :: OursModifiedTheirsRenamedAndChangedThenRename  {  .. }  => true , 
65-                 Resolution :: OursModifiedTheirsModifiedThenBlobContentMerge  {  merged_blob }  => { 
66-                     matches ! ( merged_blob. resolution,  crate :: blob:: Resolution :: Conflict ) 
67-                 } 
68-             } , 
69-             Err ( _failure)  => true , 
70-         } ) 
62+      /// This is based on `how` to determine what should be considered unresolved. 
63+      pub  fn  has_unresolved_conflicts ( & self ,  how :  UnresolvedConflict )  -> bool  { 
64+         function:: is_unresolved ( & self . conflicts ,  how) 
7165    } 
7266} 
7367
@@ -135,14 +129,16 @@ impl Conflict {
135129     pub  fn  content_merge ( & self )  -> Option < ContentMerge >  { 
136130        match  & self . resolution  { 
137131            Ok ( success)  => match  success { 
138-                 Resolution :: OursAddedTheirsAddedTypeMismatch  {  .. }  => None , 
132+                 Resolution :: SourceLocationAffectedByRename  {  .. }  => None , 
139133                Resolution :: OursModifiedTheirsRenamedAndChangedThenRename  {  merged_blob,  .. }  => * merged_blob, 
140134                Resolution :: OursModifiedTheirsModifiedThenBlobContentMerge  {  merged_blob }  => Some ( * merged_blob) , 
141135            } , 
142136            Err ( failure)  => match  failure { 
137+                 ResolutionFailure :: OursRenamedTheirsRenamedDifferently  {  merged_blob }  => * merged_blob, 
143138                ResolutionFailure :: OursModifiedTheirsDirectoryThenOursRenamed  { 
144-                     renamed_path_to_modified_blob :  _, 
139+                     renamed_unique_path_to_modified_blob :  _, 
145140                } 
141+                 | ResolutionFailure :: OursAddedTheirsAddedTypeMismatch  {  .. } 
146142                | ResolutionFailure :: OursDeletedTheirsRenamed  => None , 
147143            } , 
148144        } 
@@ -154,11 +150,18 @@ impl Conflict {
154150/// Note that all resolutions are side-agnostic, so *ours* could also have been *theirs* and vice versa. 
155151#[ derive( Debug ,  Clone ) ]  
156152pub  enum  Resolution  { 
153+     /// *ours* had a renamed directory and *theirs* made a change in the now renamed directory. 
154+      /// We moved that change into its location. 
155+      SourceLocationAffectedByRename  { 
156+         /// The repository-relative path to the location that the change ended up in after 
157+          /// being affected by a renamed directory. 
158+          final_location :  BString , 
159+     } , 
157160    /// *ours* was a modified blob and *theirs* renamed that blob. 
158161     /// We moved the changed blob from *ours* to its new location, and merged it successfully. 
159162     /// If this is a `copy`, the source of the copy was set to be the changed blob as well so both match. 
160163     OursModifiedTheirsRenamedAndChangedThenRename  { 
161-         /// If not `None `, the content of the involved blob had to be merged. 
164+         /// If `Some(…) `, the content of the involved blob had to be merged. 
162165         merged_blob :  Option < ContentMerge > , 
163166        /// The repository relative path to the location the blob finally ended up in. 
164167         /// It's `Some()` only if *they* rewrote the blob into a directory which *we* renamed on *our* side. 
@@ -172,11 +175,30 @@ pub enum Resolution {
172175        /// The outcome of the content merge. 
173176         merged_blob :  ContentMerge , 
174177    } , 
178+ } 
179+ 
180+ /// Describes of a conflict involving *our* change and *their* failed to be resolved. 
181+ #[ derive( Debug ,  Clone ) ]  
182+ pub  enum  ResolutionFailure  { 
183+     /// *ours* was renamed, but *theirs* was renamed differently. Both versions will be present in the tree, 
184+      OursRenamedTheirsRenamedDifferently  { 
185+         /// If `Some(…)`, the content of the involved blob had to be merged. 
186+          merged_blob :  Option < ContentMerge > , 
187+     } , 
188+     /// *ours* was modified, but *theirs* was turned into a directory, so *ours* was renamed to a non-conflicting path. 
189+      OursModifiedTheirsDirectoryThenOursRenamed  { 
190+         /// The path at which `ours` can be found in the tree - it's in the same directory that it was in before. 
191+          renamed_unique_path_to_modified_blob :  BString , 
192+     } , 
175193    /// *ours* was added (or renamed into place) with a different mode than theirs, e.g. blob and symlink, and we kept 
194+      /// the symlink in its original location, renaming 
176195     OursAddedTheirsAddedTypeMismatch  { 
177-         /// The location at which *their* state was placed to resolve the name and type clash. 
178-          their_final_location :  BString , 
196+         /// The location at which *their* state was placed to resolve the name and type clash, named to indicate 
197+          /// where the entry is coming from. 
198+          their_unique_location :  BString , 
179199    } , 
200+     /// *ours* was deleted, but *theirs* was renamed. 
201+      OursDeletedTheirsRenamed , 
180202} 
181203
182204/// Information about a blob content merge for use in a [`Resolution`]. 
@@ -190,18 +212,6 @@ pub struct ContentMerge {
190212     pub  resolution :  crate :: blob:: Resolution , 
191213} 
192214
193- /// Describes of a conflict involving *our* change and *their* failed to be resolved. 
194- #[ derive( Debug ,  Clone ) ]  
195- pub  enum  ResolutionFailure  { 
196-     /// *ours* was modified, but *theirs* was turned into a directory, so *ours* was renamed to a non-conflicting path. 
197-      OursModifiedTheirsDirectoryThenOursRenamed  { 
198-         /// The path at which `ours` can be found in the tree - it's in the same directory that it was in before. 
199-          renamed_path_to_modified_blob :  BString , 
200-     } , 
201-     /// *ours* was deleted, but *theirs* was renamed. 
202-      OursDeletedTheirsRenamed , 
203- } 
204- 
205215/// A way to configure [`tree()`](crate::tree()). 
206216#[ derive( Default ,  Debug ,  Clone ) ]  
207217pub  struct  Options  { 
@@ -211,8 +221,13 @@ pub struct Options {
211221     pub  blob_merge :  crate :: blob:: platform:: merge:: Options , 
212222    /// The context to use when invoking merge-drivers. 
213223     pub  blob_merge_command_ctx :  gix_command:: Context , 
214-     /// If `true`, the first conflict will cause the entire 
215-      pub  fail_on_conflict :  bool , 
224+     /// If `Some(what-is-unresolved)`, the first unresolved conflict will cause the entire merge to stop. 
225+      /// This is useful to see if there is any conflict, without performing the whole operation. 
226+      // TODO: Maybe remove this if the cost of figuring out conflicts is so low - after all, the data structures 
227+     //       and initial diff is the expensive thing right now, which are always done upfront. 
228+     //       However, this could change once we know do everything during the traversal, which probably doesn't work 
229+     //       without caching stuff and is too complicated to actually do. 
230+     pub  fail_on_conflict :  Option < UnresolvedConflict > , 
216231    /// If greater than 0, each level indicates another merge-of-merge. This can be greater than 
217232     /// 0 when merging merge-bases, which are merged like a pyramid. 
218233     /// This value also affects the size of merge-conflict markers, to allow differentiating 
0 commit comments