Skip to content

Need better handling of conflicts involving a Delete/Rename #1365

@goran-w

Description

@goran-w

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 the Conflict 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 a DD conflict and the two new paths become a AU and a UA 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:

(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 or UD
  • CONFLICT (rename/delete) = DU or UD
  • 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.

Metadata

Metadata

Assignees

Labels

enhancementNew feature or request

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions