<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Suleyman</title>
    <description>The latest articles on DEV Community by Suleyman (@_suleyman).</description>
    <link>https://dev.to/_suleyman</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3862773%2F3e3d6904-7e97-4286-ae98-672e9fc2f4e8.jpg</url>
      <title>DEV Community: Suleyman</title>
      <link>https://dev.to/_suleyman</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/_suleyman"/>
    <language>en</language>
    <item>
      <title>Why You Underestimate Haiku</title>
      <dc:creator>Suleyman</dc:creator>
      <pubDate>Mon, 08 Jun 2026 18:19:37 +0000</pubDate>
      <link>https://dev.to/_suleyman/why-you-underestimate-haiku-5gik</link>
      <guid>https://dev.to/_suleyman/why-you-underestimate-haiku-5gik</guid>
      <description>&lt;p&gt;Most people pick a model the wrong way around. They look at the leaderboard, see Opus on top, and reach for it by default. Sonnet if they want to save money. Haiku almost never, because the name says "small."&lt;/p&gt;

&lt;p&gt;That habit costs you. For a lot of what you actually build, Haiku is the right call, and you're paying three to five times more for capability the task never uses. This post is about how to choose, and why Haiku should be your default more often than it is.&lt;/p&gt;

&lt;p&gt;The short version: don't start from "what's the best model." Start from "what does this task need." Most tasks don't need much.&lt;/p&gt;




&lt;h2&gt;
  
  
  Comparison
&lt;/h2&gt;

&lt;p&gt;Here is the current lineup, with the numbers that matter when you're choosing.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Haiku 4.5&lt;/th&gt;
&lt;th&gt;Sonnet 4.6&lt;/th&gt;
&lt;th&gt;Opus 4.8&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Model ID&lt;/td&gt;
&lt;td&gt;&lt;code&gt;claude-haiku-4-5&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;claude-sonnet-4-6&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;claude-opus-4-8&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Input price (per 1M tokens)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;$1&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;$3&lt;/td&gt;
&lt;td&gt;$5&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Output price (per 1M tokens)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;$5&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;$15&lt;/td&gt;
&lt;td&gt;$25&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Context window&lt;/td&gt;
&lt;td&gt;200K&lt;/td&gt;
&lt;td&gt;1M&lt;/td&gt;
&lt;td&gt;1M&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Max output&lt;/td&gt;
&lt;td&gt;64K&lt;/td&gt;
&lt;td&gt;64K&lt;/td&gt;
&lt;td&gt;128K&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Best at&lt;/td&gt;
&lt;td&gt;speed, volume&lt;/td&gt;
&lt;td&gt;balance&lt;/td&gt;
&lt;td&gt;hardest reasoning&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Two things jump out.&lt;/p&gt;

&lt;p&gt;First, &lt;strong&gt;price&lt;/strong&gt;. Haiku input is a fifth of Opus and a third of Sonnet. Output is the same ratio. If you send a million tokens through Opus for $25 and the same work would have been fine on Haiku, you spent $20 for nothing. And that gap is per request, so it compounds. A feature that runs ten thousand times a day on Opus instead of Haiku is not a rounding error. It is the difference between a feature that ships and one that gets cut for cost.&lt;/p&gt;

&lt;p&gt;Second, the &lt;strong&gt;context window&lt;/strong&gt;. This is where Haiku gives something up: 200K tokens instead of 1M. That is the real tradeoff, and it points straight at when to use it. We'll come back to that.&lt;/p&gt;




&lt;h2&gt;
  
  
  The mental model
&lt;/h2&gt;

&lt;p&gt;Stop ranking models. Rank &lt;em&gt;tasks&lt;/em&gt;. Ask three questions about the task in front of you:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Does it need real reasoning, or is it bounded?&lt;/strong&gt; A task is bounded when a competent junior could do it from a clear spec without much judgment: pull these fields out, sort this into one of five buckets, rewrite this in a different tone, answer this from the text I gave you. A task needs reasoning when the path isn't obvious: debug this across files, plan this migration, weigh these tradeoffs.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;What does a wrong answer cost?&lt;/strong&gt; If a bad output is caught by a test, a schema check, or a human two seconds later, errors are cheap and you should go for speed and price. If a bad output ships money or breaks production, errors are expensive and you pay up for the better model.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;How often does it run, and does latency show?&lt;/strong&gt; A nightly job that runs once doesn't care about speed or per-call cost. A loop that fires on every keystroke, or a batch of a hundred thousand items, cares about both, a lot.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Now map the answers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Bounded, cheap to get wrong, high volume or latency-sensitive → Haiku.&lt;/strong&gt; This is most of what you build.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Some judgment, longer output, moderate stakes → Sonnet.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Hard reasoning, long multi-step work, expensive to get wrong → Opus.&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The reason you underestimate Haiku is that you picked the model top-down, from the leaderboard, where the test is always something hard. But almost nothing you ship in production is leaderboard-hard. It's extraction, routing, classification, summaries, and small edits, run over and over. That's exactly the work Haiku is built for.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Haiku is actually good at
&lt;/h2&gt;

&lt;p&gt;These are the jobs where Haiku is not a compromise. It's the correct tool.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Classification and routing.&lt;/strong&gt; "Is this ticket a bug, a feature request, or spam?" "Which of these eight queues does this go to?" Bounded, checkable, often high volume.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Extraction.&lt;/strong&gt; Pull the name, email, and plan out of this message. Pair it with structured outputs (Haiku supports them) so the result is a validated object, not a string you have to parse and pray over.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Summarizing and rewriting.&lt;/strong&gt; Tighten this paragraph. Turn these notes into a changelog line. Translate this. The input is right there; there's nothing to reason about.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;First-pass filtering.&lt;/strong&gt; Run Haiku over a thousand records to find the fifty worth a closer look, then send only those fifty to a bigger model. You just cut your Opus bill by 95% and barely touched quality.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The inner steps of an agent.&lt;/strong&gt; More on this next, because it's the pattern that changes the most.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;What ties these together: the answer is in the input or in a short list of options, the output is short, and you can check it cheaply. That's the Haiku zone.&lt;/p&gt;




&lt;h2&gt;
  
  
  The pattern that matters most: mixed models
&lt;/h2&gt;

&lt;p&gt;The biggest mistake is treating model choice as one decision for the whole app. It's a decision &lt;em&gt;per step&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;A real agent doesn't do one thing. It reads files, searches, plans, edits, checks. Those steps are not equally hard. The planning step might need Opus. The "go read these twelve files and tell me which ones mention auth" step does not. That's a Haiku job, and there are usually a lot of them.&lt;/p&gt;

&lt;p&gt;So run the main loop on a strong model and hand the cheap, parallel sub-tasks to Haiku. This is exactly how Claude Code works: its Explore subagents run on Haiku while the main agent stays on a bigger model. The expensive model does the thinking. The cheap fast model does the legwork, often several at once.&lt;/p&gt;

&lt;p&gt;There's a second reason to do it with subagents rather than swapping the model mid-conversation: &lt;strong&gt;switching models invalidates your prompt cache.&lt;/strong&gt; Caches are tied to one model. If you flip the main loop from Opus to Haiku and back, you throw away the cached prefix every time and pay full price to rebuild it. Spawning a Haiku subagent for the sub-task keeps the main loop's cache intact. You get the cheap model &lt;em&gt;and&lt;/em&gt; the warm cache.&lt;/p&gt;

&lt;p&gt;In rough terms, the shape is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Main loop: the model that does the hard part
&lt;/span&gt;&lt;span class="n"&gt;plan&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;claude-opus-4-8&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...)&lt;/span&gt;

&lt;span class="c1"&gt;# Fan-out: the bounded sub-tasks, cheap and parallel, on Haiku
&lt;/span&gt;&lt;span class="n"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;claude-haiku-4-5&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;max_tokens&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1024&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;role&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Does this file touch auth? &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}],&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;files&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Most of your token volume lives in those sub-tasks. Move them to Haiku and your bill changes more than any single model upgrade ever will.&lt;/p&gt;




&lt;h2&gt;
  
  
  Haiku plus Batch, for the bulk stuff
&lt;/h2&gt;

&lt;p&gt;If the work isn't time-sensitive — overnight classification, backfilling labels, processing a big export — send it through the Batch API. That's another 50% off on top of Haiku's already-low price. Haiku output drops from $5 to $2.50 per million tokens. For bulk, nothing else comes close, and the quality is fine because bulk work is almost always bounded work.&lt;/p&gt;




&lt;h2&gt;
  
  
  When Haiku is the wrong choice
&lt;/h2&gt;

&lt;p&gt;The mental model cuts both ways. Reaching for Haiku on the wrong task is its own mistake. Send it up the ladder when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The task needs deep, multi-step reasoning.&lt;/strong&gt; Haiku answers fast and direct. It doesn't even take the &lt;code&gt;effort&lt;/code&gt; parameter, the setting that tells a model how hard to think, which only Sonnet 4.6 and the Opus tier support. That's the point: Haiku is built for fast answers, not slow thinking. Send hard debugging, planning, and deep research to Opus.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The context is huge.&lt;/strong&gt; 200K is a lot, but Sonnet and Opus give you 1M. If you're feeding in a whole codebase or a pile of long documents at once, you need the bigger window.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A wrong answer is expensive.&lt;/strong&gt; Anything that moves money, ships to users without review, or is hard to undo. Pay for the better model; the error you avoid is worth more than the tokens you save.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The output is long and structured.&lt;/strong&gt; Long coding runs and big generated documents. That's where Opus's 128K output, and its knack for staying on track over long tasks, earn their price.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you're unsure which tier a task needs, the cheap experiment is to run it on Haiku first and look at the failures. If it's already good enough, you're done. If it fails in a clear, consistent way, you've learned exactly what capability the task needs before you pay for it.&lt;/p&gt;




&lt;h2&gt;
  
  
  How to try it
&lt;/h2&gt;

&lt;p&gt;It's a one-line change. The API surface is the same across all three models, so swapping the model string is usually all it takes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;claude-haiku-4-5&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;max_tokens&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1024&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;role&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Classify this ticket: ...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}],&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two things to know going in. Haiku has its own rate-limit pool, separate from the bigger models, so test your throughput at the volume you actually expect. And it doesn't take the &lt;code&gt;effort&lt;/code&gt; parameter, so strip it from the request if it's there, or the call will error.&lt;/p&gt;

&lt;p&gt;Pick your highest-volume, most boring API call — the classifier, the extractor, the summarizer you run thousands of times a day. Move it to Haiku, watch the failures for a day, and check your bill at the end of the week. That one change usually pays for the experiment many times over.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Is Haiku 4.5 actually good, or just cheap?&lt;/strong&gt;&lt;br&gt;
Both. It's a current-generation model, not a stripped-down one. On bounded, well-specified tasks the gap to the bigger models is small and often invisible once you add a schema check or a test. The gap shows up on hard reasoning, which is the work you shouldn't be sending to Haiku anyway.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What's the model ID?&lt;/strong&gt;&lt;br&gt;
&lt;code&gt;claude-haiku-4-5&lt;/code&gt;, or the pinned snapshot &lt;code&gt;claude-haiku-4-5-20251001&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How much cheaper is it, really?&lt;/strong&gt;&lt;br&gt;
$1 in / $5 out per million tokens, versus $3 / $15 for Sonnet and $5 / $25 for Opus. A fifth of Opus, a third of Sonnet. Halve it again with the Batch API.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What does Haiku give up?&lt;/strong&gt;&lt;br&gt;
A smaller context window (200K vs 1M), no &lt;code&gt;effort&lt;/code&gt; parameter, and less depth on hard multi-step reasoning. Those three lines tell you when to reach past it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Does it support structured outputs?&lt;/strong&gt;&lt;br&gt;
Yes. Haiku 4.5, Sonnet 4.6, and Opus 4.8 all do. Use them for extraction and classification so you get a validated object back instead of a string to parse.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;So when do I still use Opus?&lt;/strong&gt;&lt;br&gt;
The hardest reasoning, the longest multi-step jobs, and anything where a wrong answer is expensive. Use it for the step that needs it, not for the whole app.&lt;/p&gt;

</description>
      <category>claude</category>
      <category>ai</category>
      <category>programming</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Git Worktrees: How to Run Multiple Claude Code Sessions at Once</title>
      <dc:creator>Suleyman</dc:creator>
      <pubDate>Sun, 07 Jun 2026 18:51:12 +0000</pubDate>
      <link>https://dev.to/_suleyman/git-worktrees-how-to-run-multiple-claude-code-sessions-at-once-1i81</link>
      <guid>https://dev.to/_suleyman/git-worktrees-how-to-run-multiple-claude-code-sessions-at-once-1i81</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Git worktrees let you check out multiple branches at the same time, each in its own folder, all sharing one &lt;code&gt;.git&lt;/code&gt; directory. And with Claude Code's &lt;code&gt;--worktree&lt;/code&gt; flag, you can run multiple AI coding sessions at once without file conflicts. This guide covers everything from first setup to parallel AI workflows.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;You're working on a feature. You have uncommitted changes. A production bug comes in and needs a fix now.&lt;/p&gt;

&lt;p&gt;With standard Git, you have three bad options:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;git stash&lt;/code&gt; everything, switch branches, fix the bug, &lt;code&gt;git stash pop&lt;/code&gt;, get back to where you were&lt;/li&gt;
&lt;li&gt;Clone the repo a second time and manage two out-of-sync copies&lt;/li&gt;
&lt;li&gt;Make a half-finished WIP commit just so you can switch branches&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Git worktrees remove all three of these options by letting you work on multiple branches at the same time, each in its own folder.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Are Git Worktrees?
&lt;/h2&gt;

&lt;p&gt;A worktree is a folder with its own branch checked out. It shares the same &lt;code&gt;.git&lt;/code&gt; history as your main project, but has its own working files.&lt;/p&gt;

&lt;p&gt;A normal clone gives you one folder, one branch at a time:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;~/my-project/
  .git/         ← history lives here
  src/
  package.json  ← whatever branch you have checked out
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With worktrees, you get multiple folders, each on its own branch:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;~/my-project/
  .git/              ← one shared history
  src/
  package.json       ← main branch

~/my-project-auth/
  src/
  package.json       ← feature/auth branch

~/my-project-hotfix/
  src/
  package.json       ← hotfix/payment-crash branch
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can edit files in all three folders at the same time. Changes in one folder don't touch the others. And since they share one &lt;code&gt;.git&lt;/code&gt;, a &lt;code&gt;git fetch&lt;/code&gt; in any folder makes new remote branches visible everywhere.&lt;/p&gt;

&lt;h3&gt;
  
  
  What's shared vs. what's separate
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Shared across all worktrees&lt;/th&gt;
&lt;th&gt;Separate per worktree&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Commit history&lt;/td&gt;
&lt;td&gt;Checked-out branch&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Remote refs (after a fetch)&lt;/td&gt;
&lt;td&gt;Staged changes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hooks&lt;/td&gt;
&lt;td&gt;Unstaged changes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Local Git config&lt;/td&gt;
&lt;td&gt;Working directory files&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Local branches&lt;/td&gt;
&lt;td&gt;Shell &lt;code&gt;$PWD&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tags&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;MERGE_HEAD&lt;/code&gt;, &lt;code&gt;CHERRY_PICK_HEAD&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Worktrees vs. Multiple Clones
&lt;/h2&gt;

&lt;p&gt;Some people solve the "two branches at once" problem by cloning the repo twice. Here's why that's worse:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Storage.&lt;/strong&gt; Every clone copies the full &lt;code&gt;.git&lt;/code&gt; object store. A 500 MB repo becomes 1 GB across two clones. Worktrees only add the weight of the working files — usually a few megabytes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;No shared state.&lt;/strong&gt; &lt;code&gt;git fetch&lt;/code&gt; in Clone A does nothing for Clone B. A local branch you create in Clone A doesn't exist in Clone B until you push it. You're managing two separate databases by hand.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Hooks only exist in one place.&lt;/strong&gt; Git hooks (pre-commit linting, push guards, etc.) live in &lt;code&gt;.git/hooks&lt;/code&gt;. Clone B starts with nothing. With worktrees, all hooks apply everywhere because there's one &lt;code&gt;.git&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Git protects you.&lt;/strong&gt; Git won't let you check out the same branch in two worktrees at once. With two clones, nothing stops both from modifying the same branch simultaneously. You won't notice until you push.&lt;/p&gt;




&lt;h2&gt;
  
  
  Basic Setup: Your First Worktree
&lt;/h2&gt;

&lt;p&gt;Start from a normal clone. These commands work from there.&lt;/p&gt;

&lt;h3&gt;
  
  
  New worktree, new branch
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Creates ../feature-auth directory on a new branch named feature/auth&lt;/span&gt;
git worktree add ../feature-auth &lt;span class="nt"&gt;-b&lt;/span&gt; feature/auth
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The path and the branch name are independent. Use &lt;code&gt;-b&lt;/code&gt; to name them separately.&lt;/p&gt;

&lt;h3&gt;
  
  
  New worktree, auto-named branch
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Branch name comes from the last part of the path: "feature-auth"&lt;/span&gt;
git worktree add ../feature-auth
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Git names the branch after the last path segment. Fine for quick tasks. Use &lt;code&gt;-b&lt;/code&gt; when the branch name matters.&lt;/p&gt;

&lt;h3&gt;
  
  
  Worktree from an existing branch
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Check out a branch that already exists&lt;/span&gt;
git worktree add ../bugfix-session bugfix/payment-crash
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Worktree from a remote branch
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Create a local branch that tracks the remote one&lt;/span&gt;
git worktree add &lt;span class="nt"&gt;-b&lt;/span&gt; feature/dark-mode ../dark-mode origin/feature/dark-mode
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use this to review a colleague's branch without touching your own work.&lt;/p&gt;

&lt;h3&gt;
  
  
  Then work normally
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; ../feature-auth
npm &lt;span class="nb"&gt;install&lt;/span&gt;       &lt;span class="c"&gt;# each worktree needs its own node_modules&lt;/span&gt;
npm run dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each worktree is a fresh checkout. Dependencies, virtual environments, and build output are not shared between worktrees. Install them in each one.&lt;/p&gt;

&lt;h3&gt;
  
  
  List your worktrees
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git worktree list
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/home/you/my-project        abc1234 [main]
/home/you/feature-auth      def5678 [feature/auth]
/home/you/bugfix-session    ghi9012 [bugfix/payment-crash]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Remove a worktree
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git worktree remove ../feature-auth
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This removes the folder and the Git bookkeeping. The branch stays unless you delete it with &lt;code&gt;git branch -d feature/auth&lt;/code&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Bare Clone Setup
&lt;/h2&gt;

&lt;p&gt;The basic setup works, but the folder layout is awkward. Your main checkout sits alongside its own worktrees:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;~/my-project/    ← main branch (also has working files)
~/feature-auth/  ← worktree
~/bugfix/        ← worktree
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The main branch is in a special position even though it shouldn't be. Worktrees should all be equal.&lt;/p&gt;

&lt;p&gt;The bare clone setup fixes this. Instead of a normal clone, you clone only the history — no working files — and create every branch as a worktree, including &lt;code&gt;main&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  How to set it up
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# 1. Create a clean project folder&lt;/span&gt;
&lt;span class="nb"&gt;mkdir&lt;/span&gt; ~/Projects/my-project
&lt;span class="nb"&gt;cd&lt;/span&gt; ~/Projects/my-project

&lt;span class="c"&gt;# 2. Clone history only, no working files&lt;/span&gt;
git clone &lt;span class="nt"&gt;--bare&lt;/span&gt; git@github.com:user/repo.git .bare

&lt;span class="c"&gt;# 3. Tell Git where the history is&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"gitdir: ./.bare"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; .git

&lt;span class="c"&gt;# 4. Fix remote tracking — required, see explanation below&lt;/span&gt;
git config remote.origin.fetch &lt;span class="s2"&gt;"+refs/heads/*:refs/remotes/origin/*"&lt;/span&gt;

&lt;span class="c"&gt;# 5. Fetch all remote branches&lt;/span&gt;
git fetch &lt;span class="nt"&gt;--all&lt;/span&gt;

&lt;span class="c"&gt;# 6. Create your first worktree&lt;/span&gt;
git worktree add main
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your folder now looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;~/Projects/my-project/
  .bare/    ← git history
  .git      ← one-line file pointing to .bare
  main/     ← worktree for main branch
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add more as needed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git worktree add feature/auth
git worktree add hotfix/payment-crash
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;~/Projects/my-project/
  .bare/
  .git
  main/
  feature/
    auth/
  hotfix/
    payment-crash/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every branch is a folder. When work is done and merged, remove the folder.&lt;/p&gt;

&lt;h3&gt;
  
  
  The "blind clone" problem
&lt;/h3&gt;

&lt;p&gt;After a bare clone, run &lt;code&gt;git fetch&lt;/code&gt; and you might see only:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;* branch            HEAD       -&amp;gt; FETCH_HEAD
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That means Git only knows about the default branch. All other remote branches are invisible.&lt;/p&gt;

&lt;p&gt;This happens because bare clones are designed as server mirrors, not dev environments. They don't set up remote tracking by default.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix it:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git config remote.origin.fetch &lt;span class="s2"&gt;"+refs/heads/*:refs/remotes/origin/*"&lt;/span&gt;
git fetch &lt;span class="nt"&gt;--all&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first line tells Git to map every remote branch to a local tracking reference. After that, &lt;code&gt;git fetch --all&lt;/code&gt; shows everything. Run these two lines every time you do a bare clone.&lt;/p&gt;

&lt;p&gt;If you skip this step, &lt;code&gt;git worktree add&lt;/code&gt; will silently create a new empty branch instead of checking out the remote branch you wanted.&lt;/p&gt;

&lt;h3&gt;
  
  
  Automation script
&lt;/h3&gt;

&lt;p&gt;Save this as &lt;code&gt;wtree&lt;/code&gt; in your &lt;code&gt;~/.local/bin/&lt;/code&gt; and run &lt;code&gt;chmod +x wtree&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;
&lt;span class="c"&gt;# Usage: wtree &amp;lt;git-url&amp;gt;&lt;/span&gt;
&lt;span class="c"&gt;# Run this in an empty folder.&lt;/span&gt;

&lt;span class="nv"&gt;REPO_URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;
&lt;span class="nv"&gt;SCRIPT_NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;basename&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$0&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-z&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$REPO_URL&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
  &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Usage: &lt;/span&gt;&lt;span class="nv"&gt;$0&lt;/span&gt;&lt;span class="s2"&gt; &amp;lt;repo-url&amp;gt;"&lt;/span&gt;
  &lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;span class="k"&gt;fi

&lt;/span&gt;&lt;span class="nv"&gt;FILES&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-A&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$SCRIPT_NAME&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$FILES&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
  &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Error: folder is not empty."&lt;/span&gt;
  &lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;span class="k"&gt;fi

&lt;/span&gt;git clone &lt;span class="nt"&gt;--bare&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$REPO_URL&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; .bare
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"gitdir: ./.bare"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; .git
git config remote.origin.fetch &lt;span class="s2"&gt;"+refs/heads/*:refs/remotes/origin/*"&lt;/span&gt;
git fetch &lt;span class="nt"&gt;--all&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Done. Run: git worktree add main"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Command Reference
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;git worktree add&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# New branch, explicit name&lt;/span&gt;
git worktree add &amp;lt;path&amp;gt; &lt;span class="nt"&gt;-b&lt;/span&gt; &amp;lt;branch&amp;gt;

&lt;span class="c"&gt;# New branch, name comes from path&lt;/span&gt;
git worktree add &amp;lt;path&amp;gt;

&lt;span class="c"&gt;# Existing local branch&lt;/span&gt;
git worktree add &amp;lt;path&amp;gt; &amp;lt;existing-branch&amp;gt;

&lt;span class="c"&gt;# From a remote branch&lt;/span&gt;
git worktree add &lt;span class="nt"&gt;-b&lt;/span&gt; &amp;lt;local-name&amp;gt; &amp;lt;path&amp;gt; &amp;lt;remote&amp;gt;/&amp;lt;branch&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;code&gt;git worktree list&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git worktree list

&lt;span class="c"&gt;# Machine-readable output&lt;/span&gt;
git worktree list &lt;span class="nt"&gt;--porcelain&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;code&gt;git worktree remove&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Remove a worktree (fails if there are uncommitted changes)&lt;/span&gt;
git worktree remove &amp;lt;path&amp;gt;

&lt;span class="c"&gt;# Force removal, discards uncommitted changes&lt;/span&gt;
git worktree remove &lt;span class="nt"&gt;--force&lt;/span&gt; &amp;lt;path&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;code&gt;git worktree prune&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;If you delete a worktree folder with &lt;code&gt;rm -rf&lt;/code&gt; instead of &lt;code&gt;git worktree remove&lt;/code&gt;, Git keeps stale records pointing to the now-missing folder. &lt;code&gt;prune&lt;/code&gt; cleans those up.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Remove stale records&lt;/span&gt;
git worktree prune

&lt;span class="c"&gt;# Preview what would be removed&lt;/span&gt;
git worktree prune &lt;span class="nt"&gt;--dry-run&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;code&gt;git worktree lock&lt;/code&gt; / &lt;code&gt;unlock&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Prevent &lt;code&gt;prune&lt;/code&gt; from removing a worktree — useful for worktrees on external drives:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git worktree lock &amp;lt;path&amp;gt; &lt;span class="nt"&gt;--reason&lt;/span&gt; &lt;span class="s2"&gt;"On external drive"&lt;/span&gt;
git worktree unlock &amp;lt;path&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;code&gt;git worktree move&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Move a worktree to a new path:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git worktree move ../feature-auth ../workspaces/feature-auth
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;code&gt;git worktree repair&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;If you moved the &lt;code&gt;.git&lt;/code&gt; directory or the worktree folder manually, Git loses track of it. Run this from inside the worktree to re-link it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git worktree repair
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Real-World Workflows
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Hotfix while in the middle of a feature
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# You're in ~/project/feature/redesign with uncommitted work.&lt;/span&gt;
&lt;span class="c"&gt;# A bug needs fixing now. Don't stash, don't stop.&lt;/span&gt;

&lt;span class="c"&gt;# From the project root, add a worktree for the fix&lt;/span&gt;
git worktree add hotfix/payment-crash

&lt;span class="c"&gt;# Open a second terminal&lt;/span&gt;
&lt;span class="nb"&gt;cd&lt;/span&gt; ~/project/hotfix/payment-crash
&lt;span class="c"&gt;# Fix the bug&lt;/span&gt;
git add &lt;span class="nb"&gt;.&lt;/span&gt;
git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"fix: null check on payment processor callback"&lt;/span&gt;
git push origin hotfix/payment-crash

&lt;span class="c"&gt;# Your first terminal still has the redesign exactly where you left it&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Compare two implementations side by side
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git worktree add approach-a &lt;span class="nt"&gt;-b&lt;/span&gt; experiment/approach-a
git worktree add approach-b &lt;span class="nt"&gt;-b&lt;/span&gt; experiment/approach-b

&lt;span class="c"&gt;# Two terminals, two dev servers&lt;/span&gt;
&lt;span class="nb"&gt;cd &lt;/span&gt;approach-a &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; npm run dev &lt;span class="nt"&gt;--&lt;/span&gt; &lt;span class="nt"&gt;--port&lt;/span&gt; 3001
&lt;span class="nb"&gt;cd &lt;/span&gt;approach-b &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; npm run dev &lt;span class="nt"&gt;--&lt;/span&gt; &lt;span class="nt"&gt;--port&lt;/span&gt; 3002

&lt;span class="c"&gt;# Open both in the browser and compare&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Review a PR without losing your place
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git fetch origin pull/1234/head:pr-1234
git worktree add ../pr-1234 pr-1234

&lt;span class="nb"&gt;cd&lt;/span&gt; ../pr-1234
&lt;span class="c"&gt;# Review, run tests, check behavior&lt;/span&gt;

&lt;span class="c"&gt;# When done&lt;/span&gt;
&lt;span class="nb"&gt;cd&lt;/span&gt; ..
git worktree remove pr-1234
git branch &lt;span class="nt"&gt;-d&lt;/span&gt; pr-1234
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Long-running parallel features
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git worktree add feature/payments &lt;span class="nt"&gt;-b&lt;/span&gt; feature/payments
git worktree add feature/notifications &lt;span class="nt"&gt;-b&lt;/span&gt; feature/notifications

&lt;span class="c"&gt;# Work on payments in one terminal, notifications in another.&lt;/span&gt;
&lt;span class="c"&gt;# git fetch in either one updates both.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Shell Aliases and Shortcuts
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Git aliases
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git config &lt;span class="nt"&gt;--global&lt;/span&gt; alias.wta &lt;span class="s1"&gt;'!f() { git worktree add -b "$1" "../$1"; }; f'&lt;/span&gt;
git config &lt;span class="nt"&gt;--global&lt;/span&gt; alias.wtr &lt;span class="s1"&gt;'!f() { git worktree remove "../$1"; }; f'&lt;/span&gt;
git config &lt;span class="nt"&gt;--global&lt;/span&gt; alias.wtl &lt;span class="s1"&gt;'worktree list'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Usage:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git wta feature-auth    &lt;span class="c"&gt;# creates ../feature-auth on branch feature-auth&lt;/span&gt;
git wtr feature-auth    &lt;span class="c"&gt;# removes it&lt;/span&gt;
git wtl                 &lt;span class="c"&gt;# lists all worktrees&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  PR checkout function
&lt;/h3&gt;

&lt;p&gt;Add to your &lt;code&gt;~/.bashrc&lt;/code&gt; or &lt;code&gt;~/.zshrc&lt;/code&gt;. Requires the &lt;a href="https://cli.github.com/" rel="noopener noreferrer"&gt;GitHub CLI&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Usage: cpr &amp;lt;PR_NUMBER&amp;gt;&lt;/span&gt;
cpr&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="nb"&gt;local pr&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;remote&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;2&lt;/span&gt;&lt;span class="k"&gt;:-&lt;/span&gt;&lt;span class="nv"&gt;origin&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="nb"&gt;local &lt;/span&gt;branch
  &lt;span class="nv"&gt;branch&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;gh &lt;span class="nb"&gt;pr &lt;/span&gt;view &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$pr&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;--json&lt;/span&gt; headRefName &lt;span class="nt"&gt;-q&lt;/span&gt; .headRefName&lt;span class="si"&gt;)&lt;/span&gt;
  git fetch &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$remote&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$branch&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  git worktree add &lt;span class="s2"&gt;"../&lt;/span&gt;&lt;span class="nv"&gt;$branch&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$branch&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="nb"&gt;cd&lt;/span&gt; &lt;span class="s2"&gt;"../&lt;/span&gt;&lt;span class="nv"&gt;$branch&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="k"&gt;return
  &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Ready: PR #&lt;/span&gt;&lt;span class="nv"&gt;$pr&lt;/span&gt;&lt;span class="s2"&gt; (&lt;/span&gt;&lt;span class="nv"&gt;$branch&lt;/span&gt;&lt;span class="s2"&gt;)"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run &lt;code&gt;cpr 1234&lt;/code&gt; and you're in a folder with that PR checked out.&lt;/p&gt;

&lt;h3&gt;
  
  
  Fuzzy worktree switcher
&lt;/h3&gt;

&lt;p&gt;Requires &lt;a href="https://github.com/junegunn/fzf" rel="noopener noreferrer"&gt;&lt;code&gt;fzf&lt;/code&gt;&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;wts&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="nb"&gt;local &lt;/span&gt;selected
  &lt;span class="nv"&gt;selected&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;git worktree list | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{print $1}'&lt;/span&gt; | fzf &lt;span class="nt"&gt;--prompt&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"Switch to worktree: "&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
  &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$selected&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$selected&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Type &lt;code&gt;wts&lt;/code&gt;, search by name, press Enter.&lt;/p&gt;




&lt;h2&gt;
  
  
  Editor Tooling
&lt;/h2&gt;

&lt;h3&gt;
  
  
  VS Code, Cursor, Windsurf
&lt;/h3&gt;

&lt;p&gt;Install the &lt;a href="https://marketplace.visualstudio.com/items?itemName=PhilStainer.git-worktree" rel="noopener noreferrer"&gt;Git Worktree extension&lt;/a&gt; by PhilStainer. It adds worktree commands to the Command Palette:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;List and jump between worktrees&lt;/li&gt;
&lt;li&gt;Add and remove worktrees from the GUI&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each worktree opens as a separate VS Code window since each is a different folder on disk. Treat each one as its own project.&lt;/p&gt;

&lt;p&gt;Pairs well with the &lt;a href="https://marketplace.visualstudio.com/items?itemName=alefragnani.project-manager" rel="noopener noreferrer"&gt;Project Manager extension&lt;/a&gt; if you switch between many worktrees often.&lt;/p&gt;

&lt;h3&gt;
  
  
  Neovim / Vim
&lt;/h3&gt;

&lt;p&gt;Session managers like &lt;a href="https://github.com/tpope/vim-obsession" rel="noopener noreferrer"&gt;vim-obsession&lt;/a&gt; or &lt;a href="https://github.com/folke/persistence.nvim" rel="noopener noreferrer"&gt;persistence.nvim&lt;/a&gt; save a session per directory automatically, so each worktree gets its own saved state.&lt;/p&gt;

&lt;h3&gt;
  
  
  JetBrains IDEs
&lt;/h3&gt;

&lt;p&gt;Open each worktree as a separate project window. The IDE detects the shared &lt;code&gt;.git&lt;/code&gt; and shows branch info correctly.&lt;/p&gt;




&lt;h2&gt;
  
  
  Claude Code + Worktrees
&lt;/h2&gt;

&lt;p&gt;Without worktrees, two Claude Code sessions in the same directory will overwrite each other's files. The second session has no idea what the first wrote. You get silent conflicts.&lt;/p&gt;

&lt;p&gt;Worktrees give each session its own directory and branch. They can run at the same time without touching each other's work.&lt;/p&gt;

&lt;p&gt;Claude Code has this built in with the &lt;code&gt;--worktree&lt;/code&gt; flag.&lt;/p&gt;

&lt;h3&gt;
  
  
  Start a session in a worktree
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;claude &lt;span class="nt"&gt;--worktree&lt;/span&gt; feature-auth
&lt;span class="c"&gt;# short form:&lt;/span&gt;
claude &lt;span class="nt"&gt;-w&lt;/span&gt; bugfix-payment
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Claude creates &lt;code&gt;.claude/worktrees/feature-auth/&lt;/code&gt;, checks out a new branch named &lt;code&gt;worktree-feature-auth&lt;/code&gt;, and starts the session there.&lt;/p&gt;

&lt;p&gt;Open another terminal and run a second session:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;claude &lt;span class="nt"&gt;-w&lt;/span&gt; bugfix-payment
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Both run at the same time. Neither can see the other's files.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;First-time setup:&lt;/strong&gt; Before using &lt;code&gt;--worktree&lt;/code&gt; in a repo, run &lt;code&gt;claude&lt;/code&gt; once in that directory to accept the workspace trust prompt. After that, the flag works without extra steps.&lt;/p&gt;

&lt;h3&gt;
  
  
  Let Claude pick a name
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;claude &lt;span class="nt"&gt;--worktree&lt;/span&gt;
&lt;span class="c"&gt;# Creates something like: .claude/worktrees/bright-running-fox/&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Check out a pull request
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;claude &lt;span class="nt"&gt;--worktree&lt;/span&gt; &lt;span class="s2"&gt;"#1234"&lt;/span&gt;
&lt;span class="c"&gt;# Creates .claude/worktrees/pr-1234/ from that PR's branch&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Claude fetches the branch and starts the session in it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Copy &lt;code&gt;.env&lt;/code&gt; files into every worktree
&lt;/h3&gt;

&lt;p&gt;Worktrees are fresh checkouts. Files like &lt;code&gt;.env&lt;/code&gt; and &lt;code&gt;.env.local&lt;/code&gt; won't be there unless you copy them.&lt;/p&gt;

&lt;p&gt;Create a &lt;code&gt;.worktreeinclude&lt;/code&gt; file in the project root. Same syntax as &lt;code&gt;.gitignore&lt;/code&gt;. Only files that match AND are already gitignored will be copied:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;&lt;span class="c"&gt;# .worktreeinclude
&lt;/span&gt;.&lt;span class="n"&gt;env&lt;/span&gt;
.&lt;span class="n"&gt;env&lt;/span&gt;.&lt;span class="n"&gt;local&lt;/span&gt;
&lt;span class="n"&gt;config&lt;/span&gt;/&lt;span class="n"&gt;secrets&lt;/span&gt;.&lt;span class="n"&gt;json&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This applies to every worktree Claude creates — via &lt;code&gt;--worktree&lt;/code&gt;, via subagents, or via the desktop app.&lt;/p&gt;

&lt;p&gt;Also add this to &lt;code&gt;.gitignore&lt;/code&gt; so worktree contents don't show up as untracked files:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.claude/worktrees/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Isolate subagents
&lt;/h3&gt;

&lt;p&gt;When Claude delegates work to subagents, multiple subagents can write to the same files at once — same conflict problem. Tell Claude to isolate them:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Use worktrees for your agents."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Or set it permanently in a custom subagent's frontmatter:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;backend-agent&lt;/span&gt;
&lt;span class="na"&gt;isolation&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;worktree&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each subagent gets its own worktree. When the subagent finishes with no changes, the worktree is removed automatically.&lt;/p&gt;

&lt;h3&gt;
  
  
  Base branch: remote vs. local HEAD
&lt;/h3&gt;

&lt;p&gt;By default, new worktrees branch from &lt;code&gt;origin/HEAD&lt;/code&gt; — the latest remote state. To branch from your current local &lt;code&gt;HEAD&lt;/code&gt; instead (including unpushed commits), add this to your Claude Code settings:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"worktree"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"baseRef"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"head"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use &lt;code&gt;"head"&lt;/code&gt; when subagents need to work on code you haven't pushed yet. Use the default when you want each session to start from a clean remote state.&lt;/p&gt;

&lt;h3&gt;
  
  
  What happens when a session ends
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Session state on exit&lt;/th&gt;
&lt;th&gt;What Claude does&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;No commits, no changes&lt;/td&gt;
&lt;td&gt;Removes the worktree and branch automatically&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Has commits or uncommitted changes&lt;/td&gt;
&lt;td&gt;Asks: keep or remove?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Non-interactive mode (&lt;code&gt;-p&lt;/code&gt; flag)&lt;/td&gt;
&lt;td&gt;No cleanup — remove manually&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;To clean up manually:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git worktree list
git worktree remove .claude/worktrees/feature-auth
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Desktop app
&lt;/h3&gt;

&lt;p&gt;The Claude Code desktop app creates a worktree for every new parallel session automatically. No flags needed. &lt;code&gt;.worktreeinclude&lt;/code&gt; still applies.&lt;/p&gt;

&lt;h3&gt;
  
  
  Three sessions at once: a full example
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# .gitignore&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;".claude/worktrees/"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; .gitignore

&lt;span class="c"&gt;# .worktreeinclude&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;".env"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; .worktreeinclude

&lt;span class="c"&gt;# Three terminals&lt;/span&gt;
claude &lt;span class="nt"&gt;-w&lt;/span&gt; add-oauth-login
claude &lt;span class="nt"&gt;-w&lt;/span&gt; fix-rate-limiter
claude &lt;span class="nt"&gt;-w&lt;/span&gt; migrate-to-drizzle
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When each finishes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Review the work&lt;/span&gt;
git diff main..worktree-add-oauth-login
git diff main..worktree-fix-rate-limiter
git diff main..worktree-migrate-to-drizzle

&lt;span class="c"&gt;# Merge what's good&lt;/span&gt;
git merge worktree-fix-rate-limiter
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Common Pitfalls
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Deleting a folder with &lt;code&gt;rm -rf&lt;/code&gt; instead of &lt;code&gt;git worktree remove&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Git keeps records pointing to the deleted folder. The next time you try to add a worktree at the same path:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;fatal: 'feature-auth' is already checked out at '...'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Fix:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git worktree prune
git worktree add &amp;lt;path&amp;gt; &amp;lt;branch&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  You can't check out the same branch in two worktrees
&lt;/h3&gt;

&lt;p&gt;Git blocks this. If you need two sessions on the same branch, create a new branch from it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git worktree add ../review-copy &lt;span class="nt"&gt;-b&lt;/span&gt; review/feature-auth feature/auth
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Dependencies aren't shared
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;node_modules&lt;/code&gt;, Python virtual environments, compiled binaries — each worktree needs its own. After creating a worktree:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; ../new-worktree
npm &lt;span class="nb"&gt;install&lt;/span&gt;   &lt;span class="c"&gt;# or pip install, cargo build, etc.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Bare clone without the fetch fix
&lt;/h3&gt;

&lt;p&gt;After &lt;code&gt;git clone --bare&lt;/code&gt;, always run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git config remote.origin.fetch &lt;span class="s2"&gt;"+refs/heads/*:refs/remotes/origin/*"&lt;/span&gt;
git fetch &lt;span class="nt"&gt;--all&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Skip this and you can only see the default branch. Everything else on the remote is invisible.&lt;/p&gt;

&lt;h3&gt;
  
  
  Running many sessions in parallel is resource-heavy
&lt;/h3&gt;

&lt;p&gt;Multiple Claude sessions each running tests, compilation, and a dev server adds up. Four parallel sessions runs fine on an M-series Mac with 16 GB RAM. Adjust based on your hardware.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;claude --worktree&lt;/code&gt; fails with a workspace trust error
&lt;/h3&gt;

&lt;p&gt;Run &lt;code&gt;claude&lt;/code&gt; once in the project folder to accept the trust prompt. Then &lt;code&gt;--worktree&lt;/code&gt; works:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;claude      &lt;span class="c"&gt;# accept the prompt, then Ctrl+C&lt;/span&gt;
claude &lt;span class="nt"&gt;-w&lt;/span&gt; my-task   &lt;span class="c"&gt;# now works&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Cheat Sheet
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Git commands
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Create worktree (new branch)&lt;/span&gt;
git worktree add &amp;lt;path&amp;gt; &lt;span class="nt"&gt;-b&lt;/span&gt; &amp;lt;branch&amp;gt;

&lt;span class="c"&gt;# Create worktree (existing branch)&lt;/span&gt;
git worktree add &amp;lt;path&amp;gt; &amp;lt;existing-branch&amp;gt;

&lt;span class="c"&gt;# Create worktree (from remote branch)&lt;/span&gt;
git worktree add &lt;span class="nt"&gt;-b&lt;/span&gt; &amp;lt;name&amp;gt; &amp;lt;path&amp;gt; &amp;lt;remote&amp;gt;/&amp;lt;branch&amp;gt;

&lt;span class="c"&gt;# List all worktrees&lt;/span&gt;
git worktree list

&lt;span class="c"&gt;# Remove a worktree&lt;/span&gt;
git worktree remove &amp;lt;path&amp;gt;

&lt;span class="c"&gt;# Clean up stale worktree records&lt;/span&gt;
git worktree prune

&lt;span class="c"&gt;# Lock a worktree (protect from prune)&lt;/span&gt;
git worktree lock &amp;lt;path&amp;gt;

&lt;span class="c"&gt;# Move a worktree&lt;/span&gt;
git worktree move &amp;lt;old-path&amp;gt; &amp;lt;new-path&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Git aliases
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git config &lt;span class="nt"&gt;--global&lt;/span&gt; alias.wta &lt;span class="s1"&gt;'!f() { git worktree add -b "$1" "../$1"; }; f'&lt;/span&gt;
git config &lt;span class="nt"&gt;--global&lt;/span&gt; alias.wtr &lt;span class="s1"&gt;'!f() { git worktree remove "../$1"; }; f'&lt;/span&gt;
git config &lt;span class="nt"&gt;--global&lt;/span&gt; alias.wtl &lt;span class="s1"&gt;'worktree list'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Bare clone setup
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; ~/Projects/my-project &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd&lt;/span&gt; ~/Projects/my-project
git clone &lt;span class="nt"&gt;--bare&lt;/span&gt; &amp;lt;repo-url&amp;gt; .bare
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"gitdir: ./.bare"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; .git
git config remote.origin.fetch &lt;span class="s2"&gt;"+refs/heads/*:refs/remotes/origin/*"&lt;/span&gt;
git fetch &lt;span class="nt"&gt;--all&lt;/span&gt;
git worktree add main
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Claude Code
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Named worktree&lt;/span&gt;
claude &lt;span class="nt"&gt;--worktree&lt;/span&gt; &amp;lt;name&amp;gt;
claude &lt;span class="nt"&gt;-w&lt;/span&gt; &amp;lt;name&amp;gt;

&lt;span class="c"&gt;# Auto-named&lt;/span&gt;
claude &lt;span class="nt"&gt;--worktree&lt;/span&gt;

&lt;span class="c"&gt;# From a pull request&lt;/span&gt;
claude &lt;span class="nt"&gt;--worktree&lt;/span&gt; &lt;span class="s2"&gt;"#&amp;lt;pr-number&amp;gt;"&lt;/span&gt;

&lt;span class="c"&gt;# settings.json — branch from local HEAD&lt;/span&gt;
&lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="s2"&gt;"worktree"&lt;/span&gt;: &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="s2"&gt;"baseRef"&lt;/span&gt;: &lt;span class="s2"&gt;"head"&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Files to create in your project
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# .gitignore&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;".claude/worktrees/"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; .gitignore

&lt;span class="c"&gt;# .worktreeinclude&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;".env"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; .worktreeinclude
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;".env.local"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; .worktreeinclude
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Worktrees change one thing: instead of having one branch checked out at a time, every branch is a folder. Switching context means opening a different terminal, not stashing and hoping you remember what you were doing.&lt;/p&gt;

&lt;p&gt;The bare clone setup takes five minutes to learn and gives you a cleaner structure for repos where you work across many branches regularly.&lt;/p&gt;

&lt;p&gt;If you use Claude Code, the &lt;code&gt;--worktree&lt;/code&gt; flag is the practical way to run multiple sessions without them stepping on each other. Set up &lt;code&gt;.worktreeinclude&lt;/code&gt; once, add &lt;code&gt;.claude/worktrees/&lt;/code&gt; to &lt;code&gt;.gitignore&lt;/code&gt;, and you're done.&lt;/p&gt;

</description>
      <category>claude</category>
      <category>git</category>
      <category>tooling</category>
      <category>productivity</category>
    </item>
  </channel>
</rss>
