pf 0.0.1 → 0.0.2
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/.github/workflows/publish.yml +33 -0
- package/build.mjs +24 -0
- package/commands/completion.ts +197 -0
- package/commands/ls.ts +49 -0
- package/commands/new.ts +82 -0
- package/commands/open.ts +61 -0
- package/commands/rm.ts +61 -0
- package/dist/index.js +2316 -0
- package/go/Makefile +37 -0
- package/go/cmd/completion.go +183 -0
- package/go/cmd/list.go +126 -0
- package/go/cmd/new.go +146 -0
- package/go/cmd/open.go +128 -0
- package/go/cmd/rm.go +88 -0
- package/go/cmd/root.go +74 -0
- package/go/cmd/styles.go +9 -0
- package/go/cmd/version.go +27 -0
- package/go/cmd/where.go +57 -0
- package/go/go.mod +37 -0
- package/go/go.sum +73 -0
- package/go/internal/store/store.go +124 -0
- package/go/main.go +7 -0
- package/index.ts +107 -0
- package/package.json +20 -22
- package/store.ts +67 -0
- package/tsconfig.json +16 -0
- package/utils.ts +108 -0
- package/.npmignore +0 -1
- package/index.js +0 -90
- package/test.js +0 -14
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
name: Publish
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- 'v*'
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
publish:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
permissions:
|
|
12
|
+
contents: read
|
|
13
|
+
id-token: write
|
|
14
|
+
steps:
|
|
15
|
+
- uses: actions/checkout@v4
|
|
16
|
+
|
|
17
|
+
- uses: pnpm/action-setup@v4
|
|
18
|
+
with:
|
|
19
|
+
version: 9
|
|
20
|
+
|
|
21
|
+
- uses: actions/setup-node@v4
|
|
22
|
+
with:
|
|
23
|
+
node-version: '24'
|
|
24
|
+
registry-url: 'https://registry.npmjs.org'
|
|
25
|
+
cache: 'pnpm'
|
|
26
|
+
|
|
27
|
+
- run: pnpm install --frozen-lockfile
|
|
28
|
+
- run: pnpm run build
|
|
29
|
+
|
|
30
|
+
- uses: JS-DevTools/npm-publish@v3
|
|
31
|
+
with:
|
|
32
|
+
token: ${{ secrets.NPM_TOKEN }}
|
|
33
|
+
provenance: true
|
package/build.mjs
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import * as esbuild from "esbuild";
|
|
2
|
+
|
|
3
|
+
const watch = process.argv.includes("--watch");
|
|
4
|
+
|
|
5
|
+
const options = {
|
|
6
|
+
entryPoints: ["index.ts"],
|
|
7
|
+
bundle: true,
|
|
8
|
+
platform: "node",
|
|
9
|
+
target: "node18",
|
|
10
|
+
format: "esm",
|
|
11
|
+
outfile: "dist/index.js",
|
|
12
|
+
banner: {
|
|
13
|
+
js: "#!/usr/bin/env node",
|
|
14
|
+
},
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
if (watch) {
|
|
18
|
+
const ctx = await esbuild.context(options);
|
|
19
|
+
await ctx.watch();
|
|
20
|
+
console.log("Watching...");
|
|
21
|
+
} else {
|
|
22
|
+
await esbuild.build(options);
|
|
23
|
+
console.log("Built dist/index.js");
|
|
24
|
+
}
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
import { existsSync, readFileSync, writeFileSync, appendFileSync, mkdirSync } from "fs";
|
|
2
|
+
import { dirname, join } from "path";
|
|
3
|
+
import { homedir } from "os";
|
|
4
|
+
import { createInterface } from "readline";
|
|
5
|
+
import { success, dim } from "../utils.js";
|
|
6
|
+
|
|
7
|
+
function detectShell(): string | null {
|
|
8
|
+
const shell = process.env.SHELL || "";
|
|
9
|
+
if (shell.includes("zsh")) return "zsh";
|
|
10
|
+
if (shell.includes("bash")) return "bash";
|
|
11
|
+
if (shell.includes("fish")) return "fish";
|
|
12
|
+
return null;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function getCompletionPath(shell: string): string {
|
|
16
|
+
const home = homedir();
|
|
17
|
+
switch (shell) {
|
|
18
|
+
case "zsh":
|
|
19
|
+
return join(home, ".zsh/completions/_pf");
|
|
20
|
+
case "bash":
|
|
21
|
+
return join(home, ".bash_completion.d/pf");
|
|
22
|
+
case "fish":
|
|
23
|
+
return join(home, ".config/fish/completions/pf.fish");
|
|
24
|
+
default:
|
|
25
|
+
return "";
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function getRcConfig(shell: string): { path: string; lines: string[] } | null {
|
|
30
|
+
const home = homedir();
|
|
31
|
+
switch (shell) {
|
|
32
|
+
case "zsh":
|
|
33
|
+
return {
|
|
34
|
+
path: join(home, ".zshrc"),
|
|
35
|
+
lines: [
|
|
36
|
+
'fpath=(~/.zsh/completions $fpath)',
|
|
37
|
+
'autoload -Uz compinit && compinit',
|
|
38
|
+
],
|
|
39
|
+
};
|
|
40
|
+
case "bash":
|
|
41
|
+
return {
|
|
42
|
+
path: join(home, ".bashrc"),
|
|
43
|
+
lines: ['source ~/.bash_completion.d/pf'],
|
|
44
|
+
};
|
|
45
|
+
case "fish":
|
|
46
|
+
return null; // Fish auto-loads from completions dir
|
|
47
|
+
default:
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function fileContains(path: string, substr: string): boolean {
|
|
53
|
+
try {
|
|
54
|
+
const data = readFileSync(path, "utf-8");
|
|
55
|
+
return data.includes(substr);
|
|
56
|
+
} catch {
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function getCompletionScript(shell: string): string {
|
|
62
|
+
switch (shell) {
|
|
63
|
+
case "zsh":
|
|
64
|
+
return `#compdef pf
|
|
65
|
+
|
|
66
|
+
_pf() {
|
|
67
|
+
local -a commands worktrees
|
|
68
|
+
commands=(
|
|
69
|
+
'new:Create a new worktree with a branch'
|
|
70
|
+
'open:Open a worktree'
|
|
71
|
+
'ls:List all tracked worktrees'
|
|
72
|
+
'rm:Remove a worktree'
|
|
73
|
+
'completion:Install shell completions'
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
_arguments -C \\
|
|
77
|
+
"1: :->command" \\
|
|
78
|
+
"*::arg:->args"
|
|
79
|
+
|
|
80
|
+
case "$state" in
|
|
81
|
+
command)
|
|
82
|
+
_describe 'command' commands
|
|
83
|
+
;;
|
|
84
|
+
args)
|
|
85
|
+
case "$words[1]" in
|
|
86
|
+
open|rm)
|
|
87
|
+
worktrees=(\${(f)"\$(pf ls --plain 2>/dev/null | cut -f1)"})
|
|
88
|
+
_describe 'worktree' worktrees
|
|
89
|
+
;;
|
|
90
|
+
esac
|
|
91
|
+
;;
|
|
92
|
+
esac
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
_pf "$@"
|
|
96
|
+
`;
|
|
97
|
+
case "bash":
|
|
98
|
+
return `_pf() {
|
|
99
|
+
local cur="\${COMP_WORDS[COMP_CWORD]}"
|
|
100
|
+
local prev="\${COMP_WORDS[COMP_CWORD-1]}"
|
|
101
|
+
local commands="new open ls rm completion"
|
|
102
|
+
|
|
103
|
+
case "$prev" in
|
|
104
|
+
open|rm)
|
|
105
|
+
local worktrees="\$(pf ls --plain 2>/dev/null | cut -f1)"
|
|
106
|
+
COMPREPLY=($(compgen -W "$worktrees" -- "$cur"))
|
|
107
|
+
return
|
|
108
|
+
;;
|
|
109
|
+
esac
|
|
110
|
+
|
|
111
|
+
COMPREPLY=($(compgen -W "$commands" -- "$cur"))
|
|
112
|
+
}
|
|
113
|
+
complete -F _pf pf
|
|
114
|
+
`;
|
|
115
|
+
case "fish":
|
|
116
|
+
return `complete -c pf -n "__fish_use_subcommand" -a new -d "Create a new worktree with a branch"
|
|
117
|
+
complete -c pf -n "__fish_use_subcommand" -a open -d "Open a worktree"
|
|
118
|
+
complete -c pf -n "__fish_use_subcommand" -a ls -d "List all tracked worktrees"
|
|
119
|
+
complete -c pf -n "__fish_use_subcommand" -a rm -d "Remove a worktree"
|
|
120
|
+
complete -c pf -n "__fish_use_subcommand" -a completion -d "Install shell completions"
|
|
121
|
+
complete -c pf -n "__fish_seen_subcommand_from open rm" -a "(pf ls --plain 2>/dev/null | cut -f1)" -d "worktree"
|
|
122
|
+
`;
|
|
123
|
+
default:
|
|
124
|
+
return "";
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
async function prompt(question: string): Promise<string> {
|
|
129
|
+
const rl = createInterface({
|
|
130
|
+
input: process.stdin,
|
|
131
|
+
output: process.stdout,
|
|
132
|
+
});
|
|
133
|
+
return new Promise((resolve) => {
|
|
134
|
+
rl.question(question, (answer) => {
|
|
135
|
+
rl.close();
|
|
136
|
+
resolve(answer);
|
|
137
|
+
});
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
export async function completionCommand(shell?: string): Promise<void> {
|
|
142
|
+
// If shell specified, output script to stdout
|
|
143
|
+
if (shell) {
|
|
144
|
+
const script = getCompletionScript(shell);
|
|
145
|
+
if (!script) {
|
|
146
|
+
console.error(`Unknown shell: ${shell}`);
|
|
147
|
+
process.exit(1);
|
|
148
|
+
}
|
|
149
|
+
console.log(script);
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// Interactive mode
|
|
154
|
+
const detectedShell = detectShell();
|
|
155
|
+
if (!detectedShell) {
|
|
156
|
+
console.error("Could not detect shell. Run: pf completion [bash|zsh|fish]");
|
|
157
|
+
process.exit(1);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const completionPath = getCompletionPath(detectedShell);
|
|
161
|
+
const rcConfig = getRcConfig(detectedShell);
|
|
162
|
+
|
|
163
|
+
let desc = `1. Write completion script to ${completionPath}\n`;
|
|
164
|
+
if (rcConfig && !fileContains(rcConfig.path, rcConfig.lines[0])) {
|
|
165
|
+
desc += `2. Add to ${rcConfig.path}:\n`;
|
|
166
|
+
for (const line of rcConfig.lines) {
|
|
167
|
+
desc += ` ${line}\n`;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
console.log(`Install ${detectedShell} completions? This will:`);
|
|
172
|
+
console.log(desc);
|
|
173
|
+
const response = await prompt("Proceed? [y/N] ");
|
|
174
|
+
|
|
175
|
+
if (response.toLowerCase() !== "y" && response.toLowerCase() !== "yes") {
|
|
176
|
+
console.log("Canceled.");
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// Write completion script
|
|
181
|
+
mkdirSync(dirname(completionPath), { recursive: true });
|
|
182
|
+
writeFileSync(completionPath, getCompletionScript(detectedShell));
|
|
183
|
+
console.log(success("✓"), "Wrote", completionPath);
|
|
184
|
+
|
|
185
|
+
// Update rc file if needed
|
|
186
|
+
if (rcConfig && !fileContains(rcConfig.path, rcConfig.lines[0])) {
|
|
187
|
+
appendFileSync(rcConfig.path, "\n# pf completions\n" + rcConfig.lines.join("\n") + "\n");
|
|
188
|
+
console.log(success("✓"), "Updated", rcConfig.path);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
console.log();
|
|
192
|
+
if (rcConfig) {
|
|
193
|
+
console.log("Restart your shell or run:", dim(`source ${rcConfig.path}`));
|
|
194
|
+
} else {
|
|
195
|
+
console.log("Restart your shell to enable completions.");
|
|
196
|
+
}
|
|
197
|
+
}
|
package/commands/ls.ts
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { resolve } from "path";
|
|
2
|
+
import Table from "cli-table3";
|
|
3
|
+
import pc from "picocolors";
|
|
4
|
+
import { loadStore } from "../store.js";
|
|
5
|
+
import { getWorktreeBranch, getWorktreeStatus } from "../utils.js";
|
|
6
|
+
|
|
7
|
+
export function lsCommand(plain: boolean = false): void {
|
|
8
|
+
const store = loadStore();
|
|
9
|
+
|
|
10
|
+
if (store.worktrees.length === 0) {
|
|
11
|
+
console.log("No worktrees. Use 'pf new <name>' to create one.");
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// Detect current worktree
|
|
16
|
+
const cwd = process.cwd();
|
|
17
|
+
let currentWorktree = "";
|
|
18
|
+
for (const wt of store.worktrees) {
|
|
19
|
+
const absPath = resolve(wt.path);
|
|
20
|
+
if (cwd.startsWith(absPath)) {
|
|
21
|
+
currentWorktree = wt.name;
|
|
22
|
+
break;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if (plain) {
|
|
27
|
+
for (const wt of store.worktrees) {
|
|
28
|
+
const branch = getWorktreeBranch(wt.path);
|
|
29
|
+
const status = getWorktreeStatus(wt.path);
|
|
30
|
+
console.log(`${wt.name}\t${branch}\t${status}`);
|
|
31
|
+
}
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const table = new Table({
|
|
36
|
+
head: ["name", "branch", "git status"],
|
|
37
|
+
style: { head: [], border: [] },
|
|
38
|
+
chars: { mid: "", "left-mid": "", "mid-mid": "", "right-mid": "" },
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
for (const wt of store.worktrees) {
|
|
42
|
+
const branch = getWorktreeBranch(wt.path);
|
|
43
|
+
const status = getWorktreeStatus(wt.path);
|
|
44
|
+
const name = wt.name === currentWorktree ? `${pc.green("*")}${wt.name}` : wt.name;
|
|
45
|
+
table.push([name, branch, status]);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
console.log(table.toString());
|
|
49
|
+
}
|
package/commands/new.ts
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { basename, dirname, join } from "path";
|
|
2
|
+
import { mkdirSync } from "fs";
|
|
3
|
+
import {
|
|
4
|
+
loadStore,
|
|
5
|
+
saveStore,
|
|
6
|
+
addWorktree,
|
|
7
|
+
findWorktree,
|
|
8
|
+
getGitRoot,
|
|
9
|
+
getGitCommonDir,
|
|
10
|
+
} from "../store.js";
|
|
11
|
+
import {
|
|
12
|
+
branchExists,
|
|
13
|
+
isInsideWorktree,
|
|
14
|
+
getCurrentBranch,
|
|
15
|
+
createWorktree,
|
|
16
|
+
generateName,
|
|
17
|
+
spawnShell,
|
|
18
|
+
success,
|
|
19
|
+
bold,
|
|
20
|
+
dim,
|
|
21
|
+
} from "../utils.js";
|
|
22
|
+
|
|
23
|
+
export function newCommand(name?: string): void {
|
|
24
|
+
const worktreeName = name ?? generateName();
|
|
25
|
+
|
|
26
|
+
// Check if inside a worktree
|
|
27
|
+
if (isInsideWorktree()) {
|
|
28
|
+
console.error("Error: You are inside a pf worktree.");
|
|
29
|
+
console.error("");
|
|
30
|
+
console.error("Each time you run 'pf new' or 'pf open', pf starts a subshell in the");
|
|
31
|
+
console.error("worktree directory. To keep subshells shallow and avoid nesting, pf");
|
|
32
|
+
console.error("requires you to return to the main repo before opening another worktree.");
|
|
33
|
+
console.error("");
|
|
34
|
+
console.error("To exit the current worktree:");
|
|
35
|
+
console.error(" exit");
|
|
36
|
+
console.error("");
|
|
37
|
+
console.error("Then re-run:");
|
|
38
|
+
console.error(` pf new ${worktreeName}`);
|
|
39
|
+
process.exit(1);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Check if branch already exists
|
|
43
|
+
if (branchExists(worktreeName)) {
|
|
44
|
+
console.error(`Error: branch '${worktreeName}' already exists`);
|
|
45
|
+
process.exit(1);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Check if worktree already exists in store
|
|
49
|
+
const store = loadStore();
|
|
50
|
+
if (findWorktree(store, worktreeName)) {
|
|
51
|
+
console.error(`Error: worktree '${worktreeName}' already exists - use 'pf open ${worktreeName}'`);
|
|
52
|
+
process.exit(1);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const baseBranch = getCurrentBranch();
|
|
56
|
+
const gitRoot = getGitRoot();
|
|
57
|
+
const gitDir = getGitCommonDir();
|
|
58
|
+
const repoName = basename(gitRoot);
|
|
59
|
+
const worktreePath = join(gitDir, "pf", "worktrees", worktreeName, repoName);
|
|
60
|
+
|
|
61
|
+
// Create directory
|
|
62
|
+
mkdirSync(dirname(worktreePath), { recursive: true });
|
|
63
|
+
|
|
64
|
+
// Create worktree
|
|
65
|
+
createWorktree(worktreeName, worktreePath);
|
|
66
|
+
|
|
67
|
+
// Track in store
|
|
68
|
+
addWorktree(store, worktreeName, worktreePath);
|
|
69
|
+
saveStore(store);
|
|
70
|
+
|
|
71
|
+
console.log();
|
|
72
|
+
console.log(success(bold(worktreeName)), dim(`(from ${baseBranch})`));
|
|
73
|
+
console.log(dim(" Subshell started. Type 'exit' to return."));
|
|
74
|
+
console.log();
|
|
75
|
+
|
|
76
|
+
// Spawn shell
|
|
77
|
+
spawnShell(worktreePath);
|
|
78
|
+
|
|
79
|
+
console.log();
|
|
80
|
+
console.log(dim(" Back in main worktree"));
|
|
81
|
+
console.log();
|
|
82
|
+
}
|
package/commands/open.ts
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { existsSync } from "fs";
|
|
2
|
+
import { loadStore, findWorktree } from "../store.js";
|
|
3
|
+
import {
|
|
4
|
+
isInsideWorktree,
|
|
5
|
+
hasUncommittedChanges,
|
|
6
|
+
getWorktreeBranch,
|
|
7
|
+
spawnShell,
|
|
8
|
+
success,
|
|
9
|
+
bold,
|
|
10
|
+
dim,
|
|
11
|
+
} from "../utils.js";
|
|
12
|
+
|
|
13
|
+
export function openCommand(name: string): void {
|
|
14
|
+
// Check if inside a worktree
|
|
15
|
+
if (isInsideWorktree()) {
|
|
16
|
+
console.error("Error: You are inside a pf worktree.");
|
|
17
|
+
console.error("");
|
|
18
|
+
console.error("Each time you run 'pf new' or 'pf open', pf starts a subshell in the");
|
|
19
|
+
console.error("worktree directory. To keep subshells shallow and avoid nesting, pf");
|
|
20
|
+
console.error("requires you to return to the main repo before opening another worktree.");
|
|
21
|
+
console.error("");
|
|
22
|
+
console.error("To exit the current worktree:");
|
|
23
|
+
console.error(" exit");
|
|
24
|
+
console.error("");
|
|
25
|
+
console.error("Then re-run:");
|
|
26
|
+
console.error(` pf open ${name}`);
|
|
27
|
+
process.exit(1);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Check for uncommitted changes
|
|
31
|
+
if (hasUncommittedChanges()) {
|
|
32
|
+
console.error("Error: uncommitted changes in current worktree - commit or stash first");
|
|
33
|
+
process.exit(1);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const store = loadStore();
|
|
37
|
+
const wt = findWorktree(store, name);
|
|
38
|
+
|
|
39
|
+
if (!wt) {
|
|
40
|
+
console.error(`Error: worktree '${name}' not found`);
|
|
41
|
+
process.exit(1);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (!existsSync(wt.path)) {
|
|
45
|
+
console.error(`Error: worktree directory does not exist: ${wt.path}`);
|
|
46
|
+
process.exit(1);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const branch = getWorktreeBranch(wt.path);
|
|
50
|
+
|
|
51
|
+
console.log();
|
|
52
|
+
console.log(success(bold(name)), dim(`(${branch})`));
|
|
53
|
+
console.log(dim(" Subshell started. Type 'exit' to return."));
|
|
54
|
+
console.log();
|
|
55
|
+
|
|
56
|
+
spawnShell(wt.path);
|
|
57
|
+
|
|
58
|
+
console.log();
|
|
59
|
+
console.log(dim(" Back in main worktree"));
|
|
60
|
+
console.log();
|
|
61
|
+
}
|
package/commands/rm.ts
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { resolve } from "path";
|
|
2
|
+
import { loadStore, saveStore, findWorktree, removeWorktree as removeFromStore } from "../store.js";
|
|
3
|
+
import {
|
|
4
|
+
getWorktreeBranch,
|
|
5
|
+
removeWorktree,
|
|
6
|
+
pruneWorktrees,
|
|
7
|
+
deleteBranch,
|
|
8
|
+
success,
|
|
9
|
+
bold,
|
|
10
|
+
dim,
|
|
11
|
+
} from "../utils.js";
|
|
12
|
+
|
|
13
|
+
export function rmCommand(name: string, deleteBranchFlag: boolean = false): void {
|
|
14
|
+
const store = loadStore();
|
|
15
|
+
const wt = findWorktree(store, name);
|
|
16
|
+
|
|
17
|
+
if (!wt) {
|
|
18
|
+
console.error(`Error: worktree '${name}' not found`);
|
|
19
|
+
process.exit(1);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Check if we're currently in this worktree
|
|
23
|
+
const cwd = process.cwd();
|
|
24
|
+
const absWtPath = resolve(wt.path);
|
|
25
|
+
const absCwd = resolve(cwd);
|
|
26
|
+
if (absCwd.startsWith(absWtPath)) {
|
|
27
|
+
console.error("Error: cannot remove current worktree - exit first");
|
|
28
|
+
process.exit(1);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Get branch before removing
|
|
32
|
+
const branch = getWorktreeBranch(wt.path);
|
|
33
|
+
|
|
34
|
+
// Remove worktree
|
|
35
|
+
try {
|
|
36
|
+
removeWorktree(wt.path);
|
|
37
|
+
} catch {
|
|
38
|
+
// May already be gone
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
pruneWorktrees();
|
|
42
|
+
|
|
43
|
+
// Delete branch if requested
|
|
44
|
+
if (deleteBranchFlag && branch !== "[missing]" && branch !== "[detached]") {
|
|
45
|
+
try {
|
|
46
|
+
deleteBranch(branch);
|
|
47
|
+
} catch (err) {
|
|
48
|
+
console.error(`Warning: failed to delete branch ${branch}`);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Remove from store
|
|
53
|
+
removeFromStore(store, name);
|
|
54
|
+
saveStore(store);
|
|
55
|
+
|
|
56
|
+
if (deleteBranchFlag && branch !== "[missing]" && branch !== "[detached]") {
|
|
57
|
+
console.log(success("Removed"), bold(name), dim("(branch deleted)"));
|
|
58
|
+
} else {
|
|
59
|
+
console.log(success("Removed"), bold(name));
|
|
60
|
+
}
|
|
61
|
+
}
|