No stash, no context switching, no chaos
Constantly jumping between a feature branch, a hotfix, and a pull request review wastes time on stashing, switching, and waiting for freshly installed dependencies. Git worktree solves this structurally: multiple branches at once in their own directories, one shared repository, no lost context, and no interrupted work.
Table of Contents
- 1. The problem: stash and branch switching cost time and context
- 2. git worktree add, list, and remove at a glance
- 3. Practical case: testing a hotfix while a feature is mid-work
- 4. Running long builds and test suites while coding elsewhere
- 5. Reviewing pull requests in dedicated worktrees
- 6. Constraints: one branch per worktree, shared .git directory
- 7. Magento- and PHP-specific pitfalls: vendor, Docker, .env
- 8. Cleanup: prune, lock, and removing old worktrees
- 9. Worktree compared to stash, clone, and branch switching
- 10. Summary
- 11. FAQ
1. The problem: stash and branch switching cost time and context
The classic Git day looks like this: you're working on a feature, an urgent bug report comes in, and suddenly you have to use git stash to set aside your current state, switch to main, build and test the hotfix, merge it, switch back to the feature branch, and pop the stash again. Every one of these steps is a potential failure point: forgotten stashes, accidentally committed half-finished changes, or a composer install that reruns after every switch because composer.lock differs between branches.
On Magento projects the problem gets worse: a branch switch can mean that generated files in generated/, static content in pub/static, and sometimes even the database state no longer match the checked-out code. Forget to run bin/magento setup:upgrade afterward, and you'll be chasing error messages that have nothing to do with the actual bug. The root problem is always the same: a single working directory can only represent one branch state at a time.
Git worktree solves this structurally by attaching multiple working directories to a single repository. Each directory has its own checked-out branch, its own working directory and staging area state, but shares the object database, configuration, and commit history with the other worktrees. The hotfix runs in its own folder, the feature stays untouched in another, and git stash is no longer needed.
2. git worktree add, list, and remove at a glance
The command git worktree add <path> <branch> creates a new working directory at the given path and checks out the specified branch there. If the branch doesn't exist yet, -b <new-branch> creates it right when the worktree is added, starting from the current HEAD or an explicitly given starting point. The new folder behaves like a complete repository: git status, git log, and git diff all work normally there, just isolated from the main directory.
git worktree list shows all active worktrees with their path, current commit, and checked-out branch, a compact overview that becomes essential once several parallel directories exist and you need to keep track. To remove a worktree that's no longer needed, use git worktree remove <path>, which deletes both Git's internal entry and the physical folder, provided there are no uncommitted changes. --force can override that if needed, but should be used deliberately, not routinely.
# Add a new worktree directory for an existing branch
git worktree add ../shop-hotfix hotfix/checkout-crash
# Create a new branch directly while adding the worktree
git worktree add -b feature/wishlist-export ../shop-wishlist origin/main
# List all active worktrees with path, commit, and branch
git worktree list
# /home/dev/shop a1b2c3d [main]
# /home/dev/shop-hotfix e4f5g6h [hotfix/checkout-crash]
# /home/dev/shop-wishlist h7i8j9k [feature/wishlist-export]
# Remove a worktree directory that is no longer needed
git worktree remove ../shop-hotfix
3. Practical case: testing a hotfix while a feature is mid-work
The most common use case for worktrees in day-to-day development: a feature is 60 percent done, several files have been changed but aren't in a clean state for a commit yet. At the same time, support reports a critical checkout bug that needs fixing immediately. Instead of stashing the half-finished state and risking forgetting it later or restoring it incorrectly, you simply add a second worktree, check out main there, and create the hotfix branch.
The key advantage: both directories exist on disk at the same time, both are fully functional, and you can switch between them at any point just by switching terminal windows or the opened editor folder, no Git command required. The feature state stays exactly as it was, including uncommitted changes, open editor tabs, and running Docker containers, provided each worktree uses its own compose configuration.
After the hotfix, the branch gets merged as usual and the temporary worktree is removed again. Importantly, both worktrees see the same commit history and the same remote references, a git fetch in one worktree makes new commits immediately visible in all others, since they share the object database.
# While the main directory has a half-finished feature:
# add a new worktree for the urgent hotfix
git worktree add -b hotfix/checkout-crash ../shop-hotfix origin/main
cd ../shop-hotfix
# Develop, test, and commit the hotfix
git add src/app/code/Mironsoft/Checkout/Model/PaymentValidator.php
git commit -m "Fix null pointer in payment validator on empty cart"
# Back in the main directory, merge the hotfix from there
cd ../shop
git merge hotfix/checkout-crash
# Worktree no longer needed, clean it up
git worktree remove ../shop-hotfix
4. Running long builds and test suites while coding elsewhere
Magento test suites, especially integration tests or a full bin/magento setup:di:compile run, can take several minutes up to a quarter of an hour. In the classic setup this blocks the working directory: while the build is running, you can't switch branches without aborting the run or risking inconsistent results. With a second worktree, the long-running process can be kicked off in its own directory while normal coding continues in the main directory.
This is especially valuable for CI-like local runs: PHPUnit suites, PHPStan at level 5 across an entire module, or a full Tailwind build followed by setup:static-content:deploy. All of these processes tie up CPU and I/O, but no longer block the actual editing workflow, because they run in a physically separate directory with its own filesystem state.
A useful pattern: create a permanent worktree named shop-ci that always tracks the current feature branch and is used exclusively for long check runs. After every git push in the main directory, a simple git pull there is enough to kick off the check run with the latest state, while the main directory is already working on the next commit.
# Add a permanent worktree for long-running test runs
git worktree add ../shop-ci feature/wishlist-export
cd ../shop-ci
# Start a long run in the background, main directory stays free
bin/analyse app/code/Mironsoft/Wishlist --level=5 &
bin/magento setup:di:compile
# Keep working in the main directory in parallel, independent of the run
cd ../shop
git add src/app/code/Mironsoft/Wishlist/Model/ExportProcessor.php
git commit -m "Add CSV export chunking for large wishlists"
5. Reviewing pull requests in dedicated worktrees
Reviewing a pull request locally classically means stashing your own work state, checking out the PR branch, clicking through the code manually or opening it in the editor, and switching back afterward. With a dedicated review worktree that detour disappears entirely: git worktree add ../shop-review origin/pr-branch checks the branch out into a separate folder, the editor opens that folder as its own project, and your actual work state stays completely untouched.
This pattern is especially useful for reviews where you actually want to run the code locally, for example checking a UI change in the browser or interactively testing a Hyvä Alpine.js component, instead of relying only on the diff in the pull request interface. Since the review worktree is a standalone directory, it can even run its own Docker container with its own port while the regular development container in the main directory keeps running unaffected.
Once the review is done, the worktree is simply removed, without ever having left your own branch context. For frequent reviews, a fixed folder like shop-review that gets reused with git worktree remove and a fresh git worktree add for every new PR is worth setting up, instead of picking a new path each time.
6. Constraints: one branch per worktree, shared .git directory
The most important constraint of git worktree: the same branch cannot be checked out in two worktrees at once. Git actively prevents this with an error, because two parallel working directories with the same branch would inevitably lead to conflicting HEAD states. If you just want to look at the same branch in a second directory without editing it, you can instead create a detached state with git worktree add --detach at the desired commit.
All worktrees share the same .git directory in the main repository, the additional worktrees only contain a .git file pointing back to that directory. That means: object database, configuration, remotes, and hooks are shared, but the index, HEAD, and merge state are separate per worktree. If you accidentally delete the main directory, you take down every dependent worktree with it, so the original clone directory should never be removed carelessly.
Extra disk space is another factor: every worktree needs a full copy of the working directory, and on Magento projects with vendor/, generated/, and pub/static that quickly adds up to several gigabytes per directory. The object database itself isn't duplicated, but the actually checked-out files are, which costs meaningful disk space with many parallel worktrees.
7. Magento- and PHP-specific pitfalls: vendor, Docker, .env
The biggest practical pitfall on Magento projects: vendor/ isn't tracked by Git and simply doesn't exist yet in any new worktree. After git worktree add, a standalone bin/composer install is almost always needed in the new directory, which can take several minutes for large dependency trees. If you do this often, a local Composer cache directory shared across all worktrees saves repeated downloads.
.env files, local docker-compose.override.yml tweaks, and generated directories like generated/ or var/ are typically excluded via .gitignore and need to be maintained separately per worktree. On a Mark Shust Docker setup, that concretely means: each worktree needs either its own project name and ports in the compose configuration, or the containers are used deliberately in sequence, stopping one worktree before spinning up the next.
A proven pattern for Magento teams: a dedicated .env per worktree with a different COMPOSE_PROJECT_NAME and shifted ports, plus its own database container or at least its own database inside a shared MySQL container. That way the hotfix worktree can be tested against production-like data while the feature worktree keeps working with test data, without the two interfering with each other.
# Add a new worktree and pull in Magento-specific dependencies
git worktree add ../shop-hotfix hotfix/checkout-crash
cd ../shop-hotfix
# vendor/ does not exist here yet, must be installed separately
bin/composer install
# Own .env with a different project name and ports for Docker
cp ../shop/.env .env
sed -i 's/COMPOSE_PROJECT_NAME=shop/COMPOSE_PROJECT_NAME=shop_hotfix/' .env
sed -i 's/WEBSERVER_PORT=80/WEBSERVER_PORT=8081/' .env
bin/start
bin/magento setup:upgrade
8. Cleanup: prune, lock, and removing old worktrees
Worktrees whose directory was deleted manually instead of removed cleanly with git worktree remove leave orphaned entries in Git's internal bookkeeping. git worktree prune cleans up these orphaned references by checking which registered worktree paths no longer physically exist and removing the associated metadata. That's a sensible routine step especially after manual filesystem cleanup or after deleting a temporary branch.
For worktrees that shouldn't accidentally be deleted, for example because an important test run is currently active there, git worktree lock <path> offers protection: a locked worktree can't be removed accidentally with remove or prune until it's explicitly released again with git worktree unlock. This matters especially for worktrees on external or network drives, where Git can't always reliably detect whether the directory is actually present.
For daily use, a simple routine is recommended: run git worktree remove immediately after every finished hotfix or review instead of letting worktrees pile up. Regularly running git worktree list as a check reliably shows which directories are still active before disk space and overview get lost unnecessarily.
# Clean up orphaned worktree entries, e.g. after a manual rm -rf
git worktree prune -v
# Protect an important worktree from being removed accidentally
git worktree lock ../shop-ci --reason "Long test run active"
# Release the lock once the run is finished
git worktree unlock ../shop-ci
# Check the current state of all worktrees
git worktree list --porcelain
9. Worktree compared to stash, clone, and branch switching
There are four common ways to work with multiple branch states at once: classic branch switching with git checkout, temporarily shelving changes with git stash, a full additional clone of the repository, or git worktree. Each approach has a clear trade-off between speed, disk space, and risk, summarized in the table below.
| Approach | Disk space | Risk/effort | Recommendation |
|---|---|---|---|
| git checkout branch switching | Minimal | Blocks the working directory entirely | Only for short, isolated switches |
| git stash | Minimal | Forgotten or lost stashes, conflicts | Only for very short interruptions |
| Additional full clone | High, complete copy | Separate remotes must be kept in sync | Only makes sense for fully separate projects |
| git worktree | Moderate, shared object database | Low, one branch per worktree | Recommended for parallel work in the same repo |
In practice, git worktree is the superior solution in every case where multiple states of the same repository are needed at once: a hotfix alongside a feature, a long test run alongside active development, a PR review alongside your own code. Stash remains useful for very short, few-seconds interruptions, and an additional full clone only pays off when you genuinely need fully independent remote configurations.
Mironsoft
Git workflows, CI/CD, and developer processes for Magento teams
Want more efficient Git workflows for your Magento team?
We set up Git workflows, branching strategies, and CI/CD pipelines for Magento and Hyvä projects, from worktree-based developer setups to automated review and deployment processes.
Git workflow audit
Analyzing branching model, review process, and team conventions
Docker setup per worktree
Mark Shust Docker configuration for parallel development environments
CI/CD pipeline
Automated tests, PHPStan, and deployments for Magento projects
10. Summary
Git worktree solves a problem every developer knows: working on multiple branches at once, without stash, without lost context, and without a blocked working directory. git worktree add creates a standalone directory per branch that shares the object database and commit history with the main repository. In practice, the benefit shows up everywhere classic workflows hit their limits: a hotfix alongside a half-finished feature, a long test run alongside active development, a pull request review without interrupting your own work state.
Using worktrees productively means observing two rules: the same branch can never be checked out in two worktrees at once, and on Magento projects every worktree needs its own vendor/ installation and, where relevant, its own Docker configuration with different ports. With git worktree list, prune, and lock, managing several parallel directories stays reliable and free of data loss.
Git Worktree - The Essentials at a Glance
Core principle
Multiple working directories, one shared .git, each worktree with its own checked-out branch.
Key commands
git worktree add, list, remove, prune, and lock for day-to-day management.
Constraint
A branch can never be checked out in two worktrees at once, Git actively prevents it.
Magento in practice
Every worktree needs its own vendor/, its own .env, and possibly its own Docker ports.