Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

API examples

amber-git’s routes, with real requests and responses. Examples use a repository called myrepo and a token in $TOKEN; see Authentication. Bodies are plain text unless noted.

Git

Clone, fetch and push with stock git at https://amber-git.stackwell.tech/<repo>.git:

git -c http.extraHeader="Authorization: Bearer $TOKEN" \
  clone https://amber-git.stackwell.tech/myrepo.git

These are git’s smart HTTP routes, which git calls for you:

RouteUsed by
GET /<repo>.git/info/refs?service=git-upload-packClone, fetch
POST /<repo>.git/git-upload-packClone, fetch
GET /<repo>.git/info/refs?service=git-receive-packPush
POST /<repo>.git/git-receive-packPush

A gated update is refused with its handle and queued; the rest of the push applies:

$ git push --force origin main
To https://amber-git.stackwell.tech/myrepo.git
 ! [remote rejected] main -> main (queued for approval by the repo owner (ref VTvpvFgh_qVc))
error: failed to push some refs to 'https://amber-git.stackwell.tech/myrepo.git'

git exits non-zero, because the ref did not move. See Concepts for what happens next.

Withdraw a queued push

POST /<repo>.git/withdraw takes back a queued force-push or delete before the owner decides. Name it by the handle your push printed:

curl -sS https://amber-git.stackwell.tech/myrepo.git/withdraw \
  -H "Authorization: Bearer $TOKEN" \
  -d '{"handle": "VTvpvFgh_qVc"}'

Response, 200 OK:

withdrawn: VTvpvFgh_qVc will never apply

Only the pusher who queued it, or the repository’s owner signed in as a person, may withdraw it. Answers:

StatusBodyMeaning
200withdrawn: <handle> will never applyWithdrawn, now or earlier
400body must be {"handle": "<the ref your push printed>"}No handle in the body
401unauthenticatedNo valid token
404no queued operation <handle> of yours on <repo>No such handle on this repository, or not yours to withdraw
409the owner already approved <handle>; ...Too late: it applies on that approval
409the owner already denied <handle>; it will never applyAlready settled
502could not reach granite to withdraw <handle>; it is still queued ...Retry; until it succeeds, the owner can still approve it

Check whether a queued push is still waiting

GET /<repo>.git/queued?sha=<sha> lists your queued pushes that would move a branch to that commit, and where each one stands. <sha> is the full 40-character lowercase commit id you pushed. It only reads; it changes nothing.

curl -sS "https://amber-git.stackwell.tech/myrepo.git/queued?sha=4f2c9e1b7a3d5c8e0f1a2b3c4d5e6f708192a3b4" \
  -H "Authorization: Bearer $TOKEN"

Response, 200 OK:

{"queued":[{"handle":"VTvpvFgh_qVc","ref":"refs/heads/main","granite_status":"pending"}]}

granite_status is pending (the owner hasn’t decided) or approved (the owner approved it and amber-git is moving the branch). An empty list means nothing is waiting: the owner denied it, it was withdrawn, its 7 days ran out, or it has already applied. Fetch to tell which of the last two.

You see the pushes you queued, and the repository’s owner signed in as a person sees everyone’s. Anyone else, and a repository you can’t reach, gets an empty list. A queued delete moves no branch to a commit, so it is never listed.

StatusMeaning
200The list, possibly empty
400?sha= missing, or not 40 lowercase hex characters
401No valid token
502Granite couldn’t be read, so nothing is known; retry

Read files without cloning

Both routes are authorized like a fetch. <sha> is a full 40-character lowercase commit id, and it must be reachable from a branch or tag.

A commit’s files

GET /api/repos/<repo>/tree/<sha> returns every file in the commit, in the format of git ls-tree -r -z --full-tree <sha>: one <mode> <type> <id>\t<path> entry per file, each ending in a NUL byte. Directories are not listed; submodules are, as commit entries.

curl -sS https://amber-git.stackwell.tech/api/repos/myrepo/tree/$SHA \
  -H "Authorization: Bearer $TOKEN" | tr '\0' '\n'

Response, 200 OK, Content-Type: application/octet-stream (excerpt):

100644 blob 24bca6bbcd45b4b78e08377169f112825bf7f778	Cargo.toml
100644 blob 8fcff3bd909c31072d6159c42460d26b36d52427	README.md
100644 blob c557356b6219d280d17a5921fa695fd441cdf049	serve/src/lib.rs

One file

GET /api/repos/<repo>/blob/<sha>/<path> returns the file’s bytes as application/octet-stream, up to 1 MiB.

curl -sS https://amber-git.stackwell.tech/api/repos/myrepo/blob/$SHA/README.md \
  -H "Authorization: Bearer $TOKEN"

Answers on both routes:

StatusMeaning
200The listing, or the file’s bytes
404 with header Amber-Git-Absent: pathNo file at that path; also a directory or a submodule. Body: no file <path> at <sha>
404 without that headerThe repository is not there, or the path is malformed (<sha> not 40 lowercase hex)
413The file is over 1 MiB, or the listing or the trees it reads are over 16 MiB
422no commit <sha> is reachable from a ref of <repo>
401, 403, 410As for a fetch; see Errors and limits

A 404 without Amber-Git-Absent never means “no such file”: check the header before you treat a file as missing.

Re-send to the GitHub mirror

For a repository that mirrors to GitHub, POST /<repo>.git/remirror re-pushes every branch and tag to the mirror, and deletes from it each branch or tag deleted here. It is authorized like a fetch.

curl -sS -X POST https://amber-git.stackwell.tech/myrepo.git/remirror \
  -H "Authorization: Bearer $TOKEN"

Response, 202 Accepted, where <m> is the number of refs, <n> how many of them were queued, and <d> how many of them were deleted here:

queued <n> of <m> ref(s) of myrepo for mirroring (<d> of them deleted here, to delete on GitHub)

202 means the pushes are queued, not that GitHub matches yet. Compare with git ls-remote against both. A repository that does not mirror answers 409.

Health

curl -fsS https://amber-git.stackwell.tech/health

200 with ok, for any method, without a token.