# Reference transaction hook is unable to find the old_oid for pre-existing refs

2 messages from 2025-09-03 to 2025-09-03. Participants: elhmn, Jeff King.
Thread: https://gitlist.dev/t/64082

## elhmn, 2025-09-03 14:11

Subject: Reference transaction hook is unable to find the old_oid for pre-existing refs
Message-ID: <CACpeKQ=sPOc2AqsMT--2tu=BMEK9MaCAq5=PTG5w49gbRcQgVQ@mail.gmail.com>
URL: https://gitlist.dev/e/CACpeKQ%3DsPOc2AqsMT--2tu%3DBMEK9MaCAq5%3DPTG5w49gbRcQgVQ%40mail.gmail.com

```
While inspecting the reference transaction hook code,
I realised that the hook was receiving a CREATE ref update line even for
references that already existed on disk, where I was expecting to see
an UPDATE ref update line.
That behaviour was observed for the file and the reftable backends.

Using the `reference-transaction` file bellow:
ˋˋˋ
$ cat .git/hooks/reference-transaction
if test "$1" = prepared
then
         while read -r line
         do
                 printf "%s\n" "$line"
         done >>actual
fi
ˋˋˋ

I created the commits A and B

ˋˋˋ
$ git commit --allow-empty -m "A"; git tag A
$ git commit --allow-empty -m "B"; git tag B
ˋˋˋ

Checked which refs were created:
ˋˋˋ
$ git show-ref
9b18557013105bb7a7bf681f18757084ada9d948 refs/heads/master
01077b2840db5baea0084921d8f3158a240e8d85 refs/tags/A
9b18557013105bb7a7bf681f18757084ada9d948 refs/tags/B
ˋˋˋ

Then ran `git update-ref refs/heads/master A`  which logged:
ˋˋˋ
0000000000000000000000000000000000000000
01077b2840db5baea0084921d8f3158a240e8d85 refs/heads/master
ˋˋˋ

But I expected:
ˋˋˋ
9b18557013105bb7a7bf681f18757084ada9d948
01077b2840db5baea0084921d8f3158a240e8d85 refs/heads/master
ˋˋˋ

The git/git t1416-ref-transaction-hooks.sh update tests seem to be
expecting a $ZERO_OID for reference update, so
I am not sure if that's a bug or a feature.

A test case: https://github.com/git/git/blob/2462961280690837670d997bde64bd4ebf8ae66d/t/t1416-ref-transaction-hooks.sh#L55

CREATE: <zero_oid> <new_oid> <ref_name>
UPDATE: <old_oid> <new_oid> <ref_name>

I ran those tests using the git version `2.51.0` on `Ubuntu 20.04.6 LTS`.

```

## Jeff King, 2025-09-03 14:44

Subject: Re: Reference transaction hook is unable to find the old_oid for pre-existing refs
Message-ID: <20250903144423.GC1884731@coredump.intra.peff.net>
URL: https://gitlist.dev/e/20250903144423.GC1884731%40coredump.intra.peff.net
In-Reply-To: <CACpeKQ=sPOc2AqsMT--2tu=BMEK9MaCAq5=PTG5w49gbRcQgVQ@mail.gmail.com>

```
On Wed, Sep 03, 2025 at 04:11:05PM +0200, elhmn wrote:

> Then ran `git update-ref refs/heads/master A`  which logged:
> ˋˋˋ
> 0000000000000000000000000000000000000000
> 01077b2840db5baea0084921d8f3158a240e8d85 refs/heads/master
> ˋˋˋ
> 
> But I expected:
> ˋˋˋ
> 9b18557013105bb7a7bf681f18757084ada9d948
> 01077b2840db5baea0084921d8f3158a240e8d85 refs/heads/master
> ˋˋˋ

I don't think the first value is the old value of the ref; it's the old
value that was passed in to the transaction. From githooks(5):

         For each reference update that was added to the transaction, the
         hook receives on standard input a line of the format:
  
             <old-value> SP <new-value> SP <ref-name> LF
  
         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.

You didn't specify an old value, since your "update-ref" command only
gave the refname and the new value. So you are "force updating" the ref
in the sense that it will be overwritten regardless of the previous
value. The same doc goes on to say:

         When force updating the reference regardless of its current
         value or when the reference is 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.

So I think it is behaving as documented. It is a little unfortunate that
a transaction hook cannot tell the difference between "the ref must not
currently exist" versus "the caller did not specify the old value". An
empty string or something for the latter would have allowed that. But it
is too late to adjust the interface now. Possibly we could pass an extra
bit of information to say whether an old value was passed, but I think
it's tricky to do it in a backwards compatible way (e.g., we cannot just
do so in an environment variable, because the value may be different for
each ref that comes over stdin).

-Peff

```
