vde-worktree 0.0.6 → 0.0.7
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.ja.md +2 -0
- package/README.md +2 -0
- package/completions/fish/vw.fish +2 -1
- package/completions/zsh/_vw +7 -2
- package/dist/index.mjs +55 -5
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/README.ja.md
CHANGED
|
@@ -267,6 +267,7 @@ vw extract --current --stash
|
|
|
267
267
|
|
|
268
268
|
```bash
|
|
269
269
|
vw use feature/foo
|
|
270
|
+
vw use feature/foo --allow-shared
|
|
270
271
|
vw use feature/foo --allow-agent --allow-unsafe
|
|
271
272
|
```
|
|
272
273
|
|
|
@@ -278,6 +279,7 @@ vw use feature/foo --allow-agent --allow-unsafe
|
|
|
278
279
|
安全条件:
|
|
279
280
|
|
|
280
281
|
- primary が dirty なら拒否
|
|
282
|
+
- 対象 branch が他 worktree で使用中なら `--allow-shared` が必要(指定時は警告を表示)
|
|
281
283
|
- 非TTYでは `--allow-agent` と `--allow-unsafe` の両方が必要
|
|
282
284
|
|
|
283
285
|
### `exec`
|
package/README.md
CHANGED
|
@@ -267,6 +267,7 @@ Current limitation:
|
|
|
267
267
|
|
|
268
268
|
```bash
|
|
269
269
|
vw use feature/foo
|
|
270
|
+
vw use feature/foo --allow-shared
|
|
270
271
|
vw use feature/foo --allow-agent --allow-unsafe
|
|
271
272
|
```
|
|
272
273
|
|
|
@@ -278,6 +279,7 @@ What it does:
|
|
|
278
279
|
Safety:
|
|
279
280
|
|
|
280
281
|
- Rejects dirty primary worktree
|
|
282
|
+
- If target branch is attached by another worktree, requires `--allow-shared` and prints a warning
|
|
281
283
|
- In non-TTY mode, requires `--allow-agent` and `--allow-unsafe`
|
|
282
284
|
|
|
283
285
|
### `exec`
|
package/completions/fish/vw.fish
CHANGED
|
@@ -132,7 +132,7 @@ for __vw_bin in vw vde-worktree
|
|
|
132
132
|
complete -c $__vw_bin -n "__fish_seen_subcommand_from mv" -a "(__vw_local_branches)"
|
|
133
133
|
complete -c $__vw_bin -n "__fish_seen_subcommand_from del" -a "(__vw_worktree_candidates_with_meta)"
|
|
134
134
|
complete -c $__vw_bin -n "__fish_seen_subcommand_from get" -a "(__vw_remote_branches)"
|
|
135
|
-
complete -c $__vw_bin -n "__fish_seen_subcommand_from use" -a "(
|
|
135
|
+
complete -c $__vw_bin -n "__fish_seen_subcommand_from use" -a "(__vw_worktree_candidates_with_meta)"
|
|
136
136
|
complete -c $__vw_bin -n "__fish_seen_subcommand_from exec" -a "(__vw_worktree_candidates_with_meta)"
|
|
137
137
|
complete -c $__vw_bin -n "__fish_seen_subcommand_from invoke" -a "(__vw_hook_names)"
|
|
138
138
|
complete -c $__vw_bin -n "__fish_seen_subcommand_from lock" -a "(__vw_worktree_candidates_with_meta)"
|
|
@@ -153,6 +153,7 @@ for __vw_bin in vw vde-worktree
|
|
|
153
153
|
complete -c $__vw_bin -n "__fish_seen_subcommand_from extract" -l stash -d "Allow stash when dirty"
|
|
154
154
|
|
|
155
155
|
complete -c $__vw_bin -n "__fish_seen_subcommand_from use" -l allow-agent -d "Allow non-TTY execution for use"
|
|
156
|
+
complete -c $__vw_bin -n "__fish_seen_subcommand_from use" -l allow-shared -d "Allow checkout when branch is attached by another worktree"
|
|
156
157
|
complete -c $__vw_bin -n "__fish_seen_subcommand_from use" -l allow-unsafe -d "Allow unsafe behavior in non-TTY mode"
|
|
157
158
|
|
|
158
159
|
complete -c $__vw_bin -n "__fish_seen_subcommand_from link" -l no-fallback -d "Disable copy fallback when symlink fails"
|
package/completions/zsh/_vw
CHANGED
|
@@ -118,7 +118,11 @@ _vw_complete_local_branches() {
|
|
|
118
118
|
|
|
119
119
|
_vw_complete_switch_branches() {
|
|
120
120
|
local -a values
|
|
121
|
-
values=(
|
|
121
|
+
values=(
|
|
122
|
+
"${(@f)$(_vw_worktree_branches_raw)}"
|
|
123
|
+
"${(@f)$(_vw_local_branches_raw)}"
|
|
124
|
+
)
|
|
125
|
+
values=("${(@u)values}")
|
|
122
126
|
_vw_describe_values "branch" "${values[@]}"
|
|
123
127
|
}
|
|
124
128
|
|
|
@@ -231,7 +235,8 @@ _vw() {
|
|
|
231
235
|
;;
|
|
232
236
|
use)
|
|
233
237
|
_arguments \
|
|
234
|
-
"1:branch:
|
|
238
|
+
"1:branch:_vw_complete_worktree_branches_with_meta" \
|
|
239
|
+
"--allow-shared[Allow checkout when branch is attached by another worktree]" \
|
|
235
240
|
"--allow-agent[Allow non-TTY execution for use]" \
|
|
236
241
|
"--allow-unsafe[Allow unsafe behavior in non-TTY mode]"
|
|
237
242
|
;;
|
package/dist/index.mjs
CHANGED
|
@@ -10,6 +10,7 @@ import { parseArgs } from "citty";
|
|
|
10
10
|
import { execa } from "execa";
|
|
11
11
|
import stringWidth from "string-width";
|
|
12
12
|
import { getBorderCharacters, table } from "table";
|
|
13
|
+
import { createHash } from "node:crypto";
|
|
13
14
|
|
|
14
15
|
//#region src/core/constants.ts
|
|
15
16
|
const SCHEMA_VERSION = 1;
|
|
@@ -170,6 +171,8 @@ const doesGitRefExist = async (cwd, ref) => {
|
|
|
170
171
|
//#endregion
|
|
171
172
|
//#region src/core/paths.ts
|
|
172
173
|
const GIT_DIR_NAME = ".git";
|
|
174
|
+
const WORKTREE_ID_HASH_LENGTH = 12;
|
|
175
|
+
const WORKTREE_ID_SLUG_MAX_LENGTH = 48;
|
|
173
176
|
const resolveRepoRootFromCommonDir = ({ currentWorktreeRoot, gitCommonDir }) => {
|
|
174
177
|
if (gitCommonDir.endsWith(`/${GIT_DIR_NAME}`)) return dirname(gitCommonDir);
|
|
175
178
|
if (gitCommonDir.endsWith(`\\${GIT_DIR_NAME}`)) return dirname(gitCommonDir);
|
|
@@ -224,10 +227,14 @@ const getStateDirectoryPath = (repoRoot) => {
|
|
|
224
227
|
return join(getWorktreeMetaRootPath(repoRoot), "state");
|
|
225
228
|
};
|
|
226
229
|
const branchToWorktreeId = (branch) => {
|
|
227
|
-
return
|
|
230
|
+
return `${branch.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, WORKTREE_ID_SLUG_MAX_LENGTH) || "branch"}--${createHash("sha256").update(branch).digest("hex").slice(0, WORKTREE_ID_HASH_LENGTH)}`;
|
|
228
231
|
};
|
|
229
232
|
const branchToWorktreePath = (repoRoot, branch) => {
|
|
230
|
-
|
|
233
|
+
const worktreeRoot = getWorktreeRootPath(repoRoot);
|
|
234
|
+
return ensurePathInsideRepo({
|
|
235
|
+
repoRoot: worktreeRoot,
|
|
236
|
+
path: join(worktreeRoot, ...branch.split("/"))
|
|
237
|
+
});
|
|
231
238
|
};
|
|
232
239
|
const ensurePathInsideRepo = ({ repoRoot, path }) => {
|
|
233
240
|
const rel = relative(repoRoot, path);
|
|
@@ -1378,9 +1385,14 @@ const commandHelpEntries = [
|
|
|
1378
1385
|
},
|
|
1379
1386
|
{
|
|
1380
1387
|
name: "use",
|
|
1381
|
-
usage: "vw use <branch> [--allow-agent --allow-unsafe]",
|
|
1388
|
+
usage: "vw use <branch> [--allow-shared] [--allow-agent --allow-unsafe]",
|
|
1382
1389
|
summary: "Checkout target branch in primary worktree.",
|
|
1383
|
-
details: ["Non-TTY execution requires --allow-agent and --allow-unsafe."]
|
|
1390
|
+
details: ["If target branch is attached by another worktree, --allow-shared is required.", "Non-TTY execution requires --allow-agent and --allow-unsafe."],
|
|
1391
|
+
options: [
|
|
1392
|
+
"--allow-shared",
|
|
1393
|
+
"--allow-agent",
|
|
1394
|
+
"--allow-unsafe"
|
|
1395
|
+
]
|
|
1384
1396
|
},
|
|
1385
1397
|
{
|
|
1386
1398
|
name: "exec",
|
|
@@ -1654,6 +1666,7 @@ const ensureTargetPathWritable = async (targetPath) => {
|
|
|
1654
1666
|
try {
|
|
1655
1667
|
await access(targetPath, constants.F_OK);
|
|
1656
1668
|
} catch {
|
|
1669
|
+
await mkdir(dirname(targetPath), { recursive: true });
|
|
1657
1670
|
return;
|
|
1658
1671
|
}
|
|
1659
1672
|
if ((await readdir(targetPath)).length > 0) throw createCliError("TARGET_PATH_NOT_EMPTY", {
|
|
@@ -2140,6 +2153,10 @@ const createCli = (options = {}) => {
|
|
|
2140
2153
|
type: "boolean",
|
|
2141
2154
|
description: "Allow non-TTY execution for use command"
|
|
2142
2155
|
},
|
|
2156
|
+
allowShared: {
|
|
2157
|
+
type: "boolean",
|
|
2158
|
+
description: "Allow use checkout when target branch is attached by another worktree"
|
|
2159
|
+
},
|
|
2143
2160
|
reason: {
|
|
2144
2161
|
type: "string",
|
|
2145
2162
|
valueHint: "text",
|
|
@@ -3175,6 +3192,7 @@ const createCli = (options = {}) => {
|
|
|
3175
3192
|
max: 1
|
|
3176
3193
|
});
|
|
3177
3194
|
const branch = commandArgs[0];
|
|
3195
|
+
const allowShared = parsedArgs.allowShared === true;
|
|
3178
3196
|
if (runtime.isInteractive !== true) {
|
|
3179
3197
|
if (parsedArgs.allowAgent !== true) throw createCliError("UNSAFE_FLAG_REQUIRED", { message: "UNSAFE_FLAG_REQUIRED: use in non-TTY requires --allow-agent" });
|
|
3180
3198
|
ensureUnsafeForNonTty({
|
|
@@ -3191,6 +3209,38 @@ const createCli = (options = {}) => {
|
|
|
3191
3209
|
message: "use requires clean primary worktree",
|
|
3192
3210
|
details: { repoRoot }
|
|
3193
3211
|
});
|
|
3212
|
+
const branchCheckedOutInOtherWorktree = (await collectWorktreeSnapshot(repoRoot)).worktrees.find((worktree) => {
|
|
3213
|
+
return worktree.branch === branch && worktree.path !== repoRoot;
|
|
3214
|
+
});
|
|
3215
|
+
if (branchCheckedOutInOtherWorktree !== void 0 && allowShared !== true) throw createCliError("BRANCH_IN_USE", {
|
|
3216
|
+
message: [
|
|
3217
|
+
`branch '${branch}' is already checked out in another worktree.`,
|
|
3218
|
+
` path: ${branchCheckedOutInOtherWorktree.path}`,
|
|
3219
|
+
"",
|
|
3220
|
+
"To continue (unsafe), re-run with:",
|
|
3221
|
+
` vw use ${branch} --allow-shared`,
|
|
3222
|
+
"",
|
|
3223
|
+
"Risk:",
|
|
3224
|
+
" multiple worktrees will share the same branch."
|
|
3225
|
+
].join("\n"),
|
|
3226
|
+
details: {
|
|
3227
|
+
branch,
|
|
3228
|
+
path: branchCheckedOutInOtherWorktree.path,
|
|
3229
|
+
hint: "re-run with --allow-shared to continue",
|
|
3230
|
+
risk: "unsafe: multiple worktrees will share the same branch"
|
|
3231
|
+
}
|
|
3232
|
+
});
|
|
3233
|
+
if (branchCheckedOutInOtherWorktree !== void 0 && allowShared === true) stderr([
|
|
3234
|
+
"warning: --allow-shared enabled.",
|
|
3235
|
+
` branch: ${branch}`,
|
|
3236
|
+
` path: ${branchCheckedOutInOtherWorktree.path}`,
|
|
3237
|
+
" risk (unsafe): multiple worktrees will share the same branch."
|
|
3238
|
+
].join("\n"));
|
|
3239
|
+
const checkoutArgs = branchCheckedOutInOtherWorktree ? [
|
|
3240
|
+
"checkout",
|
|
3241
|
+
"--ignore-other-worktrees",
|
|
3242
|
+
branch
|
|
3243
|
+
] : ["checkout", branch];
|
|
3194
3244
|
const hookContext = createHookContext({
|
|
3195
3245
|
runtime,
|
|
3196
3246
|
repoRoot,
|
|
@@ -3205,7 +3255,7 @@ const createCli = (options = {}) => {
|
|
|
3205
3255
|
});
|
|
3206
3256
|
await runGitCommand({
|
|
3207
3257
|
cwd: repoRoot,
|
|
3208
|
-
args:
|
|
3258
|
+
args: checkoutArgs
|
|
3209
3259
|
});
|
|
3210
3260
|
await runPostHook({
|
|
3211
3261
|
name: "use",
|