Inside Git: How It Works and the Role of the .git Folder

Beyond the commands: a mental model of Git
Most developers learn Git as a set of commands to memorize: git add, git commit, git push. That gets you productive quickly — but it also means Git can feel like a black box, especially the moment something goes wrong and a command doesn't behave the way you expected. This article skips the command list entirely, and instead builds the mental model underneath it: what's actually sitting inside that .git folder, and what Git is genuinely doing every time you run one of those familiar commands.
Understanding the .git Folder
Every Git repository has a hidden .git folder at its root — and this folder is the actual repository. Your visible project files are just a working copy; the entire history, every commit, every branch, every piece of tracked content, lives inside .git.
.git/
HEAD
config
objects/
refs/
heads/
tags/
index
A few key pieces, at a conceptual level:
- objects/ — where Git actually stores all your content (covered in depth next) — every file version, every folder snapshot, every commit
- refs/ — pointers to specific commits, most commonly branches (refs/heads/) and tags (refs/tags/)
- HEAD — a pointer to whichever branch (or commit) you currently have checked out
- index — the staging area's actual internal representation — what you've told Git you want included in the next commit

Deleting .git doesn't just "reset" a project — it destroys the entire history, permanently. Understanding that this one folder is the repository is the first real step toward genuinely understanding Git.
Git Objects: Blob, Tree, Commit
Git stores everything as one of a small number of object types, each identified by a hash — a unique fingerprint computed directly from the object’s own content.
Blob: the content of a file
A blob stores the raw content of a single file — just the data itself, with no filename, no metadata, nothing else attached.

If two different files (even with completely different names) have identical content, Git stores that content as one single blob, referenced by both — because the blob is identified purely by its content, not by any filename.
Tree: a snapshot of a folder
A tree represents a directory — it lists the files and subdirectories it contains, pointing to the blob (for files) or another tree (for subdirectories) representing each one, along with the file/folder name and permissions.

This is where filenames actually live — blobs themselves don’t know their own names; it’s the tree that maps a name to a specific blob (or nested tree).
Commit: a snapshot in time, with context
A commit points to exactly one tree (representing the entire project’s state at that moment), along with metadata: the author, a timestamp, a commit message, and a pointer to its parent commit (or commits, for a merge) — which is exactly how Git reconstructs a project’s full history, one link at a time.

Putting all three together

Every commit in Git’s history is really this same small structure, repeated: a commit pointing to a tree, a tree pointing to blobs and further trees, forming a complete, addressable snapshot of the entire project at that exact point in time.
How Git Tracks Changes
Hashing ensures integrity
Every object’s identifier is a hash computed from its own content — meaning identical content always produces the identical hash, and any change to content — even a single character — produces a completely different hash. This is exactly how Git detects that something changed, and exactly why Git can trust that a given commit hash represents one specific, unambiguous, tamper-evident state of the project.

What happens internally during git add

git add doesn't move or copy your file anywhere visible — it computes the file's content hash, stores a blob object for that exact content (if Git hasn't already seen this exact content before), and records in the index that this blob should be included in the next commit.
What happens internally during git commit

git commit takes everything currently staged in the index, builds the corresponding tree structure (and any necessary blobs for genuinely new content), creates a commit object tying that tree to a message and a parent pointer, and finally moves the current branch's pointer forward to this brand-new commit.
Why this design matters
Because objects are identified purely by content hash, Git automatically deduplicates identical content across your entire history — the same unchanged file across a hundred commits is stored as one blob, referenced a hundred times, not duplicated a hundred times. And because every commit links to its parent, Git’s entire history is really a chain of these small, verifiable, content-addressed snapshots — not a series of incremental “diffs” being stored and replayed.

Final Takeaway
Underneath every familiar Git command sits the same small, elegant idea: everything is an object, every object is identified by a hash of its own content, and a commit is simply a snapshot — a tree of blobs — with a pointer back to whatever came before it. git add stages content into the index as blobs; git commit turns that staged content into a tree, wraps it in a commit object, and moves your branch forward to point at it. The .git folder isn't a mysterious cache — it's the literal, complete home of every object your project has ever contained. Once this structure clicks, Git commands stop feeling like magic incantations and start feeling like exactly what they are: straightforward operations on a graph of hashed, content-addressed objects.
Frequently Asked Questions
Does Git store a full copy of every file for every single commit?
> Not wastefully — because objects are identified by content hash, an unchanged file across many commits is stored as one single blob, referenced by every tree that includes it, rather than being duplicated for each commit.
What exactly is a Git hash based on?
> It’s computed from the object’s own content (and, for commits, its metadata like message and parent) — this is why identical content always produces the identical hash, and any change at all produces a completely different one.
Is the .git folder something I should ever edit directly?
> Generally, no — it’s meant to be managed through Git’s own commands, which maintain its internal consistency carefully. Directly editing its contents can corrupt the repository, though understanding its structure conceptually (as covered here) is genuinely valuable for building real intuition.
Why does understanding blobs, trees, and commits actually help in day-to-day Git use?
> Because many of Git’s more advanced or confusing behaviors — like how merges work, why some operations are fast, or what a “detached HEAD” state means — make much more sense once you can picture the underlying object graph, rather than treating each command as an isolated, memorized recipe.
Originally published by Mr Madhukar
Read the complete article on Medium with full formatting & reader responses.