Skip to Content
SimulatorsGitHubCommits through the API

Simulators

Commits through the API

The contents and git data APIs write the same repository git clone reads, so an agent that never clones can branch and commit. Create a branch, commit a file through the contents API, commit a tree through the git data API, then clone the branch:

H='Authorization: Bearer ghp_anything'
R="$RYSTIC_URL/repos/tb-owner/tb-repo"
TIP=$(curl -s "$R/git/ref/heads/tb-base-branch" -H "$H" | jq -r .object.sha)
curl -s -X POST "$R/git/refs" -H "$H" -d "{\"ref\":\"refs/heads/agent-fix\",\"sha\":\"$TIP\"}" | jq -r .ref
# -> refs/heads/agent-fix
curl -s -X PUT "$R/contents/notes.txt" -H "$H" -d '{"message":"Add notes","branch":"agent-fix","content":"aGkK"}' | jq -r .content.sha
# -> 45b983be36b73c0788dc9cbcb76cbb80fc7bb057
echo hi | git hash-object --stdin
# -> 45b983be36b73c0788dc9cbcb76cbb80fc7bb057
PARENT=$(curl -s "$R/branches/agent-fix" -H "$H" | jq -r .commit.sha)
BASE_TREE=$(curl -s "$R/branches/agent-fix" -H "$H" | jq -r .commit.commit.tree.sha)
TREE=$(curl -s -X POST "$R/git/trees" -H "$H" -d "{\"base_tree\":\"$BASE_TREE\",\"tree\":[{\"path\":\"hello.txt\",\"mode\":\"100644\",\"type\":\"blob\",\"content\":\"hello\\n\"}]}" | jq -r .sha)
COMMIT=$(curl -s -X POST "$R/git/commits" -H "$H" -d "{\"message\":\"Add hello\",\"tree\":\"$TREE\",\"parents\":[\"$PARENT\"]}" | jq -r .sha)
curl -s "$R/git/commits/$COMMIT" -H "$H" | jq -c '.verification | {verified, reason}'
# -> {"verified":false,"reason":"unsigned"}
curl -s -X PATCH "$R/git/refs/heads/agent-fix" -H "$H" -d "{\"sha\":\"$COMMIT\"}" | jq -r .ref
# -> refs/heads/agent-fix
git clone -q -b agent-fix "$RYSTIC_URL/tb-owner/tb-repo.git" repo
git -C repo log --format=%s -2
# -> Add hello
# -> Add notes
cat repo/hello.txt
# -> hello

How a push moves pull requests, how a merge builds its commit and what a push may carry are on Git repositories.

Writes land as pushes

A ref created, moved or deleted through .../git/refs lands as a push does, with its push notification, synchronize and workflows. So does the commit a contents PUT or DELETE makes. Blobs, trees and commits hash as git hashes them, so git fetch finds what the API wrote.

These writes return 422, as git would refuse them:

  • a ref outside refs/heads/ and refs/tags/;
  • a ref name git’s check-ref-format refuses;
  • a ref that clashes with a directory of refs;
  • a path with an empty, ., .. or .git segment;
  • a write onto a directory or under a file.

Differs from GitHub: a contents write or a tree created through the git data API accepts a .gitmodules or .gitattributes that GitHub refuses with 422 for its contents. A later push whose tree names that blob is refused, as on GitHub.

A scenario that seeds no repository (no git section) serves these writes from its branch and commit rows alone. Nothing is stored, and a created object’s sha is not git’s. The contents, blob and tree reads return 404 there.

Commit verification

A commit’s verification follows its signature:

Commitverifiedreason
Signed, with committer email noreply@github.comtruevalid
Signed, any other committerfalseunknown_key
Unsignedfalseunsigned

Differs from GitHub: the simulator holds no keys, so it checks no signature. A commit claiming GitHub’s ident reads valid whoever signed it, and any other signed commit reads unknown_key even when GitHub would verify its key.

Differs from GitHub: merge, squash and update-branch commits the simulator writes carry no signature, so they read unsigned, where GitHub signs them.

Commit authors

On the REST commit, branch and compare reads, a commit’s author and committer are the scenario accounts its git idents credit. The first row that matches decides:

IdentAccount credited
GitHub’s own, noreply@github.comweb-flow when the scenario seeds it, else no account
An email a scenario account hasthat account
A noreply <id>+<login>@users.noreply.github.com addressonly <login>
Any otherthe account its name names, else the login its address’s part before @ and any + names

Differs from GitHub: GitHub credits a commit only through an email address linked to an account, and reads author null for any other. The simulator also credits by the ident’s name, or by the login its address names.

Last updated on