Skip to content

Commit e05b42a

Browse files
committed
feat!: Add more modes for checking for unresolved conflicts.
They aim at making it possible to know if a conflict happened that was automatically resolved.
1 parent b053149 commit e05b42a

File tree

2 files changed

+45
-18
lines changed

2 files changed

+45
-18
lines changed

gix-merge/src/commit/virtual_merge_base.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ pub enum Error {
3030
pub(super) mod function {
3131
use super::Error;
3232
use crate::blob::builtin_driver;
33-
use crate::tree::UnresolvedConflict;
33+
use crate::tree::TreatAsUnresolved;
3434
use gix_object::FindExt;
3535

3636
/// Create a single virtual merge-base by merging `first_commit`, `second_commit` and `others` into one.
@@ -88,7 +88,7 @@ pub(super) mod function {
8888
// This shouldn't happen, but if for some buggy reason it does, we rather bail.
8989
if out
9090
.tree_merge
91-
.has_unresolved_conflicts(UnresolvedConflict::ConflictMarkers)
91+
.has_unresolved_conflicts(TreatAsUnresolved::ConflictMarkers)
9292
{
9393
return Err(Error::VirtualMergeBaseConflict.into());
9494
}

gix-merge/src/tree/mod.rs

Lines changed: 43 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -47,20 +47,38 @@ pub struct Outcome<'a> {
4747

4848
/// Determine what should be considered an unresolved conflict.
4949
///
50-
/// Note that no matter which variant, [conflicts](Conflict) with [resolution failure](`ResolutionFailure`)
51-
/// will always be unresolved.
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.
5255
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
53-
pub enum UnresolvedConflict {
56+
pub enum TreatAsUnresolved {
5457
/// Only consider content merges with conflict markers as unresolved.
58+
///
59+
/// Auto-resolved tree conflicts will *not* be considered unresolved.
5560
ConflictMarkers,
56-
/// Whenever there was any rename, or conflict markers, it is unresolved.
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.
5772
Renames,
73+
/// Similar to [`Self::Renames`], but auto-resolved content-merges will
74+
/// also be considered unresolved.
75+
RenamesAndAutoResolvedContent,
5876
}
5977

6078
impl Outcome<'_> {
6179
/// Return `true` if there is any conflict that would still need to be resolved as they would yield undesirable trees.
6280
/// This is based on `how` to determine what should be considered unresolved.
63-
pub fn has_unresolved_conflicts(&self, how: UnresolvedConflict) -> bool {
81+
pub fn has_unresolved_conflicts(&self, how: TreatAsUnresolved) -> bool {
6482
self.conflicts.iter().any(|c| c.is_unresolved(how))
6583
}
6684
}
@@ -109,20 +127,29 @@ impl ConflictMapping {
109127

110128
impl Conflict {
111129
/// Return `true` if this instance is considered unresolved based on the criterion specified by `how`.
112-
pub fn is_unresolved(&self, how: UnresolvedConflict) -> bool {
130+
pub fn is_unresolved(&self, how: TreatAsUnresolved) -> bool {
131+
use crate::blob;
132+
let content_merge_matches = |info: &ContentMerge| match how {
133+
TreatAsUnresolved::ConflictMarkers | TreatAsUnresolved::Renames => {
134+
matches!(info.resolution, blob::Resolution::Conflict)
135+
}
136+
TreatAsUnresolved::RenamesAndAutoResolvedContent | TreatAsUnresolved::ConflictMarkersAndAutoResolved => {
137+
matches!(
138+
info.resolution,
139+
blob::Resolution::Conflict | blob::Resolution::CompleteWithAutoResolvedConflict
140+
)
141+
}
142+
};
113143
match how {
114-
UnresolvedConflict::ConflictMarkers => {
115-
self.resolution.is_err()
116-
|| self.content_merge().map_or(false, |info| {
117-
matches!(info.resolution, crate::blob::Resolution::Conflict)
118-
})
144+
TreatAsUnresolved::ConflictMarkers | TreatAsUnresolved::ConflictMarkersAndAutoResolved => {
145+
self.resolution.is_err() || self.content_merge().map_or(false, |info| content_merge_matches(&info))
119146
}
120-
UnresolvedConflict::Renames => match &self.resolution {
147+
TreatAsUnresolved::Renames | TreatAsUnresolved::RenamesAndAutoResolvedContent => match &self.resolution {
121148
Ok(success) => match success {
122-
Resolution::SourceLocationAffectedByRename { .. }
123-
| Resolution::OursModifiedTheirsRenamedAndChangedThenRename { .. } => true,
149+
Resolution::SourceLocationAffectedByRename { .. } => false,
150+
Resolution::OursModifiedTheirsRenamedAndChangedThenRename { .. } => true,
124151
Resolution::OursModifiedTheirsModifiedThenBlobContentMerge { merged_blob } => {
125-
matches!(merged_blob.resolution, crate::blob::Resolution::Conflict)
152+
content_merge_matches(merged_blob)
126153
}
127154
},
128155
Err(_failure) => true,
@@ -264,7 +291,7 @@ pub struct Options {
264291
/// If `Some(what-is-unresolved)`, the first unresolved conflict will cause the entire merge to stop.
265292
/// This is useful to see if there is any conflict, without performing the whole operation, something
266293
/// that can be very relevant during merges that would cause a lot of blob-diffs.
267-
pub fail_on_conflict: Option<UnresolvedConflict>,
294+
pub fail_on_conflict: Option<TreatAsUnresolved>,
268295
/// This value also affects the size of merge-conflict markers, to allow differentiating
269296
/// merge conflicts on each level, for any value greater than 0, with values `N` causing `N*2`
270297
/// markers to be added to the configured value.

0 commit comments

Comments
 (0)