← Writing

you don't know git and you don't know it

A tour of Git features most developers never touch — interactive rebase, bisect, reflog, worktrees, fixup, and more — written to demo every construct in the MDX pipeline.

I've been using Git for over a decade — or so I thought. Turns out I was only using ~20% of what the tool actually offers. This post covers the good stuff: commands that live outside the add/commit/push loop.

If you've ever typed git commit -m "wip" more than once more than ten times on a daily basis, keep reading.


1. Interactive Rebase (rebase -i)

The single biggest productivity unlock. Instead of piling up "fix typo" commits, rewrite history before pushing.

interactive-rebase.sh
# Squash the last 3 commits into one
git rebase -i HEAD~3
 
# The editor opens with a todo list:
# pick, reword, edit, squash, fixup, exec, break, drop

Pro tip: Combine with git commit --fixup and git rebase -i --autosquash for zero-friction history rewriting. More on this in the fixup section below.

Each action in the rebase todo list maps to a function in Git's internal state machine. The formula for a clean history:

Hclean=rebase(Hmessy,squash(fixup1,,fixupn))H_{\text{clean}} = \text{rebase}\big(H_{\text{messy}}, \text{squash}(\text{fixup}_1, \ldots, \text{fixup}_n)\big)

The interactivity comes from the GIT_SEQUENCE_EDITOR variable — set it to a script and automate the whole thing:

rebase-automation.ts
import { execSync } from "child_process";
 
type RebaseAction = "pick" | "reword" | "squash" | "fixup" | "drop";
 
function autoRebase(hashes: string[], actions: RebaseAction[]): void {
  const script = hashes
    .map((hash, i) => `${actions[i]} ${hash}`)
    .join("\n");
 
  execSync(
    `GIT_SEQUENCE_EDITOR="echo '${script}' >" git rebase -i HEAD~${hashes.length}`,
  );
}

What each action does

ActionEffectWhen to use
pickKeep the commit as-isDefault for most commits
rewordKeep changes, edit messageFix a typo in the message
squashCombine into previous commitMerge related changes
fixupLike squash, discard messageMinor fix, no new message needed
dropRemove the commit entirelySomething that shouldn't exist
execRun a shell commandRun tests between steps

2. Git Bisect — Binary Search for Bugs

When a bug appears and you have no idea which commit introduced it, git bisect performs a binary search over your history.

bisect-session.sh
git bisect start
git bisect bad                     # current commit is broken
git bisect good v2.3.0            # this tag was working
# Git checks out the midpoint — test and mark:
git bisect good                    # midpoint is good → search upper half
git bisect bad                     # midpoint is bad  → search lower half
# Repeat about log₂(n) times
git bisect reset                   # back to normal
MetricLinear searchBisect
Steps for 100 commits100log2100=7\lceil \log_2 100 \rceil = 7
Steps for 1 000 commits1 000log21000=10\lceil \log_2 1000 \rceil = 10
Steps for a year (~250)250log2250=8\lceil \log_2 250 \rceil = 8

The math is unbeatable — O(logn)O(\log n) vs O(n)O(n). You can even automate it:

automated-bisect.sh
git bisect start HEAD v2.3.0 --
git bisect run npm test              # runs test at each midpoint
git bisect reset

The run subcommand expects a zero exit code (good) or non-zero (bad). This genuinely saves hours.


3. The Reflog — Your Safety Net

Think you lost work? You almost certainly haven't. The reflog records every HEAD movement for ~90 days.

git reflog
# abc1234 HEAD@{0}: reset: moving to HEAD~1
# def5678 HEAD@{1}: commit: implement oauth flow
# 123abcd HEAD@{2}: rebase -i (finish): returning to refs/heads/main

Recover a "lost" commit:

git checkout HEAD@{2}
# or cherry-pick it into your current branch:
git cherry-pick abc1234

Reflog is local-only. It's not shared via push or fetch. Each clone has its own journal. The name HEAD@{N} is a relative reference: N steps ago in the reflog.

Here's what you can recover from:

  • Accidental git reset --hard
  • Deleted branch (before GC)
  • Aborted rebase aftermath
  • Cherry-pick gone wrong
  • git push --force on a shared branch — reflog won't save teammates
  • GC'd commits older than gc.reflogExpire (default 90 days)

4. Git Worktrees — Parallel Development

Need to review a PR while keeping your working tree intact? git worktree lets you check out multiple branches simultaneously.

worktree-setup.sh
git worktree add ../hotfix hotfix/main
# Creates ../hotfix with hotfix/main checked out.
# Your current directory stays on feature/xyz.
 
git worktree list
# /Users/you/project           (main)
# /Users/you/project/hotfix    (hotfix/main)
 
git worktree remove ../hotfix

When to use worktrees vs stashing

ScenarioWorktreeStash
Short context switch (< 5 min)
Long-lived parallel feature
Reviewing a PR mid-sprint
Quick hotfix on production
No disk space for another checkout

Gotchas:

  1. You cannot check out the same branch in two worktrees
  2. Detached HEAD in a worktree means manual cleanup
  3. Each worktree needs its own node_modules — symlink if space is tight out of /Users/you/project

5. Fixup & Autosquash — History Editing on Autopilot

The --fixup / --autosquash combo is Git's most underrated feature.

fixup-workflow.ts
import { execSync } from "child_process";
 
// Stage 1: make a fixup commit targeting a specific hash
execSync(`git commit --fixup ${targetHash}`);
// Git creates: "fixup! Original commit message"
 
// Stage 2: rebase with autosquash
execSync("git rebase -i --autosquash HEAD~10");
// The todo list arrives pre-sorted — fixup commits
// land right below their target. Just save and quit.

Without --autosquash, you'd reorder manually in the editor:

manual-todo.yml
# What you'd have to type:
pick    abc1234 Add user auth
squash  def5678 Fix typo in auth
pick    789abcd Add rate limiting
fixup   012def9 Oops, fix rate limit

With --autosquash, the todo list arrives already ordered. The GIT_SEQUENCE_EDITOR variable can script the entire pipeline — worth integrating into git-scm.com your editor of choice.


6. Advanced Logging

The plain git log is table stakes. Add --graph, --format, and path filters:

pretty-log.sh
git log --graph --oneline --all --decorate --simplify-by-decoration

This shows a real picture of your branching topology. Pair it with --format for total control:

git log --graph --pretty=format:'%C(yellow)%h%Creset %C(cyan)%<(12)%an%Creset %s %Cgreen(%cr)%Creset' --all

The Math Behind Merge vs Rebase

A merge creates a three-way merge commit:

Δmerge=diff(A,B)diff(A,C)\Delta_{\text{merge}} = \text{diff}(A, B) \cup \text{diff}(A, C)

A rebase replays each commit from one branch onto another:

B=cherry-pickB0,B1,,Bn(A)B' = \text{cherry-pick}_{B_0, B_1, \ldots, B_n}(A)

The end state is functionally identical, but the graph topology is very different. Neither is "right" — it's a social contract with your team.

A deeper look at the triangle

Let ABC be three commits. Rebasing onto D replays each delta:\text{Let } A \to B \to C \text{ be three commits. Rebasing onto } D \text{ replays each delta:}

C=apply(diff(A,B)diff(B,C),D)C' = \text{apply}\big(\text{diff}(A, B) \circ \text{diff}(B, C), D\big)

If a conflict arises, Git pauses and asks you to resolve it — one commit at a time. This is the edit phase of rebase, where $GIT_SEQUENCE_EDITOR meets $EDITOR and you stare at a terminal at 2 AM wondering why you didn't just merge.


Wrapping Up

Here's what to try this week:

  • Run git rebase -i on a feature branch
  • Recover a commit from git reflog
  • Complete one git bisect session
  • Set up a git worktree for a PR review
  • Write a --fixup script for your editor
  • Customize git log format in ~/.gitconfig

Further reading

ResourceWhy
Pro Git by Scott ChaconThe canonical reference, free online
git help <command>Every flag documented
git-scm.comSearchable docs + videos

This post demonstrates every construct in the MDX pipeline: headings (H1–H4), bold, italic, strikethrough, inline code, fenced code blocks with syntax highlighting (bash, TypeScript, YAML), line highlighting {1,4}, word highlighting /pattern/, line numbers, blockquotes, ordered and unordered lists, nested lists, GFM tables and task lists, inline and display math via KaTeX, autolinks <url>, reference links, and horizontal rules.

The best tool is the one you've already got — but actually know how to use.