Why does git update-ref accepts non /refs references?

Git 2.18 (Q2 2018) improves git update-ref robustness, because "git update-ref A B" is supposed to ensure that ref A does not yet exist when B is a NULL OID, but this check was not done correctly for pseudo-refs outside refs/ hierarchy, e.g. MERGE_HEAD.

See commit db0210d, commit 65eb8fc, commit c0bdd65 (10 May 2018) by Martin Ågren (``).
Helped-by: Rafael Ascensão [email protected].
(Merged by Junio C Hamano -- gitster -- in commit 26597cb, 30 May 2018)

refs: handle zero oid for pseudorefs

According to the documentation, it is possible to "specify 40 '0' or an empty string as <oldvalue> to make sure that the ref you are creating does not exist."
But in the code for pseudorefs, we do not implement this, as demonstrated by the failing tests added in the previous commit.
If we fail to read the old ref, we immediately die. But a failure to read would actually be a good thing if we have been given the zero oid.

With the zero oid, allow -- and even require -- the ref-reading to fail. This implements the "make sure that the ref ... does not exist" part of the documentation and fixes both failing tests from the previous commit.

Since we have a strbuf err for collecting errors, let's use it and signal an error to the caller instead of dying hard.


Note: as explained in "Does git lock a remote for writing when a user pushes?"

some references are considered "pseudorefs".
These are never packed.
The pseudorefs are things like ORIG_HEAD, MERGE_HEAD, and so on.


With Git 2.29 (Q4 2020), starts preliminary changes to refs API, which does influence those non refs references.

See commit 55dd8b9, commit 0974341 (27 Jul 2020), and commit 0b7de6c (16 Jul 2020) by Han-Wen Nienhuys (hanwen).
(Merged by Junio C Hamano -- gitster -- in commit 95c687b, 17 Aug 2020)

pseudorefs:Modify pseudo refs through ref backend storage

Signed-off-by: Han-Wen Nienhuys

The previous behavior was introduced in commit 74ec19d4be ("pseudorefs: create and use pseudoref update and delete functions", Jul 31, 2015, Git v2.6.0-rc0), with the justification "**alternate ref backends still need to store pseudorefs in GIT_DIR**".

Refs such as REBASE_HEAD are read through the ref backend.
This can only work consistently if they are written through the ref backend as well. Tooling that works directly on files under .git should be updated to use commands to read refs instead.

The following behaviors change:

  • Updates to pseudorefs (eg. ORIG_HEAD) with core.logAllRefUpdates=always will create reflogs for the pseudoref.

  • non-HEAD pseudoref symrefs are also dereferenced on deletion.

git update-ref now includes in its man page:

If config parameter "core.logAllRefUpdates" is true and the ref is:

  • one under "refs/heads/", "refs/remotes/", "refs/notes/", or
  • a pseudoref like HEAD or ORIG_HEAD; or
  • the file "$GIT_DIR/logs/<ref>" exists

then git update-ref will append a line to the log file "$GIT_DIR/logs/<ref>" (dereferencing all symbolic refs before creating the log name) describing the change in ref value.


With Git 2.29 (Q4 2020), adjust sample hooks for hash algorithm other than SHA-1.

See commit d8d3d63, commit 8c7e505, commit 6a117da (23 Sep 2020) by Denton Liu (Denton-L).
(Merged by Junio C Hamano -- gitster -- in commit 9f489ac, 29 Sep 2020)

hooks--update.sample: use hash-agnostic zero OID

Signed-off-by: Denton Liu

The update sample hook has the zero OID hardcoded as 40 zeros.

However, with the introduction of SHA-256 support, this assumption no longer holds true.
Replace the hardcoded $z40 with a call to

git hash-object --stdin </dev/null | tr '[0-9a-f]' '0'  

so the sample hook becomes hash-agnostic.


tl;dr

The main reason why git log and git update-ref behave differently is because git-log is a high-level command – and therefore designed to be user-friendly – while git-update-ref is a low-level command meant to be used in scripts.

Porcelain vs. Plumbing

In Git parlance, high-level commands are referred to as porcelain while low-level ones are collectively called plumbing.

Porcelain commands are meant to be used interactively by humans and therefore expose familiar high-level concepts such as symbolic references. Plumbing commands, on the other hand, are meant to be used programmatically – usually in scripts – and allow to directly manipulate Git's internal file system structures.

git-update-ref

While git-log is able to resolve references, git-update-ref – it being a plumbing command – interprets the first argument as either a symlink or a regular file name depending on how it's specified.

From the documentation:

It follows real symlinks only if they start with "refs/": otherwise it will just try to read them and update them as a regular file.

So that's why if you say git update-ref master <value> it will treat master as a file name and create it in the .git directory. By the same token, when you say git update-ref HEAD <value> it will write <value> to the .git/HEAD file.

Tags:

Git

Reference