Git from First PrinciplesPost 15 of 16 · 16 of 16 written
- How to use this guide
- 01Why version control exists
- 02Git's data model: a content-addressed object database
- 03References: branches, HEAD and tags are pointers
- 04The three areas: working tree, index, repository
- 05Daily commands, mapped onto the model
- 06Branching and integration: merge, rebase, cherry-pick
- 07Undoing things safely
- 08Remotes and collaboration
- 09Workflows and conventions
- 10Git for AI and ML work
- 11Git with AI coding agents
- 12Power tools
- 13Under the hood: build a commit by hand
- 14Debugging mindset and recovery playbook
- 15Practice path, fluency checklist and glossary
- 16Sources
15. Practice path, fluency checklist and glossary
Git · 15 of 164 min readOct 2026
Git · Post 15 of 164 min readOct 2026
Reading builds the model; drills make it fluent. Work through these exercises in order in ~/git-lab, and tick them off as you go.
Practice path
- Objects. Make five commits with good messages. Run
git cat-file -pon a commit, its tree and a blob. Count objects withfind .git/objects -type fafter each commit. - Deduplication. Commit two files with identical content under different names. Confirm with
git ls-tree HEADthat both point to one blob. - The index. Stage a file, edit it again, commit, and check which version landed. Then use
git add -pto split one messy change into two clean commits. - Branching. Create two branches that diverge. Fast-forward one into
mainand three-way merge the other. Then cause a conflict on purpose, read it withzdiff3, and resolve it. - Rebase. Rebase a branch and note the new hashes. Use
git rebase -ito squash three commits into one, then try--fixupwith--autosquash. - Disaster drills. Reset three commits away with
--hardand recover them from the reflog. Delete a branch and restore it. Rescue commits made in detached HEAD. Drop a stash and recover it withgit fsck. - A fake server. Run
git init --bare ~/git-lab/server.git, then clone it twice asaliceandbob. Make them push conflicting work, hit a rejected push, fix it with fetch and rebase, and practise--force-with-leaseon a feature branch. - Plumbing. Do the build-a-commit-by-hand exercise in Part 13 without looking.
- An ML repository. Add the Part 5
.gitignore, pre-commit with ruff, gitleaks and nbstripout, and DVC tracking a small dataset to a local remote (dvc remote add -d local /tmp/dvcstore). Make a training script log its commit hash and dirty flag. - Bisect. Write a script that creates 30 commits, one of which breaks a test. Find it with
git bisect run. - Worktrees. Create two worktrees on two branches, make a change in each, and merge both into
main.
You are fluent when you can…
- Draw the object graph for any repository from
git lg, and explain blob, tree, commit and tag. - Say which areas (working tree, index, refs, objects, remote) any command reads and writes.
- Predict the graph after a merge and after a rebase, before running either.
- Recover from
reset --hard, a deleted branch or detached HEAD without searching. - Resolve a conflict by reasoning from the base, not by picking a side at random.
- Explain
fetch,pullandpushin terms of refs, and why a push gets rejected. - Choose between
revertandresetby asking whether the commits are shared. - Set up an ML repository with ignores, hooks, data pointers and commit-hash logging.
- Review an agent’s diff for scope, weakened tests, dependencies and secrets.
- Find a regression with
git bisect run.
Glossary
| Term | Meaning |
|---|---|
| Blob | Object holding one file’s raw content, with no name |
| Tree | Object listing names, modes and hashes of blobs and subtrees: one directory |
| Commit | Object holding a tree hash, parent hashes, author, committer and message |
| Annotated tag | Object naming another object, with tagger, message and optional signature |
| Hash | The content-derived name of an object (SHA-1 today, SHA-256 planned as default) |
| Ref | A human-readable name mapped to a hash |
| Branch | A ref under refs/heads/ that moves forward as you commit |
| HEAD | The ref for your current position, usually naming a branch |
| Detached HEAD | HEAD holding a commit hash directly, not a branch name |
| Working tree | The files on disk you edit |
| Index / staging area | The draft snapshot of your next commit |
| Reachable | Findable by following parent pointers from some ref |
| Dangling | An object nothing points to; eventually garbage-collected |
| Reflog | Local log of where each ref has pointed |
| Merge base | The best common ancestor of two commits |
| Fast-forward | Moving a ref ahead along existing history, with no new commit |
| Three-way merge | Combining two tips by comparing each with their merge base |
| Rebase | Replaying commits onto a new base, creating new commits |
| Cherry-pick | Applying one commit’s change onto HEAD as a new commit |
| Remote | A named URL of another copy of the repository |
| Remote-tracking branch | Your local bookmark of a remote branch, such as origin/main, updated by fetch |
| Upstream | The remote-tracking branch a local branch is linked to |
| Refspec | A rule mapping remote refs to local refs, used by fetch and push |
| Packfile | Compressed bundle of objects stored as deltas |
| Porcelain / plumbing | User-facing commands / the low-level commands they are built on |
| Worktree | An extra working directory attached to the same repository |
| Submodule | A pinned commit of another repository inside this one |
| Hook | A script Git runs at a fixed moment, such as before a commit |
| Git LFS | Extension that stores large files outside Git behind pointer files |