Continuous Delivery the Gitflow way
Instead of a single master branch, this approach uses two branches to track the history of the project. While the master branch contains tags and/or commits that record the project's official release history, a shared integration branch (called "develop") gives your team a place to ferret out bugs and incompatible changes.
Step 1: create your branch
For new development work, your feature branch will be based on develop (make sure you choose a clean commit to branch from!). For more details on Gitflow's variations and their branching structures, check out GitFlow Workflow section in this cookbook
Regardless of where you branch from, pull changes from the parent branch into your feature branch with each build. You'll discover integration issues right away and be able to fix them on your feature branch instead of finding them only after you've merged to develop – at which point, you've already polluted it.
With the Gitflow model, it's possible to release from master, or from stable release branches.
Step 2: code, test, repeat
The testing step gets interesting with Gitflow. Create within your pipeline stages to put your feature branch under test (as in all continuous delivery workflows), but here's the difference: when implementation is complete and all your tests are passing, merge to develop instead of master.
Develop is sort of the mixing pot where all your team's changes can stew together, and you'll want feedback from every commit so as to make debugging test failures easier (fewer changes between each build to sort through). The best way to guarantee this is to configure develop to trigger builds based on push and pull request notifications. Periodically polling the repo will occasionally capture changes from multiple commits in a single build because develop receive changes so frequently – it's better suited for branches whose changes are spaced farther apart.
Pro tip: Another advantage of repository-triggered builds for develop is that it uses the CI's CPU efficiently. For teams doing continuous deliver on a large scale, it really makes a difference.
Be sure to merge develop down into your feature branch and run tests one last time before going to develop.
Step 3: merge up
Creating a pull request when merging your feature branch to develop is standard practice. Doing peer code reviews at this stage is far easier than delaying it until you're ready to ship, in which case you'd have to review all the changes since the last release at once.
Inevitably, you'll merge your feature branch to develop, only to be met with test failures there. Instead of making changes directly to develop, checkout your branch again and do the work there. (Most teams have "handshake" agreements never to make commits directly on master – only merge commits.)
Step 4: ship it
Designating your master or release branch as the primary branch sets you up for fairly straightforward production deployments. Whatever your primary branch is, that will automatically be the primary branch for your deployment jobs, though you can set up the deploy builds from feature branches as well.
You can automatically create tags on master based on each successful build of develop and deploy from those tags right away. Or, you can wait until several features have been successfully been added to develop, and create the tag by hand going through a release branch. It simply depends on whether you're moving toward continuous deployment, or sticking with continuous delivery. Gitflow accommodates both.
If nothing else, we've seen the value of making a branch for each issue you work on. You won't step on your teammates' toes, and your most important branches stay clean and releasable at all times.