yggtree 1.0.0 → 1.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 +388 -39
- package/dist/commands/wt/bootstrap.js +8 -5
- package/dist/commands/wt/create-branch.js +52 -20
- package/dist/commands/wt/create-multi.js +23 -12
- package/dist/commands/wt/create.js +50 -20
- package/dist/commands/wt/delete.js +38 -19
- package/dist/commands/wt/enter.js +110 -0
- package/dist/commands/wt/exec.js +93 -0
- package/dist/commands/wt/leave.js +13 -0
- package/dist/commands/wt/list.js +13 -7
- package/dist/commands/wt/path.js +62 -0
- package/dist/commands/wt/prune.js +4 -1
- package/dist/index.js +57 -7
- package/dist/lib/config.js +44 -25
- package/dist/lib/git.js +13 -0
- package/dist/lib/paths.js +2 -2
- package/dist/lib/ui.js +14 -1
- package/dist/lib/version.js +17 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
# 🌳 Yggdrasil Worktree (yggtree)
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://www.npmjs.com/package/yggtree)
|
|
4
|
+
[](https://www.npmjs.com/package/yggtree)
|
|
5
|
+
|
|
6
|
+
**Yggdrasil Worktree** (invoked as `yggtree`) is an interactive CLI designed to turn Git worktrees into a first‑class workflow.
|
|
7
|
+
|
|
8
|
+
Like the mythical world tree connecting realms, Yggdrasil lets you grow isolated, parallel environments where ideas can evolve independently without colliding.
|
|
4
9
|
|
|
5
10
|
---
|
|
6
11
|
|
|
@@ -8,56 +13,158 @@
|
|
|
8
13
|
|
|
9
14
|
### Installation
|
|
10
15
|
|
|
16
|
+
Run without installing:
|
|
17
|
+
|
|
11
18
|
```bash
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
cd yggtree
|
|
19
|
+
npx yggtree
|
|
20
|
+
```
|
|
15
21
|
|
|
16
|
-
|
|
17
|
-
npm install
|
|
18
|
-
npm run build
|
|
22
|
+
Or install globally:
|
|
19
23
|
|
|
20
|
-
|
|
21
|
-
npm
|
|
24
|
+
```bash
|
|
25
|
+
npm install -g yggtree
|
|
22
26
|
```
|
|
23
27
|
|
|
24
|
-
### Usage
|
|
28
|
+
### Basic Usage
|
|
25
29
|
|
|
26
|
-
|
|
30
|
+
Run with no arguments to open the interactive menu:
|
|
27
31
|
|
|
28
32
|
```bash
|
|
29
33
|
yggtree
|
|
30
34
|
```
|
|
31
35
|
|
|
32
|
-
Or use
|
|
36
|
+
Or use commands directly:
|
|
33
37
|
|
|
34
38
|
```bash
|
|
35
|
-
yggtree wt create
|
|
36
|
-
yggtree wt list
|
|
37
|
-
yggtree wt
|
|
39
|
+
yggtree wt create
|
|
40
|
+
yggtree wt list
|
|
41
|
+
yggtree wt enter my-feature
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
## 🧠 Mental Model
|
|
47
|
+
|
|
48
|
+
Yggdrasil is built around a few core ideas:
|
|
49
|
+
|
|
50
|
+
* **Branches are ideas**
|
|
51
|
+
* **Worktrees are realities**
|
|
52
|
+
* **Each task deserves its own realm**
|
|
53
|
+
|
|
54
|
+
Instead of constantly switching branches in one working directory, Yggdrasil creates **isolated worktrees**, each mapped to a branch, living outside your main repo.
|
|
55
|
+
|
|
56
|
+
All managed worktrees live under:
|
|
57
|
+
|
|
58
|
+
```
|
|
59
|
+
~/.yggtree/<repo-name>/<worktree-slug>
|
|
38
60
|
```
|
|
39
61
|
|
|
62
|
+
This keeps your main repository clean while enabling true parallelism.
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
## 🤔 Why Yggdrasil Worktree?
|
|
67
|
+
|
|
68
|
+
Git worktrees are powerful, but once you start doing **parallel work**, they become tedious to manage manually.
|
|
69
|
+
|
|
70
|
+
Modern development looks like this:
|
|
71
|
+
|
|
72
|
+
* Fixing a bug
|
|
73
|
+
* Reviewing a PR
|
|
74
|
+
* Prototyping a feature
|
|
75
|
+
* Letting AI agents explore refactors
|
|
76
|
+
* Running tests in isolation
|
|
77
|
+
|
|
78
|
+
All at the same time.
|
|
79
|
+
|
|
80
|
+
Yggdrasil exists to solve three problems together:
|
|
81
|
+
|
|
82
|
+
1. Parallel work without context collision
|
|
83
|
+
2. Fast, repeatable environment setup
|
|
84
|
+
3. Agent‑friendly isolation for AI workflows
|
|
85
|
+
|
|
86
|
+
Each worktree becomes its own **small realm**, safe to explore and easy to discard.
|
|
87
|
+
|
|
40
88
|
---
|
|
41
89
|
|
|
42
90
|
## ✨ Key Features
|
|
43
91
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
92
|
+
🌳 **First-class worktree workflow**
|
|
93
|
+
Create, manage, and navigate Git worktrees as a primary workflow, not an afterthought.
|
|
94
|
+
|
|
95
|
+
🧠 **Parallel development by default**
|
|
96
|
+
Work on multiple branches at the same time, each in its own isolated environment.
|
|
97
|
+
|
|
98
|
+
🤖 **AI-friendly isolation**
|
|
99
|
+
One worktree per agent, per experiment, per idea. No shared state, no collisions.
|
|
100
|
+
|
|
101
|
+
⚡ **Automatic bootstrapping**
|
|
102
|
+
Run installs, submodules, and setup scripts automatically for each worktree.
|
|
103
|
+
|
|
104
|
+
🚪 **Enter, exec, and exit with ease**
|
|
105
|
+
Enter worktrees, execute commands, or run tasks without changing directories.
|
|
106
|
+
|
|
107
|
+
📍 **Predictable structure**
|
|
108
|
+
All managed worktrees live under `~/.yggtree`, keeping your repository clean.
|
|
109
|
+
|
|
110
|
+
🧭 **Interactive or scriptable**
|
|
111
|
+
Use the interactive UI or drive everything through commands and flags.
|
|
112
|
+
|
|
113
|
+
---
|
|
114
|
+
|
|
115
|
+
## 🧠 Parallel Development, Done Right
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
yggtree wt create feat/eng-2581-state-selection
|
|
119
|
+
yggtree wt create fix/eng-2610-validation
|
|
120
|
+
yggtree wt create chore/cleanup-api
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
Each command creates:
|
|
124
|
+
|
|
125
|
+
* A clean folder
|
|
126
|
+
* A dedicated branch
|
|
127
|
+
* A bootstrapped environment
|
|
128
|
+
|
|
129
|
+
No stash juggling.
|
|
130
|
+
No branch confusion.
|
|
131
|
+
No shared state accidents.
|
|
132
|
+
|
|
133
|
+
---
|
|
134
|
+
|
|
135
|
+
## 🤖 Built for AI‑Assisted Workflows
|
|
136
|
+
|
|
137
|
+
Yggdrasil shines when paired with AI agents.
|
|
138
|
+
|
|
139
|
+
Instead of running agents against the same directory, you can assign **one worktree per agent**.
|
|
140
|
+
|
|
141
|
+
```bash
|
|
142
|
+
yggtree wt create feat/ai-refactor-a --exec "cursor ."
|
|
143
|
+
yggtree wt create feat/ai-refactor-b --exec "codex"
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
Each agent operates in its own realm:
|
|
49
147
|
|
|
50
|
-
|
|
51
|
-
|
|
148
|
+
* Model A refactors architecture
|
|
149
|
+
* Model B focuses on tests
|
|
150
|
+
* Model C explores performance
|
|
52
151
|
|
|
53
|
-
|
|
54
|
-
Configure your environment automatically using an `anvil-worktree.json` (also compatible with `.cursor/worktrees.json`) file in your project root.
|
|
152
|
+
All in parallel. All reviewable. All isolated.
|
|
55
153
|
|
|
56
154
|
---
|
|
57
155
|
|
|
58
|
-
##
|
|
156
|
+
## ⚡ Bootstrapping & Configuration
|
|
157
|
+
|
|
158
|
+
Yggdrasil automatically prepares each worktree.
|
|
159
|
+
|
|
160
|
+
Resolution order:
|
|
161
|
+
|
|
162
|
+
1. `yggtree-worktree.json` inside the worktree
|
|
163
|
+
2. `yggtree-worktree.json` in the repo root
|
|
164
|
+
3. `.cursor/worktrees.json`
|
|
165
|
+
4. Fallback: `npm install` + submodules
|
|
59
166
|
|
|
60
|
-
|
|
167
|
+
### Example configuration
|
|
61
168
|
|
|
62
169
|
```json
|
|
63
170
|
{
|
|
@@ -65,26 +172,268 @@ Yggdrasil looks for setup instructions in your project root:
|
|
|
65
172
|
"npm install",
|
|
66
173
|
"git submodule sync --recursive",
|
|
67
174
|
"git submodule update --init --recursive",
|
|
68
|
-
"
|
|
69
|
-
"echo '🌳 The realm is ready!'"
|
|
175
|
+
"echo \"🌳 Realm ready\""
|
|
70
176
|
]
|
|
71
177
|
}
|
|
72
178
|
```
|
|
73
179
|
|
|
74
180
|
---
|
|
75
181
|
|
|
76
|
-
## 🛠️
|
|
182
|
+
## 🛠️ Command Reference
|
|
183
|
+
|
|
184
|
+
### `yggtree`
|
|
185
|
+
|
|
186
|
+
Open the interactive menu.
|
|
187
|
+
|
|
188
|
+
---
|
|
189
|
+
|
|
190
|
+
### `yggtree wt create [branch]`
|
|
191
|
+
|
|
192
|
+
Create a worktree from a branch.
|
|
193
|
+
|
|
194
|
+
Options:
|
|
195
|
+
|
|
196
|
+
* `--base <ref>`
|
|
197
|
+
* `--source local|remote`
|
|
198
|
+
* `--no-bootstrap`
|
|
199
|
+
* `--enter / --no-enter`
|
|
200
|
+
* `--exec "<command>"`
|
|
201
|
+
|
|
202
|
+
<details>
|
|
203
|
+
<summary>Example</summary>
|
|
204
|
+
|
|
205
|
+
```bash
|
|
206
|
+
yggtree wt create feat/new-ui --base main --exec "cursor ."
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
</details>
|
|
210
|
+
|
|
211
|
+
---
|
|
212
|
+
|
|
213
|
+
### `yggtree wt create-multi`
|
|
214
|
+
|
|
215
|
+
Create multiple worktrees at once.
|
|
216
|
+
|
|
217
|
+
<details>
|
|
218
|
+
<summary>Example</summary>
|
|
219
|
+
|
|
220
|
+
```bash
|
|
221
|
+
yggtree wt create-multi --base main
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
</details>
|
|
225
|
+
|
|
226
|
+
---
|
|
227
|
+
|
|
228
|
+
### `yggtree wt list`
|
|
229
|
+
|
|
230
|
+
List all worktrees with state.
|
|
231
|
+
|
|
232
|
+
Columns:
|
|
233
|
+
|
|
234
|
+
* TYPE (MAIN / MANAGED)
|
|
235
|
+
* STATE (clean / dirty)
|
|
236
|
+
* BRANCH
|
|
237
|
+
* PATH
|
|
238
|
+
|
|
239
|
+
---
|
|
240
|
+
|
|
241
|
+
### `yggtree wt enter [worktree]`
|
|
242
|
+
|
|
243
|
+
Enter a worktree using a sub‑shell.
|
|
244
|
+
|
|
245
|
+
* Uses your default shell
|
|
246
|
+
* Type `exit` to return
|
|
247
|
+
|
|
248
|
+
Optional:
|
|
249
|
+
|
|
250
|
+
* `--exec "<command>"`
|
|
251
|
+
|
|
252
|
+
<details>
|
|
253
|
+
<summary>Example</summary>
|
|
254
|
+
|
|
255
|
+
```bash
|
|
256
|
+
yggtree wt enter feat/new-ui --exec "npm test"
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
</details>
|
|
260
|
+
|
|
261
|
+
---
|
|
262
|
+
|
|
263
|
+
### `yggtree wt exec [worktree] -- <command>`
|
|
264
|
+
|
|
265
|
+
Run a command inside a worktree **without entering**.
|
|
266
|
+
|
|
267
|
+
<details>
|
|
268
|
+
<summary>Example</summary>
|
|
269
|
+
|
|
270
|
+
```bash
|
|
271
|
+
yggtree wt exec feat/new-ui -- npm test
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
</details>
|
|
275
|
+
|
|
276
|
+
---
|
|
277
|
+
|
|
278
|
+
### `yggtree wt path [worktree]`
|
|
279
|
+
|
|
280
|
+
Print a `cd` command for a worktree.
|
|
281
|
+
|
|
282
|
+
Useful for scripting and shell aliases.
|
|
283
|
+
|
|
284
|
+
---
|
|
285
|
+
|
|
286
|
+
### `yggtree wt bootstrap`
|
|
287
|
+
|
|
288
|
+
Re‑run bootstrap commands for a worktree.
|
|
289
|
+
|
|
290
|
+
---
|
|
291
|
+
|
|
292
|
+
### `yggtree wt delete`
|
|
293
|
+
|
|
294
|
+
Interactively delete managed worktrees.
|
|
295
|
+
|
|
296
|
+
---
|
|
297
|
+
|
|
298
|
+
### `yggtree wt prune`
|
|
299
|
+
|
|
300
|
+
Clean up stale git worktree metadata.
|
|
301
|
+
|
|
302
|
+
---
|
|
303
|
+
|
|
304
|
+
## 🌱 When Should You Use Yggdrasil?
|
|
305
|
+
|
|
306
|
+
Yggdrasil is ideal when:
|
|
307
|
+
|
|
308
|
+
* You work on multiple tasks in parallel
|
|
309
|
+
* You use AI agents for exploration
|
|
310
|
+
* You want isolation without duplication
|
|
311
|
+
* You value scripted, repeatable setups
|
|
312
|
+
* `git checkout` no longer scales
|
|
313
|
+
|
|
314
|
+
---
|
|
315
|
+
|
|
316
|
+
## 📝 Practical Examples
|
|
317
|
+
|
|
318
|
+
<details>
|
|
319
|
+
<summary>Create a worktree and enter it immediately</summary>
|
|
320
|
+
|
|
321
|
+
**Command:**
|
|
322
|
+
|
|
323
|
+
```
|
|
324
|
+
yggtree wt create feat/login-flow
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
**What happens:**
|
|
328
|
+
|
|
329
|
+
* Creates a new branch if it doesn’t exist
|
|
330
|
+
* Creates a dedicated worktree
|
|
331
|
+
* Runs bootstrap if enabled
|
|
332
|
+
* Drops you into a sub-shell inside the worktree
|
|
333
|
+
|
|
334
|
+
</details>
|
|
335
|
+
---
|
|
336
|
+
|
|
337
|
+
<details>
|
|
338
|
+
<summary>Create a worktree without bootstrap and without entering</summary>
|
|
339
|
+
|
|
340
|
+
**Command:**
|
|
341
|
+
|
|
342
|
+
```
|
|
343
|
+
yggtree wt create feat/cleanup-api --no-bootstrap --no-enter
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
**When to use:**
|
|
347
|
+
|
|
348
|
+
* You just want the folder ready
|
|
349
|
+
* You’ll enter it later
|
|
350
|
+
* You don’t want installs running automatically
|
|
351
|
+
|
|
352
|
+
</details>
|
|
353
|
+
---
|
|
354
|
+
|
|
355
|
+
<details>
|
|
356
|
+
<summary>Create a worktree and open it in your IDE</summary>
|
|
357
|
+
|
|
358
|
+
**Command:**
|
|
359
|
+
|
|
360
|
+
```
|
|
361
|
+
yggtree wt create feat/ui-refactor --exec "cursor ."
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
Works with:
|
|
365
|
+
|
|
366
|
+
* `cursor .`
|
|
367
|
+
* `code .`
|
|
368
|
+
* `codex`
|
|
369
|
+
* Any custom command available in your shell
|
|
370
|
+
|
|
371
|
+
</details>
|
|
372
|
+
---
|
|
373
|
+
|
|
374
|
+
<details>
|
|
375
|
+
<summary>Execute a command inside an existing worktree (no shell)</summary>
|
|
376
|
+
|
|
377
|
+
**Command:**
|
|
378
|
+
|
|
379
|
+
```
|
|
380
|
+
yggtree wt exec test -- npm test
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
**What this does:**
|
|
384
|
+
|
|
385
|
+
* Runs the command inside the selected worktree
|
|
386
|
+
* Does not enter a sub-shell
|
|
387
|
+
* Ideal for CI-like checks, scripts, or quick validations
|
|
388
|
+
|
|
389
|
+
</details>
|
|
390
|
+
---
|
|
391
|
+
|
|
392
|
+
<details>
|
|
393
|
+
<summary>Enter a worktree and run a command before entering</summary>
|
|
394
|
+
|
|
395
|
+
**Command:**
|
|
396
|
+
|
|
397
|
+
```
|
|
398
|
+
yggtree wt enter test --exec "codex"
|
|
399
|
+
```
|
|
400
|
+
|
|
401
|
+
**What happens:**
|
|
402
|
+
|
|
403
|
+
* Executes the command inside the worktree
|
|
404
|
+
* Then drops you into a sub-shell
|
|
405
|
+
* Type `exit` to return to your original directory
|
|
406
|
+
|
|
407
|
+
</details>
|
|
408
|
+
---
|
|
409
|
+
|
|
410
|
+
<details>
|
|
411
|
+
<summary>Get the path to a worktree</summary>
|
|
412
|
+
|
|
413
|
+
**Command:**
|
|
414
|
+
|
|
415
|
+
```
|
|
416
|
+
yggtree wt path test
|
|
417
|
+
```
|
|
418
|
+
|
|
419
|
+
**Output:**
|
|
420
|
+
|
|
421
|
+
```
|
|
422
|
+
cd ~/.yggtree/your-repo-name/test
|
|
423
|
+
```
|
|
424
|
+
|
|
425
|
+
Useful when you want to manually navigate or copy the path into scripts.
|
|
426
|
+
|
|
427
|
+
</details>
|
|
428
|
+
|
|
429
|
+
---
|
|
430
|
+
|
|
431
|
+
## 🌍 Philosophy
|
|
432
|
+
|
|
433
|
+
Branches are ideas.
|
|
434
|
+
Worktrees are realities.
|
|
77
435
|
|
|
78
|
-
|
|
79
|
-
| :--- | :--- |
|
|
80
|
-
| `yggtree` | Open the interactive main menu. |
|
|
81
|
-
| `yggtree wt create` | Create a worktree by branch name (Recommended). |
|
|
82
|
-
| `yggtree wt create-multi` | Create multiple worktrees in a single command. |
|
|
83
|
-
| `yggtree wt create-slug` | Manually specify both folder name and branch ref. |
|
|
84
|
-
| `yggtree wt list` | List all managed worktrees and their status. |
|
|
85
|
-
| `yggtree wt delete` | Interactively select and remove a worktree. |
|
|
86
|
-
| `yggtree wt bootstrap` | Re-run the setup commands for an existing worktree. |
|
|
87
|
-
| `yggtree wt prune` | Clean up Git's internal data for worktrees. |
|
|
436
|
+
Yggdrasil helps you grow many worlds and decide later which ones deserve to merge.
|
|
88
437
|
|
|
89
438
|
---
|
|
90
439
|
|
|
@@ -17,10 +17,13 @@ export async function bootstrapCommand() {
|
|
|
17
17
|
log.info('No managed worktrees found to bootstrap.');
|
|
18
18
|
return;
|
|
19
19
|
}
|
|
20
|
-
const choices = managedWts.map(wt =>
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
20
|
+
const choices = managedWts.map(wt => {
|
|
21
|
+
const relative = path.relative(WORKTREES_ROOT, wt.path);
|
|
22
|
+
return {
|
|
23
|
+
name: `${chalk.bold(relative)} (${chalk.dim(wt.branch || wt.HEAD)})`,
|
|
24
|
+
value: wt.path,
|
|
25
|
+
};
|
|
26
|
+
});
|
|
24
27
|
const { selectedPath } = await inquirer.prompt([
|
|
25
28
|
{
|
|
26
29
|
type: 'list',
|
|
@@ -35,7 +38,7 @@ export async function bootstrapCommand() {
|
|
|
35
38
|
log.success('Bootstrap completed!');
|
|
36
39
|
}
|
|
37
40
|
catch (error) {
|
|
38
|
-
log.
|
|
41
|
+
log.actionableError(error.message, 'yggtree wt bootstrap');
|
|
39
42
|
process.exit(1);
|
|
40
43
|
}
|
|
41
44
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
2
|
import inquirer from 'inquirer';
|
|
3
3
|
import path from 'path';
|
|
4
|
-
import { getRepoRoot, verifyRef, fetchAll, getCurrentBranch } from '../../lib/git.js';
|
|
4
|
+
import { getRepoRoot, getRepoName, verifyRef, fetchAll, getCurrentBranch } from '../../lib/git.js';
|
|
5
5
|
import { runBootstrap } from '../../lib/config.js';
|
|
6
6
|
import { WORKTREES_ROOT } from '../../lib/paths.js';
|
|
7
7
|
import { log, ui, createSpinner } from '../../lib/ui.js';
|
|
@@ -41,37 +41,45 @@ export async function createCommandNew(options) {
|
|
|
41
41
|
{ name: 'Local', value: 'local' },
|
|
42
42
|
],
|
|
43
43
|
default: 'remote',
|
|
44
|
-
when: !options.base,
|
|
44
|
+
when: !options.base && !options.source,
|
|
45
45
|
},
|
|
46
46
|
{
|
|
47
47
|
type: 'confirm',
|
|
48
48
|
name: 'bootstrap',
|
|
49
49
|
message: 'Run bootstrap? (npm install + submodules)',
|
|
50
50
|
default: true,
|
|
51
|
-
when: options.bootstrap !== false,
|
|
51
|
+
when: options.bootstrap !== false && options.bootstrap !== true,
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
type: 'confirm',
|
|
55
|
+
name: 'shouldEnter',
|
|
56
|
+
message: 'Do you want to enter the new worktree now?',
|
|
57
|
+
default: true,
|
|
58
|
+
when: options.enter === undefined,
|
|
52
59
|
},
|
|
60
|
+
{
|
|
61
|
+
type: 'input',
|
|
62
|
+
name: 'exec',
|
|
63
|
+
message: 'Command to run after creation (optional):',
|
|
64
|
+
default: options.exec,
|
|
65
|
+
when: options.exec === undefined,
|
|
66
|
+
}
|
|
53
67
|
]);
|
|
54
|
-
let shouldEnter = false;
|
|
55
|
-
if (!options.branch) {
|
|
56
|
-
const finalAnswer = await inquirer.prompt([{
|
|
57
|
-
type: 'confirm',
|
|
58
|
-
name: 'shouldEnter',
|
|
59
|
-
message: 'Do you want to enter the new worktree now?',
|
|
60
|
-
default: true
|
|
61
|
-
}]);
|
|
62
|
-
shouldEnter = finalAnswer.shouldEnter;
|
|
63
|
-
}
|
|
64
68
|
const branchName = options.branch || answers.branch;
|
|
65
69
|
let baseRef = options.base || answers.base;
|
|
70
|
+
const source = options.source || answers.source;
|
|
71
|
+
const shouldEnter = options.enter !== undefined ? options.enter : answers.shouldEnter;
|
|
72
|
+
const shouldBootstrap = options.bootstrap !== undefined ? options.bootstrap : answers.bootstrap;
|
|
73
|
+
const execCommandStr = options.exec || answers.exec;
|
|
66
74
|
// Append origin/ if remote is selected and not already present
|
|
67
|
-
if (!options.base &&
|
|
75
|
+
if (!options.base && source === 'remote' && !baseRef.startsWith('origin/')) {
|
|
68
76
|
baseRef = `origin/${baseRef}`;
|
|
69
77
|
}
|
|
70
|
-
const shouldBootstrap = options.bootstrap === false ? false : answers.bootstrap;
|
|
71
78
|
// Convert branch name to slug (friendly folder name)
|
|
72
79
|
// e.g. feat/eng-2222-new-button -> feat-eng-2222-new-button
|
|
73
80
|
const slug = branchName.replace(/[\/\\]/g, '-').replace(/\s+/g, '-');
|
|
74
|
-
const
|
|
81
|
+
const repoName = await getRepoName();
|
|
82
|
+
const wtPath = path.join(WORKTREES_ROOT, repoName, slug);
|
|
75
83
|
// 2. Validation
|
|
76
84
|
if (!slug)
|
|
77
85
|
throw new Error('Invalid name');
|
|
@@ -106,14 +114,38 @@ export async function createCommandNew(options) {
|
|
|
106
114
|
}
|
|
107
115
|
catch (e) {
|
|
108
116
|
spinner.fail('Failed to create worktree.');
|
|
109
|
-
|
|
117
|
+
const cmd = targetBranchExists
|
|
118
|
+
? `git worktree add ${wtPath} ${branchName}`
|
|
119
|
+
: `git worktree add -b ${branchName} ${wtPath} ${baseRef}`;
|
|
120
|
+
log.actionableError(e.message, cmd, wtPath, [
|
|
121
|
+
'Check if the folder already exists: ls ' + wtPath,
|
|
122
|
+
'Check if the branch is already used: git worktree list',
|
|
123
|
+
'Try pruning stale worktrees: yggtree wt prune',
|
|
124
|
+
`Run manually: ${cmd}`
|
|
125
|
+
]);
|
|
110
126
|
return;
|
|
111
127
|
}
|
|
112
|
-
// 4. Bootstrap
|
|
113
128
|
if (shouldBootstrap) {
|
|
114
129
|
await runBootstrap(wtPath, repoRoot);
|
|
115
130
|
}
|
|
116
|
-
// 5.
|
|
131
|
+
// 5. Exec Command
|
|
132
|
+
if (execCommandStr && execCommandStr.trim()) {
|
|
133
|
+
log.info(`Executing: ${execCommandStr} in ${ui.path(wtPath)}`);
|
|
134
|
+
try {
|
|
135
|
+
await execa(execCommandStr, {
|
|
136
|
+
cwd: wtPath,
|
|
137
|
+
stdio: 'inherit',
|
|
138
|
+
shell: true
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
catch (error) {
|
|
142
|
+
log.actionableError(error.message, execCommandStr, wtPath, [
|
|
143
|
+
`cd ${wtPath} && ${execCommandStr}`,
|
|
144
|
+
'Check your command syntax and environment variables'
|
|
145
|
+
]);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
// 6. Final Output
|
|
117
149
|
log.success('Worktree ready!');
|
|
118
150
|
if (shouldEnter) {
|
|
119
151
|
log.info(`Spawning sub-shell in ${ui.path(wtPath)}...`);
|
|
@@ -132,7 +164,7 @@ export async function createCommandNew(options) {
|
|
|
132
164
|
}
|
|
133
165
|
}
|
|
134
166
|
catch (error) {
|
|
135
|
-
log.
|
|
167
|
+
log.actionableError(error.message, 'yggtree wt create');
|
|
136
168
|
process.exit(1);
|
|
137
169
|
}
|
|
138
170
|
}
|