Re: [PATCH v2 0/5] root refs vs pseudorefs
- From
Julia Evans <julia@jvns.ca>
- Date
- Nov 2, 2026, 18:19 UTC
- Message-ID
- <840bc117-75e9-4d63-882e-3831156871a3@app.fastmail.com>
- In-Reply-To
- <20261101230326.3675597-1-andy.koppe@gmail.com>
On Sun, Nov 1, 2026, at 6:03 PM, Andy Koppe wrote:
Show 196 quoted lines
> Changes in v2:
> - Tweak pseudoref definition.
> - Add prefix lists for per-worktree refs.
> - Wrap lists in '--'.
> - Add root refs and pseudorefs to ref types in gitdatamodel(7).
> - Trim messages of commits 3 and 4.
>
> Andy Koppe (5):
> doc: glossary: add root ref and clarify pseudorefs
> doc: glossary: update per-worktree refs
> doc: stop calling root refs pseudorefs
> refs: stop calling root refs pseudorefs
> t: stop calling root refs pseudorefs
>
> Documentation/config/core.adoc | 2 +-
> Documentation/for-each-ref-options.adoc | 2 +-
> Documentation/git-ls-remote.adoc | 4 +-
> Documentation/git-update-ref.adoc | 13 ++-
> Documentation/git-worktree.adoc | 13 ++-
> Documentation/gitdatamodel.adoc | 18 +++-
> Documentation/glossary-content.adoc | 98 ++++++++++++-------
> Documentation/rev-list-options.adoc | 4 +-
> builtin/for-each-ref.c | 2 +-
> contrib/completion/git-completion.bash | 7 +-
> refs.h | 13 ++-
> revision.c | 2 +-
> t/show-ref-exists-tests.sh | 4 +-
> t/t0600-reffiles-backend.sh | 2 +-
> t/t0610-reftable-basics.sh | 2 +-
> t/t1400-update-ref.sh | 66 ++++++-------
> t/t1403-show-ref.sh | 2 +-
> t/t1460-refs-migrate.sh | 4 +-
> t/t4013-diff-various.sh | 6 +-
> ...iff.diff-tree_--cc_--summary_REVERSE_HEAD} | 2 +-
> t/t6302-for-each-ref-filter.sh | 4 +-
> t/t9902-completion.sh | 4 +-
> 22 files changed, 158 insertions(+), 116 deletions(-)
> rename t/t4013/{diff.diff-tree_--cc_--summary_REVERSE =>
> diff.diff-tree_--cc_--summary_REVERSE_HEAD} (73%)
>
> Range-diff against v1:
> 1: 9979ad1589 ! 1: 4734fa2f2e doc: glossary: add root ref and clarify
> pseudorefs
> @@ Documentation/glossary-content.adoc: exclude;;
> - A ref that has different semantics than normal refs. These refs
> can be
> - read via normal Git commands, but cannot be written to by
> commands like
> - linkgit:git-update-ref[1].
> -+ A name that can be used like a <<def_ref,ref>> to refer to
> -+ <<def_object,objects>>, but that is not stored in the same way
> as refs.
> -+ For example, pseudorefs cannot be written by
> linkgit:git-update-ref[1]
> -+ or listed by linkgit:git-for-each-ref[1].
> ++ A name that can be used like a <<def_ref,ref>> to refer to an
> ++ <<def_object,object>>, but that is not stored in the same way as
> refs.
> ++ For example, pseudorefs can be passed to linkgit:git-log[1] or
> ++ linkgit:git-diff[1], but they cannot be written by
> ++ linkgit:git-update-ref[1] or listed by
> linkgit:git-for-each-ref[1].
> +
> The following pseudorefs are known to Git:
> -
> -@@ Documentation/glossary-content.adoc: The following pseudorefs
> are known to Git:
> +-
> +++
> ++--
> + - `FETCH_HEAD` is written by linkgit:git-fetch[1] or
> linkgit:git-pull[1]. It
> + may refer to multiple object IDs. Each object ID is annotated
> with metadata
> + indicating where it was fetched from and its fetch status.
>
> - `MERGE_HEAD` is written by linkgit:git-merge[1] when resolving merge
> conflicts. It contains all commit IDs which are being merged.
> ++--
> ++
> +When a pseudoref contains more than one object ID, using it to refer to an
> +object yields the first one.
> 2: 8932691331 ! 2: 62f4ae009c doc: glossary: update per-worktree refs
> @@ Documentation/glossary-content.adoc: exclude;;
> - that start with `refs/bisect/`, but might later include other
> - unusual refs.
> + A <<def_ref,ref>> that is specific to a <<def_worktree,worktree>>,
> -+ rather than shared between all worktrees. All
> -+ <<def_root_ref,root refs>>, such as <<def_HEAD,HEAD>>, are
> -+ per-worktree, as are refs that start with `refs/bisect/`,
> -+ `refs/worktree/` or `refs/rewritten/`.
> ++ rather than shared between all worktrees. All <<def_root_ref,root refs>>,
> ++ for example <<def_HEAD,HEAD>> and `ORIG_HEAD`, are per-worktree, as are
> ++ refs with the following prefixes:
> +++
> ++--
> ++ - `refs/bisect/`
> ++
> ++ - `refs/rewritten/`
> ++
> ++ - `refs/worktree/`
> ++--
> ++
> +<<def_pseudoref,Pseudorefs>> are also specific to each worktree.
> ++
> @@ Documentation/glossary-content.adoc: exclude;;
> +per-worktree refs of other worktrees.
>
> [[def_pseudoref]]pseudoref::
> - A name that can be used like a <<def_ref,ref>> to refer to
> + A name that can be used like a <<def_ref,ref>> to refer to an
> @@ Documentation/glossary-content.adoc: and pseudorefs.
> "working tree" and repository metadata, most of which are
> shared among other worktrees of a single repository, and
> some of which are maintained separately per worktree
> - (e.g. the index, HEAD and pseudorefs like MERGE_HEAD,
> - per-worktree refs and per-worktree configuration file).
> -+ (e.g. the index, per-worktree refs such as HEAD, pseudorefs
> -+ such as MERGE_HEAD, and the per-worktree configuration file).
> ++ (e.g. the index, <<def_per_worktree_ref,per-worktree refs>>, and the
> ++ per-worktree configuration file).
> 3: 8cdd299c28 ! 3: 73dcb3d715 doc: stop calling root refs pseudorefs
> @@ Commit message
> there, correct "symbolics" to "symbolic refs".
>
> In git-update-ref(1), drop the incorrect mention of ORIG_HEAD. HEAD is
> - the only root ref that gets a reflog by default. Also restructure the
> - sentence to make it easier to parse.
> + the only root ref that gets a reflog by default. In the description of
> + core.logAllRefUpdates, correct the claim that "always" only applies to
> + refs under "refs/". It applies to root refs too.
>
> - In the description of core.logAllRefUpdates, correct the claim that
> - "always" only applies to refs under "refs/". It applies to root refs
> - too.
> + Distinguish root refs and pseudorefs in gitdatamodel(7). Lowercase the
> + "other references" list entry there to match the others.
>
> Signed-off-by: Andy Koppe <andy.koppe@gmail.com>
>
> @@ Documentation/git-worktree.adoc: but others are specific to an
> individual worktr
> -however: refs inside `refs/bisect`, `refs/worktree` and
> `refs/rewritten` are
> -not shared.
> +In general, all root refs and pseudorefs are per-worktree and all
> refs
> -+starting with `refs/` are shared. There are exceptions, however:
> refs
> -+inside `refs/bisect`, `refs/worktree` and `refs/rewritten` are not
> -+shared.
> ++starting with `refs/` are shared, except for refs with the
> following prefixes:
> ++
> ++ - `refs/bisect/`
> ++
> ++ - `refs/rewritten/`
> ++
> ++ - `refs/worktree/`
>
> Refs that are per-worktree can still be accessed from another worktree via
> two special paths, `main-worktree` and `worktrees`. The former gives
>
> + ## Documentation/gitdatamodel.adoc ##
> +@@ Documentation/gitdatamodel.adoc: remote-tracking branches:
> `refs/remotes/<remote>/<branch>`::
> + default branch. This is the branch that `git clone` checks out by
> default.
> +
> + [[other-refs]]
> +-Other references::
> ++other references::
> + Git tools may create references anywhere under `refs/`.
> + For example, linkgit:git-stash[1], linkgit:git-bisect[1],
> + and linkgit:git-notes[1] all create their own references
> + in `refs/stash`, `refs/bisect`, etc.
> + Third-party Git tools may also create their own references.
> +-+
> +-Git may also create references other than `HEAD` at the base of
> the
> +-hierarchy, like `ORIG_HEAD`.
> ++
> ++[[root-refs]]
> ++root refs::
> ++ Git may also create references other than `HEAD` at the root
> of the
> ++ hierarchy, for example `ORIG_HEAD` or `REBASE_HEAD`.
> ++
> ++[[pseudorefs]]
> ++pseudorefs::
> ++ linkgit:git-fetch[1] and linkgit:git-merge[1] create the
> so-called
> ++ pseudorefs `FETCH_HEAD` and `MERGE_HEAD`. They are not
> references as
> ++ such, as they are not stored in the same way, but they can
> neverthelessWhen I look at my .git directory during a merge conflict, I see these files:
$ ls .git | grep HEAD FETCH_HEAD HEAD MERGE_HEAD ORIG_HEAD
From the above, it sounds like ORIG_HEAD is a "root ref" and MERGE_HEAD is a "pseudoref", but naively it does look like they're stored in the same way. What's the difference?