git-worktree-organize 1.0.12 → 1.0.13
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/dist/cli.js +93 -12
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -113,7 +113,7 @@ async function listWorktrees(repoPath) {
|
|
|
113
113
|
}
|
|
114
114
|
|
|
115
115
|
// src/migrate.ts
|
|
116
|
-
import { mkdirSync, writeFileSync, readFileSync as readFileSync2, existsSync as existsSync2, renameSync, statSync as statSync2 } from "node:fs";
|
|
116
|
+
import { mkdirSync, writeFileSync, readFileSync as readFileSync2, existsSync as existsSync2, renameSync, statSync as statSync2, readdirSync } from "node:fs";
|
|
117
117
|
import { join as join2, dirname, basename, resolve as resolve2 } from "node:path";
|
|
118
118
|
|
|
119
119
|
// src/git.ts
|
|
@@ -158,23 +158,74 @@ function isPartialMigration(dest) {
|
|
|
158
158
|
const gitFile = join2(dest, ".git");
|
|
159
159
|
return existsSync2(join2(dest, ".bare")) && existsSync2(gitFile) && statSync2(gitFile).isFile();
|
|
160
160
|
}
|
|
161
|
+
function findHub(startPath) {
|
|
162
|
+
let current = resolve2(startPath);
|
|
163
|
+
while (true) {
|
|
164
|
+
if (isPartialMigration(current))
|
|
165
|
+
return current;
|
|
166
|
+
const parent = dirname(current);
|
|
167
|
+
if (parent === current)
|
|
168
|
+
return null;
|
|
169
|
+
current = parent;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
async function repairHub(dest, log = console.log) {
|
|
173
|
+
const adminBase = join2(dest, ".bare", "worktrees");
|
|
174
|
+
if (!existsSync2(adminBase))
|
|
175
|
+
return;
|
|
176
|
+
for (const adminName of readdirSync(adminBase)) {
|
|
177
|
+
const adminDir = join2(adminBase, adminName);
|
|
178
|
+
if (!statSync2(adminDir).isDirectory())
|
|
179
|
+
continue;
|
|
180
|
+
const gitdirFile = join2(adminDir, "gitdir");
|
|
181
|
+
if (!existsSync2(gitdirFile))
|
|
182
|
+
continue;
|
|
183
|
+
const registeredGitFile = readFileSync2(gitdirFile, "utf8").trim();
|
|
184
|
+
const worktreePath = dirname(registeredGitFile);
|
|
185
|
+
if (!worktreePath.startsWith(dest + "/"))
|
|
186
|
+
continue;
|
|
187
|
+
if (!existsSync2(registeredGitFile) || !statSync2(registeredGitFile).isFile())
|
|
188
|
+
continue;
|
|
189
|
+
const content = readFileSync2(registeredGitFile, "utf8");
|
|
190
|
+
const match = content.match(/^gitdir:\s*(.+)/m);
|
|
191
|
+
if (!match)
|
|
192
|
+
continue;
|
|
193
|
+
if (match[1].trim() === adminDir)
|
|
194
|
+
continue;
|
|
195
|
+
log(`Repairing .git for [${basename(worktreePath)}]`);
|
|
196
|
+
writeFileSync(registeredGitFile, `gitdir: ${adminDir}
|
|
197
|
+
`);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
161
200
|
async function resumeMigrate(dest, log = console.log) {
|
|
162
201
|
const destBare = join2(dest, ".bare");
|
|
163
202
|
const hubWorktrees = await listWorktrees(dest);
|
|
164
|
-
const pending = hubWorktrees.filter((wt) =>
|
|
203
|
+
const pending = hubWorktrees.filter((wt) => {
|
|
204
|
+
if (wt.isBare)
|
|
205
|
+
return false;
|
|
206
|
+
const branch = wt.branch ?? `detached-${wt.head.slice(0, 8)}`;
|
|
207
|
+
return wt.path !== join2(dest, sanitizeBranch(branch));
|
|
208
|
+
});
|
|
165
209
|
if (pending.length === 0) {
|
|
166
210
|
log("Nothing to resume — all worktrees are already in place.");
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
211
|
+
} else {
|
|
212
|
+
for (const wt of pending) {
|
|
213
|
+
const branch = wt.branch ?? `detached-${wt.head.slice(0, 8)}`;
|
|
214
|
+
const expectedPath = join2(dest, sanitizeBranch(branch));
|
|
215
|
+
let wtPath = wt.path;
|
|
216
|
+
if (!existsSync2(wtPath)) {
|
|
217
|
+
if (existsSync2(expectedPath)) {
|
|
218
|
+
wtPath = expectedPath;
|
|
219
|
+
} else {
|
|
220
|
+
log(`warn: Skipping [${branch}] — path no longer exists: ${wt.path}`);
|
|
221
|
+
continue;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
log(`Moving [${branch}] → ${expectedPath}`);
|
|
225
|
+
await processLinkedWorktree({ ...wt, path: wtPath }, dest, destBare);
|
|
174
226
|
}
|
|
175
|
-
log(`Moving [${branch}] → ${join2(dest, sanitizeBranch(branch))}`);
|
|
176
|
-
await processLinkedWorktree(wt, dest, destBare);
|
|
177
227
|
}
|
|
228
|
+
await repairHub(dest, log);
|
|
178
229
|
return dest;
|
|
179
230
|
}
|
|
180
231
|
async function migrate(config, options) {
|
|
@@ -307,9 +358,39 @@ async function main() {
|
|
|
307
358
|
const destArg = args[1];
|
|
308
359
|
const source = resolve3(sourcePath);
|
|
309
360
|
const dest = isPartialMigration(source) ? source : destArg ? resolve3(destArg) : join3(dirname2(source), basename2(source) + "-bare");
|
|
361
|
+
if (!isPartialMigration(source) && !destArg) {
|
|
362
|
+
const ancestorHub = findHub(dirname2(source));
|
|
363
|
+
if (ancestorHub) {
|
|
364
|
+
console.log(`
|
|
365
|
+
${yellow("warn:")} ${bold(source)} is inside an existing hub at ${bold(ancestorHub)}`);
|
|
366
|
+
console.log(`
|
|
367
|
+
This looks like manually-placed worktrees with stale .git files.`);
|
|
368
|
+
console.log(`Running repair will fix all worktree .git connections in the hub.
|
|
369
|
+
`);
|
|
370
|
+
process.stdout.write(`Repair hub at ${ancestorHub}? [y/N] `);
|
|
371
|
+
const repairAns = await prompt();
|
|
372
|
+
process.stdin.destroy();
|
|
373
|
+
if (!/^[Yy]$/.test(repairAns)) {
|
|
374
|
+
console.log("Aborted.");
|
|
375
|
+
process.exit(0);
|
|
376
|
+
}
|
|
377
|
+
console.log();
|
|
378
|
+
await repairHub(ancestorHub, (msg) => console.log(`${green("==>")} ${msg}`));
|
|
379
|
+
console.log();
|
|
380
|
+
console.log(`${green("==>")} Verifying with git worktree list...`);
|
|
381
|
+
console.log(run("git", ["-C", ancestorHub, "worktree", "list"]).stdout);
|
|
382
|
+
console.log(`Done! Hub: ${ancestorHub}`);
|
|
383
|
+
process.exit(0);
|
|
384
|
+
}
|
|
385
|
+
}
|
|
310
386
|
if (isPartialMigration(dest)) {
|
|
311
387
|
const hubWorktrees = await listWorktrees(dest);
|
|
312
|
-
const pending = hubWorktrees.filter((wt) =>
|
|
388
|
+
const pending = hubWorktrees.filter((wt) => {
|
|
389
|
+
if (wt.isBare)
|
|
390
|
+
return false;
|
|
391
|
+
const branch = wt.branch ?? `detached-${wt.head.slice(0, 8)}`;
|
|
392
|
+
return wt.path !== join3(dest, sanitizeBranch(branch));
|
|
393
|
+
});
|
|
313
394
|
console.log(`
|
|
314
395
|
${yellow("warn:")} Partial migration detected at ${bold(dest)}`);
|
|
315
396
|
if (pending.length === 0) {
|