Previously, in Part 9, we rescued a
deleted branch from the security-camera footage and let git bisect corner a
bug that an innocent-looking commit message tried to hide.
Welcome to the finale. Today we open the hood — and I promise it is the friendliest engine you will ever look at. One idea in this post reorganizes everything you have learned so far, so let's start with it.
Commits are snapshots, not diffs
For ten parts you have watched Git show you diffs — little plus and minus lines — so it is
natural to assume that is what Git stores. It is not. Every commit is a complete
snapshot of your entire project. The diffs you see are computed on the fly by
comparing two snapshots. Here is what actually lives inside your .git folder:
A commit is a tiny object holding your name, your message, a pointer to its parent commit — that is the chain that makes history — and a pointer to one tree. The tree is a snapshot of a folder: a list of filenames, each pointing to a blob, which is the file's actual content. Trees can point to other trees for subfolders, and that is the whole system. Three kinds of objects, holding hands.
Every object is named by the SHA hash of its contents — a fingerprint. Change one character in one file and its blob gets a new fingerprint, so the tree pointing at it changes, so the commit changes. This has two lovely consequences. First, history is tamper-evident: nobody can quietly edit an old commit, because every fingerprint after it would stop matching. Second, unchanged files are simply shared between snapshots — same content, same fingerprint, stored once. That is why a thousand commits do not take a thousand times the space, and why switching branches is instant: a branch is just a sticky note with a commit's fingerprint on it, and checkout merely moves the note and unpacks that snapshot. Every "aha" from this series — cheap branches, safe rebases, the reflog resurrection — falls out of this one picture.
Tags and releases
You have already met two tags without ceremony: the workshop repo's v0.1 (the
moment live search first worked — and our "good" state in Part 9) and v1.0.
A tag is a sticky note that never moves — a bookmark saying "this exact snapshot
is version 1.0." Run git tag in your clone to list them, and
git show v1.0 to see where one points. There are two kinds: a
lightweight tag (git tag v0.1) is just the note, while an
annotated tag (git tag -a v1.0 -m "First stable release")
carries a message, an author, and a date — use annotated for anything you release.
On GitHub, a tag can be dressed up as a Release: a page with release notes, downloadable files, and a friendly changelog. Push a tag, click "Create release from tag", and your project suddenly looks very grown-up.
A robot that tests every push
Here is a glimpse of where Git leads next. GitHub Actions lets you leave a small YAML file in your repo, and from then on a robot builds and tests every single push — yours, your teammates', everyone's. This is called continuous integration, and a taste of it looks like this:
name: Build and test
on: push
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-dotnet@v4
with:
dotnet-version: 10.0.x
- run: dotnet build
- run: dotnet test
That is the entire file. Break the build and the robot emails you before a human ever notices. The very site you are reading lives on GitHub and gets the same treatment — the cobbler's children have shoes now.
How teams arrange their branches
Once you join a team, someone will name a "branching strategy" in a meeting. Here is the secret: you already know all the moves. The strategies just choose how to combine them.
| Strategy | In one sentence | Reach for it when |
|---|---|---|
| GitHub Flow | Branch, open a PR, review, merge, deploy — repeat | Most projects, most teams. The recommended default, and exactly what you have done since Part 7 |
| GitFlow | Long-lived develop and release branches beside master | You ship numbered versions customers install |
| Trunk-based | Everyone merges tiny changes into master daily, behind feature flags | Big teams with a strong test robot |
Careful: a question every developer eventually asks — "do I put my database in Git?" You do not version the data; you version the shape of it. Table definitions live as code — migration scripts, committed like any other file — so the schema has history, reviews, and rollbacks while the data itself stays in the database. There is a taste of schema-as-code thinking in the PostgreSQL series.
The daily habits that make it all stick
- Commit small and often. Tiny commits are easy to review, easy to revert, easy to bisect.
- Make the message say why. The diff already shows what changed — and Part 9 proved messages get read years later.
- Pull before you push. Start from the latest shared history and conflicts stay small.
- Branch for everything. Branches are sticky notes — free, instant, disposable.
- Never force-push a shared branch. The Golden Rule from Part 8, wearing its work clothes.
- Commit before letting an AI agent loose on your code. Yes, that post again — future you will be very glad there is a restore point.
Look how far you have come
Take a second and look back down the mountain. In
Part 1 you were emailing
yourself zip files named final-FINAL-v2. In
Part 2 you typed
git init for the first time and made a photo album out of a folder. By
Part 7 you were reviewing code like a
teammate, and in Part 9 you resurrected
a deleted branch and out-detected a lying commit message. That is not a small journey. You
went from zip files to time lord — you can branch timelines, rewrite drafts, rescue the
lost, and interrogate history itself. Version control is a superpower precisely because it
removes fear, and a fearless programmer learns everything faster.
Tip: the best next step is to use Git on something real, today. Build along with the Blazor series, or containerize a project with Docker — and commit every step of the way, because now you know what those commits are worth.
Thank you for climbing all ten parts with me. If there is a topic you want this site to explain next — Git or anything else — tell me through the contact page; the best posts here started as reader questions. Happy committing, time lord.