Six Git Myths That Were Holding Me Back

I used Git for years before I actually understood it. I could commit, push, pull, and make a branch. I could copy-paste my way out of most messes. But I didn't have a model in my head for what Git was doing. So every time something went sideways, it felt like luck whether I'd get out of it.

What finally fixed that wasn't learning more commands. It was unlearning a handful of things I believed that were simply wrong. Here are the six that mattered most.

1. Commits Are Snapshots, Not Diffs

This is the big one. I thought a commit stored the changes — the lines I added and removed. A little patch, stacked on the patch before it.

It isn't. A commit stores a snapshot of your entire project at that moment. Every file, as it was. Git's own documentation is explicit about this: "Git thinks of its data more like a series of snapshots of a miniature filesystem." (Pro Git, "What is Git?")

Git is smart about storage — files that didn't change between commits aren't duplicated, they're referenced. But the model is snapshots, not diffs. The diffs you see in git show or git log -p are computed on the fly by comparing two snapshots. They aren't what's stored.

Once this clicked, half of Git stopped being mysterious. Why can you check out any old commit and get the whole project back? Because each commit is the whole project. Why is branching cheap? Because a branch doesn't copy anything — it points at a snapshot that already exists.

If you only fix one belief from this list, fix this one.

2. Rebase Is Safe on Unshared Commits

I avoided git rebase for a long time. I'd read scary warnings: "rebase rewrites history", "never rebase", "you'll lose work". So I stuck to merge and felt vaguely guilty about my messy history.

Here's the truth: rebase is completely safe on commits you haven't shared yet. The danger of rebase is exactly one thing — rewriting history that other people have already pulled. If you rebase a branch someone else has based work on, you force them into a painful reconciliation. That's the whole warning. (git-rebase docs — "Recovering from upstream rebase".)

But your own local feature branch, the one nobody else has touched? Rebase it freely. Reorder commits, squash them, clean up messages. You're rewriting history that only exists on your machine. Nobody is affected. And if you mess it up, the reflog has your back (see #6).

"Don't rebase shared branches" is good advice. "Don't rebase" is not — it just keeps you scared of one of Git's most useful tools.

3. git pull Is Two Commands, Not One Safe Step

git pull felt like the safe, simple way to get updates. It's not simple, and it's not always safe.

git pull is two commands in a trench coat: git fetch followed by git merge (or git rebase, if you've configured it). (git-pull docs — "git pull runs git fetch ... and then ... git merge".)

That hidden merge is where the surprises live. If your local branch and the remote have both moved on, pull creates a merge commit you didn't ask for. If you have uncommitted changes that conflict, it stops half-finished and leaves you confused. The "simple" command did two things, and the second one had opinions.

What changed for me was separating the steps. git fetch first — that only downloads, it never touches your working files, so it's genuinely always safe. Then I look at what came in (git log HEAD..@{u}), and then I decide: merge or rebase. The decision is mine, made on purpose, instead of baked into a command I ran on autopilot.

4. Branches Are 41-Byte Pointers

I came to Git after a brief, unhappy time with older version-control tools where creating a branch was a big deal — slow, space-hungry, something you did a few times a year.

So I hoarded branches. I'd do too much work on one branch because making a new one felt expensive.

A Git branch is a file containing one line: the 40-character hash of a commit. That's it. Creating a branch writes 41 bytes. It's instant, and it costs nothing. (Pro Git, "Git Branching in a Nutshell" — "a branch in Git is simply a lightweight movable pointer".)

Branches are so cheap that the right habit is the opposite of hoarding: make one for every small thing. Trying an idea? Branch. Reviewing a colleague's work? Branch. Worried a change might not pan out? Branch — if it doesn't work, you just delete the pointer and the snapshots get cleaned up later. There's no cost to being generous with them.

5. HEAD Is a Cursor, Not the Latest Commit

I assumed HEAD was Git's word for the newest commit — the tip of everything.

HEAD means "where you are right now". Almost always, it's a pointer to the branch you're currently on (which in turn points at a commit). It's not "the latest commit in the repo" — it's "the commit your working directory currently reflects". (Pro Git, "Git Branching".)

This sounds like a small distinction. It isn't. Once I understood HEAD as a cursor — you are here — a whole family of commands stopped being scary. HEAD~1 is "one commit back from where I am". git reset --hard HEAD~3 is "move my current branch back three steps". "Detached HEAD" stopped being an error message I feared and became a plain fact: I'd pointed HEAD directly at a commit instead of at a branch, so I was standing on a commit with no branch name attached.

Git tells you where HEAD is all the time. I just hadn't understood what it was telling me.

6. The Reflog: Your Safety Net Against "Lost" Commits

The scariest belief, and the one that made me timid. I thought a bad reset, a deleted branch, or a botched rebase could destroy my work for good. So I treated history as fragile and avoided the commands that touch it.

Almost nothing is truly lost. Git keeps a journal called the reflog that records every time HEAD moved — every commit, checkout, reset, and rebase. The commits those entries point to survive in the object database for a while even after nothing else references them: roughly 90 days for commits still reachable, and 30 days for unreachable ones, before garbage collection is even allowed to remove them. (git-reflog docs, gc.reflogExpire config.)

That means a "lost" commit is usually just a git reflog and a git reset --hard away. I wrote a whole post on exactly how to do that recovery — it's the single highest-ROI thing I learned in Git.

Knowing the reflog exists changed how I work. I stopped being precious about history. I reorder, squash, and reset freely now, because I know the footprints are still there if I need to walk back.

The Pattern

Looking back, none of my problems were about commands. They were about the model. I'd memorised what to type without understanding what it did, so I couldn't reason about anything new.

If you take one thing from this: stop collecting commands. Build the model. Six ideas — snapshots, cheap branches, HEAD-as-cursor, safe-local-rebase, the two-step truth of pull, and the reflog safety net — and Git stops feeling like luck.

What to Read Next

If you want the model to build itself instead of reading about it, the Object Model lesson opens a live terminal where you can watch snapshots and pointers move in two minutes.