Close

COMP0023: Research Software Engineering With Python

Home

Branches¶

Branches are incredibly important to why git is cool and powerful.

They are an easy and cheap way of making a second version of your software, which you work on in parallel, and pull in your changes when you are ready.

In [1]:
import os
top_dir = os.getcwd()
git_dir = os.path.join(top_dir, 'learning_git')
working_dir = os.path.join(git_dir, 'git_example')
os.chdir(working_dir)

In [2]:
%%bash
git branch # Tell me what branches exist

* main

In [3]:
%%bash
git switch -c experiment # Make a new branch (use instead checkout -b if you have a version of git older than 2.23)

Switched to a new branch 'experiment'

In [4]:
%%bash
git branch

* experiment
main

In [5]:
%%writefile Wales.md
Mountains In Wales
==================

* Pen y Fan
* Tryfan
* Snowdon
* Glyder Fawr
* Fan y Big

Overwriting Wales.md

In [6]:
%%bash

[experiment 2b7dfbf] Add Cadair Idris
1 file changed, 2 insertions(+)

In [7]:
%%bash
git switch main # Switch to an existing branch (use checkout if you are using git older than 2.23)

Switched to branch 'main'

Your branch is up to date with 'origin/main'.

In [8]:
%%bash
cat Wales.md

Mountains In Wales
==================

* Pen y Fan
* Tryfan
* Snowdon
* Fan y Big

In [9]:
%%bash
git switch experiment

Switched to branch 'experiment'

In [10]:
cat Wales.md

Mountains In Wales
==================

* Pen y Fan
* Tryfan
* Snowdon
* Glyder Fawr
* Fan y Big


Publishing branches¶

To let the server know there's a new branch use:

In [11]:
%%bash
git push -u origin experiment

remote:
remote: Create a pull request for 'experiment' on GitHub by visiting:
remote:      https://github.com/UCL/github-example/pull/new/experiment
remote:
To github.com:UCL/github-example.git
* [new branch]      experiment -> experiment

branch 'experiment' set up to track 'origin/experiment'.


We use --set-upstream origin (Abbreviation -u) to tell git that this branch should be pushed to and pulled from origin per default.

If you are following along, you should be able to see your branch in the list of branches in GitHub.

Once you've used git push -u once, you can push new changes to the branch with just a git push.

If others checkout your repository, they will be able to do git switch experiment to see your branch content, and collaborate with you in the branch.

In [12]:
%%bash
git branch -r

  origin/experiment
origin/gh-pages
origin/main
origin/master


Local branches can be, but do not have to be, connected to remote branches They are said to "track" remote branches. push -u sets up the tracking relationship. You can see the remote branch for each of your local branches if you ask for "verbose" output from git branch:

In [13]:
%%bash
git branch -vv

* experiment 2b7dfbf [origin/experiment] Add Cadair Idris
main       e61697c [origin/main] Add another Beacon


Find out what is on a branch¶

In addition to using git diff to compare to the state of a branch, you can use git log to look at lists of commits which are in a branch and haven't been merged yet.

In [14]:
%%bash
git log main..experiment

commit 2b7dfbf6c4545492c2f26e00a6698d2211ba8f2d
Author: Lancelot the Brave <l.brave@spamalot.uk>
Date:   Thu Dec 8 15:14:33 2022 +0000



Git uses various symbols to refer to sets of commits. The double dot A..B means "ancestor of B and not ancestor of A"

So in a purely linear sequence, it does what you'd expect.

In [15]:
%%bash

* e600228 Include lakes in the scope
* 2a2f993 Change title


But in cases where a history has branches, the definition in terms of ancestors is important.

In [16]:
%%bash

* 2b7dfbf Add Cadair Idris


If there are changes on both sides, like this:

In [17]:
%%bash
git switch main

Switched to branch 'main'

Your branch is up to date with 'origin/main'.

In [18]:
%%writefile Scotland.md
Mountains In Scotland
==================

* Ben Eighe
* Cairngorm
* Aonach Eagach

Writing Scotland.md

In [19]:
%%bash
git diff Scotland.md

In [20]:
%%bash
git commit -am "Commit Aonach onto main branch"

On branch main
Your branch is up to date with 'origin/main'.

Untracked files:
(use "git add <file>..." to include in what will be committed)
Scotland.md
__pycache__/
wsd.py

nothing added to commit but untracked files present (use "git add" to track)

---------------------------------------------------------------------------
CalledProcessError                        Traceback (most recent call last)
Cell In[20], line 1
----> 1 get_ipython().run_cell_magic('bash', '', 'git commit -am "Commit Aonach onto main branch"\n')

File /opt/hostedtoolcache/Python/3.8.15/x64/lib/python3.8/site-packages/IPython/core/interactiveshell.py:2417, in InteractiveShell.run_cell_magic(self, magic_name, line, cell)
2415 with self.builtin_trap:
2416     args = (magic_arg_s, cell)
-> 2417     result = fn(*args, **kwargs)
2418 return result

File /opt/hostedtoolcache/Python/3.8.15/x64/lib/python3.8/site-packages/IPython/core/magics/script.py:153, in ScriptMagics._make_script_magic.<locals>.named_script_magic(line, cell)
151 else:
152     line = script
--> 153 return self.shebang(line, cell)

File /opt/hostedtoolcache/Python/3.8.15/x64/lib/python3.8/site-packages/IPython/core/magics/script.py:305, in ScriptMagics.shebang(self, line, cell)
300 if args.raise_error and p.returncode != 0:
301     # If we get here and p.returncode is still None, we must have
302     # killed it but not yet seen its return code. We don't wait for it,
303     # in case it's stuck in uninterruptible sleep. -9 = SIGKILL
304     rc = p.returncode or -9
--> 305     raise CalledProcessError(rc, cell)

CalledProcessError: Command 'b'git commit -am "Commit Aonach onto main branch"\n'' returned non-zero exit status 1.

Then this notation is useful to show the content of what's on what branch:

In [21]:
%%bash
git log --left-right --oneline main...experiment

> 2b7dfbf Add Cadair Idris


Three dots means "everything which is not a common ancestor" of the two commits, i.e. the differences between them.

Merging branches¶

We can merge branches, and just as we would pull in remote changes, there may or may not be conflicts.

In [22]:
%%bash
git branch
git merge experiment

  experiment
* main
Updating e61697c..2b7dfbf
Fast-forward
Wales.md | 2 ++
1 file changed, 2 insertions(+)

In [23]:
%%bash

* 2b7dfbf Add Cadair Idris


Cleaning up after a branch¶

In [24]:
%%bash
git branch

  experiment
* main

In [25]:
%%bash
git branch -d experiment

Deleted branch experiment (was 2b7dfbf).

In [26]:
%%bash
git branch

* main

In [27]:
%%bash
git branch --remote

  origin/experiment
origin/gh-pages
origin/main
origin/master

In [28]:
%%bash
git push --delete origin experiment
# Remove remote branch
# - also can use github interface

To github.com:UCL/github-example.git
- [deleted]         experiment

In [29]:
%%bash
git branch --remote

  origin/gh-pages
origin/main
origin/master


A good branch strategy¶

• A develop or main branch: for general new code - (the cutting edge version of your software)
• feature branches: for specific new ideas. Normally branched out from main.
• release branches: when you share code with users. A particular moment of the develop process that it's considered stable.
• Useful for including security and bug patches once it's been released.
• A production branch: code used for active work. Normally it's the same than the latest release.

Grab changes from a branch¶

Make some changes on one branch, switch back to another, and use:

git checkout <branch> <path>


to quickly grab a file from one branch into another. This will create a copy of the file as it exists in <branch> into your current branch, overwriting it if it already existed. For example, if you have been experimenting in a new branch but want to undo all your changes to a particular file (that is, restore the file to its version in the main branch), you can do that with:

git checkout main test_file


Using git checkout with a path takes the content of files. To grab the content of a specific commit from another branch, and apply it as a patch to your branch, use:

git cherry-pick <commit>