Skip to content

Branch on Feature

Stephen Leach edited this page Feb 12, 2021 · 5 revisions

This page describes a simple git workflow called branch-on-feature, which we will be using for TeamGroove. It is a basic technique that can be used on its own or even as part of a bigger, more elaborate workflow such as gitflow.

The key idea is that we have a long-lived integration branch, which might be called something like develop. The team members iteratively implement user stories onto that central branch, so the project history can be seen as a series of changes on this branch. And in this approach, each user story gets implemented in its own short-lived branch like this:

  1. SELECT: The developer picks a feature or user story to implement.
  2. BRANCH: They branch from develop to create a new branch - called a feature-branch.
  3. IMPLEMENT: Then they make the changes they need to in the feature-branch to implement the user-story.
  4. REVIEW: The changes are then reviewed and tested by some other team members - usually via a so-called pull request. This might trigger off several rounds of bug fixes.
  5. MERGE: Once the team is happy, the feature-branch is merged into the develop branch - usually by 'completing' the pull request in a web browser. The feature-branch is usually discarded at this point.
  6. DONE: And when the feature has made it onto the integration branch, the user story is done.

Practical Considerations

Iteratively implementing user stories in their own short-lived branches is the essence. But when you put this into practice, you quickly discover that this isn't a full picture. A typical first stumbling block is how to name branches. In branch on feature there are often a lot of little branches and it's helpful if the team has a system for making up names for feature-branch that say something useful. For example, a user story for the v0.1 milestone that implements the user-story New Drop Downs with id 123 might be named v0.1/123_new_drop_downs.

Note: some teams like to include the sprint name e.g. sprint06/new_drop_downs. I found that it caused extra work when stories overran sprints. So these days I prefer the first half of the name to relate to the version number, which is much more robust and more informative when it comes to making a release.

The second stumbling block is "merge conflicts". When several team members are working on a project, it is not uncommon that a particular file gets altered in two concurrent user stories. Sometimes git can resolve this potential conflict on its own. When it can't it flags the file (or files) involved as having a merge conflict and the programmer is obliged to resolve it.

Merge conflicts can be quite complicated and time-consuming to fix. Part of good project hygiene is to keep feature-branches small and short-lived, which reduces the frequency of conflicts. And if a feature-branch lasts for more than a few days, its a good idea to regularly merge in from the develop branch so that you only have a few merge conflicts to resolve.

Merge Back into the integration branch

Feature-branches make working independently from your team mates a breeze. But sooner or later you have to bring your work back into contact with the rest of the team, which is where you have to cope with any clashing changes. One way or the other we need to pour the contents of the feature-branch into the integration branch. There are two common ways to do this. [1] You can use pull requests, which do the merge on the central server. That's what we will be doing on TeamGroove. Or [2] You can do the merge locally on your machine and then push develop to the central repo, which is sometimes a useful alternative. Both of these are explained below.

Pull Requests

This is the way we are working in TeamGroove. One of the extra services that GitHub layers on top of git is a web based front end for sharing a proposed merge a.k.a. a pull request. The proposed merge can be code-reviewed by other teams members, which is really helpful for improving quality and stopping errors early. Once everyone is happy with a proposed merge, the pull-request can be 'completed', which will do the merge on the central GitHub repo. Everyone then has to pull the updated integration branch develop into their local repos.

Pull-requests are all about integrated your changes with any other changes that may have been done in parallel by other team members. So they are potential trouble spots, especially if your feature-branch hasn't incorporated the latest changes happening on the integration-branch. To avoid trouble, the best thing to do is to bring your local integration branch up to date, then merge it into your feature-branch and resolve any conflicts locally. That way, when you push your feature branch in order to make the pull-request, it is compatible with the current state of the integration branch.

To make this a bit less abstract, lets see how this works with a feature-branch called v0.1/123_new_drop_downs and an integration branch called main. Lets assume we have got our feature-branch working nicely and now we want to publish it. Our first step is to make sure that it incorporates any recent changes on main that we have missed. Here's how you would do it on the command line.

# Refresh our local integration branch
git checkout main
git pull
# Now merge on our local machine
git checkout v0.1/123_new_drop_downs
git merge main
# If there were any conflicts, resolve them

By the way, I highly recommend using one of the excellent git GUI clients rather than the command line by the way. And I particularly recommend SmartGit, although for open source projects on GitHub, GitKraken is free and also very good.

Having got here we want to set up a pull-request. To do that we have to push v0.1/123_new_drop_downs to GitHub, so that GitHub can see it. Until this point it is quite likely that the v0.1/123_new_drop_downs only lives on our computer, so you may get prompted by git for permission to set up the branch centrally.

git checkout v0.1/123_new_drop_downs
git push

Now you can jump onto the pull-request page and create a new pull request from v0.1/123_new_drop_downs to main. When you do this you can assign people to review, which will give them a nudge to say that you are asking for their help. Once they agree that everything looks OK, you can complete the pull request.

When you complete the pull-request it is quite normal to perform a "squash commit", rolling up all the commits into a single change, which keeps the central history nice and readable. It is also normal to tick the box that says to delete the feature-branch. You don't want all these feature-branches hanging around clogging up the system and this is a good point to get rid of them.

Merge Locally and Push to the Integration Branch

In the case when you do the merge on your local machine, the trick is to ensure that your integration branch is up to date. If you do not, then your local copy of the integration branch may lag the central integration branch and, when you try pushing your local develop you get a complaint. Luckily, keeping your develop branch up to date with the central develop is not that hard. Here's a typical routine:

- A team member checkouts (switches to) the integration branch `develop`
- Then they pull from the `develop` branch to bring the local copy up to date 
- They fix up any merge conflicts 
- Then merge into `develop` from the feature branch
- They fix up any merge conflicts, again
- Push the result back to git

At the command line this looks like:

git checkout develop
git pull
# Fix up any conflicts
git merge v0.1/123_new_drop_downs
# Fix up any conflicts
git push

After this, team mates will need to update their local repos by pulling develop from GitHub.