Documentation
Pull requests
Everlock has a built-in pull request system. It is designed around a single principle: a pull request is a ref, not a database record.
How it works
When you open a pull request, the server creates a Git ref:
refs/pull/{slug}/{target} → <commit-oid>
{slug}is a human-readable name you choose for the PR, such asoauth-supportorfix-login{target}is the branch you want to merge into, such asmain- the ref points to the commit you want reviewed
Everything the system needs to know about the PR is encoded in the ref itself:
| Thing | How it is stored |
|---|---|
| PR identity | the ref name |
| Commit to merge | the OID the ref points to |
| Target branch | the last segment of the ref path |
| PR is open | ref exists |
| PR is closed or merged | ref deleted |
There is no separate database. Diff and mergeability are computed on the fly from the ref and the current state of the target branch.
Two ways to drive a pull request
Everything a contributor needs is reachable from an ordinary git client: opening, updating, listing, fetching, and closing are all pushes and fetches against refs/pull/. The admin shell offers the same operations, plus merge status and the merge itself.
| Operation | From a git client | From the admin shell |
|---|---|---|
| Open | git push origin HEAD:refs/pull/{slug}/{target} | /git pr open |
| Update | git push origin HEAD:refs/pull/{slug}/{target} | — |
| List | git ls-remote origin 'refs/pull/*' | /git pr list (adds merge status) |
| Fetch for review | git fetch origin refs/pull/{slug}/{target}:{local} | — |
| Close | git push origin :refs/pull/{slug}/{target} | /git pr close |
| Merge | — | /git pr merge |
A contributor holding reader on the repo plus writer on <repo>:pulls — the contribution grant — does everything in the left column with git alone, and needs no admin session at all.
Opening a pull request
Via git push
Push the commit you want reviewed to the PR ref:
The server validates the push:
- the target branch (
main) must exist - the ref must not already exist (to prevent accidental overwrites)
- the commit must be present in the repository
Pushing a PR ref requires writer on the repo or on <repo>:pulls — the contribution grant that allows proposing changes without any branch access. The same grant covers /git pr open and /git pr close in the admin shell; /git pr merge writes the target branch and needs repo writer.
Via the admin CLI
/git pr open my-project oauth-support main feature-branch
This creates refs/pull/oauth-support/main pointing to the current tip of feature-branch.
You can also point a PR at a specific commit OID instead of a branch name:
/git pr open my-project oauth-support main abc123def456...
Listing pull requests
Via git
PR refs are advertised like any other ref, so any client with reader sees the open pull requests:
a1b2c3d4e5f6... refs/pull/oauth-support/main
d4e5f6a1b2c3... refs/pull/fix-parser/main
The ref name carries the slug and the target branch, and the OID is the commit under review.
Via the admin CLI
The admin shell computes mergeability, which the ref alone does not carry:
/git pr list my-project
Output shows each open PR and its merge status:
oauth-support/main mergeable
fix-parser/main conflict: src/parser.rs
update-deps/main mergeable
mergeable means the PR can be merged automatically, either as a fast-forward or as a three-way merge commit.
conflict means the same lines were modified on both sides and cannot be resolved without manual intervention. The conflicting file paths are shown.
Updating a pull request
Push a new commit to the same ref:
The server replaces the ref with the new commit. Force-push semantics apply — the client controls the commit history.
Fetching a pull request locally
To review someone else's PR:
Merging a pull request
Merging is the one step with no git-client form: it advances refs/heads/{target}, which requires repo writer. A contributor holding only <repo>:pulls proposes the change; a branch writer lands it.
/git pr merge my-project oauth-support main
The server:
- reads
refs/pull/oauth-support/mainto find the source commit - reads
refs/heads/mainto find the current target tip - checks mergeability
- if the target is a direct ancestor of the source: fast-forwards
mainto the source commit - if the branches have diverged but there are no conflicts: creates a merge commit and advances
main - if there are conflicts: aborts with the list of conflicting paths
- on success: deletes
refs/pull/oauth-support/main
The target branch update is atomic — it uses a compare-and-swap against the tip the server read in step 2. If main changed between validation and the update, the merge is rejected and must be retried.
When a merge fails
If the merge reports conflicts:
error: merge conflict in: src/parser.rs, src/lexer.rs
The PR author needs to rebase or resolve the conflicts locally and push an updated commit to the PR ref:
# resolve any conflicts
Closing a pull request without merging
Via git push:
Via the admin CLI:
/git pr close my-project oauth-support main
Both delete the ref. There is no separate "closed" state — a deleted ref is a closed PR.
Ref namespace
All PR refs live under refs/pull/. They are treated as GC roots — the server will not garbage-collect commits that are only reachable from a PR ref.
Clients cannot push directly to refs/pull/ to overwrite an existing PR. The server rejects pushes that would create a PR ref that already exists. To update an existing PR, push a new commit to the same ref path (the server treats that as an update, not a creation).
Design notes
The slug is independent of the source branch. You can open a PR named oauth-support from a branch called feature/auth-rewrite, or from a specific commit on no branch at all. The PR tracks the commit, not the branch.
This means:
- PRs survive branch renames and deletions
- Multiple PRs can target the same branch from different commits
- The slug is the stable identity of the review, not the branch name