Resolve fresh Boat worktree bases from refreshed remote branches

Resolve unqualified nondefault branches and refresh exact tracking refs using operation-scoped Git credentials before new worktree creation. Keep tags, commit expressions, existing branch attachment and existing worktrees unchanged; fail before creation when refresh fails.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
DottaandPaperclip committed 2026-10-11 09:59:15 -05:00
1 parent c91eab5d07
commit cf85d018e6
2 files changed
+83 -6

No files matched your search

@@ -790,6 +790,56 @@ process.stdout.write(result.stdout||"");process.stderr.write(result.stderr||"");
expect(f.call({ ...input, branch: "another-branch" })).toEqual({ error: "conflict" });
expect(git("-C", result.remoteCwd, "status", "--short")).toContain("preserved");
});
it.each(["release", "feature/release", "origin/release", "refs/remotes/origin/release"])("refreshes the authoritative remote worktree base %s without rewriting existing work", baseRef => {
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 = (message: string) => git(source, "-c", "user.name=Test", "-c", "user.email=test@example.com", "commit", "--allow-empty", "-m", message);
git(source, "init", "-b", "main"); commit("default");
const branch = baseRef === "feature/release" ? baseRef : "release";
git(source, "checkout", "-b", branch); commit("release one");
const original = git(source, "rev-parse", "HEAD"); git(source, "tag", "baseline"); git(source, "checkout", "main");
const repositoryUrl = "https://fixture.invalid/source.git";
// Real Git clone/fetch over a local transport; the URL rewrite exists only
// in each child invocation, never in repository or global configuration.
const gitAuth = { configArgs: ["-c", `url.file://${source}.insteadOf=${repositoryUrl}`, "-c", "protocol.file.allow=always"], env: {} };
const request = { action: "workspace", mode: "worktree", repositoryUrl, gitAuth, baseRef, taskId: "one", branch: "paperclip/one" };
const first = f.call(request);
expect(first.remoteCwd).toBe(join(f.root, "tasks", "one"));
expect(git(first.remoteCwd, "rev-parse", "HEAD")).toBe(original);
const checkout = join(f.root, "checkout");
expect(spawnSync("git", ["show-ref", "--verify", `refs/heads/${branch}`], { cwd: checkout }).status).not.toBe(0);
writeFileSync(join(first.remoteCwd, "uncommitted"), "keep");
git(source, "checkout", branch); commit("release two");
const updated = git(source, "rev-parse", "HEAD"); git(source, "checkout", "main");
// A divergent local branch must not override the current remote base.
git(checkout, "branch", branch, "HEAD");
const deniedAuth = { ...gitAuth, configArgs: [...gitAuth.configArgs, "-c", "protocol.file.allow=never"] };
expect(f.call({ ...request, gitAuth: deniedAuth, taskId: "denied", branch: "paperclip/denied" })).toEqual({ error: "command_failed" });
expect(existsSync(join(f.root, "tasks", "denied"))).toBe(false);
const second = f.call({ ...request, taskId: "two", branch: "paperclip/two" });
expect(second.remoteCwd).toBe(join(f.root, "tasks", "two"));
expect(git(second.remoteCwd, "rev-parse", "HEAD")).toBe(updated);
expect(git(checkout, "rev-parse", branch)).not.toBe(updated);
const existing = f.call({ ...request, gitAuth: deniedAuth, taskId: "existing", branch, existingBranch: branch });
expect(git(existing.remoteCwd, "rev-parse", "HEAD")).toBe(git(checkout, "rev-parse", branch));
const tagged = f.call({ ...request, gitAuth: deniedAuth, taskId: "tagged", branch: "paperclip/tagged", baseRef: "baseline" });
expect(git(tagged.remoteCwd, "rev-parse", "HEAD")).toBe(original);
const ancestor = f.call({ ...request, gitAuth: deniedAuth, taskId: "ancestor", branch: "paperclip/ancestor", baseRef: `origin/${branch}~1` });
expect(git(ancestor.remoteCwd, "rev-parse", "HEAD")).toBe(original);
const defaultHead = f.call({ ...request, gitAuth: deniedAuth, taskId: "default-head", branch: "paperclip/default-head", baseRef: "origin/HEAD" });
expect(git(defaultHead.remoteCwd, "rev-parse", "HEAD")).toBe(git(checkout, "rev-parse", "HEAD"));
expect(f.call(request).remoteCwd).toBe(first.remoteCwd);
expect(git(first.remoteCwd, "rev-parse", "HEAD")).toBe(original);
expect(readFileSync(join(first.remoteCwd, "uncommitted"), "utf8")).toBe("keep");
expect(readFileSync(join(checkout, ".git/config"), "utf8")).not.toContain("insteadOf");
expect(f.call({ ...request, taskId: "missing", branch: "paperclip/missing", baseRef: "missing" })).toEqual({ error: "command_failed" });
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([false, true])("attaches only the exact existing worktree branch (present: %s)", present => {
const f = fixture(), checkout = join(f.root, "checkout");
mkdirSync(checkout, { recursive: true });
@@ -75,19 +75,46 @@ if act=='workspace':
if existing_branch and (p.get('mode')!='worktree' or existing_branch!=branch):fail('invalid')
if base_ref and (base_ref.startswith('-') or '\n' in base_ref):fail('invalid')
checkout=os.path.join(root,'checkout')
# Only network Git children receive operation-scoped credentials. Never
# persist helper configuration in the checkout or the shared agent home.
auth=p.get('gitAuth') or {};git_env=dict(os.environ)
git_env.update({'GIT_TERMINAL_PROMPT':'0','GIT_CONFIG_GLOBAL':'/dev/null','GIT_CONFIG_NOSYSTEM':'1'})
git_env.update(auth.get('env',{}))
if repo:
if not (repo.startswith('https://') or repo.startswith('ssh://') or repo.startswith('git@')) or '\n' in repo:fail('invalid')
if not os.path.exists(checkout):
# Resolve credentials on the controller for this operation. Only the clone
# child receives the helper environment; no credential file or Git config persists.
auth=p.get('gitAuth') or {};git_env=dict(os.environ)
git_env.update({'GIT_TERMINAL_PROMPT':'0','GIT_CONFIG_GLOBAL':'/dev/null','GIT_CONFIG_NOSYSTEM':'1'})
git_env.update(auth.get('env',{}))
run(['git']+auth.get('configArgs',[])+['clone','--',repo,checkout],env=git_env)
elif run(['git','remote','get-url','origin'],checkout)!=repo:fail('conflict')
else:
os.makedirs(checkout,exist_ok=True)
if not os.path.isdir(os.path.join(checkout,'.git')):run(['git','init',checkout])
def commit_ref(value):
result=subprocess.run(['git','rev-parse','--verify','--end-of-options',value+'^{commit}'],cwd=checkout,capture_output=True,text=True)
return result.stdout.strip() if result.returncode==0 else None
def worktree_base(value):
if not value or value=='HEAD':return 'HEAD'
remotes=run(['git','remote'],checkout).splitlines()
remote=None;remote_branch=None
candidate=value[len('refs/remotes/'):] if value.startswith('refs/remotes/') else value
parts=candidate.split('/',1)
if len(parts)==2 and parts[0] in remotes:
remote,remote_branch=parts
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.
if not local_branch:
resolved=commit_ref(value)
if resolved:return resolved
if 'origin' in remotes: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:
return commit_ref(value) or fail('command_failed')
target_ref='refs/remotes/'+remote+'/'+remote_branch
run(['git']+auth.get('configArgs',[])+['fetch','--no-tags','--',remote,'+refs/heads/'+remote_branch+':'+target_ref],checkout,git_env)
return commit_ref(target_ref) or fail('command_failed')
if branch:
if branch.startswith('-') or '\n' in branch:fail('invalid')
run(['git','check-ref-format','--branch',branch])
@@ -98,7 +125,7 @@ if act=='workspace':
if existing_branch:run(['git','show-ref','--verify','refs/heads/'+existing_branch],checkout)
if not os.path.exists(target):
if existing_branch:run(['git','worktree','add',target,existing_branch],checkout)
else:run(['git','worktree','add','-b',task_branch,target,base_ref or 'HEAD'],checkout)
else:run(['git','worktree','add','-b',task_branch,target,worktree_base(base_ref)],checkout)
run(['git','rev-parse','--show-toplevel'],target)
if run(['git','symbolic-ref','--quiet','--short','HEAD'],target)!=task_branch:fail('conflict')
else: