2. Fixing mistakes#

We’re still in our git working directory:

import os

try:
    from google.colab import drive  # type: ignore

    drive.mount("/content/drive")
    drive_dir = "/content/drive/MyDrive"
except ImportError:
    print("Not running on colab")
    drive_dir = os.path.join(os.getcwd(), "drive", "MyDrive")
    os.makedirs(drive_dir, exist_ok=True)

print(f"Drive dir: {drive_dir}")

git_example_dir = os.path.join(drive_dir, "learning_git", "git_example")

if os.path.exists(git_example_dir):
    print(f"Git example directory: {git_example_dir}")
    os.chdir(git_example_dir)
else:
    print("Start from the beginning")
Not running on colab
Drive dir: /mnt/nvme1n1p2/home/yj.lee/workspace/projects/lecture/book/lectures/softeng/vcs/drive/MyDrive
Git example directory: /mnt/nvme1n1p2/home/yj.lee/workspace/projects/lecture/book/lectures/softeng/vcs/drive/MyDrive/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.

%%bash
git revert HEAD~
Auto-merging test.md
[main 9793609] Revert "Add a lie about a mountain"
 Date: Sun Sep 17 20:16:18 2023 +0900
 1 file changed, 2 deletions(-)

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.

Review of changes#

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

%%bash
git log --date=short
commit 97936099145d29e1067f42a856e9aa3a41c7b50f
Author: Young Joon Lee <entelecheia@hotmail.com>
Date:   2023-09-17

    Revert "Add a lie about a mountain"
    
    This reverts commit 453f5bcf1cfe7616ded061ca2b1cf1e910351824.

commit 6626d56233bdf14ea2f9741588523c2a6d5c483d
Author: Young Joon Lee <entelecheia@hotmail.com>
Date:   2023-09-17

    Change title

commit 453f5bcf1cfe7616ded061ca2b1cf1e910351824
Author: Young Joon Lee <entelecheia@hotmail.com>
Date:   2023-09-17

    Add a lie about a mountain

commit 74efc42f190870df59f42e1013c8bcb766d119b9
Author: Young Joon Lee <entelecheia@hotmail.com>
Date:   2023-09-17

    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#

%%writefile test.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 test.md
%%bash
cat test.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.
%%bash
git diff
diff --git a/test.md b/test.md
index b4befef..e4bb8ea 100644
--- a/test.md
+++ b/test.md
@@ -1,4 +1,5 @@
 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.
+Engerland is not very mountainous.
+But has some tall hills, and maybe a
+mountain or two depending on your definition.
%%bash
git commit -am "Add a silly spelling"
[main 8aa13df] Add a silly spelling
 1 file changed, 3 insertions(+), 2 deletions(-)
%%bash
git log --date=short
commit 8aa13df1fa53b03617c8b86752d442068c0ce56a
Author: Young Joon Lee <entelecheia@hotmail.com>
Date:   2023-09-17

    Add a silly spelling

commit 97936099145d29e1067f42a856e9aa3a41c7b50f
Author: Young Joon Lee <entelecheia@hotmail.com>
Date:   2023-09-17

    Revert "Add a lie about a mountain"
    
    This reverts commit 453f5bcf1cfe7616ded061ca2b1cf1e910351824.

commit 6626d56233bdf14ea2f9741588523c2a6d5c483d
Author: Young Joon Lee <entelecheia@hotmail.com>
Date:   2023-09-17

    Change title

commit 453f5bcf1cfe7616ded061ca2b1cf1e910351824
Author: Young Joon Lee <entelecheia@hotmail.com>
Date:   2023-09-17

    Add a lie about a mountain

commit 74efc42f190870df59f42e1013c8bcb766d119b9
Author: Young Joon Lee <entelecheia@hotmail.com>
Date:   2023-09-17

    First commit of discourse on UK topography

Using reset to rewrite history#

%%bash
git reset HEAD~
Unstaged changes after reset:
M	test.md
%%bash
git log --date=short
commit 97936099145d29e1067f42a856e9aa3a41c7b50f
Author: Young Joon Lee <entelecheia@hotmail.com>
Date:   2023-09-17

    Revert "Add a lie about a mountain"
    
    This reverts commit 453f5bcf1cfe7616ded061ca2b1cf1e910351824.

commit 6626d56233bdf14ea2f9741588523c2a6d5c483d
Author: Young Joon Lee <entelecheia@hotmail.com>
Date:   2023-09-17

    Change title

commit 453f5bcf1cfe7616ded061ca2b1cf1e910351824
Author: Young Joon Lee <entelecheia@hotmail.com>
Date:   2023-09-17

    Add a lie about a mountain

commit 74efc42f190870df59f42e1013c8bcb766d119b9
Author: Young Joon Lee <entelecheia@hotmail.com>
Date:   2023-09-17

    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.

%%bash
cat test.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:

%%bash
git checkout test.md
Updated 1 path from the index
%%bash
cat test.md
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.

We can add this to our diagram:

sequenceDiagram participant WD as Working Directory participant SA as Staging Area participant LR as Local Repository Note over WD: git add WD->>SA: Stage changes Note over SA: git commit SA->>LR: Commit staged changes Note over WD: git commit -a WD->>LR: Stage and commit changes Note over LR: git checkout LR->>WD: Update to specified state Note over LR: git reset LR->>SA: Reset staging area to commit Note over LR: git reset --hard LR->>WD: Reset to commit & update files

We can add it to Jim’s story:

sequenceDiagram participant R as Jim's repo participant I as Jim's index participant J as Jim Note over J: git revert HEAD~ J->>R: Add new commit reversing change R->>I: Update staging area to reverted version I->>J: Update file to reverted version Note over J: vim test.md Note over J: git commit -am "Add another mistake" J->>I: Add mistake I->>R: Add mistake Note over J: git reset HEAD~ J->>R: Delete mistaken commit R->>I: Update staging area to reset commit Note over J: git checkout test.md R->>J: Update file to reverted version