Two interacting bugs in the Gitea/Forgejo backend, both inherited from
GitHubBackend because PR #1160 assumed Gitea's Git Data API was fully
GitHub-compatible. It isn't, on two specific points:
1. List-shaped ref response. Gitea/Forgejo's
GET /api/v1/repos/{owner}/{repo}/git/refs/heads/{branch} returns a
GET /api/v1/repos/{owner}/{repo}/git/refs/heads/{branch} returns a
list of matching refs even when only one matches; GitHub returns a
single object. The inherited push paths did
ref_response.json()["object"]["sha"] and crashed with
"list indices must be integers or slices, not str" against any
populated Gitea repo.
2. Empty-repo writes refused. GitHub accepts blob/tree/commit POSTs
against a brand-new empty repo and creates the initial commit
implicitly. Gitea refuses every blob POST with 404 until the repo
has at least one commit, so _create_initial_commit silently failed:
blobs returned 404, tree_items stayed empty, the tree POST then
also 404'd ("Failed to create tree").
Fix lives entirely in GiteaBackend — github.py is untouched so the
proven GitHub path takes zero risk. GiteaBackend now overrides
push_files, _create_branch_and_push, and _create_initial_commit:
- _ref_sha() helper accepts both list and dict shapes; called at the
two SHA extraction sites in push_files and _create_branch_and_push.
- _create_initial_commit posts to Gitea's Contents API
(POST /api/v1/repos/{owner}/{repo}/contents with a files array plus
branch + new_branch) which seeds the initial commit + branch in
one transaction and is documented to work on empty repos.
ForgejoBackend extends GiteaBackend with no overrides and inherits
both fixes; tests pin that.