<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Worktree on recca0120 Tech Notes</title><link>https://recca0120.github.io/en/tags/worktree/</link><description>Recent content in Worktree on recca0120 Tech Notes</description><generator>Hugo -- gohugo.io</generator><language>en</language><lastBuildDate>Tue, 14 Apr 2026 22:00:00 +0800</lastBuildDate><atom:link href="https://recca0120.github.io/en/tags/worktree/index.xml" rel="self" type="application/rss+xml"/><item><title>git worktree: Multiple Working Directories Per Repo, and the Key to Parallel AI Agents</title><link>https://recca0120.github.io/en/2026/04/14/git-worktree-parallel-work/</link><pubDate>Tue, 14 Apr 2026 22:00:00 +0800</pubDate><guid>https://recca0120.github.io/en/2026/04/14/git-worktree-parallel-work/</guid><description>&lt;img src="https://recca0120.github.io/" alt="Featured image of post git worktree: Multiple Working Directories Per Repo, and the Key to Parallel AI Agents" /&gt;&lt;p&gt;You&amp;rsquo;re mid-feature when PM says prod is on fire. The reflex move: &lt;code&gt;git stash&lt;/code&gt;, check out master, fix, come back, &lt;code&gt;stash pop&lt;/code&gt;. But then the dev server restarts, the IDE re-indexes, and stash silently ate your untracked build artifacts.&lt;/p&gt;
&lt;p&gt;git worktree fixes this cleanly: &lt;strong&gt;one repo, many checked-out branches in separate directories&lt;/strong&gt;, each with its own HEAD, index, and untracked files — all sharing the same object database. The feature stays exactly where you left it; the hotfix happens next door.&lt;/p&gt;
&lt;h2 id="vs-stash-vs-multiple-clones"&gt;&lt;a href="#vs-stash-vs-multiple-clones" class="header-anchor"&gt;&lt;/a&gt;vs stash, vs multiple clones
&lt;/h2&gt;&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Approach&lt;/th&gt;
 &lt;th&gt;Switch cost&lt;/th&gt;
 &lt;th&gt;Disk&lt;/th&gt;
 &lt;th&gt;Object sync&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;git stash&lt;/code&gt; + checkout&lt;/td&gt;
 &lt;td&gt;High (dev server restarts, IDE reindex)&lt;/td&gt;
 &lt;td&gt;Cheap&lt;/td&gt;
 &lt;td&gt;Single repo&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Multiple clones&lt;/td&gt;
 &lt;td&gt;Low&lt;/td&gt;
 &lt;td&gt;Wasteful (GBs on large repos)&lt;/td&gt;
 &lt;td&gt;Each fetches separately&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;git worktree&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Low&lt;/td&gt;
 &lt;td&gt;Cheap (shared objects)&lt;/td&gt;
 &lt;td&gt;One fetch updates all&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Worktree is the synthesis — filesystem isolation with a unified object store.&lt;/p&gt;
&lt;h2 id="core-commands"&gt;&lt;a href="#core-commands" class="header-anchor"&gt;&lt;/a&gt;Core Commands
&lt;/h2&gt;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Add — last path segment becomes the new branch name&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git worktree add ../hotfix &lt;span class="c1"&gt;# branch &amp;#34;hotfix&amp;#34; from HEAD&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git worktree add ../review pr-123 &lt;span class="c1"&gt;# checkout existing branch&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git worktree add -b feat-x ../feat-x main &lt;span class="c1"&gt;# new branch from main&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git worktree add -d ../throwaway &lt;span class="c1"&gt;# detached HEAD, no branch&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git worktree list &lt;span class="c1"&gt;# --porcelain for scripts&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git worktree remove ../hotfix &lt;span class="c1"&gt;# clean only; -f for dirty&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git worktree prune &lt;span class="c1"&gt;# clean metadata for manually deleted dirs&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git worktree lock ../usb-drive --reason &lt;span class="s2"&gt;&amp;#34;removable drive&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git worktree unlock ../usb-drive
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git worktree move ../old ../new &lt;span class="c1"&gt;# won&amp;#39;t move worktrees w/ submodules&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git worktree repair &lt;span class="c1"&gt;# fix links after moving the main repo&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;&lt;strong&gt;Short aliases make it muscle memory&lt;/strong&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git config --global alias.wta &lt;span class="s1"&gt;&amp;#39;worktree add&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git config --global alias.wtl &lt;span class="s1"&gt;&amp;#39;worktree list&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git config --global alias.wtr &lt;span class="s1"&gt;&amp;#39;worktree remove&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h2 id="real-workflows"&gt;&lt;a href="#real-workflows" class="header-anchor"&gt;&lt;/a&gt;Real Workflows
&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;1. Hotfix without disturbing feature work&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git worktree add ../hotfix-prod origin/main
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; ../hotfix-prod
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# fix, commit, push, open PR&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; ../myrepo
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git worktree remove ../hotfix-prod
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;The feature&amp;rsquo;s dev server never stopped. node_modules untouched. IDE didn&amp;rsquo;t re-index.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2. Long test run + keep coding&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;pytest&lt;/code&gt; / &lt;code&gt;cargo test&lt;/code&gt; takes 10 minutes. Open a worktree to run it there while you keep editing the next commit. Zero interference.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git worktree add ../ci-run branch-a
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; ../ci-run &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; pytest --slow &lt;span class="p"&gt;&amp;amp;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; - &lt;span class="c1"&gt;# back to main worktree, keep coding&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;&lt;strong&gt;3. Code review without polluting your state&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git worktree add ../review-456 pr-456-branch
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; ../review-456
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# run it, read code, leave comments&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; - &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; git worktree remove ../review-456
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;&lt;strong&gt;4. Multiple AI agents in parallel&lt;/strong&gt; (the most underrated 2025 use case)&lt;/p&gt;
&lt;p&gt;Claude Code / Cursor / Aider only work in one directory at a time — running two in the same folder means they overwrite each other&amp;rsquo;s files. One branch per worktree and you can &lt;strong&gt;run three agents on three features simultaneously&lt;/strong&gt;, each with its own dev server port and node_modules, no collisions:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git worktree add ../agent-a -b feat-a
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git worktree add ../agent-b -b feat-b
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git worktree add ../agent-c -b feat-c
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# three tmux panes / three terminal windows, one claude each&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Claude Code&amp;rsquo;s Agent tool even has a built-in &lt;code&gt;isolation: &amp;quot;worktree&amp;quot;&lt;/code&gt; option — sub-agents automatically run in a worktree and merge back when done.&lt;/p&gt;
&lt;h2 id="three-directory-layouts"&gt;&lt;a href="#three-directory-layouts" class="header-anchor"&gt;&lt;/a&gt;Three Directory Layouts
&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;Sibling dirs (simplest)&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;~/code/myrepo
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;~/code/myrepo-hotfix
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;~/code/myrepo-review-123
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Plays well with editors&amp;rsquo; &amp;ldquo;one folder, one project&amp;rdquo; mental model.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;.worktrees/&lt;/code&gt; subdir&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;myrepo/
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;├── src/
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;└── .worktrees/
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ├── feat-x/
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; └── hotfix/
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Everything co-located. Add &lt;code&gt;.worktrees/&lt;/code&gt; to global gitignore. Downside: some tools (ESLint, tsc) recurse into it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Bare repo pattern (best for heavy multi-branch work)&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;mkdir myproj &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd&lt;/span&gt; myproj
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git clone --bare git@github.com:org/repo.git .bare
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;gitdir: ./.bare&amp;#34;&lt;/span&gt; &amp;gt; .git
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git worktree add main
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git worktree add feat-x
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Result:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;myproj/
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;├── .bare/ # actual object store
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;├── .git # file, pointing to .bare
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;├── main/ # main branch checkout
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;└── feat-x/ # feat-x branch checkout
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;No &amp;ldquo;primary working copy&amp;rdquo; — every branch is a worktree, &lt;code&gt;cd&lt;/code&gt; is the branch switch. Pairs beautifully with tmux and AI agents.&lt;/p&gt;
&lt;h2 id="gotchas"&gt;&lt;a href="#gotchas" class="header-anchor"&gt;&lt;/a&gt;Gotchas
&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;.git&lt;/code&gt; in a linked worktree is a file, not a directory.&lt;/strong&gt; Contents are &lt;code&gt;gitdir: /path/to/main/.git/worktrees/&amp;lt;name&amp;gt;&lt;/code&gt;. Tools that read &lt;code&gt;.git/&lt;/code&gt; as a directory will break.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;You can&amp;rsquo;t check out the same branch in two worktrees.&lt;/strong&gt; By design — prevents index divergence. Override with &lt;code&gt;--force&lt;/code&gt; or use detached HEAD.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Submodules are painful.&lt;/strong&gt; &lt;code&gt;worktree move&lt;/code&gt; refuses; &lt;code&gt;remove&lt;/code&gt; needs &lt;code&gt;--force&lt;/code&gt;. &lt;code&gt;.git/modules/&lt;/code&gt; is shared, so switching submodule commits in one worktree affects the others. Heavy-submodule repos need care.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;node_modules&lt;/code&gt;, &lt;code&gt;venv&lt;/code&gt;, &lt;code&gt;target/&lt;/code&gt; are per-worktree.&lt;/strong&gt; Disk gets hungry. Mitigations:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;pnpm&amp;rsquo;s content-addressable store — reinstalling across worktrees uses almost no extra space&lt;/li&gt;
&lt;li&gt;Rust: &lt;code&gt;CARGO_TARGET_DIR=~/.cache/cargo-target&lt;/code&gt; shares target dirs&lt;/li&gt;
&lt;li&gt;uv, poetry caches are shareable too&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;.env&lt;/code&gt; doesn&amp;rsquo;t get copied.&lt;/strong&gt; Use direnv — drop a &lt;code&gt;.envrc&lt;/code&gt; in each worktree and it auto-loads on cd.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Hooks are shared&lt;/strong&gt; (&lt;code&gt;.git/hooks/&lt;/code&gt; is in the common dir). If a hook assumes repo root via a hardcoded path, it breaks in linked worktrees. Always use &lt;code&gt;git rev-parse --show-toplevel&lt;/code&gt; for the current worktree&amp;rsquo;s root.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;IDE indexes run per worktree.&lt;/strong&gt; VS Code / JetBrains index each one independently — duplicate CPU and disk. Structural limit, no way around it.&lt;/p&gt;
&lt;h2 id="editor-integration"&gt;&lt;a href="#editor-integration" class="header-anchor"&gt;&lt;/a&gt;Editor Integration
&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;VS Code&lt;/strong&gt;: use multi-root workspace (&lt;code&gt;File → Add Folder to Workspace&lt;/code&gt;) to open multiple worktrees at once, or just open each in its own window. Since 2024, &lt;code&gt;GitHub.vscode-pull-request-github&lt;/code&gt; checks out PRs as worktrees.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;JetBrains&lt;/strong&gt;: native worktree UI in the Git tool window since 2023.2 — create, switch, remove from the GUI.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;fzf quick-switch&lt;/strong&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;wtcd&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;cd&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;git worktree list --porcelain &lt;span class="p"&gt;|&lt;/span&gt; awk &lt;span class="s1"&gt;&amp;#39;/^worktree /{print $2}&amp;#39;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; fzf&lt;span class="k"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Type &lt;code&gt;wtcd&lt;/code&gt;, fuzzy-search worktree paths, enter to cd.&lt;/p&gt;
&lt;h2 id="whats-new-in-20242026"&gt;&lt;a href="#whats-new-in-20242026" class="header-anchor"&gt;&lt;/a&gt;What&amp;rsquo;s New in 2024–2026
&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;Git 2.44 (2024/2)&lt;/strong&gt;: &lt;code&gt;git worktree add --orphan&lt;/code&gt; — creates a worktree with an unborn branch. Handy for &lt;code&gt;gh-pages&lt;/code&gt;-style split deploy branches.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Git 2.46 (2024/7)&lt;/strong&gt;: &lt;code&gt;worktree.useRelativePaths&lt;/code&gt; config + &lt;code&gt;--relative-paths&lt;/code&gt; flag — internal links use relative paths. The main repo (or the whole dir tree) can move without breaking worktrees. Huge for Dropbox/iCloud sync and containerized dev.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Git 2.48 (2025/1)&lt;/strong&gt;: &lt;code&gt;git worktree repair&lt;/code&gt; auto-fixes absolute/relative path mismatches.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Git 2.50 (mid-2025)&lt;/strong&gt;: relative paths and porcelain output have stabilized.&lt;/p&gt;
&lt;p&gt;Ecosystem: &amp;ldquo;one worktree per AI agent branch&amp;rdquo; went mainstream in 2025. &lt;code&gt;git-town&lt;/code&gt;, &lt;code&gt;ghq&lt;/code&gt;, and the &lt;code&gt;gh&lt;/code&gt; CLI all picked up first-class worktree support.&lt;/p&gt;
&lt;h2 id="getting-started"&gt;&lt;a href="#getting-started" class="header-anchor"&gt;&lt;/a&gt;Getting Started
&lt;/h2&gt;&lt;ol&gt;
&lt;li&gt;In any repo, try &lt;code&gt;git worktree add ../test-wt -b test-branch&lt;/code&gt; and poke around&lt;/li&gt;
&lt;li&gt;Back in the main worktree, notice &lt;code&gt;git branch&lt;/code&gt; sees it but &lt;code&gt;git status&lt;/code&gt; is untouched&lt;/li&gt;
&lt;li&gt;&lt;code&gt;git worktree remove ../test-wt&lt;/code&gt; — main worktree is completely unchanged&lt;/li&gt;
&lt;li&gt;Add &lt;code&gt;wta&lt;/code&gt; / &lt;code&gt;wtl&lt;/code&gt; / &lt;code&gt;wtr&lt;/code&gt; aliases, build muscle memory&lt;/li&gt;
&lt;li&gt;Next hotfix, use worktree instead of stash — feel the difference&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;For anyone using AI coding agents, worktree isn&amp;rsquo;t a bonus — it&amp;rsquo;s required. The &amp;ldquo;only one agent per repo&amp;rdquo; limitation is the exact thing worktree removes.&lt;/p&gt;
&lt;h2 id="references"&gt;&lt;a href="#references" class="header-anchor"&gt;&lt;/a&gt;References
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://git-scm.com/docs/git-worktree" target="_blank" rel="noopener"
 &gt;git-worktree Official Docs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://github.com/git/git/blob/master/Documentation/RelNotes/2.46.0.txt" target="_blank" rel="noopener"
 &gt;Git 2.46 Release Notes — worktree.useRelativePaths&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://code.claude.com/docs/en/agent-sdk" target="_blank" rel="noopener"
 &gt;Claude Code Agent tool — isolation: worktree&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://www.git-town.com/" target="_blank" rel="noopener"
 &gt;git-town — high-level git workflow wrapper&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description></item></channel></rss>