Isolated workspaces for repo-managed multi-repository projects using git worktree + symlink
Project description
repoworktree
Create isolated workspaces for Google repo-managed multi-repository projects using git worktree + symlink. Supports a continuous spectrum from all-symlink (zero overhead, read-only) to all-worktree (fully isolated).
All symlink (read-only) All worktree (fully isolated)
│ rwt create /tmp/ws rwt create /tmp/ws --all │
│ ◄── rwt promote / demote dynamically ──► │
└────────────────────────────────────────────────────────────┘
Install
Python 3.10+, no third-party dependencies.
pip install repoworktree
Quick Start
# Create workspace, specify repos to modify
rwt create /tmp/ws-agent1 -w nuttx,apps -n "fix-serial-driver"
# Develop in the workspace
cd /tmp/ws-agent1
source envsetup.sh && lunch && m # build works normally
# Need to modify more repos during development
rwt promote frameworks/system/core
# Done with a repo, demote back to symlink
rwt demote frameworks/system/core
# Destroy workspace
rwt destroy /tmp/ws-agent1
Command Reference
rwt create — Create workspace
rwt create <path> [options]
| Option | Description |
|---|---|
<path> |
Workspace target path |
-n, --name |
Workspace name (default: directory name) |
-w, --worktree |
Comma-separated sub-repo paths to create as git worktrees |
--all |
Create git worktrees for all sub-repos |
-s, --source |
Main repo checkout path (default: auto-detect .repo/) |
--checkout |
Check out this branch or tag for all worktrees (default: source HEAD) |
--pin |
Pin version, format repo=version[,repo=version,...] |
-b, --branch |
Create named branch instead of detached HEAD |
Examples:
# All symlink read-only (extreme A)
rwt create /tmp/ws-readonly
# Typical: modify a few repos
rwt create /tmp/ws-agent1 -w nuttx,apps -n "fix-serial-driver"
# Modify nested sub-repo
rwt create /tmp/ws-bt -w nuttx,apps/system/adb
# Parent and child repos both writable
rwt create /tmp/ws-dev -w apps,apps/system/adb
# All worktree fully isolated (extreme B)
rwt create /tmp/ws-full --all
# Pin version + named branch
rwt create /tmp/ws-stable -w nuttx --pin nuttx=v12.0.0 -b feature/new-driver
# Check out a specific branch/tag for all worktrees (without pinning — sync still works)
rwt create /tmp/ws-release --all --checkout release/v2.0
rwt destroy — Destroy workspace
rwt destroy <path|name> [-s source] [-f]
| Option | Description |
|---|---|
<path|name> |
Workspace path or name |
-s, --source |
Source repo checkout path (for name lookup, default: auto-detect) |
-f, --force |
Force destroy even with uncommitted changes or local commits |
rwt destroy /tmp/ws-agent1
rwt destroy fix-serial-driver # by name
rwt destroy /tmp/ws-dirty -f # discard uncommitted changes
rwt promote — Promote sub-repo to writable worktree
Run inside a workspace to dynamically promote a symlinked sub-repo to a git worktree.
rwt promote <repo_path> [options]
| Option | Description |
|---|---|
<repo_path> |
Sub-repo path (e.g. nuttx, frameworks/system/core) |
--pin |
Checkout specific version |
-b, --branch |
Create named branch |
cd /tmp/ws-agent1
rwt promote vendor/xiaomi/miwear
rwt promote frameworks/system/core --pin abc1234
rwt promote nuttx -b fix/uart-bug
Promote handles nesting automatically:
- Top-level symlink → directly replaced with worktree
- Deep nested (e.g.
frameworks/system/core) → parent symlinks split into real dir + symlink mix - Parent repo already has child worktrees → temporarily removes children, creates parent worktree, restores children
rwt demote — Demote worktree to read-only symlink
rwt demote <repo_path> [-f]
| Option | Description |
|---|---|
<repo_path> |
Sub-repo path |
-f, --force |
Force demote, discard uncommitted changes |
rwt demote apps
rwt demote apps -f # discard changes
Demote handles nesting automatically:
- With child worktrees → preserves children, rebuilds parent as real dir + symlink structure
- Without child worktrees → the repo itself becomes a symlink; parent directories remain as real dirs
rwt list — List all workspaces
rwt list [-s <source>] [--json]
rwt status — Show workspace details
rwt status [<path|name>] [-s source] [--json]
rwt sync — Sync workspace
rwt sync [-W workspace] [--rebase]
After repo sync on the main checkout, symlinks follow automatically. Worktrees need manual sync:
| Worktree state | Default | --rebase |
|---|---|---|
| symlink | auto follows | same |
| worktree, pinned | skip | skip |
| worktree, uncommitted changes | skip, report | skip, report |
| worktree, local commits | skip, report | rebase onto latest |
| worktree, clean | update to source HEAD | same |
rwt pin / rwt unpin — Version pinning
rwt pin <repo_path> [<version>]
rwt unpin <repo_path>
rwt export — Export changes
rwt export [--format patch|bundle] [-o <dir>]
Use Cases
Parallel multi-agent development
# Two agents modify nuttx simultaneously, fully isolated
rwt create /tmp/ws-agent1 -w nuttx -n "agent1-serial-fix"
rwt create /tmp/ws-agent2 -w nuttx -n "agent2-spi-driver"
Dynamic adjustment during development
# Start with only nuttx
rwt create /tmp/ws-dev -w nuttx
cd /tmp/ws-dev
# Need to modify apps/system/adb
rwt promote apps/system/adb
# Also need to modify apps itself
rwt promote apps
# Done with apps, demote back
rwt demote apps
Push changes to Gerrit
cd /tmp/ws-agent1/nuttx
git push origin HEAD:refs/for/main
Workspace Structure
/tmp/ws-agent1/
├── .workspace.json # workspace metadata
├── nuttx/ # git worktree (writable)
├── apps/ # real directory (has worktree descendants)
│ ├── system/
│ │ ├── adb/ # git worktree (writable)
│ │ └── core/ # symlink → source (read-only)
│ └── benchmarks/ # symlink → source (read-only)
├── build/ # symlink → source (read-only)
├── frameworks/ # symlink → source (read-only)
├── build.sh # symlink (preserves original relative link)
└── CLAUDE.md # symlink → source
How It Works
- Symlinked repos: Zero overhead, point directly to the main checkout directory. Read-only — modifications affect the main checkout.
- Worktree repos: Created via
git worktree addwith their own HEAD, index, and working tree. Fully isolated. - Nested repos:
repo-managed projects have parent-child repos (e.g.apps/andapps/system/adb/are independent git repos). When a child needs a worktree, parent symlinks are split into real directories with symlinked siblings — real directories are only created along the path to the worktree. - Metadata:
.workspace.jsonstores per-workspace config..workspaces.jsonin the source root indexes all workspaces.
License
MIT
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file repoworktree-0.3.0.tar.gz.
File metadata
- Download URL: repoworktree-0.3.0.tar.gz
- Upload date:
- Size: 71.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
39741f35f86349f5d153d7924da19106054e65bfa2ccc3717e78ebc8ee6eac3b
|
|
| MD5 |
8cd2a3502627bf33f8a8104ef6726cba
|
|
| BLAKE2b-256 |
2aa2e09deb883dacc7386a0941019481a6452bf64b1fb7acfc58e3604dabbb84
|
Provenance
The following attestation bundles were made for repoworktree-0.3.0.tar.gz:
Publisher:
publish.yml on XuNeo/repoworktree
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
repoworktree-0.3.0.tar.gz -
Subject digest:
39741f35f86349f5d153d7924da19106054e65bfa2ccc3717e78ebc8ee6eac3b - Sigstore transparency entry: 1114460965
- Sigstore integration time:
-
Permalink:
XuNeo/repoworktree@8283f9d1bb63ff9b85e02733ed4b6e0bb06350e5 -
Branch / Tag:
refs/tags/v0.3.0 - Owner: https://github.com/XuNeo
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@8283f9d1bb63ff9b85e02733ed4b6e0bb06350e5 -
Trigger Event:
release
-
Statement type:
File details
Details for the file repoworktree-0.3.0-py3-none-any.whl.
File metadata
- Download URL: repoworktree-0.3.0-py3-none-any.whl
- Upload date:
- Size: 27.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
925773fd00f1a884648103f91f8fa136f199a2a0dda375c163a59990c8335638
|
|
| MD5 |
fe566564f09cd5fa15278f1f1bdc5fae
|
|
| BLAKE2b-256 |
de1c1791a634fd8fba611e53fd43a1037e8ee8cb6f5accb744b059efafd4de86
|
Provenance
The following attestation bundles were made for repoworktree-0.3.0-py3-none-any.whl:
Publisher:
publish.yml on XuNeo/repoworktree
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
repoworktree-0.3.0-py3-none-any.whl -
Subject digest:
925773fd00f1a884648103f91f8fa136f199a2a0dda375c163a59990c8335638 - Sigstore transparency entry: 1114460967
- Sigstore integration time:
-
Permalink:
XuNeo/repoworktree@8283f9d1bb63ff9b85e02733ed4b6e0bb06350e5 -
Branch / Tag:
refs/tags/v0.3.0 - Owner: https://github.com/XuNeo
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@8283f9d1bb63ff9b85e02733ed4b6e0bb06350e5 -
Trigger Event:
release
-
Statement type: