sync-worktrees 3.6.3 → 4.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +383 -261
- package/dist/components/App.d.ts +50 -0
- package/dist/components/App.d.ts.map +1 -0
- package/dist/components/BranchCreationWizard.d.ts +26 -0
- package/dist/components/BranchCreationWizard.d.ts.map +1 -0
- package/dist/components/HelpModal.d.ts +7 -0
- package/dist/components/HelpModal.d.ts.map +1 -0
- package/dist/components/LogPanel.d.ts +10 -0
- package/dist/components/LogPanel.d.ts.map +1 -0
- package/dist/components/LogViewer.d.ts +9 -0
- package/dist/components/LogViewer.d.ts.map +1 -0
- package/dist/components/OpenEditorWizard.d.ts +25 -0
- package/dist/components/OpenEditorWizard.d.ts.map +1 -0
- package/dist/components/StatusBar.d.ts +14 -0
- package/dist/components/StatusBar.d.ts.map +1 -0
- package/dist/components/WorktreeStatusView.d.ts +14 -0
- package/dist/components/WorktreeStatusView.d.ts.map +1 -0
- package/dist/constants.d.ts +112 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/errors/index.d.ts +59 -0
- package/dist/errors/index.d.ts.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2523 -1106
- package/dist/index.js.map +4 -4
- package/dist/mcp/context.d.ts +143 -0
- package/dist/mcp/context.d.ts.map +1 -0
- package/dist/mcp/handlers.d.ts +46 -0
- package/dist/mcp/handlers.d.ts.map +1 -0
- package/dist/mcp/index.d.ts +2 -0
- package/dist/mcp/index.d.ts.map +1 -0
- package/dist/mcp/server.d.ts +9 -0
- package/dist/mcp/server.d.ts.map +1 -0
- package/dist/mcp/utils.d.ts +14 -0
- package/dist/mcp/utils.d.ts.map +1 -0
- package/dist/mcp/worktree-summary.d.ts +14 -0
- package/dist/mcp/worktree-summary.d.ts.map +1 -0
- package/dist/mcp-server.js +2347 -640
- package/dist/mcp-server.js.map +4 -4
- package/dist/services/InteractiveUIService.d.ts +85 -0
- package/dist/services/InteractiveUIService.d.ts.map +1 -0
- package/dist/services/branch-created-actions.service.d.ts +27 -0
- package/dist/services/branch-created-actions.service.d.ts.map +1 -0
- package/dist/services/clone-sync.service.d.ts +93 -0
- package/dist/services/clone-sync.service.d.ts.map +1 -0
- package/dist/services/config-loader.service.d.ts +28 -0
- package/dist/services/config-loader.service.d.ts.map +1 -0
- package/dist/services/file-copy.service.d.ts +19 -0
- package/dist/services/file-copy.service.d.ts.map +1 -0
- package/dist/services/git.service.d.ts +94 -0
- package/dist/services/git.service.d.ts.map +1 -0
- package/dist/services/hook-execution.service.d.ts +20 -0
- package/dist/services/hook-execution.service.d.ts.map +1 -0
- package/dist/services/logger.service.d.ts +24 -0
- package/dist/services/logger.service.d.ts.map +1 -0
- package/dist/services/path-resolution.service.d.ts +10 -0
- package/dist/services/path-resolution.service.d.ts.map +1 -0
- package/dist/services/progress-emitter.d.ts +14 -0
- package/dist/services/progress-emitter.d.ts.map +1 -0
- package/dist/services/repo-operation-lock.d.ts +16 -0
- package/dist/services/repo-operation-lock.d.ts.map +1 -0
- package/dist/services/sparse-checkout.service.d.ts +45 -0
- package/dist/services/sparse-checkout.service.d.ts.map +1 -0
- package/dist/services/sync-outcome.d.ts +47 -0
- package/dist/services/sync-outcome.d.ts.map +1 -0
- package/dist/services/sync-retry-policy.d.ts +18 -0
- package/dist/services/sync-retry-policy.d.ts.map +1 -0
- package/dist/services/worktree-metadata.service.d.ts +25 -0
- package/dist/services/worktree-metadata.service.d.ts.map +1 -0
- package/dist/services/worktree-mode-sync-runner.d.ts +36 -0
- package/dist/services/worktree-mode-sync-runner.d.ts.map +1 -0
- package/dist/services/worktree-status.service.d.ts +60 -0
- package/dist/services/worktree-status.service.d.ts.map +1 -0
- package/dist/services/worktree-sync-planner.d.ts +62 -0
- package/dist/services/worktree-sync-planner.d.ts.map +1 -0
- package/dist/services/worktree-sync.service.d.ts +49 -0
- package/dist/services/worktree-sync.service.d.ts.map +1 -0
- package/dist/types/index.d.ts +303 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/sync-metadata.d.ts +16 -0
- package/dist/types/sync-metadata.d.ts.map +1 -0
- package/dist/utils/app-events.d.ts +31 -0
- package/dist/utils/app-events.d.ts.map +1 -0
- package/dist/utils/branch-filter.d.ts +3 -0
- package/dist/utils/branch-filter.d.ts.map +1 -0
- package/dist/utils/cli.d.ts +21 -0
- package/dist/utils/cli.d.ts.map +1 -0
- package/dist/utils/clone-skip-format.d.ts +3 -0
- package/dist/utils/clone-skip-format.d.ts.map +1 -0
- package/dist/utils/config-generator.d.ts +10 -0
- package/dist/utils/config-generator.d.ts.map +1 -0
- package/dist/utils/date-filter.d.ts +10 -0
- package/dist/utils/date-filter.d.ts.map +1 -0
- package/dist/utils/disk-space.d.ts +23 -0
- package/dist/utils/disk-space.d.ts.map +1 -0
- package/dist/utils/file-exists.d.ts +2 -0
- package/dist/utils/file-exists.d.ts.map +1 -0
- package/dist/utils/git-progress.d.ts +25 -0
- package/dist/utils/git-progress.d.ts.map +1 -0
- package/dist/utils/git-url.d.ts +23 -0
- package/dist/utils/git-url.d.ts.map +1 -0
- package/dist/utils/git-validation.d.ts +5 -0
- package/dist/utils/git-validation.d.ts.map +1 -0
- package/dist/utils/interactive.d.ts +3 -0
- package/dist/utils/interactive.d.ts.map +1 -0
- package/dist/utils/lfs-error.d.ts +35 -0
- package/dist/utils/lfs-error.d.ts.map +1 -0
- package/dist/utils/lock-path.d.ts +9 -0
- package/dist/utils/lock-path.d.ts.map +1 -0
- package/dist/utils/path-compare.d.ts +16 -0
- package/dist/utils/path-compare.d.ts.map +1 -0
- package/dist/utils/repo-mode.d.ts +8 -0
- package/dist/utils/repo-mode.d.ts.map +1 -0
- package/dist/utils/retry.d.ts +24 -0
- package/dist/utils/retry.d.ts.map +1 -0
- package/dist/utils/sanitize-name.d.ts +2 -0
- package/dist/utils/sanitize-name.d.ts.map +1 -0
- package/dist/utils/shell-escape.d.ts +5 -0
- package/dist/utils/shell-escape.d.ts.map +1 -0
- package/dist/utils/signal-handlers.d.ts +14 -0
- package/dist/utils/signal-handlers.d.ts.map +1 -0
- package/dist/utils/timing.d.ts +24 -0
- package/dist/utils/timing.d.ts.map +1 -0
- package/dist/utils/worktree-list-parser.d.ts +10 -0
- package/dist/utils/worktree-list-parser.d.ts.map +1 -0
- package/package.json +5 -2
package/README.md
CHANGED
|
@@ -1,34 +1,75 @@
|
|
|
1
1
|
# sync-worktrees
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
> Keep every branch and every repo you work on checked out as predictable directories — no stashing, no re-cloning, no re-orienting your AI assistant.
|
|
4
4
|
|
|
5
5
|

|
|
6
6
|
|
|
7
|
+
**Contents:** [Why](#why-sync-worktrees) · [How it works](#how-it-works) · [Quick start](#quick-start) · [MCP server](#mcp-server) · [Interactive TUI](#interactive-tui) · [CLI options](#cli-options)
|
|
8
|
+
|
|
9
|
+
## Why sync-worktrees
|
|
10
|
+
|
|
11
|
+
If you've ever:
|
|
12
|
+
|
|
13
|
+
- Stashed half-finished work just to check out another branch
|
|
14
|
+
- Lost minutes hunting for where you cloned a sibling repo
|
|
15
|
+
- Switched branches in five repos because one feature spans them all
|
|
16
|
+
- Re-explained to an AI assistant which directory holds which branch
|
|
17
|
+
|
|
18
|
+
…sync-worktrees fixes that. It keeps the **entire branch and repo layout you work in materialized on disk** — one directory per branch, automatically kept in sync with the remote. Switching branches becomes `cd`. Searching across repos becomes `grep -r`. **AI agents see the same shape you do, so "look in the other repo" actually works.**
|
|
19
|
+
|
|
20
|
+
It's also a clean answer to **dev-environment bootstrapping**. One config file describes every repo, branch, and folder layout your team works in. Hand it to a new hire (or a fresh laptop) and `sync-worktrees` lays down the whole workspace in a single command — no day-one cloning checklist, no "where do I put this repo?" Slack threads.
|
|
21
|
+
|
|
22
|
+
Runs as a one-shot, a background daemon, or an interactive TUI — and ships an MCP server so AI assistants can list, create, and inspect worktrees themselves.
|
|
23
|
+
|
|
7
24
|
## How it works
|
|
8
25
|
|
|
9
|
-
|
|
26
|
+
The default — **worktree mode** — gives every remote branch its own directory while sharing one Git database underneath:
|
|
27
|
+
|
|
28
|
+
1. **First run** clones the repo once as a bare repository (just the Git data, no working files).
|
|
29
|
+
2. **Each sync**:
|
|
30
|
+
- Creates a directory for every remote branch (`main`, `develop`, `feature/*`).
|
|
31
|
+
- Fetches latest changes (no merge — your local work stays untouched).
|
|
32
|
+
- Removes directories for branches deleted upstream (preserves dirty trees and unpushed commits).
|
|
33
|
+
|
|
34
|
+
Smallest config that produces this:
|
|
35
|
+
|
|
36
|
+
```javascript
|
|
37
|
+
// sync-worktrees.config.js
|
|
38
|
+
// @ts-check
|
|
39
|
+
|
|
40
|
+
/** @satisfies {import("sync-worktrees").SyncWorktreesConfig} */
|
|
41
|
+
const config = {
|
|
42
|
+
repositories: [
|
|
43
|
+
{
|
|
44
|
+
name: "my-repo",
|
|
45
|
+
repoUrl: "https://github.com/user/my-repo.git",
|
|
46
|
+
worktreeDir: "./worktrees/my-repo",
|
|
47
|
+
},
|
|
48
|
+
],
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
export default config;
|
|
52
|
+
```
|
|
10
53
|
|
|
11
|
-
|
|
12
|
-
2. **Automatic sync**:
|
|
13
|
-
- Creates a dedicated worktree for **every remote branch** (`main`, `develop`, `feature/*`, etc.)
|
|
14
|
-
- Each branch gets its own isolated directory with a full working copy
|
|
15
|
-
- Fetches latest changes (doesn't merge - preserves your local work)
|
|
16
|
-
- Removes worktrees when remote branches are deleted (preserves local changes)
|
|
54
|
+
Run `sync-worktrees` from the directory holding the config and you get:
|
|
17
55
|
|
|
18
|
-
|
|
56
|
+
```
|
|
57
|
+
.
|
|
58
|
+
├── sync-worktrees.config.js
|
|
59
|
+
├── .bare/
|
|
60
|
+
│ └── my-repo/ # Bare repository (shared Git objects)
|
|
61
|
+
└── worktrees/my-repo/
|
|
62
|
+
├── main/ # Worktree for main branch
|
|
63
|
+
├── feature-1/ # Worktree for feature-1 branch
|
|
64
|
+
└── feature-2/ # Worktree for feature-2 branch
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
**Clone mode** (`mode: "clone"`) is a first-class alternative: a plain `git clone` of one branch into `worktreeDir`, no bare repo, no per-branch subfolders. Reach for it when you want a repo to live at a fixed path — a dependency sibling, a single-branch dev clone, or any case where one checkout is enough. See [Clone mode](#clone-mode).
|
|
19
68
|
|
|
20
69
|
## Features
|
|
21
70
|
|
|
22
|
-
-
|
|
23
|
-
-
|
|
24
|
-
- ⏰ Run as a scheduled cron job or one-time execution
|
|
25
|
-
- 🛡️ Safe operations - won't delete worktrees with uncommitted changes or unpushed commits
|
|
26
|
-
- 📝 Clear logging with timestamps and progress indicators
|
|
27
|
-
- 📋 Config file support for managing multiple repositories
|
|
28
|
-
- 🔁 Automatic retry with exponential backoff for network and filesystem errors
|
|
29
|
-
- 🕐 Branch age filtering - only sync branches active within a specified time period
|
|
30
|
-
- 🔀 Smart handling of rebased/force-pushed branches with automatic divergence detection
|
|
31
|
-
- 🤖 MCP server for AI assistants - inspect and manage worktrees from Claude Desktop, Claude Code, Cursor, etc.
|
|
71
|
+
- **Filtering & lifecycle** — branch name globs, age filtering, sparse checkout, automatic divergence detection with `.diverged/` preservation, retry with exponential backoff.
|
|
72
|
+
- **Interactive TUI** — Ink-based UI with wizards for opening worktrees, creating branches, and inspecting status; diverged-directory management; live log streaming; multi-repo filtering.
|
|
32
73
|
|
|
33
74
|
## Installation
|
|
34
75
|
|
|
@@ -36,92 +77,182 @@ sync-worktrees maintains a **separate working directory for each remote branch**
|
|
|
36
77
|
npm install -g sync-worktrees
|
|
37
78
|
```
|
|
38
79
|
|
|
39
|
-
|
|
80
|
+
## Quick start
|
|
81
|
+
|
|
82
|
+
`sync-worktrees` always runs against a config file. Create one once, then run the tool.
|
|
40
83
|
|
|
41
84
|
```bash
|
|
42
|
-
|
|
85
|
+
cd ~/projects/my-sync-dir
|
|
86
|
+
sync-worktrees init # interactive wizard → writes sync-worktrees.config.js
|
|
87
|
+
sync-worktrees # auto-loads the config in the current directory and starts syncing
|
|
43
88
|
```
|
|
44
89
|
|
|
45
|
-
|
|
90
|
+
By default, bare `sync-worktrees` launches the [interactive TUI](#interactive-tui) and keeps syncing on the cron schedule from your config. Press `q` to quit. For a one-shot run (CI, scripts, ad-hoc), add `--runOnce`.
|
|
46
91
|
|
|
47
|
-
|
|
92
|
+
To manage multiple repositories, edit the generated config file and add entries under `repositories`. See [Configuration](#configuration).
|
|
48
93
|
|
|
49
|
-
|
|
50
|
-
- **Subsequent runs** — `sync-worktrees` auto-loads `sync-worktrees.config.js` (or `.mjs` / `.cjs`) from the current directory. No flags needed.
|
|
94
|
+
If the config lives outside the current directory, pass it explicitly:
|
|
51
95
|
|
|
52
96
|
```bash
|
|
53
|
-
|
|
54
|
-
sync-worktrees
|
|
55
|
-
sync-worktrees
|
|
97
|
+
sync-worktrees --config /path/to/sync-worktrees.config.js
|
|
98
|
+
sync-worktrees --config /path/to/sync-worktrees.config.js --runOnce
|
|
99
|
+
sync-worktrees list --config ./config.js --filter "frontend-*"
|
|
56
100
|
```
|
|
57
101
|
|
|
58
|
-
|
|
102
|
+
## MCP server
|
|
59
103
|
|
|
60
|
-
|
|
104
|
+
sync-worktrees ships a [Model Context Protocol](https://modelcontextprotocol.io) server so AI assistants (Claude Desktop, Claude Code, Cursor, Windsurf, etc.) can inspect and operate your workspace directly. Installing the package exposes a second binary, `sync-worktrees-mcp`, that speaks MCP over stdio.
|
|
61
105
|
|
|
62
|
-
|
|
106
|
+
In a single call, an AI assistant can discover every repo and worktree you have configured — so an agent working in `frontend/` can grep across `backend/` and `shared/` without you reorienting it. That call is `detect_context` with `includeAllWorktrees: true`; the response also includes a per-capability `{ available, reason }` block telling the agent which operations are reachable from its current vantage point, so there's no guessing whether `sync` will work. See [Available tools](#available-tools) for the full surface.
|
|
107
|
+
|
|
108
|
+
### Getting started
|
|
109
|
+
|
|
110
|
+
Install the sync-worktrees MCP server with your client.
|
|
111
|
+
|
|
112
|
+
**Standard config** works in most tools:
|
|
113
|
+
|
|
114
|
+
```json
|
|
115
|
+
{
|
|
116
|
+
"mcpServers": {
|
|
117
|
+
"sync-worktrees": {
|
|
118
|
+
"command": "npx",
|
|
119
|
+
"args": ["-y", "-p", "sync-worktrees", "sync-worktrees-mcp"],
|
|
120
|
+
"env": {
|
|
121
|
+
"SYNC_WORKTREES_CONFIG": "/absolute/path/to/sync-worktrees.config.js"
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
If installed globally, replace `command` with `sync-worktrees-mcp` and drop `args`. `SYNC_WORKTREES_CONFIG` is optional — without it the server runs in **auto-detect mode**: when the client's CWD sits inside a worktree managed by sync-worktrees, the server locates the bare repo, enumerates sibling worktrees, and enables per-worktree operations. `sync` and `initialize` require a loaded config (or call `load_config` at runtime).
|
|
129
|
+
|
|
130
|
+
<details>
|
|
131
|
+
<summary>Claude Code</summary>
|
|
132
|
+
|
|
133
|
+
Use the Claude Code CLI:
|
|
63
134
|
|
|
64
135
|
```bash
|
|
65
|
-
sync-worktrees --
|
|
66
|
-
sync-worktrees --config ./config.js --filter "frontend-*"
|
|
67
|
-
sync-worktrees --config ./config.js --list
|
|
136
|
+
claude mcp add sync-worktrees -- npx -y -p sync-worktrees sync-worktrees-mcp
|
|
68
137
|
```
|
|
69
138
|
|
|
70
|
-
|
|
139
|
+
To pass a config path, append `-e SYNC_WORKTREES_CONFIG=/absolute/path/to/sync-worktrees.config.js` to the command.
|
|
71
140
|
|
|
72
|
-
|
|
141
|
+
</details>
|
|
73
142
|
|
|
74
|
-
|
|
75
|
-
|
|
143
|
+
<details>
|
|
144
|
+
<summary>Claude Desktop</summary>
|
|
76
145
|
|
|
77
|
-
|
|
146
|
+
Edit `claude_desktop_config.json` and paste the **standard config** above into the `mcpServers` block. Default locations:
|
|
78
147
|
|
|
79
|
-
|
|
148
|
+
- macOS: `~/Library/Application Support/Claude/claude_desktop_config.json`
|
|
149
|
+
- Windows: `%APPDATA%\Claude\claude_desktop_config.json`
|
|
80
150
|
|
|
81
|
-
|
|
82
|
-
|----------|---------|------------------|
|
|
83
|
-
| `SYNC_WORKTREES_TERMINAL` | Override the terminal launcher on any platform. Value is a command string; the tmux invocation is appended via `sh -c`. Example: `SYNC_WORKTREES_TERMINAL="alacritty -e"`. | See per-platform defaults below. |
|
|
84
|
-
| `TERMINAL` | Linux-only fallback when `SYNC_WORKTREES_TERMINAL` is unset. Same format. | Probes `gnome-terminal`, `konsole`, `alacritty`, `kitty`, `xterm` in order. |
|
|
85
|
-
| `EDITOR` / `VISUAL` | Editor mode launcher. | Falls back to `code`. |
|
|
151
|
+
Restart Claude Desktop after editing.
|
|
86
152
|
|
|
87
|
-
|
|
153
|
+
</details>
|
|
88
154
|
|
|
89
|
-
|
|
90
|
-
|
|
155
|
+
<details>
|
|
156
|
+
<summary>Cursor</summary>
|
|
157
|
+
|
|
158
|
+
Edit `~/.cursor/mcp.json` (global) or `.cursor/mcp.json` (per-project). Paste the **standard config** above.
|
|
159
|
+
|
|
160
|
+
Or open `Cursor Settings` → `MCP` → `Add new MCP Server`, pick `command` type, and enter `npx -y -p sync-worktrees sync-worktrees-mcp`.
|
|
161
|
+
|
|
162
|
+
</details>
|
|
163
|
+
|
|
164
|
+
<details>
|
|
165
|
+
<summary>Windsurf</summary>
|
|
166
|
+
|
|
167
|
+
Follow the Windsurf MCP [documentation](https://docs.windsurf.com/windsurf/cascade/mcp) and use the **standard config** above.
|
|
168
|
+
|
|
169
|
+
</details>
|
|
170
|
+
|
|
171
|
+
<details>
|
|
172
|
+
<summary>VS Code</summary>
|
|
173
|
+
|
|
174
|
+
Use the VS Code CLI:
|
|
175
|
+
|
|
176
|
+
```bash
|
|
177
|
+
code --add-mcp '{"name":"sync-worktrees","command":"npx","args":["-y","-p","sync-worktrees","sync-worktrees-mcp"]}'
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
Or follow the VS Code MCP install [guide](https://code.visualstudio.com/docs/copilot/chat/mcp-servers#_add-an-mcp-server) and use the **standard config** above.
|
|
181
|
+
|
|
182
|
+
</details>
|
|
183
|
+
|
|
184
|
+
<details>
|
|
185
|
+
<summary>Codex</summary>
|
|
186
|
+
|
|
187
|
+
Use the Codex CLI:
|
|
188
|
+
|
|
189
|
+
```bash
|
|
190
|
+
codex mcp add sync-worktrees -- npx -y -p sync-worktrees sync-worktrees-mcp
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
Or edit `~/.codex/config.toml`:
|
|
194
|
+
|
|
195
|
+
```toml
|
|
196
|
+
[mcp_servers.sync-worktrees]
|
|
197
|
+
command = "npx"
|
|
198
|
+
args = ["-y", "-p", "sync-worktrees", "sync-worktrees-mcp"]
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
</details>
|
|
91
202
|
|
|
92
|
-
|
|
203
|
+
<details>
|
|
204
|
+
<summary>Gemini CLI</summary>
|
|
93
205
|
|
|
94
|
-
|
|
206
|
+
Follow the Gemini CLI MCP install [guide](https://github.com/google-gemini/gemini-cli/blob/main/docs/tools/mcp-server.md#configure-the-mcp-server-in-settingsjson) and use the **standard config** above.
|
|
95
207
|
|
|
96
|
-
|
|
208
|
+
</details>
|
|
97
209
|
|
|
98
|
-
|
|
210
|
+
<details>
|
|
211
|
+
<summary>Cline</summary>
|
|
212
|
+
|
|
213
|
+
Edit `cline_mcp_settings.json` (see [Configuring MCP Servers](https://docs.cline.bot/mcp/configuring-mcp-servers)) and add:
|
|
99
214
|
|
|
100
215
|
```json
|
|
101
216
|
{
|
|
102
217
|
"mcpServers": {
|
|
103
218
|
"sync-worktrees": {
|
|
219
|
+
"type": "stdio",
|
|
104
220
|
"command": "npx",
|
|
105
221
|
"args": ["-y", "-p", "sync-worktrees", "sync-worktrees-mcp"],
|
|
106
|
-
"
|
|
107
|
-
"SYNC_WORKTREES_CONFIG": "/absolute/path/to/sync-worktrees.config.js"
|
|
108
|
-
}
|
|
222
|
+
"disabled": false
|
|
109
223
|
}
|
|
110
224
|
}
|
|
111
225
|
}
|
|
112
226
|
```
|
|
113
227
|
|
|
114
|
-
|
|
228
|
+
</details>
|
|
115
229
|
|
|
116
|
-
|
|
230
|
+
<details>
|
|
231
|
+
<summary>opencode</summary>
|
|
232
|
+
|
|
233
|
+
Edit `~/.config/opencode/opencode.json`:
|
|
234
|
+
|
|
235
|
+
```json
|
|
236
|
+
{
|
|
237
|
+
"$schema": "https://opencode.ai/config.json",
|
|
238
|
+
"mcp": {
|
|
239
|
+
"sync-worktrees": {
|
|
240
|
+
"type": "local",
|
|
241
|
+
"command": ["npx", "-y", "-p", "sync-worktrees", "sync-worktrees-mcp"],
|
|
242
|
+
"enabled": true
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
```
|
|
117
247
|
|
|
118
|
-
|
|
248
|
+
</details>
|
|
119
249
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
250
|
+
<details>
|
|
251
|
+
<summary>Warp</summary>
|
|
252
|
+
|
|
253
|
+
Open `Settings` → `AI` → `Manage MCP Servers` → `+ Add` (see [Warp MCP docs](https://docs.warp.dev/knowledge-and-collaboration/mcp#adding-an-mcp-server)) and paste the **standard config** above. Alternatively, run `/add-mcp` in the prompt.
|
|
254
|
+
|
|
255
|
+
</details>
|
|
125
256
|
|
|
126
257
|
### Available tools
|
|
127
258
|
|
|
@@ -147,286 +278,277 @@ All tools that target a single repo accept an optional `repoName`. When omitted,
|
|
|
147
278
|
- Branches created by sync-worktrees use `--no-track` first, then publish with `git push -u origin <branch>`, so they do not inherit `origin/main` as their upstream.
|
|
148
279
|
- Path-targeted tools verify the supplied path is a registered worktree of the selected repository.
|
|
149
280
|
|
|
150
|
-
##
|
|
281
|
+
## Interactive TUI
|
|
151
282
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
|
157
|
-
|
|
158
|
-
|
|
|
159
|
-
|
|
|
160
|
-
|
|
|
283
|
+
Running `sync-worktrees` without `runOnce` drops you into an interactive terminal UI with live log streaming, manual sync triggers, and wizards for the common operations.
|
|
284
|
+
|
|
285
|
+
### Keybindings
|
|
286
|
+
|
|
287
|
+
| Key | Action |
|
|
288
|
+
|-----|--------|
|
|
289
|
+
| `s` | Manually trigger sync for all repositories |
|
|
290
|
+
| `c` | Create a new branch (wizard) |
|
|
291
|
+
| `o` | Open a worktree in terminal or editor (wizard) |
|
|
292
|
+
| `w` | View worktree status across repos |
|
|
293
|
+
| `r` | Reload configuration and re-sync |
|
|
294
|
+
| `?` / `h` | Toggle help screen |
|
|
295
|
+
| `q` / `Esc` | Gracefully quit |
|
|
296
|
+
| `j` / `↓` | Scroll log down one line |
|
|
297
|
+
| `k` / `↑` | Scroll log up one line |
|
|
298
|
+
| `gg` | Jump to top of log |
|
|
299
|
+
| `G` | Jump to bottom (re-enables auto-scroll) |
|
|
300
|
+
|
|
301
|
+
### Wizards
|
|
302
|
+
|
|
303
|
+
- **Open wizard (`o`)** — select a worktree across all configured repos with live filtering (just type to narrow the list). Press `Tab` to flip between **Terminal** mode (launches a new terminal window attached to a `tmux` session in the worktree) and **Editor** mode (launches `$EDITOR` / `$VISUAL`, falling back to `code`). Re-opening the same worktree attaches to the existing tmux session instead of creating a duplicate.
|
|
304
|
+
- **Branch creation wizard (`c`)** — pick a repo, pick a base branch from a live-filtered list, type the new branch name. Names are validated against Git's rules; if the desired name already exists, a numeric suffix (`-2`, `-3`, …) is suggested automatically.
|
|
305
|
+
- **Worktree status view (`w`)** — flat list of every worktree across every configured repo, each tagged with status flags:
|
|
306
|
+
|
|
307
|
+
| Flag | Meaning |
|
|
308
|
+
|------|---------|
|
|
309
|
+
| `✓` | Clean |
|
|
310
|
+
| `M` | Modified / uncommitted changes |
|
|
311
|
+
| `↑` | Unpushed commits |
|
|
312
|
+
| `S` | Stashed changes |
|
|
313
|
+
| `⚠` | Operation in progress (merge/rebase/cherry-pick/revert/bisect) |
|
|
314
|
+
| `⊞` | Modified submodules |
|
|
315
|
+
| `✗` | Upstream branch is gone |
|
|
316
|
+
|
|
317
|
+
Press `Enter` on an entry to expand file/commit/stash counts. The view also surfaces `.diverged/` directories preserved from past force-pushes; press `d` (with `y`/`n` confirmation) to delete one after reviewing.
|
|
161
318
|
|
|
162
|
-
|
|
319
|
+
### Terminal mode environment variables
|
|
163
320
|
|
|
164
|
-
|
|
321
|
+
| Variable | Purpose | Default behavior |
|
|
322
|
+
|----------|---------|------------------|
|
|
323
|
+
| `SYNC_WORKTREES_TERMINAL` | Override the terminal launcher on any platform. Value is a command string; the tmux invocation is appended via `sh -c`. Example: `SYNC_WORKTREES_TERMINAL="alacritty -e"`. | See per-platform defaults below. |
|
|
324
|
+
| `TERMINAL` | Linux-only fallback when `SYNC_WORKTREES_TERMINAL` is unset. Same format. | Probes `gnome-terminal`, `konsole`, `alacritty`, `kitty`, `xterm` in order. |
|
|
325
|
+
| `EDITOR` / `VISUAL` | Editor mode launcher. | Falls back to `code`. |
|
|
326
|
+
|
|
327
|
+
Per-platform terminal defaults (when no env override is set):
|
|
328
|
+
|
|
329
|
+
- **macOS** — Ghostty if `Ghostty.app` is installed, otherwise Terminal.app via AppleScript.
|
|
330
|
+
- **Linux** — `$TERMINAL` if set; otherwise the first found among the candidates above.
|
|
331
|
+
|
|
332
|
+
Terminal mode requires [`tmux`](https://github.com/tmux/tmux) to be installed.
|
|
333
|
+
|
|
334
|
+
## Configuration
|
|
165
335
|
|
|
166
|
-
|
|
336
|
+
Config files are JavaScript ES modules. Relative paths resolve from the config file's location, and you have full access to `process.env` and Node module loading.
|
|
337
|
+
|
|
338
|
+
### Minimal config
|
|
167
339
|
|
|
168
340
|
```javascript
|
|
169
|
-
|
|
170
|
-
|
|
341
|
+
// @ts-check
|
|
342
|
+
|
|
343
|
+
/** @satisfies {import("sync-worktrees").SyncWorktreesConfig} */
|
|
344
|
+
const config = {
|
|
345
|
+
repositories: [
|
|
346
|
+
{
|
|
347
|
+
name: "my-project",
|
|
348
|
+
repoUrl: "https://github.com/user/my-project.git",
|
|
349
|
+
worktreeDir: "./worktrees/my-project",
|
|
350
|
+
},
|
|
351
|
+
],
|
|
352
|
+
};
|
|
353
|
+
|
|
354
|
+
export default config;
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
### Multi-repo config
|
|
358
|
+
|
|
359
|
+
```javascript
|
|
360
|
+
// @ts-check
|
|
361
|
+
|
|
362
|
+
/** @satisfies {import("sync-worktrees").SyncWorktreesConfig} */
|
|
363
|
+
const config = {
|
|
171
364
|
defaults: {
|
|
172
|
-
cronSchedule: "0 * * * *",
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
updateExistingWorktrees: true // Auto-update worktrees that are behind (default: true)
|
|
365
|
+
cronSchedule: "0 * * * *", // hourly
|
|
366
|
+
branchMaxAge: "30d", // ignore stale branches
|
|
367
|
+
branchExclude: ["wip-*", "tmp-*"],
|
|
368
|
+
updateExistingWorktrees: true,
|
|
177
369
|
},
|
|
178
370
|
|
|
179
|
-
// Retry configuration (optional - these are the defaults)
|
|
180
371
|
retry: {
|
|
181
|
-
maxAttempts:
|
|
182
|
-
initialDelayMs: 1000,
|
|
183
|
-
maxDelayMs: 600000,
|
|
184
|
-
backoffMultiplier: 2
|
|
372
|
+
maxAttempts: "unlimited",
|
|
373
|
+
initialDelayMs: 1000,
|
|
374
|
+
maxDelayMs: 600000,
|
|
375
|
+
backoffMultiplier: 2,
|
|
185
376
|
},
|
|
186
377
|
|
|
187
378
|
repositories: [
|
|
188
379
|
{
|
|
189
|
-
name: "frontend",
|
|
380
|
+
name: "frontend",
|
|
190
381
|
repoUrl: "https://github.com/company/frontend.git",
|
|
191
|
-
worktreeDir: "./worktrees/frontend",
|
|
192
|
-
cronSchedule: "*/30 * * * *"
|
|
382
|
+
worktreeDir: "./worktrees/frontend",
|
|
383
|
+
cronSchedule: "*/30 * * * *", // override default
|
|
193
384
|
},
|
|
194
385
|
{
|
|
195
386
|
name: "backend",
|
|
196
|
-
repoUrl: process.env.BACKEND_REPO_URL
|
|
387
|
+
repoUrl: process.env.BACKEND_REPO_URL || "https://github.com/company/backend.git",
|
|
197
388
|
worktreeDir: "/absolute/path/backend-worktrees",
|
|
198
|
-
branchMaxAge: "6m",
|
|
199
|
-
branchInclude: ["feature/*", "release-*", "main"],
|
|
200
|
-
//
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
]
|
|
389
|
+
branchMaxAge: "6m",
|
|
390
|
+
branchInclude: ["feature/*", "release-*", "main"],
|
|
391
|
+
retry: { maxAttempts: 10 }, // per-repo override
|
|
392
|
+
},
|
|
393
|
+
],
|
|
204
394
|
};
|
|
395
|
+
|
|
396
|
+
export default config;
|
|
205
397
|
```
|
|
206
398
|
|
|
207
|
-
|
|
208
|
-
- Relative paths are resolved from the config file location
|
|
209
|
-
- `bareRepoDir` defaults to `.bare/<repo-name>` if not specified
|
|
210
|
-
- Repository-specific settings override defaults
|
|
399
|
+
Notes:
|
|
211
400
|
|
|
212
|
-
|
|
401
|
+
- `bareRepoDir` defaults to `.bare/<repo-name>` if not specified.
|
|
402
|
+
- Repository-specific settings override `defaults`.
|
|
213
403
|
|
|
214
|
-
|
|
404
|
+
### Clone mode
|
|
215
405
|
|
|
216
|
-
|
|
217
|
-
- **Network errors**: Connection timeouts, DNS failures, repository access issues
|
|
218
|
-
- **Filesystem errors**: Busy files, permission issues, race conditions
|
|
406
|
+
Set `mode: "clone"` to clone one checked-out branch directly into `worktreeDir` instead of maintaining one worktree per remote branch:
|
|
219
407
|
|
|
220
|
-
Simple retry examples:
|
|
221
408
|
```javascript
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
//
|
|
229
|
-
repositories: [{
|
|
230
|
-
name: "critical-repo",
|
|
231
|
-
// ... other config ...
|
|
232
|
-
retry: { maxAttempts: 'unlimited', initialDelayMs: 10000 }
|
|
233
|
-
}]
|
|
234
|
-
```
|
|
235
|
-
|
|
236
|
-
### Git LFS Support
|
|
237
|
-
|
|
238
|
-
For repositories with Git LFS issues or when large files aren't needed:
|
|
239
|
-
|
|
240
|
-
```bash
|
|
241
|
-
# Skip LFS downloads
|
|
242
|
-
sync-worktrees -u https://github.com/user/repo.git -w ./worktrees --skip-lfs
|
|
243
|
-
|
|
244
|
-
# Or in config file
|
|
245
|
-
defaults: {
|
|
246
|
-
skipLfs: true
|
|
409
|
+
{
|
|
410
|
+
name: "game-platform",
|
|
411
|
+
repoUrl: "ssh://git@example.com/game-platform.git",
|
|
412
|
+
worktreeDir: "./slots/game-platform",
|
|
413
|
+
mode: "clone",
|
|
414
|
+
branch: "main",
|
|
415
|
+
depth: 1, // optional shallow clone
|
|
247
416
|
}
|
|
248
417
|
```
|
|
249
418
|
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
### Branch Name Filtering
|
|
419
|
+
Clone mode keeps the normal `+refs/heads/*:refs/remotes/origin/*` fetch refspec, so `git branch -r` and `git fetch --all --prune` can see all remote branches. `branch` controls the checked-out branch that sync-worktrees fast-forwards on each sync. Omit `branch` and the remote HEAD is resolved at clone time.
|
|
253
420
|
|
|
254
|
-
|
|
421
|
+
`depth` is valid only for clone-mode repositories and must be a positive safe integer. Shallow clones use `--no-single-branch` so all remote branch refs remain visible at the configured depth. If you later remove `depth` from the config, the next sync unshallows the existing clone with `git fetch --unshallow`.
|
|
255
422
|
|
|
256
|
-
|
|
257
|
-
- `feature/*` - matches `feature/login`, `feature/auth/oauth`, etc.
|
|
258
|
-
- `release-*` - matches `release-1.0`, `release-2.0-beta`, etc.
|
|
259
|
-
- `*-hotfix` - matches `urgent-hotfix`, `prod-hotfix`, etc.
|
|
423
|
+
Clone mode rejects `branchInclude`, `branchExclude`, `branchMaxAge`, `updateExistingWorktrees`, and `bareRepoDir` at validation time (whether set directly or inherited via `defaults`) — they have no meaning for a single-branch checkout.
|
|
260
424
|
|
|
261
|
-
|
|
262
|
-
- `branchInclude` - only branches matching at least one pattern are synced
|
|
263
|
-
- `branchExclude` - branches matching any pattern are skipped
|
|
264
|
-
- When both are set, include runs first, then exclude removes from the result
|
|
265
|
-
- The default branch (e.g., `main`) is always retained regardless of filters
|
|
425
|
+
### Sparse checkout
|
|
266
426
|
|
|
267
|
-
|
|
268
|
-
```bash
|
|
269
|
-
# Command line
|
|
270
|
-
sync-worktrees -u https://github.com/user/repo.git -w ./worktrees \
|
|
271
|
-
--branchInclude "feature/*,release-*"
|
|
427
|
+
For monorepos where you only need a subset of folders, set `sparseCheckout` per repository entry. The tool runs `git worktree add --no-checkout`, configures sparse-checkout, then materializes only the included paths. The same repository URL can be listed multiple times under different `name`s with different sparse patterns to build domain-grouped layouts.
|
|
272
428
|
|
|
273
|
-
|
|
274
|
-
|
|
429
|
+
```javascript
|
|
430
|
+
// @ts-check
|
|
275
431
|
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
432
|
+
/** @satisfies {import("sync-worktrees").SyncWorktreesConfig} */
|
|
433
|
+
const config = {
|
|
434
|
+
repositories: [
|
|
435
|
+
{
|
|
436
|
+
name: "roulette-game-client",
|
|
437
|
+
repoUrl: "https://github.com/acme/casino-monorepo.git",
|
|
438
|
+
worktreeDir: "/Users/me/game-clients/roulette",
|
|
439
|
+
sparseCheckout: { include: ["game-client"] },
|
|
440
|
+
},
|
|
441
|
+
{
|
|
442
|
+
name: "roulette-autocue",
|
|
443
|
+
repoUrl: "https://github.com/acme/casino-monorepo.git",
|
|
444
|
+
worktreeDir: "/Users/me/autocues/roulette",
|
|
445
|
+
sparseCheckout: { include: ["autocue"] },
|
|
446
|
+
},
|
|
447
|
+
],
|
|
448
|
+
};
|
|
280
449
|
|
|
281
|
-
|
|
282
|
-
repositories: [{
|
|
283
|
-
name: "frontend",
|
|
284
|
-
branchInclude: ["feature/*", "release-*"],
|
|
285
|
-
branchExclude: ["feature/wip-*"],
|
|
286
|
-
}]
|
|
450
|
+
export default config;
|
|
287
451
|
```
|
|
288
452
|
|
|
289
|
-
**
|
|
453
|
+
**Modes:**
|
|
290
454
|
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
sync-worktrees -u https://github.com/user/repo.git -w ./worktrees \
|
|
294
|
-
--branchInclude "feature/*" --branchMaxAge 30d
|
|
295
|
-
```
|
|
455
|
+
- `cone` (default): pass folder names in `include`. Fast and recommended.
|
|
456
|
+
- `no-cone`: pass gitignore-style patterns including `!negation`. Required for `exclude` and any `!`-prefixed include.
|
|
296
457
|
|
|
297
|
-
|
|
458
|
+
If you set `exclude` or `!`-prefixed patterns while `mode: "cone"` is explicit, the tool auto-promotes to `no-cone` and logs a warning.
|
|
298
459
|
|
|
299
|
-
|
|
460
|
+
**Duplicate `repoUrl` handling:** The first entry per `repoUrl` keeps the URL-derived bare path (`.bare/<repo-slug>`). Subsequent duplicate entries auto-derive `bareRepoDir` from `name` (`.bare/<name>`). Pin `bareRepoDir` explicitly on duplicate entries if you want config order to be irrelevant.
|
|
300
461
|
|
|
301
|
-
**
|
|
302
|
-
- `h` - hours (e.g., `24h`)
|
|
303
|
-
- `d` - days (e.g., `30d`)
|
|
304
|
-
- `w` - weeks (e.g., `4w`)
|
|
305
|
-
- `m` - months (e.g., `6m`)
|
|
306
|
-
- `y` - years (e.g., `1y`)
|
|
462
|
+
**Narrowing safety:** When a sync would narrow an existing worktree's sparse patterns (remove a previously included path), it first checks the worktree is clean. If there are uncommitted changes, unpushed commits, or in-progress operations, the sparse update is skipped with a warning.
|
|
307
463
|
|
|
308
|
-
|
|
309
|
-
```bash
|
|
310
|
-
# Command line
|
|
311
|
-
sync-worktrees -u https://github.com/user/repo.git -w ./worktrees --branchMaxAge 30d
|
|
464
|
+
### Branch filtering
|
|
312
465
|
|
|
313
|
-
|
|
466
|
+
Two filters can be combined:
|
|
467
|
+
|
|
468
|
+
```javascript
|
|
314
469
|
defaults: {
|
|
315
|
-
|
|
470
|
+
branchInclude: ["feature/*", "release-*", "main"],
|
|
471
|
+
branchExclude: ["feature/wip-*"],
|
|
472
|
+
branchMaxAge: "30d",
|
|
316
473
|
}
|
|
317
|
-
|
|
318
|
-
# Config file - per repository
|
|
319
|
-
repositories: [{
|
|
320
|
-
name: "active-project",
|
|
321
|
-
branchMaxAge: "14d", // Very active project - only last 2 weeks
|
|
322
|
-
}, {
|
|
323
|
-
name: "legacy-project",
|
|
324
|
-
branchMaxAge: "1y", // Legacy project - keep branches from last year
|
|
325
|
-
}]
|
|
326
474
|
```
|
|
327
475
|
|
|
328
|
-
|
|
329
|
-
-
|
|
330
|
-
-
|
|
331
|
-
-
|
|
332
|
-
- Only create/maintain worktrees for active branches
|
|
333
|
-
|
|
334
|
-
### Handling Rebased and Force-Pushed Branches
|
|
335
|
-
|
|
336
|
-
sync-worktrees intelligently handles branches that have been rebased or force-pushed to prevent data loss:
|
|
337
|
-
|
|
338
|
-
**Automatic behavior (no configuration needed):**
|
|
476
|
+
- **Name patterns** support `*` wildcards (including across `/`): `feature/*` matches `feature/login` and `feature/auth/oauth`.
|
|
477
|
+
- **`branchInclude`** keeps only matching branches; **`branchExclude`** removes matching branches. When both are set, include runs first, then exclude.
|
|
478
|
+
- **`branchMaxAge`** drops branches whose latest commit is older than the duration (`h`/`d`/`w`/`m`/`y` — e.g. `24h`, `30d`, `6m`, `1y`). Applied after name filtering.
|
|
479
|
+
- The default branch is always retained regardless of filters.
|
|
339
480
|
|
|
340
|
-
|
|
341
|
-
- Automatically resets the worktree to match the upstream
|
|
342
|
-
- No data loss since the content is the same
|
|
481
|
+
### Diverged branches
|
|
343
482
|
|
|
344
|
-
|
|
345
|
-
- Automatically resets to the new upstream state
|
|
346
|
-
- No move to `.diverged` since you have no work to preserve
|
|
347
|
-
- Keeps `.diverged` clean by only preserving actual user work
|
|
483
|
+
When upstream is force-pushed and your worktree contains divergent local commits, sync-worktrees moves the worktree to a hidden `.diverged/` directory before creating a fresh one from the new upstream. No data loss; you can review the old state later.
|
|
348
484
|
|
|
349
|
-
3. **Diverged branches WITH local changes** - When a branch has different content AND you've made local commits:
|
|
350
|
-
- Moves the worktree to `.diverged` directory within your worktrees folder
|
|
351
|
-
- Preserves all your local changes and commits
|
|
352
|
-
- Creates a fresh worktree from the upstream branch
|
|
353
|
-
- Logs clear instructions for reviewing diverged changes
|
|
354
|
-
|
|
355
|
-
**Example diverged structure:**
|
|
356
485
|
```
|
|
357
486
|
my-repo-worktrees/
|
|
358
487
|
├── main/
|
|
359
488
|
├── feature-a/
|
|
360
|
-
|
|
361
|
-
└──
|
|
362
|
-
├── 2024-01-15-feature-x/ # Timestamp + branch name
|
|
363
|
-
│ ├── .diverged-info.json # Metadata about the divergence
|
|
364
|
-
│ └── [all your local files]
|
|
365
|
-
└── 2024-01-16-feature-y/
|
|
489
|
+
└── .diverged/
|
|
490
|
+
└── 2024-01-15-feature-x/
|
|
366
491
|
├── .diverged-info.json
|
|
367
492
|
└── [all your local files]
|
|
368
493
|
```
|
|
369
494
|
|
|
370
|
-
|
|
495
|
+
Reviewing a diverged worktree:
|
|
496
|
+
|
|
371
497
|
```bash
|
|
372
|
-
# See what's different
|
|
373
498
|
cd my-repo-worktrees/.diverged/2024-01-15-feature-x
|
|
374
499
|
git diff origin/feature-x
|
|
375
500
|
|
|
376
|
-
#
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
# If you want to discard and use upstream
|
|
380
|
-
cd ../..
|
|
381
|
-
rm -rf .diverged/2024-01-15-feature-x
|
|
501
|
+
# keep local: git push --force-with-lease
|
|
502
|
+
# discard: cd ../.. && rm -rf .diverged/2024-01-15-feature-x
|
|
382
503
|
```
|
|
383
504
|
|
|
384
|
-
|
|
505
|
+
The TUI's worktree status view (`w`) lists diverged directories and offers a guided delete (`d` with `y`/`n` confirmation) once you've decided.
|
|
385
506
|
|
|
386
|
-
|
|
507
|
+
Clean rebases where file content matches the upstream are auto-applied with no detour through `.diverged/`. Diverged-but-no-local-commits is also handled without preservation, since there's no user work to keep.
|
|
387
508
|
|
|
388
|
-
|
|
509
|
+
### Retry and LFS
|
|
389
510
|
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
worktreeDir: "/Users/me/game-clients/roulette",
|
|
397
|
-
sparseCheckout: { include: ["game-client"] },
|
|
398
|
-
},
|
|
399
|
-
{
|
|
400
|
-
name: "roulette-autocue",
|
|
401
|
-
repoUrl: "https://github.com/acme/casino-monorepo.git",
|
|
402
|
-
worktreeDir: "/Users/me/autocues/roulette",
|
|
403
|
-
sparseCheckout: { include: ["autocue"] },
|
|
404
|
-
// bareRepoDir: ".bare/roulette-autocue", // pin to make config reorder-proof
|
|
405
|
-
},
|
|
406
|
-
],
|
|
407
|
-
};
|
|
511
|
+
The tool retries network errors (timeouts, DNS failures, access issues) and filesystem race conditions automatically:
|
|
512
|
+
|
|
513
|
+
```javascript
|
|
514
|
+
retry: { maxAttempts: 5 } // try 5 times then stop
|
|
515
|
+
retry: { maxAttempts: "unlimited" } // keep trying forever (default)
|
|
516
|
+
retry: { maxDelayMs: 60000 } // cap retry delay at 1 minute
|
|
408
517
|
```
|
|
409
518
|
|
|
410
|
-
|
|
519
|
+
For repositories with Git LFS issues or large files you don't need, set `skipLfs: true` in `defaults` or per repository. The tool also retries LFS-specific failures with LFS disabled (configurable via `retry.maxLfsRetries`).
|
|
411
520
|
|
|
412
|
-
|
|
413
|
-
|
|
521
|
+
### Hooks and file copying
|
|
522
|
+
|
|
523
|
+
Two lifecycle hooks the example config covers in depth:
|
|
524
|
+
|
|
525
|
+
- `hooks.onBranchCreated` — array of shell commands run after a new branch's worktree is created. Placeholders: `{BRANCH_NAME}`, `{WORKTREE_PATH}`, `{REPO_NAME}`, `{BASE_BRANCH}`, `{REPO_URL}`. Fire-and-forget.
|
|
526
|
+
- `filesToCopyOnBranchCreate` — paths copied into every newly created worktree (e.g. `.env.local`, `.npmrc`). Glob patterns are resolved relative to the config file's directory.
|
|
414
527
|
|
|
415
|
-
|
|
528
|
+
In clone mode, `filesToCopyOnBranchCreate` fires once on the initial clone, and `hooks.onBranchCreated` fires only for TUI-initiated branch creation (clone mode tracks a single fixed branch with no later branch-creation events).
|
|
416
529
|
|
|
417
|
-
|
|
530
|
+
For every knob (timeouts, parallelism, jitter, sparse-update behavior, retry tuning), see [`sync-worktrees.config.example.js`](./sync-worktrees.config.example.js).
|
|
418
531
|
|
|
419
|
-
|
|
532
|
+
## CLI options
|
|
533
|
+
|
|
534
|
+
The CLI loads a config file and runs it. Most run-mode settings (branch filters, retry, parallelism, LFS, clone mode, depth, etc.) live in the config file. Use `--runOnce` for an ad-hoc one-shot run without editing config.
|
|
535
|
+
|
|
536
|
+
| Option | Alias | Description | Default |
|
|
537
|
+
|--------|-------|-------------|---------|
|
|
538
|
+
| `--config` | `-c` | Path to JavaScript config file (auto-detected in CWD when omitted) | - |
|
|
539
|
+
| `--runOnce` | - | Run a sync once and exit, overriding config `runOnce` settings for this invocation | `false` |
|
|
540
|
+
| `--help` | `-h` | Show help | - |
|
|
541
|
+
| `--version` | - | Print version | - |
|
|
420
542
|
|
|
421
|
-
|
|
543
|
+
Subcommands:
|
|
422
544
|
|
|
423
|
-
|
|
545
|
+
- `sync-worktrees init [--config <path>] [--force]` — interactive wizard that writes a minimal config file (`./sync-worktrees.config.js` by default). Refuses to overwrite an existing target unless `--force` is passed.
|
|
546
|
+
- `sync-worktrees list [--config <path>] [--filter <pattern>]` — print the resolved repositories and exit.
|
|
424
547
|
|
|
425
548
|
## Requirements
|
|
426
549
|
|
|
427
550
|
- Node.js >= 22.0.0
|
|
428
551
|
- Git
|
|
429
|
-
- [`tmux`](https://github.com/tmux/tmux) (optional, required only for Terminal mode in the TUI)
|
|
430
552
|
- An MCP-capable client (optional, only for the `sync-worktrees-mcp` server)
|
|
431
553
|
|
|
432
554
|
## Contributing
|