Three Git Commands That Save an Hour Every Week

Git ships with far more than commit, push and pull. Three of its built-in commands remove entire categories of busywork.

1. git bisect — find the commit that broke it

When something worked last week and is broken today, do not read forty commits. Let Git search them for you:

Advertisement
git bisect start
git bisect bad                  # current HEAD is broken
git bisect good v1.4.0          # this tag was fine
# Git checks out a commit in the middle; test it, then:
git bisect good   # or: git bisect bad
# … repeat until Git prints "… is the first bad commit"
git bisect reset

Twenty commits means about five test cycles. If the test is scriptable, add git bisect run ./test.sh and walk away.

2. git commit --fixup + rebase --autosquash — clean history without the pain

Reviewer asks for a change in the commit you made two commits ago. Instead of an ugly “fix review comments” commit:

git commit --fixup <sha-of-target-commit>
git rebase -i --autosquash main

The fixup commit is folded into its target automatically, in the right order, and history reads as if you had written it correctly the first time. Note that this rewrites commits — only do it on branches nobody else has pulled.

3. git worktree — two branches, one repository

You are deep in a feature and an urgent fix lands on main. Stashing is fine until you forget what you stashed. Instead, check out a second working directory that shares the same repository:

git worktree add ../hotfix main
cd ../hotfix          # a normal checkout of main, no stashing
# … fix, commit, push …
cd - && git worktree remove ../hotfix

Each worktree has its own index and its own checked-out branch, so your unfinished work stays exactly where it was.

Why these three

  • bisect turns “which commit did it?” from a memory test into a binary search.
  • autosquash removes the low-value merge noise that makes history unreadable.
  • worktree removes the stash-and-pray ritual from interrupted work.

None of them need a plugin, a GUI or a server change — they are in the Git you already have.

git bisect run: hand the search to a script

Automated bisect needs nothing but an exit code:

git bisect start
git bisect bad HEAD
git bisect good v1.4.0
git bisect run ./scripts/check-auth.sh
git bisect reset

The exit code is the answer: 0 good, 1–124 bad, 125 “cannot test this commit, skip it” — the right response when a commit does not compile.

The search takes log2(n) steps, so 1 000 commits costs 10 builds — cheap only if each build is cheap. Cache hard (a warm container, ccache), and keep git bisect log > bisect.txt so a colleague can resume with git bisect replay bisect.txt.

A flaky test produces a confident wrong answer: at a 1-in-20 failure rate, a healthy commit eventually gets marked bad. Run the test three times and exit 125 when results disagree.

Recovering from a bad autosquash rebase

A rebase that has resolved conflicts into the wrong shape is recoverable, if you know which lever to pull:

  • Still running? git rebase --abort restores the exact pre-rebase state.
  • Already finished? git reset --hard ORIG_HEAD, because rebase stores the previous tip there.
  • Mid-flight? git rebase --edit-todo changes the remaining steps without starting over.

Two refinements worth memorising: --fixup=amend:<sha> also replaces the target's message, and --fixup=reword:<sha> changes only the message. If the same conflict returns on every rebase, git config rerere.enabled true records your resolution and replays it.

To audit the result, git range-diff main ORIG_HEAD HEAD prints one line per commit with any content change visible. Keep it to branches nobody else has pulled, because this rewrites commits.

git worktree in real projects: dependencies, ports and databases

A worktree shares .git and nothing else. Expect a second set of dependencies, and check what else is per-directory:

  • Dependencies and ports. Another vendor/ or node_modules/, and a dev server that needs its own port — put the port in an environment file rather than editing code.
  • The local database. Two worktrees reading one .env share a database, so migrating in one breaks the other. Name each database after its branch.

A branch can be checked out in only one worktree; the second attempt fails with fatal: 'main' is already checked out at …. For read-only inspection, git worktree add --detach ../probe v1.4.0 takes the tag without claiming a branch. Keep worktrees as siblings of the repository, never inside it, or the parent directory shows them as untracked files. Remove them with git worktree remove ../probe (--force when dirty), and git worktree prune after deleting a directory by hand. A worktree is also the polite place to run a bisect: it never checks out your work in progress.

git reflog is the undo log you already have

Nearly every “I destroyed my branch” story ends with the reflog, which records every position HEAD has held, newest first, with the command that moved it:

git reflog                 # where HEAD has been, with the reason
git reflog show my-branch  # where that branch has been
git reset --hard HEAD@{3}  # go back three moves

A deleted branch is not gone while its commit is in the reflog: find the SHA, then git branch recovered <sha>. A dropped stash is harder — git fsck --unreachable lists the dangling commits.

The limits matter as much as the trick: a reflog is local and never pushed, so a fresh clone has none, and entries expire — 90 days for reachable commits, 30 for unreachable ones. It is an undo log, not a backup: before a large rebase, push the branch under a throwaway remote name.

When these commands are the wrong answer

All three tools assume the history is yours to move, which fails in predictable places:

  • Shared, long-lived branches. Rebasing or reset --hard where others have pulled breaks their clones. Undo a published commit with git revert, which adds a commit and leaves the timeline honest; if you must push rewritten history, use --force-with-lease, never plain --force.
  • Bisect without a deterministic test or a build budget. Where every step costs a 20-minute build, reading the commits is faster, and when the change is one symbol, git log -S'functionName' narrows it down in seconds.
  • Worktree as a substitute for process. It removes the stashing, not the risk: an untested hotfix in a second directory is still an untested hotfix.
Advertisement
khallaf

Writing about programming, AI and the tools that make engineering teams faster. Published by A1 Systems.

Last updated 19 Sep 2026

// Keep reading

Related articles

Tools & Tricks 5 min read

Regex you will actually use

The small set of regex constructs that cover everyday work, the patterns worth keeping in a snippet file, and how to avoid catastrophic backtracking.

khallaf Tip