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

Concepts

This page lists what amber-git enforces today. Where a protection is missing or partial, it says “Not yet”.

What needs approval

amber-git sorts every ref update in a push by whether it can lose history:

UpdateLoses history?What happens
Create a branch or tagNoApplied at once
Fast-forward a branchNoApplied at once
Force-push (non-fast-forward update)YesQueued for the owner’s approval
Delete a branch or tagYesQueued for the owner’s approval
Move an existing tag, forward or notEither wayRefused, for everyone

A tag doesn’t move once it exists, because people and tools treat a release tag’s name as fixed. To repoint one, delete it (queued for the owner’s approval) and push it again.

In a push of several refs, each is decided on its own: the free ones apply and the gated ones are refused with their handles. Every repository is gated this way; there is no setting that turns it off.

Queued force-pushes and deletes

When you push a gated update:

  1. amber-git refuses the visible ref move. git prints ! [remote rejected] <ref> -> <ref> (queued for approval by the repo owner (ref <handle>)).
  2. It files a request with Granite for the repository’s owner, whoever pushed. The request names the repository, the ref, the old and new commits, who asked (a person, or an agent and its client), and your handle.
  3. The owner approves or denies it in Granite.
  4. On approval, amber-git applies the update. Granite notifies it as soon as the owner decides; amber-git also checks Granite for decisions every 5 minutes, so a lost notification only delays the update.

Rules that hold throughout:

  • Approved for one exact move. A force-push applies only if the ref still points where it did when you pushed. If anyone has moved it since, the approval applies nothing; push again.
  • A delete names the commit it removes. Your push must send the ref’s current commit as its old value, as stock git does; a delete that names no commit is refused. The owner’s approval deletes the ref at once, and only if it still points at that commit; if it has moved since, nothing is deleted. You can push the branch again later.
  • Denied or withdrawn means never. Once the owner denies a request or you withdraw it, nothing can apply it.
  • The handle is not the Granite request. The pusher only ever sees the handle. Use it to withdraw the update before the owner decides.
  • You can check on it. Ask by the commit you pushed whether the update is still waiting; an empty answer means it never will apply, or already has.
  • A deadline. Granite gives the owner 7 days to decide. After that the request can’t be approved; withdraw it and push again if you still need it.
  • The owner approves their own pushes too. A force-push by the owner is queued like anyone else’s.

Default branch protected by CI

A repository can be set up so that its default branch moves only to commits whose required CI checks passed. It applies to repositories registered with Stackwell’s CI whose default branch holds .amber/ci.toml. In such a repository, a push that creates or moves the default branch is refused unless CI reports that every required check of the new commit passed:

RefusalWhat to do
amber-ci gate: no CI run for <sha>; push it to a branch first, then push main once amber-ci/* is greenPush the commit to another branch, wait for CI, then push it to the default branch
amber-ci gate: required checks for <sha> are still running ...Push again once they pass
amber-ci gate: required checks for <sha> did not pass ...Fix the commit

If amber-git can’t get an answer from CI, it refuses the push and asks you to retry. A force-push to the default branch must pass this check before it is queued for approval.

Storage

Every object you push is stored append-only: amber-git never deletes it. Branches and tags are pointers, updated only by compare-and-set, so two writers can’t silently overwrite each other’s update.

Objects from a push that was refused, in whole or in part, are kept too. They can’t be fetched or read: amber-git serves only commits reachable from a branch or tag.

Renamed and retired repositories

Stackwell can rename or retire a repository, with the owner’s approval. A renamed repository answers 410 with its new name. A retired one answers 410 and stops accepting pushes and serving fetches. Neither erases its contents, and a used name is never given to another repository.

GitHub mirror

A repository can be set up to mirror to GitHub. amber-git then pushes every accepted ref update to the mirror, so the mirror follows amber-git, never the other way round. Mirroring is best effort: a mirror push that fails in transit is tried four more times, after 30 seconds, 2, 5 and 10 minutes (to ride out a GitHub outage of several minutes), and then dropped; one GitHub refuses is dropped at once. POST /<repo>.git/remirror re-sends every ref, and every delete; see API examples.

Reading files without cloning

GET /api/repos/<repo>/tree/<sha> lists a commit’s files and GET /api/repos/<repo>/blob/<sha>/<path> returns one file. Both are authorized like a fetch and serve only commits reachable from a branch or tag. See API examples.

Not yet

  • A failed apply is not retried. If applying an approved update fails on the server, the update is dropped and the approval is used up. Check the ref after approval, and push again if it didn’t move.
  • Large histories. One clone, fetch or push may read only a bounded amount of commit and tree data: about 64 MB for a clone or fetch, about 16 MB for a push. Past it, the operation is refused with a message. Fetch fewer branches or a shallow clone, or push a long history in parts.
  • Deleting contents. Nothing removes a pushed object or a repository’s contents; see Data and privacy.
  • No public repositories. Every read needs a token and a grant.