<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Git Internals Explained]]></title><description><![CDATA[Git Internals Explained]]></description><link>https://inside-git-how-it-works-internally.hashnode.dev</link><generator>RSS for Node</generator><lastBuildDate>Wed, 09 Sep 2026 14:26:58 GMT</lastBuildDate><atom:link href="https://inside-git-how-it-works-internally.hashnode.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Inside Git: How It Works and the Role of the .git Folder]]></title><description><![CDATA[Introduction
Git is powerful but often feels mysterious. When you use Git commands, you interact with a complex system running behind the scenes. Understanding what actually happens when you commit code deepens your knowledge and makes Git less confu...]]></description><link>https://inside-git-how-it-works-internally.hashnode.dev/inside-git-how-it-works-and-the-role-of-the-git-folder</link><guid isPermaLink="true">https://inside-git-how-it-works-internally.hashnode.dev/inside-git-how-it-works-and-the-role-of-the-git-folder</guid><category><![CDATA[wed development]]></category><category><![CDATA[bca]]></category><dc:creator><![CDATA[Avinash Sharma]]></dc:creator><pubDate>Thu, 29 Jan 2026 20:53:08 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1769720017826/311771c7-2167-49d7-80f1-0dfd2bb4edce.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-introduction">Introduction</h2>
<p>Git is powerful but often feels mysterious. When you use Git commands, you interact with a complex system running behind the scenes. Understanding what actually happens when you commit code deepens your knowledge and makes Git less confusing. This guide explains how Git works internally and what the hidden .git folder does.</p>
<hr />
<h2 id="heading-how-git-works-internally">How Git Works Internally</h2>
<p>Git is a version control system that tracks changes to files over time. Most importantly, Git is a content-addressable storage system. This means Git stores content and retrieves it based on the content itself.</p>
<p>When you create a file and commit it to Git, Git does not store the file name directly. Instead, Git calculates a hash (a unique fingerprint) of the file content. This hash is used as a unique identifier for that content. If the exact same content exists elsewhere, Git recognizes it immediately.</p>
<p>This design has powerful implications. Two files with identical content produce the same hash. If you rename a file without changing its content, Git still recognizes it is the same file. If content is duplicated across commits, Git stores it only once, saving storage space.</p>
<p>Git tracks content through commits. A commit is a snapshot of your project at a specific point in time. Each commit contains a reference to the previous commit, forming a chain. This chain is your project history. You can navigate backward through history by following the chain of commits.</p>
<p>Git uses cryptographic hashing (SHA-1) to ensure integrity. Every piece of data Git stores is hashed. If even one byte changes, the hash changes completely. This makes it impossible to alter history without Git detecting the change. If someone attempts to modify a commit, the hash changes, revealing the tampering.</p>
<p><img src="https://i.ytimg.com/vi/e9lnsKot_SQ/maxresdefault.jpg" alt="How Git Works: Explained in 4 Minutes" /></p>
<hr />
<h2 id="heading-understanding-the-git-folder">Understanding the .git Folder</h2>
<p>When you run <code>git init</code> in a project directory, Git creates a hidden .git folder. This folder is the entire repository. Everything Git does—tracking history, managing branches, storing commits—depends on what is inside .git.</p>
<p>The .git folder has a standard structure:</p>
<p><strong>HEAD file:</strong> Points to the current branch. When you switch branches, this file is updated to point to the new branch. When you check out a specific commit directly, HEAD points to that commit instead of a branch.</p>
<p><strong><mark>config file:</mark></strong> <mark> Stores repository configuration. Settings specific to this repository, like the remote URL (origin), are stored here. When you run </mark> <code>git remote add origin URL</code><mark>, this information is saved in config.</mark></p>
<p><strong>refs directory:</strong> Contains references to commits. Subdirectories include:</p>
<ul>
<li><p><mark>heads: Contains branch references. Each branch is a file containing the commit hash that branch points to.</mark></p>
</li>
<li><p><mark>remotes: Contains remote branch references. Tracks what branches exist on remote repositories.</mark></p>
</li>
<li><p><mark>tags: Contains tag references. Tags mark specific commits, usually for releases.</mark></p>
</li>
</ul>
<p><strong>objects directory:</strong> Stores all Git objects (blobs, trees, commits, tags). This is where the actual content lives. Objects are organized in subdirectories named after the first two characters of their hash for efficient lookup.</p>
<p><strong>index file:</strong> The staging area. When you run <code>git add</code>, Git writes information about your files to the index. The index tracks which files are staged for the next commit.</p>
<p><strong>logs directory:</strong> Stores reference logs. Git records when branches and HEAD change. You can view this history with <code>git reflog</code>.</p>
<p><strong><mark>hooks directory:</mark></strong> <mark> Contains scripts that run automatically at certain points in Git workflows. Hooks can run before commits, after pushes, etc.</mark></p>
<p>The .git folder is self-contained. Your entire project history exists in this folder. You can clone this folder to another location and have a complete copy of the repository.</p>
<p><img src="https://miro.medium.com/v2/resize:fit:1400/1*dvtRuUkYISe-Olyy1gqNeg.png" alt="Exploring The Magic Inside .git Folder: All You Need To Know About Git And  Its Files | by Anmol Agrawal | Medium" /></p>
<hr />
<h2 id="heading-git-objects-blob-tree-commit">Git Objects: Blob, Tree, Commit</h2>
<p>Git stores four types of objects. The first three are fundamental to understanding how Git works.</p>
<p><strong>Blob Object</strong></p>
<p>A blob is a file's content. When you add a file to Git, Git creates a blob object containing that file's data.</p>
<p>A blob contains only the file content. It does not contain the filename, file permissions, or any metadata. Two files with identical content create the same blob hash.</p>
<p>When you store a blob, Git calculates its hash based on the content. The hash becomes the blob's identifier. Later, when you need to retrieve that content, you use the hash.</p>
<p><strong>Tree Object</strong></p>
<p>A tree is a directory listing. It maps filenames to blob objects (files) and other tree objects (subdirectories).</p>
<p>A tree contains entries like:</p>
<ul>
<li><p>filename: hash of blob object</p>
</li>
<li><p>directoryname: hash of another tree object</p>
</li>
</ul>
<p>When you commit, Git creates a tree object representing your entire project structure at that moment. The tree maps all files and directories to their respective blob and tree objects.</p>
<p>If a file has not changed since the previous commit, its hash is the same. The tree references the same blob. Git does not duplicate the file content.</p>
<p><strong>Commit Object</strong></p>
<p>A commit ties everything together. A commit contains:</p>
<ul>
<li><p><mark>The hash of the tree object (what the project looks like)</mark></p>
</li>
<li><p><mark>The hash of the parent commit (the previous commit)</mark></p>
</li>
<li><p><mark>Author name and email</mark></p>
</li>
<li><p><mark>Committer name and email</mark></p>
</li>
<li><p><mark>Commit timestamp</mark></p>
</li>
<li><p><mark>Commit message</mark></p>
</li>
</ul>
<p>The commit hash is calculated from all this information. If any detail changes, the hash changes.</p>
<p>Commits form a linked list. Each commit points to its parent. By following parent pointers, you can traverse the entire history.</p>
<p><img src="https://thoughtbot-images.s3.amazonaws.com/upcase/git-course/git-base-object-model.png" alt="Git Object Model | Online Video Tutorial by thoughtbot" /></p>
<p><strong>Tag Object</strong></p>
<p>A tag marks a specific commit, usually for releases. A tag contains the commit it marks, the tag name, and optionally a message. Annotated tags are objects stored in the objects directory. Lightweight tags are simple references in refs/tags.</p>
<hr />
<h2 id="heading-how-git-tracks-changes">How Git Tracks Changes</h2>
<p>Understanding how Git tracks changes reveals the elegance of its design.</p>
<p><strong>When you run</strong> <code>git add</code>:</p>
<p>Git examines each file you are adding. Git calculates the hash of each file's content and creates blob objects in the objects directory.</p>
<p>Git updates the index (staging area). The index now contains references to the blob hashes of the files you added.</p>
<p>At this point, the blobs exist in the objects directory, but they are not yet part of a commit. The index tracks what will be included in the next commit.</p>
<p><strong>When you run</strong> <code>git commit</code>:</p>
<p>Git reads the index to see what files are staged.</p>
<p>Git creates a tree object that represents the current directory structure, mapping filenames to blob hashes (for files) and tree hashes (for directories).</p>
<p>Git creates a commit object that contains the tree hash, parent commit hash, author info, and your commit message. <mark>The commit hash is calculated from all this information.</mark></p>
<p>Git updates the current branch reference to point to the new commit hash. When you are on main, the file .git/refs/heads/main is updated to contain the new commit hash.</p>
<p><strong>How Git Computes Differences:</strong></p>
<p>Git does not store differences between commits (deltas). Instead, Git stores complete snapshots.</p>
<p>To see what changed between two commits, Git compares their tree objects. Git looks at what files exist in each tree and what blob hashes they reference. If a file has a different blob hash, the content changed. If a filename is missing in one tree, the file was deleted.</p>
<p>This snapshot approach seems wasteful, but it is actually efficient. If a file is unchanged, it references the same blob. The blob is stored only once. Multiple commits can reference the same blob.</p>
<p><strong>Ensuring Integrity:</strong></p>
<p>Every object is identified by its hash. If you change even one byte of an object, the hash changes. This makes it impossible to secretly modify history.</p>
<p>If someone attempts to change a commit, the commit hash changes. The parent pointer in the next commit no longer matches. The entire chain is broken, revealing the tampering.</p>
<p><img src="https://www.transifex.com/hubfs/Imported_Blog_Media/Gitflow-workflow-1-1.png" alt="How to Use Git to Track Changes in Translation Files" /></p>
<hr />
<h2 id="heading-the-mental-model">The Mental Model</h2>
<p>Think of Git as a filing system. Each file is stored in a cabinet (objects directory). Each file is labeled with a unique code (the hash).</p>
<p>When you create a folder structure (tree), you document what files are in each folder and reference them by their code.</p>
<p>When you create a snapshot (commit), you record what the folder structure looks like at that moment and note the previous snapshot.</p>
<p><mark>When you make a new snapshot, files that have not changed are referenced by their old code. New files get new codes. The new snapshot documents the changes.</mark></p>
<p>By following the chain of snapshots backward, you can see your project's entire history.</p>
<p><img src="https://substackcdn.com/image/fetch/$s_!nv6m!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F52cf76c0-c8dc-4be5-adda-482b1834ff4d_1080x686.png" alt="First Git Mental Model - by Bernhard Wenzel" /></p>
<hr />
<h2 id="heading-practical-example">Practical Example</h2>
<p>Consider a simple workflow:</p>
<p>You create a file hello.txt with content "Hello":</p>
<ul>
<li><p>Git creates blob hash abc123</p>
</li>
<li><p>You run <code>git add hello.txt</code></p>
</li>
<li><p>Index now references abc123 for hello.txt</p>
</li>
</ul>
<p><mark>You run </mark> <code>git commit -m "Add hello file"</code><mark>:</mark></p>
<ul>
<li><p><mark>Git creates tree object def456 that maps hello.txt to abc123</mark></p>
</li>
<li><p><mark>Git creates commit object ghi789 that references tree def456 and has no parent</mark></p>
</li>
<li><p><mark>.git/refs/heads/main is updated to ghi789</mark></p>
</li>
</ul>
<p>You modify hello.txt to "Hello World":</p>
<ul>
<li><p>Git creates new blob hash jkl012</p>
</li>
<li><p>You run <code>git add hello.txt</code></p>
</li>
<li><p>Index now references jkl012 for hello.txt</p>
</li>
</ul>
<p>You run <code>git commit -m "Update hello"</code><mark>:</mark></p>
<ul>
<li><p>Git creates tree object mno345 that maps hello.txt to jkl012</p>
</li>
<li><p>Git creates commit object pqr678 that references tree mno345 and parent ghi789</p>
</li>
<li><p>.git/refs/heads/main is updated to pqr678</p>
</li>
</ul>
<p>Now your history is a chain: ghi789 → pqr678. Both commits reference blob abc123 for the first file, but the second commit references jkl012. Storage is efficient because the first version is stored once.</p>
<hr />
<h2 id="heading-building-your-mental-model">Building Your Mental Model</h2>
<p>Rather than memorizing Git commands, understanding Git's internal model makes Git intuitive.</p>
<p><strong>Commits are snapshots</strong>, not changes. Each commit is a complete picture of your project at that moment.</p>
<p><strong><mark>Hashes identify content</mark></strong><mark>. The same content always produces the same hash. Different content produces different hashes.</mark></p>
<p><strong><mark>Branches are pointers</mark></strong><mark>. A branch is simply a file containing a commit hash. Switching branches changes which commit you are on.</mark></p>
<p><strong><mark>The index is staging</mark></strong><mark>. Before committing, you add files to the index. The index determines what will be in the next commit.</mark></p>
<p><strong><mark>History is a chain</mark></strong><mark>. Commits point to parent commits, forming a linked list. Traversing the chain shows your history.</mark></p>
<p><strong>Hashing ensures integrity</strong>. Changing history requires changing hashes, which breaks the chain.</p>
<p>Understanding these concepts makes Git commands make sense. You are not memorizing arbitrary commands. You are performing specific operations on Git's internal structures.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1769719787916/f67fd710-9bad-4bbe-a26a-c1c915f699a4.png" alt class="image--center mx-auto" /></p>
<hr />
<h2 id="heading-conclusion">Conclusion</h2>
<p>Git's internal design is elegant and powerful. The .git folder contains your entire project history. Git objects (blobs, trees, commits) represent files, directories, and snapshots. Hashes identify content and ensure integrity.</p>
<p>When you run Git commands, you are manipulating these structures. Understanding what happens internally transforms Git from a mysterious tool into a logical system.</p>
<p>Key takeaways:</p>
<ul>
<li><p><mark>The .git folder contains the entire repository</mark></p>
</li>
<li><p><mark>Git objects include blobs (file content), trees (directory structure), and commits (snapshots)</mark></p>
</li>
<li><p><mark>Commits are linked in a chain, forming history</mark></p>
</li>
<li><p><mark>Hashes ensure data integrity and enable efficient storage</mark></p>
</li>
<li><p><mark>Branches are simple pointers to commits</mark></p>
</li>
<li><p><mark>Understanding Git internally is more valuable than memorizing commands</mark></p>
</li>
</ul>
<p>As you continue using Git, refer back to this mental model. When something confuses you, thinking about the underlying structures often clarifies what Git is doing.</p>
<hr />
<h2 id="heading-quick-reference">Quick Reference</h2>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Component</td><td>Purpose</td></tr>
</thead>
<tbody>
<tr>
<td>Blob</td><td><mark>File content</mark></td></tr>
<tr>
<td>Tree</td><td>Directory listing</td></tr>
<tr>
<td>Commit</td><td><mark>Snapshot with parent reference</mark></td></tr>
<tr>
<td>Hash</td><td><mark>Unique identifier for content</mark></td></tr>
<tr>
<td>Branch</td><td><mark>Reference to a commit</mark></td></tr>
<tr>
<td>Index</td><td>Staging area</td></tr>
<tr>
<td>HEAD</td><td>Current location in history</td></tr>
<tr>
<td>.git folder</td><td><mark>The entire repository</mark></td></tr>
</tbody>
</table>
</div>]]></content:encoded></item></channel></rss>