← All 45 books Git advanced, one move per page Get the full edition · £10
One move per page

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.


Steve Hodgkiss 5 moves

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

Contents


Part 1 · The net first4
Reflog: the safety net5
Rebase -i: the commit editor6
Part 2 · Moving work
worktree7
Part 3 · The hunt
bisect: binary search8
Part 4 · Sharing without fear
--force-with-lease9
Part 1 of 4
Why nothing here is dangerous
1

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.


In this part
  1. 01Reflog: the safety net
  2. 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.

Git · No. 01
Recover

Reflog: the safety net

Every move is recorded, even the bad ones

RESET MOVED A LABEL. THE REfLOG REMEMBERS WHERE FROM.HEAD nowc2f1a9eb81e04d40c77the movegit reflogd40c77 HEAD@{0}: reset: moving to HEAD~1b81e04 HEAD@{1}: commit: fix loginc2f1a9e HEAD@{2}: commit: wipb81e04 HEAD@{3}: rebase (finish)still there90 days kept30 if orphanreset d40c77

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.

TRY IT THIS WEEK

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.

Git · No. 02
Rewrite

Rebase -i: the commit editor

Fix the history before anyone sees it

THE TODO LIST READS OLDEST FIRST. EACH LINE IS AN ORDER.git rebase -i HEAD~4pick a1b2 add login formreword c3d4 fix typosquash e5f6 testsdrop g7h8 debug junkpick i9j0 clean upreplayrewordedsquasheddrop gonecommandspickrewordsquashedit, drop

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.

TRY IT THIS WEEK

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.

Git · No. 03
Move

worktree

Two branches, two folders, one repo

ONE REPOSITORY, TWO WORKING FOLDERS, TWO BRANCHES.one repositoryshared objects, refs,one .git underneath~/shop (main)HEAD: main../hotfix (hotfix)HEAD: hotfixsame repo, separate HEAD and index eachone branch can only be checked out in one worktree at a time

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.

TRY IT THIS WEEK

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.

Git · No. 04
Hunt

bisect: binary search

Find the breaking commit in log time

EACH ANSWER HALVES THE RANGE. ROUGHLY 10 STEPS FOR 1000.goodbadBisecting: 675 revisions left (roughly 10 steps)first bad commit, namedbisect reset

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.

TRY IT THIS WEEK

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.

Git · No. 05
Share

--force-with-lease

The force push that checks first

THE LEASE ASKS ONE QUESTION FIRST: DID THE REMOTE MOVE?you (rebased)featureteammate (pushed)featurelease check:remote still atlast fetch?no: refuseorigin/featureone branch,one truthfetch, look, then decide--force: no check

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.

TRY IT THIS WEEK

Push a branch, amend the tip, push with --force-with-lease. From a second clone, push first: the lease refuses.

Index

Index


--force-with-lease9
bisect: binary search8
Rebase -i: the commit editor6
Reflog: the safety net5
worktree7