yggtree 1.0.1 → 1.1.1
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 +308 -112
- package/dist/commands/wt/bootstrap.js +1 -1
- package/dist/commands/wt/create-branch.js +65 -20
- package/dist/commands/wt/create-multi.js +42 -17
- package/dist/commands/wt/create.js +47 -18
- package/dist/commands/wt/delete.js +31 -15
- 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 +8 -6
- package/dist/commands/wt/path.js +62 -0
- package/dist/commands/wt/prune.js +4 -1
- package/dist/index.js +58 -8
- package/dist/lib/config.js +44 -25
- package/dist/lib/git.js +42 -0
- package/dist/lib/ui.js +14 -1
- package/dist/lib/version.js +17 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -3,7 +3,9 @@
|
|
|
3
3
|
[](https://www.npmjs.com/package/yggtree)
|
|
4
4
|
[](https://www.npmjs.com/package/yggtree)
|
|
5
5
|
|
|
6
|
-
**Yggdrasil Worktree** (invoked as `yggtree`) is
|
|
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.
|
|
7
9
|
|
|
8
10
|
---
|
|
9
11
|
|
|
@@ -11,84 +13,106 @@
|
|
|
11
13
|
|
|
12
14
|
### Installation
|
|
13
15
|
|
|
14
|
-
|
|
16
|
+
Run without installing:
|
|
15
17
|
|
|
16
18
|
```bash
|
|
17
19
|
npx yggtree
|
|
18
20
|
```
|
|
19
21
|
|
|
20
|
-
Or install
|
|
22
|
+
Or install globally:
|
|
21
23
|
|
|
22
24
|
```bash
|
|
23
25
|
npm install -g yggtree
|
|
24
26
|
```
|
|
25
27
|
|
|
26
|
-
### Usage
|
|
28
|
+
### Basic Usage
|
|
27
29
|
|
|
28
|
-
|
|
30
|
+
Run with no arguments to open the interactive menu:
|
|
29
31
|
|
|
30
32
|
```bash
|
|
31
33
|
yggtree
|
|
32
34
|
```
|
|
33
35
|
|
|
34
|
-
Or use
|
|
36
|
+
Or use commands directly:
|
|
35
37
|
|
|
36
38
|
```bash
|
|
37
|
-
yggtree wt create
|
|
38
|
-
yggtree wt list
|
|
39
|
-
yggtree wt
|
|
39
|
+
yggtree wt create
|
|
40
|
+
yggtree wt list
|
|
41
|
+
yggtree wt enter my-feature
|
|
40
42
|
```
|
|
41
43
|
|
|
42
44
|
---
|
|
43
45
|
|
|
44
|
-
##
|
|
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.
|
|
45
55
|
|
|
46
|
-
|
|
47
|
-
The primary way to start working. Instead of worrying about folder names, just tell Yggdrasil which branch you want to work on.
|
|
48
|
-
- **Auto-Slug**: Converts `feat/eng-123-ui` to a clean folder name like `feat-eng-123-ui`.
|
|
49
|
-
- **Auto-Branching**: If the branch doesn't exist, Yggdrasil creates it for you from a base branch.
|
|
50
|
-
- **Remote Awareness**: Seamlessly base your work on `origin/main` or local refs.
|
|
56
|
+
All managed worktrees live under:
|
|
51
57
|
|
|
52
|
-
|
|
53
|
-
|
|
58
|
+
```
|
|
59
|
+
~/.yggtree/<repo-name>/<worktree-slug>
|
|
60
|
+
```
|
|
54
61
|
|
|
55
|
-
|
|
56
|
-
Configure your environment automatically using an `anvil-worktree.json` (also compatible with `.cursor/worktrees.json`) file in your project root.
|
|
62
|
+
This keeps your main repository clean while enabling true parallelism.
|
|
57
63
|
|
|
58
64
|
---
|
|
59
65
|
|
|
60
66
|
## 🤔 Why Yggdrasil Worktree?
|
|
61
67
|
|
|
62
|
-
Git worktrees are
|
|
68
|
+
Git worktrees are powerful, but once you start doing **parallel work**, they become tedious to manage manually.
|
|
63
69
|
|
|
64
|
-
Modern development
|
|
70
|
+
Modern development looks like this:
|
|
65
71
|
|
|
66
|
-
|
|
72
|
+
* Fixing a bug
|
|
73
|
+
* Reviewing a PR
|
|
74
|
+
* Prototyping a feature
|
|
75
|
+
* Letting AI agents explore refactors
|
|
76
|
+
* Running tests in isolation
|
|
67
77
|
|
|
68
|
-
|
|
69
|
-
2. **Fast environment setup per task**
|
|
70
|
-
3. **Agent-friendly isolation for AI workflows**
|
|
78
|
+
All at the same time.
|
|
71
79
|
|
|
72
|
-
|
|
73
|
-
|
|
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.
|
|
74
87
|
|
|
75
88
|
---
|
|
76
89
|
|
|
77
|
-
##
|
|
90
|
+
## ✨ Key Features
|
|
78
91
|
|
|
79
|
-
|
|
92
|
+
🌳 **First-class worktree workflow**
|
|
93
|
+
Create, manage, and navigate Git worktrees as a primary workflow, not an afterthought.
|
|
80
94
|
|
|
81
|
-
|
|
95
|
+
🧠 **Parallel development by default**
|
|
96
|
+
Work on multiple branches at the same time, each in its own isolated environment.
|
|
82
97
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
* Prototyping a feature
|
|
86
|
-
* Letting an AI agent explore refactors
|
|
87
|
-
* Letting another agent generate tests
|
|
98
|
+
🤖 **AI-friendly isolation**
|
|
99
|
+
One worktree per agent, per experiment, per idea. No shared state, no collisions.
|
|
88
100
|
|
|
89
|
-
|
|
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.
|
|
90
109
|
|
|
91
|
-
|
|
110
|
+
🧭 **Interactive or scriptable**
|
|
111
|
+
Use the interactive UI or drive everything through commands and flags.
|
|
112
|
+
|
|
113
|
+
---
|
|
114
|
+
|
|
115
|
+
## 🧠 Parallel Development, Done Right
|
|
92
116
|
|
|
93
117
|
```bash
|
|
94
118
|
yggtree wt create feat/eng-2581-state-selection
|
|
@@ -96,74 +120,184 @@ yggtree wt create fix/eng-2610-validation
|
|
|
96
120
|
yggtree wt create chore/cleanup-api
|
|
97
121
|
```
|
|
98
122
|
|
|
99
|
-
Each command
|
|
123
|
+
Each command creates:
|
|
100
124
|
|
|
101
125
|
* A clean folder
|
|
102
126
|
* A dedicated branch
|
|
103
|
-
* A
|
|
127
|
+
* A bootstrapped environment
|
|
104
128
|
|
|
105
|
-
No context bleeding.
|
|
106
129
|
No stash juggling.
|
|
107
|
-
No
|
|
130
|
+
No branch confusion.
|
|
131
|
+
No shared state accidents.
|
|
108
132
|
|
|
109
133
|
---
|
|
110
134
|
|
|
111
|
-
## 🤖 Built for AI
|
|
112
|
-
|
|
113
|
-
Yggdrasil shines when combined with AI agents.
|
|
135
|
+
## 🤖 Built for AI‑Assisted Workflows
|
|
114
136
|
|
|
115
|
-
|
|
137
|
+
Yggdrasil shines when paired with AI agents.
|
|
116
138
|
|
|
117
|
-
|
|
118
|
-
* Let each agent explore independently
|
|
119
|
-
* Compare results safely
|
|
120
|
-
* Merge only what makes sense
|
|
121
|
-
|
|
122
|
-
Example workflow:
|
|
139
|
+
Instead of running agents against the same directory, you can assign **one worktree per agent**.
|
|
123
140
|
|
|
124
141
|
```bash
|
|
125
|
-
|
|
126
|
-
yggtree wt create feat/ai-refactor-
|
|
127
|
-
yggtree wt create feat/ai-refactor-b
|
|
142
|
+
yggtree wt create feat/ai-refactor-a --exec "cursor ."
|
|
143
|
+
yggtree wt create feat/ai-refactor-b --exec "codex"
|
|
128
144
|
```
|
|
129
145
|
|
|
130
|
-
Each agent operates in its own realm
|
|
131
|
-
|
|
132
|
-
This enables patterns like:
|
|
146
|
+
Each agent operates in its own realm:
|
|
133
147
|
|
|
134
148
|
* Model A refactors architecture
|
|
135
149
|
* Model B focuses on tests
|
|
136
|
-
* Model C explores performance
|
|
150
|
+
* Model C explores performance
|
|
137
151
|
|
|
138
|
-
All in parallel.
|
|
139
|
-
All reviewable.
|
|
140
|
-
All under your control.
|
|
152
|
+
All in parallel. All reviewable. All isolated.
|
|
141
153
|
|
|
142
154
|
---
|
|
143
155
|
|
|
144
|
-
## ⚡
|
|
156
|
+
## ⚡ Bootstrapping & Configuration
|
|
157
|
+
|
|
158
|
+
Yggdrasil automatically prepares each worktree.
|
|
145
159
|
|
|
146
|
-
|
|
160
|
+
Resolution order:
|
|
147
161
|
|
|
148
|
-
|
|
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
|
|
166
|
+
|
|
167
|
+
### Example configuration
|
|
149
168
|
|
|
150
169
|
```json
|
|
151
170
|
{
|
|
152
171
|
"setup-worktree": [
|
|
153
172
|
"npm install",
|
|
154
173
|
"git submodule sync --recursive",
|
|
155
|
-
"git submodule update --init --recursive"
|
|
174
|
+
"git submodule update --init --recursive",
|
|
175
|
+
"echo \"🌳 Realm ready\""
|
|
156
176
|
]
|
|
157
177
|
}
|
|
158
178
|
```
|
|
159
179
|
|
|
160
|
-
|
|
180
|
+
---
|
|
181
|
+
|
|
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.
|
|
161
231
|
|
|
162
|
-
|
|
163
|
-
* No forgotten submodules
|
|
164
|
-
* No “works on main but not here” surprises
|
|
232
|
+
Columns:
|
|
165
233
|
|
|
166
|
-
|
|
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.
|
|
167
301
|
|
|
168
302
|
---
|
|
169
303
|
|
|
@@ -172,74 +306,136 @@ You enter a worktree and it’s ready to work.
|
|
|
172
306
|
Yggdrasil is ideal when:
|
|
173
307
|
|
|
174
308
|
* You work on multiple tasks in parallel
|
|
175
|
-
* You use AI agents for exploration
|
|
309
|
+
* You use AI agents for exploration
|
|
176
310
|
* You want isolation without duplication
|
|
177
|
-
* You value
|
|
178
|
-
*
|
|
311
|
+
* You value scripted, repeatable setups
|
|
312
|
+
* `git checkout` no longer scales
|
|
179
313
|
|
|
180
|
-
|
|
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:**
|
|
181
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>
|
|
182
335
|
---
|
|
183
336
|
|
|
184
|
-
|
|
337
|
+
<details>
|
|
338
|
+
<summary>Create a worktree without bootstrap and without entering</summary>
|
|
185
339
|
|
|
186
|
-
|
|
187
|
-
|
|
340
|
+
**Command:**
|
|
341
|
+
|
|
342
|
+
```
|
|
343
|
+
yggtree wt create feat/cleanup-api --no-bootstrap --no-enter
|
|
344
|
+
```
|
|
188
345
|
|
|
189
|
-
|
|
346
|
+
**When to use:**
|
|
190
347
|
|
|
348
|
+
* You just want the folder ready
|
|
349
|
+
* You’ll enter it later
|
|
350
|
+
* You don’t want installs running automatically
|
|
191
351
|
|
|
352
|
+
</details>
|
|
192
353
|
---
|
|
193
354
|
|
|
194
|
-
|
|
355
|
+
<details>
|
|
356
|
+
<summary>Create a worktree and open it in your IDE</summary>
|
|
195
357
|
|
|
196
|
-
|
|
358
|
+
**Command:**
|
|
197
359
|
|
|
198
|
-
```
|
|
199
|
-
|
|
200
|
-
"setup-worktree": [
|
|
201
|
-
"npm install",
|
|
202
|
-
"git submodule sync --recursive",
|
|
203
|
-
"git submodule update --init --recursive",
|
|
204
|
-
"npm run build",
|
|
205
|
-
"echo '🌳 The realm is ready!'"
|
|
206
|
-
]
|
|
207
|
-
}
|
|
360
|
+
```
|
|
361
|
+
yggtree wt create feat/ui-refactor --exec "cursor ."
|
|
208
362
|
```
|
|
209
363
|
|
|
364
|
+
Works with:
|
|
365
|
+
|
|
366
|
+
* `cursor .`
|
|
367
|
+
* `code .`
|
|
368
|
+
* `codex`
|
|
369
|
+
* Any custom command available in your shell
|
|
370
|
+
|
|
371
|
+
</details>
|
|
210
372
|
---
|
|
211
373
|
|
|
212
|
-
|
|
374
|
+
<details>
|
|
375
|
+
<summary>Execute a command inside an existing worktree (no shell)</summary>
|
|
376
|
+
|
|
377
|
+
**Command:**
|
|
213
378
|
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
| `yggtree wt create` | Create a worktree by branch name (Recommended). |
|
|
218
|
-
| `yggtree wt create-multi` | Create multiple worktrees in a single command. |
|
|
219
|
-
| `yggtree wt create-slug` | Manually specify both folder name and branch ref. |
|
|
220
|
-
| `yggtree wt list` | List all managed worktrees and their status. |
|
|
221
|
-
| `yggtree wt delete` | Interactively select and remove a worktree. |
|
|
222
|
-
| `yggtree wt bootstrap` | Re-run the setup commands for an existing worktree. |
|
|
223
|
-
| `yggtree wt prune` | Clean up Git's internal data for worktrees. |
|
|
379
|
+
```
|
|
380
|
+
yggtree wt exec test -- npm test
|
|
381
|
+
```
|
|
224
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>
|
|
225
390
|
---
|
|
226
391
|
|
|
227
|
-
|
|
392
|
+
<details>
|
|
393
|
+
<summary>Enter a worktree and run a command before entering</summary>
|
|
228
394
|
|
|
229
|
-
|
|
395
|
+
**Command:**
|
|
230
396
|
|
|
231
|
-
```
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
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
|
+
---
|
|
235
409
|
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
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:**
|
|
239
420
|
|
|
240
|
-
# Link the CLI locally
|
|
241
|
-
npm link
|
|
242
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.
|
|
435
|
+
|
|
436
|
+
Yggdrasil helps you grow many worlds and decide later which ones deserve to merge.
|
|
437
|
+
|
|
438
|
+
---
|
|
243
439
|
|
|
244
440
|
## 📄 License
|
|
245
441
|
|
|
@@ -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, getRepoName, verifyRef, fetchAll, getCurrentBranch } from '../../lib/git.js';
|
|
4
|
+
import { getRepoRoot, getRepoName, verifyRef, fetchAll, getCurrentBranch, ensureCorrectUpstream } 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,33 +41,40 @@ 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, '-');
|
|
@@ -103,18 +110,56 @@ export async function createCommandNew(options) {
|
|
|
103
110
|
else {
|
|
104
111
|
await execa('git', ['worktree', 'add', '-b', branchName, wtPath, baseRef]);
|
|
105
112
|
}
|
|
106
|
-
spinner.succeed('Worktree created.');
|
|
107
113
|
}
|
|
108
114
|
catch (e) {
|
|
109
115
|
spinner.fail('Failed to create worktree.');
|
|
110
|
-
|
|
116
|
+
const cmd = targetBranchExists
|
|
117
|
+
? `git worktree add ${wtPath} ${branchName}`
|
|
118
|
+
: `git worktree add -b ${branchName} ${wtPath} ${baseRef}`;
|
|
119
|
+
log.actionableError(e.message, cmd, wtPath, [
|
|
120
|
+
'Check if the folder already exists: ls ' + wtPath,
|
|
121
|
+
'Check if the branch is already used: git worktree list',
|
|
122
|
+
'Try pruning stale worktrees: yggtree wt prune',
|
|
123
|
+
`Run manually: ${cmd}`
|
|
124
|
+
]);
|
|
111
125
|
return;
|
|
112
126
|
}
|
|
113
|
-
|
|
127
|
+
try {
|
|
128
|
+
// Strong Safety Mode: Ensure upstream is origin/<branchName> and publish
|
|
129
|
+
spinner.text = 'Safely publishing branch...';
|
|
130
|
+
await ensureCorrectUpstream(wtPath, branchName);
|
|
131
|
+
spinner.succeed('Worktree created and branch published.');
|
|
132
|
+
}
|
|
133
|
+
catch (e) {
|
|
134
|
+
spinner.fail('Worktree created, but branch publication failed.');
|
|
135
|
+
log.actionableError(e.message, 'git push -u origin HEAD', wtPath, [
|
|
136
|
+
`cd ${wtPath}`,
|
|
137
|
+
'Attempt to push manually: git push -u origin HEAD',
|
|
138
|
+
'Check if the remote branch already exists or if you have push permissions'
|
|
139
|
+
]);
|
|
140
|
+
// We don't return here because the worktree IS created, we just failed to publish
|
|
141
|
+
}
|
|
114
142
|
if (shouldBootstrap) {
|
|
115
143
|
await runBootstrap(wtPath, repoRoot);
|
|
116
144
|
}
|
|
117
|
-
// 5.
|
|
145
|
+
// 5. Exec Command
|
|
146
|
+
if (execCommandStr && execCommandStr.trim()) {
|
|
147
|
+
log.info(`Executing: ${execCommandStr} in ${ui.path(wtPath)}`);
|
|
148
|
+
try {
|
|
149
|
+
await execa(execCommandStr, {
|
|
150
|
+
cwd: wtPath,
|
|
151
|
+
stdio: 'inherit',
|
|
152
|
+
shell: true
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
catch (error) {
|
|
156
|
+
log.actionableError(error.message, execCommandStr, wtPath, [
|
|
157
|
+
`cd ${wtPath} && ${execCommandStr}`,
|
|
158
|
+
'Check your command syntax and environment variables'
|
|
159
|
+
]);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
// 6. Final Output
|
|
118
163
|
log.success('Worktree ready!');
|
|
119
164
|
if (shouldEnter) {
|
|
120
165
|
log.info(`Spawning sub-shell in ${ui.path(wtPath)}...`);
|
|
@@ -133,7 +178,7 @@ export async function createCommandNew(options) {
|
|
|
133
178
|
}
|
|
134
179
|
}
|
|
135
180
|
catch (error) {
|
|
136
|
-
log.
|
|
181
|
+
log.actionableError(error.message, 'yggtree wt create');
|
|
137
182
|
process.exit(1);
|
|
138
183
|
}
|
|
139
184
|
}
|