{"thread":{"id":"64082","subject":"Reference transaction hook is unable to find the old_oid for pre-existing refs","startedAt":"2025-09-03T14:11:18Z","lastAt":"2025-09-03T14:44:24Z","messageCount":2,"participants":["elhmn","Jeff King"],"isPatch":false,"patchVersion":null,"patchTotal":null},"messages":[{"id":"525430","messageId":"CACpeKQ=sPOc2AqsMT--2tu=BMEK9MaCAq5=PTG5w49gbRcQgVQ@mail.gmail.com","threadId":"64082","inReplyTo":null,"subject":"Reference transaction hook is unable to find the old_oid for pre-existing refs","fromName":"elhmn","fromEmail":"elhmn42@gmail.com","sentAt":"2025-09-03T14:11:05Z","receivedAt":"2025-09-03T14:11:18Z","isPatch":false,"sender":{"key":"elhmn42@gmail.com","avatar":null},"body":"While inspecting the reference transaction hook code,\nI realised that the hook was receiving a CREATE ref update line even for\nreferences that already existed on disk, where I was expecting to see\nan UPDATE ref update line.\nThat behaviour was observed for the file and the reftable backends.\n\nUsing the `reference-transaction` file bellow:\n```\n$ cat .git/hooks/reference-transaction\nif test \"$1\" = prepared\nthen\n         while read -r line\n         do\n                 printf \"%s\\n\" \"$line\"\n         done >>actual\nfi\n```\n\nI created the commits A and B\n\n```\n$ git commit --allow-empty -m \"A\"; git tag A\n$ git commit --allow-empty -m \"B\"; git tag B\n```\n\nChecked which refs were created:\n```\n$ git show-ref\n9b18557013105bb7a7bf681f18757084ada9d948 refs/heads/master\n01077b2840db5baea0084921d8f3158a240e8d85 refs/tags/A\n9b18557013105bb7a7bf681f18757084ada9d948 refs/tags/B\n```\n\nThen ran `git update-ref refs/heads/master A`  which logged:\n```\n0000000000000000000000000000000000000000\n01077b2840db5baea0084921d8f3158a240e8d85 refs/heads/master\n```\n\nBut I expected:\n```\n9b18557013105bb7a7bf681f18757084ada9d948\n01077b2840db5baea0084921d8f3158a240e8d85 refs/heads/master\n```\n\nThe git/git t1416-ref-transaction-hooks.sh update tests seem to be\nexpecting a $ZERO_OID for reference update, so\nI am not sure if that's a bug or a feature.\n\nA test case: https://github.com/git/git/blob/2462961280690837670d997bde64bd4ebf8ae66d/t/t1416-ref-transaction-hooks.sh#L55\n\nCREATE: <zero_oid> <new_oid> <ref_name>\nUPDATE: <old_oid> <new_oid> <ref_name>\n\nI ran those tests using the git version `2.51.0` on `Ubuntu 20.04.6 LTS`.\n"},{"id":"525434","messageId":"20250903144423.GC1884731@coredump.intra.peff.net","threadId":"64082","inReplyTo":"CACpeKQ=sPOc2AqsMT--2tu=BMEK9MaCAq5=PTG5w49gbRcQgVQ@mail.gmail.com","subject":"Re: Reference transaction hook is unable to find the old_oid for pre-existing refs","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2025-09-03T14:44:23Z","receivedAt":"2025-09-03T14:44:24Z","isPatch":false,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Wed, Sep 03, 2025 at 04:11:05PM +0200, elhmn wrote:\n\n> Then ran `git update-ref refs/heads/master A`  which logged:\n> ```\n> 0000000000000000000000000000000000000000\n> 01077b2840db5baea0084921d8f3158a240e8d85 refs/heads/master\n> ```\n> \n> But I expected:\n> ```\n> 9b18557013105bb7a7bf681f18757084ada9d948\n> 01077b2840db5baea0084921d8f3158a240e8d85 refs/heads/master\n> ```\n\nI don't think the first value is the old value of the ref; it's the old\nvalue that was passed in to the transaction. From githooks(5):\n\n         For each reference update that was added to the transaction, the\n         hook receives on standard input a line of the format:\n  \n             <old-value> SP <new-value> SP <ref-name> LF\n  \n         where <old-value> is the old object name passed into the\n         reference transaction, <new-value> is the new object name to be\n         stored in the ref and <ref-name> is the full name of the ref.\n\nYou didn't specify an old value, since your \"update-ref\" command only\ngave the refname and the new value. So you are \"force updating\" the ref\nin the sense that it will be overwritten regardless of the previous\nvalue. The same doc goes on to say:\n\n         When force updating the reference regardless of its current\n         value or when the reference is to be created anew, <old-value>\n         is the all-zeroes object name. To distinguish these cases, you\n         can inspect the current value of <ref-name> via git rev-parse.\n\nSo I think it is behaving as documented. It is a little unfortunate that\na transaction hook cannot tell the difference between \"the ref must not\ncurrently exist\" versus \"the caller did not specify the old value\". An\nempty string or something for the latter would have allowed that. But it\nis too late to adjust the interface now. Possibly we could pass an extra\nbit of information to say whether an old value was passed, but I think\nit's tricky to do it in a backwards compatible way (e.g., we cannot just\ndo so in an environment variable, because the value may be different for\neach ref that comes over stdin).\n\n-Peff\n"}]}