@lsst/pik-plugin-worktree 0.4.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/README.md +78 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +352 -0
- package/dist/lib/commands/create.d.ts +4 -0
- package/dist/lib/commands/create.d.ts.map +1 -0
- package/dist/lib/commands/list.d.ts +3 -0
- package/dist/lib/commands/list.d.ts.map +1 -0
- package/dist/lib/commands/remove.d.ts +3 -0
- package/dist/lib/commands/remove.d.ts.map +1 -0
- package/dist/lib/git.d.ts +62 -0
- package/dist/lib/git.d.ts.map +1 -0
- package/dist/lib/plugin.d.ts +3 -0
- package/dist/lib/plugin.d.ts.map +1 -0
- package/dist/lib/types.d.ts +20 -0
- package/dist/lib/types.d.ts.map +1 -0
- package/package.json +36 -0
package/README.md
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# @lsst/pik-plugin-worktree
|
|
2
|
+
|
|
3
|
+
Git worktree management plugin for pik - easily create, list, and remove git worktrees.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
This plugin is included with `@lsst/pik` by default.
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install -g @lsst/pik
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Usage
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
# Interactive create (default)
|
|
17
|
+
pik worktree
|
|
18
|
+
|
|
19
|
+
# Create with specific name
|
|
20
|
+
pik worktree create my-feature
|
|
21
|
+
|
|
22
|
+
# Create new branch
|
|
23
|
+
pik worktree create my-feature -n -b feature/new-thing
|
|
24
|
+
|
|
25
|
+
# List all worktrees
|
|
26
|
+
pik worktree list
|
|
27
|
+
|
|
28
|
+
# Remove a worktree
|
|
29
|
+
pik worktree remove
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Configuration
|
|
33
|
+
|
|
34
|
+
Add to your `pik.config.ts`:
|
|
35
|
+
|
|
36
|
+
```typescript
|
|
37
|
+
import { defineConfig } from '@lsst/pik';
|
|
38
|
+
|
|
39
|
+
export default defineConfig({
|
|
40
|
+
worktree: {
|
|
41
|
+
// Directory where worktrees are created (relative to repo root)
|
|
42
|
+
baseDir: '../',
|
|
43
|
+
|
|
44
|
+
// Files to copy to new worktrees
|
|
45
|
+
copyFiles: ['.env.local', '.env.development'],
|
|
46
|
+
|
|
47
|
+
// Command to run after creating worktree
|
|
48
|
+
postCreate: 'npm install',
|
|
49
|
+
},
|
|
50
|
+
});
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Commands
|
|
54
|
+
|
|
55
|
+
| Command | Alias | Description |
|
|
56
|
+
|---------|-------|-------------|
|
|
57
|
+
| `pik worktree` | `wt` | Interactive worktree creation |
|
|
58
|
+
| `pik worktree create [name]` | `add` | Create a new worktree |
|
|
59
|
+
| `pik worktree list` | `ls` | List all worktrees |
|
|
60
|
+
| `pik worktree remove [path]` | `rm` | Remove a worktree |
|
|
61
|
+
|
|
62
|
+
### Create Options
|
|
63
|
+
|
|
64
|
+
| Option | Description |
|
|
65
|
+
|--------|-------------|
|
|
66
|
+
| `-b, --branch <branch>` | Branch to checkout or create |
|
|
67
|
+
| `-n, --new` | Create a new branch |
|
|
68
|
+
|
|
69
|
+
### Remove Options
|
|
70
|
+
|
|
71
|
+
| Option | Description |
|
|
72
|
+
|--------|-------------|
|
|
73
|
+
| `-f, --force` | Force removal even if dirty |
|
|
74
|
+
| `-D, --delete-branch` | Also delete the branch |
|
|
75
|
+
|
|
76
|
+
## License
|
|
77
|
+
|
|
78
|
+
MIT
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { worktreePlugin } from './lib/plugin.js';
|
|
2
|
+
export type { WorktreeConfig } from './lib/types.js';
|
|
3
|
+
export { git, getRepoRoot, getCurrentBranch, isClean, listWorktrees, createWorktree, removeWorktree, listBranches, branchExists, type Worktree, } from './lib/git.js';
|
|
4
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAGjD,YAAY,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAGrD,OAAO,EACL,GAAG,EACH,WAAW,EACX,gBAAgB,EAChB,OAAO,EACP,aAAa,EACb,cAAc,EACd,cAAc,EACd,YAAY,EACZ,YAAY,EACZ,KAAK,QAAQ,GACd,MAAM,cAAc,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,352 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import { select, input, confirm } from "@inquirer/prompts";
|
|
3
|
+
import pc from "picocolors";
|
|
4
|
+
import { basename, resolve, relative } from "path";
|
|
5
|
+
import { execSync } from "child_process";
|
|
6
|
+
import { existsSync, mkdirSync, copyFileSync } from "fs";
|
|
7
|
+
import { glob } from "glob";
|
|
8
|
+
import { loadConfig } from "@lsst/pik-core";
|
|
9
|
+
function git(args, cwd) {
|
|
10
|
+
try {
|
|
11
|
+
return execSync(`git ${args.join(" ")}`, {
|
|
12
|
+
cwd,
|
|
13
|
+
encoding: "utf-8",
|
|
14
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
15
|
+
}).trim();
|
|
16
|
+
} catch (error) {
|
|
17
|
+
const err = error;
|
|
18
|
+
const gitError = new Error(
|
|
19
|
+
err.stderr?.toString().trim() || err.message
|
|
20
|
+
);
|
|
21
|
+
gitError.stderr = err.stderr?.toString();
|
|
22
|
+
throw gitError;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
function getRepoRoot(cwd) {
|
|
26
|
+
return git(["rev-parse", "--show-toplevel"], cwd);
|
|
27
|
+
}
|
|
28
|
+
function getCurrentBranch(cwd) {
|
|
29
|
+
return git(["rev-parse", "--abbrev-ref", "HEAD"], cwd);
|
|
30
|
+
}
|
|
31
|
+
function isClean(cwd) {
|
|
32
|
+
const status = git(["status", "--porcelain"], cwd);
|
|
33
|
+
return status === "";
|
|
34
|
+
}
|
|
35
|
+
function listWorktrees(cwd) {
|
|
36
|
+
const output = git(["worktree", "list", "--porcelain"], cwd);
|
|
37
|
+
if (!output) return [];
|
|
38
|
+
const worktrees = [];
|
|
39
|
+
let current = {};
|
|
40
|
+
for (const line of output.split("\n")) {
|
|
41
|
+
if (line.startsWith("worktree ")) {
|
|
42
|
+
if (current.path) {
|
|
43
|
+
worktrees.push(current);
|
|
44
|
+
}
|
|
45
|
+
current = { path: line.slice(9), isBare: false, isMain: false };
|
|
46
|
+
} else if (line.startsWith("HEAD ")) {
|
|
47
|
+
current.commit = line.slice(5);
|
|
48
|
+
} else if (line.startsWith("branch ")) {
|
|
49
|
+
current.branch = line.slice(7).replace("refs/heads/", "");
|
|
50
|
+
} else if (line === "bare") {
|
|
51
|
+
current.isBare = true;
|
|
52
|
+
} else if (line === "") {
|
|
53
|
+
if (worktrees.length === 0 && current.path) {
|
|
54
|
+
current.isMain = true;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
if (current.path) {
|
|
59
|
+
if (worktrees.length === 0) {
|
|
60
|
+
current.isMain = true;
|
|
61
|
+
}
|
|
62
|
+
worktrees.push(current);
|
|
63
|
+
}
|
|
64
|
+
return worktrees;
|
|
65
|
+
}
|
|
66
|
+
function createWorktree(path, branch, options, cwd) {
|
|
67
|
+
const args = ["worktree", "add"];
|
|
68
|
+
if (options?.newBranch) {
|
|
69
|
+
args.push("-b", branch);
|
|
70
|
+
args.push(path);
|
|
71
|
+
if (options.baseBranch) {
|
|
72
|
+
args.push(options.baseBranch);
|
|
73
|
+
}
|
|
74
|
+
} else {
|
|
75
|
+
args.push(path, branch);
|
|
76
|
+
}
|
|
77
|
+
git(args, cwd);
|
|
78
|
+
}
|
|
79
|
+
function removeWorktree(path, force, cwd) {
|
|
80
|
+
const args = ["worktree", "remove"];
|
|
81
|
+
if (force) {
|
|
82
|
+
args.push("--force");
|
|
83
|
+
}
|
|
84
|
+
args.push(path);
|
|
85
|
+
git(args, cwd);
|
|
86
|
+
}
|
|
87
|
+
function listBranches(cwd) {
|
|
88
|
+
const output = git(["branch", "--format=%(refname:short)"], cwd);
|
|
89
|
+
return output.split("\n").filter(Boolean);
|
|
90
|
+
}
|
|
91
|
+
function branchExists(branch, cwd) {
|
|
92
|
+
try {
|
|
93
|
+
git(["rev-parse", "--verify", branch], cwd);
|
|
94
|
+
return true;
|
|
95
|
+
} catch {
|
|
96
|
+
return false;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
const createCommand = new Command("create").alias("add").description("Create a new worktree").argument("[name]", "Name for the worktree directory").option("-b, --branch <branch>", "Branch to checkout (or create with -n)").option("-n, --new", "Create a new branch").action(async (name, options) => {
|
|
100
|
+
try {
|
|
101
|
+
const repoRoot = getRepoRoot();
|
|
102
|
+
const config = await loadConfig(repoRoot);
|
|
103
|
+
const worktreeConfig = config?.worktree ?? {};
|
|
104
|
+
const currentBranch = getCurrentBranch(repoRoot);
|
|
105
|
+
const branches = listBranches(repoRoot);
|
|
106
|
+
const existingWorktrees = listWorktrees(repoRoot);
|
|
107
|
+
const existingPaths = new Set(existingWorktrees.map((w) => w.path));
|
|
108
|
+
let branch = options.branch;
|
|
109
|
+
let isNewBranch = options.new ?? false;
|
|
110
|
+
if (!branch) {
|
|
111
|
+
const branchAction = await select({
|
|
112
|
+
message: "What do you want to do?",
|
|
113
|
+
choices: [
|
|
114
|
+
{ name: "Create a new branch", value: "new" },
|
|
115
|
+
{ name: "Checkout existing branch", value: "existing" }
|
|
116
|
+
]
|
|
117
|
+
});
|
|
118
|
+
if (branchAction === "new") {
|
|
119
|
+
isNewBranch = true;
|
|
120
|
+
branch = await input({
|
|
121
|
+
message: "New branch name:",
|
|
122
|
+
validate: (value) => {
|
|
123
|
+
if (!value.trim()) return "Branch name is required";
|
|
124
|
+
if (branchExists(value, repoRoot)) return "Branch already exists";
|
|
125
|
+
return true;
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
} else {
|
|
129
|
+
const worktreeBranches = new Set(
|
|
130
|
+
existingWorktrees.map((w) => w.branch).filter(Boolean)
|
|
131
|
+
);
|
|
132
|
+
const availableBranches = branches.filter(
|
|
133
|
+
(b) => !worktreeBranches.has(b)
|
|
134
|
+
);
|
|
135
|
+
if (availableBranches.length === 0) {
|
|
136
|
+
console.log(pc.yellow("All branches already have worktrees"));
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
branch = await select({
|
|
140
|
+
message: "Select branch:",
|
|
141
|
+
choices: availableBranches.map((b) => ({
|
|
142
|
+
name: b === currentBranch ? `${b} (current)` : b,
|
|
143
|
+
value: b
|
|
144
|
+
}))
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
let worktreeName = name;
|
|
149
|
+
if (!worktreeName) {
|
|
150
|
+
const defaultName = branch.replace(/\//g, "-");
|
|
151
|
+
worktreeName = await input({
|
|
152
|
+
message: "Worktree directory name:",
|
|
153
|
+
default: defaultName,
|
|
154
|
+
validate: (value) => {
|
|
155
|
+
if (!value.trim()) return "Name is required";
|
|
156
|
+
return true;
|
|
157
|
+
}
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
const baseDir = worktreeConfig.baseDir ?? "../";
|
|
161
|
+
const repoName = basename(repoRoot);
|
|
162
|
+
const worktreePath = resolve(repoRoot, baseDir, `${repoName}-${worktreeName}`);
|
|
163
|
+
if (existingPaths.has(worktreePath) || existsSync(worktreePath)) {
|
|
164
|
+
console.error(pc.red(`Path already exists: ${worktreePath}`));
|
|
165
|
+
process.exit(1);
|
|
166
|
+
}
|
|
167
|
+
console.log(pc.dim(`Creating worktree at ${worktreePath}...`));
|
|
168
|
+
createWorktree(
|
|
169
|
+
worktreePath,
|
|
170
|
+
branch,
|
|
171
|
+
{ newBranch: isNewBranch, baseBranch: currentBranch },
|
|
172
|
+
repoRoot
|
|
173
|
+
);
|
|
174
|
+
console.log(pc.green(`✓ Created worktree for branch ${pc.bold(branch)}`));
|
|
175
|
+
if (worktreeConfig.copyFiles?.length) {
|
|
176
|
+
console.log(pc.dim("Copying files..."));
|
|
177
|
+
for (const pattern of worktreeConfig.copyFiles) {
|
|
178
|
+
const files = await glob(pattern, { cwd: repoRoot, nodir: true });
|
|
179
|
+
for (const file of files) {
|
|
180
|
+
const src = resolve(repoRoot, file);
|
|
181
|
+
const dest = resolve(worktreePath, file);
|
|
182
|
+
const destDir = resolve(dest, "..");
|
|
183
|
+
if (!existsSync(destDir)) {
|
|
184
|
+
mkdirSync(destDir, { recursive: true });
|
|
185
|
+
}
|
|
186
|
+
if (existsSync(src)) {
|
|
187
|
+
copyFileSync(src, dest);
|
|
188
|
+
console.log(pc.dim(` Copied ${file}`));
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
if (worktreeConfig.postCreate) {
|
|
194
|
+
const shouldRun = await confirm({
|
|
195
|
+
message: `Run "${worktreeConfig.postCreate}"?`,
|
|
196
|
+
default: true
|
|
197
|
+
});
|
|
198
|
+
if (shouldRun) {
|
|
199
|
+
console.log(pc.dim(`Running: ${worktreeConfig.postCreate}`));
|
|
200
|
+
try {
|
|
201
|
+
execSync(worktreeConfig.postCreate, {
|
|
202
|
+
cwd: worktreePath,
|
|
203
|
+
stdio: "inherit"
|
|
204
|
+
});
|
|
205
|
+
console.log(pc.green("✓ Post-create command completed"));
|
|
206
|
+
} catch {
|
|
207
|
+
console.log(pc.yellow("⚠ Post-create command failed"));
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
console.log();
|
|
212
|
+
console.log(pc.green("✓ Worktree ready!"));
|
|
213
|
+
console.log(pc.dim(` cd ${worktreePath}`));
|
|
214
|
+
} catch (error) {
|
|
215
|
+
if (error instanceof Error) {
|
|
216
|
+
if (error.name === "ExitPromptError") {
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
console.error(pc.red(error.message));
|
|
220
|
+
}
|
|
221
|
+
process.exit(1);
|
|
222
|
+
}
|
|
223
|
+
});
|
|
224
|
+
const listCommand = new Command("list").alias("ls").description("List all worktrees").option("--json", "Output in JSON format").action(async (options) => {
|
|
225
|
+
try {
|
|
226
|
+
const repoRoot = getRepoRoot();
|
|
227
|
+
const worktrees = listWorktrees(repoRoot);
|
|
228
|
+
if (options.json) {
|
|
229
|
+
console.log(JSON.stringify(worktrees, null, 2));
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
if (worktrees.length === 0) {
|
|
233
|
+
console.log(pc.yellow("No worktrees found"));
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
console.log(pc.bold("Worktrees:\n"));
|
|
237
|
+
for (const wt of worktrees) {
|
|
238
|
+
const relativePath = relative(process.cwd(), wt.path) || ".";
|
|
239
|
+
const branchDisplay = wt.branch || pc.dim("(detached)");
|
|
240
|
+
const mainLabel = wt.isMain ? pc.cyan(" [main]") : "";
|
|
241
|
+
console.log(` ${pc.green(relativePath)}${mainLabel}`);
|
|
242
|
+
console.log(` Branch: ${branchDisplay}`);
|
|
243
|
+
console.log(` Commit: ${pc.dim(wt.commit?.slice(0, 7) || "unknown")}`);
|
|
244
|
+
console.log();
|
|
245
|
+
}
|
|
246
|
+
} catch (error) {
|
|
247
|
+
if (error instanceof Error) {
|
|
248
|
+
console.error(pc.red(error.message));
|
|
249
|
+
}
|
|
250
|
+
process.exit(1);
|
|
251
|
+
}
|
|
252
|
+
});
|
|
253
|
+
const removeCommand = new Command("remove").alias("rm").description("Remove a worktree").argument("[path]", "Path to the worktree to remove").option("-f, --force", "Force removal even if worktree is dirty").option("-D, --delete-branch", "Also delete the branch").action(async (pathArg, options) => {
|
|
254
|
+
try {
|
|
255
|
+
const repoRoot = getRepoRoot();
|
|
256
|
+
const worktrees = listWorktrees(repoRoot);
|
|
257
|
+
const removableWorktrees = worktrees.filter((w) => !w.isMain);
|
|
258
|
+
if (removableWorktrees.length === 0) {
|
|
259
|
+
console.log(pc.yellow("No removable worktrees found"));
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
let targetPath = pathArg;
|
|
263
|
+
let targetWorktree = removableWorktrees.find((w) => w.path === targetPath);
|
|
264
|
+
if (!targetPath) {
|
|
265
|
+
const selected = await select({
|
|
266
|
+
message: "Select worktree to remove:",
|
|
267
|
+
choices: removableWorktrees.map((wt) => {
|
|
268
|
+
const relativePath2 = relative(process.cwd(), wt.path) || wt.path;
|
|
269
|
+
return {
|
|
270
|
+
name: `${relativePath2} (${wt.branch || "detached"})`,
|
|
271
|
+
value: wt
|
|
272
|
+
};
|
|
273
|
+
})
|
|
274
|
+
});
|
|
275
|
+
targetWorktree = selected;
|
|
276
|
+
targetPath = selected.path;
|
|
277
|
+
}
|
|
278
|
+
if (!targetWorktree) {
|
|
279
|
+
targetWorktree = removableWorktrees.find((w) => {
|
|
280
|
+
const rel = relative(process.cwd(), w.path);
|
|
281
|
+
return rel === targetPath || w.path.endsWith(targetPath);
|
|
282
|
+
});
|
|
283
|
+
if (!targetWorktree) {
|
|
284
|
+
console.error(pc.red(`Worktree not found: ${targetPath}`));
|
|
285
|
+
process.exit(1);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
const relativePath = relative(process.cwd(), targetWorktree.path);
|
|
289
|
+
const shouldRemove = await confirm({
|
|
290
|
+
message: `Remove worktree ${pc.bold(relativePath)}?`,
|
|
291
|
+
default: false
|
|
292
|
+
});
|
|
293
|
+
if (!shouldRemove) {
|
|
294
|
+
console.log(pc.dim("Cancelled"));
|
|
295
|
+
return;
|
|
296
|
+
}
|
|
297
|
+
console.log(pc.dim("Removing worktree..."));
|
|
298
|
+
removeWorktree(targetWorktree.path, options.force, repoRoot);
|
|
299
|
+
console.log(pc.green(`✓ Removed worktree ${relativePath}`));
|
|
300
|
+
if (options.deleteBranch && targetWorktree.branch) {
|
|
301
|
+
const shouldDeleteBranch = await confirm({
|
|
302
|
+
message: `Also delete branch ${pc.bold(targetWorktree.branch)}?`,
|
|
303
|
+
default: false
|
|
304
|
+
});
|
|
305
|
+
if (shouldDeleteBranch) {
|
|
306
|
+
try {
|
|
307
|
+
git(["branch", "-D", targetWorktree.branch], repoRoot);
|
|
308
|
+
console.log(pc.green(`✓ Deleted branch ${targetWorktree.branch}`));
|
|
309
|
+
} catch (error) {
|
|
310
|
+
if (error instanceof Error) {
|
|
311
|
+
console.log(pc.yellow(`⚠ Could not delete branch: ${error.message}`));
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
} catch (error) {
|
|
317
|
+
if (error instanceof Error) {
|
|
318
|
+
if (error.name === "ExitPromptError") {
|
|
319
|
+
return;
|
|
320
|
+
}
|
|
321
|
+
console.error(pc.red(error.message));
|
|
322
|
+
}
|
|
323
|
+
process.exit(1);
|
|
324
|
+
}
|
|
325
|
+
});
|
|
326
|
+
const worktreePlugin = {
|
|
327
|
+
name: "Worktree",
|
|
328
|
+
description: "Manage git worktrees",
|
|
329
|
+
command: "worktree",
|
|
330
|
+
aliases: ["wt"],
|
|
331
|
+
register(program) {
|
|
332
|
+
const worktreeCmd = program.command("worktree").alias("wt").description("Manage git worktrees");
|
|
333
|
+
worktreeCmd.addCommand(createCommand);
|
|
334
|
+
worktreeCmd.addCommand(listCommand);
|
|
335
|
+
worktreeCmd.addCommand(removeCommand);
|
|
336
|
+
worktreeCmd.action(async () => {
|
|
337
|
+
await createCommand.parseAsync([], { from: "user" });
|
|
338
|
+
});
|
|
339
|
+
}
|
|
340
|
+
};
|
|
341
|
+
export {
|
|
342
|
+
branchExists,
|
|
343
|
+
createWorktree,
|
|
344
|
+
getCurrentBranch,
|
|
345
|
+
getRepoRoot,
|
|
346
|
+
git,
|
|
347
|
+
isClean,
|
|
348
|
+
listBranches,
|
|
349
|
+
listWorktrees,
|
|
350
|
+
removeWorktree,
|
|
351
|
+
worktreePlugin
|
|
352
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"create.d.ts","sourceRoot":"","sources":["../../../src/lib/commands/create.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAgBpC,OAAO,aAAa,CAAC;AAOrB,eAAO,MAAM,aAAa,SAyJtB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"list.d.ts","sourceRoot":"","sources":["../../../src/lib/commands/list.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AASpC,eAAO,MAAM,WAAW,SAqCpB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"remove.d.ts","sourceRoot":"","sources":["../../../src/lib/commands/remove.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAWpC,eAAO,MAAM,aAAa,SA+FtB,CAAC"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
export interface Worktree {
|
|
2
|
+
path: string;
|
|
3
|
+
branch: string;
|
|
4
|
+
commit: string;
|
|
5
|
+
isBare: boolean;
|
|
6
|
+
isMain: boolean;
|
|
7
|
+
}
|
|
8
|
+
export interface GitError extends Error {
|
|
9
|
+
stderr?: string;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Execute a git command and return stdout
|
|
13
|
+
*/
|
|
14
|
+
export declare function git(args: string[], cwd?: string): string;
|
|
15
|
+
/**
|
|
16
|
+
* Execute a git command asynchronously with live output
|
|
17
|
+
*/
|
|
18
|
+
export declare function gitAsync(args: string[], cwd?: string): Promise<{
|
|
19
|
+
code: number;
|
|
20
|
+
stdout: string;
|
|
21
|
+
stderr: string;
|
|
22
|
+
}>;
|
|
23
|
+
/**
|
|
24
|
+
* Get the root directory of the git repository
|
|
25
|
+
*/
|
|
26
|
+
export declare function getRepoRoot(cwd?: string): string;
|
|
27
|
+
/**
|
|
28
|
+
* Get the current branch name
|
|
29
|
+
*/
|
|
30
|
+
export declare function getCurrentBranch(cwd?: string): string;
|
|
31
|
+
/**
|
|
32
|
+
* Check if working directory is clean
|
|
33
|
+
*/
|
|
34
|
+
export declare function isClean(cwd?: string): boolean;
|
|
35
|
+
/**
|
|
36
|
+
* List all worktrees
|
|
37
|
+
*/
|
|
38
|
+
export declare function listWorktrees(cwd?: string): Worktree[];
|
|
39
|
+
/**
|
|
40
|
+
* Create a new worktree
|
|
41
|
+
*/
|
|
42
|
+
export declare function createWorktree(path: string, branch: string, options?: {
|
|
43
|
+
newBranch?: boolean;
|
|
44
|
+
baseBranch?: string;
|
|
45
|
+
}, cwd?: string): void;
|
|
46
|
+
/**
|
|
47
|
+
* Remove a worktree
|
|
48
|
+
*/
|
|
49
|
+
export declare function removeWorktree(path: string, force?: boolean, cwd?: string): void;
|
|
50
|
+
/**
|
|
51
|
+
* Get list of local branches
|
|
52
|
+
*/
|
|
53
|
+
export declare function listBranches(cwd?: string): string[];
|
|
54
|
+
/**
|
|
55
|
+
* Check if a branch exists
|
|
56
|
+
*/
|
|
57
|
+
export declare function branchExists(branch: string, cwd?: string): boolean;
|
|
58
|
+
/**
|
|
59
|
+
* Check if path is inside a git worktree
|
|
60
|
+
*/
|
|
61
|
+
export declare function isWorktree(path: string): boolean;
|
|
62
|
+
//# sourceMappingURL=git.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"git.d.ts","sourceRoot":"","sources":["../../src/lib/git.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,OAAO,CAAC;IAChB,MAAM,EAAE,OAAO,CAAC;CACjB;AAED,MAAM,WAAW,QAAS,SAAQ,KAAK;IACrC,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;GAEG;AACH,wBAAgB,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,MAAM,CAexD;AAED;;GAEG;AACH,wBAAgB,QAAQ,CACtB,IAAI,EAAE,MAAM,EAAE,EACd,GAAG,CAAC,EAAE,MAAM,GACX,OAAO,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CAwB3D;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,MAAM,CAEhD;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,MAAM,CAErD;AAED;;GAEG;AACH,wBAAgB,OAAO,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,OAAO,CAG7C;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,QAAQ,EAAE,CAmCtD;AAED;;GAEG;AACH,wBAAgB,cAAc,CAC5B,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,EACd,OAAO,CAAC,EAAE;IAAE,SAAS,CAAC,EAAE,OAAO,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAA;CAAE,EACtD,GAAG,CAAC,EAAE,MAAM,GACX,IAAI,CAcN;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,OAAO,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,IAAI,CAOhF;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAGnD;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,OAAO,CAOlE;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAQhD"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../../src/lib/plugin.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAKhD,eAAO,MAAM,cAAc,EAAE,SAsB5B,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration for the worktree plugin
|
|
3
|
+
*/
|
|
4
|
+
export interface WorktreeConfig {
|
|
5
|
+
/** Directory where worktrees will be created (relative to repo root, default: '../') */
|
|
6
|
+
baseDir?: string;
|
|
7
|
+
/** Files to copy to new worktrees (supports globs) */
|
|
8
|
+
copyFiles?: string[];
|
|
9
|
+
/** Command to run after creating worktree (e.g., 'npm install') */
|
|
10
|
+
postCreate?: string;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Extend PikConfig to include worktree plugin config
|
|
14
|
+
*/
|
|
15
|
+
declare module '@lsst/pik-core' {
|
|
16
|
+
interface PikConfig {
|
|
17
|
+
worktree?: WorktreeConfig;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/lib/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,wFAAwF;IACxF,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,sDAAsD;IACtD,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IACrB,mEAAmE;IACnE,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;GAEG;AACH,OAAO,QAAQ,gBAAgB,CAAC;IAC9B,UAAU,SAAS;QACjB,QAAQ,CAAC,EAAE,cAAc,CAAC;KAC3B;CACF"}
|
package/package.json
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@lsst/pik-plugin-worktree",
|
|
3
|
+
"version": "0.4.2",
|
|
4
|
+
"description": "Git worktree management plugin for pik CLI",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"author": "Yurii Tatar <lsst25@gmail.com>",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/lsst25/pik.git",
|
|
11
|
+
"directory": "packages/plugin-worktree"
|
|
12
|
+
},
|
|
13
|
+
"main": "./dist/index.js",
|
|
14
|
+
"module": "./dist/index.js",
|
|
15
|
+
"types": "./dist/index.d.ts",
|
|
16
|
+
"exports": {
|
|
17
|
+
"./package.json": "./package.json",
|
|
18
|
+
".": {
|
|
19
|
+
"@pik/source": "./src/index.ts",
|
|
20
|
+
"types": "./dist/index.d.ts",
|
|
21
|
+
"import": "./dist/index.js",
|
|
22
|
+
"default": "./dist/index.js"
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
"files": [
|
|
26
|
+
"dist",
|
|
27
|
+
"!**/*.tsbuildinfo"
|
|
28
|
+
],
|
|
29
|
+
"dependencies": {
|
|
30
|
+
"@lsst/pik-core": "*",
|
|
31
|
+
"@inquirer/prompts": "^8.1.0",
|
|
32
|
+
"commander": "^14.0.2",
|
|
33
|
+
"glob": "^10.5.0",
|
|
34
|
+
"picocolors": "^1.1.1"
|
|
35
|
+
}
|
|
36
|
+
}
|