workspace_repo_default_branch: resolve the default branch or fail, never guess 'master' #126
Labels
No labels
bug
duplicate
enhancement
help wanted
invalid
question
wontfix
bug
duplicate
enhancement
help wanted
invalid
question
wontfix
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
allod/tools#126
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Make
workspace_repo_default_brancheither return a branch it actually resolved or fail loudly, instead of returning a value that depends on whether the caller happened to setpipefail.Primary goals:
masterunderset -o pipefailand the empty string without it, for the same repo in the same state. No consumer can be written correctly against that.architecture.mdprinciple 11 forbids.Current state
lib/workspace.sh:69-74:The
||binds to the pipeline, not togit. Withoutpipefailthe pipeline's status issed's, which is always 0, so the fallback never fires and an unresolvableorigin/HEADyields the empty string. Withpipefailthe status is git's, the fallback does fire, and the same repo yieldsmaster. Both branches are reachable and both are silent; verified against git 2.51.2 by sourcing the file with and withoutset -o pipefail.Every current consumer sets
set -euo pipefail, so the live behaviour today is themasterguess, not the empty return:allod:198-200(wrapping it asdefault_remote_branch_for_dir),workspace/pull-all:55,flake/flake-status:148, andflake/flake-update-cascade:189,:202,:273,:291.An earlier reading of this, recorded in
allod/strategydev-plans/concurrent-agent-workspace.mdunder C5 and repeated indev-plans/allod-change-always-isolate.md, concluded the fallback is dead and the helper returns empty. That is the no-pipefailhalf only. Both documents should be corrected when this lands.Reachability, stated plainly
Narrow, and worth saying so rather than overselling it: git 2.51.2 sets
refs/remotes/origin/HEADoncloneand repairs it on anyfetch, including a refspec-limited one, so a checkout in normal use has it. Verified. The unresolvable state persists in a repo built bygit initplusremote addpluspushwith no fetch since, in one whose remote has no resolvable HEAD, and transiently aftergit remote set-head origin -duntil the next fetch.That bounds the severity but not the defect: a helper whose return value depends on the caller's shell options is wrong regardless of how often the branch is taken, and the two places where a wrong answer costs something are worth naming.
What a wrong answer costs
change submitdefaults its PR base to this helper (allod:438), so a repo whose default branch is notmastergets a PR opened against a base that may not exist.has_unpushed_commits(allod:216) uses it as its last-resort base, and answersreturn 1— "no unpushed commits" — when that base does not resolve. That is the guard standing in front of the destructivechange cleanup, and a "cannot tell" reported as "nothing to lose" is a fail-open in the one place the project cannot afford one. Whether thatreturn 1should become a refusal is a separate question from this helper's contract, and it is now issue allod/tools#127.pull-all --switch(workspace/pull-all:55) compares the current branch against the guess and, on a mismatch, tries to switch to a branch that may not exist.flake-status:148emits a false "[on branch main, not master]" warning.flake-update-cascade:189matches the guess against aprotected-branchesline, so a miss drops the repo out ofskip:protected— the subsequent branch check at:202catches it with a confusing message rather than letting a direct push through, so this one is a legibility failure, not a fail-open.Scope
In:
lib/workspace.sh:69-74and the six callsites above, plus their tests undertests/workspace/andtests/flake/.The default: have the helper print the resolved branch and exit non-zero with no output when
origin/HEADdoes not resolve, then handle that explicitly per consumer — the mutating tools (pull-all --switch,flake-update-cascade) skip the repo naminggit -C <repo> remote set-head origin -aas the repair, and the reporting tools (flake-status) show the repo with the default branch marked unknown rather than inventing one. Keeping a fallback but making it deterministic is the cheaper alternative and is worse: it preserves the guess and only fixes the inconsistency.Out:
allod change begin, which as of PR allod/tools#125 assertssymbolic-ref refs/remotes/origin/HEADitself before consuming the helper and so is already immune; that assertion becomes redundant once this lands and can be dropped then. Also out: thehas_unpushed_commitsfail-open described above, which is issue allod/tools#127.Found while implementing allod/tools#116.
The fail-open named in the Scope section above is now issue allod/tools#127. Reproduced: with no
originremote the helper hitsallod:223, answers "no unpushed commits", andallod change cleanupremoves the worktree and deletes the branch, leaving the commit reachable from no ref.