githooks(5): what is the Git "null-ref"

Viewed 44

On the documentation for the post-checkout hook, it says

It is also run after git-clone[1], unless the --no-checkout (-n) option is used. The first parameter given to the hook is the null-ref, the second the ref of the new HEAD and the flag is always 1. Likewise for git worktree add unless --no-checkout is used.

What does the "null-ref" refer to here?

3 Answers

It is the commit ID consisting of all zeros.

I added the following line to ~/.config/git/template/hooks/post-checkout:

echo "post-checkout" "$@" 1>&2

and then cloned a new repository, getting the following output:

post-checkout 0000000000000000000000000000000000000000 4abcac4ddfb69f6dfde1af0164f2f0ee0e230336 1

So it looks like the "null-ref" in the post-checkout script is 0000000000000000000000000000000000000000.

The context behind this question is that I want to set a special per-repo Git config user.email value only when I clone repositories from my work's Gitlab server. Now I can put the following in the post-checkout script to change it:

# This value of `$1` is the null-ref from githooks(5) - see
# https://stackoverflow.com/a/73000183/207384.
# for sed trick see torek's answer
if [[ "$1" = "$(git rev-parse HEAD | sed s/./0/g)" ]]; then
    # Set user.email specially for work repositories.
    if [[ "$(git config remote."$(git config branch."$(git branch --show-current)".remote)".url)" =~ "gitlab.example.com" ]]; then
        git config user.name "John Doe"
        git config user.email "John Doe@example.com"
    fi
fi

This documentation probably ought to be updated a bit as the word null-ref is misleading, but you're correct that it is an all-zeros hash ID. Git uses the phrase "null oid" elsewhere, especially internally with a function is_null_oid. Since "OID" is an acronym it's usually upper-cased in English text, and "null" sometimes comes along for the ride: see, e.g., the 2.18 Release Notes, vs the git cat-file documentation where it's spelled "null OID".

I personally think git rev-parse should have an option for spitting this out (along with the hash ID of the empty tree), since SHA-256 OIDs are 64 bytes long instead of 40 bytes long. However, as a stopgap measure, one can use:

null_oid=$(git rev-parse HEAD | sed s/./0/g)

or, taking the hash-object trick a bit further:

empty_tree=$(git hash-object -t tree --stdin </dev/null)
null_oid=$(echo $empty_tree | sed s/./0/g)

which gets you both and will continue to work in some future Git that has longer OIDs.

The documentation says, for the normal git checkout case that the first two parameters are

The hook is given three parameters: the ref of the previous HEAD, the ref of the new HEAD (which may or may not have changed), ..."

But in a git clone case, obviously, there is no previous ref. So some sort of a "null" parameter must be given. In git, the "null ref" of all zeros is provided.

Related