<?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 Isn't Magic-Here's What's Actually Happening Under the Hood]]></title><description><![CDATA[Git Isn't Magic-Here's What's Actually Happening Under the Hood]]></description><link>https://sarveshmohite.hashnode.dev</link><generator>RSS for Node</generator><lastBuildDate>Sat, 24 Oct 2026 14:43:56 GMT</lastBuildDate><atom:link href="https://sarveshmohite.hashnode.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Git Isn't Magic-Here's What's Actually Happening Under the Hood]]></title><description><![CDATA[You probably use Git every day. I do. You run git add, git commit, git push. Most of the time it just works. But the moment something goes wrong — merge…]]></description><link>https://sarveshmohite.hashnode.dev/git-isn-t-magic-here-s-what-s-actually-happening-under-the-hood</link><guid isPermaLink="true">https://sarveshmohite.hashnode.dev/git-isn-t-magic-here-s-what-s-actually-happening-under-the-hood</guid><dc:creator><![CDATA[Sarvesh Mohite]]></dc:creator><pubDate>Thu, 26 Mar 2026 13:35:18 GMT</pubDate><content:encoded><![CDATA[<p>You probably use Git every day. I do. You run <code>git add</code>, <code>git commit</code>, <code>git push</code>. Most of the time it just works. But the moment something goes wrong — merge conflicts, weird states, lost commits — Git suddenly feels… unpredictable.</p>
<p>That's not because Git is complicated. It's because we usually learn Git as a set of commands, not as a system. Once you see what Git actually stores and how it links things together, it stops feeling like magic.</p>
<h2>The shift that makes Git click</h2>
<p>Here's the idea that changes everything: Git is a content-addressable database. It stores data, computes a cryptographic hash for that data, and uses those hashes as addresses. Objects reference other objects by hash, which gives Git immutability, cheap snapshots, and global deduplication.</p>
<h2>The 3 building blocks of Git</h2>
<p>Everything in Git is built from three object types.</p>
<ol>
<li><p>Blob — file contents</p>
<ul>
<li><p>A blob is just the raw contents of a file.</p>
</li>
<li><p>No filename, no mode, no metadata — just the bytes.</p>
</li>
<li><p>If two files have identical contents, Git stores only one blob and reuses it.</p>
</li>
</ul>
<p>Example:</p>
<pre><code class="language-bash">echo "hello world" &gt; a.txt
# write the blob to the object database and print its hash
git hash-object -w a.txt
# inspect contents of the object
git cat-file -p &lt;blob-sha&gt;
</code></pre>
<p>If you create another file with the same content and run <code>git hash-object -w</code> on it, you’ll get the same SHA.</p>
</li>
<li><p>Tree — directory snapshot</p>
<ul>
<li><p>A tree records filenames → blob SHA and subdirectory names → tree SHA.</p>
</li>
<li><p>Think of a tree as a snapshot of a directory structure (file names and modes included).</p>
</li>
</ul>
<p>Examples:</p>
<pre><code class="language-bash"># show the tree for the current commit
git ls-tree HEAD

# after staging files, create a tree object
git add .
git write-tree   # prints a tree SHA

# inspect the tree contents
git cat-file -p &lt;tree-sha&gt;
</code></pre>
</li>
<li><p>Commit — snapshot + metadata</p>
<ul>
<li><p>A commit points to a tree and records metadata: author, committer, message, timestamp, and 0+ parent commit SHAs.</p>
</li>
<li><p>A commit is not a "diff" or a set of changes — it's a pointer to a full snapshot (the tree) plus context about how that snapshot was created.</p>
</li>
</ul>
<p>Example:</p>
<pre><code class="language-bash"># create a commit object that points to an existing tree
git commit-tree &lt;tree-sha&gt; -p &lt;parent-sha&gt; -m "Message"
# inspect the commit
git cat-file -p &lt;commit-sha&gt;
</code></pre>
</li>
</ol>
<h2>Look under the hood: plumbing commands</h2>
<p>Porcelain commands (<code>git add</code>, <code>git commit</code>, <code>git checkout</code>, etc.) are convenient high-level commands. Underneath them, Git uses lower-level "plumbing" commands you can use to inspect and manipulate objects directly.</p>
<p>Useful plumbing commands:</p>
<pre><code class="language-shell">git hash-object  # create a blob (optionally write to object DB with -w)
git cat-file     # show object type or pretty-print its contents (e.g., -t, -p)
git ls-tree      # list a tree object
git write-tree   # write index to a tree object and print SHA
git commit-tree  # create a commit object from a tree SHA
git update-ref   # update a ref to point at a commit SHA
git rev-parse    # resolve revision names to SHAs
</code></pre>
<p>A quick plumbing example: create a commit without <code>git commit</code></p>
<pre><code class="language-shell"># 1. stage files
git add file1 file2

# 2. write a tree from the index
tree_sha=$(git write-tree)

# 3. create a commit pointing to that tree
commit_sha=\((echo "Commit created via plumbing" | git commit-tree \)tree_sha -p $(git rev-parse HEAD))

# 4. update a branch to point to the new commit
git update-ref refs/heads/tmp-branch $commit_sha

# inspect
git cat-file -p $commit_sha
</code></pre>
<h2>Where objects live</h2>
<p>Objects are stored under <code>.git/objects/</code> using their SHA: the first two hex chars are a directory, the remaining chars are the filename. Each object is zlib-compressed and stores a header (type + size) followed by the raw content.</p>
<p>For example:</p>
<pre><code class="language-shell">.git/objects/ab/cdef1234...  # object with sha ab cdef1234...
</code></pre>
<h2>Why this model matters</h2>
<ul>
<li><p>Snapshots, not diffs: every commit points to a full tree, which makes branching and switching cheap — Git simply points refs at different commits.</p>
</li>
<li><p>Deduplication: identical file contents are stored once (blobs are content-addressed).</p>
</li>
<li><p>Integrity: hashes provide tamper detection — changing an object changes its SHA and breaks references.</p>
</li>
<li><p>Rewriting and recovery: because commits are immutable and linked, you can reason about parent-child relationships (and use tools like reflog to recover lost refs).</p>
</li>
</ul>
<h2>How this helps when things go wrong</h2>
<ul>
<li><p>Merge conflicts: knowing commits are snapshots helps you reason about three-way merges — Git compares trees by their SHAs.</p>
</li>
<li><p>Lost commits: if you lost a branch, <code>git reflog</code> and <code>git fsck --lost-found</code> can help locate dangling commits (their SHAs still exist until garbage collection).</p>
</li>
<li><p>Rebase vs merge: rebase creates new commits with different SHAs (rewriting history); merge creates a commit with two parents.</p>
</li>
</ul>
<h2>Practical next steps</h2>
<ul>
<li><p>Try the plumbing commands in a throwaway repository to see the raw objects.</p>
</li>
<li><p>Use <code>git cat-file -t &lt;sha&gt;</code> and <code>git cat-file -p &lt;sha&gt;</code> to inspect objects.</p>
</li>
<li><p>Read the "git internals" chapter of the Pro Git book for a deeper dive.</p>
</li>
</ul>
<h2>Conclusion</h2>
<p>Git isn’t magic — it’s a simple, elegant content-addressable database built from blobs, trees, and commits. Once you internalize those primitives and a few plumbing commands, many mysterious behaviors become predictable and understandable.</p>
]]></content:encoded></item></channel></rss>