<?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[// TODO: Fix Title]]></title><description><![CDATA[This blog is a collection of technical notes on the challenges I've faced and the things I've learned. It helps me solve similar problems more efficiently in th]]></description><link>https://notes.hunterko.dev</link><generator>RSS for Node</generator><lastBuildDate>Wed, 29 Apr 2026 20:59:45 GMT</lastBuildDate><atom:link href="https://notes.hunterko.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Offline Git Collaboration: Bundles vs. Patches]]></title><description><![CDATA[When working with Git, we typically rely on a central server like GitHub or GitLab. However, in scenarios like air-gapped networks, unstable internet, or transferring data via USB, a direct connection is impossible. In these cases, Git provides two p...]]></description><link>https://notes.hunterko.dev/offline-git-collaboration-bundles-vs-patches</link><guid isPermaLink="true">https://notes.hunterko.dev/offline-git-collaboration-bundles-vs-patches</guid><category><![CDATA[notes]]></category><dc:creator><![CDATA[ko hunter]]></dc:creator><pubDate>Sat, 24 Jan 2026 12:35:10 GMT</pubDate><content:encoded><![CDATA[<p>When working with Git, we typically rely on a central server like GitHub or GitLab. However, in scenarios like <strong>air-gapped networks</strong>, <strong>unstable internet</strong>, or <strong>transferring data via USB</strong>, a direct connection is impossible. In these cases, Git provides two powerful offline solutions: <strong>Git Bundle</strong> and <strong>Git Patch</strong>.</p>
<hr />
<h2 id="heading-1-git-bundle-git-bundle">1. Git Bundle (<code>git bundle</code>)</h2>
<p>A Git bundle is a single binary file that acts exactly like a remote repository. It packs objects, references, and history into one file.</p>
<ul>
<li><p><strong>How it works:</strong> You treat the <code>.bundle</code> file exactly like a remote server (<code>origin</code>). You can clone, fetch, and pull from it.</p>
</li>
<li><p><strong>Key Advantage:</strong> It preserves the <strong>full Git graph</strong> (DAG). This allows Git to perform smart merges, detect renames, and resolve conflicts using history.</p>
</li>
<li><p><strong>Best Use Case:</strong> Syncing branches, transferring large history, or backing up repositories.</p>
</li>
</ul>
<h3 id="heading-on-the-source-computer-sender">On the Source Computer (Sender)</h3>
<pre><code class="lang-bash"><span class="hljs-comment"># Syntax: git bundle create &lt;filename&gt; &lt;branch_name_or_range&gt;</span>

<span class="hljs-comment"># 1. Full Backup: Bundle the 'master' branch (and full history)</span>
git bundle create repo.bundle master

<span class="hljs-comment"># 2. Bundle all branches and tags</span>
git bundle create all-changes.bundle --all

<span class="hljs-comment"># 3. PRO TIP: Incremental Bundle (Save space)</span>
<span class="hljs-comment"># Only bundle commits added since the last time you synced (e.g., since 'v1.0')</span>
<span class="hljs-comment"># Note: The receiver MUST have the 'v1.0' commit for this to work.</span>
git bundle create update.bundle v1.0..master
</code></pre>
<h3 id="heading-on-the-target-computer-receiver">On the Target Computer (Receiver)</h3>
<pre><code class="lang-bash"><span class="hljs-comment"># 1. Verify the file is valid and you have the prerequisite commits</span>
git bundle verify repo.bundle

<span class="hljs-comment"># 2. See what branches are inside</span>
git bundle list-heads repo.bundle

<span class="hljs-comment"># 3. Safer Method: Fetch into a temporary branch first</span>
<span class="hljs-comment"># This lets you inspect changes before merging</span>
git fetch repo.bundle master:temp-branch

<span class="hljs-comment"># 4. Integrate changes (Standard Git workflow)</span>
git checkout master
git rebase temp-branch
<span class="hljs-comment"># or</span>
git merge temp-branch
</code></pre>
<hr />
<h2 id="heading-2-git-patch-git-format-patch">2. Git Patch (<code>git format-patch</code>)</h2>
<p>A Git patch is a text-based file containing the differences (diffs) and metadata (author, date, message) for specific commits.</p>
<ul>
<li><p><strong>How it works:</strong> It generates email-friendly text files. The recipient uses <code>git am</code> (Apply Mailbox) to reconstruct the commits.</p>
</li>
<li><p><strong>Limitation:</strong> Patches are <strong>"brittle."</strong> They rely on context (line numbers and surrounding text). If the file structure on the target differs (e.g., renamed files), the patch often fails.</p>
</li>
<li><p><strong>Binary Files:</strong> While modern patches <em>can</em> include binary data, it is inefficient and bloats the text file. Bundles are preferred for binaries.</p>
</li>
<li><p><strong>Best Use Case:</strong> Sending single bug fixes, code reviews via email, or applying a commit to a disconnected codebase.</p>
</li>
</ul>
<h3 id="heading-on-the-source-computer-sender-1">On the Source Computer (Sender)</h3>
<pre><code class="lang-bash"><span class="hljs-comment"># 1. Create a patch for the very last commit</span>
git format-patch -1 HEAD

<span class="hljs-comment"># 2. Create patches for all commits not on origin/master</span>
git format-patch origin/master

<span class="hljs-comment"># 3. Create a patch for a specific commit hash</span>
git format-patch -1 &lt;commit-hash&gt;
<span class="hljs-comment"># (Output: 0001-fix-login-bug.patch)</span>
</code></pre>
<h3 id="heading-on-the-target-computer-receiver-1">On the Target Computer (Receiver)</h3>
<pre><code class="lang-bash"><span class="hljs-comment"># 1. Dry Run: Check statistics without applying</span>
git apply --<span class="hljs-built_in">stat</span> 0001-fix-login-bug.patch

<span class="hljs-comment"># 2. Check for errors/conflicts</span>
git apply --check 0001-fix-login-bug.patch

<span class="hljs-comment"># 3. APPLY: The Correct Way (Preserves Author/Date)</span>
git am 0001-fix-login-bug.patch

<span class="hljs-comment"># 4. Troubleshooting: If conflict occurs</span>
git am --abort
</code></pre>
<blockquote>
<p>CRITICAL WARNING:</p>
<p>Do not use git apply to merge the code unless you want to lose the commit history.</p>
<ul>
<li><p><code>git apply</code>: Changes the files in your working directory (Author = You, Date = Now).</p>
</li>
<li><p><code>git am</code>: Reconstructs the actual commit (Author = Original Sender, Date = Original Date).</p>
</li>
</ul>
</blockquote>
<hr />
<h2 id="heading-3-summary-which-one-should-you-choose">3. Summary: Which one should you choose?</h2>
<div class="hn-table">
<table>
<thead>
<tr>
<td><strong>Feature</strong></td><td><strong>Git Bundle</strong></td><td><strong>Git Patch</strong></td></tr>
</thead>
<tbody>
<tr>
<td><strong>Data Type</strong></td><td>Binary (Packfile + Refs)</td><td>Text (Diffs + Metadata)</td></tr>
<tr>
<td><strong>Context Awareness</strong></td><td><strong>High</strong> (Uses history graph)</td><td><strong>Low</strong> (Uses line numbers/text)</td></tr>
<tr>
<td><strong>Renamed Files</strong></td><td>Handles automatically</td><td>Usually fails</td></tr>
<tr>
<td><strong>Prerequisites</strong></td><td><strong>Strict</strong> (Receiver needs basis commit if incremental)</td><td><strong>Loose</strong> (Receiver just needs matching file content)</td></tr>
<tr>
<td><strong>Commit History</strong></td><td>100% Preserved (GPG sigs, etc.)</td><td>Preserved only if using <code>git am</code></td></tr>
<tr>
<td><strong>Recommended For</strong></td><td><strong>Syncing</strong> branches or large updates.</td><td><strong>Communication</strong> (Email reviews, single fixes).</td></tr>
</tbody>
</table>
</div>]]></content:encoded></item></channel></rss>