Profile    Mohammed Shiroz Status   Loading  
Logo
Share This
Back to blog
Filter by:
Tags
//Article title

Git Under the Hood: Commits, Branches and HEAD Explained

About Post

Most of us learned Git as a list of spells. add, commit, push. When something goes wrong, we search for a different spell, paste it, and hope. And when the terminal says "You are in 'detached HEAD' state", it sounds less like a status message and more like a diagnosis.

The secret is that Git's core model is small. Three or four ideas explain almost everything, including the scary messages. Once you see them, rebase stops being magic and the reflog becomes your undo button for nearly any mistake.

Let's go myth by myth.

Myth: a commit stores the changes you made

Reality: a commit is a snapshot of the whole project.

It feels like a commit is a diff, because that's what git show displays. But Git computes that diff on the fly by comparing two snapshots. What it actually stores for each commit is a pointer to a complete picture of your files at that moment.

You can look inside a commit yourself:

$ git cat-file -p HEAD
tree 9c4e1f...
parent 5a7d2b...
author Mohammed Shiroz ...
committer Mohammed Shiroz ...

Add idempotency middleware to payments

A commit is a small text object: a tree (the snapshot of the folder structure), its parent commit, author information and the message. The tree points to blobs, which hold file contents.

Isn't storing full snapshots wasteful? No, because every object is named by a hash of its content. If README.md didn't change between two commits, both trees point to the exact same blob. Only changed files produce new objects. (Git also compresses objects into pack files behind the scenes, but that's a storage detail. The model is snapshots.)

Myth: a branch is a copy of the code

Reality: a branch is a sticky note with a commit hash on it.

That's genuinely all it is. A branch is a tiny file in .git/refs/heads/ that contains one commit hash:

$ cat .git/refs/heads/main
e83c5163316f89bfbde7d9ab23ca2e25604af290

(In some repositories, refs get packed into .git/packed-refs instead, but the idea is the same.)

That's why creating a branch is instant, even in a huge repository. Nothing is copied. Git just writes a new sticky note pointing at the current commit. When you commit on a branch, Git creates the new commit and moves that branch's note forward to point at it.

The history itself isn't stored "in" the branch at all. It's the chain of parent pointers between commits. A branch just marks where one particular line of work currently ends.

Myth: HEAD is the latest commit

Reality: HEAD is "you are here".

HEAD tells Git what you currently have checked out. Normally, it doesn't point to a commit directly. It points to a branch:

$ cat .git/HEAD
ref: refs/heads/main

So the chain is: HEAD → main → a commit. When you commit, the branch moves forward, and HEAD comes along because it points to the branch.

So what is "detached HEAD"?

Detached HEAD just means HEAD points directly at a commit instead of at a branch. It happens when you check out a specific commit hash or a tag, or in the middle of some rebases.

Nothing is broken. You can look around, run the code, even commit. The catch: new commits made here don't belong to any branch. There's no sticky note marking them. If you switch away, they become hard to find, and Git will eventually clean up unreachable commits.

The fix is to give them a sticky note before you leave:

git switch -c fix-from-old-release

Remember: commits that no branch or tag points to are not deleted immediately, but they are invisible. Naming them with a branch is what makes them safe.

Why rebase "rewrites history"

Here's the piece that ties it all together. A commit's hash is calculated from its content, and that content includes the parent's hash.

So when you rebase your feature branch onto a newer main, Git can't just move your commits. Their parent changes, which means their content changes, which means their hash changes. Rebase actually creates brand-new commits with the same changes and messages, then moves your branch's sticky note to the new ones.

The old commits still exist, quietly, with nothing pointing at them.

This explains the golden rule: don't rebase commits other people have already pulled. They still have the old commits. Your branch now has different ones. Git sees two unrelated histories, and the next merge gets messy. If you do need to push a rebased branch you own, use git push --force-with-lease, which refuses to overwrite work you haven't seen.

The reflog: Git's undo history

Remember those old commits with nothing pointing at them? Git keeps a private diary of where HEAD and your branches have pointed recently. That's the reflog.

$ git reflog
a1b2c3d HEAD@{0}: rebase (finish): returning to refs/heads/feature
f4e5d6c HEAD@{1}: rebase (pick): Add payment retry
9a8b7c6 HEAD@{2}: commit: Add payment retry
...

Botched a rebase? Ran git reset --hard on the wrong branch? Find the entry from before the mistake and point a branch back at it:

# Look at the state before the rebase
git log --oneline 9a8b7c6

# Put the branch back there
git reset --hard 9a8b7c6

# Or keep both and rescue it on a new branch
git branch rescued-work 9a8b7c6

A few things to know about the reflog:

  • It's local. It's not pushed, and a fresh clone has an empty one.
  • Entries expire eventually, so it's a safety net for recent mistakes, not an archive.
  • It can't recover changes that were never committed. Uncommitted work wiped by reset --hard is gone. One more reason to commit often, even on a messy local branch.

The whole model on a napkin

  • Commit = snapshot + parent + message, named by a hash of all that.
  • Branch = a movable sticky note pointing at a commit.
  • HEAD = "you are here", usually pointing at a branch.
  • Detached HEAD = you're on a commit with no sticky note.
  • Rebase = make new commits, move the note, leave the old ones behind.
  • Reflog = the diary of where the notes used to be.

If you want to go further, the Git Internals chapter of Pro Git is free and surprisingly readable.

What's the Git mistake that taught you the most? Bonus points if the reflog saved you from it.

Comments (0)
Leave your review

Thanks for your valuable comments. Your comments has been updated and appreciate your getting in touch...

01. About Shiroz

Mohammed Shiroz

Hi, I'm Mohammed Shiroz, a software engineer and AI enthusiast from Sri Lanka who turns ideas into intelligent, real-world solutions. With over 9 years of hands-on experience, I currently lead real estate ERP development at Kate Group, a...

03.My Projects

04. Categories

Ready To order Your Project ?

Get in Touch
Close