Skip to content

Branch per feature process

Alberto Trujillo González edited this page Jan 14, 2015 · 41 revisions

Theory

## Feature branches Development work is done exclusively on individual 'feature' branches. Typically there is only one developer at a time working on any particular feature. Breaking development work into individual features contained within their own branches makes it very easy to make stable releases regularly.

(see: "Starting a new feature")

## The integration branch The integration branch exists as a sandbox for ensuring parallel development on features integrates cleanly.

Developers should frequently (every couple of commits) pull the latest version of the integration branch, merge in their in-progress feature branch and push it back to the remote. This makes it easy to catch nasty conflicts between features before they become too unwieldy. Also, with the help of git-rerere resolutions of these conflicts are recorded and can then be shared with the team.

The integration branch is never deployed or merged into any other branches.

A continuous integration service such as Travis CI can be hooked up to track the integration branch. Whenever code is pushed Travis CI will run the project's automated tests and send a report (via email, or chat notification). This gives the team early notification of unexpected breakage that occurred as a result of their changes.

(see: "Starting a new feature")

## A "running" QA branch During a sprint cycle a QA branch should exist. Its purpose is to collect all completed features that are flagged for inclusion in the next release.

At the beginning of a sprint, the QA branch should be created off the latest release:

$ git checkout -b QA <latest release tag>
$ git push origin QA [--force]

If you use --force, git will replace the remote's version of the branch with your local version instead of merging it.

The QA branch can exist in two states:

  • stable: Matches the latest release or the last feature merged passed QA testing.
  • broken: The last feature merged failed QA testing.

Once a developer considers their feature to be complete it should be merged into the QA branch. Before this can happen a stable QA branch must first be created with the following steps:

  1. Ensure local QA is up to date.
  2. Create a new branch from master or the latest release (they should be the same). $ git checkout -b QA <latest_release|master>
  3. From the latest release, walk the QA branch up to its HEAD. For each merge commit encountered, determine the feature branch that was merged and merge it into the new branch, excluding any feature branches that failed QA testing.
  4. Delete the old QA branch and rename the new branch.

Purpose:

  • The new QA branch contains only completed features that have passed QA testing.
  • The features get merged in the order they were previously merged which ensures that any pre-recorded conflict resolutions are applied.

This process has been automated with the Affinity Bridge BPF Git Scripts

Example usage:

(…)$ git recreate-branch QA [[--exclude <f-<ticket_number>_<feature_which_failed_testing>]…]

With a stable QA branch, the developer can now merge their feature. Unresolved conflicts are less likely to occur due to frequent merges with the integration branch (develop) during the feature's development. Any that do occur need to be resolved by the developer. It is important to use the '--no-ff' flag when merging into QA to create an explicit reference to the merge in the repository history.

(QA)$ git merge f-<ticket_num>_<short_description> --no-ff
… resolve any conflicts
(QA)$ git push origin QA

After merging, QA testing (automated and/or manual) will determine whether feature is complete. If it fails, the next developer to recreate QA will ensure it's excluded from the branch.

## Hotfixes Ideally our code should be adequately tested and reviewed before deployment. In reality however, bugs slip through. If broken code gets deployed to production it needs to be fixed immediately by deploying a 'hotfix'.

For most scenarios a hotfix should change no more than 5 lines of code and have a low chance of conflicting or causing integration problems with any features in development. If this is the case, the changes can be made in a branch off of 'master' or the latest release (if your workflow allows it, the change could actually be made directly on 'master', it makes no difference). A new release containing the hotfix should then be tagged and deployed.

Note: Due to the minimal changes made by the hotfix, the feature branches, integration and QA branches shouldn't require rebasing. The hotfix is unlikely to conflict and they will be merged together with the creation of the next release.

If your fix requires changes to more than five lines or if it spans multiple files then it's best to:

  1. halt work on features
  2. prepare your hotfix following the 'feature branch' workflow (see: "Starting a new feature")
  3. create a release and deploy the hotfix (see: "Prepare a release")
  4. recreate the integration and QA branches of latest release (see: "Start a development cycle")
  5. rebase all working feature branches onto the latest release and resolve conflicts (see: "Rebase unreleased features")

Client frustration aside, the overhead in creating hotfixes means they should be avoided as best as possible. If you find you are frequently performing these steps it is likely that you have problems with your QA/testing workflow that need to be addressed.

Implementation steps

## Start a new project $ git init (master)$ git add […] (…)$ git commit -m 'First commit' (…)$ git remote add origin (…)$ git push origin master (…)$ git-bpf-init ## Start a development cycle

####Steps for the Release Manager:

// Make sure your local repo has a snapshot of the latest remote state.
(…)$ git fetch --all --tags
(…)$ git checkout master
(master)$ git pull origin master

// Delete local versions of integration and QA branches.
(master)$ git branch -d develop QA

// Create integration branch off of master or the last release tag (they should be the same).
(master)$ git checkout -b develop master
(develop)$ git push origin develop --force

// Create QA branch off of master or the last release tag (they should be the same).
(…)$ git checkout -b QA master
(QA)$ git push origin QA --force

####Steps for developers:

// Make sure your local repo has a snapshot of the latest remote state.
(…)$ git fetch --all --tags
(…)$ git checkout develop
(develop)$ git reset --hard origin/develop
(develop)$ git checkout QA
(QA)$ git reset --hard origin/QA
## Starting a new feature // Make sure your local repo has a snapshot of the latest remote state. (…)$ git fetch --all --tags // Create feature branch from master or last release (they should be the same). (…)$ git checkout -b f-_ origin/master
… do some work

(F.B.)$ git add [<filepattern>…]
(…)$ git commit -m 'refs #<ticket_num> - <description>'
// Push feature branch to origin
(…)$ git push origin f-<ticket_num>_<short_description>

// Integrate
(…)$ git checkout develop
(develop)$ git merge f-<ticket_num>_<short_description> [--no-edit]

… resolve any conflicts

// Push changes for continuous integration testing.
(…)$ git push origin develop
/// Back to work.
(…)$ git checkout f-<ticket_num>_<short_description>

… do more work and repeat steps until the feature is complete
## Prepare a release // Make sure your local repo has a snapshot of the latest remote state. (…)$ git fetch --all --tags (…)$ git checkout QA (QA)$ git pull origin QA
// If you want to exclude a feature from the release:
(QA)$ git recreate_branch QA --exclude f-<ticket_num>_<short_description>
// We have rebuilt the QA branch, it has a different history to the QA branch that exists on the remote. 
// It has to be 'force' pushed to completely replace the original QA branch. 
(QA)$ git push origin QA --force

// Merge into master.
(QA)$ git checkout master
(master)$ git merge QA

// This is where updating any VERSION.txt would take place, make sure to commit it.

// Create a tag on our production ready code.
(master)$ git tag -a 0.0.1 -m 'first release!'

// Push the updates to master and the tag.
(master)$ git push origin master --tags

// Checkout that tag on sites we need to deploy to.

After we successfully deploy we need to prepare the new sprint's development cycle (See: "Start a development cycle").

## Rebase unreleased features Existing feature branches that didn't make the release can be seen with:
(…)$ git branch --no-merged <latest_release_tag|master>

Each feature branch then needs to be rebased onto the latest release:

(…)$ git checkout f-<ticket_num>_<short_description>
(F.B.)$ git rebase <last_release_tag|master>
(F.B.)$ git push origin f-<ticket_num>_<short_description> --force
… continue development

Links

Clone this wiki locally