June 25, 2019

The GIT version control system - checking out by date

In truth, this is the reason many people use a version control system. It offers the ability to have a change history and the ability to checkout a snapshot of the source code at any given date. Of course the resolution in time is limited by how often you commit sources to your repository.

I am facing this scenario as some obnoxious regression bug has cropped up in a RTOS project I work with. I am now eager to build prior versions and define exactly the point in time when the code got broken and then examine what files were changed by the commit that introduced the bug. Sounds simple enough.

First of all, I don't want to disturb my existing working copy in any way. My plan is to clone the project "off to the side", do the checkout I want, build, test, and when it is all done be able to just delete that cloned experimental project entirely and forget about it.

Cloning an experimental repository

A normal git clone will just checkout the most recent files, and that is of no interest to us. We could clone from the repository URL using the --bare option, but there is a better way. We can clone directly from the repository in our local working copy as follows:
cd Project/src
cd ..
git clone --no-checkout src src_OLD
This yields a bare git clone in src_OLD alongside of our untouched working copy.
If you are lazy, note that "-n" is short for --no-checkout, so you could type this and get the same result:
git clone -n src src_OLD
You can repeat this as many times as you want generating a dozen experimental directories alongside of each other. Then you go into each and do a "git checkout" as described in the following section, then build in each directory and perform testing to get your issue sorted out. Once you are done, you could discard them all after making the appropriate changes to your original working copy.

Checkout files as per a given date

The trick here is to obtain the SHA hash key that specifies the commit you want. You can use "git log" to get a reverse chronological history of your commits. You can browse through this and find a likely candidate, then you cut and paste the key. Each commit yields an entry like the following:
commit fdd1ebb96bee93d344729107fdb2c72b045ab494
Author: Tom Trebisky 
Date:   Sat Oct 27 15:06:48 2018 -0700

    Make more IO tests work for Fire3
To checkout the above commit, you would do this:
cd src_OLD
git checkout fdd1ebb96bee93d344729107fdb2c72b045ab494
I selected instead the following, since the commit message indicated that I had sucessfully tested the feature that is now buggy.
cd src_OLD
git checkout 45cc7f105980903f153c45f9aa903816eef10d29
A bit of experimenting serves to isolate the commit that introduced the problem. I do git clone -n into a variety of experimental directories, checkout versions in each, and do a sort of informed binary search.

Using "git log --stat" yields the following for the commit that yielded the problem:

commit e474df2aefa5a1daeb8ab62a9068c9c1fef7c59d
Author: Tom Trebisky 
Date:   Sat Jun 16 13:58:24 2018 -0700

    Major cleanup in locore.S
    replacing stub functions with macros in cpu.h

 arm/cpu.h             |  27 +-
 arm/hardware.c        |  81 ++---
 arm/interrupts.c      |  12 +-
 arm/locore.S          | 927 ++++----------------------------------------------
 bbb/board.c           |  16 +-
 bbb/gpio.c            |  11 +-
 bbb/timer.c           |   7 +-
 console.c             |   4 +-
 main.c                |  29 +-
 orange_pi/board.c     |  22 +-
 orange_pi/multicore.c |   9 +-
 symbols.c             |  12 +-
 tests.c               |  34 +-
 thread.c              |   2 +-
 14 files changed, 189 insertions(+), 1004 deletions(-)
A pretty big commit. Having both sets of files before and after this commit in separate directories, allows a "diff -r" command to be run that shows exactly what is changed in each file.

Lessons learned from all this

These are project management lessons. The first is to make quality commit messages, since these are vital clues when browsing the "git log" output. The second is to commit often. It is never wrong to commit, though I make every effort to only commit code that successfully builds.

The third lesson is more comprehensive. It is good to clone your project once in a while and try to build it just as a neophyte discovering your project on Github might do. This will uncover files that are missing in the respository that you have neglected to git add. In my case there was an important symlink as well as an entirely missing Makefile in a subdirectory. This would have caused major headaches for anyone trying to clone and build my project. It is causing me headaches now as I try to clone and build my own project at a point in history!


Feedback? Questions? Drop me a line!

Tom's Computer Info / tom@mmto.org