2.2 Some conflict
Let’s see what happens when there are conflicts. Checkout example9/branchB and
merge example9/branchA:
Listing 2.9:Merging$ git merge example9/branchA Auto-merging example.py CONFLICT (content): Merge conflict in example.py Automatic merge failed; fix conflicts and then commit the result. $ git status On branch example9/branchB You have unmerged paths. (fix conflicts and run "git commit") (use "git merge --abort" to abort the merge) Changes to be committed: modified: module.py Unmerged paths: (use "git add <file>..." to mark resolution) both modified: example.py $ git ls-tree -r HEAD 100644 blob 2f78cf5b66514f2506d9af5f3dadf3dee7aa6d9f .gitignore 100755 blob 500616dc70f4847f244d29d827a192b7fa03de93 example.py 100644 blob 2d1fe2ea52267ab6e75cca853c393fc6929a0e45 module.py $ git ls-files -s 100644 2f78cf5b66514f2506d9af5f3dadf3dee7aa6d9f 0 .gitignore 100755 b0a7eae10629dec61246f86c08f2432f0e276675 1 example.py 100755 500616dc70f4847f244d29d827a192b7fa03de93 2 example.py 100755 ad957360597fb9bd3e83d4bfa869f6e19b7fbf2b 3 example.py 100644 475f980e6c6e24f8fc4a144e498fa1c1c59da370 0 module.py
We can see that example.py is showing up in Unmerged paths, and
module.py is listed in Changes to be committed. .gitignore is not listed.
.gitignore hasn’t changed between HEAD, index and what we have in the
working tree so nothing to report on it. module.py has changed as part of the
merge process without any conflicts and that’s why it is present in HEAD and in
index with different ids. The file currently on the working tree is just like it is on
index and that’s why the file is listed in Changes to be committed. The
interesting stuff is on example.py. Instead of having a single item for it in index,
there are 3 and each one of them has a different index stage number. When
there is content conflict on a file (like in this case), git will hold 3 versions
of the file in index. Stage number 1 is the file as it is in the common
ancestor .
Stage number 2 is the file as it is in HEAD. Stage number 3 is the file as it is in the
other branch. See for yourselves:
Listing 2.10:Checking the ids of example.py in different revisions$ git ls-tree $( git merge-base HEAD MERGE_HEAD ) example.py 100755 blob b0a7eae10629dec61246f86c08f2432f0e276675 example.py $ git ls-tree HEAD example.py 100755 blob 500616dc70f4847f244d29d827a192b7fa03de93 example.py $ git ls-tree MERGE_HEAD example.py 100755 blob ad957360597fb9bd3e83d4bfa869f6e19b7fbf2b example.py
Stage number 0, as we had seen before, means that the file is either unchanged or
added to index.
Let’s solve the conflict as we had done before (which means that both files have to
be modified) and then let’s see the output of the git commands:
Listing 2.11:Status after modifying the files$ git status On branch example9/branchB You have unmerged paths. (fix conflicts and run "git commit") (use "git merge --abort" to abort the merge) Changes to be committed: modified: module.py Unmerged paths: (use "git add <file>..." to mark resolution) both modified: example.py Changes not staged for commit: (use "git add <file>..." to update what will be committed) (use "git restore <file>..." to discard changes in working directory) modified: module.py $ git ls-tree -r HEAD 100644 blob 2f78cf5b66514f2506d9af5f3dadf3dee7aa6d9f .gitignore 100755 blob 500616dc70f4847f244d29d827a192b7fa03de93 example.py 100644 blob 2d1fe2ea52267ab6e75cca853c393fc6929a0e45 module.py $ git ls-files -s 100644 2f78cf5b66514f2506d9af5f3dadf3dee7aa6d9f 0 .gitignore 100755 b0a7eae10629dec61246f86c08f2432f0e276675 1 example.py 100755 500616dc70f4847f244d29d827a192b7fa03de93 2 example.py 100755 ad957360597fb9bd3e83d4bfa869f6e19b7fbf2b 3 example.py 100644 475f980e6c6e24f8fc4a144e498fa1c1c59da370 0 module.py
Even though the ids of the objects in index and HEAD hasn’t changed, the
content of the files has changed on the working tree. Given that module.py has
an id that is different between index and HEAD, git lists it in Changes to be
committed. Given that the content of the file on the working tree is
different from index, it is also listed in Changes not staged for commit.
example.py hasn’t been added to index and so it is still conflicted so we still
have the same 3 versions of the file in index and is listed in Unmerged
paths.
Let’s add module.py first and see what happens:
Listing 2.12:After adding module.py$ git add module.py $ git status On branch example9/branchB You have unmerged paths. (fix conflicts and run "git commit") (use "git merge --abort" to abort the merge) Changes to be committed: modified: module.py Unmerged paths: (use "git add <file>..." to mark resolution) both modified: example.py $ git ls-tree -r HEAD 100644 blob 2f78cf5b66514f2506d9af5f3dadf3dee7aa6d9f .gitignore 100755 blob 500616dc70f4847f244d29d827a192b7fa03de93 example.py 100644 blob 2d1fe2ea52267ab6e75cca853c393fc6929a0e45 module.py $ git ls-files -s 100644 2f78cf5b66514f2506d9af5f3dadf3dee7aa6d9f 0 .gitignore 100755 b0a7eae10629dec61246f86c08f2432f0e276675 1 example.py 100755 500616dc70f4847f244d29d827a192b7fa03de93 2 example.py 100755 ad957360597fb9bd3e83d4bfa869f6e19b7fbf2b 3 example.py 100644 42004985f8888627d3985174325a235401568e0b 0 module.py
For this file, it behaved like it did when there was no conflict. The ids for the
object are different between HEAD and index and that’s why it is listed in
Changes to be committed. The file in the working tree is like it is in index
now and that’s why git knows there’s nothing else to report about the file.
example.py is still conflicted and so there are no changes for it from what we had
seen before.
Let’s add example.py now:
Listing 2.13:After adding example.py$ git add example.py $ git status On branch example10/branchB All conflicts fixed but you are still merging. (use "git commit" to conclude merge) Changes to be committed: modified: example.py modified: module.py $ git ls-tree -r HEAD 100644 blob 2f78cf5b66514f2506d9af5f3dadf3dee7aa6d9f .gitignore 100755 blob 500616dc70f4847f244d29d827a192b7fa03de93 example.py 100644 blob 2d1fe2ea52267ab6e75cca853c393fc6929a0e45 module.py $ git ls-files -s 100644 2f78cf5b66514f2506d9af5f3dadf3dee7aa6d9f 0 .gitignore 100755 ad957360597fb9bd3e83d4bfa869f6e19b7fbf2b 0 example.py 100644 42004985f8888627d3985174325a235401568e0b 0 module.py
Now that the (originally) conflicted file has been added to index, we have a single
item for it in index. Given that the ids are not the same between HEAD and
index, git reports it in Changed to be committed and given that the content of
the files in the working tree is the same as it is in index, git doesn’t say anything
else about the files. Let’s create a new revision at this moment and see what
happens:
Listing 2.14:Committing after conflict$ git commit -m "A new revision" [example9/branchB 9f80666] A new revision $ git status On branch example9/branchB nothing to commit, working tree clean $ git ls-tree -r HEAD 100644 blob 2f78cf5b66514f2506d9af5f3dadf3dee7aa6d9f .gitignore 100755 blob ad957360597fb9bd3e83d4bfa869f6e19b7fbf2b example.py 100644 blob 42004985f8888627d3985174325a235401568e0b module.py $ git ls-files -s 100644 2f78cf5b66514f2506d9af5f3dadf3dee7aa6d9f 0 .gitignore 100755 ad957360597fb9bd3e83d4bfa869f6e19b7fbf2b 0 example.py 100644 42004985f8888627d3985174325a235401568e0b 0 module.py
As it had happened before when there was no conflict, when we create a new
revision, the ids in HEAD are updated to the same ids of the objects that index
had right before the revision was created. Given that now index and HEAD have
the same ids, there are no Changes to be committed and given that there are
no differences between index and working tree, there’s nothing else to
report.
If you stop to think about it, other than having the 3 items listed in index while
the file was conflicted, there were no other differences in terms of what was held in
index and the revisions. All in all, git does not save any information whatsoever
related to the conflicts that happened on a merge when a new revision is
created. It simply creates a new revision with multiple parents and the files
and their (final) contents on the revision just like it would do on any other
revision:
Listing 2.15:Checking revision information$ git cat-file -p HEAD tree 396032b1546d75672f3a85c13a858d3b187d2046 parent 3cd9cfd24b13fa2381c5cc8009275b961ea7a26b parent dfde76c316ff0a070ddc7560f86b7279b73ed807 author Developer A <dev.a@localhost> 1592110970 -0600 committer Developer A <dev.a@localhost> 1592111469 -0600 A new revision $ git cat-file -p HEAD^{tree} 100644 blob 2f78cf5b66514f2506d9af5f3dadf3dee7aa6d9f .gitignore 100755 blob ad957360597fb9bd3e83d4bfa869f6e19b7fbf2b example.py 100644 blob 42004985f8888627d3985174325a235401568e0b module.py
Copyright 2020 Edmundo Carmona Antoranz