@junheep/gwt 0.2.1 → 0.3.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 +24 -11
- package/bin/gwt.mjs +139 -40
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -82,7 +82,6 @@ Repositories without a remote use their canonical path.
|
|
|
82
82
|
{
|
|
83
83
|
"projects": {
|
|
84
84
|
"github.com/owner/repository": {
|
|
85
|
-
"worktreeDirectory": ".worktrees",
|
|
86
85
|
"copyFiles": [
|
|
87
86
|
"apps/server/.env",
|
|
88
87
|
"apps/web/.env"
|
|
@@ -114,7 +113,6 @@ The project file contains the configuration fields directly:
|
|
|
114
113
|
```json
|
|
115
114
|
{
|
|
116
115
|
"base": "origin/main",
|
|
117
|
-
"worktreeDirectory": ".worktrees",
|
|
118
116
|
"copyFiles": [
|
|
119
117
|
"apps/server/.env",
|
|
120
118
|
"apps/web/.env"
|
|
@@ -133,12 +131,16 @@ The two files are not merged. Run `gwt config show` to see whether user and
|
|
|
133
131
|
repository configuration is available, the location of each existing config
|
|
134
132
|
file, the active source, and its resolved value.
|
|
135
133
|
|
|
136
|
-
All fields are optional. Without either config, worktrees are created
|
|
137
|
-
|
|
138
|
-
|
|
134
|
+
All fields are optional. Without either config, worktrees are created outside
|
|
135
|
+
the repository beneath `~/.gwt/worktrees`. Set `GWT_HOME` to an absolute path
|
|
136
|
+
to use a different gwt home directory. A repository normally uses a directory
|
|
137
|
+
named after it. If another repository already uses that name, gwt adds a short
|
|
138
|
+
hash derived from the canonical path. Worktrees use the primary worktree's
|
|
139
|
+
current commit as their base and run no setup actions.
|
|
139
140
|
|
|
140
141
|
- `base`: Git revision used when `--base` is omitted.
|
|
141
|
-
- `worktreeDirectory`:
|
|
142
|
+
- `worktreeDirectory`: Optional repository-relative directory for managed
|
|
143
|
+
worktrees. Setting it opts out of the external default.
|
|
142
144
|
- `copyFiles`: Ignored local files copied from the primary worktree without
|
|
143
145
|
overwriting an existing destination.
|
|
144
146
|
- `ports`: Environment variable names assigned stable ports in the range
|
|
@@ -146,8 +148,17 @@ no setup actions.
|
|
|
146
148
|
- `postCreate`: Executable run after files and ports are prepared.
|
|
147
149
|
- `preRemove`: Executable run before removal.
|
|
148
150
|
|
|
149
|
-
|
|
150
|
-
are not modified.
|
|
151
|
+
An explicitly configured repository-relative worktree directory is added to
|
|
152
|
+
`.git/info/exclude`; tracked project files are not modified.
|
|
153
|
+
|
|
154
|
+
For example, an existing configuration can retain the previous in-repository
|
|
155
|
+
layout explicitly:
|
|
156
|
+
|
|
157
|
+
```json
|
|
158
|
+
{
|
|
159
|
+
"worktreeDirectory": ".worktrees"
|
|
160
|
+
}
|
|
161
|
+
```
|
|
151
162
|
|
|
152
163
|
## Hooks
|
|
153
164
|
|
|
@@ -192,8 +203,8 @@ Approval is invalidated when `.gwt.json` or either hook changes.
|
|
|
192
203
|
gwt new [branch] [--base <ref>] [--no-hooks]
|
|
193
204
|
gwt setup [id|branch|path] [--no-hooks]
|
|
194
205
|
gwt list
|
|
195
|
-
gwt switch [id|branch|path]
|
|
196
|
-
gwt info [id|branch|path]
|
|
206
|
+
gwt switch [primary|id|branch|path]
|
|
207
|
+
gwt info [primary|id|branch|path]
|
|
197
208
|
gwt remove [id|branch|path] [--keep-branch|--discard] [--yes] [--no-hooks]
|
|
198
209
|
gwt trust [--revoke]
|
|
199
210
|
gwt config create [--project]
|
|
@@ -216,7 +227,9 @@ Setup failures retain the worktree and record the failure. Retry with
|
|
|
216
227
|
Run `gwt switch` without a target to open the interactive picker. Use the
|
|
217
228
|
arrow keys, `j`/`k`, or Ctrl-n/Ctrl-p to move; press `/` to filter by branch,
|
|
218
229
|
ID, or path. Enter switches to the selected worktree. Escape leaves filter
|
|
219
|
-
mode or cancels the picker.
|
|
230
|
+
mode or cancels the picker. `primary` is a reserved ID for the repository's
|
|
231
|
+
primary worktree, so `gwt switch primary` returns to it from any linked
|
|
232
|
+
worktree.
|
|
220
233
|
|
|
221
234
|
`gwt remove` refuses dirty worktrees and first tries to delete the branch with
|
|
222
235
|
`git branch -d`. If Git rejects safe deletion, an interactive terminal asks
|
package/bin/gwt.mjs
CHANGED
|
@@ -19,7 +19,7 @@ import {
|
|
|
19
19
|
} from "node:fs"
|
|
20
20
|
import { createServer } from "node:net"
|
|
21
21
|
import { homedir } from "node:os"
|
|
22
|
-
import { dirname, isAbsolute, join, relative, resolve, sep } from "node:path"
|
|
22
|
+
import { basename, dirname, isAbsolute, join, relative, resolve, sep } from "node:path"
|
|
23
23
|
import { emitKeypressEvents } from "node:readline"
|
|
24
24
|
import { createInterface } from "node:readline/promises"
|
|
25
25
|
|
|
@@ -28,7 +28,7 @@ const PORT_MIN = 20_000
|
|
|
28
28
|
const PORT_MAX = 39_999
|
|
29
29
|
const PICKER_ESCAPE_CODE_TIMEOUT_MS = 50
|
|
30
30
|
const ENV_NAME = /^[A-Za-z_][A-Za-z0-9_]*$/
|
|
31
|
-
const DEFAULT_CONFIG = {
|
|
31
|
+
const DEFAULT_CONFIG = { copyFiles: [], ports: [] }
|
|
32
32
|
const SKILL_DIRECTORIES = { claude: ".claude", codex: ".agents" }
|
|
33
33
|
const SKILL_USAGE = `Usage: gwt skill install <${Object.keys(SKILL_DIRECTORIES).join("|")}> [--project] [--dry-run] [--yes]`
|
|
34
34
|
|
|
@@ -136,9 +136,12 @@ function validateConfig(parsed, label) {
|
|
|
136
136
|
throw new CliError("base must be a non-empty string")
|
|
137
137
|
}
|
|
138
138
|
|
|
139
|
-
|
|
140
|
-
if (worktreeDirectory
|
|
141
|
-
|
|
139
|
+
let worktreeDirectory
|
|
140
|
+
if (parsed.worktreeDirectory !== undefined) {
|
|
141
|
+
worktreeDirectory = validateRelativePath(parsed.worktreeDirectory, "worktreeDirectory")
|
|
142
|
+
if (worktreeDirectory.split(/[\\/]+/).some((part) => !/^[A-Za-z0-9._-]+$/.test(part))) {
|
|
143
|
+
throw new CliError("worktreeDirectory can only contain letters, digits, '.', '_', '-', and path separators")
|
|
144
|
+
}
|
|
142
145
|
}
|
|
143
146
|
if (!Array.isArray(parsed.copyFiles ?? [])) throw new CliError("copyFiles must be an array")
|
|
144
147
|
const copyFiles = (parsed.copyFiles ?? []).map((path, index) => validateRelativePath(path, `copyFiles[${index}]`))
|
|
@@ -156,18 +159,61 @@ function validateConfig(parsed, label) {
|
|
|
156
159
|
if (parsed[hook] !== undefined) validateRelativePath(parsed[hook], hook)
|
|
157
160
|
}
|
|
158
161
|
|
|
159
|
-
|
|
162
|
+
const config = {
|
|
160
163
|
...parsed,
|
|
161
|
-
worktreeDirectory,
|
|
162
164
|
copyFiles,
|
|
163
165
|
ports,
|
|
164
166
|
}
|
|
167
|
+
if (worktreeDirectory !== undefined) config.worktreeDirectory = worktreeDirectory
|
|
168
|
+
return config
|
|
165
169
|
}
|
|
166
170
|
|
|
167
171
|
function configHome() {
|
|
168
172
|
return process.env.XDG_CONFIG_HOME || join(homedir(), ".config")
|
|
169
173
|
}
|
|
170
174
|
|
|
175
|
+
function gwtHome() {
|
|
176
|
+
const path = process.env.GWT_HOME || join(homedir(), ".gwt")
|
|
177
|
+
if (!isAbsolute(path)) throw new CliError("GWT_HOME must be an absolute path")
|
|
178
|
+
return path
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function repositoryUsesDirectory(repository, directory) {
|
|
182
|
+
const resolvedDirectory = pathExists(directory) ? canonical(directory) : resolve(directory)
|
|
183
|
+
return repository.worktrees.some((worktree) => {
|
|
184
|
+
if (!pathExists(worktree.path)) return false
|
|
185
|
+
const worktreePath = canonical(worktree.path)
|
|
186
|
+
return worktreePath !== repository.primaryPath && dirname(worktreePath) === resolvedDirectory
|
|
187
|
+
})
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function directoryIsEmpty(path) {
|
|
191
|
+
try {
|
|
192
|
+
return statSync(path).isDirectory() && readdirSync(path).length === 0
|
|
193
|
+
} catch {
|
|
194
|
+
return false
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
function defaultWorktreeDirectory(repository) {
|
|
199
|
+
const root = join(gwtHome(), "worktrees")
|
|
200
|
+
const name = basename(repository.primaryPath)
|
|
201
|
+
const namedDirectory = join(root, name)
|
|
202
|
+
const digest = createHash("sha256").update(repository.primaryPath).digest("hex").slice(0, 8)
|
|
203
|
+
const disambiguatedDirectory = join(root, `${name}-${digest}`)
|
|
204
|
+
|
|
205
|
+
if (repositoryUsesDirectory(repository, disambiguatedDirectory)) return disambiguatedDirectory
|
|
206
|
+
if (repositoryUsesDirectory(repository, namedDirectory)) return namedDirectory
|
|
207
|
+
if (!pathExists(namedDirectory) || directoryIsEmpty(namedDirectory)) return namedDirectory
|
|
208
|
+
return disambiguatedDirectory
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
function resolveWorktreeDirectory(repository, config) {
|
|
212
|
+
return config.worktreeDirectory
|
|
213
|
+
? resolve(repository.primaryPath, config.worktreeDirectory)
|
|
214
|
+
: defaultWorktreeDirectory(repository)
|
|
215
|
+
}
|
|
216
|
+
|
|
171
217
|
function userConfigPath() {
|
|
172
218
|
return join(configHome(), "gwt", "config.json")
|
|
173
219
|
}
|
|
@@ -311,6 +357,8 @@ function resolveWorktree(repository, selector, options = {}) {
|
|
|
311
357
|
return current
|
|
312
358
|
}
|
|
313
359
|
|
|
360
|
+
if (selector === "primary") return repository.primary
|
|
361
|
+
|
|
314
362
|
const metadata = loadMetadata(repository)
|
|
315
363
|
const idMatch = metadata.find((item) => item.id === selector)
|
|
316
364
|
if (idMatch) {
|
|
@@ -330,9 +378,10 @@ function resolveWorktree(repository, selector, options = {}) {
|
|
|
330
378
|
}
|
|
331
379
|
|
|
332
380
|
function generateId(repository, config) {
|
|
381
|
+
const directory = resolveWorktreeDirectory(repository, config)
|
|
333
382
|
for (let attempt = 0; attempt < 100; attempt += 1) {
|
|
334
383
|
const id = randomBytes(4).toString("hex")
|
|
335
|
-
const target = join(
|
|
384
|
+
const target = join(directory, id)
|
|
336
385
|
if (!existsSync(metadataPath(repository, id)) && !pathExists(target)) return id
|
|
337
386
|
}
|
|
338
387
|
throw new CliError("Could not generate a unique worktree ID")
|
|
@@ -632,24 +681,27 @@ async function commandNew(args) {
|
|
|
632
681
|
const base = requestedBase
|
|
633
682
|
? gitOutput(["rev-parse", "--verify", `${requestedBase}^{commit}`], repository.primaryPath)
|
|
634
683
|
: gitOutput(["rev-parse", "HEAD"], repository.primaryPath)
|
|
635
|
-
const target = join(repository
|
|
636
|
-
|
|
684
|
+
const target = join(resolveWorktreeDirectory(repository, configDocument.value), id)
|
|
685
|
+
if (configDocument.value.worktreeDirectory) {
|
|
686
|
+
ensureLocalExclude(repository, configDocument.value.worktreeDirectory)
|
|
687
|
+
}
|
|
637
688
|
|
|
638
689
|
git(["worktree", "add", "-b", branch, target, base], repository.primaryPath, { stdio: "inherit" })
|
|
690
|
+
const targetPath = canonical(target)
|
|
639
691
|
const refreshed = discoverRepository(repository.primaryPath)
|
|
640
|
-
const worktree = refreshed.worktrees.find((item) => resolve(item.path) ===
|
|
692
|
+
const worktree = refreshed.worktrees.find((item) => resolve(item.path) === targetPath)
|
|
641
693
|
|
|
642
694
|
try {
|
|
643
695
|
const metadata = await setupWorktree(refreshed, configDocument, worktree, {
|
|
644
696
|
id,
|
|
645
697
|
noHooks: options["no-hooks"],
|
|
646
698
|
})
|
|
647
|
-
console.log(`Worktree ${metadata.id} is ready at ${
|
|
699
|
+
console.log(`Worktree ${metadata.id} is ready at ${targetPath}`)
|
|
648
700
|
console.log(`Branch: ${branch}`)
|
|
649
701
|
for (const [name, port] of Object.entries(metadata.ports)) console.log(`${name}: ${port}`)
|
|
650
|
-
writeCdDirective(
|
|
702
|
+
writeCdDirective(targetPath)
|
|
651
703
|
} catch (error) {
|
|
652
|
-
console.error(`Setup failed; worktree retained at ${
|
|
704
|
+
console.error(`Setup failed; worktree retained at ${targetPath}`)
|
|
653
705
|
console.error(`Retry: gwt setup ${id}`)
|
|
654
706
|
console.error(`Remove: gwt remove ${id}`)
|
|
655
707
|
throw error
|
|
@@ -711,22 +763,19 @@ async function chooseWorktree(repository) {
|
|
|
711
763
|
|
|
712
764
|
const terminalWidth = Math.max(48, process.stdout.columns ?? 100)
|
|
713
765
|
const idWidth = 8
|
|
714
|
-
const setupWidth = Math.max(5, ...filtered.map((choice) => choice.setup
|
|
715
|
-
const longestBranch = Math.max(12, ...filtered.map((choice) => choice.branch
|
|
766
|
+
const setupWidth = Math.max(5, ...filtered.map((choice) => displayWidth(choice.setup)))
|
|
767
|
+
const longestBranch = Math.max(12, ...filtered.map((choice) => displayWidth(choice.branch)))
|
|
716
768
|
const flexibleWidth = terminalWidth - idWidth - setupWidth - 10
|
|
717
769
|
const branchWidth = Math.min(32, longestBranch, Math.max(8, flexibleWidth - 8))
|
|
718
770
|
const pathWidth = Math.max(8, flexibleWidth - branchWidth)
|
|
719
|
-
const fit = (value, width) => value.length > width
|
|
720
|
-
? `${value.slice(0, Math.max(0, width - 1))}…`
|
|
721
|
-
: value.padEnd(width)
|
|
722
771
|
const visibleCount = Math.max(3, (process.stdout.rows ?? 24) - 5)
|
|
723
772
|
const start = Math.max(0, Math.min(selected - Math.floor(visibleCount / 2), filtered.length - visibleCount))
|
|
724
773
|
const visible = filtered.slice(start, start + visibleCount)
|
|
725
774
|
const escapeAction = filtering ? "clear" : "cancel"
|
|
726
775
|
const lines = [
|
|
727
|
-
`${colors.dim}${
|
|
728
|
-
...(filtering ? [
|
|
729
|
-
`${colors.dim} ${
|
|
776
|
+
`${colors.dim}${fitDisplay(`Switch worktree Esc ${escapeAction} · Enter select · ↑↓/jk/C-n/C-p · / filter`, terminalWidth)}${colors.reset}`,
|
|
777
|
+
...(filtering ? [fitDisplay(`Filter: /${query}`, terminalWidth)] : []),
|
|
778
|
+
`${colors.dim} ${fitDisplay("BRANCH", branchWidth)} ${fitDisplay("ID", idWidth)} ${fitDisplay("SETUP", setupWidth)} ${fitDisplay("PATH", pathWidth)}${colors.reset}`,
|
|
730
779
|
]
|
|
731
780
|
|
|
732
781
|
if (visible.length === 0) {
|
|
@@ -736,7 +785,7 @@ async function chooseWorktree(repository) {
|
|
|
736
785
|
const index = start + visibleIndex
|
|
737
786
|
const selection = index === selected ? `${colors.cyan}>${colors.reset}` : " "
|
|
738
787
|
const currentMarker = choice.current ? `${colors.yellow}*${colors.reset}` : " "
|
|
739
|
-
lines.push(`${selection} ${currentMarker} ${
|
|
788
|
+
lines.push(`${selection} ${currentMarker} ${fitDisplay(choice.branch, branchWidth)} ${fitDisplay(choice.id, idWidth)} ${fitDisplay(choice.setup, setupWidth)} ${fitDisplay(choice.path, pathWidth)}`)
|
|
740
789
|
})
|
|
741
790
|
}
|
|
742
791
|
|
|
@@ -809,7 +858,7 @@ async function chooseWorktree(repository) {
|
|
|
809
858
|
}
|
|
810
859
|
|
|
811
860
|
async function commandSwitch(args) {
|
|
812
|
-
if (args.length > 1) throw new CliError("Usage: gwt switch [id|branch|path]")
|
|
861
|
+
if (args.length > 1) throw new CliError("Usage: gwt switch [primary|id|branch|path]")
|
|
813
862
|
const repository = discoverRepository()
|
|
814
863
|
const worktree = args[0] ? resolveWorktree(repository, args[0]) : await chooseWorktree(repository)
|
|
815
864
|
writeCdDirective(canonical(worktree.path))
|
|
@@ -854,6 +903,38 @@ function padDisplay(value, width) {
|
|
|
854
903
|
return `${value}${" ".repeat(Math.max(0, width - displayWidth(value)))}`
|
|
855
904
|
}
|
|
856
905
|
|
|
906
|
+
function fitDisplay(value, width) {
|
|
907
|
+
const normalized = value.normalize("NFC")
|
|
908
|
+
if (displayWidth(normalized) <= width) return padDisplay(normalized, width)
|
|
909
|
+
|
|
910
|
+
const contentWidth = width - displayWidth("…")
|
|
911
|
+
let fitted = ""
|
|
912
|
+
let fittedWidth = 0
|
|
913
|
+
for (const character of normalized) {
|
|
914
|
+
const characterWidth = displayWidth(character)
|
|
915
|
+
if (fittedWidth + characterWidth > contentWidth) break
|
|
916
|
+
fitted += character
|
|
917
|
+
fittedWidth += characterWidth
|
|
918
|
+
}
|
|
919
|
+
return padDisplay(`${fitted}…`, width)
|
|
920
|
+
}
|
|
921
|
+
|
|
922
|
+
function truncateDisplayTail(value, width) {
|
|
923
|
+
const normalized = value.normalize("NFC")
|
|
924
|
+
if (displayWidth(normalized) <= width) return normalized
|
|
925
|
+
|
|
926
|
+
const contentWidth = width - displayWidth("…")
|
|
927
|
+
let fitted = ""
|
|
928
|
+
let fittedWidth = 0
|
|
929
|
+
for (const character of [...normalized].reverse()) {
|
|
930
|
+
const characterWidth = displayWidth(character)
|
|
931
|
+
if (fittedWidth + characterWidth > contentWidth) break
|
|
932
|
+
fitted = `${character}${fitted}`
|
|
933
|
+
fittedWidth += characterWidth
|
|
934
|
+
}
|
|
935
|
+
return `…${fitted}`
|
|
936
|
+
}
|
|
937
|
+
|
|
857
938
|
function commandList(args) {
|
|
858
939
|
if (args.length > 0) throw new CliError("Usage: gwt list")
|
|
859
940
|
const repository = discoverRepository()
|
|
@@ -864,15 +945,25 @@ function commandList(args) {
|
|
|
864
945
|
id: Math.max(2, ...rows.map((row) => displayWidth(row.id))),
|
|
865
946
|
setup: Math.max(5, ...rows.map((row) => displayWidth(row.setup))),
|
|
866
947
|
}
|
|
867
|
-
|
|
948
|
+
let pathWidth = null
|
|
949
|
+
if (process.stdout.isTTY) {
|
|
950
|
+
const minimumWidth = 8 + 6 + widths.id + widths.setup + 4
|
|
951
|
+
const terminalWidth = Math.max(minimumWidth, process.stdout.columns || 100)
|
|
952
|
+
const flexibleWidth = terminalWidth - widths.id - widths.setup - 8
|
|
953
|
+
widths.branch = Math.min(32, widths.branch, Math.max(6, flexibleWidth - 12))
|
|
954
|
+
pathWidth = flexibleWidth - widths.branch
|
|
955
|
+
}
|
|
956
|
+
|
|
957
|
+
console.log(`${colors.dim} ${fitDisplay("BRANCH", widths.branch)} ${fitDisplay("ID", widths.id)} ${fitDisplay("SETUP", widths.setup)} PATH${colors.reset}`)
|
|
868
958
|
for (const row of rows) {
|
|
869
959
|
const currentMarker = row.current ? `${colors.yellow}*${colors.reset}` : " "
|
|
870
|
-
|
|
960
|
+
const path = pathWidth === null ? row.path : truncateDisplayTail(row.path, pathWidth)
|
|
961
|
+
console.log(`${currentMarker} ${fitDisplay(row.branch, widths.branch)} ${fitDisplay(row.id, widths.id)} ${fitDisplay(row.setup, widths.setup)} ${path}`)
|
|
871
962
|
}
|
|
872
963
|
}
|
|
873
964
|
|
|
874
965
|
function commandInfo(args) {
|
|
875
|
-
if (args.length > 1) throw new CliError("Usage: gwt info [id|branch|path]")
|
|
966
|
+
if (args.length > 1) throw new CliError("Usage: gwt info [primary|id|branch|path]")
|
|
876
967
|
const repository = discoverRepository()
|
|
877
968
|
const worktree = resolveWorktree(repository, args[0])
|
|
878
969
|
const metadata = metadataForWorktree(repository, worktree)
|
|
@@ -985,7 +1076,7 @@ function commandTrust(args) {
|
|
|
985
1076
|
}
|
|
986
1077
|
|
|
987
1078
|
function configScaffold() {
|
|
988
|
-
return {
|
|
1079
|
+
return { copyFiles: [], ports: [] }
|
|
989
1080
|
}
|
|
990
1081
|
|
|
991
1082
|
function commandConfigCreate(args) {
|
|
@@ -1040,6 +1131,7 @@ function commandConfigShow(args) {
|
|
|
1040
1131
|
console.log(`Repository config: ${repositoryConfigured ? "configured" : "not created"}`)
|
|
1041
1132
|
if (repositoryConfigured) console.log(` File: ${projectPath}`)
|
|
1042
1133
|
console.log(`Active config: ${activeLabel}`)
|
|
1134
|
+
console.log(`Worktree directory: ${resolveWorktreeDirectory(repository, active.value)}`)
|
|
1043
1135
|
console.log(JSON.stringify(active.value, null, 2))
|
|
1044
1136
|
}
|
|
1045
1137
|
|
|
@@ -1293,7 +1385,7 @@ function commandComplete(args) {
|
|
|
1293
1385
|
|
|
1294
1386
|
if (args[0] === "worktrees") {
|
|
1295
1387
|
const repository = discoverRepository()
|
|
1296
|
-
const values = []
|
|
1388
|
+
const values = ["primary"]
|
|
1297
1389
|
for (const worktree of repository.worktrees) {
|
|
1298
1390
|
const metadata = metadataForWorktree(repository, worktree)
|
|
1299
1391
|
if (metadata?.id) values.push(metadata.id)
|
|
@@ -1372,10 +1464,13 @@ Options:
|
|
|
1372
1464
|
|
|
1373
1465
|
Behavior:
|
|
1374
1466
|
The worktree receives an immutable 8-character ID. gwt creates it below the
|
|
1375
|
-
|
|
1467
|
+
resolved worktree directory, copies configured local files, assigns stable
|
|
1376
1468
|
ports, and runs postCreate. A setup failure keeps the worktree so setup can
|
|
1377
1469
|
be retried. With shell integration installed, the current shell moves into
|
|
1378
|
-
the new worktree after setup succeeds.
|
|
1470
|
+
the new worktree after setup succeeds. Without worktreeDirectory, the default
|
|
1471
|
+
is $GWT_HOME/worktrees or ~/.gwt/worktrees when GWT_HOME is not set. A short
|
|
1472
|
+
repository hash is added to the directory name only when needed to avoid a
|
|
1473
|
+
name collision.
|
|
1379
1474
|
|
|
1380
1475
|
Examples:
|
|
1381
1476
|
gwt new feature/auth
|
|
@@ -1418,36 +1513,40 @@ Example:
|
|
|
1418
1513
|
switch: `Switch the current shell to another worktree.
|
|
1419
1514
|
|
|
1420
1515
|
Usage:
|
|
1421
|
-
gwt switch [id|branch|path]
|
|
1516
|
+
gwt switch [primary|id|branch|path]
|
|
1422
1517
|
|
|
1423
1518
|
Arguments:
|
|
1424
|
-
|
|
1519
|
+
selector Worktree to switch to. Opens the picker when omitted.
|
|
1425
1520
|
|
|
1426
1521
|
Options:
|
|
1427
1522
|
-h, --help Show help for this command.
|
|
1428
1523
|
|
|
1429
1524
|
Behavior:
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1525
|
+
'primary' is the reserved ID for the primary worktree. Other worktrees can be
|
|
1526
|
+
selected by ID, exact branch name, or path. The picker supports arrow keys,
|
|
1527
|
+
j/k, Ctrl-n/Ctrl-p, and '/' filtering. Shell integration must be installed for
|
|
1528
|
+
gwt to change the parent shell's directory; otherwise the path is only printed.
|
|
1433
1529
|
|
|
1434
1530
|
Examples:
|
|
1435
1531
|
gwt switch
|
|
1532
|
+
gwt switch primary
|
|
1436
1533
|
gwt switch feature/auth
|
|
1437
1534
|
gwt switch a1b2c3d4`,
|
|
1438
1535
|
info: `Show a worktree's identity, Git state, setup status, and assigned ports.
|
|
1439
1536
|
|
|
1440
1537
|
Usage:
|
|
1441
|
-
gwt info [id|branch|path]
|
|
1538
|
+
gwt info [primary|id|branch|path]
|
|
1442
1539
|
|
|
1443
1540
|
Arguments:
|
|
1444
|
-
|
|
1541
|
+
selector 'primary', an ID, an exact branch name, or a path. Defaults
|
|
1542
|
+
to the current worktree.
|
|
1445
1543
|
|
|
1446
1544
|
Options:
|
|
1447
1545
|
-h, --help Show help for this command.
|
|
1448
1546
|
|
|
1449
1547
|
Examples:
|
|
1450
1548
|
gwt info
|
|
1549
|
+
gwt info primary
|
|
1451
1550
|
gwt info feature/auth`,
|
|
1452
1551
|
remove: `Safely remove a linked worktree and, by default, its branch.
|
|
1453
1552
|
|
|
@@ -1540,8 +1639,8 @@ Options:
|
|
|
1540
1639
|
-h, --help Show help for this command.
|
|
1541
1640
|
|
|
1542
1641
|
The output distinguishes a missing user config file from an existing file that
|
|
1543
|
-
does not configure the current project.
|
|
1544
|
-
precedence over user configuration.
|
|
1642
|
+
does not configure the current project. It also shows the resolved worktree
|
|
1643
|
+
directory. Repository configuration takes precedence over user configuration.
|
|
1545
1644
|
|
|
1546
1645
|
Example:
|
|
1547
1646
|
gwt config show`,
|