@@ -486,7 +486,7 @@ 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
-does not cover symbolic references (but that may change in the future).
+also cover symbolic references.
The hook takes exactly one argument, which is the current state the
given reference transaction is in:
@@ -503,16 +503,19 @@ given reference transaction is in:
For each reference update that was added to the transaction, the hook
receives on standard input a line of the format:
- <old-oid> SP <new-oid> SP <ref-name> LF
+ <old-value> SP <new-value> SP <ref-name> LF
-where `<old-oid>` is the old object name passed into the reference
-transaction, `<new-oid>` is the new object name to be stored in the
+where `<old-value>` is the old object name passed into the reference
+transaction, `<new-value>` is the new object name to be stored in the
ref and `<ref-name>` 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, `<old-oid>` is the all-zeroes object name. To
+to be created anew, `<old-value>` is the all-zeroes object name. To
distinguish these cases, you can inspect the current value of
`<ref-name>` via `git rev-parse`.
+For symbolic reference updates the `<old_value>` and `<new-value>`
+fields could denote references instead of objects.
+
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
@@ -2362,15 +2362,20 @@ static int run_transaction_hook(struct ref_transaction *transaction,
for (i = 0; i < transaction->nr; i++) {
struct ref_update *update = transaction->updates[i];
+ const char *new_value, *old_value;
- if (update->flags & REF_SYMREF_UPDATE)
- continue;
+ new_value = oid_to_hex(&update->new_oid);
+ old_value = oid_to_hex(&update->old_oid);
+
+ if (update->flags & REF_SYMREF_UPDATE) {
+ if (update->flags & REF_HAVE_NEW && !null_new_value(update))
+ new_value = update->new_ref;
+ if (update->flags & REF_HAVE_OLD && update->old_ref)
+ old_value = update->old_ref;
+ }
strbuf_reset(&buf);
- strbuf_addf(&buf, "%s %s %s\n",
- oid_to_hex(&update->old_oid),
- oid_to_hex(&update->new_oid),
- update->refname);
+ strbuf_addf(&buf, "%s %s %s\n", old_value, new_value, update->refname);
if (write_in_full(proc.in, buf.buf, buf.len) < 0) {
if (errno != EPIPE) {
@@ -108,6 +108,10 @@ test_expect_success 'hook gets all queued updates in aborted state' '
test_cmp expect actual
'
+# This test doesn't add a check for 'symref-delete' since there is a
+# variation between the ref backends WRT 'delete'. In the files backend,
+# 'delete' also triggers an additional transaction update on the
+# packed-refs backend, which constitutes additional reflog entries.
test_expect_success 'interleaving hook calls succeed' '
test_when_finished "rm -r target-repo.git" &&
@@ -134,4 +138,41 @@ test_expect_success 'interleaving hook calls succeed' '
test_cmp expect target-repo.git/actual
'
+test_expect_success 'hook gets all queued symref updates' '
+ test_when_finished "rm actual" &&
+
+ git update-ref refs/heads/branch $POST_OID &&
+ git symbolic-ref refs/heads/symref refs/heads/main &&
+ git symbolic-ref refs/heads/symrefu refs/heads/main &&
+
+ test_hook reference-transaction <<-\EOF &&
+ echo "$*" >>actual
+ while read -r line
+ do
+ printf "%s\n" "$line"
+ done >>actual
+ EOF
+
+ cat >expect <<-EOF &&
+ prepared
+ refs/heads/main $ZERO_OID refs/heads/symref
+ $ZERO_OID refs/heads/main refs/heads/symrefc
+ refs/heads/main refs/heads/branch refs/heads/symrefu
+ committed
+ refs/heads/main $ZERO_OID refs/heads/symref
+ $ZERO_OID refs/heads/main refs/heads/symrefc
+ refs/heads/main refs/heads/branch refs/heads/symrefu
+ EOF
+
+ git update-ref --no-deref --stdin <<-EOF &&
+ start
+ symref-verify refs/heads/symref refs/heads/main
+ symref-create refs/heads/symrefc refs/heads/main
+ symref-update refs/heads/symrefu refs/heads/branch refs/heads/main
+ prepare
+ commit
+ EOF
+ test_cmp expect actual
+'
+
test_done