I have a merge conflict and the first thing I wanted to see is what is the difference between the local and remote versions of the file. For that I have executed the following:
git diff origin:path/to/my/file.py path/to/my/file.py
This way I saw that my local version has some additional blocks.
Then I wanted to see what exactly did I commit. So I have executed:
git diff --staged path/to/my/file.py
Surprisingly to me I saw another block of changes that is not the same as the block returned by the previous command.
I have opened my file in a text editors and saw both blocks. Now I do not know how to interpret it.
It looks like there is a difference between the local and remote versions and this difference is not the one that I have committed.
I'm not sure why you expect those diffs to be the same in the first place.
Remember that origin:path/to/my/file.py
refers to the version of path/to/my/file.py
extracted from the commit to which origin
resolves, which is generally origin/HEAD
, which is generally origin/master
. The git diff
command will then compare that file, extracted from that commit, to whatever is in your work-tree right now, which—if you're resolving a merge conflict—will generally be the version with conflict markers surrounding the conflict.
git diff
Note that running git diff
during a merge conflict likes to try to show combined diffs. But running git diff <commit-specifier>:<path> <path>
avoids that. Meanwhile, running git diff --staged <path>
just says:
$ git diff --staged path/to/file
* Unmerged path path/to/file
(unless you have git add
ed a version of path/to/file
from the work-tree, to tell Git that you have correctly merged the file already, in which case it compares the HEAD
version to the one you copied into the index by git add
ing).
git merge
is doingThat's not what git merge
is looking at and merging. What git merge
is looking at is two diffs:
HEAD
.origin/master
, maybe not).You can obtain these two diffs by first obtaining the merge base commit hash. Let's say, to simplify things, you ran git merge origin/master
and are now looking at a merge conflict on path/to/my/file.py
. Your Git has first run:
git merge-base --all HEAD origin/master
With any luck, this produces just one commit hash ID.1 You can save that in a variable:
base=$(git merge-base --all HEAD origin/master)
and then use it in the two specific diffs:
git diff $base HEAD -- path/to/my/file.py # what git thinks I changed
git diff $base origin/master -- path/to/my/file.py # what git thinks they changed
If you want to compare all three files directly, you can extract them into temporaries. This is what git mergetool
does, for instance: it extracts the base path/to/my/file.py
, the local (HEAD) file, and the other ("remote head") file, from each of these three commits, and then runs your chosen merge tool on all three files.
If git merge-base --all
produces more than one hash ID, your git merge -s recursive
did an internal merge of all of those commits, committed the result as a temporary commit, and is using that commit's hash as the merge base.