We have three branches:

  • master
  • staging
  • dev

We branch per issue and each issue needs to go through testing and staging before being merged to master.

We branch feature branches from master and work on that feature.

  • When ready for testing, we merge the feature branch into dev branch and TeamCity deploys to test environment.
  • When ready for staging, we merge the feature branch into staging branch and TeamCity deploys to staging environment.

This involves a lot of merge commits on dev/staging branches, but very few on master, because the feature branches are always branched from master.

It's always a merge commit merged with a feature branch, so this is a merge from a merge, from a merge, from a merge, etc... So at any time TeamCity has 100+ pending changes, even though there is only 1 changed file when doing:

git diff dev~1...dev

Is there some way to minimize these merge commits?
Are we doing Git wrong?

3 Answers 3


Your workflow seems similar to the “Git Flow”, except that you start feature branches from master, whereas the Git Flow recommends branching from dev.

The idea is that dev closely reflects the current development state. Feature branches are supposed to be very short-lived (at most a couple of days of development). So when a feature is branched from dev and merged back into dev a day later, there will be very few conflicts. The dev branch would be used for Continuous Integration.

Git Flow then suggest branching a release branch (your staging) from the dev branch with multiple new features for QA and release preparation. On release, the code is merged into master so that master only contains fully tested published versions.

If we were to branch new features off master (i.e. the last published version), this would ignore all other features that have been merged into dev in the meanwhile. Although the diff between dev^ and dev might be rather clean, there would be vast differences in the history:

             Am                                    B..Zm
  master ----x------------------------------------x
            / \                  \               /
 staging --x---|------------------|-------------x B..Zs
          /As  |         B  C..Y  |          Z /
     dev x-----|---------x--x--x--|-----------x
         A      \       /  /  /   |          /
featureB         x--x--x  .....   |         |
                 B1 B2 B3         |         |
  ...                             |         |
                                   \       /
featureZ                            x--x--x
                                    Z1 Z2 Z3

At merge B into dev (A) from featureB (A As Am B1 B2 B3), the merge is rather clean. We then add a couple of other features C through Y into dev. After that, the history of dev looks like A As Am B1 B2 B3 C1 C2 C3 ... Y1 Y2 Y3. That still looks reasonable. Finally, we have the featureZ branch with the history A As Am Z1 Z2 Z3 – note that Am is the nearest common ancestor commit of dev and featureZ.

The result is that at merge Z, we do not only test the effect of the commits Z1 Z2 Z3, but the effect of combining all the features B1 B2 B3 C1 C2 ... Y2 Y3 Z1 Z2 Z3.

The impact of this problem is reduced by frequent releases, so that the difference between dev and master is not that pronounced. However, this only makes the problem smaller, and does not solve it. TeamCity seems to realise that it's actually testing all the changes you made since you last merged into master, although these are not obvious in the branch history (since dev only contains merge commits).

I recreated the above branching example as an actual git repo, which is available as latk/Git-Branching-Madness on GitHub. You can look at the branch graph online, or clone the repo and inspect it yourself, e.g.:

                          # tip: use "git log" with "--pretty=oneline"
$ git log featureZ        # history of featureZ
$ git log dev^            # complete history of dev before featureZ merge
$ git log dev^..dev       # what commits were added during the featureZ merge
$ git log master --graph  # display complete branching history
  • Excellent answer, thank you so much. The main problem is that we have to get different features (for different things) through testing and staging on a different pace. The best way to do that is to branch per issue and branch off from master. The down-side is that we have to keep stale branches dev and staging. But what alternatives do we have? Could you advice?
    – Gaui
    Commented Nov 17, 2014 at 22:38

Merging multiple times should not be a problem, and that's actually a normal way of working with Git.

Your team agreed to have a specific workflow. For small fixes, that may induce some overhead, compared to, e.g., merging directly on master. But this is okay, at least you have a process to perform continuous integration.

Now, I don't know TeamCity, but maybe you should focus on why it has 100+ pending changes when the diff is actually small.

  • Ok thank you, but I guess these multiple pending changes are because of all these merge commits. So each time I merge something with a small diff, it triggers build/deployment on all projects instead of only the ones within the diff.
    – Gaui
    Commented Nov 16, 2014 at 14:35

One option is to frequently git fetch and git merge / git rebase master during development and qa in order to keep up with the changes and resolve conflicts as they happen. I prefer git rebase which replays your changes on top of the lastest master

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.