XClose

COMP0233: Research Software Engineering With Python

Home
Menu

Fixing mistakes

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

We're still in our git working directory:

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)
working_dir
Out[1]:
'/home/runner/work/rsd-engineeringcourse/rsd-engineeringcourse/ch00git/learning_git/git_example'

Referring to changes with HEAD and ^

The commit we want to revert to is the one before the latest.

HEAD refers to the latest commit. That is, we want to go back to the change before the current HEAD.

We could use the hash code (e.g. 73fbeaf) to reference this, but you can also refer to the commit before the HEAD as HEAD^, the one before that as HEAD^^, the one before that as HEAD~3.

Reverting

Ok, so now we'd like to undo the nasty commit with the lie about Mount Fictional.

In [2]:
%%bash
git revert HEAD^
CONFLICT (modify/delete): index.md deleted in (empty tree) and modified in HEAD.  Version HEAD of index.md left in tree.
error: could not revert aae0ecc... First commit of discourse on UK topography
hint: After resolving the conflicts, mark them with
hint: "git add/rm <pathspec>", then run
hint: "git revert --continue".
hint: You can instead skip this commit with "git revert --skip".
hint: To abort and get back to the state before "git revert",
hint: run "git revert --abort".
hint: Disable this message with "git config advice.mergeConflict false"
---------------------------------------------------------------------------
CalledProcessError                        Traceback (most recent call last)
Cell In[2], line 1
----> 1 get_ipython().run_cell_magic('bash', '', 'git revert HEAD^\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 revert HEAD^\n'' returned non-zero exit status 1.

An editor may pop up, with some default text which you can accept and save.

Conflicted reverts

You may, depending on the changes you've tried to make, get an error message here.

If this happens, it is because git could not automagically decide how to combine the change you made after the change you want to revert, with the attempt to revert the change: this could happen, for example, if they both touch the same line.

If that happens, you need to manually edit the file to fix the problem. Skip ahead to the section on resolving conflicts, or ask a demonstrator to help.

Review of changes

The file should now contain the change to the title, but not the extra line with the lie. Note the log:

In [3]:
%%bash
git log --date=short
commit bb375a00b9b5d28b6fa8a3fa7011021460d9a0de
Author: Lancelot the Brave <l.brave@spamalot.uk>
Date:   2024-12-20

    Add a lie about a mountain

commit aae0ecc21cb78b895f3702266d90d8cc3f6be68f
Author: Lancelot the Brave <l.brave@spamalot.uk>
Date:   2024-12-20

    First commit of discourse on UK topography

Antipatch

Notice how the mistake has stayed in the history.

There is a new commit which undoes the change: this is colloquially called an "antipatch". This is nice: you have a record of the full story, including the mistake and its correction.

Rewriting history

It is possible, in git, to remove the most recent change altogether, "rewriting history". Let's make another bad change, and see how to do this.

A new lie

In [4]:
%%writefile index.md
Mountains and Hills in the UK   
===================   
Engerland is not very mountainous.   
But has some tall hills, and maybe a
mountain or two depending on your definition.
Overwriting index.md
In [5]:
%%bash
cat index.md
Mountains and Hills in the UK   
===================   
Engerland is not very mountainous.   
But has some tall hills, and maybe a
mountain or two depending on your definition.
In [6]:
%%bash
git diff
* Unmerged path index.md
diff --git a/index.md b/index.md
index 3a2f7b0..4801c98 100644
--- a/index.md
+++ b/index.md
@@ -1,6 +1,5 @@
-Mountains in the UK   
+Mountains and Hills in the UK   
 ===================   
-England is not very mountainous.   
-But has some tall hills, and maybe a mountain or two depending on your definition.
-
-Mount Fictional, in Barsetshire, U.K. is the tallest mountain in the world.
+Engerland is not very mountainous.   
+But has some tall hills, and maybe a
+mountain or two depending on your definition.
In [7]:
%%bash
git add index.md
git commit -m "Add a silly spelling"
[main b0dd920] Add a silly spelling
 1 file changed, 4 insertions(+), 5 deletions(-)
In [8]:
%%bash
git log --date=short
commit b0dd920826fa838ba3080aba942447b6ab3cecf1
Author: Lancelot the Brave <l.brave@spamalot.uk>
Date:   2024-12-20

    Add a silly spelling

commit bb375a00b9b5d28b6fa8a3fa7011021460d9a0de
Author: Lancelot the Brave <l.brave@spamalot.uk>
Date:   2024-12-20

    Add a lie about a mountain

commit aae0ecc21cb78b895f3702266d90d8cc3f6be68f
Author: Lancelot the Brave <l.brave@spamalot.uk>
Date:   2024-12-20

    First commit of discourse on UK topography

Using reset to rewrite history

In [9]:
%%bash
git reset HEAD^
Unstaged changes after reset:
M	index.md
In [10]:
%%bash
git log --date=short
commit bb375a00b9b5d28b6fa8a3fa7011021460d9a0de
Author: Lancelot the Brave <l.brave@spamalot.uk>
Date:   2024-12-20

    Add a lie about a mountain

commit aae0ecc21cb78b895f3702266d90d8cc3f6be68f
Author: Lancelot the Brave <l.brave@spamalot.uk>
Date:   2024-12-20

    First commit of discourse on UK topography

Covering your tracks

The silly spelling is no longer in the log. This approach to fixing mistakes, "rewriting history" with reset, instead of adding an antipatch with revert, is dangerous, and we don't recommend it. But you may want to do it for small silly mistakes, such as to correct a commit message.

Resetting the working area

When git reset removes commits, it leaves your working directory unchanged -- so you can keep the work in the bad change if you want.

In [11]:
%%bash
cat index.md
Mountains and Hills in the UK   
===================   
Engerland is not very mountainous.   
But has some tall hills, and maybe a
mountain or two depending on your definition.

If you want to lose the change from the working directory as well, you can do git reset --hard.

I'm going to get rid of the silly spelling, and I didn't do --hard, so I'll reset the file from the working directory to be the same as in the index:

In [12]:
%%bash
git checkout index.md
Updated 1 path from the index
In [13]:
%%bash
cat index.md
Mountains in the UK   
===================   
England is not very mountainous.   
But has some tall hills, and maybe a mountain or two depending on your definition.

Mount Fictional, in Barsetshire, U.K. is the tallest mountain in the world.

We can add this to our diagram:

In [14]:
message="""
Working Directory -> Staging Area : git add
Staging Area -> Local Repository : git commit
Working Directory -> Local Repository : git commit -a
Staging Area -> Working Directory : git checkout
Local Repository -> Staging Area : git reset
Local Repository -> Working Directory: git reset --hard
"""
from wsd import wsd
%matplotlib inline
wsd(message)
Out[14]:
No description has been provided for this image

We can add it to Cleese's story:

In [15]:
message="""
participant "Cleese's repo" as R
participant "Cleese's index" as I
participant Cleese as C

note right of C: git revert HEAD^

C->R: Add new commit reversing change
R->I: update staging area to reverted version
I->C: update file to reverted version



note right of C: vim index.md
note right of C: git add index.md
note right of C: git commit -m "Add another mistake"
C->I: Add mistake
I->R: Add mistake

note right of C: git reset HEAD^

C->R: Delete mistaken commit
R->I: Update staging area to reset commit

note right of C: git checkout index.md

I->C: Update file to reverted version


"""
wsd(message)
Out[15]:
No description has been provided for this image