Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Git help to understand merge-base conflicts

I am getting conflicts on files I have not modified. Conflicts on files with one line changes when merging. I am starting to get a handle on what might be happening but still unable to resolve the problem.

Here is the branch structure.

  • master
    • current_iteration
      • mapr_autoinit

Master is quite old and has not had changes from current_iteration in quite some time. However master has had changes directly applied it by rebasing other bases directly onto master. So technically master and current_iteration have diverged. We have consistently branched and tag off of current_iteration. The problem I am seeing is that when I do a merge from current_iteration back into my branch. I get way more conflicts than should occur based on the changes to current_iteration. I am able to do a git diff / git apply from current_iteration and it applies cleanly.

When I run a git show-branch --merge-base on mapr-autoinit I see the commit was actually a commit on master months ago. However when I check git merge-base current_iteration mapr_autoninit I see that the version is very recent and most likely would not have conflicts.

From what I have seen for the last few days it seems logical that if I merge master into current_iteration, commit and then merge current_iteration back into master. This should probably resolve my branches merge-base to point to a more recent version.

Also is it possible that I could stop tracking master. I tried using git config --unset branch.master.merge; git config --unset branch.master.remote to stop tracking master but this did not solve my issue.

Is this a problem where the master and current_iteration have diverged and merge is trying to reconcile the merge by replaying the entire log?

Also here is more detail. I am opening a new question now that the question is much more specific. I will update both with a valid answer or delete the older depending on which is preferred.

Git merge resulting in unreasonable conflicts

like image 451
Chris Hinshaw Avatar asked Aug 06 '18 18:08

Chris Hinshaw


People also ask

How do I see merge conflicts in Git?

General tools. The status command is in frequent use when a working with Git and during a merge it will help identify conflicted files. Passing the --merge argument to the git log command will produce a log with a list of commits that conflict between the merging branches.

What do you understand about the Git merge conflict?

Merge conflicts happen when you merge branches that have competing commits, and Git needs your help to decide which changes to incorporate in the final merge. Git can often resolve differences between branches and merge them automatically.

How do I see merge conflicts?

To see the beginning of the merge conflict in your file, search the file for the conflict marker <<<<<<< . When you open the file in your text editor, you'll see the changes from the HEAD or base branch after the line <<<<<<< HEAD .


1 Answers

I've never found git show-branch output very useful myself and I am not sure how it is intended to be used, nor how others might use it successfully.

Also is it possible that I could stop tracking master ...

This irrelevant. What matters is the actual commit graph. The upstream of any branch is independent of the commit graph.

Is this a problem where the master and current_iteration have diverged and merge is trying to reconcile the merge by replaying the entire log?

No. What matters after the merge-base-finding is only that—the merge base commit, that is—plus the two branch tip commits. Everything in between is irrelevant.

However when I check git merge-base current_iteration mapr_autoinit I see that the version is very recent and most likely would not have conflicts.

This is usually the way to get started, since git merge-base examines the commit graph, and uses it to compute the merge base commit(s). Note that git merge by default runs the equivalent of git merge-base --all. If this produces more than one merge base (more than one commit hash ID), you have a special situation. Let's assume that you don't for now, but it's a good idea to check.

Having found that merge base commit, you can look at git log output (see the last section) and see how the merge base relates to the two branch tips. Git does not care about this—well, that's not quite right: after Git has found the merge base (which depends on this), Git stops caring about it—but you might find it helpful.

Having found the (single) merge base of current_iteration and mapr_autoinit (and assuming you are on one of these two branches), here's what Git does:

git diff --find-renames <merge-base-hash> current_iteration > /tmp/1
git diff --find-renames <merge-base-hash> mapr_autoinit > /tmp/2

I redirected these to /tmp files here for ease of viewing multiple times and side-by-side or whatever, depending on your file viewer(s). Since I don't know which direction you're merging—from what, to what—I just numbered the two files, rather than calling them "ours" and "theirs". Note that the combining changes step is largely symmetric anyway. For each change to each file, there are four possibilities:

  • You touched the lines and they didn't: Git takes your change.
  • They touched the lines and you didn't: Git takes their change.
  • You and they touched the lines, but you made the same change: Git takes one copy of the change.
  • You and they touched the lines, but made different changes: Git declares a merge conflict, and puts both sets of changes into the work-tree file. If you set merge.conflictStyle to diff3—I highly recommend this—Git includes the merge-base version as well, to show you what you and they started with before you and they made conflicting changes.

Viewing the results, with merge conflict markers and with the base version included via merge.conflictStyle set to diff3, is usually sufficient. (In particular, it sometimes shows up cases where Git has mis-paired changes and thinks they conflict, when in fact the changes are to other regions that do not conflict.) If not, it may help to look at the commit graph.

More about the commit graph

Something that does work, but can be difficult to make sense out of,1 is the output of git log --graph. It's helpful to include the --decorate --oneline options, and for this particular case, you will want to list the names of the current branch2 and the branch you intend to merge. For instance, if you have mapr_autoinit checked out and intend to run git merge master, you could run:

git log --graph --decorate --oneline mapr_autoinit master

Git will do its best to draw, in ASCII art with optional extra color added to help, the commit graph. The result depends a great deal on what's in the graph, of course. Here is a snippet from the Git repository for the Linux kernel:

* 1ffaddd029c8 (HEAD -> master, tag: v4.18-rc8, origin/master, origin/HEAD) Linux 4.18-rc8
*   a8c199208cd6 Merge branch 'x86-urgent-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
|\  
| * 1b3a62643660 x86/boot/compressed/64: Validate trampoline placement against E820
* |   2f3672cbf9da Merge branch 'timers-urgent-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
|\ \  
| * | 0a0e0829f990 nohz: Fix missing tick reprogram when interrupting an inline softirq
| * | 80d20d35af1e nohz: Fix local_timer_softirq_pending()
* | |   0cdf6d4607df Merge branch 'perf-urgent-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip

One can use this to trace what has happened since the merge base. There are more tools:

git log --graph --decorate --oneline --boundary mapr_autoinit...master

for instance will show you what's not merged (commits marked *), plus (due to --boundary) boundary commits that are merged (commits marked o) but without going any deeper into the graph than that.

For very simple cases—graphs that are not horrid with internal branch-and-re-merge—adding --left-right to the three-dot syntax is sometimes helpful as well. For complex cases, with lots of internal branch-and-merge actions down each leg, --first-parent is sometimes useful, and --simplify-by-decoration is sometimes useful.


1Despite this, it's still worth perusing. If you prefer, use a GUI or similar that draws the graph more prettily, but be aware that at least some Git GUIs seem to draw incorrect graphs (coughvariouscoughwebservicescough).

2You can always just use the name HEAD, in all capital letters, in place of the current branch name. All-lowercase works on case-insensitive file systems on Windows and MacOS for instance, but it's not good to get into that habit. A lone at-sign @ means HEAD in any modern Git, though, so if you prefer, use that instead of spelling out HEAD. Note that when using the two or three dot syntax (A..B and A...B), omitting one name entirely also means HEAD: HEAD..B, @..B, and ..B are three spellings for the same thing.

like image 135
torek Avatar answered Sep 29 '22 04:09

torek