Search code examples
gitgit-mergerevision-history

Why does git merge with no conflicts still produce a merge commit?


I'm trying to understand why git will produce a commit with a unique SHA1 for a merge between two branches with no conflicts. Is storing the information that there were no conflicts really worth the extra clutter to the revision history?


Solution

  • The short answer is that a commit records a specific state of your working directory, and a merge will create a state that doesn't match either of the parents, so a new commit is required, as opposed to just moving a branch pointer. To elaborate a bit more, though, it helps to understand exactly what it means to merge two branches.

    The two most common ways git merge is used are to a) catch up a local branch to a remote branch and b) actually merge two separate branches together.

    In the first case, you have something like this:

    A--B--C--D--E--F--G
          ^           ^
          |           +-- origin/master
          +-- master
    

    In this case, git merge origin/master simply moves the pointer for master to the new location. This is what's called a "fast forward" merge, and usually does not result in a new commit (although you can explicitly request one if you have a reason to).

    On the other hand, if you have something like this:

    A--B--C--D--E--F--G <-- branch1
          \
           J--K--L--M--N <-- branch2
    

    and you are on branch2 and want to merge in branch1, simply moving your branch2 pointer would not make sense. If you just moved branch2 to point to G, you would lose the changes made in J through N. In this case git merge must create a new commit, where the resulting working tree would look like you took a copy of C (the merge base of G and N, which you can see by running git merge-base branch1 branch2), and then applied all of the changes in D through G and in J through N. Even if this results in no conflicts (the two branches either modified different sets of files or at least just different areas of the files), the resulting working directory state does not match either G or N, so a new commit is required. So git will create a new commit with a working directory that contains the results of applying the changes from both branches, and will mark that commit as having both G and N as parents.

    So, it's not really "storing the information that there were no conflicts", it's storing a new unique state of your project. Sure, you can look at that commit with git log, git diff, etc. and see that either there were or weren't conflicts, but that's not the reason the new commit was made.