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

*“I know how to use git commands… but what actually happens inside git?”*

This was the exact question I had after getting comfortable with commands like `git add` and `git commit`.

In the previous blogs, we discussed about [Why version control exists](https://codeandcuriosity.hashnode.dev/the-pendrive-problem) and [How to use git as a beginner](https://codeandcuriosity.hashnode.dev/git-for-beginners). In this article, we’ll go one level deeper and not get confuse but build a clear mental model of how git works internally.

No fancy theory. No unnecessary depth. Just enough to just to understand git more better.

## Why Should we care about git internals?

We don’t need to know git internals to use git.

But understanding them helps us in:

* Stop memorizing commands blindly
    
* Debug git issues with confidence
    
* Truly understand what git is doing for you
    

Once this clicks, git feels far less magical and far more logical.

## The most important folder in git: `.git`

When we want git to track a particular file or folder we run this command:

`git init`

This creates a hidden folder called:

`.git/`

This is the heart of git, the entire repository lives here.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1768589987142/12ba9fc4-5ba6-4b37-83b6-564faa24388c.png align="center")

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1768590004100/e2a3083a-8ba8-452c-a80c-44c209cbcaee.png align="center")

If we delete the .git folder:

* The code stays
    
* But git history is completely gone
    

Which means:

Without `.git`, the project is not longer a git repository.

## What’s Inside the `.git` Folder?

When we open the `.git`, we can see something like:

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1768590110728/380713e5-dc81-4873-a891-2c1beca18470.png align="center")

This folders and the files inside it look veryy overwhelming right!? I too felt the same when i first saw them, we don’t need to understand everything actually.

Let’s focus on what actually matters.

## The Most Important Parts of .git

1. ### `HEAD`
    
    HEAD tells git:
    
    “Where am I right now?”
    
    It points to:
    
    The current branch, or the current commit
    
    Whenever we switch branches or commit code, HEAD moves
    
    ![](https://cdn.hashnode.com/res/hashnode/image/upload/v1768590521001/91053e4b-15b4-4c05-acdc-0ef10acc3e07.png align="center")
    
2. ### `objects/` - Git’s Database
    
    This is where git stores everything.
    
    The files, commits, branches, all of it actually lives here.
    
    Inside `objects/`, git stores data as **objects**, identified by **hashes**.
    
    ![](https://cdn.hashnode.com/res/hashnode/image/upload/v1768590958012/a1b2bcf0-4c26-4995-b9db-5f1c9060736f.png align="center")
    
3. ### `index` - The Staging Area
    
    The `index` file represents the `staging area.`
    
    When you run:
    
    `git add file.js`
    
    ![](https://cdn.hashnode.com/res/hashnode/image/upload/v1768591120014/de645f35-8470-4e4c-81df-fdf3a1b67e83.png align="center")
    
    git updates the `index`.
    
    This is how git knows what will go into the next commit.
    

## Git Objects

Technical Insight: Git internally uses immutable (unable to change), hash-linked data structures that closely resembles **linked-lists** and **trees**. Understanding this makes git feel more easy to understand and logical.

At a low level, git is essentially a **content-addressable database** built on top of simple data structures.

### Commits as a Linked List (or DAG)

Each commit stores:

* A reference (hash) to its parent commit
    
* A reference to a tree object (snapshot of the project)
    
* Metadata (author, time, message)
    

This parent reference means commits are connected like a linked list:

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1768592371004/41177a20-9ab0-4d2e-8b90-c9c05277c36b.png align="center")

If a commit has multiple parents (like after a merge), the structure becomes a Directed Acyclic Graph (DAG)

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1768592697169/dda1f77c-9ffd-4e80-b035-5bb47e8a99aa.png align="center")

So when we run commands like git log, git is literally walking this linked structure backwards.

### Trees as Hierarchical Data Structures

A tree object represents a directory and contains:

* References to blobs (files)
    
* References to other trees (subdirectories)
    

This forms a classic tree data structure:

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1768593162554/fdd7864e-2e49-43fe-b9ad-4f9844a2db35.png align="center")

Each commit points to exactly **one root tree,** which recursively points to everything else.

### Blobs: Pure Data Nodes

Blogs are the simplest objects:

* They store file content only
    
* No filename, no path
    
* Identified by a hash
    

This design is why git can efficiently detect duplicate content and avoid storing it twice.

### Why these data structures matter

Because git uses:

* Linked references (commits → parents)
    
* Tree hierarchies (directories)
    
* Hash-based immutability
    

It gains:

* Fast history traversal
    
* Safe branching and merging
    
* Guaranteed integrity (no silent corruption)
    

This is why git feels so powerful compared to the old pendrive or folder-based workflows.

Once we see git as data structures + hashes, everything starts to click. The core of git stores everything as objects. There are **three main types** that we should know.

1. ### Blob (File Content)
    
    A blob stores the content of a file, not the filename.
    
    Example:
    
    `console.log(“Hello World”)`
    
    If 2 files have the same content, git stores only one blob.
    
    This makes git efficient.
    
2. ### Tree (Directory Structure)
    
    A **tree** represents:
    
    A folder, File names and folder hierarchy
    
    It Links:
    
    Filenames → blobs
    
    Subfolders → other trees
    
    Think of a tree as:
    
    A snapshot the project structure
    
3. ### Commit (Snapshot + Metadata)
    
    A commit message contains:
    
    A reference to a tree
    
    Author information
    
    Commit message
    
    Parent commits
    

### How commits, trees, and blobs are connected

Commit → Tree → Blobs (file contents)

This simple structure powers everything in git.

## How git tracks changes (important concept)

Git does not store diffs like traditional systems.

Instead, git stores:

Snapshots of your project at each commit

But because blobs are reused, git remains efficient.

## What Actually happens during `git add`

Let’s say we edit a file:

`app.js`

Then run:

`git add app.js`

Internally, git:

1. Creates a blob from file content
    
2. Stores it in `objects/`
    
3. Updates the `index` (staging area)
    

No commit yet, just preparation

## What happens during `git commit`

When we run:

`git commit -m “initial commit”`

git:

1. Creates a tree from the staging area
    
2. Creates a commit object pointing to that tree
    
3. Updates HEAD to point to the new commit
    

That’s it. Now it sounds somewhat understandable?

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1768596007740/e3ba1265-8a95-4367-93e5-b8f3ddf88481.png align="center")

## How git uses Hashes

Git uses SHA-1 hashes (40-character hashes) to identify objects.

Each hash is based on:

* Object content
    
* Object type
    

If content changes → hash changes.

This means:

* Data corruption is detectable
    
* History cannot be silently altered
    

Now I can truly say that git is built on trust through mathematics

## Advanced but beginner-friendly git internals

### Git refs: How git knows where you are

Refs (short for references) are simple pointer that tell git which commit is important.

We can find them inside:

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1768596740809/84ab650c-261e-4db0-8991-fb6992c4d24c.png align="center")

Each ref file contains a commit hash.

Example:

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1768596834794/43893bf9-d54c-44c6-8b36-09ae6973f16d.png align="center")

This means:

* “The branch `main` currently points to this commit.”
    

So:

* Branches are just pointers
    
* Switching branches = moving pointers
    
* HEAD points to one of these refs
    

This is why branching in git is cheap and fast, no file copying involved.

### git logs folder: the safety net

Git keeps detailed logs of reference changes inside:

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1768597091964/63be6413-6cd5-40b6-9570-af6de9d62251.png align="center")

These logs record:

* Every checkout
    
* Every commit
    
* Every reset
    
* Every rebase
    

This is why commands like:

`git reflog`

can recover lost commits even after:

* `git reset --hard`
    
* deleting branches
    
* accidental mistakes
    

git rarely loses data because it logs almost everything.

### git hooks

git hooks are scripts that run automatically on certain git events.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1768597296917/4b044b30-0a3a-4de2-a4cb-e5060e000e72.png align="center")

Common hooks include:

* pre-commit → run before a commit is created
    
* commit-msg → validate commit messages
    
* pre-push → run before pushing to remote
    

Example use cases:

* Prevent commits with failed tests
    
* Block bad commit messages
    

Hooks are:

* Local by default
    
* Written in shell, Python, node, etc
    

### How all this fits together

Refs (branches, tags) → HEAD → Commit (linked list / DAG) → Tree (directory structure) → blob (file content)

And everything is:

* Stored by hash
    
* Logged for safety
    
* Scriptable via hooks
    

## Conclusion

Instead of thinking:

* “git saves my files”
    

Think:

* “git stores content-addressed snapshots with references”
    

Once we see git this way, commands start making sense naturally.

Understanding git internals doesn’t make us git expert overnight, but it changes hot we think about version control.
