Diff files for troubleshooting Git branches
A couple months ago I wrote about using diff files to review code in Neovim, instead of using GitHub's UI. That's a recent innovation, but I've long used diffs to troubleshoot problems in unfamiliar code, to isolate the cause of a bug or test failure when neither inspection of the code nor step-by-step debugging has uncovered it. When commits are granular and atomic, I can use git bisect to isolate the problem, but that's not always an option. Sometimes the branch has messy commits; maybe mine, maybe someone else's. So when bisecting doesn't work, I've applied partial reverse diff files to find what causes a problem.
For a long time I would revert the whole branch without committing, then stage reverted chunks one at a time, clear the unstaged changes, and check for the bug or test failure. That worked alright, but it could often get confusing. It can be a little tricky to think in terms of staging reversions, and if the set of changes didn't fix the problem, or introduced a new problem, such as by not undoing some related changes, then I had to reset my git index and start over. Now I stage changes via diff files, instead of using Git's staging area. Here's how.
I start on a branch off the main development branch (main), then clear the working directory of uncommitted changes, either by committing them, stashing them, or discarding them. Then I create a reverse diff and save it to a file with git diff main... -R > changes.diff. That diff captures the changes that will undo the entire branch and make it identical to main. Since I only want to undo specific changes, I open a new file, say, candidate.diff, and copy sections of changes.diff over to it one at a time. Diff files are arranged as chunks within files, so it's easiest to copy across the full file then delete any unwanted chunks. Once I have the changes I want undone in candidate.diff, I apply it with cat candidate.diff | patch -p1, then check for the bug or test failure. If the problem isn't fixed, or if some additional reversions are needed, I try again by clearing changes with git checkout -- ., then I edit candidate.diff, run the new patch, and test again.
I've been using much smaller, more atomic commits lately, so git bisect works well, and it's been a while since I needed this workflow. Next time I do need it, though, I plan to write some helper scripts. For example, here's a small script to alleviate the confusion of patching reversions by first reverting the whole branch, then applying the positive diff:
#!/bin/bash
patch=$1
git diff main... -R | patch -p1
cat $patch | patch -p1
If you have other tips for troubleshooting changes on a Git branch, email me.