Case study: roll back and bug fix#

This case study assumes you have worked through case study 1 and have a the following project that is a git repo.

analysis_code
├── main.py
├── readme.md
├── run_1.log
└── run_2.log

Issuing the git log --oneline command you should get the following history.

Remember that your commit hash values will be unique to your repo.

6475df6 DOCS: run instructions
186d91b SETUP: .gitignore + *.log
d404939 INIT: add main.py

Scenario#

You have written an analysis program. This code executes each night on critical patient level data. At a recent meeting a senior researcher requested a new subgroup analysis in the code. You know how to do this and quickly make the change to the data. Unfortunately your change introduces an unexpected bug into the main analysis. Given the importance of the code, it is necessary to roll back to the previous version that works with no problems while a fix is found.

To simulate this, you are going to commit a change to an existing code base that results in a bug. We will explore using git to undo the change by rolling back to a previous commit and then fixing the bug.

Step 1: Modify and commit changes tomain.py#

Let’s make a change to the main.py and commit it to the repo. The twist is that our new modifications are going to contain a hidden and sneaky bug!

This would never happen in real life

You may be reading this and thinking - I will never commit a bug to the main branch of my git repo because all of my code is tested and triple checked beforehand. If so good for you. It never happens to me either… ahem.

'''
main module

'''

def do_something():
    print('Friendly code')

def do_something_else():
    print('This is a major bug!')

if __name__ == '__main__':
    do_something()
    do_something_else()

The git command to commit is

git status
git add main.py
git status
git commit -m "MAIN:+do_something_else() extends analysis"
git log -2 --oneline

Note that I checked the status of my repo before and after staging a file.

The final git command requests a one line summary of the last two commits:

e1c4fd3 MAIN:+do_something_else() extends analysis
6475df6 DOCS: run instructions

Step 2: Finding the bug#

When you run the analysis code:

This assumes you are in the analysis_code directory that contains main.py

python3 main.py

You are shocked to receive the following output:

Friendly code
This is a major bug!

This is a major problem for your project. This code runs every night and needs to be rolled back to a previous working version. Luckily this is simple because it is only 1 commit previous.

Step 3: Reviewing changes#

Before we undo the last commit to the repo let’s have quick look at what changes were actually made to main.py. We can do that by using commit id or by using HEAD~1 where ~1 refers to 1 commit previous.

git diff HEAD~1 main.py

In English, this asks git for the difference in main.py one commit previous. This results in:

diff --git a/main.py b/main.py
index 38056d8..d1c50df 100644
--- a/main.py
+++ b/main.py
@@ -4,7 +4,11 @@ main module

 
 def do_something():
-    pass
+    print('Friendly code')
 
-if __name__ = '__main__':
+def do_something_else():
+    print('This is a major bug!')
+
+if __name__ == '__main__':
     do_something()
+    do_something_else()

Step 3: Rolling back#

In my view “undo” operations in git can be some of the most confusing because there is more than one way to do it.

git revert#

Here we will take a safe option and git revert a commit. This command creates a new commit and reverses changes made in a previous commit. It is safe because you don’t lose any history. The old buggy commit remains and you can access the code within it.

The commit we want to revert is the last one. To be clear its is the commit that contains the code that introduced the bug. We first look up its commit hash:

git log -2 --oneline
e1c4fd3 (HEAD -> master) MAIN:+do_something_else() extends analysis
6475df6 DOCS: run instructions

and then issue the revert command referencing e1c4fd3

a reminder again that this is the commit that introduced the bug!

git revert e1c4fd3

When you do this you will be prompted to add a commit message. One is provided for you by git. I’m just going to accept it as is.

Revert "MAIN:+do_something_else() extends analysis"

This reverts commit e1c4fd3ce836f6fe1f7df3a6d1fb805209a790d8.

After reverting git we can check main.py and find that it has returned to a bug free state!

'''
main module

'''

def do_something():
    pass

if __name__ = '__main__':
    do_something()

We can also confirm that our history is intact and git revert has created a new commit by git log -3 --oneline

73fcaa5 (HEAD -> master) Revert "MAIN:+do_something_else() extends analysis"
e1c4fd3 MAIN:+do_something_else() extends analysis
6475df6 DOCS: run instructions