Add worktree start-point support

This commit is contained in:
Dotta
2026-03-11 09:15:27 -05:00
parent 57d8d01079
commit d9492f02d6
4 changed files with 48 additions and 10 deletions

View File

@@ -115,6 +115,28 @@ describe("worktree helpers", () => {
).toEqual(["worktree", "add", "/tmp/feature-branch", "feature-branch"]);
});
it("builds git worktree add args with a start point", () => {
expect(
resolveGitWorktreeAddArgs({
branchName: "my-worktree",
targetPath: "/tmp/my-worktree",
branchExists: false,
startPoint: "public-gh/master",
}),
).toEqual(["worktree", "add", "-b", "my-worktree", "/tmp/my-worktree", "public-gh/master"]);
});
it("uses start point even when a local branch with the same name exists", () => {
expect(
resolveGitWorktreeAddArgs({
branchName: "my-worktree",
targetPath: "/tmp/my-worktree",
branchExists: true,
startPoint: "origin/main",
}),
).toEqual(["worktree", "add", "-b", "my-worktree", "/tmp/my-worktree", "origin/main"]);
});
it("rewrites loopback auth URLs to the new port only", () => {
expect(rewriteLocalUrlPort("http://127.0.0.1:3100", 3110)).toBe("http://127.0.0.1:3110/");
expect(rewriteLocalUrlPort("https://paperclip.example", 3110)).toBe("https://paperclip.example");

View File

@@ -62,7 +62,9 @@ type WorktreeInitOptions = {
force?: boolean;
};
type WorktreeMakeOptions = WorktreeInitOptions;
type WorktreeMakeOptions = WorktreeInitOptions & {
startPoint?: string;
};
type WorktreeEnvOptions = {
config?: string;
@@ -166,11 +168,13 @@ export function resolveGitWorktreeAddArgs(input: {
branchName: string;
targetPath: string;
branchExists: boolean;
startPoint?: string;
}): string[] {
if (input.branchExists) {
if (input.branchExists && !input.startPoint) {
return ["worktree", "add", input.targetPath, input.branchName];
}
return ["worktree", "add", "-b", input.branchName, input.targetPath, "HEAD"];
const commitish = input.startPoint ?? "HEAD";
return ["worktree", "add", "-b", input.branchName, input.targetPath, commitish];
}
function readPidFilePort(postmasterPidFile: string): number | null {
@@ -715,10 +719,25 @@ export async function worktreeMakeCommand(nameArg: string, opts: WorktreeMakeOpt
}
mkdirSync(path.dirname(targetPath), { recursive: true });
if (opts.startPoint) {
const [remote] = opts.startPoint.split("/", 1);
try {
execFileSync("git", ["fetch", remote], {
cwd: sourceCwd,
stdio: ["ignore", "pipe", "pipe"],
});
} catch (error) {
throw new Error(
`Failed to fetch from remote "${remote}": ${extractExecSyncErrorMessage(error) ?? String(error)}`,
);
}
}
const worktreeArgs = resolveGitWorktreeAddArgs({
branchName: name,
targetPath,
branchExists: localBranchExists(sourceCwd, name),
branchExists: !opts.startPoint && localBranchExists(sourceCwd, name),
startPoint: opts.startPoint,
});
const spinner = p.spinner();
@@ -775,6 +794,7 @@ export function registerWorktreeCommands(program: Command): void {
.command("worktree:make")
.description("Create ~/NAME as a git worktree, then initialize an isolated Paperclip instance inside it")
.argument("<name>", "Worktree directory and branch name (created at ~/NAME)")
.option("--start-point <ref>", "Remote ref to base the new branch on (e.g. origin/main)")
.option("--instance <id>", "Explicit isolated instance id")
.option("--home <path>", `Home root for worktree instances (default: ${DEFAULT_WORKTREE_HOME})`)
.option("--from-config <path>", "Source config.json to seed from")