Repository navigation
Description
Describe the bug
When branch does not exist, push_files creates it from the default branch first (createReferenceFromDefaultBranch) and only then builds the tree and commit. If the tree or commit step fails, the new ref is not rolled back. The caller gets an error, but a branch pointing at the default branch's head stays behind. A retry then pushes onto that leftover branch, and it shows up in branch lists.
When the branch already exists, the same failure leaves no partial commit, so only the new-branch path is affected.
Silently creating a branch that doesn't exist is already raised as a question in #3473 (item 6). This issue is only about the ref left behind when the push then fails.
Code (main 99d6d65, unchanged since v2.1.0): pkg/github/repositories.go:1767-1772 (ref created when the branch is missing), :1795-1800 (same for the empty-repo path), then CreateTree at :1809 and CreateCommit/UpdateRef after it. Helper: pkg/github/repositories_helper.go:75. Open PR #3288 keeps the ensure-ref-first order (ensurePushFilesBranchRef), so it would not change this.
Affected version
v2.1.0 (image sha256:f6056fc1…0f8a, commit 4bcdcdf) and main nightly (image sha256:48bc16d0…338f, revision 99d6d65).
Steps to reproduce the behavior
- Make sure the branch
new-branchdoes not exist. - Call it with one valid file and one path that
CreateTreerejects:{"name":"push_files","arguments":{"owner":"OWNER","repo":"REPO","branch":"new-branch","message":"push","files":[{"path":"p4/ok.txt","content":"ok"},{"path":".git/config","content":"x"}]}} - Result:
isErrorwithfailed to create tree: tree.path contains a malformed path component. GET /repos/OWNER/REPO/git/ref/heads/new-branchreturns 200, and the branch points at the default branch's head commit.
Expected vs actual behavior
Expected: a failed push leaves the repository unchanged, with no new branch.
Actual: the error is returned and an orphan branch exists at the default branch's head.
Suggested fix: tree and commit objects don't need a ref. Create the tree and the commit (parent = default branch head) first, then create the new ref directly at the new commit. Alternatively, delete the ref on any later failure.
Logs
push_files branch=new-branch -> ISERROR failed to create tree: tree.path contains a malformed path component
GET git/ref/heads/new-branch -> 200, object.sha = faf1b251… (= default branch head) [v2.1.0 and main]
Found during an independent audit of v2.1.0 on a throwaway org and private test repo. Prepared with AI assistance (Claude Code); reproduced by two independent test runs before filing.
Activity