Branching
Last updated on 2025-02-26 | Edit this page
Overview
Questions
- What are branches?
- Why use branches?
- How can I switch between branches and commits?
- Comparing commits on a branch
Objectives
- What branches are and why they are used
- Switching branches and commits
- Comparing branches/commits
- Stashing commits
Branches
Branches are key to working with version control as they allow the
development of new features or fixing of bugs without touching the
current working version of code. New features and bug fixes are then
merged into the main
branch to update the code base, but
what is a branch?
The word suggests an analogy with trees where branches are parts of a tree that extend from the “main” trunk or recursively from parent “branches”. An intuitive model of this is shown in the figure below.
The branch
has two commits on it and stems from the
parent main
at a point referred to as base
. A
branch is not just the two commits that appear to exist on it
(i.e. 3-8c52dce
and 5-2315fa0
) rather it is
the full commit history of that lineage including the commits in the
“parent”. That means the branch
consists of the commits
0-472f101
, 1-98f9a30
and
2-6769ff2
as well as 3-8c52dce
and
5-2315fa0
.
In a repository that is version controlled you will typically be
checked out on the HEAD
of a named branch. The
HEAD
means the most recent commit in the history of that
branch which on the branch
is commit 5-2315fa0
whilst on main
the HEAD
is
6-93e787c
.
You can change branches by using
git switch <branchname>
.
We can improve the output of git log
using the following
options.
Challenge 1: What is the first and last commit
on branch divide
?
Using the python-maths
repository you have cloned look
up the first and last commit of the divide
branch.
What are the commit hashes, commit messages, date/time and committers’ names?
BASH
git switch divide
git log --graph --pretty="%h %ad (%cr) %x09 %an : %s"
* 6353fb4 - (HEAD -> divide, origin/divide) bug: Fix tpyo in divide function (2024-03-26 10:28:36 +0000) <Neil Shephard>
* 7485e56 - chore: Fix merge conflict (2024-03-26 10:28:11 +0000) <Neil Shephard>
* adfef4d - feat: Divide branch (2024-03-25 15:55:15 +0000) <Neil Shephard>
* 400896a - Divide branch (2024-03-25 15:55:15 +0000) <Neil Shephard>
* c1f64b0 - Setting up the repository for git-collaboration (2024-02-02 15:48:50 +0000) <Neil Shephard>
* fa76751 - (origin/main, main) Merge pull request #6 from RSE-Sheffield/ns-rse/5-setup-clean-up (2023-10-19 22:46:14 +0100) <Neil Shephard>
|\
| * c8f0697 - 5 | Removing comment from setup.cfg (2022-10-04 11:12:23 +0100) <Neil Shephard>
* | aff8153 - Merge pull request #7 from RSE-Sheffield/subtract-mistake (2023-01-20 10:07:58 +0000) <bobturneruk>
|\ \
| |/
|/|
| * a45a8dd - introduce mistake in subtract issue (2023-01-20 09:50:03 +0000) <Robert (Bob) Turner>
| * 604a397 - introduce delibarate mistake (2022-12-21 10:29:34 +0000) <Robert (Bob) Turner>
|/
* f06c0ab - Merge pull request #4 from RSE-Sheffield/simplify_deliberate_errors (2022-06-07 14:58:27 +0100) <David Wilby>
|\
| * f55c0d2 - remove missing colon and no newline deliberate errors (2022-05-06 11:50:24 +0100) <David Wilby>
|/
* 5c9ae75 - correct python testing instruction (2021-05-18 16:15:23 +0300) <Anna Krystalli>
* 86d7633 - add correct details to each issue (2021-05-18 16:01:50 +0300) <Anna Krystalli>
* a58d6e7 - add all github issue templates (2021-05-17 13:43:57 +0300) <Anna Krystalli>
* 9429ab4 - complete subtract issue template (2021-05-14 15:53:25 +0300) <Anna Krystalli>
* bb560b0 - simplify function (2021-05-14 15:53:01 +0300) <Anna Krystalli>
* 325d038 - Merge pull request #1 from RSE-Sheffield/tests_changes (2021-05-14 14:40:36 +0300) <Anna Krystalli>
|\
| * 608ad59 - Restructure so tests pass (2021-05-14 12:24:23 +0100) <Will Furnass>
|/
* 8584b0f - correct pull request branch spec (2021-05-14 12:45:21 +0300) <Anna Krystalli>
* cdc9ea3 - correct push branch specification (2021-05-14 12:40:01 +0300) <Anna Krystalli>
* c01ff62 - add instructions to README (2021-05-14 12:38:29 +0300) <Anna Krystalli>
* 585287a - add test and CI (2021-05-14 12:38:09 +0300) <Anna Krystalli>
* 3f4d54b - rename python_package folder (2021-05-14 12:37:48 +0300) <Anna Krystalli>
* 4b1707b - use requirements.txt instead of env.yml (2021-05-14 10:04:02 +0100) <davidwilby>
* 2556966 - remove build specs from conda env (2021-05-14 10:01:28 +0100) <davidwilby>
* b50e658 - move env.yml to right place.. (2021-05-14 09:54:59 +0100) <davidwilby>
* 0d2f520 - Merge branch 'main' of github.com:RSE-Sheffield/python-calculator into main (2021-05-14 09:53:44 +0100) <davidwilby>
|\
| * b1179a7 - add package name folder (2021-05-14 11:33:06 +0300) <Anna Krystalli>
* | c883789 - add conda environment yaml (2021-05-14 09:53:06 +0100) <davidwilby>
|/
* fdb8716 - draft commit (2021-05-14 11:23:42 +0300) <Anna Krystalli>
* 328e61b - Add subtraction issue template (2021-05-13 12:23:42 +0300) <Anna Krystalli>
* 31a4a93 - Initial commit (2021-05-13 12:14:08 +0300) <Anna Krystalli>
From the git log
graph we see the first and last commits
were:
Commit | Hash | Message | Date/time | Committer |
---|---|---|---|---|
First | 31a4a93 | Initial commit | 2021-05-13 12:14:08 | Anna Krystalli |
Last | 6353fb4 | bug: Fix tpyo in divide function | 2024-03-26 10:28:36 | Neil Shephard |
Challenge 2: What commit did the
multiply
branch diverge from main
?
Again using the python-maths
repository switch to the
multiply
branch. Use git log
to find the
commit at which multiply
diverged from main
.
How many commits have been made on the main
branch?
Hint You will need to find
HEAD -> multiply, origin/multiply
and follow the line
backwards.
BASH
git switch main
git log --graph --pretty="%h %ad (%cr) %x09 %an : %s"
* 9a267a0 - (origin/main, origin/HEAD, main) Merge pull request #7 from slackline/ns-rse/6-square-root-warning (2025-02-10 14:22:58 +0000) <slackline>
...
* 3f6494f - chore: tidying up prior to trial run (2024-05-02 14:26:33 +0100) <Neil Shephard>
* ba7a5da - bug: ISSUE_TEMPLATES > ISSUE_TEMPLATES (2024-04-24 14:49:45 +0100) <Neil Shephard>
* ae5402f - Merge pull request #3 from ns-rse/multiply (2024-04-23 23:14:14 +0100) <Neil Shephard>
|\
| * b702501 - (HEAD -> multiply, origin/multiply) bug: multiply instead of add arguments (2024-03-26 10:33:37 +0000) <Neil Shephard>
| * 11e36a3 - feat: Adding multiply function and tests (2024-03-26 10:32:42 +0000) <Neil Shephard>
* | 12a159c - Merge pull request #2 from ns-rse/divide (2024-04-23 23:13:13 +0100) <Neil Shephard>
|\ \
| * | 6353fb4 - (origin/divide, divide) bug: Fix tpyo in divide function (2024-03-26 10:28:36 +0000) <Neil Shephard>
| * | 7485e56 - chore: Fix merge conflict (2024-03-26 10:28:11 +0000) <Neil Shephard>
| * | adfef4d - feat: Divide branch (2024-03-25 15:55:15 +0000) <Neil Shephard>
| * | 400896a - Divide branch (2024-03-25 15:55:15 +0000) <Neil Shephard>
| |/
* | f8dcc8d - Merge pull request #1 from ns-rse/ns-rse/initial-setup (2024-03-15 18:04:02 +0000) <Neil Shephard>
|\ \
| * | a4b1bb4 - Min Python version >= 3.10 to align with workflow (2024-03-15 18:01:01 +0000) <Neil Shephard>
| * | 4f5daa2 - Quoting Python versions and adding os matrix to workflow (2024-03-15 17:59:05 +0000) <Neil Shephard>
| * | 74e909b - Issue template for amend and fixup of square_root function (2024-03-08 13:01:42 +0000) <Neil Shephard>
| * | bffc993 - Issue template for amend/fixup on zero-division branch (2024-03-07 17:28:49 +0000) <Neil Shephard>
| * | 44c70e6 - Tidying .pre-commit-config.yaml| (2024-02-02 15:48:50 +0000) <Neil Shephard>
| |/
| * c1f64b0 - Setting up the repository for git-collaboration (2024-02-02 15:48:50 +0000) <Neil Shephard>
|/
* fa76751 - Merge pull request #6 from RSE-Sheffield/ns-rse/5-setup-clean-up (2023-10-19 22:46:14 +0100) <Neil Shephard>
|\
| * c8f0697 - 5 | Removing comment from setup.cfg (2022-10-04 11:12:23 +0100) <Neil Shephard>
* | aff8153 - Merge pull request #7 from RSE-Sheffield/subtract-mistake (2023-01-20 10:07:58 +0000) <bobturneruk>
|\ \
| |/
|/|
| * a45a8dd - introduce mistake in subtract issue (2023-01-20 09:50:03 +0000) <Robert (Bob) Turner>
| * 604a397 - introduce delibarate mistake (2022-12-21 10:29:34 +0000) <Robert (Bob) Turner>
|/
* f06c0ab - Merge pull request #4 from RSE-Sheffield/simplify_deliberate_errors (2022-06-07 14:58:27 +0100) <David Wilby>
|\
...
This is a little more challenging to interpret but reading the output
carefully we have an indicator of where the multiply
branch
is where it reads (HEAD -> multiply, origin/multiply)
.
We can trace that line back to the furthest left branch (which is
main
and origin/main
).
We can see that the multiply
branch diverged from the
fa76751
commit on main
and that three commits
have been made on the multiply
branch.
Working with Branches
The git switch
command is the common method for working
with branches. It allows you to create and switch between branches along
with a few other tasks.
To list the branches that are available you can just type
git branch
or optionally include the --list
option. In the python-maths
repository you have cloned you
should see a number of branches listed. The branch you are currently
checked out on is listed first with an asterisk (*
) at the
start and they are listed alphabetically. Later we will change the
default order to be more informative.
switch
v’s
checkout
git switch
was introduced in Git
v2.23.0 along with git restore
to provide two separate
commands for the functionality that was originally available in
git checkout
. The main reason was to separate the
functionality of git checkout
which could “switch”
branches, including creating branches using the
--branch
/-b
flag, and change (“restore”)
individual files with
git checkout [treeish] -- <filename>
(more on this
later).
Splitting this functionality means that git switch
is
solely for switch
ing branches whilst
git restore
is solely concerned with
restore
ing files but is destructive and we will cover later
the git revert
command as an alternative.
git checkout
has not been deprecated and is still
available and many people still use it as old habits die hard.
Creating Branches
You can create a new branch using
git switch -c <new_branch>
. By default it will use
the branch you currently have checked out as a basis for the new branch.
If you wish to use a different branch as a basis you can do so by
including its name before the name of the new branch.
Starting off up-to-date
Most of the time when creating branches you should do so from the
main
branch. It is therefore important to make sure your
local copy of the main
branch is up-to-date. Before
creating a branch you should switch to the main
branch and
ensure it is up-to-date.
This means you can omit the explicit statement of which branch you
wish to use as the basis for the new branch, typically
main
, when creating it as you will be already be checked
out on that branch when git pull
.
To create a new branch called ns-rse/test
you can use
the following.
Git will use the current HEAD
of the main
branch as a basis for creating the ns-rse/test
branch.
Naming Branches
Branch names can not include spaces, you should use underscores or
dashes instead. You can include some special characters too but I would
avoid using #
as this is the character used by most shells
to indicate a comment and you would therefore have to always
double quote the branch name at the command line.
A useful convention when creating branches is to include some meta
data about who owns the branch and what it is for and to construct the
branch name from your GitHub/GitLab username followed by a
/
. Because you will typically be working on a particular
issue then including the issue number followed by a short few words
which describe the work or issue can make it easy for others (including
your future self) to find out more information about the branch and why
the work has been undertaken. For example GitHub user
ns-rse
working on issue 1 to fix typehints might create a
branch called ns-rse/1-fix-typehints
from main.
This structure is informative as it provides other people you collaborate with or who look at the repository an indication of who created the branch, what issue they are working on and a very short indication of what it is concerned about. With this information it is very easy to look up the relevant Issue.
Challenge 3: Assign Issues, Create Branches and Complete the Tasks
In the python-maths
repository you have cloned and setup
on GitHub there are issue templates.
In your pairs assign the
01 Add zero division exception and test
to one person and
the 02 Add a square root function and test
to the other
person.
Work through the tasks adding the necessary code, saving, staging and
committing your changes then pushing to origin
(GitHub).
The issues contain all the code you need and it can be copy and
pasted into files.
NB Please note the comment about holding back on merging the Square Root work, there will be conflicts that need resolving.
Assign the person who worked on the Square root function to review the Zero Division exception and if everything looks good merge the pull request.
BASH
git switch main
git pull
git switch -c ns-rse/2-square-root
# MAKE EDITS AS DESCRIBED IN THE GITHUB ISSUE
git add -u
git commit -m "Adds square root function"
git push
You should not have created a pull request, if you have you may find that there are conflicts which need resolving. This is by design and we will resolve them later.
Deleting branches
Branches are typically short lived as they are created to address
small focused pieces of work such as fixing a bug or implementing a new
feature before being merged into the main
branch. Over time
you will accrue a number of redundant, out-dated branches and it is
therefore good practice to delete unwanted branches after they have been
merged.
You can not delete a branch you currently have checked out so you
must first checkout an alternative branch. Typically this would be the
main
branch after your Pull Request has been merged and the
changes you were working on have been incorporated. You should
git pull
the main
branch after merging changes
so your local copy is aware of any recent merges from branches you are
about to delete.
Challenge 4: Delete a branch
Create a throw away branch from main
and then delete it
(hint see git branch --help
). You can create a branch with
your username and throwaway
(e.g. ns-rse/throwaway
) with the following.
Pretending the branch you just created has been merged into the
main
branch via a Pull Request delete the now redundant
branch (in this example ns-rse/throwaway
).
Force branch deletion
You were able to delete the branch you created because you hadn’t
made any changes to it. If you have made changes on a branch and they
have not been merged into main
then Git will warn you of
this and refuse to delete the branch. This can be over-ridden with the
--force
flag or the shorthand -D
which is the
same as --delete --force
.
BASH
git switch -c ns-rse/throwaway
touch test_file
git add test_file
git commit -m "Adding test_file"
git switch -
git branch -d ns-rse/throwaway
error: the branch 'ns-rse/throwaway' is not fully merged
hint: If you are sure you want to delete it, run 'git branch -D ns-rse/throwaway'
hint: Disable this message with "git config advice.forceDeleteBranch false"
git -D ns-rse/0-divide
Be very careful when forcing deletion of branches, if you
have not pushed your changes to the remote origin
then you
_will_lose them.
Challenge 5 : Automatically delete branches on GitHub
In your pairs navigate to the Settings page and enable the Automatically delete head branches option.
Hint - Search GitHub documentation for how to automatically delete branches.
This option is on the Settings > General page under the Pull Requests section where you can select “Automatically delete head branches”.
Time Travelling - Losing your HEAD
A branch is a history of commits and you can use git log
to see the commit history (and customise the output so it can be easier
to read), but what if you wanted to look at the state of the branch at a
previous point in time? Well because Git has kept track of everything
you can do that and the command to do so is git checkout
which takes a “reference” as an argument. So far you have been using
branch names as references but commit hashes are also references and so
can be used to checkout the state of the repository in the past.
main
branch showing the position of HEAD
.Here we have a simple linear history and the HEAD
of
branch is on commit 8-a80cef8
If you want to checkout
commit 4-8ec389a
then you would
git checkout 4-8ec389a
and you will see the following
useful and informative warning message.
BASH
git checkout 4-8ec389a
Note: switching to 4-8ec389a'.
You are in 'detached HEAD' state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
state without impacting any branches by switching back to a branch.
If you want to create a new branch to retain commits you create, you may
do so (now or later) by using -c with the switch command. Example:
git switch -c <new-branch-name>
Or undo this operation with:
git switch -
Turn off this advice by setting config variable advice.detachedHead to false
HEAD is now at 4-8ec389a complete subtract issue template
Have you lost your head because it is now detached
? No,
HEAD
is just a special reference that points to a specific
commit (tags are the same) and it is a short hand way of referring to a
commit, what has happened is that Git has moved the commit
HEAD
points to from 8-a40cef8
to
4-8ec389a
. If you make changes to this branch they will be
lost when you switch back to the 8-a40cef8
commit and you
are told you can do this with git switch -
. If you want to
make changes and save them you are advised to create a new branch to do
so.
Work in Progress
If you try to git checkout
whilst you have unstaged
changes you are likely to hit an error about changes being
overwritten.
BASH
error: Your local changes to the following files would be overwritten by checkout:
README.md
Please commit your changes or stash them before you switch branches.
Aborting
This is to be expected, here the README.md
file has
changed but those changes have not been staged and committed and if we
checkout another commit or branch the state of the
README.md
will be reverted. Without committing or stashing
the changes as advised Git would not be able to revert back to the
changes you have made.
We’re going to cover switching branches whilst working later on.
Challenge 6: Checkout old commits
- Look at the history of the
python-maths
repository and find out who the author of commit585287a
was (there are several ways of doing this). - Checkout this commit and look at the contents of the file
tests/test_add.py
(you can usecat tests/test_add.py
). - Switch back to
HEAD
, has anything changed in thetests/test_add.py
file?
You could query the logs for the specific commit and it will show you the history from there.
BASH
git log 585287a
commit 585287aec5d94b10d03ae8fd35ad0423b2c1b0db (HEAD)
Author: Anna Krystalli <annakrystalli@googlemail.com>
Date: 2021-05-14 12:38:09 +0300
add test and CI
Alternatively you can checkout the specific hash and use
git log
to look at who made the commit.
BASH
git checkout 585287a
git log
commit 585287aec5d94b10d03ae8fd35ad0423b2c1b0db (HEAD)
Author: Anna Krystalli <annakrystalli@googlemail.com>
Date: 2021-05-14 12:38:09 +0300
add test and CI
We can see it was made by
Anna Krystalli <annakrystalli@googlemail.com>
almost
four years ago on 2021-05-14 12:38:09 +0300
.
BASH
cat tests/test_add.py
import src.python_calculator.add as add
def test_add():
assert add.add(1, 3) == 4
git switch -
cat tests/test_add.py
cat: tests/test_add.py: No such file or directory
The file tests/test_add.py
has an import statement and
defines the test_add()
function which checks if the
add.add()
function returns the value of 4 when given the
numbers 1 and 3.
The tests/test_add.py
file no longer exists on the
HEAD
of the main
branch!
Checkout Anything
You are not restricted to switch to commits on the same branch you are currently on. You can checkout any commit in the history as long as you know the commit hash.
Comparing References
This is quite a convoluted way of comparing branches though and in this instance the difference is quite simple the file no longer exists, but imagine you wanted to compare a file between branches or commits without having to switch branches and try and hold in your head what the file looked like on one branch whilst you look at the other. That would probably be very challenging.
Fortunately Git can help you here with git diff
. This
takes one or two arguments, which are commits or references that you
want to compare. If only one argument is given it compares the currently
checked out commit to the supplied commit/reference.
Thus to compare the HEAD
of the divide
branch you would
BASH
git switch ns-rse/1-zero-divide-exception
git diff 585287a
# Equivalent to...
git diff HEAD 585287a
If you want to limit this to a specific file use the
-- <file1> <file2>
option.
Ooops! I Did It Again
Nothing to do with Britney Spears but you are at some stage likely to
commit changes to the wrong branch. This can easily happen when starting
to work on an issue without first creating a new branch to contain the
work and you commit the changes to either the main
branch,
which is often protected so you won’t be able to push your changes, or
the last branch you were working on.
git reset
One way to solve this with Git is to git reset
the
branch to which you have just mistakenly made the commit. This removes
reference to the changes from the Git history but leaves the changes to
the files in place and they appear as unstaged
files. It is
ideal if you have only one commit you wish to undo.
Relative Refs
Normally you are working on the HEAD
of a branch which
is the most recent commit that has been made along with any staged, but
uncommitted changes. Git has a simple way of referring to previous
commits relative to HEAD
using the ~
(tilde)
character and counting backwards.
If you want to undo the last commit then you can do this
using git reset HEAD~1
.
reset
options
There are three options to git reset
that influence how
the changes in commits are handled, these are --soft
,
--mixed
(the default) and --hard
.
For a detailed exposition of git reset
see the excellent
Atlassian
| Git reset article.
Challenge 7: Commits on the wrong branch
- Switch to the
main
branch of thepytest-maths
repository. - Create a new file using
echo "# This is TODO list" > TODO.md
- Stage and commit the file to the
main
branch of your repository. NB to do this you will have to disable thepre-commit
checks with the-n
flag.
Ooops you’ve just committed to the main
branch which is
protected so you can’t push your changes. Now move the commit to a new
branch so you can push them.
- Reset the change.
- Create a new branch called
<github_user>/todo
. - Stage and commit the file to
<github_user>/todo
.
You can git reset --mixed
to HEAD~1
,
i.e. the previous commit, which removes the CONTRIBUTING.md
file from the commit history, leaving it unstaged, then create a new
branch and add it to that.
Alternatively you can checkout the previous commit before
you added the file by mistake, create the
<github_user>/contributing
branch, and
git cherrypick
the commit from main
which
contains the CONTRIBUTING.md
file and then remove
the commit from main.
BASH
git switch main
echo "# This is a TODO list" > TODO.md
git add TODO.md
git commit -n -m "docs: Adding a todo file"
git log # Note the commit of the mistaken hash
git checkout HEAD~1 # Checkout the previous commit on the main branch
git switch -c ns-rse/todo
git cherrypick <hash>
git switch main
git reset --hard HEAD~1
git revert
git reset
is destructive, you can lose work using it and
it is advisable not to use it when you have more than one
commit you wish to undo as you lose the intermediary work between
commits as you are restored to the commit you reset to. Fortunately Git
has the revert
option which is a non-destructive approach
to undoing changes in your Git history. Instead it takes a specified
commit and inverts the changes, i.e. goes back to the previous state and
rather than discarding the changes it makes a new “revert” commit to
record the inversion and this new “revert” commit becomes the
HEAD
of the branch. git revert
has to
have a reference in order to work, whether that is absolute (i.e. a
hash) or relative.
reset v’s revert
The differences between reset
and revert
is
that one (reset
) is destructive and loses changes the other
(revert
) undoes the changes and makes a new commit
recording these changes.
Be very careful when using reset
, if you have
not pushed your changes to the remote origin
then you will
lose them.
Switching Branches during Work in Progress
Sometimes you will be doing some work and a colleague will ask you to review a pull request or help them with a problem they have on their branch. When performing pull request reviews it can be quite common to run tests to check everything passes if you don’t have Continuous Integration doing this automatically for you (we will come to that in another episode).
But there is a challenge, in order to switch branches you have to stage and commit all changes to tracked files.
BASH
git switch -c branch2
echo "Please feel free to contribute to this repository" >> CONTRIBUTING.md
git add CONTRIBUTING.md
git commit -m "Adding CONTRIBUTING.md"
echo "\nPlease don't break my repository though!" >> CONTRIBUTING.md
git switch main
error: Your local changes to the following files would be overwritten by checkout:
CONTRIBUTING.md
Please commit your changes or stash them before you switch branches.
Aborting
Whilst you could commit your changes and subsequently
git commit --amend
(more on this in the next episode) there
is another option.
git stash
git stash
allows you to save your current changes in a
temporary location and then reverts to the last commit
(HEAD
). This allows you to move
aboutgit switch
to other branches and undertake work. There
are lots of options to git stash
but the basics are pretty
straight-forward. You start by git stash push
(the
push
is actually optional) and you can include a
--message
that explains what the stash contains, you are
told if this has worked and on what branch the stash was made and can
then switch branches, pull down changes, create a new branch and do
something different.
3. Return to branch2
When you have finished this other work you can return to
branch2
and pop
the stash back. To see what
stashes there are you can use git stash list
4. pop
the last stash
When you are ready to restore the work you can do so using
git stash pop
which by default will restore the
last stash.
BASH
git stash pop
On branch branch2
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: CONTRIBUTING.md
no changes added to commit (use "git add" and/or "git commit -a")
Dropped refs/stash@{0} (13c8c6fb23f9fcdd884b4528356db37527c9b3e4)
The changes to CONTRIBUTING.md
and the corresponding
entry are removed from the stash list.
Multiple Stashes
Over time though you may collect multiple stashes.
1. Make two stashes
We stash CONTRIBUTING.md
, the last message is reused by
default, then we add ANOTHER.md
and stash it with a
different message.
2. Pop the CONTRIBUTING.md
stash
There are now two stashes each with different names.
You may not want to restore the work stashed with the commit message
WIP ANOTHER.md file
but rather restore the earlier
WIP CONTRIBUTING.md file
work first. You can do this by
referring to the number associated with the stash that is within the
curly braces. For the WIP CONTRIBUTING.md file
stash this
is 1
.
BASH
git stash list
git stash pop 1
On branch branch2
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: CONTRIBUTING.md
no changes added to commit (use "git add" and/or "git commit -a")
Dropped refs/stash@{1} (dd538beb8f14590f720e9b9f677ba7381240bd92)
Only the CONTRIBUTING.md
file has been restored and not
the ANOTHER.md
and there is only one stash left.
BASH
git stash list
stash@{0}: On example-stashes: WIP ANOTHER.md file
ls -lha CONTRIBUTING.md ANOTHER.md
ANOTHER.md: No such file or directory (os error 2).
.rw-r--r-- neil neil 0 B Thu Feb 13 13:36:29 2025 CONTRIBUTING.md
Challenge 7: Stashing
Working in your pairs on the python-maths
repository…
- Create a
<github-username>/todo
branch. - Create a
TODO.md
withecho "# TODO Tasks." > TODO.md
- Do not add and commit, instead
git stash push -m "Adding a TODO file"
your changes - Switch to the
main
branch and create a<github-username>/citation
branch. - Add a basic
CITATION.cff
withecho "cff-version: 1.2.0\ntitle: Python \ntype: software" > CITATION.cff
- Add and commit this file.
- Unstash the
TODO.md
file on thecitation
branch. - Stage and commit the changes. Do NOT create a pull request or merge these changes.
- Delete the branches locally (try and avoid any messages telling you
there are unmerged changes, see
git branch --help
on how to force deletion of unmerged branches).
Lets create the <github-username>/todo
branch
BASH
git switch main
git pull
git switch -c <github-username>/todo
echo "# This is a ToDo List\n\n1. Task1\n2.Task2." > TODO.md
If we want to switch branches without making a commit but save our
work in progress we stash the work and switch to main
and
before creating a new branch
(<github-username>/citation
) for adding a
CITATION.cff
file.
BASH
git stash -m "WIP : adding TODO.md"
git switch main
git switch -c <github-username>/citation
echo "cff-version: 1.2.0\ntitle: Python Maths Package\ntype: software" > CITATION.cff
git add CITATION.cff
git commit -m "doc: Adding a CITATION.cff"
We now unstash the WIP : adding TODO.md
stash to this
branch and commit the changes, amending the commit and push to
GitHub.
You can now make a pull request to merge these changes, assign it and have it reviewed and merged.
Popping around and applying
You can git stash apply
to pop
a stash but
leave it in the stash list. This might be useful if you want to apply
the stashed changes to several branches you are working on.
There are a lot of useful things git stash
can be used
for. Refer to the help pages (git stash --help
) for more
information as well as the Further Resources.
References - a revelation
Whilst we have focused on consolidating our understanding of branches in this introductory episode there have been hints as to the true nature of branches in Git, have you worked out what this is yet?
Internally Git does not have branches at all! Branches are merely a reference to a series of commits and each commit in a “branch” references the commit prior to it. In fact everything in Git that allows us to look at the different states of the repository and move between them is a reference, whether that is a named branch, or a tag which is a relative reference. They all point to a commit.
This was a revelation that came to me as I wrote the material for this Episode and thought it worth sharing.
Key Points
- Branches and how they relate to each other are fundamental to collaborating using Git.
- The history of a branch is a series of commits and extends all the way back to the very first commit and not the point at which it forked from its parents.
- Branches can be easily created, merged and deleted.
- Commits all have references and Git can move you between these
references using
git commit
or compare them usinggit diff
.