Git Worktrees

What Is A Git Worktree?

Normally, when you clone a Git repository, you work in one project directory:

projects/
└── shop/

If shop/ has feature/checkout checked out, its files reflect that branch. Suppose an urgent fix is needed on hotfix/payment-timeout. You would usually commit or stash your unfinished changes, switch branches, make the fix, and switch back.

A Git worktree is another working directory attached to the same Git repository, letting you have another branch or commit checked out at the same time. For example:

projects/
├── shop/                  → feature/checkout
└── shop-payment-hotfix/   → hotfix/payment-timeout

You can work on the hotfix in one directory without switching the original directory away from your unfinished feature.

                    SAME GIT REPOSITORY
               commits, branches and history
                         │
             ┌───────────┴───────────┐
             │                       │
             ▼                       ▼
          shop/              shop-payment-hotfix/
     feature/checkout        hotfix/payment-timeout
       working files             working files
       staging area              staging area

This is different from cloning the repository again. A second clone has its own Git repository data. Worktrees share commits, branches, tags, the Git object database (history), and most repository configuration. Each worktree has separate files on disk, a checked-out branch or commit, HEAD (what is checked out there), a staging area (index), and uncommitted changes. A worktree gives you another workspace without creating another independent clone.

Git calls the original directory made by git clone or git init the main worktree. Directories added with git worktree add are linked worktrees. “Main worktree” describes the original directory, not the main branch: shop/ can be the main worktree while feature/checkout is checked out there.

Why Use A Worktree?

Use one when an urgent production hotfix interrupts a feature, you want to review or compare another branch without disturbing your current files, or you need two development tasks open at once. Separate directories also let different AI coding agents work on separate branches. If you have only one task and switching branches is easy, git switch is simpler.

If shop/ has an uncommitted change to Checkout.php on feature/checkout, that change does not automatically appear in shop-payment-hotfix/. Each directory has its own files and staging area. Once you make a commit, the commit belongs to the shared repository and is visible from the other worktrees, though their checked-out files do not change automatically.

Git normally prevents the same local branch from being checked out in two worktrees at once. Each directory can change files independently; if both controlled feature/checkout, they could move that branch to different commits. Use separate branches, as in shop/ on feature/checkout and shop-payment-hotfix/ on hotfix/payment-timeout, rather than putting feature/checkout in both directories.

Add Your First Worktree

The form for creating a new branch in another directory is:

git worktree add -b <new-branch> <new-directory> <starting-point>

git worktree add creates the worktree. -b hotfix/payment-timeout creates its new branch. ../shop-payment-hotfix is the directory for it, next to shop/. main is the existing branch or commit from which the new branch starts. From inside shop/, the complete command is:

git worktree add -b hotfix/payment-timeout ../shop-payment-hotfix main

Git creates the branch and the new directory, checks out the branch there, and leaves the original shop/ directory on feature/checkout with its unfinished changes. The command does not copy those uncommitted changes into the new directory.

Create And Inspect Worktrees

Suppose shop/ is your existing repository. You are in shop/ on feature/checkout, with unfinished edits, and main already exists locally. All paths below are relative to shop/.

projects/
├── shop/                 main worktree: feature/checkout
├── shop-invoice-hotfix/  linked worktree: hotfix/invoice-total
└── shop-review/          linked worktree: review/invoice-tests

First inspect the current state. Creating a linked worktree does not require the original directory to be clean.

git status --short
git branch --show-current
git worktree add -b hotfix/invoice-total ../shop-invoice-hotfix main
git worktree list
git -C ../shop-invoice-hotfix branch --show-current

If the intended starting point is the latest fetched remote branch, fetch first and use origin/main instead of main. git worktree list shows paths, commits, and branch names, including the main worktree.

You can now work in the hotfix directory without switching the feature checkout:

cd ../shop-invoice-hotfix
composer install
php -l src/InvoiceTotal.php
git status --short
git add src/InvoiceTotal.php tests/InvoiceTotalTest.php
git commit -m "Fix invoice total calculation"
cd ../shop

The edit and test commands assume those project files exist and that you have made the fix. Review and merge the hotfix through your normal workflow before deleting its branch.

To inspect an existing local branch in another worktree, choose one that is not already checked out. For example, after creating a review branch at main:

git branch review/invoice-tests main
git worktree add ../shop-review review/invoice-tests
git worktree list

The last argument names an existing branch. Without -b, Git may create a branch named after the new directory when no branch argument is given; supplying the branch explicitly makes the intent clear. For read-only inspection of a tag, git worktree add --detach ../shop-old-release v1.4.0 avoids moving any branch. Create a branch before making lasting commits from a detached checkout.

Keep Each Workspace Usable

Tracked files appear in each worktree. Ignored files such as .env, vendor/, caches, and local uploads usually do not. Set up each worktree's own .env from the project's safe template and install dependencies as needed; never assume secrets or local databases are copied. Keep a shared mutable database out of simultaneous test runs, or give each worktree its own test database.

If two worktrees start the same Docker Compose project, they may collide on container names, volumes, networks, and host ports. Use distinct Compose project names (for example, docker compose -p shop-hotfix up) and non-conflicting host port mappings. A fixed container_name still collides even when project names differ. Stop containers and check local data before removing a worktree.

Parallel AI Coding Agents

Starting from shop/ on main, suppose checkout validation and an invoice bug fix need to happen in parallel. A developer, script, IDE, or orchestration tool can create a worktree for each task and then run an agent inside it. AI coding agents do not inherently or automatically use Git worktrees.

git worktree add -b feature/checkout-validation ../shop-checkout-validation main
git worktree add -b fix/invoice-rounding ../shop-invoice-rounding main
projects/
├── shop/                       main worktree
├── shop-checkout-validation/   feature/checkout-validation
└── shop-invoice-rounding/      fix/invoice-rounding

A developer can open a separate terminal or agent session in each worktree. For example, with the Codex CLI, start one session from shop/:

cd ../shop-checkout-validation
codex

In another terminal, also starting from shop/:

cd ../shop-invoice-rounding
codex

codex is only an example. The same Git workflow works with other coding agents or ordinary human development. Each worktree has its own checked-out files, HEAD, staging area (index), current branch, and uncommitted changes. Repository objects, commits, branches, and history remain shared. To inspect the checkouts:

git worktree list

After work on the branches, representative output might look like this; the commit hashes are illustrative:

/home/user/projects/shop                       abc1234 [main]
/home/user/projects/shop-checkout-validation   def5678 [feature/checkout-validation]
/home/user/projects/shop-invoice-rounding      987abcd [fix/invoice-rounding]

In one directory, two agents could edit the same files, replace each other's uncommitted work, switch branches underneath each other, change generated files or dependencies during the other task, and leave a mixed diff. Separate worktrees give each task its own branch and filesystem checkout:

main
│
├── feature/checkout-validation  → AI agent 1
├── fix/invoice-rounding         → AI agent 2
└── main                         → developer

As described above, worktrees do not provide full runtime isolation. Agents can still collide over .env settings, databases, Redis, queues, Docker container or Compose project names, volumes, host ports, development servers, and shared caches. For example, if the application supports it, use separate ports:

Agent 1: APP_PORT=8001
Agent 2: APP_PORT=8002

Docker Compose may also need distinct project names. Review and test each agent's changes before integrating them. Worktrees solve workspace isolation, not code review or merge conflicts; the branches may still conflict when merged. After reviewing and integrating the branches, stop running processes and remove the clean temporary worktrees from shop/:

git worktree remove ../shop-checkout-validation
git worktree remove ../shop-invoice-rounding

Removing the worktrees does not delete their branches.

Remove And Repair Safely

After the hotfix is merged, inspect both linked worktrees and stop any servers. From shop/:

git -C ../shop-invoice-hotfix status --short
git -C ../shop-review status --short
git worktree remove ../shop-invoice-hotfix
git worktree remove ../shop-review
git worktree list

remove deletes a clean linked worktree and its Git registration. It refuses a dirty worktree by default, including untracked files; save wanted work before retrying. It does not delete the branch. After verifying the branch is merged and no longer needed, delete it separately with git branch -d hotfix/invoice-total (and likewise for a finished review branch). The main worktree cannot be removed with this command.

For a planned relocation of an existing linked worktree, use git worktree move <worktree> <new-path> (with the destination parent directory already in place):

git worktree move ../shop-review ../archive/shop-review

If a worktree was moved manually, the main repository was moved, or Git's administrative links became outdated, use git worktree repair to reconnect them. If a worktree directory was deleted instead, Git may retain a stale registration; inspect git worktree list, then use git worktree prune -n to preview and git worktree prune to remove stale records. Pruning does not recover deleted files.

A worktree is a checkout, not a backup. Commit valuable work, review the diff, and clean up its processes and files deliberately.

Check Your Understanding

Create a disposable repository, add a second branch with git worktree add -b, and add an existing branch in a third directory. Confirm git worktree list and git status in each. Make an uncommitted edit in one directory and observe that it does not appear in the other. Remove the clean linked worktrees and confirm their branches still exist.

See the Git worktree manual for the command's full behaviour.

Practice

Handle An Urgent Fix Without Disturbing Your Feature

You are working in shop/ on feature/checkout with unfinished, uncommitted changes. An urgent production bug needs a fix based on the latest fetched main. Do not commit, stash, or discard the unfinished feature, or switch the existing worktree away from feature/checkout.

Create ../shop-payment-hotfix on a new branch named hotfix/payment-timeout.

  1. Inspect your current branch and uncommitted changes.
  2. Fetch the latest remote refs.
  3. Create hotfix/payment-timeout in a new linked worktree based on origin/main.
  4. Confirm the new worktree is on the correct branch.
  5. List all worktrees.
  6. Make and commit a small fix in the hotfix worktree.
  7. Return to the original shop/ directory.
  8. Confirm it is still on feature/checkout.
  9. Confirm its original uncommitted changes are still present.
  10. After the hotfix has been reviewed and merged, remove the linked worktree.
  11. Confirm the Git branch still exists after removing the worktree.
  12. Delete the branch separately once it is genuinely merged and no longer needed.
Show solution
git status --short
git branch --show-current

git fetch origin

git worktree add -b hotfix/payment-timeout ../shop-payment-hotfix origin/main

git worktree list
git -C ../shop-payment-hotfix branch --show-current

The original shop/ directory remains on feature/checkout, with its uncommitted files still there. ../shop-payment-hotfix is a separate checkout on hotfix/payment-timeout, starting from the fetched origin/main.

Work in the new checkout:

cd ../shop-payment-hotfix

git status

Make the required code and test change, then stage, inspect, and commit it:

git add -A
git status
git commit -m "Fix payment timeout handling"

Return to the original project:

cd ../shop

git branch --show-current
git status --short

This should still show feature/checkout and the unfinished changes noted at the start. Have the hotfix reviewed, tested, and merged through your normal workflow before cleanup. Once that is done, check that its checkout is clean:

git -C ../shop-payment-hotfix status --short
git worktree remove ../shop-payment-hotfix

git worktree list
git branch --list hotfix/payment-timeout

git worktree remove removes the linked checkout and its worktree registration. It does not delete hotfix/payment-timeout; the branch listing should still show it. Once the branch is genuinely merged and no longer needed, delete it separately:

git branch -d hotfix/payment-timeout

-d is deliberately the safe deletion form. It may refuse if Git does not consider the branch merged. Check the integration status before trying to delete it.

The original feature was never stashed, committed, discarded, or switched away from. The urgent fix was developed independently because it had its own worktree and branch.