fangorn/ex_git_objectstore
public
ref:8de061c4ca174f71cee22b04293470bc64ce4f6c
feat: ReceivePack supports atomic push
Advertises `atomic` alongside `report-status` and `delete-refs`. When
the client indicates atomic in its capabilities list, ReceivePack
validates every ref-update command before touching storage; if any
command fails validation (bad ref name, stale old_sha, non-ff,
update_hook rejection), the whole batch is refused with `ng` per ref
and no ref moves.
Two-phase flow:
1. Validate all commands. Per-command validation checks:
* Ref.validate_ref_name/1
* old_sha state (must be zero for create, current for update,
present for delete)
* update_hook's return value
2. Commit all. Snapshot every target ref's current value, then
apply commands sequentially. If any apply fails mid-way, restore
every touched ref from its snapshot (and delete refs that didn't
exist pre-flight).
Note on transactionality: ref storage backends (filesystem, S3) are
not multi-key atomic. A rollback can itself fail, leaving refs in a
partially-applied state. This implementation is as atomic as the
storage layer allows — the doc comment on do_atomic_ref_updates/1
calls this out explicitly.
Test rewritten to actually exercise the atomic path: a pre-registered
update_hook rejects any update to refs/heads/rejected. The client
pushes `main:refs/heads/accepted` and `main:refs/heads/rejected` in a
single --atomic push; the test asserts BOTH refs are absent on the
server afterwards (not just that the push exit code is non-zero).
Non-atomic behaviour is unchanged: per-ref failures still produce
per-ref `ng` entries without affecting other refs.
SHA:
8de061c4ca174f71cee22b04293470bc64ce4f6c
Author:
Cole Christensen <cole.christensen@macmillan.com>
Date:
2026-04-19 00:51
Parents:
dfd72ff
4 files changed
+184
-56
| Type | ||
|---|---|---|
|
|
lib/ex_git_objectstore/pack/filter.ex | +4 −6 |
|
||