threads / discuss / 64082

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

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

## tl;dr

2 messages between Sep 3, 2025 and Sep 3, 2025.

replies: 1people: 2as markdown or json

elhmn· Sep 3, 2025, 14:11 UTC · lore

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· Sep 3, 2025, 14:44 UTC · re: elhmn · lore

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

On Wed, Sep 03, 2025 at 04:11:05PM +0200, elhmn wrote:
Show 11 quoted lines
> 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

← back to recent threads