worktree-add 1.2.1 → 1.3.1
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 +58 -22
- package/dist/app/get-unsafe-app-name-reason.d.ts +11 -0
- package/dist/app/get-unsafe-app-name-reason.js +21 -0
- package/dist/app/resolve-apps.d.ts +6 -0
- package/dist/app/resolve-apps.js +31 -0
- package/dist/cli/cleanup-worktree.d.ts +2 -0
- package/dist/cli/cleanup-worktree.js +13 -0
- package/dist/cli/open-worktree-apps.d.ts +5 -0
- package/dist/cli/open-worktree-apps.js +33 -0
- package/dist/cli/register-sigint-handler.d.ts +8 -0
- package/dist/cli/register-sigint-handler.js +19 -0
- package/dist/cli/resolve-worktree-context.d.ts +6 -0
- package/dist/cli/resolve-worktree-context.js +22 -0
- package/dist/cli/run-worktree-add.d.ts +9 -0
- package/dist/cli/run-worktree-add.js +120 -0
- package/dist/cli.d.ts +0 -6
- package/dist/cli.js +38 -76
- package/dist/git/create-worktree.d.ts +9 -0
- package/dist/git/create-worktree.js +39 -0
- package/dist/git/extract-diagnostic-line.d.ts +1 -0
- package/dist/git/extract-diagnostic-line.js +13 -0
- package/dist/git/fetch-remote-branch.d.ts +21 -0
- package/dist/git/fetch-remote-branch.js +106 -0
- package/dist/git/git.d.ts +11 -5
- package/dist/git/git.js +25 -17
- package/dist/git/remove-worktree.d.ts +1 -0
- package/dist/git/remove-worktree.js +4 -0
- package/dist/output/create-status-logger.d.ts +13 -0
- package/dist/output/create-status-logger.js +32 -0
- package/dist/output/get-status-logger.d.ts +2 -0
- package/dist/output/get-status-logger.js +12 -0
- package/dist/project/install-commands.js +2 -2
- package/dist/project/next.d.ts +4 -1
- package/dist/project/next.js +2 -2
- package/dist/project/package-manager.d.ts +7 -1
- package/dist/project/package-manager.js +12 -3
- package/dist/project/setup.d.ts +5 -1
- package/dist/project/setup.js +15 -7
- package/dist/worktree/destination-directory.d.ts +10 -3
- package/dist/worktree/destination-directory.js +94 -16
- package/dist/worktree/untracked-file-copy.d.ts +5 -1
- package/dist/worktree/untracked-file-copy.js +51 -9
- package/package.json +4 -2
- package/dist/git/parse-branch-head-mismatch-choice.d.ts +0 -7
- package/dist/git/parse-branch-head-mismatch-choice.js +0 -26
- package/dist/git/resolve-branch-head-mismatch.d.ts +0 -6
- package/dist/git/resolve-branch-head-mismatch.js +0 -79
- package/dist/git/worktree-creation.d.ts +0 -13
- package/dist/git/worktree-creation.js +0 -35
|
@@ -15,31 +15,109 @@
|
|
|
15
15
|
* may not be visible in a GUI. Files can be recovered by navigating to
|
|
16
16
|
* the platform-specific trash location.
|
|
17
17
|
*/
|
|
18
|
-
import trash from "trash";
|
|
19
18
|
import path from "node:path";
|
|
20
|
-
import
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
*/
|
|
25
|
-
export async function handleExistingDirectory(destinationDirectory) {
|
|
19
|
+
import trash from "trash";
|
|
20
|
+
import { fileExists, confirm, exitWithMessage, git } from "../git/git.js";
|
|
21
|
+
import { getStatusLogger } from "../output/get-status-logger.js";
|
|
22
|
+
export async function handleExistingDirectory(destinationDirectory, options = {}) {
|
|
26
23
|
if (!(await fileExists(destinationDirectory))) {
|
|
27
|
-
return;
|
|
24
|
+
return true;
|
|
25
|
+
}
|
|
26
|
+
const logger = getStatusLogger(options.logger);
|
|
27
|
+
const dryRun = options.dryRun ?? false;
|
|
28
|
+
const assumeYes = options.assumeYes ?? false;
|
|
29
|
+
const interactive = options.interactive ?? false;
|
|
30
|
+
const isTty = process.stdin.isTTY;
|
|
31
|
+
const ciValue = process.env.CI?.toLowerCase();
|
|
32
|
+
const isCi = ciValue === "true" || ciValue === "1";
|
|
33
|
+
const directoryName = path.basename(destinationDirectory);
|
|
34
|
+
if (dryRun) {
|
|
35
|
+
if (!assumeYes) {
|
|
36
|
+
// Explicit --interactive in CI without a TTY can't prompt; call it out.
|
|
37
|
+
if (isCi && interactive && !isTty) {
|
|
38
|
+
logger.warn(`Dry run: directory '${directoryName}' already exists, and CI mode is enabled. Interactive prompts are disabled in CI. Re-run with --yes to move the directory to trash, or remove it manually.`);
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
if (!interactive) {
|
|
42
|
+
const ciHint = isCi ? " (CI is enabled)" : "";
|
|
43
|
+
logger.warn(`Dry run: directory '${directoryName}' already exists${ciHint}. Refusing to prompt in non-interactive mode. Re-run with --interactive to confirm, or --yes to move it to trash.`);
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
if (!isTty) {
|
|
47
|
+
logger.warn(`Dry run: directory '${directoryName}' already exists, but stdin is not a TTY. Re-run with --yes to move it to trash, or remove it manually.`);
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
logger.step(`Would prompt to move existing directory '${directoryName}' to trash`);
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
logger.step(`Would move existing directory '${directoryName}' to trash`);
|
|
54
|
+
return true;
|
|
55
|
+
}
|
|
56
|
+
if (!assumeYes) {
|
|
57
|
+
// Explicit --interactive in CI without a TTY can't prompt; call it out.
|
|
58
|
+
if (isCi && interactive && !isTty) {
|
|
59
|
+
exitWithMessage(`Directory '${directoryName}' already exists, and CI mode is enabled.\n` +
|
|
60
|
+
"Interactive prompts are disabled in CI.\n" +
|
|
61
|
+
"Re-run with --yes to move the directory to trash, or remove it manually.");
|
|
62
|
+
}
|
|
63
|
+
if (!interactive) {
|
|
64
|
+
const ciHint = isCi ? " (CI is enabled)" : "";
|
|
65
|
+
exitWithMessage(`Directory '${directoryName}' already exists${ciHint}.\n` +
|
|
66
|
+
"Refusing to prompt in non-interactive mode.\n" +
|
|
67
|
+
"Re-run with --interactive to confirm, or --yes to move it to trash.");
|
|
68
|
+
}
|
|
69
|
+
if (!isTty) {
|
|
70
|
+
exitWithMessage(`Directory '${directoryName}' already exists, but stdin is not a TTY.\n` +
|
|
71
|
+
"Re-run with --yes to move it to trash, or remove it manually.");
|
|
72
|
+
}
|
|
73
|
+
const proceed = await confirm(`Directory '${directoryName}' already exists. Move to trash and recreate? (You can restore it from your system trash if needed)`);
|
|
74
|
+
if (!proceed) {
|
|
75
|
+
console.error("Operation cancelled.");
|
|
76
|
+
// eslint-disable-next-line unicorn/no-process-exit -- User requested cancellation
|
|
77
|
+
process.exit(0);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
const resolvedDestination = path.resolve(destinationDirectory);
|
|
81
|
+
let shouldPruneWorktree = false;
|
|
82
|
+
try {
|
|
83
|
+
const worktreeList = git("worktree", "list", "--porcelain");
|
|
84
|
+
const lines = worktreeList.split(/\n/u);
|
|
85
|
+
for (const line of lines) {
|
|
86
|
+
if (!line.startsWith("worktree "))
|
|
87
|
+
continue;
|
|
88
|
+
const worktreePath = line.replace(/^worktree\s+/u, "").trim();
|
|
89
|
+
if (path.resolve(worktreePath) === resolvedDestination) {
|
|
90
|
+
shouldPruneWorktree = true;
|
|
91
|
+
break;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
28
94
|
}
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
// eslint-disable-next-line unicorn/no-process-exit -- User requested cancellation
|
|
33
|
-
process.exit(0);
|
|
95
|
+
catch (error) {
|
|
96
|
+
const details = error instanceof Error ? (error.stack ?? error.message) : String(error);
|
|
97
|
+
logger.detail(`Error checking for existing worktree registration: ${details}`);
|
|
34
98
|
}
|
|
35
99
|
// Move the existing directory to trash
|
|
36
|
-
|
|
100
|
+
logger.step(`Moving existing directory '${directoryName}' to trash...`);
|
|
37
101
|
try {
|
|
38
102
|
await trash(destinationDirectory);
|
|
39
|
-
|
|
103
|
+
logger.success("Directory moved to trash successfully");
|
|
40
104
|
}
|
|
41
105
|
catch (error) {
|
|
42
|
-
|
|
106
|
+
const details = error instanceof Error ? (error.stack ?? error.message) : String(error);
|
|
107
|
+
logger.detail(`Error details: ${details}`);
|
|
43
108
|
exitWithMessage(`Failed to move existing directory to trash: ${error instanceof Error ? error.message : String(error)}`);
|
|
44
109
|
}
|
|
110
|
+
if (!shouldPruneWorktree) {
|
|
111
|
+
return true;
|
|
112
|
+
}
|
|
113
|
+
logger.detail(`Pruning stale worktree registration for '${directoryName}'.`);
|
|
114
|
+
try {
|
|
115
|
+
git("worktree", "prune");
|
|
116
|
+
}
|
|
117
|
+
catch (error) {
|
|
118
|
+
const details = error instanceof Error ? (error.stack ?? error.message) : String(error);
|
|
119
|
+
logger.detail(`Error details: ${details}`);
|
|
120
|
+
exitWithMessage(`Failed to prune stale worktree registration for '${directoryName}'. Run 'git worktree prune' and retry.`);
|
|
121
|
+
}
|
|
122
|
+
return true;
|
|
45
123
|
}
|
|
@@ -3,7 +3,11 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Utilities for copying untracked files to worktrees
|
|
5
5
|
*/
|
|
6
|
+
import type { StatusLogger } from "../output/create-status-logger.js";
|
|
6
7
|
/**
|
|
7
8
|
* Copy untracked files from source to destination, excluding ignored patterns
|
|
8
9
|
*/
|
|
9
|
-
export declare function copyUntrackedFiles(repoRoot: string, destinationDirectory: string
|
|
10
|
+
export declare function copyUntrackedFiles(repoRoot: string, destinationDirectory: string, options?: {
|
|
11
|
+
dryRun?: boolean;
|
|
12
|
+
logger?: StatusLogger;
|
|
13
|
+
}): Promise<void>;
|
|
@@ -7,29 +7,71 @@ import * as fs from "node:fs/promises";
|
|
|
7
7
|
import path from "node:path";
|
|
8
8
|
import { git } from "../git/git.js";
|
|
9
9
|
import { EXTRA_IGNORED_PATTERNS, globToRegExp, toPosixPath, } from "./file-patterns.js";
|
|
10
|
+
import { getStatusLogger } from "../output/get-status-logger.js";
|
|
10
11
|
/**
|
|
11
12
|
* Copy untracked files from source to destination, excluding ignored patterns
|
|
12
13
|
*/
|
|
13
|
-
export async function copyUntrackedFiles(repoRoot, destinationDirectory) {
|
|
14
|
+
export async function copyUntrackedFiles(repoRoot, destinationDirectory, options) {
|
|
15
|
+
const logger = getStatusLogger(options?.logger);
|
|
16
|
+
const dryRun = options?.dryRun ?? false;
|
|
14
17
|
const extraIgnoredRegexes = EXTRA_IGNORED_PATTERNS.map((pattern) => globToRegExp(pattern));
|
|
15
|
-
const untrackedEntries =
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
18
|
+
const untrackedEntries = new Set();
|
|
19
|
+
// Combine untracked-not-ignored (`--others`) and untracked-ignored (`--others --ignored`) entries.
|
|
20
|
+
// These sets should be disjoint, but we dedupe defensively.
|
|
21
|
+
const untrackedPaths = [
|
|
22
|
+
...git("ls-files", "--others", "--exclude-standard", "--full-name", "-z", // null-separated output for safe parsing
|
|
23
|
+
{ cwd: repoRoot })
|
|
24
|
+
.split("\0")
|
|
25
|
+
.filter(Boolean),
|
|
26
|
+
...git("ls-files", "--others", "--ignored", "--exclude-standard", "--full-name", "-z", // null-separated output for safe parsing
|
|
27
|
+
{ cwd: repoRoot })
|
|
28
|
+
.split("\0")
|
|
29
|
+
.filter(Boolean),
|
|
30
|
+
];
|
|
31
|
+
for (const entry of untrackedPaths) {
|
|
32
|
+
untrackedEntries.add(entry);
|
|
33
|
+
}
|
|
21
34
|
for (const relativePath of untrackedEntries) {
|
|
35
|
+
const resolvedSourcePath = path.resolve(repoRoot, relativePath);
|
|
36
|
+
const relativeToRoot = path.relative(repoRoot, resolvedSourcePath);
|
|
37
|
+
const escapesRepoRoot = path.isAbsolute(relativeToRoot) ||
|
|
38
|
+
relativeToRoot === ".." ||
|
|
39
|
+
relativeToRoot.startsWith(`..${path.sep}`);
|
|
40
|
+
if (escapesRepoRoot) {
|
|
41
|
+
logger.warn(`Skipping ${relativePath} (path escapes repo root).`);
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
22
44
|
const posixPath = toPosixPath(relativePath);
|
|
23
45
|
if (extraIgnoredRegexes.some((regex) => regex.test(posixPath))) {
|
|
24
46
|
continue;
|
|
25
47
|
}
|
|
26
|
-
const sourcePath =
|
|
48
|
+
const sourcePath = resolvedSourcePath;
|
|
27
49
|
const destinationPath = path.join(destinationDirectory, relativePath);
|
|
50
|
+
const destinationExists = await fs
|
|
51
|
+
.stat(destinationPath)
|
|
52
|
+
.then(() => true)
|
|
53
|
+
.catch((error) => {
|
|
54
|
+
if (error &&
|
|
55
|
+
typeof error === "object" &&
|
|
56
|
+
"code" in error &&
|
|
57
|
+
error.code === "ENOENT") {
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
throw error;
|
|
61
|
+
});
|
|
62
|
+
if (destinationExists) {
|
|
63
|
+
logger.detail(`Skipped ${relativePath} (destination already exists).`);
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
if (dryRun) {
|
|
67
|
+
logger.detail(`Would copy ${relativePath}`);
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
28
70
|
await fs.mkdir(path.dirname(destinationPath), { recursive: true });
|
|
29
71
|
await fs.cp(sourcePath, destinationPath, {
|
|
30
72
|
recursive: true,
|
|
31
73
|
errorOnExist: false,
|
|
32
74
|
});
|
|
33
|
-
|
|
75
|
+
logger.detail(`Copied ${relativePath}`);
|
|
34
76
|
}
|
|
35
77
|
}
|
package/package.json
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
"name": "worktree-add",
|
|
3
3
|
"author": "Łukasz Jerciński",
|
|
4
4
|
"license": "MIT",
|
|
5
|
-
"version": "1.
|
|
6
|
-
"description": "Create a Git worktree next to your current repo for a branch, copy useful local files, install deps, and open it in your
|
|
5
|
+
"version": "1.3.1",
|
|
6
|
+
"description": "Create a Git worktree next to your current repo for a branch, copy useful local files, install deps, and open it in your apps.",
|
|
7
7
|
"repository": {
|
|
8
8
|
"type": "git",
|
|
9
9
|
"url": "git+https://github.com/Jercik/worktree-add.git"
|
|
@@ -52,7 +52,9 @@
|
|
|
52
52
|
"node": ">=22.14.0"
|
|
53
53
|
},
|
|
54
54
|
"dependencies": {
|
|
55
|
+
"@commander-js/extra-typings": "^14.0.0",
|
|
55
56
|
"commander": "^14.0.2",
|
|
57
|
+
"open": "^11.0.0",
|
|
56
58
|
"package-manager-detector": "^1.6.0",
|
|
57
59
|
"trash": "^10.0.1"
|
|
58
60
|
},
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* parse-branch-head-mismatch-choice.ts
|
|
3
|
-
*
|
|
4
|
-
* Pure helpers for parsing interactive choices in the branch-head mismatch flow.
|
|
5
|
-
*/
|
|
6
|
-
export type ResolutionChoice = "keep-local" | "update-local" | "abort";
|
|
7
|
-
export declare function parseResolutionChoice(input: string, defaultChoice: ResolutionChoice): ResolutionChoice | undefined;
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* parse-branch-head-mismatch-choice.ts
|
|
3
|
-
*
|
|
4
|
-
* Pure helpers for parsing interactive choices in the branch-head mismatch flow.
|
|
5
|
-
*/
|
|
6
|
-
export function parseResolutionChoice(input, defaultChoice) {
|
|
7
|
-
const normalized = input.trim().toLowerCase();
|
|
8
|
-
if (normalized === "")
|
|
9
|
-
return defaultChoice;
|
|
10
|
-
if (normalized === "1" || normalized === "keep" || normalized === "local") {
|
|
11
|
-
return "keep-local";
|
|
12
|
-
}
|
|
13
|
-
if (normalized === "2" ||
|
|
14
|
-
normalized === "update" ||
|
|
15
|
-
normalized === "remote") {
|
|
16
|
-
return "update-local";
|
|
17
|
-
}
|
|
18
|
-
if (normalized === "3" ||
|
|
19
|
-
normalized === "abort" ||
|
|
20
|
-
normalized === "a" ||
|
|
21
|
-
normalized === "q" ||
|
|
22
|
-
normalized === "quit") {
|
|
23
|
-
return "abort";
|
|
24
|
-
}
|
|
25
|
-
return undefined;
|
|
26
|
-
}
|
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* resolve-branch-head-mismatch.ts
|
|
3
|
-
*
|
|
4
|
-
* Warn and resolve mismatched local/remote branch heads.
|
|
5
|
-
*/
|
|
6
|
-
import { confirm, fetchOriginBranch, getLocalBranchHead, getRemoteBranchHead, git, localBranchExists, normalizeBranchName, prompt, remoteBranchExists, } from "./git.js";
|
|
7
|
-
import { parseResolutionChoice } from "./parse-branch-head-mismatch-choice.js";
|
|
8
|
-
const shortHash = (value) => value.slice(0, 7);
|
|
9
|
-
const getAheadBehindCounts = (localHead, remoteHead) => {
|
|
10
|
-
const output = git("rev-list", "--left-right", "--count", `${localHead}...${remoteHead}`);
|
|
11
|
-
const [aheadRaw, behindRaw] = output.split(/\s+/u);
|
|
12
|
-
const ahead = Number.parseInt(aheadRaw ?? "0");
|
|
13
|
-
const behind = Number.parseInt(behindRaw ?? "0");
|
|
14
|
-
return {
|
|
15
|
-
ahead: Number.isNaN(ahead) ? 0 : ahead,
|
|
16
|
-
behind: Number.isNaN(behind) ? 0 : behind,
|
|
17
|
-
};
|
|
18
|
-
};
|
|
19
|
-
const promptResolution = async (branch, localHead, remoteHead, ahead, behind) => {
|
|
20
|
-
const updateLabel = ahead > 0
|
|
21
|
-
? `Update local branch to match origin (drops ${ahead} local commit${ahead === 1 ? "" : "s"})`
|
|
22
|
-
: "Update local branch to match origin (fast-forward)";
|
|
23
|
-
const defaultChoice = ahead === 0 && behind > 0 ? "update-local" : "keep-local";
|
|
24
|
-
const defaultIndex = defaultChoice === "update-local" ? "2" : "1";
|
|
25
|
-
console.warn(`⚠️ Local branch '${branch}' and origin/${branch} point to different commits.`);
|
|
26
|
-
console.warn(` local: ${shortHash(localHead)}`);
|
|
27
|
-
console.warn(` origin: ${shortHash(remoteHead)}`);
|
|
28
|
-
if (ahead > 0 || behind > 0) {
|
|
29
|
-
const descriptors = [];
|
|
30
|
-
if (ahead > 0)
|
|
31
|
-
descriptors.push(`ahead by ${ahead}`);
|
|
32
|
-
if (behind > 0)
|
|
33
|
-
descriptors.push(`behind by ${behind}`);
|
|
34
|
-
console.warn(` local is ${descriptors.join(" and ")} relative to origin`);
|
|
35
|
-
}
|
|
36
|
-
console.warn("Choose how to proceed:");
|
|
37
|
-
console.warn(" 1) Keep local branch (use local head for the worktree)");
|
|
38
|
-
console.warn(` 2) ${updateLabel}`);
|
|
39
|
-
console.warn(" 3) Abort");
|
|
40
|
-
for (;;) {
|
|
41
|
-
const answer = await prompt(`Select an option (default: ${defaultIndex}): `);
|
|
42
|
-
const choice = parseResolutionChoice(answer, defaultChoice);
|
|
43
|
-
if (choice)
|
|
44
|
-
return choice;
|
|
45
|
-
console.warn("Please enter 1, 2, or 3.");
|
|
46
|
-
}
|
|
47
|
-
};
|
|
48
|
-
export async function resolveBranchHeadMismatch(branch) {
|
|
49
|
-
const normalized = normalizeBranchName(branch);
|
|
50
|
-
if (!localBranchExists(normalized) || !remoteBranchExists(normalized)) {
|
|
51
|
-
return true;
|
|
52
|
-
}
|
|
53
|
-
console.warn(`➤ Fetching origin/${normalized} to check for divergence …`);
|
|
54
|
-
fetchOriginBranch(normalized);
|
|
55
|
-
const localHead = getLocalBranchHead(normalized);
|
|
56
|
-
const remoteHead = getRemoteBranchHead(normalized);
|
|
57
|
-
if (!localHead || !remoteHead || localHead === remoteHead) {
|
|
58
|
-
return true;
|
|
59
|
-
}
|
|
60
|
-
const { ahead, behind } = getAheadBehindCounts(localHead, remoteHead);
|
|
61
|
-
const resolution = await promptResolution(normalized, localHead, remoteHead, ahead, behind);
|
|
62
|
-
if (resolution === "abort") {
|
|
63
|
-
console.warn("Operation cancelled.");
|
|
64
|
-
return false;
|
|
65
|
-
}
|
|
66
|
-
if (resolution === "keep-local") {
|
|
67
|
-
return true;
|
|
68
|
-
}
|
|
69
|
-
if (ahead > 0) {
|
|
70
|
-
const confirmed = await confirm(`Updating local '${normalized}' will discard ${ahead} local commit${ahead === 1 ? "" : "s"} not on origin. Continue?`);
|
|
71
|
-
if (!confirmed) {
|
|
72
|
-
console.warn("Operation cancelled.");
|
|
73
|
-
return false;
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
console.warn(`➤ Updating local branch '${normalized}' to origin/${normalized} …`);
|
|
77
|
-
git("branch", "-f", "--", normalized, `origin/${normalized}`);
|
|
78
|
-
return true;
|
|
79
|
-
}
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* worktree-creation.ts
|
|
3
|
-
*
|
|
4
|
-
* Utilities for creating Git worktrees
|
|
5
|
-
*/
|
|
6
|
-
/**
|
|
7
|
-
* Fetch a remote branch if it exists remotely but not locally.
|
|
8
|
-
*/
|
|
9
|
-
export declare function fetchRemoteBranch(branch: string): void;
|
|
10
|
-
/**
|
|
11
|
-
* Create a worktree for the given branch at the specified destination
|
|
12
|
-
*/
|
|
13
|
-
export declare function createWorktree(branch: string, destinationDirectory: string): void;
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* worktree-creation.ts
|
|
3
|
-
*
|
|
4
|
-
* Utilities for creating Git worktrees
|
|
5
|
-
*/
|
|
6
|
-
import { fetchOriginBranch, git, localBranchExists, remoteBranchExists, } from "./git.js";
|
|
7
|
-
/**
|
|
8
|
-
* Fetch a remote branch if it exists remotely but not locally.
|
|
9
|
-
*/
|
|
10
|
-
export function fetchRemoteBranch(branch) {
|
|
11
|
-
if (!localBranchExists(branch) && remoteBranchExists(branch)) {
|
|
12
|
-
console.log(`➤ Fetching origin/${branch} …`);
|
|
13
|
-
fetchOriginBranch(branch);
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
/**
|
|
17
|
-
* Create a worktree for the given branch at the specified destination
|
|
18
|
-
*/
|
|
19
|
-
export function createWorktree(branch, destinationDirectory) {
|
|
20
|
-
if (localBranchExists(branch)) {
|
|
21
|
-
// Reuse existing local branch
|
|
22
|
-
console.log(`➤ git worktree add ${destinationDirectory} ${branch}`);
|
|
23
|
-
git("worktree", "add", destinationDirectory, branch);
|
|
24
|
-
}
|
|
25
|
-
else if (remoteBranchExists(branch)) {
|
|
26
|
-
// Create new local branch tracking the remote branch
|
|
27
|
-
console.log(`➤ git worktree add --track -b ${branch} ${destinationDirectory} origin/${branch}`);
|
|
28
|
-
git("worktree", "add", "--track", "-b", branch, destinationDirectory, `origin/${branch}`);
|
|
29
|
-
}
|
|
30
|
-
else {
|
|
31
|
-
// Create new branch in the worktree from current HEAD
|
|
32
|
-
console.log(`➤ git worktree add -b ${branch} ${destinationDirectory}`);
|
|
33
|
-
git("worktree", "add", "-b", branch, destinationDirectory);
|
|
34
|
-
}
|
|
35
|
-
}
|