<?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>Forem: Jakub Gause</title>
    <description>The latest articles on Forem by Jakub Gause (@gause).</description>
    <link>https://forem.com/gause</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.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3787690%2Fd572832c-22d8-4507-b9ec-012c7eb384aa.jpeg</url>
      <title>Forem: Jakub Gause</title>
      <link>https://forem.com/gause</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/gause"/>
    <language>en</language>
    <item>
      <title>Git Worktrees with Claude Code, Laravel, and Herd</title>
      <dc:creator>Jakub Gause</dc:creator>
      <pubDate>Fri, 27 Feb 2026 22:41:47 +0000</pubDate>
      <link>https://forem.com/gause/git-worktrees-with-claude-code-laravel-and-herd-49d1</link>
      <guid>https://forem.com/gause/git-worktrees-with-claude-code-laravel-and-herd-49d1</guid>
      <description>&lt;p&gt;I've been running multiple Claude Code sessions in parallel. One agent is refactoring the booking flow, another is building a new integration, and I need to push a hotfix to production. All at the same time. All on the same codebase.&lt;/p&gt;

&lt;p&gt;If you've ever tried this with &lt;code&gt;git stash&lt;/code&gt; and branch switching, you know the pain. Uncommitted changes, shared databases getting corrupted by competing migrations. It's a mess.&lt;/p&gt;

&lt;p&gt;Git worktrees fixed all of that for me.&lt;/p&gt;

&lt;h2&gt;
  
  
  What are git worktrees?
&lt;/h2&gt;

&lt;p&gt;A worktree is a separate checkout of your repository in a different directory, linked to the same &lt;code&gt;.git&lt;/code&gt; history. Each worktree has its own branch, its own working tree, its own &lt;code&gt;node_modules&lt;/code&gt;, its own &lt;code&gt;.env&lt;/code&gt;. But they share the same git history, so commits, branches, and remotes are all in sync.&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 .claude/worktrees/feature-billing feature-billing
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it. You now have a full copy of your project in &lt;code&gt;.claude/worktrees/feature-billing&lt;/code&gt;, checked out on the &lt;code&gt;feature-billing&lt;/code&gt; branch. Your main directory stays exactly where it was.&lt;/p&gt;

&lt;p&gt;No stashing. No context switching. No "wait, which branch am I on?"&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this matters for AI coding agents
&lt;/h2&gt;

&lt;p&gt;Claude Code (and similar tools) work best when they have a stable, isolated environment. An agent planning a database migration shouldn't accidentally break the working directory where another agent is running tests.&lt;/p&gt;

&lt;p&gt;With worktrees, each Claude session gets its own sandbox. They can't step on each other's toes. One agent can be mid-migration on a feature branch while another is writing tests on main, and neither knows the other exists.&lt;/p&gt;

&lt;p&gt;But here's the thing: a raw &lt;code&gt;git worktree add&lt;/code&gt; only gives you the files. For a Laravel project, you also need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Composer and npm dependencies installed&lt;/li&gt;
&lt;li&gt;Its own database (not the one your main app is using)&lt;/li&gt;
&lt;li&gt;Its own &lt;code&gt;.env&lt;/code&gt; with the right &lt;code&gt;APP_URL&lt;/code&gt; and &lt;code&gt;DB_DATABASE&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;A working local domain (if you're using Laravel Herd)&lt;/li&gt;
&lt;li&gt;A Vite dev server on a port that doesn't conflict&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Setting all of that up manually every time would defeat the purpose.&lt;/p&gt;

&lt;h2&gt;
  
  
  One command to create everything
&lt;/h2&gt;

&lt;p&gt;I wrote a setup script that does the whole thing. Create a worktree, and within a minute you have a fully functional Laravel environment:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./claude-worktree.sh feature-billing
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What happens behind the scenes:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Creates the git worktree with a new branch&lt;/li&gt;
&lt;li&gt;Copies &lt;code&gt;.env&lt;/code&gt; from the main project and adjusts it (new &lt;code&gt;APP_URL&lt;/code&gt;, &lt;code&gt;DB_DATABASE&lt;/code&gt;, &lt;code&gt;VITE_PORT&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Finds a free port for Vite (scans 5100–5199, checks both &lt;code&gt;.env&lt;/code&gt; files and &lt;code&gt;lsof&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Runs &lt;code&gt;composer install&lt;/code&gt; and &lt;code&gt;pnpm install&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Creates a fresh database&lt;/li&gt;
&lt;li&gt;Runs migrations and seeders&lt;/li&gt;
&lt;li&gt;Links the worktree to Laravel Herd with HTTPS&lt;/li&gt;
&lt;li&gt;Starts Claude Code in the new directory&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The result:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Branch:    feature-billing
Path:      /project/.claude/worktrees/feature-billing
URL:       https://feature-billing.myapp.test
Database:  myapp_feature_billing
Vite port: 5101
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A completely isolated environment. Its own database, its own domain, its own port. You can open it in a browser alongside your main app and they don't interfere at all.&lt;/p&gt;

&lt;h2&gt;
  
  
  One command to tear it down
&lt;/h2&gt;

&lt;p&gt;When you're done:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./claude-worktree-remove.sh feature-billing
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This drops the database, removes the Herd link and TLS certificate, deletes the worktree, and cleans up the branch. Gone. No leftover databases, no phantom Herd domains.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Vite port problem
&lt;/h2&gt;

&lt;p&gt;This one was surprisingly annoying. Vite defaults to port 5173, and if you have three worktrees all trying to run dev servers, they'll either fight over the port or silently pick random ones.&lt;/p&gt;

&lt;p&gt;The setup script assigns each worktree a unique port from a reserved range (5100–5199). It checks both existing worktree &lt;code&gt;.env&lt;/code&gt; files and active listeners to avoid conflicts. Then it patches the &lt;code&gt;package.json&lt;/code&gt; dev script directly:&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="nl"&gt;"dev"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"vite --port 5101"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No config file gymnastics. The port is baked into the command. It works every time.&lt;/p&gt;

&lt;p&gt;On the Laravel side, the Blade directive that loads Vite assets reads the port from &lt;code&gt;.env&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$port&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Env&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'VITE_PORT'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'5173'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s1"&gt;'&amp;lt;script type="module" src="https://127.0.0.1:'&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="nv"&gt;$port&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s1"&gt;'/resources/js/app.ts"&amp;gt;&amp;lt;/script&amp;gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This only runs in development. Production uses the CDN build, completely unaffected.&lt;/p&gt;

&lt;h2&gt;
  
  
  Hooks for safety
&lt;/h2&gt;

&lt;p&gt;If you're using Claude Code, you can add hooks that automatically handle worktree setup and cleanup. A &lt;code&gt;PreToolUse&lt;/code&gt; hook on the Bash tool can detect when Claude enters an unconfigured worktree and run the setup script. Another hook can detect &lt;code&gt;git worktree remove&lt;/code&gt; commands and run cleanup before the directory is deleted.&lt;/p&gt;

&lt;p&gt;This means even if Claude creates or removes worktrees on its own (which it does with its built-in worktree support), everything gets set up and torn down properly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Being honest about parallel sessions
&lt;/h2&gt;

&lt;p&gt;I'll be honest. I can't effectively run more than 2–3 Claude sessions at once. I've tried. It's like playing StarCraft 2 and trying to manage 10 bases simultaneously. You end up switching between them so frantically that you play worse than if you'd just focused on one.&lt;/p&gt;

&lt;p&gt;But for me, that's not really the point. Maybe you're better at juggling than I am, and if so, go for it. But the real value for me isn't just in running five agents at the same time. It's in &lt;strong&gt;isolation and persistence&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;You can start a feature branch, let Claude plan and scaffold for an hour, then pause it. Switch to your main branch, push a hotfix, deal with a support ticket. Come back to the feature worktree later and everything is exactly where you left it. The database has the right migrations, the dependencies are installed, the branch is untouched.&lt;/p&gt;

&lt;p&gt;No stash popping. No "where was I?" No accidentally running migrations on the wrong database.&lt;/p&gt;

&lt;h2&gt;
  
  
  Set it up in your project
&lt;/h2&gt;

&lt;p&gt;I packaged the whole thing as a &lt;a href="https://docs.anthropic.com/en/docs/claude-code" rel="noopener noreferrer"&gt;Claude Code skill&lt;/a&gt;. Install it, run &lt;code&gt;/setup-worktrees&lt;/code&gt;, and Claude will generate everything — the create script, the remove script, the cleanup logic, the hooks, all of it.&lt;/p&gt;

&lt;p&gt;It detects your package manager, reads your &lt;code&gt;.env&lt;/code&gt; for database credentials, derives Herd subdomains from your app name. Works with MySQL, PostgreSQL, and SQLite. You don't need to configure anything upfront.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What you'll need:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A Laravel project served by &lt;a href="https://herd.laravel.com" rel="noopener noreferrer"&gt;Laravel Herd&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;A database (MySQL, PostgreSQL, or SQLite)&lt;/li&gt;
&lt;li&gt;A git repository&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;What you'll get:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;claude-worktree.sh&lt;/code&gt; — one command to create a fully isolated environment&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;claude-worktree-remove.sh&lt;/code&gt; — one command to tear it all down&lt;/li&gt;
&lt;li&gt;Claude Code hooks for automatic setup/cleanup&lt;/li&gt;
&lt;li&gt;Each worktree gets its own database, Herd domain, and Vite port&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Install the skill:&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;/plugin marketplace add gausejakub/claude-skills
/plugin &lt;span class="nb"&gt;install &lt;/span&gt;laravel-worktrees@gause-claude-skills
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then run &lt;code&gt;/setup-worktrees&lt;/code&gt; in Claude Code. That's it. One slash command, fully configured worktree system.&lt;/p&gt;

&lt;p&gt;If you're using a different stack, the principle is the same. Each worktree needs its own isolated runtime: its own database, its own dev server port, its own local URL. Automate the setup, automate the teardown, and branch switching becomes a non-issue.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;&lt;a href="https://gause.cz/blog/git-worktrees-with-claude-code-laravel-and-herd" rel="noopener noreferrer"&gt;Blog post&lt;/a&gt; · &lt;a href="https://github.com/gausejakub/claude-skills" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>laravel</category>
      <category>php</category>
      <category>git</category>
      <category>ai</category>
    </item>
  </channel>
</rss>
