Git: branching and merging

Branch is a core concept in Git and many other version control systems. Generally speaking, a branch is a line of development which is parallel and independent of all other lines but which still shares the same history with all other branches if you look far enough in time. Because the branches are independent the changes applied to one branch does not automatically propagate to other branches and are not visible there. This way the development can be done in parallel by many contributors without disturbing each other.

Mainline

Git by default (after creating a repository) comes with a single branch named master. This branch represent the main development line (often named mainline) of the repository and is a branch from which new branches can be created of. It is also the destination branch where other branches can be merged to.

Of course, new branches doesn’t have to be created directly of this branch and merged directly into it but may also be created from other custom branches and merged into them.

Feature branches

A typical reason to create a branch is to have a “private space” to develop a new feature without disturbing other people’s work and being disturbed by them. It also keeps the mainline free from questionable and incomplete code. Only after the feature is finished and tested on the feature branch, it is merged as a whole into the mainline. Usually, after merging the feature branch can be safely deleted.

Bug-fix branches

Another reason to create a branch is to develop a bug-fix. Because the same bug is often present in many different branches (mainline and few releases), it is generally easier and faster to create a new branch with the bug-fix and merge it into all branches where the bug is present. It is completely fine if such branch contains a single commit only.

Experimental branches

A branch can be also created to experiment with tentative idea without affecting other people’s work. If the experiment turns out very well, the experimental branch may be merged back. If it fails, the experimental branch can be deleted without any merging.

Release branches

Branches are also used to take a snapshot of the mainline at some point in time and prepare it for release. Usually, only bug-fixes and small improvements are added to a release branch in order to stabilize it and get ready for final testing and build.

Creating branches

Creating a branch in Git is very easy:

$ git branch feature1-branch

The created branch feature1-branch is a child of the current branch and has exactly the same history up to the moment they were branched. The command above merely creates a branch so if you want to work on the new branch, you have to switch to it using command:

$ git checkout feature1-branch
Switched to branch 'feature1-branch'

After this you can safely commit changes to the new branch similarly as you would do with the mainline.

There is also a shorthand command which creates a branch and immediately switches to it:

$ git checkout -b feature1-branch
Switched to a new branch 'feature1-branch'

As you should know Git has a notion of local and remote repositories. The branch we have just created is a local one and is present only in a local repository. Usually, you would want to push this new branch to a remote repository so that other team members can access and work on it:

$ git push -u origin feature1-branch
Total 0 (delta 0), reused 0 (delta 0)
To file:///home/robert/tmp/git2/
 * [new branch]      feature1-branch -> feature1-branch
Branch feature1-branch set up to track remote branch feature1-branch from origin.

Option -u ensures that the local branch tracks the new remote branch. This way Git is able to find the right local branch when pulling the changes from a remote repository using argument-less git pull command.

Listing branches

With git branch command you can also see all local branches:

$ git branch
* feature1-branch
  master

all remote branches:

$  git branch -r
  origin/HEAD -> origin/master
  origin/feature1-branch
  origin/master

or just all (local and remote) branches:

$  git branch -a
* feature1-branch
  master
  remotes/origin/HEAD -> origin/master
  remotes/origin/feature1-branch
  remotes/origin/master

The branch annotated with an asterisk is the current branch. Additionally, Git provides an option to list branches which are already merged into the current branch (either directly or indirectly):

$ git branch --merged
  feature1-branch
* master
  test

There is also an opposite option –no-merged. These two options are very useful in determining which branches can be safely deleted from the repository.

Switching between branches

As shown before switching between branches is done using git checkout command:

$ git checkout feature1-branch
Switched to branch 'feature1-branch'

All untracked files and local uncommitted changes in the working tree are left untouched so they can be later committed to the new branch. If the target branch is not found in the local repository but there exists a tracking branch in exactly one remote repository, the command creates a local branch pointing to the remote one and switches to it.

Deleting branches

If a branch was fully merged and is no longer needed, it can be deleted with command:

$ git branch -d bugfix1-branch
Deleted branch bugfix1-branch (was b12dd4e).

If it was not merged and we don’t plan to do so for some reason, we can remove the branch forcefully:

$ git branch -D experimental1-branch
Deleted branch experimental1-branch (was b12dd4e).

These commands operate on the local repository only so after removing a local branch, it is usually a good idea to remove the same branch from the remote repository:

$ git push origin :experimental1-branch
To file:///home/robert/tmp/git2/
 - [deleted]         experimental1-branch

While this looks almost like pushing a new branch to a repository, there is a slight difference – a colon before branch name (actually empty branch name before the colon) which informs Git that the branch should be removed rather than created. If the command is too obscure to you, an alternative may be used:

$ git push origin --delete experimental1-branch
To file:///home/robert/tmp/git2/
 - [deleted]         experimental1-branch

Synchronizing with remote

Even if a local branch has a tracking remote branch in the remote repository, the changes committed to the local branch won’t automatically appear in the remote one. Changes made to the current local branch can be pushed to the remote repository (possibly with changes to other branches depending on Git version and configuration) using command:

$ git push

Additionally, to fetch changes made by other developers in a remote tracking branch and apply them to the current local one, the pull command can be used:

$ git pull

When working in many people on a project, you may end up in a situation that somebody removed a branch from a remote repository but you still see it in the output of git branch -a command even after running git pull many times. It is because git pull does not automatically prune no-longer-existing branches and it has to be done manually:

$ git remote prune origin
Pruning origin
URL: file:///home/robert/tmp/git2/
 * [pruned] origin/feature7-branch

Merging

When a new child branch is created based on a parent branch, they have exactly the same history. But once you start applying changes to one of them, their histories start to diverge. At some point you may decide that you want to share some of the changes from one of the branches (usually child branch) with another one (usually parent branch). This concept is commonly called merging in version control systems. After merging, the changes from the source branch will become available and visible in the destination branch.

To merge a branch into another one, you have to switch to the destination branch and then run git merge command with the name of the source branch to merge in:

$ git merge feature11-branch 
Updating b12dd4e..0b7f55a
Fast-forward
 ABC.txt | 0
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 ABC.txt

Of course, after this the source branch may be deleted and the changes to the destination branch should be pushed to a remote repository.

In case Git complains about conflicts during merge operation, you can refer to the article explaining how to resolve merge conflicts.

Conclusion

Branching and merging is one of the most important concepts in version control systems that every developer should know. In this article I have concentrated on the basics which should be enough in most cases. For details you can always consult git-branch, git-checkout and git-merge manual pages.

Advertisement

About Robert Piasecki

Husband and father, Java software developer, Linux and open-source fan.
This entry was posted in Git, Version control and tagged , , . Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.