-
Notifications
You must be signed in to change notification settings - Fork 289
Description
Handling of merge conflicts was recently improved, by handling submodule conflicts differently (#1356) and by removing redundant conflicting entries from the Staged
area (#1359). However, cases of "unmerged paths" involving a Delete
or Rename
operation are still not handled well.
The IsConflictResolved
check works fine for the following conflict-related <XY>
values (of the git-status "Short Format" and --porcelain
formats) where a Diff
can be performed:
X, Y
----
A, A = both added
U, U = both modified
However, for the following conflict-related values (involving a Delete
or Rename
) a Diff
can NOT be performed, but the IsConflictResolved
check incorrectly returns true
:
X, Y
----
D, U = deleted by us, (modified by them)
U, D = (modified by us), deleted by them
D, D = both deleted = both renamed from (see notes below)
A, U = added by us, (modified by them) = renamed to by us (see notes below)
U, A = (modified by us), added by them = renamed to by them (see notes below)
For these, the IsConflictResolved
check should instead return false
(or simply be skipped, as we now do for Submodule
items).
Then we can choose to resolve the conflict by Theirs
or Mine
(either keep the modification and discard the delete, or vice versa).
However, to see immediately which side of Theirs
and Mine
has the Deletion, more info would need to be presented in the Conflict
view for these cases (based on the detailed <XY>
status)! I.e, we should be presented with the two merge-contributors along with info on which one of them was Deleted/Renamed and which one was Modified.
Additionally, special care needs to be taken when resolving Deleted/Renamed conflicts using Theirs
or Mine
, since the Git commands involved may need to be adjusted. For example, resolving a file with DD
(both deleted) status using either choice (in the current implementation) will result in an error: path <...> does not have [their|our] version
(Commands used here should probably instead be git add
or git rm
.)
Notes:
- Remember that the 'X / Y' values for unmerged paths do NOT stand for 'Index / Working-area' but INSTEAD stand for 'Our / Their' branch. These values should still be stored in the
Change
class (as a separate conflict-type enum perhaps) and passed into theConflict
class, in order to make more educated assumptions about the conflict and present better information about the two merge-contributors when needed. - The [
DD
,AU
,UA
] cases actually work in combination, to represent a "rename/rename" conflict - when a file is renamed into two different target paths on the two branches, the old path becomes aDD
conflict and the two new paths become aAU
and aUA
conflict, respectively. - The rename/rename (both renamed) conflicts are complicated since they involve not 1 but 3 files, and if there are also other conflicting files then it's not obvious which 3 files are related (info on original-path seems to be missing in git-status for these conflicts). Ideally we could (perhaps) find out which 3 files belong together (old file, our renamed file, their renamed file) and present a special view where we could choose which ones to keep/discard. But that would make for a separate feature request - for now let's just fix the basics so that all types of conflicts can be resolved using the
Conflict
view...
Besides the above fix, it would be very helpful to expand the ChangeStatusIcon
for Conflict, from a single '!
' icon/badge into the following variants:
±!
= Modified conflict (UU
)+!
= Added conflict (AA
)-!
= Deleted conflict (DU
,UD
)➜!
= Renamed conflict (DD
,AU
,UA
)S!
= Submodule conflict (UU
) - (See Use separate icon/badge for Submodule in Changes lists #1369)
(This could be a implemented as a single wider badge/icon or two separate ones - the ordinary Change icon and the additional Conflict one.)
That way, we could see at a glance which conflicts involve Addition / Deletion / Rename / Submodule, this is important information since these need special care. For example, conflicts involving Deletions / Renames will make "Open all conflicts in external mergetool
" fail, so we'd want to resolve them early before handling the more common conflicts involving only Modifications. Also, Renamed conflicts will be easier to understand if they are indicated as such.
Finally, the Conflict
view currently allows 'Open external mergetool
' on Submodule
conflicts. However, that will not work, so that button should be disabled (or hidden) in that case (but we still need the Use Theirs
and Use Mine
buttons available). The same is true for any of the conflict types involving a Delete
or Rename
!
Further background:
The errors/warnings we get from git-merge when a merges causes conflicts are listing more details on the type of conflict.
Here are most of the possible variants (and their corresponding git-status <XY>
values) :
CONFLICT (content)
=UU
CONFLICT (binary)
=UU
CONFLICT (submodule)
=UU
CONFLICT (add/add)
=AA
CONFLICT (modify/delete)
=DU
orUD
CONFLICT (rename/delete)
=DU
orUD
CONFLICT (rename/rename)
=DD
+AU
+UA
CONFLICT (rename involved in collision)
= ❓CONFLICT (file/directory)
= ❓CONFLICT (file location)
= ❓CONFLICT (implicit dir rename)
= ❓CONFLICT (directory rename split)
= ❓CONFLICT (distinct types)
= ❓
(These messages, and some more, can be found in the Git source-code: https://github.com/git/git/blob/master/merge-ort.c)
Also, some more information can be found in the docs for git-merge-tree
.