Thread (2 messages) 2 messages, 2 authors, 10h ago

[BUG] branch copy/rename update refs without running the reference-transaction hook

From: Никита Поникаров <hidden>
Date: 2026-10-02 04:13:09

Hi,

The report below was investigated and drafted by Claude in the process
of building a branch-protection tool; I've reviewed it and verified
the reproduction before sending. Happy to answer questions or test
patches.

---

githooks(5) says the reference-transaction hook "is invoked by any Git
command that performs reference updates". Some branch copy and rename
operations update a ref without invoking it.

Observed on git version 2.55.0.windows.5, with the hook registered both
in .git/hooks and as a config-defined hook (hook.<name>.event).

1. `git branch -C <src> <dst>` where <dst> exists and is not checked out
   anywhere: <dst> is overwritten with <src>'s value and the hook is not
   invoked in any phase. This happens on both the files and the reftable
   backend. With every documented hook event registered to a logging
   script, none of them fired, and a GIT_TRACE2_EVENT log showed no child
   process.

2. `git branch -c <src> <dst>` where <dst> does not exist: <dst> is
   created and the hook sees no line for it.

3. `git branch -m/-M <src> <dst>`:
   - files backend: the hook sees the deletion of <src> and a
     "0000... 0000... refs/heads/<dst>" line, but no line carrying
     <dst>'s new value;
   - reftable backend: no line names <dst> at all, and renaming a branch
     away deletes it without a line for it.

4. `git reflog delete --updateref --rewrite <branch>@{0}` rewinds the
   branch without invoking the hook, on both backends.

Reproduction (any POSIX shell):

  git init -q -b main t && cd t
  git commit -q --allow-empty -m one
  git branch other
  git commit -q --allow-empty -m two
  git switch -q other
  printf '#!/bin/sh\necho "hook $1" >>"$PWD/hook.log"\ncat
/dev/null\n' >../h.sh
  git config hook.log.event reference-transaction
  git config hook.log.command "sh $(cd ..; pwd)/h.sh"
  : >hook.log
  git branch -C other main     # main now points at "one"
  cat hook.log                 # empty

Expected: the hook runs with a line updating refs/heads/main, as it does
for `git branch -f main other`. Tools that enforce policy through this
hook cannot otherwise see these updates.

Note that git already refuses -C/-M onto a branch checked out in any
worktree, so the gap is limited to branches that are not checked out.
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help