Branching

Overview

Teaching: 25 min
Exercises: 15 min
Questions
  • What is a branch?

  • How can I merge changes from another branch?

Objectives
  • Know what branches are and why you would use them

  • Understand how to merge branches

  • Understand how to resolve conflicts during a merge

What is a branch?

You might have noticed the term branch in status messages:

$ git status
On branch master
nothing to commit (working directory clean)

and when we wanted to get back to our most recent version of the repository, we used git switch master.

Not only can our repository store the changes made to files and directories, it can store multiple sets of these, which we can use and edit and update in parallel. Each of these sets, or parallel instances, is termed a branch and master is Git’s default branch.

A new branch can be created from any commit. Branches can also be merged together.

Why are branches useful?

Suppose we’ve developed some software and now we want to try out some new ideas but we’re not sure yet whether we’ll keep them. We can then create a branch feature1 and keep our master branch clean. When we’re done developing the feature and we are sure that we want to include it in our program, we can merge the feature branch with the master branch. This keeps all the work-in-progress separate from the master branch, which contains tested, working code.

When we merge our feature branch with master git creates a new commit which contains merged files from master and feature1. After the merge we can continue developing. The merged branch is not deleted. We can continue developing (and making commits) in feature1 as well.

Branching workflows

A simple workflow I recommend using is the feature branch workflow.

This consists of:

The main idea is to start each piece of work in a new feature branch, and merge finished work into master. You shouldn’t normally be committing directly to master.

For example:

Feature branches

There are various possible workflows when using Git for code development. If you want to learn more about different workflows with Git, have a look at this discussion on the Atlassian website.

Branching in practice

One of our colleagues wants to contribute to the paper but is not quite sure if it will actually make a publication. So it will be safer to create a branch and carry on working on this “experimental” version of the paper in a branch rather than in the master.

So we create a new branch:

$ git branch simulations

and then switch to it.

$ git switch simulations
Switched to branch 'simulations'

In practice you’d probably want to combine these two steps using git switch -c simulations which both creates the new branch, and switches to it all in one command.

We’re going to change the title of the paper and update the author list (adding John Smith). However, before we get started it’s a good practice to check that we’re working on the right branch.

$ git branch			# Double check which branch we are working on
  master
* simulations

The * indicates which branch we’re currently in. Now let’s make the changes to the paper.

$ nano paper.md		# Change title and add co-author
$ git add paper.md
$ git commit		# "Modify title and add John as co-author"

If we now want to work in our master branch. We can switch back by using:

$ git switch master
Switched to branch 'master'

Having written some of the paper, we have thought of a better title for the master version of the paper.

$ nano paper.md		# Rewrite the title
$ git add paper.md
$ git commit		# "Include aircraft in title"

Merging and resolving conflicts

We are now working on two papers: the main one in our master branch and the one which may possibly be collaborative work in our “simulations” branch. Let’s add another section to the paper to write about John’s simulations.

$ git switch simulations	# Switch branch
$ nano paper.md			# Add 'simulations' section
$ git add paper.md
$ git commit -m "Add simulations" paper.md

At this point let’s visualise the state of our repo, and we can see the diverged commit history reflecting the recent work on our two branches:

git log --graph --all --oneline --decorate
* 89d5c6e (simulations) Add simulations
* 05d393a Change title and add coauthor
| * (HEAD, master) bdebbe0 Include aircraft in title
|/
* 87a65e6 Explain motivation for research
* 6a48241 Cite previous work in introduction
* ed26351 Cite PCASP paper
* 7446b1d Start the introduction
* 4f572d5 Add title and author

After some discussions with John we decided that we will publish together, hence it makes sense to now merge all that was authored together with John in branch “simulations”. We can do that by merging that branch with the master branch. Let’s try doing that:

$ git switch master		# Switch branch
$ git merge simulations		# Merge simulations into master
Auto-merging paper.md
CONFLICT (content): Merge conflict in paper.md
Automatic merge failed; fix conflicts and then commit the result.

Git cannot complete the merge because there is a conflict - if you recall, after creating the new branch, we changed the title of the paper on both branches. We have to resolve the conflict and then complete the merge. We can get some more detail

$ git status
On branch master
You have unmerged paths.
  (fix conflicts and run "git commit")

Unmerged paths:
  (use "git add <file>..." to mark resolution)

	both modified:	    paper.md

Let’s look inside paper.md:

# Title
<<<<<<< HEAD
Aircraft measurements of biomass burning aerosols over West Africa
=======
Simulations of biomass burning aerosols over West Africa
>>>>>>> simulations

The mark-up shows us the parts of the file causing the conflict and the versions they come from. We now need to manually edit the file to resolve the conflict. This means removing the mark-up and doing one of:

We edit the file. Then commit our changes:

$ nano paper.md		# Resolve conflict by editing paper.md
$ git add paper.md		# Let Git know we have resolved the conflict
$ git commit

This is where version control proves itself better than DropBox or GoogleDrive, this ability to merge text files line-by-line and highlight the conflicts between them, so no work is ever lost.

We can see the two branches merged if we take another look at the log graph:

$ git log --graph --decorate --all --oneline
*   39cc80d (HEAD, master) Merge branch 'simulations'
|\
| * 89d5c6e (simulations) Add simulations
| * 05d393a Change title and add coauthor
* | bdebbe0 Include aircraft in title
|/
* 87a65e6 Explain motivation for research
* 6a48241 Cite previous work in introduction
* ed26351 Cite PCASP paper
* 7446b1d Start the introduction
* 4f572d5 Add title and author

Looking at our history - revisited

We already looked at “going back in time with Git”. But now we’ll look at it in more detail to see how moving back relates to branches and we will learn how to actually undo things. So far we were moving back in time in one branch by switching to one of the past commits.

But we were then in the “detached HEAD” state.

Add a commit to detached HEAD

  • Switch to one of the previous commits from our repository.
  • Make some changes and commit them. What happened?
  • Now try to run git branch. What can you see?

Solution

git switch -d HEAD~1		# Check out the commit one before last
nano paper.md			# Make some edits
git add paper.md		# Stage the changes
git commit			# Commit the changes
git branch			# You should see a message like the one below,
				# indicating your commit does not belong to a branch
* (detached from 57289fb)
  master

You have just made a commit on a detached HEAD – as you can see from the output above, a new temporary branch has been created, which doesn’t have a name.

See this detached HEAD animation of the above process.

Abandon the commit on a detached HEAD

You decide that you want to abandon that commit. How would you get back to the current version of your project?

Solution

git switch master

Git will warn you that you are leaving behind changes that would be lost:

The output you see will be slightly different to that below, reflecting your previous commit message and commit ID.

Warning: you are leaving 1 commit behind, not connected to
any of your branches:

eb7c650 Add empty line for branching exercise

If you want to keep them by creating a new branch, this may be a good time
to do so with:

 git branch new_branch_name eb7c650

 Switched to branch 'master'
 Your branch is up-to-date with 'master'.

See this abandon detached HEAD animation.

Save your changes in a new branch

Preparation:

  • You should be on the master branch after that last exercise. If not, switch to master again: git switch master
  • Switch (-d) to one of the previous commits from your repository.
  • Make some changes, save the file(s), and make a commit on the detached HEAD as you did in the first exercise.
  • Run git branch to list your local branches, and see that you are on a temporary branch.

This time we want to keep the commit rather than abandon it.

  • Create a new branch and switch to it.
  • Now run git log and see that your new commit belongs to this new branch.
  • List your local branches again and see that the temporary branch has gone.
  • Switch back to the master branch

Solution

git switch -d HEAD~1        # Checkout the commit before last
nano paper.md               # Modify one of your files
git commit -a               # Commit all the modified files
git branch			# List local branches
* (HEAD detached from f908519)
 master
 simulations

You are currently on a temporary, unnamed branch, as indicated by the *.

git switch -c dh-exercise	# Create and switch to a new branch
Switched to a new branch 'dh-exericise'
git branch			# View local branches
* dh-exericise
 master
 simulations

The commit you made on the detached HEAD now belongs to a named branch (dh-exercise in the example above), rather than a temporary branch.

git switch master		# Switch back to the 'master' branch

See this new branch animation for the key points in this exercise.

Key Points

  • git switch switches to another branch

  • git switch -c <branch_name> creates a new branch and switches to it

  • git merge <branch_name> merges into current branch

  • Use feature branches for new ideas and fixes, before merging into master

  • Merging does not delete any branches