Quick Git: Finding Old Code via the CLI

I’m working on compiling some examples of React best practices. I remember a few months ago how a colleague had created a componentDidMount method that would always end up calling setState. This results in an endless loop. I wanted to find this specific example to show for educational purposes, and show how it was corrected.

In order to do this, I pulled the latest copy of the code and started working through it. To search code, I typically use ack. It’s my personal favorite search tool for my development workflow. At any rate, I first searched for componentdidupdate. There were quite a few examples, but nothing really looked like what I was looking for. I knew I was going to have to search history.

I started out by using git log --author=PR (P. R. is the name of the author who wrote the code I was trying to find). This includes merges, so git log --no-merges --author=PR helped. He had many commits, and I knew the commit was several months old. However, I didn’t know exactly when it was, so searching through git log wasn’t going to help. I attempted to use git log --grep, but this actually greps commit messages, rather than code changes.

I found what I thought might have been the file after looking at some of the original search results. git blame revealed that the file was not updated by P. R. in the way that was consistent with the problem I remember. I also verified this by looking at git show -p <commit> to see what the changes were. Unfortunately, from what I can tell git grep does not allow you to search by author. Additionally, it greps code for files at the time of a list of provided commits rather than the diffs of the commits themselves.

I knew the author, and I had a pretty good idea that the code was at least two months old. This allowed me to narrow down the range of commits to git log --no-merges --author=PR --before="2 months" -p. The -p shows a full patch of the changes made during that commit. This allowed me to pipe it to grep and also provide -C with a relatively large number to provide context to see what file the changes were made in so that I could find its name.

git log --no-merges --author=PR --before="2 months" -p \
| grep -C8 componentDidUpdate | less

At this point I had to do a visual scan to see if I could find the file I needed, but I had narrowed it down to only a few results definitely including the one I wanted. I found it pretty quickly — turns out the file had been deleted. That explained why it was hard to find initially.

Fortunately, you can still view what deleted files looked like in version control under normal circumstances. I did this via gilt log --author=Andrew --all --full-history -- <file>. “Andrew” is me since I made the fixes I was looking for. gilt is a command line tool I’m working on that allows you to interact with git log a little better. This made it easier to find the specific changes and commit I was interested in.

Finally once I found the commit, I used git show <commit>:<file> to see what the file looked like at that commit. I actually needed to go back one commit to find what I needed, so <commit>^ did that.

I also could have used git log -S componentDidUpdate alongside some of the other options to find the changes faster. That will be good to know for the future. git log -S does what I wanted git log --grep to do. -S uses strings, and there is also git log -G for regular expressions.

These Stackoverflow questions helped (and probably a few more):

To summarize:

# see your command line history
# see log of commits made by the author ("Author:" field in git log)
git log --author=PR
# ... and omit merge commits. These are not useful for finding      # changes actually made by the author
git log --author=PR --no-merges
# Limit to a timeframe. You can also use --after/--since/--until and
# date strings
git log --author=PR --no-merges --before="2 months"
# Also print out the patch/diff (code changes) with the log info
git log --author=PR --no-merges --before="2 months" -p
# Search for the specific string you're looking for too
git log --author=PR --no-merges --before="2 months" -p -S cDU
# Git log for removed file. Not exactly sure why you need both.       # the `--` is intended; it means end options, start arguments.            #                                                                    # You can include all of the other `git log` options from above too
git log --all --full-history -- src/Map.tsx
# Show what a file looked like at a certain commit
git show bfb704:src/Map.tsx
# Show what a file looked like at the commit right before a certain      # commit
git show bfb704^:src/Map.tsx

Git is very powerful. You can do practically everything you could think of with it, especially when it comes to looking for things in history.