Overview
If you've worked with Claude Code for a while, you've probably seen this message in your terminal:
Tip: Use git worktrees to run multiple Claude sessions in parallel.
If you're already using worktrees to run multiple Claude sessions in parallel, great. You're already familiar with the concepts LaborForest builds upon.
If you're new to git worktrees, below is a surface-level overview:
- Git worktrees allow multiple branches of the same repository to be checked out in different directories simultaneously
- The same branch cannot be checked out in multiple worktrees at the same time
- Git worktrees do not require cloning the same repository more than once
- You can switch contexts and work on different things in parallel without affecting other branches
- Git worktrees have been built into Git since 2015, but AI coding agents are greatly popularizing their adoption
Git worktrees in practice
In practice, a development workflow can look something like this:
- Create a new branch or check out an existing branch for the feature or bug fix you're working on
- Plan your changes and kick off implementation using Claude Code
- While Claude Code works, create a new git worktree, on a new or existing branch, for another initiative
- In the new worktree's directory, plan your changes and kick off implementation using Claude Code
- Repeat steps 3 and 4 as much as you'd like
- When changes on a branch within a worktree are complete: commit and push the changes; open a pull request and remove the worktree
This allows you to switch between multiple Claude Code sessions on different branches within the same project, at the same time.
No more stashing changes to check behavior on another branch and no more abandoning current progress to tackle something more urgent.
Pain points
This works great in theory, but there's a part I glossed over above:
What if your repository requires some setup for the local development environment?
For some projects, this can just be a few commands you need to run per new worktree. For other projects, this can be a tedious sequence of steps, time that adds up and takes away from developer velocity.
Yes, you can write setup scripts for each project to address this. However, unless you have significant control over each repository, standardization can be difficult. You can easily end up with a collection of brittle bash spaghetti.
Take, for example, a Laravel project using Herd Pro for services. For each new worktree, you may need to:
- create an environment file from the example in the project
- update the environment file variables for just this worktree (to maintain isolation)
APP_URL,AWS_BUCKET,DB_DATABASE,REDIS_PREFIX, etc.
- create a local S3-compatible bucket for storage using MinIO
- create a new MySQL schema
- install composer dependencies
- install NPM dependencies
- build front-end assets
- link and secure the site in Laravel Herd
- seed the new database with test data
Then of course, when you are done with a worktree, you should tear down these resources too.
Introducing LaborForest
LaborForest is a completely free and open source desktop app for macOS to manage git worktrees and local action workflows.
With LaborForest, all the steps above can become a few clicks. You can easily spin up or tear down local development environments, coupled to git worktrees, and visually keep track of your worktrees and development state of each.
LaborForest in action
The below gif is a demonstration of using LaborForest for a local Laravel project.
The steps taken are:
- Create a new workspace for a new branch
feature/example, usingmainas the base branch - Set up a local development environment for the new workspace (LaborForest term for the combination of a worktree and branch)
- Observe the sequence of steps executing in realtime via a UI loosely inspired by GitHub Actions
- Open a browser using the new environment's
APP_URLto confirm the local site loads successfully

An example with Laravel
Let's dive into what was required to record the above gif.
Project assumptions
The demonstration above was with a newly generated Laravel project, configured to match my preferences and setup with Herd.
SESSION_DRIVERset toredisQUEUE_CONNECTIONset toredisREDIS_PREFIXset toexample-laravel-database-AWS_BUCKETset toexample-laravelAWS_ACCESS_KEY_IDset toherdAWS_SECRET_ACCESS_KEYset tosecretkeyAWS_URLset tohttp://localhost:9000/labor-forest-example-laravelAWS_ENDPOINTset tohttp://localhost:9000
You are free to customize your configuration and workflows as needed for your project.
Example workflows
Upon loading a project in LaborForest, a new directory is created at the root of your project: .laborforest.
It is up to you if you'd like to commit this directory or ignore it by adding the path to .git/info/exclude.
Workflows are .yaml files placed in the .laborforest/workflows directory within your project.
Each .yaml file describes:
- The status the workspace must be in to allow running the workflow (
suspended,ready, ornull) - The status the workspace will be in upon successful execution of the entire workflow (
suspendedorready) - Where in the list of workflows this specific workflow should appear (sort order)
- The list of steps the workflow executes when run
Workflows: up, refresh, and down
The example workflows for a Laravel project, shipped with LaborForest, are below.
up.yamlis the workflow used when spinning up a new local development environment for a new workspace- copies the
.envfile from the primary project directory (if workspace is not the primary workspace) - updates the
.envfile with workspace-specific configuration, for isolation (if workspace is not the primary workspace) - creates a local S3 bucket (if it doesn't already exist)
- creates the MySQL schema (if it doesn't already exist)
- installs composer dependencies
- installs NPM dependencies
- builds front-end assets
- links and secures the Laravel Herd site
- runs the refresh workflow below
- copies the
1resource_type: workflow 2require_status: suspended 3ending_status: ready 4sort_order: 0 5steps: 6 - name: 'Copy .env file from primary project directory' 7 type: shell 8 if: 'test "{{ WORKSPACE_DIR }}" != "{{ PROJECT_PRIMARY_DIR }}"' 9 run: 'cp "{{ PROJECT_PRIMARY_DIR }}/.env" .env'10 - name: 'Update .env file'11 type: update_env12 if: 'test "{{ WORKSPACE_DIR }}" != "{{ PROJECT_PRIMARY_DIR }}"'13 map:14 APP_URL: 'https://{{ WORKSPACE_SLUG_KEBAB }}.test'15 AWS_BUCKET: '{{ WORKSPACE_SLUG_KEBAB }}'16 DB_DATABASE: '{{ WORKSPACE_SLUG_SNAKE }}'17 REDIS_PREFIX: '{{ WORKSPACE_SLUG_KEBAB }}-database-'18 - name: 'Create S3 bucket'19 type: shell20 unless: 'aws --endpoint={{ ENV_AWS_ENDPOINT }} s3api head-bucket --bucket {{ ENV_AWS_BUCKET }}'21 run: 'aws --endpoint={{ ENV_AWS_ENDPOINT }} s3api create-bucket --bucket {{ ENV_AWS_BUCKET }}'22 env:23 AWS_ACCESS_KEY_ID: '{{ ENV_AWS_ACCESS_KEY_ID }}'24 AWS_SECRET_ACCESS_KEY: '{{ ENV_AWS_SECRET_ACCESS_KEY }}'25 AWS_DEFAULT_REGION: '{{ ENV_AWS_DEFAULT_REGION }}'26 - name: 'Create MySQL schema'27 type: shell28 run: 'mysql -h {{ ENV_DB_HOST }} -P {{ ENV_DB_PORT }} -u {{ ENV_DB_USERNAME }} $([[ -z "{{ ENV_DB_PASSWORD }}" ]] && echo "--skip-password" || echo "-p{{ ENV_DB_PASSWORD }}") -e "CREATE DATABASE IF NOT EXISTS {{ ENV_DB_DATABASE }} CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci"'29 - name: 'Install Composer dependencies'30 type: shell31 run: 'composer install -v --ansi --no-interaction'32 - name: 'Install NPM dependencies'33 type: shell34 run: 'npm ci'35 - name: 'Build assets'36 type: shell37 run: 'npm run build'38 - name: 'Link Laravel Herd site'39 type: shell40 run: 'herd link --no-interaction --secure {{ WORKSPACE_SLUG_KEBAB }}'41 - name: 'Refresh application data'42 type: workflow43 run: refresh
refresh.yamlis the workflow used to reset a local development environment for an existing workspace- runs fresh migrations
- empties the local S3 bucket
- clears the default queue
- wipes the Laravel log file
- runs the database seeder
1resource_type: workflow 2require_status: ready 3ending_status: ready 4sort_order: 1 5steps: 6 - name: 'Run fresh migrations' 7 type: shell 8 run: 'php artisan -vvv migrate:fresh' 9 - name: 'Empty S3 bucket'10 type: shell11 if: 'aws --endpoint={{ ENV_AWS_ENDPOINT }} s3api head-bucket --bucket {{ ENV_AWS_BUCKET }}'12 run: 'aws --endpoint={{ ENV_AWS_ENDPOINT }} s3 rm s3://{{ ENV_AWS_BUCKET }} --recursive --include="*"'13 env:14 AWS_ACCESS_KEY_ID: '{{ ENV_AWS_ACCESS_KEY_ID }}'15 AWS_SECRET_ACCESS_KEY: '{{ ENV_AWS_SECRET_ACCESS_KEY }}'16 AWS_DEFAULT_REGION: '{{ ENV_AWS_DEFAULT_REGION }}'17 - name: 'Clear the default queue'18 type: shell19 run: 'php artisan queue:clear'20 - name: 'Wipe logs'21 type: shell22 run: 'truncate -s 0 storage/logs/laravel.log'23 - name: 'Run database seeder'24 type: shell25 run: 'php artisan -vvv db:seed'
down.yamlis the workflow used to tear down a local development environment for a workspace- empties the local S3 bucket (prerequisite for deleting it)
- deletes the local S3 bucket
- drops the MySQL schema
- removes the Laravel Herd site
1resource_type: workflow 2require_status: ready 3ending_status: suspended 4sort_order: 100 5steps: 6 - name: 'Empty S3 bucket' 7 type: shell 8 if: 'aws --endpoint={{ ENV_AWS_ENDPOINT }} s3api head-bucket --bucket {{ ENV_AWS_BUCKET }}' 9 run: 'aws --endpoint={{ ENV_AWS_ENDPOINT }} s3 rm s3://{{ ENV_AWS_BUCKET }} --recursive --include="*"'10 env:11 AWS_ACCESS_KEY_ID: '{{ ENV_AWS_ACCESS_KEY_ID }}'12 AWS_SECRET_ACCESS_KEY: '{{ ENV_AWS_SECRET_ACCESS_KEY }}'13 AWS_DEFAULT_REGION: '{{ ENV_AWS_DEFAULT_REGION }}'14 - name: 'Delete S3 bucket'15 type: shell16 if: 'aws --endpoint={{ ENV_AWS_ENDPOINT }} s3api head-bucket --bucket {{ ENV_AWS_BUCKET }}'17 run: 'aws --endpoint={{ ENV_AWS_ENDPOINT }} s3api delete-bucket --bucket {{ ENV_AWS_BUCKET }}'18 env:19 AWS_ACCESS_KEY_ID: '{{ ENV_AWS_ACCESS_KEY_ID }}'20 AWS_SECRET_ACCESS_KEY: '{{ ENV_AWS_SECRET_ACCESS_KEY }}'21 AWS_DEFAULT_REGION: '{{ ENV_AWS_DEFAULT_REGION }}'22 - name: 'Drop MySQL schema'23 type: shell24 run: 'mysql -h {{ ENV_DB_HOST }} -P {{ ENV_DB_PORT }} -u {{ ENV_DB_USERNAME }} $([[ -z "{{ ENV_DB_PASSWORD }}" ]] && echo "--skip-password" || echo "-p{{ ENV_DB_PASSWORD }}") -e "DROP DATABASE IF EXISTS {{ ENV_DB_DATABASE }}"'25 - name: 'Remove Laravel Herd site'26 type: shell27 run: 'herd unlink --no-interaction {{ WORKSPACE_SLUG_KEBAB }}'
Claude Code in parallel
With these workflows in place, we can now easily manage our local development environments per workspace.
In a few clicks, we can:
- create a new workspace
- spin up the local development environment for the new workspace
- launch our favorite IDE or Terminal with a working directory of the workspace
This allows us to use Claude Code on different copies of the same project in tandem. While waiting for Claude Code to finish responding to one prompt, you can be prompting in another isolated environment for the same project.
Conclusion
LaborForest is free and entirely open source. You can build from source, or grab the latest release binary for your Mac's architecture on the releases page.
Read the Docs
I encourage you to read the documentation included in the LaborForest repository.
Thanks for reading :)