XClose

Research Software Engineering Summer School

Home
Menu

Git miscellany

Git Stash

NOTE: using bash/git commands is not fully supported on jupyterlite yet (due to single thread/process restriction), and the cells below might error out on the browser (jupyterlite) version of this notebook

Before you can git pull, you need to have committed any changes you have made. If you find you want to pull, but you're not ready to commit, you have to temporarily "put aside" your uncommitted changes. For this, you can use the git stash command, like in the following example:

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]:
%%writefile Wales.md
Mountains In Wales
==================

* Pen y Fan
* Tryfan
* Snowdon
* Glyder Fawr
* Fan y Big
* Cadair Idris
Overwriting Wales.md
In [3]:
%%bash
git stash
git pull
No local changes to save
git@github.com: Permission denied (publickey).
fatal: Could not read from remote repository.

Please make sure you have the correct access rights
and the repository exists.
---------------------------------------------------------------------------
CalledProcessError                        Traceback (most recent call last)
Cell In[3], line 1
----> 1 get_ipython().run_cell_magic('bash', '', 'git stash\ngit pull\n')

File /opt/hostedtoolcache/Python/3.12.8/x64/lib/python3.12/site-packages/IPython/core/interactiveshell.py:2541, in InteractiveShell.run_cell_magic(self, magic_name, line, cell)
   2539 with self.builtin_trap:
   2540     args = (magic_arg_s, cell)
-> 2541     result = fn(*args, **kwargs)
   2543 # The code below prevents the output from being displayed
   2544 # when using magics with decorator @output_can_be_silenced
   2545 # when the last Python token in the expression is a ';'.
   2546 if getattr(fn, magic.MAGIC_OUTPUT_CAN_BE_SILENCED, False):

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

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

CalledProcessError: Command 'b'git stash\ngit pull\n'' returned non-zero exit status 1.

By stashing your work first, your repository becomes clean, allowing you to pull. To restore your changes, use git stash apply.

In [4]:
%%bash --no-raise-error
git stash apply
No stash entries found.

The "Stash" is a way of temporarily saving your working area, and can help out in a pinch.

Tagging

Tags are easy to read labels for revisions, and can be used anywhere we would name a commit.

Produce real results only with tagged revisions

In [5]:
%%bash
git tag -a v1.0 -m "Release 1.0"
git push origin --delete v1.0  # clear the tag if it already exists on the remote origin
git push --tags
git@github.com: Permission denied (publickey).
fatal: Could not read from remote repository.

Please make sure you have the correct access rights
and the repository exists.
git@github.com: Permission denied (publickey).
fatal: Could not read from remote repository.

Please make sure you have the correct access rights
and the repository exists.
---------------------------------------------------------------------------
CalledProcessError                        Traceback (most recent call last)
Cell In[5], line 1
----> 1 get_ipython().run_cell_magic('bash', '', 'git tag -a v1.0 -m "Release 1.0"\ngit push origin --delete v1.0  # clear the tag if it already exists on the remote origin\ngit push --tags\n')

File /opt/hostedtoolcache/Python/3.12.8/x64/lib/python3.12/site-packages/IPython/core/interactiveshell.py:2541, in InteractiveShell.run_cell_magic(self, magic_name, line, cell)
   2539 with self.builtin_trap:
   2540     args = (magic_arg_s, cell)
-> 2541     result = fn(*args, **kwargs)
   2543 # The code below prevents the output from being displayed
   2544 # when using magics with decorator @output_can_be_silenced
   2545 # when the last Python token in the expression is a ';'.
   2546 if getattr(fn, magic.MAGIC_OUTPUT_CAN_BE_SILENCED, False):

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

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

CalledProcessError: Command 'b'git tag -a v1.0 -m "Release 1.0"\ngit push origin --delete v1.0  # clear the tag if it already exists on the remote origin\ngit push --tags\n'' returned non-zero exit status 128.
In [6]:
%%writefile Pennines.md

Mountains In the Pennines
========================

* Cross Fell
* Ingleborough
Writing Pennines.md
In [7]:
%%bash
git add Pennines.md
git commit -m "Add Pennines"
[main bb6c0f5] Add Pennines
 1 file changed, 6 insertions(+)
 create mode 100644 Pennines.md

You can also use tag names in the place of commmit hashes, such as to list the history between particular commits:

In [8]:
%%bash
git log v1.0.. --graph --oneline
* bb6c0f5 Add Pennines

If .. is used without a following commit name, HEAD is assumed.

Working with generated files: gitignore

We often end up with files that are generated by our program. It is bad practice to keep these in Git; just keep the sources.

Examples include .o and .x files for compiled languages, .pyc files in Python.

In our example, we might want to make our .md files into a PDF with pandoc:

In [9]:
%%writefile Makefile

MDS=$(wildcard *.md)
PDFS=$(MDS:.md=.pdf)

default: $(PDFS)

%.pdf: %.md
	pandoc $< -o $@
Writing Makefile
In [10]:
%%bash
make
make[1]: Entering directory '/home/runner/work/rsd-summerschool/rsd-summerschool/ch00git/learning_git/git_example'
pandoc Pennines.md -o Pennines.pdf
pandoc Scotland.md -o Scotland.pdf
pandoc Wales.md -o Wales.pdf
make[1]: Leaving directory '/home/runner/work/rsd-summerschool/rsd-summerschool/ch00git/learning_git/git_example'

We now have a bunch of output .pdf files corresponding to each Markdown file.

But we don't want those to show up in git:

In [11]:
%%bash
git status
On branch main
Untracked files:
  (use "git add <file>..." to include in what will be committed)
	Makefile
	Pennines.pdf
	Scotland.pdf
	Wales.pdf

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

Use .gitignore files to tell Git not to pay attention to files with certain paths:

In [12]:
%%writefile .gitignore
*.pdf
Writing .gitignore
In [13]:
%%bash
git status
On branch main
Untracked files:
  (use "git add <file>..." to include in what will be committed)
	.gitignore
	Makefile

nothing added to commit but untracked files present (use "git add" to track)
In [14]:
%%bash
git add Makefile
git add .gitignore
git commit -m "Add a makefile and ignore generated files"
git push
[main 434eaac] Add a makefile and ignore generated files
 2 files changed, 9 insertions(+)
 create mode 100644 .gitignore
 create mode 100644 Makefile
fatal: The current branch main has no upstream branch.
To push the current branch and set the remote as upstream, use

    git push --set-upstream origin main

To have this happen automatically for branches without a tracking
upstream, see 'push.autoSetupRemote' in 'git help config'.

---------------------------------------------------------------------------
CalledProcessError                        Traceback (most recent call last)
Cell In[14], line 1
----> 1 get_ipython().run_cell_magic('bash', '', 'git add Makefile\ngit add .gitignore\ngit commit -m "Add a makefile and ignore generated files"\ngit push\n')

File /opt/hostedtoolcache/Python/3.12.8/x64/lib/python3.12/site-packages/IPython/core/interactiveshell.py:2541, in InteractiveShell.run_cell_magic(self, magic_name, line, cell)
   2539 with self.builtin_trap:
   2540     args = (magic_arg_s, cell)
-> 2541     result = fn(*args, **kwargs)
   2543 # The code below prevents the output from being displayed
   2544 # when using magics with decorator @output_can_be_silenced
   2545 # when the last Python token in the expression is a ';'.
   2546 if getattr(fn, magic.MAGIC_OUTPUT_CAN_BE_SILENCED, False):

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

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

CalledProcessError: Command 'b'git add Makefile\ngit add .gitignore\ngit commit -m "Add a makefile and ignore generated files"\ngit push\n'' returned non-zero exit status 128.

Git clean

Sometimes you end up creating various files that you do not want to include in version control. An easy way of deleting them (if that is what you want) is the git clean command, which will remove the files that git is not tracking.

In [15]:
%%bash
git clean -fX
Removing Pennines.pdf
Removing Scotland.pdf
Removing Wales.pdf
In [16]:
%%bash
ls
Makefile
Pennines.md
Scotland.md
Wales.md
  • with -f: don't prompt
  • with -d: remove directories
  • with -x: Also remote .gitignored files
  • with -X: Only remove .gitignore files

Git Hunks

A "Hunk" is one git change. This changeset has three hunks:

+import matplotlib
+import numpy as np

 from matplotlib import pylab
 from matplotlib.backends.backend_pdf import PdfPages

+def increment_or_add(key,hash,weight=1):
+       if key not in hash:
+               hash[key]=0
+       hash[key]+=weight
+
 data_path=os.path.join(os.path.dirname(
                        os.path.abspath(__file__)),
-regenerate=False
+regenerate=True

Interactive add

git add and git reset can be used to stage/unstage a whole file, but you can use interactive mode to stage by hunk, choosing yes or no for each hunk.

git add -p myfile.py
+import matplotlib
+import numpy as np
#Stage this hunk [y,n,a,d,/,j,J,g,e,?]?

GitHub pages

Yaml Frontmatter

GitHub will publish repositories containing markdown as web pages, automatically.

You'll need to add this content:

   ---
   ---

A pair of lines with three dashes, to the top of each markdown file. This is how GitHub knows which markdown files to make into web pages. Here's why for the curious.

In [17]:
%%writefile index.md
---
title: Github Pages Example
---
Mountains and Lakes in the UK
===================

Engerland is not very mountainous.
But has some tall hills, and maybe a mountain or two depending on your definition.
Writing index.md
In [18]:
%%bash
git add index.md
git commit -m "Add github pages YAML frontmatter"
[main 0ca8e25] Add github pages YAML frontmatter
 1 file changed, 8 insertions(+)
 create mode 100644 index.md

The gh-pages branch

GitHub creates github pages when you use a special named branch.

This is best used to create documentation for a program you write, but you can use it for anything.

In [19]:
os.chdir(working_dir)
In [20]:
%%bash

git switch -c gh-pages
git push -uf origin gh-pages
Switched to a new branch 'gh-pages'
git@github.com: Permission denied (publickey).
fatal: Could not read from remote repository.

Please make sure you have the correct access rights
and the repository exists.
---------------------------------------------------------------------------
CalledProcessError                        Traceback (most recent call last)
Cell In[20], line 1
----> 1 get_ipython().run_cell_magic('bash', '', '\ngit switch -c gh-pages\ngit push -uf origin gh-pages\n')

File /opt/hostedtoolcache/Python/3.12.8/x64/lib/python3.12/site-packages/IPython/core/interactiveshell.py:2541, in InteractiveShell.run_cell_magic(self, magic_name, line, cell)
   2539 with self.builtin_trap:
   2540     args = (magic_arg_s, cell)
-> 2541     result = fn(*args, **kwargs)
   2543 # The code below prevents the output from being displayed
   2544 # when using magics with decorator @output_can_be_silenced
   2545 # when the last Python token in the expression is a ';'.
   2546 if getattr(fn, magic.MAGIC_OUTPUT_CAN_BE_SILENCED, False):

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

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

CalledProcessError: Command 'b'\ngit switch -c gh-pages\ngit push -uf origin gh-pages\n'' returned non-zero exit status 128.

The first time you do this, GitHub takes a few minutes to generate your pages.

The website will appear at http://username.github.io/repositoryname, for example:

http://UCL.github.io/github-example/

UCL layout for GitHub pages

You can use GitHub pages to make HTML layouts, here's an example of how to do it, and how it looks. We won't go into the detail of this now, but after the class, you might want to try this.