Branching and tags

Overview

Teaching: 40 min
Exercises: 15 min
Questions
  • How can I or my team work on multiple features in parallel?

  • How can I permanently reference a point in history, like a software version?

Objectives
  • Be able to create and navigate between branches.

  • Know the difference between a branch and a tag.

So far we have gone back in our git history by doing checking out a particular commit (eg git checkout e03af1).

After doing that, you will see a message from git that starts with the ominous warning: You are in 'detached HEAD' state.

What exactly does that mean? It has to do with something git uses to make navigating repositories much more straightforward. Git uses pointers, which are labels for particular commits in the history. You can think of them as sticky notes, because they can move around.

There is one pointer that every git repository has, called HEAD. It simply means that commit that is currently checked out (and therefore reflect the state of the files on your local disk).

If you have a look in git log you will see the label HEAD at the place in history where you are.

We can use these pointers to make life much easier for ourselves. Instead of referring to a particular commit, we can refer to a commit in relation to a label. For example, HEAD~1 refers to the commit immediately prior to wherever HEAD is pointing. HEAD~2 refers to the commit two commits previous to HEAD.

So if we want to checkout a commit that is two previous to where we are, we can run:

git checkout HEAD~2

In addition to HEAD, git also uses other pointers called branches (we’ll look at these in more detail below). By default, every git repository starts with one branch: master.

While using branches enables lots of capability, in themselves they are just labels that point to a specific commit.

While HEAD can move backward and forward around the repository, the master label always points to the newest commit made (while the master branch is checked out).

So what is this detached HEAD all about?

A detached HEAD means that the HEAD label is pointing directly to a commit, rather than a branch label. That means that git won’t let you make any commits from where you are, because commits always have to happen on a branch. So to “re-attach” your HEAD, it needs to be pointing at a branch label, not a commit.

Challenge

Enter a detached HEAD state in your git repository. See if you can exit the detached state.

Hint

Remember that to be “attached”, HEAD needs to be pointing at a branch label

Solution

We can enter a detached HEAD state by

$ git checkout HEAD~1

And re-attach it by checking out the master branch (so that the HEAD label is pointing at master not a commit)

$ git checkout master

Motivation for branches

In the previous section we tracked a guacamole recipe with git.

Up until now our repository had only one branch with one commit coming after the other:

Linear

Software development, and analytical code, is often non-linear:

The strength of version control is that it permits the researcher to isolate different tracks of work. Researchers can work on different things and merge the changes they made to the source code files afterwards to create a composite version that contains both the changes:

Git collaborative

A group of commits that create a single narrative are often referred to as a branch, but from git’s perspective the branch is really just the label that points to the commit. There are different branching strategies, but it is useful to think that a branch tells the story of a feature, e.g. “fast sequence extraction” or “Python interface” or “fixing bug in matrix inversion algorithm”.


What is a commit?

Before we exercise branching, a quick recap of what we got so far.

We have three commits (we use the first two characters of the commits) and only one development line (branch) and this branch is called “master”:

$ git log --oneline 

40a87b4 (HEAD -> master) Revert "Add an onion to the recipe"
295b424 Add an onion to the recipe
53f42b7 (origin/master, origin/HEAD) Added salt to ingredients
1fea6fd Added instructions file
1805665 added txt file extension to ingredients
db9b3a9 Initial commit - added ingredients

Challenge

Go to http://git-school.github.io/visualizing-git/ and create several commits. Note the way the master and head pointers move with each commit.

  • What happens when you enter a ‘detached HEAD state’?

Solution

You can enter a detached HEAD state by checking out a previous commit git checkout HEAD~1 The HEAD pointer no longer points to a branch pointer, but directly to a commit. This is what a ‘detached HEAD’ means - HEAD is detached from a branch.

Which branch are we on?

We can see which branch we’re on (where HEAD is poinging) with the output from git status.

To see all existing branches, as well as which one we’re on use git branch:

$ git branch

* master

In the following section we will learn how to create branches, how to switch between them, how to merge branches, and how to remove them afterwards.

Creating and working with branches

In the vizualising git environment, let’s create a branch called experiment.

git branch experiment

Notice that there is a new pointer (at the same commit) with the experiment label.

We can move HEAD to that pointer by using checkout:

git checkout experiment

Notice that HEAD is now pointing to experiment instead of master.

Let’s make a commit on experiment.

git commit -m "new commit on experiment branch"

The two branches, master and experiment have started to diverge.

Challenge

Make some commits on the master branch, and notice the way the graph is growing.

Solution

git checkout master
git commit -m "new commit on master"

Challenge

Back in our git-guacamole repository, create and switch to a branch called experiment pointing to the present commit.

  • Add garlic as a new ingredient at the top of the ingredients list, and commit the change.
  • Now make a new commit where you reduce the amount of garlic.
  • Use git branch and git log to view the branches and commits.

Solution

$ git branch experiment
$ git checkout experiment
$ echo "* 1 clove crushed garlic" >> ingredients.txt
$ git add ingredients.txt
$ git commit -m "I wonder if garlic will work"
$ vim ingredients.txt #OR YOUR FAVOURITE EDITOR
$ git add ingredients.txt
$ git commit -m "A bit less garlic"
$ git branch
* experiment
master
$ git log --oneline 
47d0a9d (HEAD -> experiment) A bit less garlic
e1e6f7d I wonder if garlic will work
40a87b4 (master) Revert "Add an onion to the recipe"
295b424 Add an onion to the recipe
53f42b7 (origin/master, origin/HEAD) Added salt to ingredients
1fea6fd Added instructions file
1805665 added txt file extension to ingredients
db9b3a9 Initial commit - added ingredients

Extra options to git log

You can get a graph view from git log by adding --graph.

Try:

git log --all --graph --oneline

Challenge

  • Change to the branch master.
  • Create another branch called less-salt where you reduce the amount of salt.
  • Commit your changes to the less-salt branch.
  • View the branches and commits.

Solution

git checkout master
git branch less-salt
git checkout less-salt
vim ingredients
git add ingredients.txt
git commit -m "reduce the salt"
git branch
  experiment
* less-salt
  master
git log --all --graph --oneline 
* 3db05f9 (HEAD -> less-salt) reduce the salt
| * 47d0a9d (experiment) A bit less garlic
| * e1e6f7d I wonder if garlic will work
|/
* 40a87b4 (master) Revert "Add an onion to the recipe"
* 295b424 Add an onion to the recipe
* 53f42b7 (origin/master, origin/HEAD) Added salt to ingredients
* 1fea6fd Added instructions file
* 1805665 added txt file extension to ingredients
* db9b3a9 Initial commit - added ingredients

Here is a graphical representation of what we have created:

# Guacamole recipe

Used in teaching git.

Now you should have this situation:

$ git log --all --graph --oneline 

* 29e2be2 (HEAD -> master) added readme
| * 3db05f9 (less-salt) reduce the salt
|/
| * 47d0a9d (experiment) A bit less garlic
| * e1e6f7d I wonder if garlic will work
|/
* 40a87b4 Revert "Add an onion to the recipe"
* 295b424 Add an onion to the recipe
* 53f42b7 (origin/master, origin/HEAD) Added salt to ingredients
* 1fea6fd Added instructions file
* 1805665 added txt file extension to ingredients
* db9b3a9 Initial commit - added ingredients

And for comparison this is how it looks on GitHub.

Some branch tips

Branch naming

$ git branch -r | grep sarah

Always have only one main development line

Every commit on the main development line should build/compile

Tags

Let’s add an annotated tag to our current state of the guacamole recipe:

$ git tag -a nobel-2017 -m "recipe I made for the 2017 Nobel banquet"

As you may have found out already, git show is a very versatile command. Try this:

$ git show nobel-2017

Have a look at the log to see how your tag looks.

For more information about tags see for example the Pro Git book chapter on the subject.


Summary

Let us pause for a moment and recapitulate what we have just learned:

$ git branch               # see where we are
$ git branch <name>        # create branch <name>
$ git checkout <name>      # switch to branch <name>

Since the following command combo is so frequent:

$ git branch <name>        # create branch <name>
$ git checkout <name>      # switch to branch <name>

There is a shortcut for it:

$ git checkout -b <name>   # create branch <name> and switch to it

Key Points

  • A branch is a division unit of work, to be merged with other units of work.

  • A tag is a pointer to a moment in the history of a project.