From 60d8c1e97d62c27ef60db0bc3d5deadd6dfdb98d Mon Sep 17 00:00:00 2001 From: Eric Ju Date: Mon, 16 Mar 2026 22:36:24 -0400 Subject: refs: add 'preparing' phase to the reference-transaction hook The "reference-transaction" hook is invoked multiple times during a ref transaction. Each invocation corresponds to a different phase: - The "prepared" phase indicates that references have been locked. - The "committed" phase indicates that all updates have been written to disk. - The "aborted" phase indicates that the transaction has been aborted and that all changes have been rolled back. This hook can be used to learn about the updates that Git wants to perform. For example, forges use it to coordinate reference updates across multiple nodes. However, the phases are insufficient for some specific use cases. The earliest observable phase in the "reference-transaction" hook is "prepared", at which point Git has already taken exclusive locks on every affected reference. This makes it suitable for last-chance validation, but not for serialization. So by the time a hook sees the "prepared" phase, it has no way to defer locking, and thus it cannot rearrange multiple concurrent ref transactions relative to one another. Introduce a new "preparing" phase that runs before the "prepared" phase, that is before Git acquires any reference lock on disk. This gives callers a well-defined window to perform validation, enable higher-level ordering of concurrent transactions, or reject the transaction entirely, all without interfering with the locking state. This change is strictly speaking not backwards compatible. Existing hook scripts that do not know how to handle unknown phases may treat 'preparing' as an error and return non-zero. But the hook is considered to expose internal implementation details of how Git works, and as such we have been a bit more lenient with changing its exact semantics, like for example in a8ae923f85 (refs: support symrefs in 'reference-transaction' hook, 2024-05-07). An alternative would be to introduce a "reference-transaction-v2" hook that knows about the new phase. This feels like a rather heavy-weight option though, and was thus discarded. Helped-by: Patrick Steinhardt Helped-by: Justin Tobler Helped-by: Karthik Nayak Signed-off-by: Eric Ju Signed-off-by: Junio C Hamano --- Documentation/githooks.adoc | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) (limited to 'Documentation') diff --git a/Documentation/githooks.adoc b/Documentation/githooks.adoc index 056553788d..ed045940d1 100644 --- a/Documentation/githooks.adoc +++ b/Documentation/githooks.adoc @@ -484,13 +484,16 @@ reference-transaction ~~~~~~~~~~~~~~~~~~~~~ This hook is invoked by any Git command that performs reference -updates. It executes whenever a reference transaction is prepared, -committed or aborted and may thus get called multiple times. The hook -also supports symbolic reference updates. +updates. It executes whenever a reference transaction is preparing, +prepared, committed or aborted and may thus get called multiple times. +The hook also supports symbolic reference updates. The hook takes exactly one argument, which is the current state the given reference transaction is in: + - "preparing": All reference updates have been queued to the + transaction but references are not yet locked on disk. + - "prepared": All reference updates have been queued to the transaction and references were locked on disk. @@ -511,16 +514,18 @@ ref and `` is the full name of the ref. When force updating the reference regardless of its current value or when the reference is to be created anew, `` is the all-zeroes object name. To distinguish these cases, you can inspect the current value of -`` via `git rev-parse`. +`` via `git rev-parse`. During the "preparing" state, symbolic +references are not resolved: `` will reflect the symbolic reference +itself rather than the object it points to. For symbolic reference updates the `` and `` fields could denote references instead of objects. A reference will be denoted with a 'ref:' prefix, like `ref:`. The exit status of the hook is ignored for any state except for the -"prepared" state. In the "prepared" state, a non-zero exit status will -cause the transaction to be aborted. The hook will not be called with -"aborted" state in that case. +"preparing" and "prepared" states. In these states, a non-zero exit +status will cause the transaction to be aborted. The hook will not be +called with "aborted" state in that case. push-to-checkout ~~~~~~~~~~~~~~~~ -- cgit v1.3