0

When git runs into a merge conflict, its response always seems to be to modify the file by adding conflict markers (a.k.a. "line noise") to the file. Is there a way to make it not do that?

My first choice would for it to leave the file totally unmodified. A very close second would be for it to apply all the non-conflicting edits and keep my local edit for the ones that conflict. (And in either case, mark the files' metadata as needing to be resolved.) Third best would be, in the event of a conflict, abort the whole pull, stash pop, etc. before modifying any local state, as if I'd never even attempted it.

Regardless of what git does (its current default or any of my preferences) the very next thing I'm going to do after a conflict is to bring up a diff tool on the local state vs. a clean head (running another git clone if needed). As such, its attempts to help me by making the conflicts easier to find actually make them harder to understand.

In close to 20 years of using source control, I've yet to have a single situation were what git is trying to make me do was the most productive way to deal with conflicts. The only case I can even think of where it might be more useful would be back 40 years ago when things like GUIs and tmux didn't exist yet. Even then, having it only proceed when I tell it to would be an improvement for how I work.

Edit:

@mkrieger1 pointed out that -Xours, at least as far as the file content goes, does my option 2. But their quote makes it sound like it will gobble up the conflict and risk me not finding and fixing them.

@qneill pointed out a way to get a --dry-run effect, which is option 3, but really what I want is just a different file-content when a merge operation fails.

BCS
  • 75,627
  • 68
  • 187
  • 294
  • 2
    When you initiate a merge you're telling Git to mash things together. If that's not what you want, don't merge. [Do a diff first](https://stackoverflow.com/questions/6335717/can-git-tell-me-if-a-merge-will-conflict-without-actually-merging) and deal with anything reported as a conflict. – isherwood Jan 30 '20 at 19:20
  • You could do that by starting with a simple step: Create a new branch. Then initialize the merge, take notes?, mess with it, delete the temp branch (go back to the original branch) => Your files were not modified during and/or after the merge. – Sandra K Jan 30 '20 at 20:06
  • 1
    @SandraK that would be a bit of a complex workflow to replace something as simple as a `git pull` or `git stash pop`. – BCS Jan 30 '20 at 22:43
  • "and risk me not finding and fixing them" – The conflict markers, which you do not want, are precisely there to help you find and fix the conflicts. – mkrieger1 Jan 31 '20 at 00:19
  • 1
    @mkrieger1 Those only work if I know to look at the file (which the metadata status indicates) and once I know that, I don't need them (I'm going to review everything anyway). – BCS Jan 31 '20 at 03:26
  • I update my answer to address option #1. There may be a misunderstanding between option #1 and #2 depending on what you mean by "apply the non-conflicting edits". My answer for option #1 will leave non-conflicting changes in files marked as merged state (in the index and workspace, but not committed). – qneill Feb 10 '20 at 22:09
  • Hi @BCS after re-reading your question (including the final paragraph) - perhaps you really just need a tool that will help you unravel the mess that git leaves? I've found that the VIM merge tool works great - you get the 4 versions of the file in 4 buffers, and with a few macros I've gotten good at enabling/disabling vimdiff to examine the changes involved in the most meaningful context. Would that help? – qneill May 27 '20 at 00:11
  • @qneill what I already have is a tool that helps me unravel the merge conflict, that's the "diff tool on the local state vs. a clean head" I mentioned. Adding complexity on complexity to remove git's (erroneous and presumptuous) assumptions about how to do that seems backwards. An option for "do nothing" would still support the current option while making other option easy. See "the Unix philosophy". – BCS May 27 '20 at 16:29
  • Hi @BCS I spent some considerable time understanding the git merge algorithms and the git conflict state on large source bases with non-linear history. What git leaves is messy only when the commits that lead up to the merge are messy - IOW it's just a text transform operation. I wish I could walk you through one of those messy merge conflicts with the VIM merge tool (with some handy macros), but that'd probably bust the seams of stackoverflow comments :) Sounds like you have what you need, good luck. – qneill Jun 05 '20 at 22:16

2 Answers2

1

The main problem is that the only way to detect a conflict in git is to run the operation and check the results.

This seems to be related to How can I preview a merge in git? and Is there a git-merge --dry-run option?

Your option #1 "leave file totally unmodified" - but marked as conflicted

The easiest way will be to run git merge then fixup the contents with git checkout --ours to get the HEAD version of conflicting files while maintaining their conflict status:

$ git checkout REF1
$ git merge REF2
### git fails here with a conflict
$ git status

$ git diff --name-status --diff-filter=U | 
  awk '{print $2}' |
  xargs git checkout --ours --

$ git diff HEAD
$ git status
### no diffs for unmerged files but still marked as unmerged

FYI: Other versions of the unmerged files can be checked out with git checkout --ours -- <files> for HEAD versions, git checkout --theirs -- <files> for MERGE_HEAD versions, and git checkout --merge -- <files> for merged contents with conflict markers.

Your option #2

I am not sure about the difference between option 1 and 2... If by "apply all the non-conflicting edits" you mean changes to other files, then the above answer will do that. If you mean changes to the conflicting file (keep those that don't conflict, clear the edits that do conflict) you'll need to parse and post-process the conflicting file

Your option #3 dry-run - "abort the whole thing as if never attempted"

One can use --no-commit (for merge and pull) to craft a less intrusive test (less to clean up). But the problem still arises in how to restore the original state, which is either git merge --abort if the operation failed, or git reset HEAD@{1} if things worked.

Here I just assume REF1 is where you want to start.

$ git checkout REF1
$ git merge REF2
$ if [ $? -ne 0 ] ; then
>     echo "conflicts"
>     git ls-files -u     ## or 'git status'
> else
>     echo "merged cleanly"
> fi
$ git reset --hard REF1
qneill
  • 1,643
  • 14
  • 18
  • 1
    Sure, running the operation to detect the result is fine. I'd *like* a `--dry-run` option but that's another issue. --- But what I'm asking for here is that the result of the operation hitting a merge conflict to be `-Xours` (thanks mkrieger1) as far as the file *content* goes but to mark the file as un-resolved as far as the *metadata* goes. ... And for that to be true any time it attempts a merge. I'd rather not have to remember to pass those flags every time, nor hunt down every git command that could need them. – BCS Jan 30 '20 at 22:57
  • Hi @BCS I think I have what you wanted now in my "option #1" answer above - which is run the 'git merge' and then 'git checkout --ours' the conflicting files after the fact. This will leave the files marked 'unmerged' but contain the original (REF1) contents. – qneill Feb 25 '20 at 19:11
0

You could use the ours option for the recursive merge strategy (the default one) by using git merge -Xours:

This option forces conflicting hunks to be auto-resolved cleanly by favoring our version. Changes from the other tree that do not conflict with our side are reflected in the merge result. For a binary file, the entire contents are taken from our side.

Documentation

As it says further down,

This should not be confused with the ours merge strategy, which does not even look at what the other tree contains at all.

which you would invoke by git merge --strategy ours.

mkrieger1
  • 19,194
  • 5
  • 54
  • 65
  • I think the OP wants to "test the merge" and see if a conflict would have occurred, but leave the files unchanged – qneill Jan 30 '20 at 20:08
  • 1
    If I'm reading that correctly, that would actually *resolve* the merge. Which is not what I want. If the is s conflict then the file should still show as unresolved. Other than that however... – BCS Jan 30 '20 at 22:46
  • 1
    @qneill, what you suggest is my third choice. – BCS Jan 30 '20 at 22:47