Git advanced, one move per page
The twenty-two moves past the basics: rewrite safely, hunt bugs by halving, and never lose work to a bad force push.
You know the commands. These are the moves.
Git advanced, one move per page
The twenty-two moves past the basics: rewrite safely, hunt bugs by halving, and never lose work to a bad force push.
Set in Space Grotesk, Inter and JetBrains Mono (SIL Open Font License).
Command behaviour verified against the official Git documentation and man pages: git-scm.com/docs, including git-reflog, git-rebase, git-cherry-pick, git-stash, git-bisect, git-blame, git-log, gitrevisions, git-push, git-pull, git-merge, git-worktree, git-notes and githooks.
This book is the advanced companion to 'Git, one command per page' and stands alone: it assumes the everyday commands and teaches the moves.
Your purchase is for personal use only. You do not have redistribution rights: please do not share, resell, or republish this book or its pages.
© 2026 Steve Hodgkiss. All rights reserved. Personal use only; no redistribution rights.
Edition 1.0 · stevehodkiss.net
Contents
The net first
The safety net under every rewrite: reflog records where HEAD has been, rebase -i edits commits before anyone sees them, fixup files fixes under their targets, and --onto moves a branch to a new base.
- 01Reflog: the safety net
- 02Rebase -i: the commit editor
The reflog: every place HEAD has pointed is recorded locally, default 90 days for reachable entries, 30 days for unreachable. This is the safety net under every dangerous move in this book.
Reflog: the safety net
You just ran git reset --hard and watched your work vanish. It hasn't.
The reflog records every place HEAD has pointed, local to your machine. The panic move is assuming the commits were deleted. A label moved. The commits stayed.
Find the entry before the damage and reset to it. Reachable entries are kept 90 days by default, 30 for unreachable ones.
The work is almost never gone. Now you can use every other page safely.
Scratch repo: commit, git reset --hard HEAD~1, then git reflog. Your commit is listed.
Interactive rebase: rebase -i lists commits oldest first and lets you pick, reword, squash, edit or drop each one before replaying them onto the new base.
Rebase -i: the commit editor
Five commits. One has a typo, two should be one, one's junk.
git rebase -i lists commits oldest first, each line starting with a command. pick keeps, reword fixes the message, squash folds into the line above, drop removes. The classic mistake is squashing upwards: squash folds into the commit above it in the list.
The rewritten commits get new hashes, but the originals sit in the reflog if you need them back.
Write history for the reader. Rebase -i is the editor.
Scratch repo, 3 commits: git rebase -i HEAD~3, reword one message.
git worktree add checks out a second branch in a second directory from the same repository, sharing object storage and refs. One main worktree, zero or more linked worktrees; remove with git worktree remove.
worktree
You're three files deep in a feature and someone needs a hotfix on main. Stash and hope, or not.
git worktree add ../hotfix hotfix checks out a second branch in a second folder, same repository underneath. Refs are shared; each worktree gets its own HEAD and index. The limit: a branch can be checked out in only one worktree.
The urgent fix gets a folder and your feature never noticed. Done? git worktree remove.
Stashing is for minutes. Worktrees are for days.
git worktree add ../spare main. Run one command in each folder, then git worktree remove ../spare.
git bisect performs a binary search: mark one bad commit and one good commit, Git checks out midpoints until the first bad commit is found. Roughly ten steps covers about a thousand commits.
bisect: binary search
The build was fine Tuesday and broken today. Two hundred commits sit between, and you suspect none of them.
git bisect start, then mark the known bad and good commits. Git checks out the midpoint and asks: good or bad? Each answer halves the range. One guessed answer, and the search names the wrong commit.
Roughly ten steps covers about a thousand commits. git bisect reset puts you back.
Don't read two hundred commits. Let the halving read them for you.
Scratch repo: git bisect start; git bisect bad; git bisect good HEAD~5. Answer the halving honestly.
git push --force-with-lease pushes only if the remote ref is at the expected position, failing if someone else advanced it, where --force overwrites unconditionally.
--force-with-lease
You rebased a pushed branch and Git refuses your push. The internet says force. Your teammate says don't.
--force-with-lease overwrites only if the remote branch is still where your last fetch left it. Someone pushed meanwhile? It refuses, you fetch, you look. Plain --force doesn't check: a teammate's commits stop being reachable from the branch.
Rebasing pushed work is fine on your own branches, with a lease.
Force says move. Force-with-lease says move if nobody else did.
Push a branch, amend the tip, push with --force-with-lease. From a second clone, push first: the lease refuses.