To add to jthill's answer (which is correct and I've upvoted it): You need to realize that your first merge isn't a merge at all. That's why Git says:
Fast-forward
The merge process, as jthill said:
finds (and in some cases Git's merge will actually construct) the common base and compares diffs from that base to each branch tip. Any differences in overlapping ranges constitutes a conflict.
But for the first git merge
command, the common base is one of the branch tips.
The first one is free
Let's see how that is:
$ mkdir tt; cd tt; git init
Initialized empty Git repository in ...
$ cat << END > myfile.txt
01
02
03
04
END
$ git add myfile.txt
$ git commit -m initial
[master (root-commit) b1a22ca] initial
1 file changed, 4 insertions(+)
create mode 100644 myfile.txt
$ git checkout -b br1
Switched to a new branch 'br1'
$ ed myfile.txt
12
1s/$/ one/
w
16
q
$ git add myfile.txt
$ git commit -m 'change line 1'
[br1 b31f04a] change line 1
1 file changed, 1 insertion(+), 1 deletion(-)
$ git log --all --decorate --oneline --graph
* b31f04a (HEAD -> br1) change line 1
* b1a22ca (master) initial
(If you're not familiar with the ed
editor, it is a rather minimal text-only editor that prints the number of bytes in the input file when started up, and has commands of the form: line-number(s) operation. So 1s/$/ one/
means "replace the end of line 1 with one
. The w
command writes the file back, and q
quits the editor.)
We do not have to create the extra branches br2
, br3
, and br4
yet, but let's go ahead and do that anyway, creating them so that they point to commit b1a22ca
:
$ git checkout master
Switched to branch 'master'
$ git branch br2 && git branch br3 && git branch br4
$ git log --all --decorate --oneline --graph
* b31f04a (br1) change line 1
* b1a22ca (HEAD -> master, br4, br3, br2) initial
At this point, we have a graph that looks like this, if we draw it horizontally instead of vertically the way Git does:
b1a22ca <-- master (HEAD), br2, br3, br4
\
b31f04a <-- br1
That is, the branch name br1
indicates that its tip commit is b31f04a
, while the branch names master
and the other three br
s all indicate that their tip commit is b1a22ca
.
Let's go ahead and create a new commit on br2 now:
$ git checkout br2
Switched to branch 'br2'
$ ed myfile.txt
12
1,$p
01
02
03
04
2s/$/ two/
w
16
q
$ git add myfile.txt
$ git commit -m 'change line 2'
[br2 805ea58] change line 2
1 file changed, 1 insertion(+), 1 deletion(-)
and see how git log --all --decorate --oneline --graph
draws it:
$ git log --all --decorate --oneline --graph
* 805ea58 (HEAD -> br2) change line 2
| * b31f04a (br1) change line 1
|/
* b1a22ca (master, br4, br3) initial
In my preferred horizontal graph drawing—where newer commits are to the right, rather than above—we have:
805ea58 <-- br2 (HEAD)
/
b1a22ca <-- master, br3, br4
\
b31f04a <-- br1
If we now run git checkout master && git merge br1
, Git will locate, for us, the merge base commit. That's the "nearest" commit that's on both branches br1
and master
.
Note that at this point, commit b31f04a
is only on br1
, and commit 805ea58
is only on br2
, but commit b1a22ca
is on every branch. This is a key thing in Git: branches "contain" commits, and any given commit can be on many branches simultaneously.
Having found the merge base, Git now must combine two sets of changes:
- the changes from the merge base
b1a22ca
to the tip of master
: b1a22ca
;
- the changes from the merge base
b1a22ca
to the tip of br1
: b31f04a
.
But b1a22ca
is b1a22ca
. There cannot possibly be any changes here!
What Git does by default for this case is to do a fast forward. A fast forward is not a merge at all! It just amounts to switching to the other commit, in this case b31f04a
, and dragging the name master
forward to point to that commit:
805ea58 <-- br2
/
b1a22ca <-- br3, br4
\
b31f04a <-- master (HEAD), br1
No new commits get added, during this operation: the only thing that changes is that your current commit is now b31f04a
instead of b1a22ca
, and that the branch label master
points to b31f04a
. (The word HEAD
moved along with master
because HEAD
is "attached to" master
. Git shows this as HEAD -> master
in the git log
output.)
You can, if you like, run git merge --no-ff br1
instead. This forces Git to make a real merge. However, there are still no changes to find when Git compares b1a22ca
to itself, so there are no merge conflicts. If you do this, you get a new commit, with two parents. You didn't, so I'll just run with the fast-forward:
$ git checkout master
Switched to branch 'master'
$ git merge br1
Updating b1a22ca..b31f04a
Fast-forward
myfile.txt | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
$ git log --all --decorate --oneline --graph
* 805ea58 (br2) change line 2
| * b31f04a (HEAD -> master, br1) change line 1
|/
* b1a22ca (br4, br3) initial
The second merge is conflicted
When we get to the second merge, though, things are different indeed. This time, the first commit that is shared between master
(now b31f04a
) and br2
(805ea58
) is b1a22ca
. So Git will run:
$ git diff --find-renames b1a22ca b31f04a # what happened on master
diff --git a/myfile.txt b/myfile.txt
index cb3ff6d..8bc2250 100644
--- a/myfile.txt
+++ b/myfile.txt
@@ -1,4 +1,4 @@
-01
+01 one
02
03
04
Then Git will run:
$ git diff --find-renames b1a22ca 805ea58 # what happened on br2
diff --git a/myfile.txt b/myfile.txt
index cb3ff6d..8bfe146 100644
--- a/myfile.txt
+++ b/myfile.txt
@@ -1,4 +1,4 @@
01
-02
+02 two
03
04
Git now goes to combine these two changes. But in Git's eyes, the two changes overlap: the first one touches line 1, and the second touches line 2, and lines 1 and 2 touch each other: that's an overlap. The two changes are not identical, so they cannot be collapsed down to a single change that changes both lines. The change is therefore a merge conflict, which is what you see.
The same thing happens for the remaining merges, once branches br3
and br4
have appropriate tip commits.