In a collaborative environment, it is common for several developers to share
and work on the same source code. While some developers will be fixing bugs,
others will be implementing new features, etc. With so much going on, there
needs to be a system in place for managing different versions of the same code
Branching allows each developer to branch out from the original code base
and isolate their work from others. It also helps Git to easily merge versions
What is a Git branch?
A Git branch is essentially an independent line of development. You can take
advantage of branching when working on new features or bug fixes because it
isolates your work from that of other team members.
Different branches can be merged into any one branch as long as they belong
to the same repository.
The diagram below illustrates how development can take place in parallel
Branching enables you to isolate your work from others. Changes in the
primary branch or other branches will not affect your branch, unless you decide
to pull the latest changes from those branches.
It is a common practice to create a new branch for each task (i.e., a branch
for bug fixing, a branch for new features, etc.). This method allows others to
easily identify what changes to expect and also makes backtracking simple.
Create a branch
Creating a new branch does not change the repository; it simply points out
For example, let’s create a branch called “issue1” using the command git
git branch issue1
The illustration below provides a visual on what happens when the branch is
created. The repository is the same, but a new pointer is added to the current
The git checkout command allows you to switch branches by updating the files in your working tree to match the version stored in the branch that you wish to switch to.
You can think of it as a way of switching between different workspaces.
HEAD is used to represent the current snapshot of a branch. For a new repository, Git will by default point HEAD to the master branch. Changing where HEAD is pointing will update your current active branch.
The ~(tilde) and ^(caret) symbols are used to point to a position relative to a specific commit. The symbols are used together with a commit reference, typically HEAD or a commit hash.
Whenever you switch to another branch with uncommitted changes (or new files added) in your working tree, these uncommitted changes will also be carried to the new branch that you switch to. Changes that you commit will be committed to the newly switched branch.
However, if Git finds a conflict between the files from the newly switched branch and the uncommitted changes from the previous branch, you will not be allowed to switch to the other branch. You must commit or stash those changes first before switching branches.
You can think of stash as a drawer to store uncommitted changes temporarily. Stashing allows you to put aside the “dirty” changes in your working tree and continue working on other things in a different branch on a clean slate.
Uncommitted changes that are stored in the stash can be taken out and applied to the original branch and other branches as well.
Although Git is local on your computer, you can also have remote copies of a repository. Remote repositories can be on a private central server or even simply on a coworker’s computer.
You can also have a remote repository hosted using an online service–such as Backlog.
You can retrieve others’ changes to the repository or move your local copy of a repository to the remote server.
Pull remote branch
You can apply the latest changes from a remote repository to your local repository using the git pull command.
For example, say the remote branch is upstream of your local branch. The
remote branch would include all of the changes that belong to the local branch
as shown below.
In this case, if we were to apply a merge from the remote branch (origin/master) into our local branch (master), it would be a fast-forwardwar merge.
However, if there are changes in the local master branch that are not present in the remote origin/master branch, the git pull command will execute a merge and create a merge commit that ties those changes together.
When a pull is executed, a merge commit will be automatically created in the local repository. If there is a conflict, you will have to resolve the conflict and commit the merge manually.
Fetch remote branch
When you execute a pull, the changes from the remote branch automatically merge into your current local branch. If you want to obtain the remote changes but not have them merged into your current local branch, you can execute the git fetch command.
Fetch will download the changes from remote that do not yet exist on your local branch. The FETCH_HEAD ref can be used to track the fetched changes from the remote repository.
The revision history will look like below when both the remote and local branch contain different descendants.
Once changes are fetched, you can apply those changes to your local repository by merging in FETCH_HEAD or by executing a pull.
Once FETCH_HEAD has been merged, the revision history will yield the same result as a git pull operation. Pull is essentially a simultaneous execution of fetch and merge operations.
Push branch to remote
All of your commits are available to you until you push your local branch to the remote repository. That is, you can work on your own local branch at your own pace without affecting other members of the team.
When you push your local branch to remote, Git will do a fast-forward merge to the destination repository.
However, if the push results in a non-fast-forward merge, Git will decline your push to prevent you from overwriting previous commits. In that case, you have to pull the latest remote changes and push again.
You must not overwrite or change commits that have already been committed to the remote repository. Doing so will cause other team members’ local repositories to desynchronize with the remote repository.
Let’s take a look at the Gitflow Workflow as outlined in A successful Git branching model.
This workflow consists of five types of branches, each with different roles:
- Feature branch (aka Topic branch)
- Release branch
- Hotfix branch
- Develop branch (aka Integration branch)
Upon making the first commit in a repository, Git will automatically create a master branch by default. Subsequent commits will go under the master branch until you decide to create and switch over to another branch.
Codebase residing in the master branch is considered to be production-ready. When it is ready for a specific release, the latest commit will be given a release tag.
When you start working on a new feature/bug fix, you should create a feature/topic branch. A feature/topic branch is normally created off a develop/integration branch. This feature/topic branch can reside in your local machine throughout the entire development lifecycle of the feature.
You will push this branch to the remote repository whenever you are ready to merge the change set with the develop/integration branch.
When you roll out a new release, you create a release branch. A release branch helps you to ensure that the new features are running correctly.
By convention, release branch names normally start with the prefix “release-“.
The release branch is typically created off the develop/integration branch when it’s close to being production-ready.
Only bug fixes and release related issues should be addressed on this branch. Having this branch will allow other team members to continue pushing new features to the develop/integration branch without interrupting the release workflow.
When you are ready to release, merge the release branch with the master branch and tag a release number to the newly created merge commit.
You should also merge the release branch with the develop/integration branch so that both the master and develop/integration branches receive the latest changes/bug fixes from the release branch.
When you need to add an important fix to your production codebase quickly, you can create a Hotfix branch off the master branch.
By convention, hotfix branch names normally start with the prefix “hotfix-“.
The advantage of a hotfix branch is that it allows you to quickly issue a patch and have the change merged with the master branch without having to wait for the next release.
A hotfix branch should be merged with the develop/integration branch as well.
A develop/integration branch should be kept stable at all times. This is important because new branches are created off of this branch, and this branch could eventually go out live on production. Continuous integration tools such as Jenkins can be used to help do just that.
When some changes need to be merged into the develop/integration branch, it is generally a good idea to create a feature/topic branch to work on independently.
Sample feature branch workflow
You will need to create a branching strategy that works best for your team, of course. But here is a quick example of how to follow a branching strategy workflow involving two types of branches: a develop/integration branch and a feature/topic branch.
Let’s say you are working on a new feature. Suddenly, somebody finds a bug in the production and you are now tasked to fix that bug parallel to working on the new feature.
Before starting on the bug fix, you can create a new branch off of the develop/integration branch. This new branch will isolate the bug fix from the new feature that you were working on.
When you are ready to release the bug fix, merge the bug fix feature/topic branch into the develop/integration branch.
Then you can switch back to your original feature/topic branch and continue working on the new feature.
On your feature/topic branch, you will notice that commit “X”, which is the bug fix commit, is needed to continue implementing the new feature. In other words, you will have to synchronize your current branch with the changes on the develop/integration branch.
There are two options to go about doing this. The first is to merge the develop/integration branch that includes commit “X” with the current branch.
The second option is to rebase the current branch to the develop/integration branch that includes commit “X”.
For this example, we will use the rebase approach.
Once you commit “X” on our current branch, you can safely proceed to work on your new feature.
Once you are done working on a feature/topic branch (i.e., new feature or bug fix), you would typically merge it with a develop/integration branch. You can accomplish that by using the git merge or git rebase commands, although both commands will give you different results.
Merge: Retains all changes to and history of the merged branch. The revision history can become complicated after many merge commits.
Rebase: Maintains a clean revision history since merged commits are appended at the end of the target branch. Conflicts may occur more often than in the merge method, and they need to be resolved immediately.
You and your team should decide on which method of merging to use. If you want to keep your revision history simple, you can do the following :
- Use rebase on your feature/topic branch when you want to pull the latest change from the develop/integration branch.
- If you want to merge the change from your feature/topic branch to the develop/integration branch, rebase the feature/topic branch onto the develop/integration branch first. After which, merge the changes from the feature/topic branch into the develop/integration branch. This will be a fast-forward merge with no extra merge commits being created.
You can integrate several branches by using the git merge command.
Consider the situation below. There are two branches: a “bugfix” branch with a few commits coming off the “master” branch.
In this case, merging “bugfix” back into “master” is not much of an issue. That’s because the state of “master” has not changed since “bugfix” was created. Git will merge this by moving the “master” position to the latest position of “bugfix”. This merge is called a “fast-forward”.
In the example below, however, “master” has been updated several times since “bugfix” was branched out. The changes from “bugfix” and “master” need to be combined when a merge is executed on these two branches.
For this sort of merge, a “merge commit” will be created and the “master” position will be updated to the newly created merge commit.
Even when a fast-forward merge is possible, you could still explicitly force it to merge without a fast-forward merge.
As shown above, a non fast-forward merge leaves the “bugfix” branch as it is. This leaves you with a clearer picture of the feature/topic branch “bugfix”. You can easily find where the feature/topic branch starts or ends and also track the changes that are made to the feature/topic branch.
For a cleaner revision history, you can use the git rebase command to integrate your branches.g
Say we have two branches with a non fast-forward merge scenario.
Doing a rebase will result in the branch history looking similar to the example below.
When you rebase a bugfix branch to the master branch, commits from the bugfix branch will be replayed and appended to the end of the master branch. The end result is a single simple stream of commits in the bugfix branch history.
In the event of a conflict when the commit is being appended, you will be asked by Git to fix the conflict before proceeding with rebasing the other commits.
A rebase does not move the position of the master. In any case, you will be able to do a fast-forward or a clean merge from bugfix to master after rebasing.
A Git tag is used to label and mark a specific commit in the history. Tags are commonly used to indicate release versions, with the release name (i.e., v1.0) being the name of the tag.
There are two types of Git tags:
- Lightweight tag
- Annotated tag
A lightweight tag is similar to a branch that does not change. It just points directly to a specific commit in the history. Lightweight tags are mainly used temporarily in your local workspace.
An annotated tag is checksummed and often used when you are planning to mark an important commit. You can add comments, a signature, the date, plus the tagger’s name and e-mail.
There are many questions and opinions when it comes to reviewing source code. Code review can be difficult to stick with because people become busy with other tasks or feel it is too time consuming to go through the changes that have been made. Often, code review becomes a neglected task.
It can be challenging to make code review an integral part of your team’s workflow; pull requests make that easier.
A pull request notifies other development team members of changes made in your local repository. Pull requests provide the following functions:
- Notify team members when a review or merge of work is needed
- Display changes made to source code in an easy-to-understand manner
- Provide a platform for communicating about source code
Pull requests are not a Git function. They were created by GitHub to make it easier for developers to participate in open source development, and, as a result, enabled them to create higher-quality source code. Pull requests are available in most major Git hosting services, such as Backlog and Bitbucket.
Benefits of pull requests
Below are a list of pull requests in Backlog. You can easily see which ones are open or have not been completed. This makes it easy for team members to review pull requests.
The creator and reviewer of a pull request can have discussions right on the page using comments. These comments are recorded on the server, so they can be revisited at a later time.g
You can also commit and push changes to a specific branch. The pushed commit will automatically be reflected in the pull request.
This structured code review leads to higher quality source code and provides greater context for future discussions.
Pull request clearly display what changes have been made to source code. The pull request creator can also add notes about what their goal was for the source code and provide supplementary information. This info helps inform the reviewer.
Development process with pull requests
Here is a simple development workflow with pull requests your team can follow:
- [Developer] Clone or pull the source of the work target.
- [Developer] Create a branch for the work.
- [Developer] Perform development work such as adding and modifying functions.
- [Developer] Push after the task is completed.
- [Developer] Create a pull request.
- [Review / Merge Personnel] Check the changes from the notified pull request and review.
- [Review / Merge Personnel] Judge the work and send a feedback to the developer if necessary.
- [Review / Merge Personnel] Merge if there is no problem as a result of the review.
- [Review / Merge Personnel] Close if the pull request itself becomes unnecessary as a result of the review.
Repeat steps 3 through 7 as often as needed to improve the quality of the source code.