26.10.12

Go back in time with "git blame" + "git show"

There are no shortage of ways to look at individual changes to individual lines in a Git repository.  But what happens if you want to see what an entire file looked like, as of a particularly "long time ago", in the absence of an exact commit message or date?  
Unfortunately, alot of times, we don't know the exact hash of the commit we want to investigate.  In this very breif and embarrassingly simple post I'll outline a simple strategy for finding the "last version that probably worked".

**NOTE: If you have to do this alot - there is probably something systemically wrong in your dev process.**

Lets consider the following scenario: 

1) You know a file used to work, about 2 or 3 months ago.
2) You want to see how it was different
3) Line by line structural changes don't help - the file has since been restructured.  Methods were moved around.  Objects were refactored out, etc....  So you want the ENTIRE file as of a certain state.  In order to do this, you need to use "date" as a search filter.

First: You have to find out the "state" of the file which you are interested in.  In git, this means finding the commit hash.

The simple method, if you trust the commiters: "git log".

The "git log" command will give all commits related to a file:
git log src/myfilethatchanged.clj
 Will yield :
 
           Author:jayunit100
           Date:Dec 25 1:1:1 2012 +010
           Ripping out everything related to this really important function b/c its christmass
           .......

However, this is hardly the case.  Rather - commit messages are often (at best) highly granular, or (at worst) non existent.  Very rarely will a commit define a possible bug that you can search for by eye using git log.

In the (common) case where you don't know quite which commit you are interested in, you can simply use"git blame | grep <date regex>" to view several file states around the time you are interested in: 
git blame src/myfilethatchanged.clj  | grep jayunit100 | grep 2012-[89]
 At this point, you will get a list of git commit ids, followed by the commit contents.  Not particularly useful for fine grained debugging.  But it narrows it down to the right date!

a28jcz0a jayunit100 2012-09-22 18:43:58 +0200 934 //this is a bad idea.
e36fb8c9 jayunit100 2012-09-22 17:13:11 +0200 934 //some other inoccous line of code 

Ah  ^^ this looks like the commit which might have been done at the time that everything was working correctly.

Now, finally, here is how we write the entire file at that state in time out:
 
          git show e36fb8c9:src/myfilethatchanged.clj > myfilethatchangedOLD.clj

Thus, it is super easy to be a source dectective by simply using "git blame" along with "git show" to recreate a "whole" version of a working file at the time of an ancient commit, even if you don't know the exact date/hash of the commit.

The trick is to first use "git blame"with, for example, a simple grep to scan for the date range you are interested in.  Then, we find some candidate commits of interest, followed by "git show" to recover the exact state of the file at a given timepoint. 

2 comments:

  1. Instead of git blame + grep, you could also use `git log --since= --until= --follow `.

    ReplyDelete
  2. Thanks for the idea - just tried that but... i got an error --- because we want to track commits that specifically effected "src/myfilethatchanged.clj". It looks like "git log" works with the --since option iff you don't have a file as input. odd...

    ReplyDelete