Repository navigation
fix(github): return 409 for ref lookups in an empty repository #3
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -687,6 +687,11 @@ export function branchesAndGitRoutes({ app, store, webhooks, baseUrl }: RouteCon | |
| const repo = lookupRepo(gh, owner, repoName); | ||
| if (!repo) throw notFoundResponse(); | ||
| assertRepoContentsRead(gh, c.get("authUser"), repo); | ||
| // GitHub answers a ref lookup in a repository with no commits with 409, | ||
| // not 404, so clients can tell "empty" from "missing branch". | ||
| if (gh.commits.findBy("repo_id", repo.id).length === 0) { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🟡 Empty-repo 409 guard duplicated from commits.ts. 🤖 Fix with your agentWhy this mattersThe new inline guard (branches.ts:692-694) is a verbatim copy of the existing block in the commits-list handler GET /repos/:owner/:repo/commits (packages/@emulators/github/src/routes/commits.ts:99-101): same predicate, same status code, same literal "Git Repository is empty." string. Two hand-maintained copies of the same rule across route files will drift (message wording or the emptiness definition), and the sibling matching-refs handler will need a third copy. The codebase already centralises route guards (notFoundResponse, assertRepoContentsRead) in route-helpers.ts, so this predicate belongs there too. const repo = lookupRepo(gh, owner, repoName);
if (!repo) throw notFoundResponse();
assertRepoContentsRead(gh, c.get("authUser"), repo);
// GitHub answers a ref lookup in a repository with no commits with 409,
// not 404, so clients can tell "empty" from "missing branch".
if (gh.commits.findBy("repo_id", repo.id).length === 0) {
throw new ApiError(409, "Git Repository is empty.");
}
const fullRef = fullRefFromParam(refParam);
const r = gh.refs.findBy("repo_id", repo.id).find((x) => x.ref === fullRef);
if (!r) throw notFoundResponse();
return c.json(formatRefJson(gh, repo, r.ref, r.sha, baseUrl)); |
||
| throw new ApiError(409, "Git Repository is empty."); | ||
| } | ||
| const fullRef = fullRefFromParam(refParam); | ||
| const r = gh.refs.findBy("repo_id", repo.id).find((x) => x.ref === fullRef); | ||
| if (!r) throw notFoundResponse(); | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🟡 409 gate counts commits; GitHub empties have no refs.
🤖 Fix with your agent
Why this matters
The new gate treats a repo as empty when it has zero commits, but GitHub's "Git Repository is empty." 409 fires for a repository without branches (no refs). The emulator allows creating dangling Git objects without any ref: existing tests (e.g. "keeps the branch head before newer-dated parents") POST /git/commits and only afterwards PATCH /git/refs — so the store can hold commits with zero refs. In that state this endpoint now falls through to the ref lookup and returns 404, while real GitHub would still answer 409 "Git Repository is empty." — the exact case the PR was written to match. Gating on the refs table is also the self-consistent predicate for a ref endpoint. Repro: Given octocat/empty-repo has no refs; When the client seeds dangling objects via POST /git/blobs, POST /git/trees, POST /git/commits (all 201, no ref created); And calls GET /repos/octocat/empty-repo/git/ref/heads/main; Then the emulator responds 404 but real GitHub responds 409 "Git Repository is empty.".