Allow verified local-only Boat worktree bases

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
DottaandPaperclip committed 2026-10-11 10:21:36 -05:00
1 parent e72f29cd85
commit d2bd3b8f6f
2 files changed
+65 -3

No files matched your search

@@ -840,6 +840,61 @@ process.stdout.write(result.stdout||"");process.stderr.write(result.stderr||"");
expect(existsSync(join(f.root, "tasks", "missing"))).toBe(false);
expect(spawnSync("git", ["show-ref", "--verify", "refs/heads/paperclip/missing"], { cwd: checkout }).status).not.toBe(0);
});
it.each(["unpublished", "feature/unpublished"])("uses a verified unpushed local base only after proving its remote absence: %s", branch => {
const f = fixture(), source = join(f.temp, "source"); mkdirSync(source);
const git = (cwd: string, ...args: string[]) => {
const result = spawnSync("git", args, { cwd, encoding: "utf8" });
if (result.status !== 0) throw new Error(result.stderr);
return result.stdout.trim();
};
const commit = (cwd: string, message: string) => git(cwd, "-c", "user.name=Test", "-c", "user.email=test@example.com", "commit", "--allow-empty", "-m", message);
git(source, "init", "-b", "main"); commit(source, "default");
const repositoryUrl = "https://fixture.invalid/local-base.git";
const gitAuth = { configArgs: ["-c", `url.file://${source}.insteadOf=${repositoryUrl}`, "-c", "protocol.file.allow=always"], env: {} };
const shared = { action: "workspace", mode: "shared", repositoryUrl, gitAuth };
const checkout = f.call(shared).remoteCwd;
git(checkout, "checkout", "-b", branch); commit(checkout, "unpushed agent work");
const localCommit = git(checkout, "rev-parse", "HEAD");
writeFileSync(join(checkout, "uncommitted"), "keep");
const request = { ...shared, mode: "worktree", baseRef: branch, taskId: "local-base", branch: "paperclip/local-base" };
const result = f.call(request);
expect(result.remoteCwd).toBe(join(f.root, "tasks", "local-base"));
expect(git(result.remoteCwd, "rev-parse", "HEAD")).toBe(localCommit);
const deniedAuth = { ...gitAuth, configArgs: [...gitAuth.configArgs, "-c", "protocol.file.allow=never"] };
expect(f.call({ ...request, gitAuth: deniedAuth, taskId: "unknown-remote", branch: "paperclip/unknown-remote" })).toEqual({ error: "command_failed" });
expect(existsSync(join(f.root, "tasks", "unknown-remote"))).toBe(false);
for (const baseRef of [`origin/${branch}`, `refs/remotes/origin/${branch}`]) {
expect(f.call({ ...request, baseRef, taskId: "remote-only", branch: "paperclip/remote-only" })).toEqual({ error: "command_failed" });
}
// Explicit local-ref intent does not depend on the remote's availability.
const explicit = f.call({ ...request, gitAuth: deniedAuth, baseRef: `refs/heads/${branch}`, taskId: "explicit-local", branch: "paperclip/explicit-local" });
expect(git(explicit.remoteCwd, "rev-parse", "HEAD")).toBe(localCommit);
git(source, "checkout", "-b", branch); commit(source, "published remote work");
const remoteCommit = git(source, "rev-parse", "HEAD");
const remote = f.call({ ...request, taskId: "remote-base", branch: "paperclip/remote-base" });
expect(git(remote.remoteCwd, "rev-parse", "HEAD")).toBe(remoteCommit);
const bin = join(f.temp, "bin"); mkdirSync(bin);
const realGit = spawnSync("which", ["git"], { encoding: "utf8" }).stdout.trim();
const probe = join(f.temp, "remote-probe"), fetch = join(f.temp, "fetch-attempt");
writeFileSync(join(bin, "git"), `#!${process.execPath}
const fs=require("node:fs"),{spawnSync}=require("node:child_process"),args=process.argv.slice(2);
if(args.includes("fetch")){fs.writeFileSync(process.env.FIXTURE_FETCH,"attempted");process.exit(128);}
const result=spawnSync(process.env.FIXTURE_GIT,args,{encoding:"utf8"});
if(args.includes("ls-remote")&&result.status===0)fs.writeFileSync(process.env.FIXTURE_PROBE,"present");
process.stdout.write(result.stdout||"");process.stderr.write(result.stderr||"");process.exit(result.status??1);
`, { mode: 0o700 });
expect(f.call({ ...request, taskId: "failed-refresh", branch: "paperclip/failed-refresh" }, {
...process.env, PATH: `${bin}:${process.env.PATH}`, FIXTURE_GIT: realGit, FIXTURE_PROBE: probe, FIXTURE_FETCH: fetch,
})).toEqual({ error: "command_failed" });
expect(readFileSync(probe, "utf8")).toBe("present");
expect(readFileSync(fetch, "utf8")).toBe("attempted");
expect(existsSync(join(f.root, "tasks", "failed-refresh"))).toBe(false);
const explicitAfter = f.call({ ...request, baseRef: `refs/heads/${branch}`, taskId: "explicit-after", branch: "paperclip/explicit-after" });
expect(git(explicitAfter.remoteCwd, "rev-parse", "HEAD")).toBe(localCommit);
expect(readFileSync(join(checkout, "uncommitted"), "utf8")).toBe("keep");
expect(git(checkout, "rev-parse", `refs/heads/${branch}`)).toBe(localCommit);
});
it.each([false, true])("attaches only the exact existing worktree branch (present: %s)", present => {
const f = fixture(), checkout = join(f.root, "checkout");
mkdirSync(checkout, { recursive: true });
@@ -93,6 +93,7 @@ if act=='workspace':
return result.stdout.strip() if result.returncode==0 else None
def worktree_base(value):
if not value or value=='HEAD':return 'HEAD'
if value.startswith('refs/heads/'):return commit_ref(value) or fail('command_failed')
remotes=run(['git','remote'],checkout).splitlines()
remote=None;remote_branch=None
candidate=value[len('refs/remotes/'):] if value.startswith('refs/remotes/') else value
@@ -102,12 +103,18 @@ if act=='workspace':
else:
local=value[len('refs/heads/'):] if value.startswith('refs/heads/') else value
local_branch=subprocess.run(['git','show-ref','--verify','--quiet','refs/heads/'+local],cwd=checkout,capture_output=True).returncode==0
# Tags and commit expressions retain their exact meaning. A local branch
# with origin uses the remote counterpart rather than unpushed divergence.
# Tags and commit expressions retain their exact meaning. Bare branches
# prefer origin; only a successful absence check permits a local-only base.
if not local_branch:
resolved=commit_ref(value)
if resolved:return resolved
if 'origin' in remotes:remote,remote_branch='origin',local
if 'origin' in remotes:
if local_branch:
local_commit=commit_ref('refs/heads/'+local)
present=subprocess.run(['git']+auth.get('configArgs',[])+['ls-remote','--exit-code','--heads','--','origin','refs/heads/'+local],cwd=checkout,env=git_env,capture_output=True,text=True)
if present.returncode==2:return local_commit or fail('command_failed')
if present.returncode:fail('command_failed')
remote,remote_branch='origin',local
else:return commit_ref(value) or fail('command_failed')
if remote_branch=='HEAD':return commit_ref(value) or fail('command_failed')
if subprocess.run(['git','check-ref-format','refs/heads/'+remote_branch],cwd=checkout,capture_output=True).returncode: