96

When I do git merge from another branch to current workspace, git sometimes makes a new commit:

Merge remote-tracking branch xxx into xxx

Sometimes, it does not:

Fast-forward
... src files ...

So what determines whether a commit should be created or not?

5 Answers5

137

So-called "Fast-forward" merges don't produce a commit, whereas other merges (often refered to as "octopus merge" (now you see why github's mascott is an octocat)) produce commits.

Basically, a Fast-forward happens when your branches did not diverge.

Say you want to merge a branch foo in the master branch. If these branches did not diverge, you would have an history like this (each * represents a commit):

*---* (master)
     \
      *---*---* (foo)

In this situation, the merge is fast-forward because (according to the graph theory, which is the underlying foundation of a git graph), master is reachable from foo. In other words, you just have to move the master reference to foo, and you're done:

*---*
     \
      *---*---* (master, foo)

When your branches diverge:

*---*---* (master)
     \
      *---*---* (foo)

You have to create a commit to "join" the two branches:

                ↓
*---*---*-------* (master)
     \         / 
      *---*---* (foo)

The commit pointed by the arrow is the merge commit and has two parent commits (the former master branch tip, and the current foo branch tip).

Note that you can force Git to create a merge commit for a fast-forward merge with the --no-ff option.

I highly recommend reading http://think-like-a-git.net/ for a good understanding of how the graph theory applies to git (you don't need to know the graph theory, everything you need to know is on the website), which will make working with Git incredibly easier :)

Geoffrey Bachelet
  • 4,047
  • 2
  • 21
  • 17
28

You can use the --no-ff option to force a new commit to avoid the fast-forward.

user3021843
  • 384
  • 3
  • 4
  • 2
    Thanks, but my question is why does git consider it more appropriate to make a merge commit, instead of simply fast-forwarding workspace? –  Nov 26 '13 at 16:41
3

A fast forward means that a commit has already happened and is stored in your log, and your HEAD (pointer) has moved forward to that commit. You can check out merge behavior here

hyleaus
  • 755
  • 1
  • 8
  • 21
  • Thanks, but my question is why does git consider it more appropriate to make a merge commit, instead of simply fast-forwarding workspace? –  Nov 26 '13 at 16:41
  • 1
    Are you making changes in the branch you're working on? In the case that you've made changes locally to the repo you're pulling in, the merge commit addresses those, whereas a fast forward simply moves the pointer. – hyleaus Nov 26 '13 at 16:47
2

When no fast-forward --no-ff option is presented git will not create a commit if the head of the branch you are merging in is the ancestor of the merged branch. In this case (no --no-ff option) it will just move the head (it's a fast-forward).

Alexey Kozhevnikov
  • 4,249
  • 1
  • 21
  • 29
0

Excerpt from the documentation:

FAST-FORWARD MERGE

Often the current branch head is an ancestor of the named commit. This is the most common case especially when invoked from git pull: you are tracking an upstream repository, you have committed no local changes, and now you want to update to a newer upstream revision. In this case, a new commit is not needed to store the combined history; instead, the HEAD (along with the index) is updated to point at the named commit, without creating an extra merge commit.

This behavior can be suppressed with the --no-ff option.

TRUE MERGE

Except in a fast-forward merge (see above), the branches to be merged must be tied together by a merge commit that has both of them as its parents.

A merged version reconciling the changes from all branches to be merged is committed, and your HEAD, index, and working tree are updated to it. It is possible to have modifications in the working tree as long as they do not overlap; the update will preserve them.

When it is not obvious how to reconcile the changes, the following happens:

The HEAD pointer stays the same.

The MERGE_HEAD ref is set to point to the other branch head.

Paths that merged cleanly are updated both in the index file and in your working tree.

For conflicting paths, the index file records up to three versions: stage 1 stores the version from the common ancestor, stage 2 from HEAD, and stage 3 from MERGE_HEAD (you can inspect the stages with git ls-files -u). The working tree files contain the result of the "merge" program; i.e. 3-way merge results with familiar conflict markers <<< === >>>.

No other changes are made. In particular, the local modifications you had before you started merge will stay the same and the index entries for them stay as they were, i.e. matching HEAD.

If you tried a merge which resulted in complex conflicts and want to start over, you can recover with git merge --abort.

jumping_monkey
  • 5,941
  • 2
  • 43
  • 58