A few git commands I wish I knew sooner.
A loose continuation of the Master Git series on this blog — about eight years late, but the topic still comes up.
Three subcommands worth using more often than most people do. None of them are new. All of them are easy to miss in the man pages.
git bisect — when did this break?
Imagine the situation. Your test suite was green last month. It is red today. Somewhere between 200 commits ago and now, somebody (very possibly you) introduced the bug. You don’t want to read 200 diffs.
git bisect does a binary search through history for you. You tell it one bad commit and one good commit, and it checks out the midpoint. You run your test. You tell git whether the midpoint was good or bad. It narrows the range. Repeat until you have a single guilty commit.
$ git bisect start
$ git bisect bad # current HEAD is broken
$ git bisect good v1.4.0 # last release I know was fine
Bisecting: 92 revisions left to test after this (roughly 7 steps)
[a1b2c3d…] some commit message
$ npm test # or whatever your check is
$ git bisect bad # or good
…
$ git bisect resetFor 200 commits you need about 7-8 iterations. If your check is scriptable (and most are), you can let git run it for you with git bisect run <script>:
$ git bisect start HEAD v1.4.0
$ git bisect run ./scripts/repro.sh
…
a1b2c3d is the first bad commitThat is the entire workflow. You start it, you walk away, you come back to a single commit hash. I have used this to track down regressions that I had been staring at for an hour and would have stared at for the rest of the day.
git worktree — checking out two branches at once
The standard story: you’re working on a feature branch, your colleague pings you with “quick, can you check what production looks like in main?” You stash. You switch. You forget about the stash for three days.
git worktree lets a single repository have multiple branches checked out into multiple directories at the same time:
$ git worktree add ../site-main main
Preparing worktree (checking out 'main')
HEAD is now at 8f3a2c… some commitNow you have two directories. Your original one is still on your feature branch with all its untouched changes. ../site-main has main checked out. Same Git history, same remotes, no stash, no tears. When you’re done:
$ git worktree remove ../site-mainI use this for three things mostly:
- Reviewing a colleague’s pull request without disturbing my own work.
- Running a long-running test on the old branch while I work on the new one.
- Comparing builds — I have two different commits compiled at the same time and can diff their outputs.
git sparse-checkout — only the bits you need
This one is more situational, but when you need it, you really need it. If you work in a monorepo or any large repository, you can ask Git to only materialize a subset of paths in your working directory. The history is still complete, the objects still get fetched, but your filesystem only has what you asked for.
$ git clone --filter=blob:none --no-checkout git@github.com:org/mono.git
$ cd mono
$ git sparse-checkout init --cone
$ git sparse-checkout set services/payments libs/common
$ git checkout mainThe --filter=blob:none bit makes the clone a partial clone (blobs are fetched on demand). --cone mode restricts the patterns to directory prefixes, which is faster and almost always what you want. The result: a repo that pretends, for the purposes of your editor and shell, to only contain services/payments and libs/common.
I will not pretend this is for everyone. If you work on a thirty-thousand-file monorepo, this turns a five-minute clone into a twenty-second one and saves you from having to teach your editor to ignore most of the disk.
That is it
Three commands. None of them were invented in the last year. All of them have been quietly waiting in git --help for me to actually read it.