little cubes

How to Suck Less at Git

Part 3 - Other Useful Git Tools

In parts 1 and 2 we covered the most basic tools in the developers Git toolbox after add and commit. This post dives a bit deeper into other lesser used but extremely powerful git tools; git rebase -i, git bisect, and git reflog.

Terminal window
git rebase -i <commitish (exclusive)>

Interactive rebase is an advanced technique. Even though -i looks like just a flag on the rebase command that we already talked about, it’s best to think of this as a completely separate tool from regular rebase.

When you run this command, git will present you with a prompt that looks something like this (I’ve trimmed this down slightly):

Terminal window
pick f7f3f6d Change my name a bit
pick 310154e Update README formatting and add blame
pick a5f4a0d Add cat-file
# Commands:
# p, pick <commit> = use commit
# r, reword <commit> = use commit, but edit the commit message
# e, edit <commit> = use commit, but stop for amending
# s, squash <commit> = use commit, but meld into previous commit
# f, fixup <commit> = like "squash", but discard this commit's log message
#
# These lines can be re-ordered; they are executed from top to bottom.
# If you remove a line here THAT COMMIT WILL BE LOST.

It shows you a list of commits and a list of commands. Change the command on a specific commit to modify that commit accordingly:

  • pick: don’t modify this commit
  • reword: edit this commit’s message
  • edit: pause replay of the commits at this step so you can do something to it
    • for example you can manually make additional changes and use commit --amend to add those changes to this commit
    • or you can use reset to “undo” this commit and then split it into multiple commits by using add --patch to only stage certain parts
  • squash: combine this commit with the previous one, kind of like commit --amend
  • fixup: combine this commit with the previous one, and also delete its commit message (squash opens an editor for you to combine the commit messages)
  • delete a line to delete that commit
  • reorder the lines to change the order of the commits

Bisect is an extremely powerful debugging tool that helps you find the source of a bug by tracking down (via binary search) the exact commit that created it.

  1. You give it start and end commits; one you know works (is “good”) and one you know has the bug (is “bad”).
  2. It goes to the commit in the middle
  3. You test that commit and tell Git if that commit is good or bad
  4. Git picks a new commit and you repeat step #3.

Bisect works best when:

  • You have a linear history (every commit comes before and after exactly 1 other commit)
  • Every commit compiles. This process takes much much longer if you have to make code compile before testing a commit.
  • Your commits are small. If the commit that introduced the bug is 5,000 lines, you haven’t narrowed it down all that much.

There are very few truly destructive actions in Git. If you commit something, no matter what you do next (apart from deleting the .git folder), it will always be possible (for someone who knows what they’re doing) to recover that commit for at least 30 days. After 30 days “hanging” commits that no branch points to can be garbage collected.

Until then, you can find it in the reflog!

Terminal window
git reflog
97299e1 (HEAD -> main, origin/main, origin/HEAD) HEAD@{0}: pull: Fast-forward
7d5fb7d HEAD@{1}: commit: fix(mac-setup): Cleanshot preview don't move between screens
d570c6c HEAD@{2}: commit: fix(mac-setup): Less scrolling issue
b783846 HEAD@{3}: commit: fix(jargon-prod): Add \"going live\" synonym
bf4b5c2 HEAD@{4}: checkout: moving from zp/learnability to main

You can think of reflog as the Git graveyard. When you’ve lost something, the reflog is where you can recover it. Specifically, reflog is a list of every action that you’ve performed in Git for the last month. It lists every hash that your HEAD has pointed to.

I like to use it in conjunction with grep to filter the results:

Terminal window
git reflog | grep scroll
d570c6c HEAD@{2}: commit: fix(mac-setup): Less scrolling issue