Shipping static sites or component libraries often means juggling two urgent branches—a hotfix for production CSS and a feature branch for next week’s landing page. Cloning the repository twice burns disk and mental overhead. Git worktrees let you check out multiple branches side by side from one object database, which is ideal when your “second machine” is actually a cloud Mac mini you SSH into for Safari checks and fast builds.
Why HTML/CSS teams adopt worktrees
Traditional flow—stash, checkout, pull, run npm install again—destroys context when you only needed to tweak a 12-line hero CSS file. Worktrees keep each branch in its own directory while sharing history, so you can leave npm run dev running in folder A and run npm run build in folder B without constant branch hopping.
On Apple Silicon, repeated installs still cost time: even with shared Git objects, each worktree’s node_modules can consume 250–400 MB for a modest Vite + Tailwind stack. Planning three active trees means budgeting roughly 1.2 GB just for dependencies before counting build output—reasonable on a cloud Mac with hundreds of gigabytes free, painful on a 256 GB laptop also holding photos and Docker images.
Core commands you will use daily
From your main repository path, add a sibling checkout:
git fetch origin
git worktree add ../site-hotfix origin/hotfix/css-hero
git worktree list
Remove a finished branch’s tree after merging:
git worktree remove ../site-hotfix
If Git complains about uncommitted changes, either commit or pass --force after you are certain nothing valuable lives in that directory. Prune stale metadata with git worktree prune if you deleted folders manually by mistake.
Worktree vs full clone vs separate repo
| Approach | Disk for Git objects | Complexity | Best for |
|---|---|---|---|
| Second full clone | Duplicates .git | Low | Air-gapped machines |
| Worktree | Shared object store | Medium | Parallel branches, same origin |
| Separate repo (fork) | Independent history | High | Vendor drops with divergent remotes |
Dev servers, ports, and Safari tabs
Each worktree is a separate filesystem root, so you can run npx vite --port 5173 in one and npx vite --port 5174 in another. Document the mapping in README or a shared Notion table: hotfix → 5173, feature-cards → 5174. Safari pinned tabs survive remote sessions poorly, so bookmark both URLs or use a tiny shell script that echoes the correct link after starting the server.
When you combine worktrees with the access patterns from SSH versus VNC on cloud Mac, you typically keep long-running dev commands inside SSH panes and open VNC only when you need pixel-level Safari inspection across two branches at once.
Cleanup checklist before you delete paths
- Confirm the branch merged or abandoned in your Git host UI.
- Stop watchers (
Ctrl+C) so no file handles linger. - Run
git worktree remove <path>instead ofrm -rfwhen possible. - Delete stray
dist/or.vitecaches if you removed the tree manually. - Reclaim space with
npm cache verifyif installs ballooned during experiments.
Teams that skip step three sometimes leave phantom entries in git worktree list, which confuses CI scripts that iterate trees with a 30-second timeout—clean metadata matters for automation as much as for humans.
Prettier, ESLint, and Stylelint caches live under each tree’s node_modules/.cache; if you symlink caches across worktrees you risk cross-contaminating formatting rules when one branch bumps plugin majors. Safer: accept duplicate caches until disk pressure forces a policy change—usually above 85 % volume utilization on monitoring dashboards.
For static export pipelines (Eleventy, Astro static), run npm run build in each tree sequentially during heavy release days; parallel builds can contend for CPU on M4 Pro hosts when four trees each spawn eight worker threads. Throttle with --max-old-space-size=4096 on Node if you observe kernel memory pressure warnings in Console.app.
Why run worktrees on a rented Mac
A dedicated Apple Silicon Mac mini in the cloud behaves like a shared build locker: everyone pushes branches there, adds worktrees with predictable paths under /Users/ci/sites/, and tears them down after review. Your laptop keeps a single clone for local typing; the heavy parallel installs happen where power and SSD are cheaper per hour.
If you are still wiring SSH keys, start with the remote Mac setup guide before scripting worktree creation—stable auth beats clever directory layouts.
Automation-friendly shops often wrap git worktree add in a Makefile target or a 30-line shell script that also runs npm ci and prints the chosen dev URL. Standardizing on /var/tmp/worktrees/$BRANCH_SLUG avoids collisions when five engineers share one node; the slug truncates to 48 characters to stay under macOS path limits when Webpack writes deep temp files.
Submodules and worktrees interact subtly: each tree may need its own git submodule update --init pass, so bake that into your script after every add. LFS-backed asset repos should run git lfs pull per tree; skipping that step produces mysterious 404s in Safari for hero videos even when CSS builds succeed.
When CI posts preview links, tag each URL with the worktree path in your Slack message—hotfix → ~/wt/hero—so reviewers SSH into the right folder before running tests. A three-minute saved context switch multiplied across four reviewers pays for the rental minute cost many times over during release week.
Observability helps: ship a tiny worktree-health.sh that prints disk free space, active node PIDs, and the last five lines of each dev-server log. Cron it every 15 minutes during crunch weeks; when a tree wedges, you can git worktree remove --force knowing no orphan webpack processes remain.
Security note for shared hosts: each worktree inherits the same credential helpers as the main repo, so rotate deploy keys if a contractor had shell access. Pair per-tree .env.local files with chmod 600 and avoid copying secrets between branches—leaked API keys in a discarded hotfix tree are still readable until the directory is removed.
Finally, snapshot disk before major refactors: tmutil localsnapshot on macOS or a quick tarball of dist/ gives you a rollback if a CSS purge step nukes assets across every active tree during a bad script run.
FAQ
How many worktrees should I keep on one Mac?
Most teams cap active worktrees between three and six per repo on a 256 GB disk. Each worktree still creates its own working files and node_modules unless you use advanced tooling, so monitor disk with df -h weekly.
Do worktrees share Git objects?
Yes. All worktrees attached to the same repository share one .git object database, which saves space versus cloning the repo multiple times. They do not share uncommitted node_modules folders.
Can I run two Vite servers at once?
Yes, assign explicit ports such as --port 5173 and --port 5174 and document them in a team runbook. Safari can open both localhost URLs side by side for visual diffs.
Git worktrees do not replace good branch naming, but they remove the friction of constant context switching—especially when paired with a quiet, always-on Mac mini that mirrors your production Safari environment. Renting that capacity by the day lets you spin up parallel HTML/CSS lanes during crunch weeks, then delete trees and downgrade spend when the release ships.
Parallel branches deserve parallel disk
Rent a cloud Mac mini for shared worktrees, Safari reviews, and long-running dev servers. Pick a nearby region, then follow the SSH guide to automate adds and removes.