{"slug": "git-worktrees-are-great-they-stop-at-the-repo-boundary", "title": "Git worktrees are great. They stop at the repo boundary.", "summary": "A developer argues that git worktrees, while ideal for isolating parallel AI coding agent sessions within a single repository, hit a hard structural boundary in polyrepo setups because the shared object store is scoped to one repo. The writeup walks through a three-repository API contract change (Rust api-service, TypeScript web-frontend, docs-site) and shows a roughly 60-line bash script that automates worktree creation and symlinks them into a unified workspace for agent sessions.", "body_md": "Git worktrees (`git worktree add`) have become an indispensable primitive in modern software engineering and agentic workflows. Most community articles explaining how to use git worktrees for AI coding agents get the core single-repository mechanics completely right:\n\n`.git` object store. Creating a worktree takes milliseconds because Git simply writes a lightweight `.git/worktrees/<id>` in the main repository, avoiding the disk and network overhead of cloning full repository copies.`index`), `HEAD` ref pointer, and reflog. This allows multiple agent sessions to run in parallel on different feature branches without encountering lock contention or file collision.\n*The shared object store is what makes a worktree cheap, and it is also what makes it stop at the repository it belongs to.*\n\nFor single-repository engineering, git worktrees provide an ideal local execution environment. The agent operates inside an isolated file tree, language servers index files correctly, build tools run natively, and git status tracks changes cleanly.\n\nHowever, modern software applications rarely reside in a single repository. The moment a software feature spans multiple repositories, the single-repo worktree model encounters a hard structural boundary.\n\nConsider a routine architectural task in a polyrepo ecosystem: modifying an API contract. The feature spans three distinct git repositories:\n\n`api-service`: The Rust backend implementing the updated endpoint payload.`web-frontend`: The TypeScript application consuming the API response.` docs-site`: The documentation repository hosting public API reference specs.\nTo execute this change with an AI coding agent, you require a composite workspace configuration:\n\n`feature-auth` inside `main` inside Using raw `git worktree` commands manually requires executing a tedious multi-step sequence across three separate repository paths:\n\n```\n# Manual worktree creation across three repos\ngit -C ~/repos/api-service worktree add -b feature-auth ../worktrees/api-feature-auth\ngit -C ~/repos/web-frontend worktree add -b feature-auth ../worktrees/web-feature-auth\ngit -C ~/repos/docs-site worktree add ../worktrees/docs-main main\n\n# Create a temporary workspace directory and symlink worktrees\nmkdir -p ~/workspaces/feature-auth\nln -s ~/worktrees/api-feature-auth ~/workspaces/feature-auth/api\nln -s ~/worktrees/web-feature-auth ~/workspaces/feature-auth/web\nln -s ~/worktrees/docs-main ~/workspaces/feature-auth/docs\n```\n\nExecuting these steps manually for every feature task is slow, repetitive, and error-prone. Consequently, developers build shell scripts to automate multi-repository worktree assembly.\n\nHere is a typical, 60-line bash script that developers construct to automate multi-repository worktree setup for AI agent sessions:\n\n``` bash\n#!/usr/bin/env bash\nset -euo pipefail\n\nFEATURE_NAME=\"${1:-}\"\nif [[ -z \"$FEATURE_NAME\" ]]; then\n  echo \"Error: Feature name required.\"\n  echo \"Usage: $0 <feature-name>\"\n  exit 1\nfi\n\nWORKSPACE_DIR=\"$HOME/.workspaces/$FEATURE_NAME\"\nmkdir -p \"$WORKSPACE_DIR\"\n\ndeclare -A REPOS=(\n  [\"api\"]=\"$HOME/repos/api-service\"\n  [\"web\"]=\"$HOME/repos/web-frontend\"\n  [\"docs\"]=\"$HOME/repos/docs-site\"\n)\n\necho \"Initializing multi-repo worktrees for feature: $FEATURE_NAME\"\n\nfor NAME in \"${!REPOS[@]}\"; do\n  REPO_PATH=\"${REPOS[$NAME]}\"\n  TARGET_WORKTREE=\"$REPO_PATH/../worktrees/$NAME-$FEATURE_NAME\"\n\n  if [[ \"$NAME\" == \"docs\" ]]; then\n    # Read-only reference repository stays checked out on main\n    echo \"Creating reference worktree for $NAME on main...\"\n    git -C \"$REPO_PATH\" worktree add \"$TARGET_WORKTREE\" main 2>/dev/null || true\n  else\n    # Feature repositories check out feature branch\n    echo \"Creating feature worktree for $NAME on branch $FEATURE_NAME...\"\n    git -C \"$REPO_PATH\" worktree add -b \"$FEATURE_NAME\" \"$TARGET_WORKTREE\" 2>/dev/null || true\n  fi\n\n  # Symlink worktree into unified workspace folder\n  ln -sfn \"$TARGET_WORKTREE\" \"$WORKSPACE_DIR/$NAME\"\ndone\n\n# Create combined harness instructions\ncat <<EOF > \"$WORKSPACE_DIR/CLAUDE.md\"\n# Multi-Repo Workspace Context: $FEATURE_NAME\n- api/: Rust backend service (editable feature worktree)\n- web/: TypeScript web application (editable feature worktree)\n- docs/: Documentation site (reference context only, DO NOT EDIT)\nEOF\n\necho \"Multi-repo workspace materialized at: $WORKSPACE_DIR\"\ncd \"$WORKSPACE_DIR\" && claude\n```\n\nThis script automates worktree creation, symlinks the directories into a unified workspace folder, generates a basic `CLAUDE.md`, and launches the agent harness. \n\nWhile this script works for basic local demos, attempting to rely on it in production engineering environments reveals four fundamental limitations.\n\nWhile an ad-hoc shell script automates directory symlinking, it lacks the technical infrastructure required for reliable, safe multi-repository agent execution.\n\n| What it needs to do | What the script does instead | \n|---|---|\n| Guard writes to reference repos | Relies on a soft prompt rule | \n| Keep branches and lifecycle aligned | Leaves dirty and orphaned trees | \n| Materialise instructions | Dirties tracked files in each repo | \n| Deliver the feature | Pushes each branch on its own | \n\nThe shell script creates worktrees on disk using standard user write permissions (`0755`/` 0644`). To restrict modifications in `docs/`, the script relies exclusively on a text instruction inside `CLAUDE.md`: `\"docs/: reference context only, DO NOT EDIT\"`.\n\nUnder high context load or multi-step reasoning tasks, language models non-deterministically violate prompt instructions. An agent fixing a broken build or lint error will edit files in `docs/` directly, mutating git state in a reference repository. The script cannot enforce write restrictions at the operating system layer.\n\nWhen an agent session finishes or crashes, the script leaves active worktrees and branches dangling across multiple repository directories. If a session fails midway:\n\n`.git/worktrees/` across individual repos.`git worktree prune` and delete orphaned feature branches.\nFurthermore, if branch creation fails in one repository (e.g., due to an uncommitted conflict on `main`), the script leaves the workspace in a partially initialized, inconsistent state across repos without atomic rollback.\n\nThe script injects instructions by writing a static `CLAUDE.md` file at the root of the workspace. However, real-world repositories already contain their own provider-native instruction files (e.g., `api/CLAUDE.md` and `web/CLAUDE.md`). The script cannot merge or synthesize instructions across repositories.\n\nIf the script writes instruction files directly inside individual repository worktrees, it leaves untracked, dirty files in git status that risk being accidentally committed by the agent.\n\nOnce an agent completes changes across `api/` and `web/`, delivering the feature requires verifying status, pushing feature branches across multiple repositories, and establishing cross-repository pull request references.\n\nThe shell script leaves delivery entirely to manual git operations. It cannot verify atomic feature readiness across mounted repositories, track which repositories were actually modified during the session, or trigger synchronized multi-repo branch pushes (`deliver`). Note that multi-repo delivery coordinates push operations across repositories, rather than attempting complex dependency sorting or automated merge ordering.\n\nThe central vulnerability of custom multi-repo worktree scripts is relying on LLM prompt compliance for write protection. Soft prompt instructions and software hooks inevitably fail under context saturation, complex agent reasoning, or raw shell command execution.\n\nRobust write protection requires enforcing file immutability at the operating system layer using POSIX file permission masks (`chmod`).\n\n```\nif mode & 0o222 != 0 {\n    chmod(path, mode & !0o222)?;\n}\n```\n\nIn an orchestrated View Directory architecture:\n\n`docs/`) is mounted into the session workspace, write bits (` 0o222`) are stripped recursively across its worktree files and directories via `clear_write_bits`.`>`), or an automated code modifier—the operating system kernel rejects the write operation immediately with an `EACCES` (Permission Denied) error.`u+w`) via `restore_write_bits` (`mode | 0o200`), permitting authorized modifications:\n\n```\nchmod(path, mode | 0o200)?;\n```\n\nBy operating at the kernel permission layer, write enforcement remains absolute regardless of LLM prompt drift, context saturation, or tool execution style.\n\nGit worktrees provide the essential foundation for local agent workspaces, but scaling them across multiple repositories requires dedicated session orchestration rather than fragile shell scripts. How a View Dir combines those worktrees with a kernel-enforced write guard is covered in [what a View Dir is](https://ivar.run/docs/what-is-ivar), and the [quickstart](https://ivar.run/docs/quickstart) walks through mounting your first one.", "url": "https://wpnews.pro/news/git-worktrees-are-great-they-stop-at-the-repo-boundary", "canonical_source": "https://dev.to/mnzs/git-worktrees-are-great-they-stop-at-the-repo-boundary-4b9h", "published_at": "2026-09-29 16:15:37+00:00", "updated_at": "2026-09-29 16:16:43.186409+00:00", "lang": "en", "topics": ["ai-agents", "developer-tools", "ai-tools"], "entities": ["Git"], "also_reported_by": [], "alternates": {"html": "https://wpnews.pro/news/git-worktrees-are-great-they-stop-at-the-repo-boundary", "markdown": "https://wpnews.pro/news/git-worktrees-are-great-they-stop-at-the-repo-boundary.md", "text": "https://wpnews.pro/news/git-worktrees-are-great-they-stop-at-the-repo-boundary.txt", "jsonld": "https://wpnews.pro/news/git-worktrees-are-great-they-stop-at-the-repo-boundary.jsonld"}}