root refs vs pseudorefs

Started by Andy Koppe on Oct 22, 2026 · 45 messages from 4 authors · Last reply Nov 6, 2026

Series

5 patches, 21 files

+129 −111

Subsystems (2)

Maintainers (2)

WARM1d SUPERSEDED

Status: Superseded by v3 (posted 2026-11-06). [go to v3]

Revision v1 (3 revisions archived).

Revisions (3)
  1. v1 current
  2. v2 [diff against previous]
  3. v3 [diff against previous]

open the first message on its own page

  1. [PATCH 1/5] doc: glossary: add root ref and clarify pseudorefs
    Revisions and related patches
    Revisions (2)
    1. v1 current re-posted go to this post
    2. v3 [diff against previous]
  2. [PATCH 2/5] doc: glossary: update per-worktree refs
    Revisions and related patches
    Revisions (2)
    1. v1 current re-posted go to this post
    2. v3 [diff against previous]
  3. [PATCH 3/5] doc: stop calling root refs pseudorefs
    Revisions and related patches
    Revisions (2)
    1. v1 current re-posted go to this post
    2. v3 [diff against previous]
  4. [PATCH 4/5] refs: stop calling root refs pseudorefs
    Revisions and related patches
    Revisions (2)
    1. v1 current re-posted go to this post
    2. v3 [diff against previous]
  5. [PATCH 5/5] t: stop calling root refs pseudorefs
    Revisions and related patches
    Revisions (2)
    1. v1 current re-posted go to this post
    2. v3 [diff against previous]
  6. [PATCH v2 1/5] doc: glossary: add root ref and clarify pseudorefs
    Revisions and related patches
    Revisions (2)
    1. v1 current re-posted current
    2. v3 [diff against previous]
  7. [PATCH v2 2/5] doc: glossary: update per-worktree refs
    Revisions and related patches
    Revisions (2)
    1. v1 current re-posted current
    2. v3 [diff against previous]
  8. [PATCH v2 3/5] doc: stop calling root refs pseudorefs
    Revisions and related patches
    Revisions (2)
    1. v1 current re-posted current
    2. v3 [diff against previous]
  9. [PATCH v2 4/5] refs: stop calling root refs pseudorefs
    Revisions and related patches
    Revisions (2)
    1. v1 current re-posted current
    2. v3 [diff against previous]
  10. [PATCH v2 5/5] t: stop calling root refs pseudorefs
    Revisions and related patches
    Revisions (2)
    1. v1 current re-posted current
    2. v3 [diff against previous]
  11. [PATCH v3 1/5] doc: glossary: add root ref and clarify pseudorefs
    Revisions and related patches
    Revisions (2)
    1. v1 re-posted go to this post
    2. v3 current [diff against previous]
  12. [PATCH v3 2/5] doc: glossary: update per-worktree refs
    Revisions and related patches
    Revisions (2)
    1. v1 re-posted go to this post
    2. v3 current [diff against previous]
  13. [PATCH v3 3/5] doc: stop calling root refs pseudorefs
    Revisions and related patches
    Revisions (2)
    1. v1 re-posted go to this post
    2. v3 current [diff against previous]
  14. [PATCH v3 4/5] refs: stop calling root refs pseudorefs
    Revisions and related patches
    Revisions (2)
    1. v1 re-posted go to this post
    2. v3 current [diff against previous]
  15. [PATCH v3 5/5] t: stop calling root refs pseudorefs
    Revisions and related patches
    Revisions (2)
    1. v1 re-posted go to this post
    2. v3 current [diff against previous]
Message details and related content
Since 993d57eded (refs: pseudorefs are no refs, 2024-05-15), the term
"pseudoref" has been restricted to FETCH_HEAD and MERGE_HEAD, which are
not stored in the ref backends, while other refs outside of "refs/",
such as HEAD and ORIG_HEAD, are called root refs. However, a fair number
of docs, comments, messages and tests still use "pseudoref" in its old
sense. And while the glossary describes the rules for refs in the root
of the hierarchy, it does not explicitly define the term, even though
"git for-each-ref --include-root-refs" uses it.

This series adds a "root ref" entry to the glossary and clarifies the
"pseudoref" entry, and then replaces the outdated uses of "pseudoref" in
docs, code and tests. While at it, it also updates the long outdated
glossary entry for per-worktree refs, and fixes a few inaccuracies
found along the way, for example about which refs get a reflog in
git-update-ref(1) and core.logAllRefUpdates.

I've left __git_pseudoref_exists() in the completion script alone apart
from its comment, even though it's used for both pseudorefs and root
refs. It could be renamed, keeping the old name as a deprecated wrapper
like elsewhere in the script, but it's not obvious what to, because
there is no term covering both.

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               |  9 +-
 Documentation/glossary-content.adoc           | 86 ++++++++++++-------
 Documentation/rev-list-options.adoc           |  4 +-
 builtin/for-each-ref.c                        |  2 +-
 contrib/completion/git-completion.bash        |  7 +-
 refs.h                                        | 13 ++-
 revision.c                                    |  2 +-

18 more lines

[PATCH 1/5] doc: glossary: add root ref and clarify pseudorefs

Message details and related content
The glossary describes refs in the root of the ref hierarchy, but does
not actually name them "root refs", even though the term is used
elsewhere, for example by "git for-each-ref --include-root-refs". It
also defines pseudorefs as refs, yet is_root_ref() deliberately excludes
them, as they are not stored or handled by the ref backends.

Add a "root ref" entry, moving the naming rules and the list of
irregular root refs there from the "ref" entry. State that pseudorefs
are not root refs, and refer to gitrevisions(7) for descriptions of
commonly used root refs and pseudorefs.

Reword the "pseudoref" entry so that it no longer calls them refs, add
git-for-each-ref(1) as another example of how they are not proper refs,
and explain that using a pseudoref containing multiple object IDs yields
the first one.

Signed-off-by: Andy Koppe <redacted>
---
 Documentation/glossary-content.adoc | 68 +++++++++++++++++------------
 1 file changed, 41 insertions(+), 27 deletions(-)
diff --git a/Documentation/glossary-content.adoc b/Documentation/glossary-content.adoc
index 52301a56dd..b2c582254b 100644
--- a/Documentation/glossary-content.adoc
+++ b/Documentation/glossary-content.adoc
@@ -500,9 +500,10 @@ exclude;;
 	unusual refs.
 
 [[def_pseudoref]]pseudoref::
-	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].
 +
 The following pseudorefs are known to Git:
 
@@ -512,6 +513,9 @@ The following pseudorefs are known to Git:
 
  - `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.
 
 [[def_pull]]pull::
 	Pulling a <<def_branch,branch>> means to <<def_fetch,fetch>> it and
@@ -562,33 +566,12 @@ The following pseudorefs are known to Git:
 +
 The ref namespace is hierarchical.
 Ref names must either start with `refs/` or be located in the root of
-the hierarchy. For the latter, their name must follow these rules:
-+
---
- - The name consists of only upper-case characters or underscores.
-
- - The name ends with "`_HEAD`" or is equal to "`HEAD`".
---
-+
-There are some irregular refs in the root of the hierarchy that do not
-match these rules. The following list is exhaustive and shall not be
-extended in the future:
-+
---
- - `AUTO_MERGE`
-
- - `BISECT_EXPECTED_REV`
-
- - `NOTES_MERGE_PARTIAL`
-
- - `NOTES_MERGE_REF`
-
- - `MERGE_AUTOSTASH`
---
+the hierarchy, in which case they must follow the naming rules for
+<<def_root_ref,root refs>>.
 +
 Different subhierarchies are used for different purposes. For example,
 the `refs/heads/` hierarchy is used to represent local branches whereas
-the `refs/tags/` hierarchy is used to represent local tags..
+the `refs/tags/` hierarchy is used to represent local tags.
 +
 See also "REFERENCES" in linkgit:gitdatamodel[7].
 
@@ -640,6 +623,37 @@ format. See linkgit:git-reflog[1] for details.
 	To throw away part of the development, i.e. to assign the
 	<<def_head,head>> to an earlier <<def_revision,revision>>.
 
+[[def_root_ref]]root ref::
+	A <<def_ref,ref>> located in the root of the ref hierarchy, outside
+	`refs/`. Root ref names must follow these rules:
++
+--
+ - The name consists of only upper-case characters or underscores.
+
+ - The name ends with "`_HEAD`" or is equal to "`HEAD`".
+--
++
+The following irregular root refs are exceptions to these rules. This
+list is exhaustive and shall not be extended in future:
++
+--
+ - `AUTO_MERGE`
+
+ - `BISECT_EXPECTED_REV`
+
+ - `NOTES_MERGE_PARTIAL`
+
+ - `NOTES_MERGE_REF`
+
+ - `MERGE_AUTOSTASH`
+--
++
+<<def_pseudoref,Pseudorefs>> are not considered root refs, even though
+they follow the naming rules.
++
+See linkgit:gitrevisions[7] for descriptions of commonly used root refs
+and pseudorefs.
+
 [[def_SCM]]SCM::
 	Source code management (tool).
 
-- 
2.56.0

[PATCH 2/5] doc: glossary: update per-worktree refs

Message details and related content
The "per-worktree ref" entry says that these are only HEAD and refs
under "refs/bisect/", but that has been out of date since a9be29c981
(sequencer: make refs generated by the `label` command worktree-local,
2018-04-25). All root refs are per-worktree, as are refs under
"refs/worktree/" and "refs/rewritten/". Pseudorefs are specific to each
worktree too.

Bring the list up to date, and refer to git-worktree(1) for how to
access per-worktree refs of other worktrees. In the "worktree" entry,
describe HEAD as a per-worktree ref and MERGE_HEAD as a pseudoref,
rather than listing them separately from per-worktree refs.

Signed-off-by: Andy Koppe <redacted>
---
 Documentation/glossary-content.adoc | 18 ++++++++++++------
 1 file changed, 12 insertions(+), 6 deletions(-)
diff --git a/Documentation/glossary-content.adoc b/Documentation/glossary-content.adoc
index b2c582254b..eff6d23215 100644
--- a/Documentation/glossary-content.adoc
+++ b/Documentation/glossary-content.adoc
@@ -494,10 +494,16 @@ exclude;;
 	interface than the <<def_plumbing,plumbing>>.
 
 [[def_per_worktree_ref]]per-worktree ref::
-	Refs that are per-<<def_worktree,worktree>>, rather than
-	global.  This is presently only <<def_HEAD,HEAD>> and any refs
-	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/`.
++
+<<def_pseudoref,Pseudorefs>> are also specific to each worktree.
++
+See "REFS" in linkgit:git-worktree[1] for how to access the
+per-worktree refs of other worktrees.
 
 [[def_pseudoref]]pseudoref::
 	A name that can be used like a <<def_ref,ref>> to refer to
@@ -788,5 +794,5 @@ 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).
-- 
2.56.0

[PATCH 3/5] doc: stop calling root refs pseudorefs

Message details and related content
Several manual pages still use "pseudoref" in its old sense of any ref
outside of "refs/", which the glossary now calls a root ref. Pseudorefs
have since been restricted to FETCH_HEAD and MERGE_HEAD, which are not
considered root refs.

Replace "pseudoref" with "root ref" in those pages, giving HEAD and
ORIG_HEAD as examples. In git-worktree(1), say that both root refs and
pseudorefs are per-worktree, and drop the description of them as being
directly under $GIT_DIR, because that does not apply to the reftable
backend.

In git-ls-remote(1), describe --refs as omitting refs outside "refs/",
since that is what it actually checks. In the example, refer to HEAD,
which is the only root ref that git-upload-pack(1) advertises. While
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.

In the description of core.logAllRefUpdates, correct the claim that
"always" only applies to refs under "refs/". It applies to root refs
too.

Signed-off-by: Andy Koppe <redacted>
---
 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         |  9 ++++-----
 Documentation/rev-list-options.adoc     |  4 ++--
 6 files changed, 16 insertions(+), 18 deletions(-)
diff --git a/Documentation/config/core.adoc b/Documentation/config/core.adoc
index bdc74d291c..8f8fe36a58 100644
--- a/Documentation/config/core.adoc
+++ b/Documentation/config/core.adoc
@@ -376,7 +376,7 @@ core.logAllRefUpdates::
 	`refs/heads/`), remote refs (i.e. under `refs/remotes/`),
 	note refs (i.e. under `refs/notes/`), and the symbolic ref `HEAD`.
 	If it is set to `always`, then a missing reflog is automatically
-	created for any ref under `refs/`.
+	created for any ref, including root refs such as `ORIG_HEAD`.
 +
 This information can be used to determine what commit
 was the tip of a branch "2 days ago".
diff --git a/Documentation/for-each-ref-options.adoc b/Documentation/for-each-ref-options.adoc
index 54e2fa95c2..2b6d4ede0b 100644
--- a/Documentation/for-each-ref-options.adoc
+++ b/Documentation/for-each-ref-options.adoc
@@ -73,7 +73,7 @@ TAB %(refname)`.
 	using the same rules as _<pattern>_ above.
 
 `--include-root-refs`::
-	List root refs (`HEAD` and pseudorefs) apart from regular refs.
+	Also list root refs, such as `HEAD` or `ORIG_HEAD`.
 
 `--start-after=<marker>`::
     Allows paginating the output by skipping references up to and including the
diff --git a/Documentation/git-ls-remote.adoc b/Documentation/git-ls-remote.adoc
index d71c4ab3e2..7ce7df3149 100644
--- a/Documentation/git-ls-remote.adoc
+++ b/Documentation/git-ls-remote.adoc
@@ -35,7 +35,7 @@ OPTIONS
 	with other git subcommands.
 
 --refs::
-	Do not show peeled tags or pseudorefs like `HEAD` in the output.
+	Do not show peeled tags or refs outside `refs/`.
 
 -q::
 --quiet::
@@ -118,7 +118,7 @@ line shows the name of the object the tag points at.
 EXAMPLES
 --------
 
-* List all references (including symbolics and pseudorefs), peeling
+* List all references (including `HEAD` and symbolic refs), peeling
   tags:
 +
 ----
diff --git a/Documentation/git-update-ref.adoc b/Documentation/git-update-ref.adoc
index 37a5019a8b..c775334230 100644
--- a/Documentation/git-update-ref.adoc
+++ b/Documentation/git-update-ref.adoc
@@ -163,13 +163,12 @@ still see a subset of the modifications.
 
 LOGGING UPDATES
 ---------------
-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.  Log lines are
-formatted as:
+If config parameter "core.logAllRefUpdates" is true and the ref is
+HEAD or one under "refs/heads/", "refs/remotes/" or "refs/notes/", or
+if 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.  Log lines are formatted as:
 
     oldsha1 SP newsha1 SP committer LF
 
diff --git a/Documentation/git-worktree.adoc b/Documentation/git-worktree.adoc
index e6e77252ab..f0eb6d4137 100644
--- a/Documentation/git-worktree.adoc
+++ b/Documentation/git-worktree.adoc
@@ -303,11 +303,10 @@ but others are specific to an individual worktree. One example is `HEAD`,
 which is different for each worktree. This section is about the sharing
 rules and how to access refs of one worktree from another.
 
-In general, all pseudo refs are per-worktree and all refs starting with
-`refs/` are shared. Pseudo refs are ones like `HEAD` which are directly
-under `$GIT_DIR` instead of inside `$GIT_DIR/refs`. There are exceptions,
-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.
 
 Refs that are per-worktree can still be accessed from another worktree via
 two special paths, `main-worktree` and `worktrees`. The former gives
diff --git a/Documentation/rev-list-options.adoc b/Documentation/rev-list-options.adoc
index bd9f345690..822604278a 100644
--- a/Documentation/rev-list-options.adoc
+++ b/Documentation/rev-list-options.adoc
@@ -350,8 +350,8 @@ Under `--pretty=reference`, this information will not be shown at all.
 
 `--merge`::
 	Show commits touching conflicted paths in the range `HEAD...<other>`,
-	where `<other>` is the first existing pseudoref in `MERGE_HEAD`,
-	`CHERRY_PICK_HEAD`, `REVERT_HEAD` or `REBASE_HEAD`. Only works
+	where `<other>` is the first of `MERGE_HEAD`, `CHERRY_PICK_HEAD`,
+	`REVERT_HEAD` or `REBASE_HEAD` that exists. Only works
 	when the index has unmerged entries. This option can be used to show
 	relevant commits when resolving conflicts from a 3-way merge.
 
-- 
2.56.0

[PATCH 4/5] refs: stop calling root refs pseudorefs

Message details and related content
Some messages and comments in the code still use "pseudoref" in its old
sense of any ref outside of "refs/", whereas the glossary now calls
those root refs and restricts pseudorefs to FETCH_HEAD and MERGE_HEAD.

Replace "pseudoref" with "root ref" in the help text for
"git for-each-ref --include-root-refs", in comments in refs.h, and in
comments in the bash completion script, including the one for
__git_pseudoref_exists(), which is also used for root refs. The function
itself is not renamed. There is no obvious term covering both root refs
and pseudorefs, and the old name might need to be kept for third-party
completion scripts.

Update the description of pseudorefs in refs.h to match the glossary.

Signed-off-by: Andy Koppe <redacted>
---
 builtin/for-each-ref.c                 |  2 +-
 contrib/completion/git-completion.bash |  7 ++++---
 refs.h                                 | 13 ++++++-------
 revision.c                             |  2 +-
 4 files changed, 12 insertions(+), 12 deletions(-)
diff --git a/builtin/for-each-ref.c b/builtin/for-each-ref.c
index 4a2fc421db..6ff32e919c 100644
--- a/builtin/for-each-ref.c
+++ b/builtin/for-each-ref.c
@@ -48,7 +48,7 @@ int for_each_ref_core(int argc, const char **argv, const char *prefix, struct re
 		OPT_NO_CONTAINS(&filter.no_commit, N_("print only refs which don't contain the commit")),
 		OPT_BOOL(0, "ignore-case", &icase, N_("sorting and filtering are case insensitive")),
 		OPT_BOOL(0, "stdin", &from_stdin, N_("read reference patterns from stdin")),
-		OPT_BOOL(0, "include-root-refs", &include_root_refs, N_("also include HEAD ref and pseudorefs")),
+		OPT_BOOL(0, "include-root-refs", &include_root_refs, N_("also include root refs")),
 		OPT_END(),
 	};
 
diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
index c3a49e2e22..e12ac0359c 100644
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -147,8 +147,9 @@ __git_eread ()
 	test -r "$1" && IFS=$'\r\n' read -r "$2" <"$1"
 }
 
-# Runs git in $__git_repo_path to determine whether a pseudoref exists.
-# 1: The pseudo-ref to search
+# Runs git in $__git_repo_path to determine whether a root ref or
+# pseudoref exists.
+# 1: The ref to check
 __git_pseudoref_exists ()
 {
 	local ref=$1
@@ -954,7 +955,7 @@ __git_refs ()
 	esac
 }
 
-# Completes refs, short and long, local and remote, symbolic and pseudo.
+# Completes refs, short and long, local and remote, symbolic, root and pseudo.
 #
 # Usage: __git_complete_refs [<option>]...
 # --remote=<remote>: The remote to list refs from, can be the name of a
diff --git a/refs.h b/refs.h
index ee3b8a62ef..3be7ef3b0e 100644
--- a/refs.h
+++ b/refs.h
@@ -85,7 +85,7 @@ enum ref_transaction_error {
  * Even with RESOLVE_REF_ALLOW_BAD_NAME, names that escape the refs/
  * directory and do not consist of all caps and underscores cannot be
  * resolved. The function returns NULL for such ref names.
- * Caps and underscores refers to the pseudorefs, such as HEAD,
+ * Caps and underscores refers to root refs and pseudorefs, such as HEAD,
  * FETCH_HEAD and friends, that all live outside of the refs/ directory.
  */
 #define RESOLVE_REF_READING 0x01
@@ -448,8 +448,8 @@ enum refs_for_each_flag {
 	REFS_FOR_EACH_OMIT_DANGLING_SYMREFS = (1 << 2),
 
 	/*
-	 * Include root refs i.e. HEAD and pseudorefs along with the regular
-	 * refs.
+	 * Include root refs, such as HEAD and ORIG_HEAD, along with the
+	 * regular refs.
 	 */
 	REFS_FOR_EACH_INCLUDE_ROOT_REFS = (1 << 3),
 };
@@ -1306,10 +1306,9 @@ void update_ref_namespace(enum ref_namespace namespace, char *ref);
 int is_root_ref(const char *refname);
 
 /*
- * Pseudorefs are refs that have different semantics compared to
- * "normal" refs. These refs can thus not be stored in the ref backend,
- * but must always be accessed via the filesystem. The following refs
- * are pseudorefs:
+ * Pseudorefs are names that can be used like refs, but have different
+ * semantics. They cannot be stored in the ref backend, but must always
+ * be accessed via the filesystem. The following are pseudorefs:
  *
  * - FETCH_HEAD may contain multiple object IDs, and each one of them
  *   carries additional metadata like where it came from.
diff --git a/revision.c b/revision.c
index a83e499047..d6dd56faf1 100644
--- a/revision.c
+++ b/revision.c
@@ -2000,7 +2000,7 @@ static const char *lookup_other_head(struct object_id *oid)
 			return other_head[i];
 		}
 
-	die(_("--merge requires one of the pseudorefs MERGE_HEAD, CHERRY_PICK_HEAD, REVERT_HEAD or REBASE_HEAD"));
+	die(_("--merge requires one of MERGE_HEAD, CHERRY_PICK_HEAD, REVERT_HEAD or REBASE_HEAD"));
 }
 
 static void prepare_show_merge(struct rev_info *revs)
-- 
2.56.0

[PATCH 5/5] t: stop calling root refs pseudorefs

Message details and related content
Several test titles and comments still use "pseudoref" in its old sense
of any ref outside of "refs/", which the glossary now calls root refs.

Replace "pseudoref" with "root ref" there. In t1400 and t4013, rename
the root refs that the tests create from PSEUDOREF to ROOT_REF_HEAD and
from REVERSE to REVERSE_HEAD, so that they follow the naming rules for
root refs. This includes renaming the expected output file in t4013.

Some tests also still refer to FETCH_HEAD and MERGE_HEAD as "special
refs", which was the previous term for "pseudorefs". Correct them.

Signed-off-by: Andy Koppe <redacted>
---
 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 +-
 10 files changed, 48 insertions(+), 48 deletions(-)
 rename t/t4013/{diff.diff-tree_--cc_--summary_REVERSE => diff.diff-tree_--cc_--summary_REVERSE_HEAD} (73%)
diff --git a/t/show-ref-exists-tests.sh b/t/show-ref-exists-tests.sh
index 1351f690eb..d4a021a0c6 100644
--- a/t/show-ref-exists-tests.sh
+++ b/t/show-ref-exists-tests.sh
@@ -64,11 +64,11 @@ test_expect_success '--exists with directory fails with generic error' '
 	test_cmp expect err
 '
 
-test_expect_success '--exists with non-existent special ref' '
+test_expect_success '--exists with non-existent pseudoref' '
 	test_expect_code 2 ${git_show_ref_exists} FETCH_HEAD
 '
 
-test_expect_success '--exists with existing special ref' '
+test_expect_success '--exists with existing pseudoref' '
 	test_when_finished "rm .git/FETCH_HEAD" &&
 	git rev-parse HEAD >.git/FETCH_HEAD &&
 	${git_show_ref_exists} FETCH_HEAD
diff --git a/t/t0600-reffiles-backend.sh b/t/t0600-reffiles-backend.sh
index 62b0851852..26385aab7d 100755
--- a/t/t0600-reffiles-backend.sh
+++ b/t/t0600-reffiles-backend.sh
@@ -267,7 +267,7 @@ test_expect_success 'setup worktree' '
 	)
 '
 
-# Some refs (refs/bisect/*, pseudorefs) are kept per worktree, so they should
+# Some refs (refs/bisect/*, root refs) are kept per worktree, so they should
 # only appear in the for-each-reflog output if it is called from the correct
 # worktree, which is exercised in this test. This test is poorly written for
 # multiple reasons: 1) it creates invalidly formatted log entries. 2) it uses
diff --git a/t/t0610-reftable-basics.sh b/t/t0610-reftable-basics.sh
index a702745839..01a0310762 100755
--- a/t/t0610-reftable-basics.sh
+++ b/t/t0610-reftable-basics.sh
@@ -1156,7 +1156,7 @@ test_expect_success 'worktree: adds worktree with detached HEAD' '
 	test_cmp expect actual
 '
 
-test_expect_success 'fetch: accessing FETCH_HEAD special ref works' '
+test_expect_success 'fetch: accessing FETCH_HEAD pseudoref works' '
 	test_when_finished "rm -rf repo sub" &&
 
 	git init sub &&
diff --git a/t/t1400-update-ref.sh b/t/t1400-update-ref.sh
index 1a164c96f9..95a617254b 100755
--- a/t/t1400-update-ref.sh
+++ b/t/t1400-update-ref.sh
@@ -503,61 +503,61 @@ test_expect_success 'git cat-file blob main@{2005-05-26 23:42}:F (expect OTHER)'
 	test OTHER = $(git cat-file blob "main@{2005-05-26 23:42}:F")
 '
 
-# Test adding and deleting pseudorefs
+# Test adding and deleting root refs
 
-test_expect_success 'given old value for missing pseudoref, do not create' '
-	test_must_fail git update-ref PSEUDOREF $A $B 2>err &&
-	test_must_fail git rev-parse PSEUDOREF &&
+test_expect_success 'given old value for missing root ref, do not create' '
+	test_must_fail git update-ref ROOT_REF_HEAD $A $B 2>err &&
+	test_must_fail git rev-parse ROOT_REF_HEAD &&
 	test_grep "unable to resolve reference" err
 '
 
-test_expect_success 'create pseudoref' '
-	git update-ref PSEUDOREF $A &&
-	test $A = $(git show-ref -s --verify PSEUDOREF)
+test_expect_success 'create root ref' '
+	git update-ref ROOT_REF_HEAD $A &&
+	test $A = $(git show-ref -s --verify ROOT_REF_HEAD)
 '
 
-test_expect_success 'overwrite pseudoref with no old value given' '
-	git update-ref PSEUDOREF $B &&
-	test $B = $(git show-ref -s --verify PSEUDOREF)
+test_expect_success 'overwrite root ref with no old value given' '
+	git update-ref ROOT_REF_HEAD $B &&
+	test $B = $(git show-ref -s --verify ROOT_REF_HEAD)
 '
 
-test_expect_success 'overwrite pseudoref with correct old value' '
-	git update-ref PSEUDOREF $C $B &&
-	test $C = $(git show-ref -s --verify PSEUDOREF)
+test_expect_success 'overwrite root ref with correct old value' '
+	git update-ref ROOT_REF_HEAD $C $B &&
+	test $C = $(git show-ref -s --verify ROOT_REF_HEAD)
 '
 
-test_expect_success 'do not overwrite pseudoref with wrong old value' '
-	test_must_fail git update-ref PSEUDOREF $D $E 2>err &&
-	test $C = $(git show-ref -s --verify PSEUDOREF) &&
+test_expect_success 'do not overwrite root ref with wrong old value' '
+	test_must_fail git update-ref ROOT_REF_HEAD $D $E 2>err &&
+	test $C = $(git show-ref -s --verify ROOT_REF_HEAD) &&
 	test_grep "cannot lock ref.*expected" err
 '
 
-test_expect_success 'delete pseudoref' '
-	git update-ref -d PSEUDOREF &&
-	test_must_fail git show-ref -s --verify PSEUDOREF
+test_expect_success 'delete root ref' '
+	git update-ref -d ROOT_REF_HEAD &&
+	test_must_fail git show-ref -s --verify ROOT_REF_HEAD
 '
 
-test_expect_success 'do not delete pseudoref with wrong old value' '
-	git update-ref PSEUDOREF $A &&
-	test_must_fail git update-ref -d PSEUDOREF $B 2>err &&
-	test $A = $(git show-ref -s --verify PSEUDOREF) &&
+test_expect_success 'do not delete root ref with wrong old value' '
+	git update-ref ROOT_REF_HEAD $A &&
+	test_must_fail git update-ref -d ROOT_REF_HEAD $B 2>err &&
+	test $A = $(git show-ref -s --verify ROOT_REF_HEAD) &&
 	test_grep "cannot lock ref.*expected" err
 '
 
-test_expect_success 'delete pseudoref with correct old value' '
-	git update-ref -d PSEUDOREF $A &&
-	test_must_fail git show-ref -s --verify PSEUDOREF
+test_expect_success 'delete root ref with correct old value' '
+	git update-ref -d ROOT_REF_HEAD $A &&
+	test_must_fail git show-ref -s --verify ROOT_REF_HEAD
 '
 
-test_expect_success 'create pseudoref with old OID zero' '
-	git update-ref PSEUDOREF $A $Z &&
-	test $A = $(git show-ref -s --verify PSEUDOREF)
+test_expect_success 'create root ref with old OID zero' '
+	git update-ref ROOT_REF_HEAD $A $Z &&
+	test $A = $(git show-ref -s --verify ROOT_REF_HEAD)
 '
 
-test_expect_success 'do not overwrite pseudoref with old OID zero' '
-	test_when_finished git update-ref -d PSEUDOREF &&
-	test_must_fail git update-ref PSEUDOREF $B $Z 2>err &&
-	test $A = $(git show-ref -s --verify PSEUDOREF) &&
+test_expect_success 'do not overwrite root ref with old OID zero' '
+	test_when_finished git update-ref -d ROOT_REF_HEAD &&
+	test_must_fail git update-ref ROOT_REF_HEAD $B $Z 2>err &&
+	test $A = $(git show-ref -s --verify ROOT_REF_HEAD) &&
 	test_grep "already exists" err
 '
 
diff --git a/t/t1403-show-ref.sh b/t/t1403-show-ref.sh
index 226b3ec0e8..38648fd560 100755
--- a/t/t1403-show-ref.sh
+++ b/t/t1403-show-ref.sh
@@ -181,7 +181,7 @@ test_expect_success 'show-ref --verify HEAD' '
 	test_must_be_empty actual
 '
 
-test_expect_success 'show-ref --verify pseudorefs' '
+test_expect_success 'show-ref --verify root refs' '
 	git update-ref CHERRY_PICK_HEAD HEAD $ZERO_OID &&
 	test_when_finished "git update-ref -d CHERRY_PICK_HEAD" &&
 	git show-ref -s --verify HEAD >actual &&
diff --git a/t/t1460-refs-migrate.sh b/t/t1460-refs-migrate.sh
index ecf6411288..1f59d820a0 100755
--- a/t/t1460-refs-migrate.sh
+++ b/t/t1460-refs-migrate.sh
@@ -170,7 +170,7 @@ do
 			test_cmp expect actual
 		'
 
-		test_expect_success "$from_format -> $to_format: pseudo-refs" '
+		test_expect_success "$from_format -> $to_format: root refs" '
 			test_when_finished "rm -rf repo" &&
 			git init --ref-storage-format=$from_format repo &&
 			test_commit -C repo initial &&
@@ -178,7 +178,7 @@ do
 			test_migration repo "$to_format"
 		'
 
-		test_expect_success "$from_format -> $to_format: special refs are left alone" '
+		test_expect_success "$from_format -> $to_format: pseudorefs are left alone" '
 			test_when_finished "rm -rf repo" &&
 			git init --ref-storage-format=$from_format repo &&
 			test_commit -C repo initial &&
diff --git a/t/t4013-diff-various.sh b/t/t4013-diff-various.sh
index 21c9ad7c5d..43a9bf6d7c 100755
--- a/t/t4013-diff-various.sh
+++ b/t/t4013-diff-various.sh
@@ -115,10 +115,10 @@ test_expect_success setup '
 	git checkout -f main &&
 
 	# Same merge as main, but with parents reversed. Hide it in a
-	# pseudo-ref to avoid impacting tests with --all.
+	# root ref to avoid impacting tests with --all.
 	commit=$(echo reverse |
 		 git commit-tree -p main^2 -p main^1 main^{tree}) &&
-	git update-ref REVERSE $commit &&
+	git update-ref REVERSE_HEAD $commit &&
 
 	git config diff.renames false &&
 
@@ -328,7 +328,7 @@ diff-tree --cc --stat --summary main
 diff-tree -c --stat --summary side
 diff-tree --cc --stat --summary side
 diff-tree --cc --shortstat main
-diff-tree --cc --summary REVERSE
+diff-tree --cc --summary REVERSE_HEAD
 # improved by Timo's patch
 diff-tree --cc --patch-with-stat main
 # improved by Timo's patch
diff --git a/t/t4013/diff.diff-tree_--cc_--summary_REVERSE b/t/t4013/diff.diff-tree_--cc_--summary_REVERSE_HEAD
similarity index 73%
rename from t/t4013/diff.diff-tree_--cc_--summary_REVERSE
rename to t/t4013/diff.diff-tree_--cc_--summary_REVERSE_HEAD
index e208dd5682..0f86ae60db 100644
--- a/t/t4013/diff.diff-tree_--cc_--summary_REVERSE
+++ b/t/t4013/diff.diff-tree_--cc_--summary_REVERSE_HEAD
@@ -1,4 +1,4 @@
-$ git diff-tree --cc --summary REVERSE
+$ git diff-tree --cc --summary REVERSE_HEAD
 2562325a7ee916efb2481da93073b82cec801cbc
  create mode 100644 file1
  delete mode 100644 file2
diff --git a/t/t6302-for-each-ref-filter.sh b/t/t6302-for-each-ref-filter.sh
index 7f060d97bf..14f6d7e597 100755
--- a/t/t6302-for-each-ref-filter.sh
+++ b/t/t6302-for-each-ref-filter.sh
@@ -31,7 +31,7 @@ test_expect_success 'setup some history and refs' '
 	git update-ref refs/odd/spot main
 '
 
-test_expect_success '--include-root-refs pattern prints pseudorefs' '
+test_expect_success '--include-root-refs pattern prints root refs' '
 	cat >expect <<-\EOF &&
 	HEAD
 	ORIG_HEAD
@@ -52,7 +52,7 @@ test_expect_success '--include-root-refs pattern prints pseudorefs' '
 	test_cmp expect actual
 '
 
-test_expect_success '--include-root-refs pattern does not print special refs' '
+test_expect_success '--include-root-refs pattern does not print pseudorefs' '
 	test_when_finished "rm -rf repo" &&
 	git init repo &&
 	(
diff --git a/t/t9902-completion.sh b/t/t9902-completion.sh
index f6de74054a..1cb55f9301 100755
--- a/t/t9902-completion.sh
+++ b/t/t9902-completion.sh
@@ -2693,13 +2693,13 @@ test_expect_success 'checkout matches case insensitively with GIT_COMPLETION_IGN
 	)
 '
 
-test_expect_success 'checkout completes pseudo refs' '
+test_expect_success 'checkout completes root refs' '
 	test_completion "git checkout H" <<-\EOF
 	HEAD Z
 	EOF
 '
 
-test_expect_success 'checkout completes pseudo refs case insensitively with GIT_COMPLETION_IGNORE_CASE' '
+test_expect_success 'checkout completes root refs case insensitively with GIT_COMPLETION_IGNORE_CASE' '
 	(
 		GIT_COMPLETION_IGNORE_CASE=1 &&
 		test_completion "git checkout h" <<-\EOF
-- 
2.56.0

Re: [PATCH 1/5] doc: glossary: add root ref and clarify pseudorefs

Message details and related content
Andy Koppe [off-list ref] writes:
quoted hunk
The glossary describes refs in the root of the ref hierarchy, but does
not actually name them "root refs", even though the term is used
elsewhere, for example by "git for-each-ref --include-root-refs". It
also defines pseudorefs as refs, yet is_root_ref() deliberately excludes
them, as they are not stored or handled by the ref backends.

Add a "root ref" entry, moving the naming rules and the list of
irregular root refs there from the "ref" entry. State that pseudorefs
are not root refs, and refer to gitrevisions(7) for descriptions of
commonly used root refs and pseudorefs.

Reword the "pseudoref" entry so that it no longer calls them refs, add
git-for-each-ref(1) as another example of how they are not proper refs,
and explain that using a pseudoref containing multiple object IDs yields
the first one.

Signed-off-by: Andy Koppe <redacted>
---
 Documentation/glossary-content.adoc | 68 +++++++++++++++++------------
 1 file changed, 41 insertions(+), 27 deletions(-)
diff --git a/Documentation/glossary-content.adoc b/Documentation/glossary-content.adoc
index 52301a56dd..b2c582254b 100644
--- a/Documentation/glossary-content.adoc
+++ b/Documentation/glossary-content.adoc
@@ -500,9 +500,10 @@ exclude;;
 	unusual refs.
 
 [[def_pseudoref]]pseudoref::
-	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].
I would love to get input from Patrick who wrote 993d57eded (refs:
pseudorefs are no refs, 2024-05-15), as I consider that the loss of
"different semantics" from this description is a rather serious one.
FETCH_HEAD and MERGE_HEAD cannot be written with update-ref simply
because it is normal for them to record multiple object names, so it
is not just "how" they are stored, but also "what" they can store,
that makes them "strange", "pseudo", "different", and "special".
quoted hunk
 The following pseudorefs are known to Git:
 
@@ -512,6 +513,9 @@ The following pseudorefs are known to Git:
 
  - `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.
This is an important addition.
quoted hunk
@@ -562,33 +566,12 @@ The following pseudorefs are known to Git:
 +
 The ref namespace is hierarchical.
 Ref names must either start with `refs/` or be located in the root of
+the hierarchy, in which case they must follow the naming rules for
+<<def_root_ref,root refs>>.
 +
 Different subhierarchies are used for different purposes. For example,
 the `refs/heads/` hierarchy is used to represent local branches whereas
-the `refs/tags/` hierarchy is used to represent local tags..
+the `refs/tags/` hierarchy is used to represent local tags.
 +
 See also "REFERENCES" in linkgit:gitdatamodel[7].
 
@@ -640,6 +623,37 @@ format. See linkgit:git-reflog[1] for details.
 	To throw away part of the development, i.e. to assign the
 	<<def_head,head>> to an earlier <<def_revision,revision>>.
 
+[[def_root_ref]]root ref::
+	A <<def_ref,ref>> located in the root of the ref hierarchy, outside
+	`refs/`. Root ref names must follow these rules:
++
+--
+ - The name consists of only upper-case characters or underscores.
+
+ - The name ends with "`_HEAD`" or is equal to "`HEAD`".
+--
++
+The following irregular root refs are exceptions to these rules. This
+list is exhaustive and shall not be extended in future:
++
+--
+ - `AUTO_MERGE`
+
+ - `BISECT_EXPECTED_REV`
+
+ - `NOTES_MERGE_PARTIAL`
+
+ - `NOTES_MERGE_REF`
+

8 more quoted lines

I think this is a great reorganization to split out the "these are
exceptions" into its own section.
Message details and related content
Andy Koppe [off-list ref] writes:
Since 993d57eded (refs: pseudorefs are no refs, 2024-05-15), the term
"pseudoref" has been restricted to FETCH_HEAD and MERGE_HEAD, which are
not stored in the ref backends, while other refs outside of "refs/",
such as HEAD and ORIG_HEAD, are called root refs. However, a fair number
of docs, comments, messages and tests still use "pseudoref" in its old
sense. And while the glossary describes the rules for refs in the root
of the hierarchy, it does not explicitly define the term, even though
"git for-each-ref --include-root-refs" uses it.
Thanks.  Overall this was a pleasant read.

Re: [PATCH 1/5] doc: glossary: add root ref and clarify pseudorefs

Message details and related content
On Thu, Oct 22, 2026 at 10:28:49PM +0100, Andy Koppe wrote:
quoted hunk
diff --git a/Documentation/glossary-content.adoc b/Documentation/glossary-content.adoc
index 52301a56dd..b2c582254b 100644
--- a/Documentation/glossary-content.adoc
+++ b/Documentation/glossary-content.adoc
@@ -500,9 +500,10 @@ exclude;;
 	unusual refs.
 
 [[def_pseudoref]]pseudoref::
-	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].
As Junio already mentioned, I think we should retain the information
about different semantics.

I also think that the previous guidance around how pseudorefs can be
used is weakened, because now you only list examples of how they cannot
be used. Previously though we explicitly specified that the only
supported operation is reading the refs directly. So instead of giving
examples of how they cannot be used, I think we should continue to list
the few operations that _are_ possible.
quoted hunk
@@ -512,6 +513,9 @@ The following pseudorefs are known to Git:
 
  - `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.
This is indeed a good addition.
quoted hunk
@@ -640,6 +623,37 @@ format. See linkgit:git-reflog[1] for details.
 	To throw away part of the development, i.e. to assign the
 	<<def_head,head>> to an earlier <<def_revision,revision>>.
 
+[[def_root_ref]]root ref::
And splitting this out makes sense, as well.
quoted hunk
+	A <<def_ref,ref>> located in the root of the ref hierarchy, outside
+	`refs/`. Root ref names must follow these rules:
++
+--
+ - The name consists of only upper-case characters or underscores.
+
+ - The name ends with "`_HEAD`" or is equal to "`HEAD`".
+--
++
+The following irregular root refs are exceptions to these rules. This
+list is exhaustive and shall not be extended in future:
Okay, this paragraph has been shortened a bit, which I think is a good
change.

Thanks!

Patrick

Re: [PATCH 2/5] doc: glossary: update per-worktree refs

Message details and related content
On Thu, Oct 22, 2026 at 10:28:50PM +0100, Andy Koppe wrote:
quoted hunk
diff --git a/Documentation/glossary-content.adoc b/Documentation/glossary-content.adoc
index b2c582254b..eff6d23215 100644
--- a/Documentation/glossary-content.adoc
+++ b/Documentation/glossary-content.adoc
@@ -494,10 +494,16 @@ exclude;;
 	interface than the <<def_plumbing,plumbing>>.
 
 [[def_per_worktree_ref]]per-worktree ref::
-	Refs that are per-<<def_worktree,worktree>>, rather than
-	global.  This is presently only <<def_HEAD,HEAD>> and any refs
-	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/`.
++
+<<def_pseudoref,Pseudorefs>> are also specific to each worktree.
Should we maybe also turn this into an itemized list? Makes it easier to
see at a glance which rrefs are per-worktree, and it becomes easier to
extend going forward.
quoted hunk
+See "REFS" in linkgit:git-worktree[1] for how to access the
+per-worktree refs of other worktrees.
 
 [[def_pseudoref]]pseudoref::
 	A name that can be used like a <<def_ref,ref>> to refer to
@@ -788,5 +794,5 @@ 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).
This is a bit curious as you distinguish between per-worktree refs and
pseudorefs, even though you mention both of them as part of the
per-worktree ref section. I wonder whether we should simply refer to
that section without naming any examples.

Patrick

Re: [PATCH 3/5] doc: stop calling root refs pseudorefs

Message details and related content
On Thu, Oct 22, 2026 at 10:28:51PM +0100, Andy Koppe wrote:
quoted hunk
diff --git a/Documentation/git-update-ref.adoc b/Documentation/git-update-ref.adoc
index 37a5019a8b..c775334230 100644
--- a/Documentation/git-update-ref.adoc
+++ b/Documentation/git-update-ref.adoc
@@ -163,13 +163,12 @@ still see a subset of the modifications.
 
 LOGGING UPDATES
 ---------------
-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.  Log lines are
-formatted as:
+If config parameter "core.logAllRefUpdates" is true and the ref is
+HEAD or one under "refs/heads/", "refs/remotes/" or "refs/notes/", or
+if the file "$GIT_DIR/logs/<ref>" exists, then `git update-ref` will
It's kind of hard to understand how the ANDs and ORs play together.
Like, does the config parameter extend to all subsequent ORs? That's not
really a new issue though.
+append a line to the log file "$GIT_DIR/logs/<ref>" (dereferencing all
Also, this infomation about "$GIT_DIR/logs/<ref>" is out-of-date, as the
reftable backend doesn't store its reflogs in there.

I wonder whether we should maybe just stop explaining how
"core.logAllRefUpdates" works and instead refer to git-config(1).
quoted hunk
diff --git a/Documentation/git-worktree.adoc b/Documentation/git-worktree.adoc
index e6e77252ab..f0eb6d4137 100644
--- a/Documentation/git-worktree.adoc
+++ b/Documentation/git-worktree.adoc
@@ -303,11 +303,10 @@ but others are specific to an individual worktree. One example is `HEAD`,
 which is different for each worktree. This section is about the sharing
 rules and how to access refs of one worktree from another.
 
-In general, all pseudo refs are per-worktree and all refs starting with
-`refs/` are shared. Pseudo refs are ones like `HEAD` which are directly
-under `$GIT_DIR` instead of inside `$GIT_DIR/refs`. There are exceptions,
-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.
It might make sense to have a bulleted list here, same as I proposed in
a preceding patch.

Patrick

Re: [PATCH 4/5] refs: stop calling root refs pseudorefs

Message details and related content
On Thu, Oct 22, 2026 at 10:28:52PM +0100, Andy Koppe wrote:
Some messages and comments in the code still use "pseudoref" in its old
sense of any ref outside of "refs/", whereas the glossary now calls
those root refs and restricts pseudorefs to FETCH_HEAD and MERGE_HEAD.

Replace "pseudoref" with "root ref" in the help text for
"git for-each-ref --include-root-refs", in comments in refs.h, and in
comments in the bash completion script, including the one for
__git_pseudoref_exists(), which is also used for root refs.
I feel like this sentence is somewhat pointless, as you basically
just explain the diff.
The function
itself is not renamed. There is no obvious term covering both root refs
and pseudorefs, and the old name might need to be kept for third-party
completion scripts.
This is useful though.
Update the description of pseudorefs in refs.h to match the glossary.
And this is again info that can simply be dropped.

Patrick 
Message details and related content
On Oct 22, 2026, at 5:28 PM, Andy Koppe [off-list ref] wrote:

Since 993d57eded (refs: pseudorefs are no refs, 2024-05-15), the term
"pseudoref" has been restricted to FETCH_HEAD and MERGE_HEAD, which are
not stored in the ref backends, while other refs outside of "refs/",
such as HEAD and ORIG_HEAD, are called root refs. However, a fair number
of docs, comments, messages and tests still use "pseudoref" in its old
sense. And while the glossary describes the rules for refs in the root
of the hierarchy, it does not explicitly define the term, even though
"git for-each-ref --include-root-refs" uses it.

This series adds a "root ref" entry to the glossary and clarifies the
"pseudoref" entry, and then replaces the outdated uses of "pseudoref" in
docs, code and tests. While at it, it also updates the long outdated
glossary entry for per-worktree refs, and fixes a few inaccuracies
found along the way, for example about which refs get a reflog in
git-update-ref(1) and core.logAllRefUpdates.

I've left __git_pseudoref_exists() in the completion script alone apart
from its comment, even though it's used for both pseudorefs and root
refs. It could be renamed, keeping the old name as a deprecated wrapper
like elsewhere in the script, but it's not obvious what to, because
there is no term covering both.
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               |  9 +-
Documentation/glossary-content.adoc           | 86 ++++++++++++-------
Documentation/rev-list-options.adoc           |  4 +-
I wonder if gitdatamodel.adoc should also cover pseudorefs and root refs
in the “References” section. Right now it only says something extremely
short on the topic:

  Git may also create references other than HEAD at the base of
  the hierarchy, like ORIG_HEAD.

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 +-
21 files changed, 129 insertions(+), 111 deletions(-)
rename t/t4013/{diff.diff-tree_--cc_--summary_REVERSE => diff.diff-tree_--cc_--summary_REVERSE_HEAD} (73%)


base-commit: 7e34d8174dcda3435d1404c9a30bef46afe6c13e
-- 
2.56.0

[PATCH v2 0/5] root refs vs pseudorefs

Revisions (3)
  1. v1
  2. v2 current [diff against previous]
  3. v3 [diff against previous]
Message details and related content
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:

170 more lines

[PATCH v2 1/5] doc: glossary: add root ref and clarify pseudorefs

Message details and related content
The glossary describes refs in the root of the ref hierarchy, but does
not actually name them "root refs", even though the term is used
elsewhere, for example by "git for-each-ref --include-root-refs". It
also defines pseudorefs as refs, yet is_root_ref() deliberately excludes
them, as they are not stored or handled by the ref backends.

Add a "root ref" entry, moving the naming rules and the list of
irregular root refs there from the "ref" entry. State that pseudorefs
are not root refs, and refer to gitrevisions(7) for descriptions of
commonly used root refs and pseudorefs.

Reword the "pseudoref" entry so that it no longer calls them refs, add
git-for-each-ref(1) as another example of how they are not proper refs,
and explain that using a pseudoref containing multiple object IDs yields
the first one.

Signed-off-by: Andy Koppe <redacted>
---
 Documentation/glossary-content.adoc | 73 ++++++++++++++++++-----------
 1 file changed, 45 insertions(+), 28 deletions(-)
diff --git a/Documentation/glossary-content.adoc b/Documentation/glossary-content.adoc
index 52301a56dd..f905b4467b 100644
--- a/Documentation/glossary-content.adoc
+++ b/Documentation/glossary-content.adoc
@@ -500,18 +500,25 @@ exclude;;
 	unusual refs.
 
 [[def_pseudoref]]pseudoref::
-	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 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:
-
++
+--
  - `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.
 
 [[def_pull]]pull::
 	Pulling a <<def_branch,branch>> means to <<def_fetch,fetch>> it and
@@ -562,33 +569,12 @@ The following pseudorefs are known to Git:
 +
 The ref namespace is hierarchical.
 Ref names must either start with `refs/` or be located in the root of
-the hierarchy. For the latter, their name must follow these rules:
-+
---
- - The name consists of only upper-case characters or underscores.
-
- - The name ends with "`_HEAD`" or is equal to "`HEAD`".
---
-+
-There are some irregular refs in the root of the hierarchy that do not
-match these rules. The following list is exhaustive and shall not be
-extended in the future:
-+
---
- - `AUTO_MERGE`
-
- - `BISECT_EXPECTED_REV`
-
- - `NOTES_MERGE_PARTIAL`
-
- - `NOTES_MERGE_REF`
-
- - `MERGE_AUTOSTASH`
---
+the hierarchy, in which case they must follow the naming rules for
+<<def_root_ref,root refs>>.
 +
 Different subhierarchies are used for different purposes. For example,
 the `refs/heads/` hierarchy is used to represent local branches whereas
-the `refs/tags/` hierarchy is used to represent local tags..
+the `refs/tags/` hierarchy is used to represent local tags.
 +
 See also "REFERENCES" in linkgit:gitdatamodel[7].
 
@@ -640,6 +626,37 @@ format. See linkgit:git-reflog[1] for details.
 	To throw away part of the development, i.e. to assign the
 	<<def_head,head>> to an earlier <<def_revision,revision>>.
 
+[[def_root_ref]]root ref::
+	A <<def_ref,ref>> located in the root of the ref hierarchy, outside
+	`refs/`. Root ref names must follow these rules:
++
+--
+ - The name consists of only upper-case characters or underscores.
+
+ - The name ends with "`_HEAD`" or is equal to "`HEAD`".
+--
++
+The following irregular root refs are exceptions to these rules. This
+list is exhaustive and shall not be extended in future:
++
+--
+ - `AUTO_MERGE`
+
+ - `BISECT_EXPECTED_REV`
+
+ - `NOTES_MERGE_PARTIAL`
+
+ - `NOTES_MERGE_REF`
+
+ - `MERGE_AUTOSTASH`
+--
++
+<<def_pseudoref,Pseudorefs>> are not considered root refs, even though
+they follow the naming rules.
++
+See linkgit:gitrevisions[7] for descriptions of commonly used root refs
+and pseudorefs.
+
 [[def_SCM]]SCM::
 	Source code management (tool).
 
-- 
2.56.0

[PATCH v2 2/5] doc: glossary: update per-worktree refs

Message details and related content
The "per-worktree ref" entry says that these are only HEAD and refs
under "refs/bisect/", but that has been out of date since a9be29c981
(sequencer: make refs generated by the `label` command worktree-local,
2018-04-25). All root refs are per-worktree, as are refs under
"refs/worktree/" and "refs/rewritten/". Pseudorefs are specific to each
worktree too.

Bring the list up to date, and refer to git-worktree(1) for how to
access per-worktree refs of other worktrees. In the "worktree" entry,
describe HEAD as a per-worktree ref and MERGE_HEAD as a pseudoref,
rather than listing them separately from per-worktree refs.

Signed-off-by: Andy Koppe <redacted>
---
 Documentation/glossary-content.adoc | 25 +++++++++++++++++++------
 1 file changed, 19 insertions(+), 6 deletions(-)
diff --git a/Documentation/glossary-content.adoc b/Documentation/glossary-content.adoc
index f905b4467b..67fe49b26d 100644
--- a/Documentation/glossary-content.adoc
+++ b/Documentation/glossary-content.adoc
@@ -494,10 +494,23 @@ exclude;;
 	interface than the <<def_plumbing,plumbing>>.
 
 [[def_per_worktree_ref]]per-worktree ref::
-	Refs that are per-<<def_worktree,worktree>>, rather than
-	global.  This is presently only <<def_HEAD,HEAD>> and any refs
-	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>>,
+	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.
++
+See "REFS" in linkgit:git-worktree[1] for how to access the
+per-worktree refs of other worktrees.
 
 [[def_pseudoref]]pseudoref::
 	A name that can be used like a <<def_ref,ref>> to refer to an
@@ -791,5 +804,5 @@ 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, <<def_per_worktree_ref,per-worktree refs>>, and the
+	per-worktree configuration file).
-- 
2.56.0

[PATCH v2 3/5] doc: stop calling root refs pseudorefs

Message details and related content
Several manual pages still use "pseudoref" in its old sense of any ref
outside of "refs/", which the glossary now calls a root ref. Pseudorefs
have since been restricted to FETCH_HEAD and MERGE_HEAD, which are not
considered root refs.

Replace "pseudoref" with "root ref" in those pages, giving HEAD and
ORIG_HEAD as examples. In git-worktree(1), say that both root refs and
pseudorefs are per-worktree, and drop the description of them as being
directly under $GIT_DIR, because that does not apply to the reftable
backend.

In git-ls-remote(1), describe --refs as omitting refs outside "refs/",
since that is what it actually checks. In the example, refer to HEAD,
which is the only root ref that git-upload-pack(1) advertises. While
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. 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 <redacted>
---
 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/rev-list-options.adoc     |  4 ++--
 7 files changed, 34 insertions(+), 22 deletions(-)
diff --git a/Documentation/config/core.adoc b/Documentation/config/core.adoc
index bdc74d291c..8f8fe36a58 100644
--- a/Documentation/config/core.adoc
+++ b/Documentation/config/core.adoc
@@ -376,7 +376,7 @@ core.logAllRefUpdates::
 	`refs/heads/`), remote refs (i.e. under `refs/remotes/`),
 	note refs (i.e. under `refs/notes/`), and the symbolic ref `HEAD`.
 	If it is set to `always`, then a missing reflog is automatically
-	created for any ref under `refs/`.
+	created for any ref, including root refs such as `ORIG_HEAD`.
 +
 This information can be used to determine what commit
 was the tip of a branch "2 days ago".
diff --git a/Documentation/for-each-ref-options.adoc b/Documentation/for-each-ref-options.adoc
index 54e2fa95c2..2b6d4ede0b 100644
--- a/Documentation/for-each-ref-options.adoc
+++ b/Documentation/for-each-ref-options.adoc
@@ -73,7 +73,7 @@ TAB %(refname)`.
 	using the same rules as _<pattern>_ above.
 
 `--include-root-refs`::
-	List root refs (`HEAD` and pseudorefs) apart from regular refs.
+	Also list root refs, such as `HEAD` or `ORIG_HEAD`.
 
 `--start-after=<marker>`::
     Allows paginating the output by skipping references up to and including the
diff --git a/Documentation/git-ls-remote.adoc b/Documentation/git-ls-remote.adoc
index d71c4ab3e2..7ce7df3149 100644
--- a/Documentation/git-ls-remote.adoc
+++ b/Documentation/git-ls-remote.adoc
@@ -35,7 +35,7 @@ OPTIONS
 	with other git subcommands.
 
 --refs::
-	Do not show peeled tags or pseudorefs like `HEAD` in the output.
+	Do not show peeled tags or refs outside `refs/`.
 
 -q::
 --quiet::
@@ -118,7 +118,7 @@ line shows the name of the object the tag points at.
 EXAMPLES
 --------
 
-* List all references (including symbolics and pseudorefs), peeling
+* List all references (including `HEAD` and symbolic refs), peeling
   tags:
 +
 ----
diff --git a/Documentation/git-update-ref.adoc b/Documentation/git-update-ref.adoc
index 37a5019a8b..c775334230 100644
--- a/Documentation/git-update-ref.adoc
+++ b/Documentation/git-update-ref.adoc
@@ -163,13 +163,12 @@ still see a subset of the modifications.
 
 LOGGING UPDATES
 ---------------
-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.  Log lines are
-formatted as:
+If config parameter "core.logAllRefUpdates" is true and the ref is
+HEAD or one under "refs/heads/", "refs/remotes/" or "refs/notes/", or
+if 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.  Log lines are formatted as:
 
     oldsha1 SP newsha1 SP committer LF
 
diff --git a/Documentation/git-worktree.adoc b/Documentation/git-worktree.adoc
index e6e77252ab..ba620d8ace 100644
--- a/Documentation/git-worktree.adoc
+++ b/Documentation/git-worktree.adoc
@@ -303,11 +303,14 @@ but others are specific to an individual worktree. One example is `HEAD`,
 which is different for each worktree. This section is about the sharing
 rules and how to access refs of one worktree from another.
 
-In general, all pseudo refs are per-worktree and all refs starting with
-`refs/` are shared. Pseudo refs are ones like `HEAD` which are directly
-under `$GIT_DIR` instead of inside `$GIT_DIR/refs`. There are exceptions,
-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, 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
diff --git a/Documentation/gitdatamodel.adoc b/Documentation/gitdatamodel.adoc
index 8d9be02036..6958ac033c 100644
--- a/Documentation/gitdatamodel.adoc
+++ b/Documentation/gitdatamodel.adoc
@@ -211,15 +211,25 @@ 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 nevertheless
+    be used to refer to objects in commands such as linkgit:git-log[1] and
+    linkgit:git-diff[1].
 
 NOTE: Git may delete objects that aren't "reachable" from any reference
 or <<reflogs,reflog>>.
diff --git a/Documentation/rev-list-options.adoc b/Documentation/rev-list-options.adoc
index bd9f345690..822604278a 100644
--- a/Documentation/rev-list-options.adoc
+++ b/Documentation/rev-list-options.adoc
@@ -350,8 +350,8 @@ Under `--pretty=reference`, this information will not be shown at all.
 
 `--merge`::
 	Show commits touching conflicted paths in the range `HEAD...<other>`,
-	where `<other>` is the first existing pseudoref in `MERGE_HEAD`,
-	`CHERRY_PICK_HEAD`, `REVERT_HEAD` or `REBASE_HEAD`. Only works
+	where `<other>` is the first of `MERGE_HEAD`, `CHERRY_PICK_HEAD`,
+	`REVERT_HEAD` or `REBASE_HEAD` that exists. Only works
 	when the index has unmerged entries. This option can be used to show
 	relevant commits when resolving conflicts from a 3-way merge.
 
-- 
2.56.0

[PATCH v2 4/5] refs: stop calling root refs pseudorefs

Message details and related content
Some messages and comments in the code still use "pseudoref" in its old
sense of any ref outside of "refs/", whereas the glossary now calls
those root refs and restricts pseudorefs to FETCH_HEAD and MERGE_HEAD,
so update them.

Bash completion function __git_pseudoref_exists() is not renamed, as
there is no obvious term covering both root refs and pseudorefs, and
third-party completion scripts might depend on the name.

Signed-off-by: Andy Koppe <redacted>
---
 builtin/for-each-ref.c                 |  2 +-
 contrib/completion/git-completion.bash |  7 ++++---
 refs.h                                 | 13 ++++++-------
 revision.c                             |  2 +-
 4 files changed, 12 insertions(+), 12 deletions(-)
diff --git a/builtin/for-each-ref.c b/builtin/for-each-ref.c
index 4a2fc421db..6ff32e919c 100644
--- a/builtin/for-each-ref.c
+++ b/builtin/for-each-ref.c
@@ -48,7 +48,7 @@ int for_each_ref_core(int argc, const char **argv, const char *prefix, struct re
 		OPT_NO_CONTAINS(&filter.no_commit, N_("print only refs which don't contain the commit")),
 		OPT_BOOL(0, "ignore-case", &icase, N_("sorting and filtering are case insensitive")),
 		OPT_BOOL(0, "stdin", &from_stdin, N_("read reference patterns from stdin")),
-		OPT_BOOL(0, "include-root-refs", &include_root_refs, N_("also include HEAD ref and pseudorefs")),
+		OPT_BOOL(0, "include-root-refs", &include_root_refs, N_("also include root refs")),
 		OPT_END(),
 	};
 
diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
index c3a49e2e22..e12ac0359c 100644
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -147,8 +147,9 @@ __git_eread ()
 	test -r "$1" && IFS=$'\r\n' read -r "$2" <"$1"
 }
 
-# Runs git in $__git_repo_path to determine whether a pseudoref exists.
-# 1: The pseudo-ref to search
+# Runs git in $__git_repo_path to determine whether a root ref or
+# pseudoref exists.
+# 1: The ref to check
 __git_pseudoref_exists ()
 {
 	local ref=$1
@@ -954,7 +955,7 @@ __git_refs ()
 	esac
 }
 
-# Completes refs, short and long, local and remote, symbolic and pseudo.
+# Completes refs, short and long, local and remote, symbolic, root and pseudo.
 #
 # Usage: __git_complete_refs [<option>]...
 # --remote=<remote>: The remote to list refs from, can be the name of a
diff --git a/refs.h b/refs.h
index ee3b8a62ef..3be7ef3b0e 100644
--- a/refs.h
+++ b/refs.h
@@ -85,7 +85,7 @@ enum ref_transaction_error {
  * Even with RESOLVE_REF_ALLOW_BAD_NAME, names that escape the refs/
  * directory and do not consist of all caps and underscores cannot be
  * resolved. The function returns NULL for such ref names.
- * Caps and underscores refers to the pseudorefs, such as HEAD,
+ * Caps and underscores refers to root refs and pseudorefs, such as HEAD,
  * FETCH_HEAD and friends, that all live outside of the refs/ directory.
  */
 #define RESOLVE_REF_READING 0x01
@@ -448,8 +448,8 @@ enum refs_for_each_flag {
 	REFS_FOR_EACH_OMIT_DANGLING_SYMREFS = (1 << 2),
 
 	/*
-	 * Include root refs i.e. HEAD and pseudorefs along with the regular
-	 * refs.
+	 * Include root refs, such as HEAD and ORIG_HEAD, along with the
+	 * regular refs.
 	 */
 	REFS_FOR_EACH_INCLUDE_ROOT_REFS = (1 << 3),
 };
@@ -1306,10 +1306,9 @@ void update_ref_namespace(enum ref_namespace namespace, char *ref);
 int is_root_ref(const char *refname);
 
 /*
- * Pseudorefs are refs that have different semantics compared to
- * "normal" refs. These refs can thus not be stored in the ref backend,
- * but must always be accessed via the filesystem. The following refs
- * are pseudorefs:
+ * Pseudorefs are names that can be used like refs, but have different
+ * semantics. They cannot be stored in the ref backend, but must always
+ * be accessed via the filesystem. The following are pseudorefs:
  *
  * - FETCH_HEAD may contain multiple object IDs, and each one of them
  *   carries additional metadata like where it came from.
diff --git a/revision.c b/revision.c
index a83e499047..d6dd56faf1 100644
--- a/revision.c
+++ b/revision.c
@@ -2000,7 +2000,7 @@ static const char *lookup_other_head(struct object_id *oid)
 			return other_head[i];
 		}
 
-	die(_("--merge requires one of the pseudorefs MERGE_HEAD, CHERRY_PICK_HEAD, REVERT_HEAD or REBASE_HEAD"));
+	die(_("--merge requires one of MERGE_HEAD, CHERRY_PICK_HEAD, REVERT_HEAD or REBASE_HEAD"));
 }
 
 static void prepare_show_merge(struct rev_info *revs)
-- 
2.56.0

[PATCH v2 5/5] t: stop calling root refs pseudorefs

Message details and related content
Several test titles and comments still use "pseudoref" in its old sense
of any ref outside of "refs/", which the glossary now calls root refs.

Replace "pseudoref" with "root ref" there. In t1400 and t4013, rename
the root refs that the tests create from PSEUDOREF to ROOT_REF_HEAD and
from REVERSE to REVERSE_HEAD, so that they follow the naming rules for
root refs. This includes renaming the expected output file in t4013.

Some tests also still refer to FETCH_HEAD and MERGE_HEAD as "special
refs", which was the previous term for "pseudorefs". Correct them.

Signed-off-by: Andy Koppe <redacted>
---
 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 +-
 10 files changed, 48 insertions(+), 48 deletions(-)
 rename t/t4013/{diff.diff-tree_--cc_--summary_REVERSE => diff.diff-tree_--cc_--summary_REVERSE_HEAD} (73%)
diff --git a/t/show-ref-exists-tests.sh b/t/show-ref-exists-tests.sh
index 1351f690eb..d4a021a0c6 100644
--- a/t/show-ref-exists-tests.sh
+++ b/t/show-ref-exists-tests.sh
@@ -64,11 +64,11 @@ test_expect_success '--exists with directory fails with generic error' '
 	test_cmp expect err
 '
 
-test_expect_success '--exists with non-existent special ref' '
+test_expect_success '--exists with non-existent pseudoref' '
 	test_expect_code 2 ${git_show_ref_exists} FETCH_HEAD
 '
 
-test_expect_success '--exists with existing special ref' '
+test_expect_success '--exists with existing pseudoref' '
 	test_when_finished "rm .git/FETCH_HEAD" &&
 	git rev-parse HEAD >.git/FETCH_HEAD &&
 	${git_show_ref_exists} FETCH_HEAD
diff --git a/t/t0600-reffiles-backend.sh b/t/t0600-reffiles-backend.sh
index 62b0851852..26385aab7d 100755
--- a/t/t0600-reffiles-backend.sh
+++ b/t/t0600-reffiles-backend.sh
@@ -267,7 +267,7 @@ test_expect_success 'setup worktree' '
 	)
 '
 
-# Some refs (refs/bisect/*, pseudorefs) are kept per worktree, so they should
+# Some refs (refs/bisect/*, root refs) are kept per worktree, so they should
 # only appear in the for-each-reflog output if it is called from the correct
 # worktree, which is exercised in this test. This test is poorly written for
 # multiple reasons: 1) it creates invalidly formatted log entries. 2) it uses
diff --git a/t/t0610-reftable-basics.sh b/t/t0610-reftable-basics.sh
index a702745839..01a0310762 100755
--- a/t/t0610-reftable-basics.sh
+++ b/t/t0610-reftable-basics.sh
@@ -1156,7 +1156,7 @@ test_expect_success 'worktree: adds worktree with detached HEAD' '
 	test_cmp expect actual
 '
 
-test_expect_success 'fetch: accessing FETCH_HEAD special ref works' '
+test_expect_success 'fetch: accessing FETCH_HEAD pseudoref works' '
 	test_when_finished "rm -rf repo sub" &&
 
 	git init sub &&
diff --git a/t/t1400-update-ref.sh b/t/t1400-update-ref.sh
index 1a164c96f9..95a617254b 100755
--- a/t/t1400-update-ref.sh
+++ b/t/t1400-update-ref.sh
@@ -503,61 +503,61 @@ test_expect_success 'git cat-file blob main@{2005-05-26 23:42}:F (expect OTHER)'
 	test OTHER = $(git cat-file blob "main@{2005-05-26 23:42}:F")
 '
 
-# Test adding and deleting pseudorefs
+# Test adding and deleting root refs
 
-test_expect_success 'given old value for missing pseudoref, do not create' '
-	test_must_fail git update-ref PSEUDOREF $A $B 2>err &&
-	test_must_fail git rev-parse PSEUDOREF &&
+test_expect_success 'given old value for missing root ref, do not create' '
+	test_must_fail git update-ref ROOT_REF_HEAD $A $B 2>err &&
+	test_must_fail git rev-parse ROOT_REF_HEAD &&
 	test_grep "unable to resolve reference" err
 '
 
-test_expect_success 'create pseudoref' '
-	git update-ref PSEUDOREF $A &&
-	test $A = $(git show-ref -s --verify PSEUDOREF)
+test_expect_success 'create root ref' '
+	git update-ref ROOT_REF_HEAD $A &&
+	test $A = $(git show-ref -s --verify ROOT_REF_HEAD)
 '
 
-test_expect_success 'overwrite pseudoref with no old value given' '
-	git update-ref PSEUDOREF $B &&
-	test $B = $(git show-ref -s --verify PSEUDOREF)
+test_expect_success 'overwrite root ref with no old value given' '
+	git update-ref ROOT_REF_HEAD $B &&
+	test $B = $(git show-ref -s --verify ROOT_REF_HEAD)
 '
 
-test_expect_success 'overwrite pseudoref with correct old value' '
-	git update-ref PSEUDOREF $C $B &&
-	test $C = $(git show-ref -s --verify PSEUDOREF)
+test_expect_success 'overwrite root ref with correct old value' '
+	git update-ref ROOT_REF_HEAD $C $B &&
+	test $C = $(git show-ref -s --verify ROOT_REF_HEAD)
 '
 
-test_expect_success 'do not overwrite pseudoref with wrong old value' '
-	test_must_fail git update-ref PSEUDOREF $D $E 2>err &&
-	test $C = $(git show-ref -s --verify PSEUDOREF) &&
+test_expect_success 'do not overwrite root ref with wrong old value' '
+	test_must_fail git update-ref ROOT_REF_HEAD $D $E 2>err &&
+	test $C = $(git show-ref -s --verify ROOT_REF_HEAD) &&
 	test_grep "cannot lock ref.*expected" err
 '
 
-test_expect_success 'delete pseudoref' '
-	git update-ref -d PSEUDOREF &&
-	test_must_fail git show-ref -s --verify PSEUDOREF
+test_expect_success 'delete root ref' '
+	git update-ref -d ROOT_REF_HEAD &&
+	test_must_fail git show-ref -s --verify ROOT_REF_HEAD
 '
 
-test_expect_success 'do not delete pseudoref with wrong old value' '
-	git update-ref PSEUDOREF $A &&
-	test_must_fail git update-ref -d PSEUDOREF $B 2>err &&
-	test $A = $(git show-ref -s --verify PSEUDOREF) &&
+test_expect_success 'do not delete root ref with wrong old value' '
+	git update-ref ROOT_REF_HEAD $A &&
+	test_must_fail git update-ref -d ROOT_REF_HEAD $B 2>err &&
+	test $A = $(git show-ref -s --verify ROOT_REF_HEAD) &&
 	test_grep "cannot lock ref.*expected" err
 '
 
-test_expect_success 'delete pseudoref with correct old value' '
-	git update-ref -d PSEUDOREF $A &&
-	test_must_fail git show-ref -s --verify PSEUDOREF
+test_expect_success 'delete root ref with correct old value' '
+	git update-ref -d ROOT_REF_HEAD $A &&
+	test_must_fail git show-ref -s --verify ROOT_REF_HEAD
 '
 
-test_expect_success 'create pseudoref with old OID zero' '
-	git update-ref PSEUDOREF $A $Z &&
-	test $A = $(git show-ref -s --verify PSEUDOREF)
+test_expect_success 'create root ref with old OID zero' '
+	git update-ref ROOT_REF_HEAD $A $Z &&
+	test $A = $(git show-ref -s --verify ROOT_REF_HEAD)
 '
 
-test_expect_success 'do not overwrite pseudoref with old OID zero' '
-	test_when_finished git update-ref -d PSEUDOREF &&
-	test_must_fail git update-ref PSEUDOREF $B $Z 2>err &&
-	test $A = $(git show-ref -s --verify PSEUDOREF) &&
+test_expect_success 'do not overwrite root ref with old OID zero' '
+	test_when_finished git update-ref -d ROOT_REF_HEAD &&
+	test_must_fail git update-ref ROOT_REF_HEAD $B $Z 2>err &&
+	test $A = $(git show-ref -s --verify ROOT_REF_HEAD) &&
 	test_grep "already exists" err
 '
 
diff --git a/t/t1403-show-ref.sh b/t/t1403-show-ref.sh
index 226b3ec0e8..38648fd560 100755
--- a/t/t1403-show-ref.sh
+++ b/t/t1403-show-ref.sh
@@ -181,7 +181,7 @@ test_expect_success 'show-ref --verify HEAD' '
 	test_must_be_empty actual
 '
 
-test_expect_success 'show-ref --verify pseudorefs' '
+test_expect_success 'show-ref --verify root refs' '
 	git update-ref CHERRY_PICK_HEAD HEAD $ZERO_OID &&
 	test_when_finished "git update-ref -d CHERRY_PICK_HEAD" &&
 	git show-ref -s --verify HEAD >actual &&
diff --git a/t/t1460-refs-migrate.sh b/t/t1460-refs-migrate.sh
index ecf6411288..1f59d820a0 100755
--- a/t/t1460-refs-migrate.sh
+++ b/t/t1460-refs-migrate.sh
@@ -170,7 +170,7 @@ do
 			test_cmp expect actual
 		'
 
-		test_expect_success "$from_format -> $to_format: pseudo-refs" '
+		test_expect_success "$from_format -> $to_format: root refs" '
 			test_when_finished "rm -rf repo" &&
 			git init --ref-storage-format=$from_format repo &&
 			test_commit -C repo initial &&
@@ -178,7 +178,7 @@ do
 			test_migration repo "$to_format"
 		'
 
-		test_expect_success "$from_format -> $to_format: special refs are left alone" '
+		test_expect_success "$from_format -> $to_format: pseudorefs are left alone" '
 			test_when_finished "rm -rf repo" &&
 			git init --ref-storage-format=$from_format repo &&
 			test_commit -C repo initial &&
diff --git a/t/t4013-diff-various.sh b/t/t4013-diff-various.sh
index 21c9ad7c5d..43a9bf6d7c 100755
--- a/t/t4013-diff-various.sh
+++ b/t/t4013-diff-various.sh
@@ -115,10 +115,10 @@ test_expect_success setup '
 	git checkout -f main &&
 
 	# Same merge as main, but with parents reversed. Hide it in a
-	# pseudo-ref to avoid impacting tests with --all.
+	# root ref to avoid impacting tests with --all.
 	commit=$(echo reverse |
 		 git commit-tree -p main^2 -p main^1 main^{tree}) &&
-	git update-ref REVERSE $commit &&
+	git update-ref REVERSE_HEAD $commit &&
 
 	git config diff.renames false &&
 
@@ -328,7 +328,7 @@ diff-tree --cc --stat --summary main
 diff-tree -c --stat --summary side
 diff-tree --cc --stat --summary side
 diff-tree --cc --shortstat main
-diff-tree --cc --summary REVERSE
+diff-tree --cc --summary REVERSE_HEAD
 # improved by Timo's patch
 diff-tree --cc --patch-with-stat main
 # improved by Timo's patch
diff --git a/t/t4013/diff.diff-tree_--cc_--summary_REVERSE b/t/t4013/diff.diff-tree_--cc_--summary_REVERSE_HEAD
similarity index 73%
rename from t/t4013/diff.diff-tree_--cc_--summary_REVERSE
rename to t/t4013/diff.diff-tree_--cc_--summary_REVERSE_HEAD
index e208dd5682..0f86ae60db 100644
--- a/t/t4013/diff.diff-tree_--cc_--summary_REVERSE
+++ b/t/t4013/diff.diff-tree_--cc_--summary_REVERSE_HEAD
@@ -1,4 +1,4 @@
-$ git diff-tree --cc --summary REVERSE
+$ git diff-tree --cc --summary REVERSE_HEAD
 2562325a7ee916efb2481da93073b82cec801cbc
  create mode 100644 file1
  delete mode 100644 file2
diff --git a/t/t6302-for-each-ref-filter.sh b/t/t6302-for-each-ref-filter.sh
index a2fc257f98..5a6d6fef55 100755
--- a/t/t6302-for-each-ref-filter.sh
+++ b/t/t6302-for-each-ref-filter.sh
@@ -31,7 +31,7 @@ test_expect_success 'setup some history and refs' '
 	git update-ref refs/odd/spot main
 '
 
-test_expect_success '--include-root-refs pattern prints pseudorefs' '
+test_expect_success '--include-root-refs pattern prints root refs' '
 	cat >expect <<-\EOF &&
 	HEAD
 	ORIG_HEAD
@@ -52,7 +52,7 @@ test_expect_success '--include-root-refs pattern prints pseudorefs' '
 	test_cmp expect actual
 '
 
-test_expect_success '--include-root-refs pattern does not print special refs' '
+test_expect_success '--include-root-refs pattern does not print pseudorefs' '
 	test_when_finished "rm -rf repo" &&
 	git init repo &&
 	(
diff --git a/t/t9902-completion.sh b/t/t9902-completion.sh
index f6de74054a..1cb55f9301 100755
--- a/t/t9902-completion.sh
+++ b/t/t9902-completion.sh
@@ -2693,13 +2693,13 @@ test_expect_success 'checkout matches case insensitively with GIT_COMPLETION_IGN
 	)
 '
 
-test_expect_success 'checkout completes pseudo refs' '
+test_expect_success 'checkout completes root refs' '
 	test_completion "git checkout H" <<-\EOF
 	HEAD Z
 	EOF
 '
 
-test_expect_success 'checkout completes pseudo refs case insensitively with GIT_COMPLETION_IGNORE_CASE' '
+test_expect_success 'checkout completes root refs case insensitively with GIT_COMPLETION_IGNORE_CASE' '
 	(
 		GIT_COMPLETION_IGNORE_CASE=1 &&
 		test_completion "git checkout h" <<-\EOF
-- 
2.56.0
Message details and related content
On Fri, 23 Oct 2026 at 14:21, Julia Evans [off-list ref] wrote:
I wonder if gitdatamodel.adoc should also cover pseudorefs and root refs
in the “References” section. Right now it only says something extremely
short on the topic:

  Git may also create references other than HEAD at the base of
  the hierarchy, like ORIG_HEAD.
I've given it a go as part of commit 3 in v2.

Andy

Re: [PATCH 1/5] doc: glossary: add root ref and clarify pseudorefs

Message details and related content
On Fri, 23 Oct 2026 at 08:41, Patrick Steinhardt [off-list ref] wrote:
On Thu, Oct 22, 2026 at 10:28:49PM +0100, Andy Koppe wrote:
quoted
diff --git a/Documentation/glossary-content.adoc b/Documentation/glossary-content.adoc
index 52301a56dd..b2c582254b 100644
--- a/Documentation/glossary-content.adoc
+++ b/Documentation/glossary-content.adoc
@@ -500,9 +500,10 @@ exclude;;
      unusual refs.

 [[def_pseudoref]]pseudoref::
-     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].
As Junio already mentioned, I think we should retain the information
about different semantics.

I also think that the previous guidance around how pseudorefs can be
used is weakened, because now you only list examples of how they cannot
be used. Previously though we explicitly specified that the only
supported operation is reading the refs directly. So instead of giving
examples of how they cannot be used, I think we should continue to list
the few operations that _are_ possible.
Instead of saying that they are different, I tried to describe how
they are different . But, yep, I struggled a bit with that.

In v2, I have added git-log and git-diff as examples of where
pseudorefs can be used like references.

Thanks,
Andy

Re: [PATCH 2/5] doc: glossary: update per-worktree refs

Message details and related content
On Fri, 23 Oct 2026 at 08:41, Patrick Steinhardt [off-list ref] wrote:
On Thu, Oct 22, 2026 at 10:28:50PM +0100, Andy Koppe wrote:
quoted
diff --git a/Documentation/glossary-content.adoc b/Documentation/glossary-content.adoc
index b2c582254b..eff6d23215 100644
--- a/Documentation/glossary-content.adoc
+++ b/Documentation/glossary-content.adoc
@@ -494,10 +494,16 @@ exclude;;
      interface than the <<def_plumbing,plumbing>>.

 [[def_per_worktree_ref]]per-worktree ref::
-     Refs that are per-<<def_worktree,worktree>>, rather than
-     global.  This is presently only <<def_HEAD,HEAD>> and any refs
-     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/`.
++
+<<def_pseudoref,Pseudorefs>> are also specific to each worktree.
Should we maybe also turn this into an itemized list? Makes it easier to
see at a glance which rrefs are per-worktree, and it becomes easier to
extend going forward.
Done in v2.
quoted
+See "REFS" in linkgit:git-worktree[1] for how to access the
+per-worktree refs of other worktrees.

 [[def_pseudoref]]pseudoref::
      A name that can be used like a <<def_ref,ref>> to refer to
@@ -788,5 +794,5 @@ 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).
This is a bit curious as you distinguish between per-worktree refs and
pseudorefs, even though you mention both of them as part of the
per-worktree ref section.
It's difficult, because pseudorefs are per-worktree, but they aren't
refs. I thought they should still be mentioned under "per-worktree
refs", without calling them refs.
I wonder whether we should simply refer to
that section without naming any examples.
Good idea. Done in v2.

Andy

Re: [PATCH 3/5] doc: stop calling root refs pseudorefs

Message details and related content
On Fri, 23 Oct 2026 at 08:42, Patrick Steinhardt [off-list ref] wrote:
On Thu, Oct 22, 2026 at 10:28:51PM +0100, Andy Koppe wrote:
quoted
diff --git a/Documentation/git-update-ref.adoc b/Documentation/git-update-ref.adoc
index 37a5019a8b..c775334230 100644
--- a/Documentation/git-update-ref.adoc
+++ b/Documentation/git-update-ref.adoc
@@ -163,13 +163,12 @@ still see a subset of the modifications.

 LOGGING UPDATES
 ---------------
-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.  Log lines are
-formatted as:
+If config parameter "core.logAllRefUpdates" is true and the ref is
+HEAD or one under "refs/heads/", "refs/remotes/" or "refs/notes/", or
+if the file "$GIT_DIR/logs/<ref>" exists, then `git update-ref` will
It's kind of hard to understand how the ANDs and ORs play together.
Like, does the config parameter extend to all subsequent ORs? That's not
really a new issue though.
quoted
+append a line to the log file "$GIT_DIR/logs/<ref>" (dereferencing all
Also, this infomation about "$GIT_DIR/logs/<ref>" is out-of-date, as the
reftable backend doesn't store its reflogs in there.

I wonder whether we should maybe just stop explaining how
"core.logAllRefUpdates" works and instead refer to git-config(1).
The whole LOGGING UPDATES section in git-update-ref(1) seems outdated
as it assumes ref files and SHA-256, but I think that's out of scope
of this patch series.
quoted
diff --git a/Documentation/git-worktree.adoc b/Documentation/git-worktree.adoc
index e6e77252ab..f0eb6d4137 100644
--- a/Documentation/git-worktree.adoc
+++ b/Documentation/git-worktree.adoc
@@ -303,11 +303,10 @@ but others are specific to an individual worktree. One example is `HEAD`,
 which is different for each worktree. This section is about the sharing
 rules and how to access refs of one worktree from another.

-In general, all pseudo refs are per-worktree and all refs starting with
-`refs/` are shared. Pseudo refs are ones like `HEAD` which are directly
-under `$GIT_DIR` instead of inside `$GIT_DIR/refs`. There are exceptions,
-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.
It might make sense to have a bulleted list here, same as I proposed in
a preceding patch.
Yep. Done in v2.

Andy

Re: [PATCH 4/5] refs: stop calling root refs pseudorefs

Message details and related content
On Fri, 23 Oct 2026 at 08:42, Patrick Steinhardt wrote:
On Thu, Oct 22, 2026 at 10:28:52PM +0100, Andy Koppe wrote:
quoted
Replace "pseudoref" with "root ref" in the help text for
"git for-each-ref --include-root-refs", in comments in refs.h, and in
comments in the bash completion script, including the one for
__git_pseudoref_exists(), which is also used for root refs.
I feel like this sentence is somewhat pointless, as you basically
just explain the diff.
quoted
Update the description of pseudorefs in refs.h to match the glossary.
And this is again info that can simply be dropped.
Fair. Dropped in v2.

Andy

Re: [PATCH v2 0/5] root refs vs pseudorefs

Message details and related content

On Sun, Nov 1, 2026, at 6:03 PM, Andy Koppe wrote:
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%)

156 more quoted lines

When 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?

Re: [PATCH v2 0/5] root refs vs pseudorefs

Message details and related content
On Mon, 2 Nov 2026 at 18:20, Julia Evans wrote:
On Sun, Nov 1, 2026, at 6:03 PM, Andy Koppe wrote:
quoted
    ++[[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
nevertheless
When 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?
Good question. The differences certainly aren't obvious, and they
barely matter to users.

Root refs:
- Can be stored in reftables instead of files in .git.
- Contain either a single hash or a single symbolic ref such as 'ref:
refs/heads/main'.
- Can be modified with 'git update-ref' (generally a bad idea for
built-in root refs) and listed with 'git for-each-ref' and similar
commands.

Pseudorefs:
- Are always stored as files in .git.
- May contain multiple hashes, and in the case of FETCH_HEAD, carry
additional information about where each hash came from.
- Are only modified by the commands they serve (i.e. fetch, merge and
pull) and can't be listed.

The big thing they have in common, apart from the *_HEAD names, is
that you can pass both types to commands such as 'git log', 'git diff'
or 'git checkout' to refer to a commit or tree, just like any ref
under refs/.

Re: [PATCH v2 0/5] root refs vs pseudorefs

Message details and related content

On Mon, Nov 2, 2026, at 2:47 PM, Andy Koppe wrote:
On Mon, 2 Nov 2026 at 18:20, Julia Evans wrote:
quoted
On Sun, Nov 1, 2026, at 6:03 PM, Andy Koppe wrote:
quoted
quoted
    ++[[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
nevertheless
When 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?
Good question. The differences certainly aren't obvious, and they
barely matter to users.

Root refs:
- Can be stored in reftables instead of files in .git.
- Contain either a single hash or a single symbolic ref such as 'ref:
refs/heads/main'.
- Can be modified with 'git update-ref' (generally a bad idea for
built-in root refs) and listed with 'git for-each-ref' and similar

13 more quoted lines

That’s very helpful. I think it would make sense to include
“may contain multiple hashes” at least, that on its own
makes it very clear what the difference is.

My impression is that FETCH_HEAD specifically is a
relatively popular pseudoref and folks would be interested to
know that it can contain multiple hashes.

If a pseudoref contains multiple hashes and you pass it to 
a command, will the command use the first hash?

Also are FETCH_HEAD and MERGE_HEAD the only
pseudorefs?

Re: [PATCH v2 0/5] root refs vs pseudorefs

Message details and related content
"Julia Evans" [off-list ref] writes:

... 200 lines of quoted material omitted ...
quoted
    ++[[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 
nevertheless
When 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?
A pseudoref cannot be treated as a true ref because it can describe
multiple objects.  If you force it to be interpreted as an object,
you will still get one of the objects, but that is not the primary
way it is designed to be used.  FETCH_HEAD even describes the nature
of each object, e.g.,

a018953...4		branch 'maint' of gitolite.kernel.org:/pub/scm/git/git
8d1b215...8	not-for-merge	branch 'master' of gitolite.kernel.org:/pub/scm/git/git
882cbad...5	not-for-merge	branch 'next' of gitolite.kernel.org:/pub/scm/git/git
4231a2f...4	not-for-merge	branch 'jch' of gitolite.kernel.org:/pub/scm/git/git
2fa90fe...8	not-for-merge	branch 'seen' of gitolite.kernel.org:/pub/scm/git/git

The other kind, just like a normal ref, describes one and only one
object.  The only special trait it has is that it is outside the
refs/ hierarchy, so it may be a slight misnomer to call them "root"
refs.  We do accept $GIT_DIR/ORIG_HEAD as if it were a real ref, but
if we did:

        $ mkdir -p .git/foo/bar
        $ git rev-parse HEAD~24 >.git/foo/bar/baz
        $ git show -s foo/bar/baz

we would end up treating "foo/bar/baz" as if it were a real ref,
too, with exactly the same mechanism used to treat ORIG_HEAD.

The only difference between .git/ORIG_HEAD and .git/foo/bar/baz in
the above example is that we _do_ create "root" refs ourselves,
while we do not create such funky things like foo/bar/baz (hence
they do not deserve a name, and such "use" may suddenly break
without notice).

By the way, *please* trim your quote.  Having to scroll past 200
lines of material that are totally irrelevant to understanding your
comment is a real pain.

Thanks.

Re: [PATCH v2 0/5] root refs vs pseudorefs

Message details and related content
On Mon, 2 Nov 2026 at 21:01, Julia Evans wrote:
On Mon, Nov 2, 2026, at 2:47 PM, Andy Koppe wrote:
quoted
Root refs:
- Can be stored in reftables instead of files in .git.
- Contain either a single hash or a single symbolic ref such as 'ref:
refs/heads/main'.
- Can be modified with 'git update-ref' (generally a bad idea for
built-in root refs) and listed with 'git for-each-ref' and similar
commands.

Pseudorefs:
- Are always stored as files in .git.
- May contain multiple hashes, and in the case of FETCH_HEAD, carry
additional information about where each hash came from.
- Are only modified by the commands they serve (i.e. fetch, merge and
pull) and can't be listed.

The big thing they have in common, apart from the *_HEAD names, is
that you can pass both types to commands such as 'git log', 'git diff'
or 'git checkout' to refer to a commit or tree, just like any ref
under refs/.
That’s very helpful. I think it would make sense to include
“may contain multiple hashes” at least, that on its own
makes it very clear what the difference is.

My impression is that FETCH_HEAD specifically is a
relatively popular pseudoref and folks would be interested to
know that it can contain multiple hashes.

If a pseudoref contains multiple hashes and you pass it to
a command, will the command use the first hash?

Also are FETCH_HEAD and MERGE_HEAD the only
pseudorefs?
I think the pseudoref glossary entry covers these questions. As it
appears in 'man' after v2:

pseudoref
    A name that can be used like a ref to refer to an object, but that
    is not stored in the same way as refs. For example, pseudorefs can
    be passed to git-log(1) or git-diff(1), but they cannot be written
    by git-update-ref(1) or listed by git-for-each-ref(1).

    The following pseudorefs are known to Git:

    •   FETCH_HEAD is written by git-fetch(1) or 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 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.

Re: [PATCH v2 1/5] doc: glossary: add root ref and clarify pseudorefs

Message details and related content
On Sun, Nov 01, 2026 at 11:03:19PM +0000, Andy Koppe wrote:
quoted hunk
diff --git a/Documentation/glossary-content.adoc b/Documentation/glossary-content.adoc
index 52301a56dd..f905b4467b 100644
--- a/Documentation/glossary-content.adoc
+++ b/Documentation/glossary-content.adoc
@@ -500,18 +500,25 @@ exclude;;
 	unusual refs.
 
 [[def_pseudoref]]pseudoref::
-	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 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].
I'm still not entirely convinced by this change, as it only focusses on
the difference in storage. How about this instead:

    [[def_pseudoref]]pseudoref::
        A name that can be used like a <<def_ref,ref>> to refer to an
        <<def_object,object>>, but that has different semantics: it can
        record more than one object ID and may carry additional metadata.
        In contrast to root refs, pseudorefs are therefore not stored or
        handled by the ref backend. They can be passed to commands like
        linkgit:git-log[1] or linkgit:git-diff[1], but cannot be written
        by linkgit:git-update-ref[1] or listed by
        linkgit:git-for-each-ref[1].

Patrick

Re: [PATCH v2 2/5] doc: glossary: update per-worktree refs

Message details and related content
On Sun, Nov 01, 2026 at 11:03:20PM +0000, Andy Koppe wrote:
[snip]
Bring the list up to date, and refer to git-worktree(1) for how to
access per-worktree refs of other worktrees. In the "worktree" entry,
describe HEAD as a per-worktree ref and MERGE_HEAD as a pseudoref,
rather than listing them separately from per-worktree refs.
This doesn't match what you're doing anymore, as you don't mention
MERGE_HEAD at all anymore.
quoted hunk
diff --git a/Documentation/glossary-content.adoc b/Documentation/glossary-content.adoc
index f905b4467b..67fe49b26d 100644
--- a/Documentation/glossary-content.adoc
+++ b/Documentation/glossary-content.adoc
@@ -791,5 +804,5 @@ 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, <<def_per_worktree_ref,per-worktree refs>>, and the
+	per-worktree configuration file).
Patrick

Re: [PATCH v2 3/5] doc: stop calling root refs pseudorefs

Message details and related content
On Sun, Nov 01, 2026 at 11:03:21PM +0000, Andy Koppe wrote:
quoted hunk
diff --git a/Documentation/gitdatamodel.adoc b/Documentation/gitdatamodel.adoc
index 8d9be02036..6958ac033c 100644
--- a/Documentation/gitdatamodel.adoc
+++ b/Documentation/gitdatamodel.adoc
@@ -211,15 +211,25 @@ 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
Nit: I'd drop the "also" here. It made sense before, but it doesn't
anymore now that this is its own list entry.
quoted hunk
+    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 nevertheless
+    be used to refer to objects in commands such as linkgit:git-log[1] and
+    linkgit:git-diff[1].
Do we maybe want to provide pointers to the glossary for those two
entries?

Patrick

Re: [PATCH v2 0/5] root refs vs pseudorefs

Message details and related content
pseudoref
    A name that can be used like a ref to refer to an object, but that
    is not stored in the same way as refs. For example, pseudorefs can
    be passed to git-log(1) or git-diff(1), but they cannot be written
    by git-update-ref(1) or listed by git-for-each-ref(1).

    The following pseudorefs are known to Git:

    •   FETCH_HEAD is written by git-fetch(1) or 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.
I found "Each object ID is annotated with metadata  indicating where it was
fetched from and its fetch status." to be very hard to parse, and I had to
go look at .git/FETCH_HEAD myself to understand what it was talking about.

It might help to instead describe `FETCH_HEAD` with an example,
especially since we already aren't giving a lot of details. 
Maybe something like this:

  Git has 2 special pseudorefs: `MERGE_HEAD` and `FETCH_HEAD`.
  Unlike references (which can only contain an object ID or the name of
  another reference), these have their own format:
  
  - `FETCH_HEAD` has a list of entries like this:
    `d4c5f27105ee2e44366d718dbc5db95bbe99c714 not-for-merge branch 'example' of https://example.com/x/y`
    with details about what was fetched by linkgit:git-fetch[1].
  - `MERGE_HEAD` has a list of commit IDs
  
  These are stored as files in the `.git` directory. You can pass a pseudoref
  to `git show`, `git log`, etc, and it will use the first ID in the list. 
  Pseudorefs can't be written by git-update-ref(1) or listed by
  git-for-each-ref(1).

Re: [PATCH v2 1/5] doc: glossary: add root ref and clarify pseudorefs

Message details and related content
Patrick Steinhardt [off-list ref] writes:
I'm still not entirely convinced by this change, as it only focusses on
the difference in storage. How about this instead:

    [[def_pseudoref]]pseudoref::
        A name that can be used like a <<def_ref,ref>> to refer to an
        <<def_object,object>>, but that has different semantics: it can
        record more than one object ID and may carry additional metadata.
        In contrast to root refs, pseudorefs are therefore not stored or
        handled by the ref backend. They can be passed to commands like
        linkgit:git-log[1] or linkgit:git-diff[1], but cannot be written
        by linkgit:git-update-ref[1] or listed by
        linkgit:git-for-each-ref[1].
Between the two I would prefer yours.  I would replace "different"
with "additional", because a pseudoref can be used as the name of
the first object it contains in a fully compatible way as any other
refs, I want to avoid "different" that implies "there are additional
things it can do but there are things it cannot do" Venn diagram.

Then replace "carry additional" with "carry extra", just to avoid
repetitions of "additional" due to the earlier change.

[PATCH v3 0/5] root refs vs pseudorefs

Revisions (3)
  1. v1
  2. v2 [diff against previous]
  3. v3 current [diff against previous]
Message details and related content
Changes in v3:
- Explain the additional semantics of pseudorefs in the glossary.
- Clarify the FETCH_HEAD and MERGE_HEAD descriptions.
- Point gitdatamodel(7) to the glossary for root refs and pseudorefs.
- Reword messages of commits 1 and 2.

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               |  21 +++-
 Documentation/glossary-content.adoc           | 109 +++++++++++-------
 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, 167 insertions(+), 121 deletions(-)
 rename t/t4013/{diff.diff-tree_--cc_--summary_REVERSE => diff.diff-tree_--cc_--summary_REVERSE_HEAD} (73%)

Range-diff against v2:
1:  c52fbbd369 ! 1:  5630c671db doc: glossary: add root ref and clarify pseudorefs

130 more lines

[PATCH v3 1/5] doc: glossary: add root ref and clarify pseudorefs

Message details and related content
The glossary describes refs in the root of the ref hierarchy, but does
not actually name them "root refs", even though the term is used
elsewhere, for example by "git for-each-ref --include-root-refs". It
also defines pseudorefs as refs, yet is_root_ref() deliberately excludes
them, as they are not stored or handled by the ref backend.

Add a "root ref" entry, moving the naming rules and the list of
irregular root refs there from the "ref" entry. State that pseudorefs
are not root refs, and refer to gitrevisions(7) for descriptions of
commonly used root refs and pseudorefs.

Reword the "pseudoref" entry so that it no longer calls them refs, and
explain how their additional semantics set them apart. Clarify what
FETCH_HEAD records and what its "fetch status" annotation means, and
that MERGE_HEAD is also written by git-pull(1) and not only for merge
conflicts.

Signed-off-by: Andy Koppe <redacted>
---
 Documentation/glossary-content.adoc | 84 +++++++++++++++++------------
 1 file changed, 51 insertions(+), 33 deletions(-)
diff --git a/Documentation/glossary-content.adoc b/Documentation/glossary-content.adoc
index 52301a56dd..668f309a6f 100644
--- a/Documentation/glossary-content.adoc
+++ b/Documentation/glossary-content.adoc
@@ -500,18 +500,26 @@ exclude;;
 	unusual refs.
 
 [[def_pseudoref]]pseudoref::
-	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 an
+	<<def_object,object>>, but that has additional semantics: it can
+	record more than one object ID and may carry extra metadata. In
+	contrast to <<def_root_ref,root refs>>, pseudorefs are therefore
+	not stored or handled by the ref backend.
++
+Pseudorefs can be passed to commands such as linkgit:git-log[1] or
+linkgit:git-diff[1], but cannot be written by linkgit:git-update-ref[1]
+or listed by linkgit:git-for-each-ref[1]. When a pseudoref contains more
+than one object ID, using it to refer to an object yields the first one.
 +
 The following pseudorefs are known to Git:
++
+ - `FETCH_HEAD` is written by linkgit:git-fetch[1] or linkgit:git-pull[1]
+   to record the objects that have been fetched. Each object is annotated
+   with the ref and repository it was fetched from and whether it is to be
+   merged when pulling.
 
- - `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.
+ - `MERGE_HEAD` is written by linkgit:git-merge[1] or linkgit:git-pull[1]
+   to record the commits being merged while a merge is in progress.
 
 [[def_pull]]pull::
 	Pulling a <<def_branch,branch>> means to <<def_fetch,fetch>> it and
@@ -562,33 +570,12 @@ The following pseudorefs are known to Git:
 +
 The ref namespace is hierarchical.
 Ref names must either start with `refs/` or be located in the root of
-the hierarchy. For the latter, their name must follow these rules:
-+
---
- - The name consists of only upper-case characters or underscores.
-
- - The name ends with "`_HEAD`" or is equal to "`HEAD`".
---
-+
-There are some irregular refs in the root of the hierarchy that do not
-match these rules. The following list is exhaustive and shall not be
-extended in the future:
-+
---
- - `AUTO_MERGE`
-
- - `BISECT_EXPECTED_REV`
-
- - `NOTES_MERGE_PARTIAL`
-
- - `NOTES_MERGE_REF`
-
- - `MERGE_AUTOSTASH`
---
+the hierarchy, in which case they must follow the naming rules for
+<<def_root_ref,root refs>>.
 +
 Different subhierarchies are used for different purposes. For example,
 the `refs/heads/` hierarchy is used to represent local branches whereas
-the `refs/tags/` hierarchy is used to represent local tags..
+the `refs/tags/` hierarchy is used to represent local tags.
 +
 See also "REFERENCES" in linkgit:gitdatamodel[7].
 
@@ -640,6 +627,37 @@ format. See linkgit:git-reflog[1] for details.
 	To throw away part of the development, i.e. to assign the
 	<<def_head,head>> to an earlier <<def_revision,revision>>.
 
+[[def_root_ref]]root ref::
+	A <<def_ref,ref>> located in the root of the ref hierarchy, outside
+	`refs/`. Root ref names must follow these rules:
++
+--
+ - The name consists of only upper-case characters or underscores.
+
+ - The name ends with "`_HEAD`" or is equal to "`HEAD`".
+--
++
+The following irregular root refs are exceptions to these rules. This
+list is exhaustive and shall not be extended in future:
++
+--
+ - `AUTO_MERGE`
+
+ - `BISECT_EXPECTED_REV`
+
+ - `NOTES_MERGE_PARTIAL`
+
+ - `NOTES_MERGE_REF`
+
+ - `MERGE_AUTOSTASH`
+--
++
+<<def_pseudoref,Pseudorefs>> are not considered root refs, even though
+they follow the naming rules.
++
+See linkgit:gitrevisions[7] for descriptions of commonly used root refs
+and pseudorefs.
+
 [[def_SCM]]SCM::
 	Source code management (tool).
 
-- 
2.56.0

[PATCH v3 2/5] doc: glossary: update per-worktree refs

Message details and related content
The "per-worktree ref" entry says that only HEAD and refs under
"refs/bisect/" are per-worktree, but that has been out of date since
a9be29c981 (sequencer: make refs generated by the `label` command
worktree-local, 2018-04-25). All root refs are per-worktree, as are refs
under "refs/worktree/" and "refs/rewritten/". Pseudorefs are specific to
each worktree too.

Bring the list up to date, and refer to git-worktree(1) for how to
access per-worktree refs of other worktrees. In the "worktree" entry,
link to "per-worktree ref" instead of listing HEAD and MERGE_HEAD.

Signed-off-by: Andy Koppe <redacted>
---
 Documentation/glossary-content.adoc | 25 +++++++++++++++++++------
 1 file changed, 19 insertions(+), 6 deletions(-)
diff --git a/Documentation/glossary-content.adoc b/Documentation/glossary-content.adoc
index 668f309a6f..c7ab1c981f 100644
--- a/Documentation/glossary-content.adoc
+++ b/Documentation/glossary-content.adoc
@@ -494,10 +494,23 @@ exclude;;
 	interface than the <<def_plumbing,plumbing>>.
 
 [[def_per_worktree_ref]]per-worktree ref::
-	Refs that are per-<<def_worktree,worktree>>, rather than
-	global.  This is presently only <<def_HEAD,HEAD>> and any refs
-	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>>,
+	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.
++
+See "REFS" in linkgit:git-worktree[1] for how to access the
+per-worktree refs of other worktrees.
 
 [[def_pseudoref]]pseudoref::
 	A name that can be used like a <<def_ref,ref>> to refer to an
@@ -792,5 +805,5 @@ 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, <<def_per_worktree_ref,per-worktree refs>>, and the
+	per-worktree configuration file).
-- 
2.56.0

[PATCH v3 3/5] doc: stop calling root refs pseudorefs

Message details and related content
Several manual pages still use "pseudoref" in its old sense of any ref
outside of "refs/", which the glossary now calls a root ref. Pseudorefs
have since been restricted to FETCH_HEAD and MERGE_HEAD, which are not
considered root refs.

Replace "pseudoref" with "root ref" in those pages, giving HEAD and
ORIG_HEAD as examples. In git-worktree(1), say that both root refs and
pseudorefs are per-worktree, and drop the description of them as being
directly under $GIT_DIR, because that does not apply to the reftable
backend.

In git-ls-remote(1), describe --refs as omitting refs outside "refs/",
since that is what it actually checks. In the example, refer to HEAD,
which is the only root ref that git-upload-pack(1) advertises. While
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. 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 <redacted>
---
 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         | 21 +++++++++++++++++----
 Documentation/rev-list-options.adoc     |  4 ++--
 7 files changed, 37 insertions(+), 22 deletions(-)
diff --git a/Documentation/config/core.adoc b/Documentation/config/core.adoc
index bdc74d291c..8f8fe36a58 100644
--- a/Documentation/config/core.adoc
+++ b/Documentation/config/core.adoc
@@ -376,7 +376,7 @@ core.logAllRefUpdates::
 	`refs/heads/`), remote refs (i.e. under `refs/remotes/`),
 	note refs (i.e. under `refs/notes/`), and the symbolic ref `HEAD`.
 	If it is set to `always`, then a missing reflog is automatically
-	created for any ref under `refs/`.
+	created for any ref, including root refs such as `ORIG_HEAD`.
 +
 This information can be used to determine what commit
 was the tip of a branch "2 days ago".
diff --git a/Documentation/for-each-ref-options.adoc b/Documentation/for-each-ref-options.adoc
index 54e2fa95c2..2b6d4ede0b 100644
--- a/Documentation/for-each-ref-options.adoc
+++ b/Documentation/for-each-ref-options.adoc
@@ -73,7 +73,7 @@ TAB %(refname)`.
 	using the same rules as _<pattern>_ above.
 
 `--include-root-refs`::
-	List root refs (`HEAD` and pseudorefs) apart from regular refs.
+	Also list root refs, such as `HEAD` or `ORIG_HEAD`.
 
 `--start-after=<marker>`::
     Allows paginating the output by skipping references up to and including the
diff --git a/Documentation/git-ls-remote.adoc b/Documentation/git-ls-remote.adoc
index d71c4ab3e2..7ce7df3149 100644
--- a/Documentation/git-ls-remote.adoc
+++ b/Documentation/git-ls-remote.adoc
@@ -35,7 +35,7 @@ OPTIONS
 	with other git subcommands.
 
 --refs::
-	Do not show peeled tags or pseudorefs like `HEAD` in the output.
+	Do not show peeled tags or refs outside `refs/`.
 
 -q::
 --quiet::
@@ -118,7 +118,7 @@ line shows the name of the object the tag points at.
 EXAMPLES
 --------
 
-* List all references (including symbolics and pseudorefs), peeling
+* List all references (including `HEAD` and symbolic refs), peeling
   tags:
 +
 ----
diff --git a/Documentation/git-update-ref.adoc b/Documentation/git-update-ref.adoc
index 37a5019a8b..c775334230 100644
--- a/Documentation/git-update-ref.adoc
+++ b/Documentation/git-update-ref.adoc
@@ -163,13 +163,12 @@ still see a subset of the modifications.
 
 LOGGING UPDATES
 ---------------
-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.  Log lines are
-formatted as:
+If config parameter "core.logAllRefUpdates" is true and the ref is
+HEAD or one under "refs/heads/", "refs/remotes/" or "refs/notes/", or
+if 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.  Log lines are formatted as:
 
     oldsha1 SP newsha1 SP committer LF
 
diff --git a/Documentation/git-worktree.adoc b/Documentation/git-worktree.adoc
index e6e77252ab..ba620d8ace 100644
--- a/Documentation/git-worktree.adoc
+++ b/Documentation/git-worktree.adoc
@@ -303,11 +303,14 @@ but others are specific to an individual worktree. One example is `HEAD`,
 which is different for each worktree. This section is about the sharing
 rules and how to access refs of one worktree from another.
 
-In general, all pseudo refs are per-worktree and all refs starting with
-`refs/` are shared. Pseudo refs are ones like `HEAD` which are directly
-under `$GIT_DIR` instead of inside `$GIT_DIR/refs`. There are exceptions,
-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, 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
diff --git a/Documentation/gitdatamodel.adoc b/Documentation/gitdatamodel.adoc
index 8d9be02036..2e44cc5003 100644
--- a/Documentation/gitdatamodel.adoc
+++ b/Documentation/gitdatamodel.adoc
@@ -211,15 +211,28 @@ 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 create references other than `HEAD` at the root of the hierarchy,
+    for example `ORIG_HEAD` or `REBASE_HEAD`.
+
+[[pseudorefs]]
+pseudorefs::
+    linkgit:git-fetch[1], linkgit:git-merge[1] and linkgit:git-pull[1]
+    create the so-called pseudorefs `FETCH_HEAD` and `MERGE_HEAD`.
+    They are not references as such, because they are not stored by the
+    ref backend, but they can nevertheless be used to refer to objects in
+    commands such as linkgit:git-log[1] and linkgit:git-diff[1].
+
+See linkgit:gitglossary[7] for further details on root refs and
+pseudorefs.
 
 NOTE: Git may delete objects that aren't "reachable" from any reference
 or <<reflogs,reflog>>.
diff --git a/Documentation/rev-list-options.adoc b/Documentation/rev-list-options.adoc
index bd9f345690..822604278a 100644
--- a/Documentation/rev-list-options.adoc
+++ b/Documentation/rev-list-options.adoc
@@ -350,8 +350,8 @@ Under `--pretty=reference`, this information will not be shown at all.
 
 `--merge`::
 	Show commits touching conflicted paths in the range `HEAD...<other>`,
-	where `<other>` is the first existing pseudoref in `MERGE_HEAD`,
-	`CHERRY_PICK_HEAD`, `REVERT_HEAD` or `REBASE_HEAD`. Only works
+	where `<other>` is the first of `MERGE_HEAD`, `CHERRY_PICK_HEAD`,
+	`REVERT_HEAD` or `REBASE_HEAD` that exists. Only works
 	when the index has unmerged entries. This option can be used to show
 	relevant commits when resolving conflicts from a 3-way merge.
 
-- 
2.56.0

[PATCH v3 4/5] refs: stop calling root refs pseudorefs

Message details and related content
Some messages and comments in the code still use "pseudoref" in its old
sense of any ref outside of "refs/", whereas the glossary now calls
those root refs and restricts pseudorefs to FETCH_HEAD and MERGE_HEAD,
so update them.

Bash completion function __git_pseudoref_exists() is not renamed, as
there is no obvious term covering both root refs and pseudorefs, and
third-party completion scripts might depend on the name.

Signed-off-by: Andy Koppe <redacted>
---
 builtin/for-each-ref.c                 |  2 +-
 contrib/completion/git-completion.bash |  7 ++++---
 refs.h                                 | 13 ++++++-------
 revision.c                             |  2 +-
 4 files changed, 12 insertions(+), 12 deletions(-)
diff --git a/builtin/for-each-ref.c b/builtin/for-each-ref.c
index 4a2fc421db..6ff32e919c 100644
--- a/builtin/for-each-ref.c
+++ b/builtin/for-each-ref.c
@@ -48,7 +48,7 @@ int for_each_ref_core(int argc, const char **argv, const char *prefix, struct re
 		OPT_NO_CONTAINS(&filter.no_commit, N_("print only refs which don't contain the commit")),
 		OPT_BOOL(0, "ignore-case", &icase, N_("sorting and filtering are case insensitive")),
 		OPT_BOOL(0, "stdin", &from_stdin, N_("read reference patterns from stdin")),
-		OPT_BOOL(0, "include-root-refs", &include_root_refs, N_("also include HEAD ref and pseudorefs")),
+		OPT_BOOL(0, "include-root-refs", &include_root_refs, N_("also include root refs")),
 		OPT_END(),
 	};
 
diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
index c3a49e2e22..e12ac0359c 100644
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -147,8 +147,9 @@ __git_eread ()
 	test -r "$1" && IFS=$'\r\n' read -r "$2" <"$1"
 }
 
-# Runs git in $__git_repo_path to determine whether a pseudoref exists.
-# 1: The pseudo-ref to search
+# Runs git in $__git_repo_path to determine whether a root ref or
+# pseudoref exists.
+# 1: The ref to check
 __git_pseudoref_exists ()
 {
 	local ref=$1
@@ -954,7 +955,7 @@ __git_refs ()
 	esac
 }
 
-# Completes refs, short and long, local and remote, symbolic and pseudo.
+# Completes refs, short and long, local and remote, symbolic, root and pseudo.
 #
 # Usage: __git_complete_refs [<option>]...
 # --remote=<remote>: The remote to list refs from, can be the name of a
diff --git a/refs.h b/refs.h
index ee3b8a62ef..3be7ef3b0e 100644
--- a/refs.h
+++ b/refs.h
@@ -85,7 +85,7 @@ enum ref_transaction_error {
  * Even with RESOLVE_REF_ALLOW_BAD_NAME, names that escape the refs/
  * directory and do not consist of all caps and underscores cannot be
  * resolved. The function returns NULL for such ref names.
- * Caps and underscores refers to the pseudorefs, such as HEAD,
+ * Caps and underscores refers to root refs and pseudorefs, such as HEAD,
  * FETCH_HEAD and friends, that all live outside of the refs/ directory.
  */
 #define RESOLVE_REF_READING 0x01
@@ -448,8 +448,8 @@ enum refs_for_each_flag {
 	REFS_FOR_EACH_OMIT_DANGLING_SYMREFS = (1 << 2),
 
 	/*
-	 * Include root refs i.e. HEAD and pseudorefs along with the regular
-	 * refs.
+	 * Include root refs, such as HEAD and ORIG_HEAD, along with the
+	 * regular refs.
 	 */
 	REFS_FOR_EACH_INCLUDE_ROOT_REFS = (1 << 3),
 };
@@ -1306,10 +1306,9 @@ void update_ref_namespace(enum ref_namespace namespace, char *ref);
 int is_root_ref(const char *refname);
 
 /*
- * Pseudorefs are refs that have different semantics compared to
- * "normal" refs. These refs can thus not be stored in the ref backend,
- * but must always be accessed via the filesystem. The following refs
- * are pseudorefs:
+ * Pseudorefs are names that can be used like refs, but have different
+ * semantics. They cannot be stored in the ref backend, but must always
+ * be accessed via the filesystem. The following are pseudorefs:
  *
  * - FETCH_HEAD may contain multiple object IDs, and each one of them
  *   carries additional metadata like where it came from.
diff --git a/revision.c b/revision.c
index a83e499047..d6dd56faf1 100644
--- a/revision.c
+++ b/revision.c
@@ -2000,7 +2000,7 @@ static const char *lookup_other_head(struct object_id *oid)
 			return other_head[i];
 		}
 
-	die(_("--merge requires one of the pseudorefs MERGE_HEAD, CHERRY_PICK_HEAD, REVERT_HEAD or REBASE_HEAD"));
+	die(_("--merge requires one of MERGE_HEAD, CHERRY_PICK_HEAD, REVERT_HEAD or REBASE_HEAD"));
 }
 
 static void prepare_show_merge(struct rev_info *revs)
-- 
2.56.0

[PATCH v3 5/5] t: stop calling root refs pseudorefs

Message details and related content
Several test titles and comments still use "pseudoref" in its old sense
of any ref outside of "refs/", which the glossary now calls root refs.

Replace "pseudoref" with "root ref" there. In t1400 and t4013, rename
the root refs that the tests create from PSEUDOREF to ROOT_REF_HEAD and
from REVERSE to REVERSE_HEAD, so that they follow the naming rules for
root refs. This includes renaming the expected output file in t4013.

Some tests also still refer to FETCH_HEAD and MERGE_HEAD as "special
refs", which was the previous term for "pseudorefs". Correct them.

Signed-off-by: Andy Koppe <redacted>
---
 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 +-
 10 files changed, 48 insertions(+), 48 deletions(-)
 rename t/t4013/{diff.diff-tree_--cc_--summary_REVERSE => diff.diff-tree_--cc_--summary_REVERSE_HEAD} (73%)
diff --git a/t/show-ref-exists-tests.sh b/t/show-ref-exists-tests.sh
index 1351f690eb..d4a021a0c6 100644
--- a/t/show-ref-exists-tests.sh
+++ b/t/show-ref-exists-tests.sh
@@ -64,11 +64,11 @@ test_expect_success '--exists with directory fails with generic error' '
 	test_cmp expect err
 '
 
-test_expect_success '--exists with non-existent special ref' '
+test_expect_success '--exists with non-existent pseudoref' '
 	test_expect_code 2 ${git_show_ref_exists} FETCH_HEAD
 '
 
-test_expect_success '--exists with existing special ref' '
+test_expect_success '--exists with existing pseudoref' '
 	test_when_finished "rm .git/FETCH_HEAD" &&
 	git rev-parse HEAD >.git/FETCH_HEAD &&
 	${git_show_ref_exists} FETCH_HEAD
diff --git a/t/t0600-reffiles-backend.sh b/t/t0600-reffiles-backend.sh
index 62b0851852..26385aab7d 100755
--- a/t/t0600-reffiles-backend.sh
+++ b/t/t0600-reffiles-backend.sh
@@ -267,7 +267,7 @@ test_expect_success 'setup worktree' '
 	)
 '
 
-# Some refs (refs/bisect/*, pseudorefs) are kept per worktree, so they should
+# Some refs (refs/bisect/*, root refs) are kept per worktree, so they should
 # only appear in the for-each-reflog output if it is called from the correct
 # worktree, which is exercised in this test. This test is poorly written for
 # multiple reasons: 1) it creates invalidly formatted log entries. 2) it uses
diff --git a/t/t0610-reftable-basics.sh b/t/t0610-reftable-basics.sh
index a702745839..01a0310762 100755
--- a/t/t0610-reftable-basics.sh
+++ b/t/t0610-reftable-basics.sh
@@ -1156,7 +1156,7 @@ test_expect_success 'worktree: adds worktree with detached HEAD' '
 	test_cmp expect actual
 '
 
-test_expect_success 'fetch: accessing FETCH_HEAD special ref works' '
+test_expect_success 'fetch: accessing FETCH_HEAD pseudoref works' '
 	test_when_finished "rm -rf repo sub" &&
 
 	git init sub &&
diff --git a/t/t1400-update-ref.sh b/t/t1400-update-ref.sh
index 1a164c96f9..95a617254b 100755
--- a/t/t1400-update-ref.sh
+++ b/t/t1400-update-ref.sh
@@ -503,61 +503,61 @@ test_expect_success 'git cat-file blob main@{2005-05-26 23:42}:F (expect OTHER)'
 	test OTHER = $(git cat-file blob "main@{2005-05-26 23:42}:F")
 '
 
-# Test adding and deleting pseudorefs
+# Test adding and deleting root refs
 
-test_expect_success 'given old value for missing pseudoref, do not create' '
-	test_must_fail git update-ref PSEUDOREF $A $B 2>err &&
-	test_must_fail git rev-parse PSEUDOREF &&
+test_expect_success 'given old value for missing root ref, do not create' '
+	test_must_fail git update-ref ROOT_REF_HEAD $A $B 2>err &&
+	test_must_fail git rev-parse ROOT_REF_HEAD &&
 	test_grep "unable to resolve reference" err
 '
 
-test_expect_success 'create pseudoref' '
-	git update-ref PSEUDOREF $A &&
-	test $A = $(git show-ref -s --verify PSEUDOREF)
+test_expect_success 'create root ref' '
+	git update-ref ROOT_REF_HEAD $A &&
+	test $A = $(git show-ref -s --verify ROOT_REF_HEAD)
 '
 
-test_expect_success 'overwrite pseudoref with no old value given' '
-	git update-ref PSEUDOREF $B &&
-	test $B = $(git show-ref -s --verify PSEUDOREF)
+test_expect_success 'overwrite root ref with no old value given' '
+	git update-ref ROOT_REF_HEAD $B &&
+	test $B = $(git show-ref -s --verify ROOT_REF_HEAD)
 '
 
-test_expect_success 'overwrite pseudoref with correct old value' '
-	git update-ref PSEUDOREF $C $B &&
-	test $C = $(git show-ref -s --verify PSEUDOREF)
+test_expect_success 'overwrite root ref with correct old value' '
+	git update-ref ROOT_REF_HEAD $C $B &&
+	test $C = $(git show-ref -s --verify ROOT_REF_HEAD)
 '
 
-test_expect_success 'do not overwrite pseudoref with wrong old value' '
-	test_must_fail git update-ref PSEUDOREF $D $E 2>err &&
-	test $C = $(git show-ref -s --verify PSEUDOREF) &&
+test_expect_success 'do not overwrite root ref with wrong old value' '
+	test_must_fail git update-ref ROOT_REF_HEAD $D $E 2>err &&
+	test $C = $(git show-ref -s --verify ROOT_REF_HEAD) &&
 	test_grep "cannot lock ref.*expected" err
 '
 
-test_expect_success 'delete pseudoref' '
-	git update-ref -d PSEUDOREF &&
-	test_must_fail git show-ref -s --verify PSEUDOREF
+test_expect_success 'delete root ref' '
+	git update-ref -d ROOT_REF_HEAD &&
+	test_must_fail git show-ref -s --verify ROOT_REF_HEAD
 '
 
-test_expect_success 'do not delete pseudoref with wrong old value' '
-	git update-ref PSEUDOREF $A &&
-	test_must_fail git update-ref -d PSEUDOREF $B 2>err &&
-	test $A = $(git show-ref -s --verify PSEUDOREF) &&
+test_expect_success 'do not delete root ref with wrong old value' '
+	git update-ref ROOT_REF_HEAD $A &&
+	test_must_fail git update-ref -d ROOT_REF_HEAD $B 2>err &&
+	test $A = $(git show-ref -s --verify ROOT_REF_HEAD) &&
 	test_grep "cannot lock ref.*expected" err
 '
 
-test_expect_success 'delete pseudoref with correct old value' '
-	git update-ref -d PSEUDOREF $A &&
-	test_must_fail git show-ref -s --verify PSEUDOREF
+test_expect_success 'delete root ref with correct old value' '
+	git update-ref -d ROOT_REF_HEAD $A &&
+	test_must_fail git show-ref -s --verify ROOT_REF_HEAD
 '
 
-test_expect_success 'create pseudoref with old OID zero' '
-	git update-ref PSEUDOREF $A $Z &&
-	test $A = $(git show-ref -s --verify PSEUDOREF)
+test_expect_success 'create root ref with old OID zero' '
+	git update-ref ROOT_REF_HEAD $A $Z &&
+	test $A = $(git show-ref -s --verify ROOT_REF_HEAD)
 '
 
-test_expect_success 'do not overwrite pseudoref with old OID zero' '
-	test_when_finished git update-ref -d PSEUDOREF &&
-	test_must_fail git update-ref PSEUDOREF $B $Z 2>err &&
-	test $A = $(git show-ref -s --verify PSEUDOREF) &&
+test_expect_success 'do not overwrite root ref with old OID zero' '
+	test_when_finished git update-ref -d ROOT_REF_HEAD &&
+	test_must_fail git update-ref ROOT_REF_HEAD $B $Z 2>err &&
+	test $A = $(git show-ref -s --verify ROOT_REF_HEAD) &&
 	test_grep "already exists" err
 '
 
diff --git a/t/t1403-show-ref.sh b/t/t1403-show-ref.sh
index 226b3ec0e8..38648fd560 100755
--- a/t/t1403-show-ref.sh
+++ b/t/t1403-show-ref.sh
@@ -181,7 +181,7 @@ test_expect_success 'show-ref --verify HEAD' '
 	test_must_be_empty actual
 '
 
-test_expect_success 'show-ref --verify pseudorefs' '
+test_expect_success 'show-ref --verify root refs' '
 	git update-ref CHERRY_PICK_HEAD HEAD $ZERO_OID &&
 	test_when_finished "git update-ref -d CHERRY_PICK_HEAD" &&
 	git show-ref -s --verify HEAD >actual &&
diff --git a/t/t1460-refs-migrate.sh b/t/t1460-refs-migrate.sh
index ecf6411288..1f59d820a0 100755
--- a/t/t1460-refs-migrate.sh
+++ b/t/t1460-refs-migrate.sh
@@ -170,7 +170,7 @@ do
 			test_cmp expect actual
 		'
 
-		test_expect_success "$from_format -> $to_format: pseudo-refs" '
+		test_expect_success "$from_format -> $to_format: root refs" '
 			test_when_finished "rm -rf repo" &&
 			git init --ref-storage-format=$from_format repo &&
 			test_commit -C repo initial &&
@@ -178,7 +178,7 @@ do
 			test_migration repo "$to_format"
 		'
 
-		test_expect_success "$from_format -> $to_format: special refs are left alone" '
+		test_expect_success "$from_format -> $to_format: pseudorefs are left alone" '
 			test_when_finished "rm -rf repo" &&
 			git init --ref-storage-format=$from_format repo &&
 			test_commit -C repo initial &&
diff --git a/t/t4013-diff-various.sh b/t/t4013-diff-various.sh
index 21c9ad7c5d..43a9bf6d7c 100755
--- a/t/t4013-diff-various.sh
+++ b/t/t4013-diff-various.sh
@@ -115,10 +115,10 @@ test_expect_success setup '
 	git checkout -f main &&
 
 	# Same merge as main, but with parents reversed. Hide it in a
-	# pseudo-ref to avoid impacting tests with --all.
+	# root ref to avoid impacting tests with --all.
 	commit=$(echo reverse |
 		 git commit-tree -p main^2 -p main^1 main^{tree}) &&
-	git update-ref REVERSE $commit &&
+	git update-ref REVERSE_HEAD $commit &&
 
 	git config diff.renames false &&
 
@@ -328,7 +328,7 @@ diff-tree --cc --stat --summary main
 diff-tree -c --stat --summary side
 diff-tree --cc --stat --summary side
 diff-tree --cc --shortstat main
-diff-tree --cc --summary REVERSE
+diff-tree --cc --summary REVERSE_HEAD
 # improved by Timo's patch
 diff-tree --cc --patch-with-stat main
 # improved by Timo's patch
diff --git a/t/t4013/diff.diff-tree_--cc_--summary_REVERSE b/t/t4013/diff.diff-tree_--cc_--summary_REVERSE_HEAD
similarity index 73%
rename from t/t4013/diff.diff-tree_--cc_--summary_REVERSE
rename to t/t4013/diff.diff-tree_--cc_--summary_REVERSE_HEAD
index e208dd5682..0f86ae60db 100644
--- a/t/t4013/diff.diff-tree_--cc_--summary_REVERSE
+++ b/t/t4013/diff.diff-tree_--cc_--summary_REVERSE_HEAD
@@ -1,4 +1,4 @@
-$ git diff-tree --cc --summary REVERSE
+$ git diff-tree --cc --summary REVERSE_HEAD
 2562325a7ee916efb2481da93073b82cec801cbc
  create mode 100644 file1
  delete mode 100644 file2
diff --git a/t/t6302-for-each-ref-filter.sh b/t/t6302-for-each-ref-filter.sh
index 7f060d97bf..14f6d7e597 100755
--- a/t/t6302-for-each-ref-filter.sh
+++ b/t/t6302-for-each-ref-filter.sh
@@ -31,7 +31,7 @@ test_expect_success 'setup some history and refs' '
 	git update-ref refs/odd/spot main
 '
 
-test_expect_success '--include-root-refs pattern prints pseudorefs' '
+test_expect_success '--include-root-refs pattern prints root refs' '
 	cat >expect <<-\EOF &&
 	HEAD
 	ORIG_HEAD
@@ -52,7 +52,7 @@ test_expect_success '--include-root-refs pattern prints pseudorefs' '
 	test_cmp expect actual
 '
 
-test_expect_success '--include-root-refs pattern does not print special refs' '
+test_expect_success '--include-root-refs pattern does not print pseudorefs' '
 	test_when_finished "rm -rf repo" &&
 	git init repo &&
 	(
diff --git a/t/t9902-completion.sh b/t/t9902-completion.sh
index f6de74054a..1cb55f9301 100755
--- a/t/t9902-completion.sh
+++ b/t/t9902-completion.sh
@@ -2693,13 +2693,13 @@ test_expect_success 'checkout matches case insensitively with GIT_COMPLETION_IGN
 	)
 '
 
-test_expect_success 'checkout completes pseudo refs' '
+test_expect_success 'checkout completes root refs' '
 	test_completion "git checkout H" <<-\EOF
 	HEAD Z
 	EOF
 '
 
-test_expect_success 'checkout completes pseudo refs case insensitively with GIT_COMPLETION_IGNORE_CASE' '
+test_expect_success 'checkout completes root refs case insensitively with GIT_COMPLETION_IGNORE_CASE' '
 	(
 		GIT_COMPLETION_IGNORE_CASE=1 &&
 		test_completion "git checkout h" <<-\EOF
-- 
2.56.0

Re: [PATCH v2 0/5] root refs vs pseudorefs

Message details and related content
On Tue, 3 Nov 2026 at 14:36, Julia Evans wrote:
quoted
pseudoref
    A name that can be used like a ref to refer to an object, but that
    is not stored in the same way as refs. For example, pseudorefs can
    be passed to git-log(1) or git-diff(1), but they cannot be written
    by git-update-ref(1) or listed by git-for-each-ref(1).

    The following pseudorefs are known to Git:

    •   FETCH_HEAD is written by git-fetch(1) or 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.
I found "Each object ID is annotated with metadata  indicating where it was
fetched from and its fetch status." to be very hard to parse, and I had to
go look at .git/FETCH_HEAD myself to understand what it was talking about.

It might help to instead describe `FETCH_HEAD` with an example,
especially since we already aren't giving a lot of details.
Maybe something like this:

  Git has 2 special pseudorefs: `MERGE_HEAD` and `FETCH_HEAD`.
  Unlike references (which can only contain an object ID or the name of
  another reference), these have their own format:

  - `FETCH_HEAD` has a list of entries like this:
    `d4c5f27105ee2e44366d718dbc5db95bbe99c714 not-for-merge branch 'example' of https://example.com/x/y`
    with details about what was fetched by linkgit:git-fetch[1].
  - `MERGE_HEAD` has a list of commit IDs

  These are stored as files in the `.git` directory. You can pass a pseudoref
  to `git show`, `git log`, etc, and it will use the first ID in the list.
  Pseudorefs can't be written by git-update-ref(1) or listed by
  git-for-each-ref(1).
I agree that the FETCH_HEAD and MERGE_HEAD descriptions are difficult
to parse, but I don't think the rather cryptic FETCH_HEAD example
belongs in the glossary.

I've taken Patrick's and Junio's suggestions, moved up the point about
reading the first object ID, and rewrote the FETCH_HEAD and MERGE_HEAD
descriptions, ending up with this:

pseudoref
    A name that can be used like a ref to refer to an object, but that
    has additional semantics: it can record more than one object ID and
    may carry extra metadata. In contrast to root refs, pseudorefs are
    therefore not stored or handled by the ref backend.

    Pseudorefs can be passed to commands such as git-log(1) or git-
    diff(1), but cannot be written by git-update-ref(1) or listed by
    git-for-each-ref(1). When a pseudoref contains more than one object
    ID, using it to refer to an object yields the first one.

    The following pseudorefs are known to Git:

    •   FETCH_HEAD is written by git-fetch(1) or git-pull(1) to record
        the objects that have been fetched. Each object is annotated
        with the ref and repository it was fetched from and whether it
        is to be merged when pulling.

    •   MERGE_HEAD is written by git-merge(1) or git-pull(1) to record
        the commits being merged while a merge is in progress.

Re: [PATCH v3 1/5] doc: glossary: add root ref and clarify pseudorefs

Message details and related content
Andy Koppe [off-list ref] writes:
Reword the "pseudoref" entry so that it no longer calls them refs,
Very very good.  They are "pseudo" and are *not* "refs".
quoted hunk
 [[def_pseudoref]]pseudoref::
-	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 an
+	<<def_object,object>>, but that has additional semantics: it can
+	record more than one object ID and may carry extra metadata.
Good.
quoted hunk
+	... In
+	contrast to <<def_root_ref,root refs>>, pseudorefs are therefore
+	not stored or handled by the ref backend.
I am not sure if we need to contrast this with root refs, though.
Normal refs are also handled by the ref backend after all, but we do
not contrast pseudorefs with them, either.

Can we get rid of "In contrast to ..., " and start the sentence with
"Therefore, pseudorefs are not..."?
quoted hunk
+the hierarchy, in which case they must follow the naming rules for
+<<def_root_ref,root refs>>.
 +
 Different subhierarchies are used for different purposes. For example,
 the `refs/heads/` hierarchy is used to represent local branches whereas
-the `refs/tags/` hierarchy is used to represent local tags..
+the `refs/tags/` hierarchy is used to represent local tags.
 +
 See also "REFERENCES" in linkgit:gitdatamodel[7].
Do we want to mention remotes and notes hierarchies, I wonder.
If we do not mention remotes, calling things in refs/heads
specifically "local" branches is not very useful.
quoted hunk
+[[def_root_ref]]root ref::
+	A <<def_ref,ref>> located in the root of the ref hierarchy, outside
+	`refs/`. Root ref names must follow these rules:
++
+--
+ - The name consists of only upper-case characters or underscores.
+
+ - The name ends with "`_HEAD`" or is equal to "`HEAD`".
+--
++
+The following irregular root refs are exceptions to these rules. This
+list is exhaustive and shall not be extended in future:
++
+--
+ - `AUTO_MERGE`
+
+ - `BISECT_EXPECTED_REV`
+
+ - `NOTES_MERGE_PARTIAL`
+
+ - `NOTES_MERGE_REF`
+
+ - `MERGE_AUTOSTASH`
+--
Good.
+<<def_pseudoref,Pseudorefs>> are not considered root refs, even though
+they follow the naming rules.
Again, I am not sure what the value of saying this is.  It somehow
feels like elephants are not rhinos even though they are both gray.
Why should a confused reader expect it to be considered root refs?
We already said that pseudorefs are not even refs upfront.

Other than these "do not contrast pseudorefs with refs or root refs"
suggestions on two places, the patch looks ready to me.

Thanks.


This is a tangent but is very deeply related to how "root refs" are
handled by the code, so I'll mention it.  Random things outside refs/
are usable as if they are refs in the same code path that handles
the root refs.  E.g.,

    $ git update-ref out/mister refs/heads/master
    $ git for-each-ref ;# not shown just like root refs
    29f5d436870d37a2e36388e96e9175e0a5bf53e9 commit	refs/heads/master
    $ ls -l .git/out/mister
    -rw------- 1 gitster gitster 41 Nov  6 14:01 .git/refo/heads/mister
    $ git show --oneline -s out/mister
    eddc71c initial

We probably should tighten the system to forbid them, not just from
creation side by tightening update-ref but from blind application of
the first element of rev_parse_rules[].

In other words, things "outside `refs/`" that are not at the root
level should not be treated by the code as if they were.

As solving this (or leaving it unsolved) has no impact on the status
of this patch we are discussing, I'll just leave #leftoverbits mark
here.

Re: [PATCH v3 2/5] doc: glossary: update per-worktree refs

Message details and related content
Andy Koppe [off-list ref] writes:
quoted hunk
diff --git a/Documentation/glossary-content.adoc b/Documentation/glossary-content.adoc
index 668f309a6f..c7ab1c981f 100644
--- a/Documentation/glossary-content.adoc
+++ b/Documentation/glossary-content.adoc
@@ -494,10 +494,23 @@ exclude;;
 	interface than the <<def_plumbing,plumbing>>.
 
 [[def_per_worktree_ref]]per-worktree ref::
-	Refs that are per-<<def_worktree,worktree>>, rather than
-	global.  This is presently only <<def_HEAD,HEAD>> and any refs
-	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>>,
+	for example <<def_HEAD,HEAD>> and `ORIG_HEAD`, are per-worktree, as are
+	refs with the following prefixes:
++
+--
+ - `refs/bisect/`
+
+ - `refs/rewritten/`
+
+ - `refs/worktree/`
+--
Very nice.  Do we want to mention, like the root refs whose names do
not end with HEAD, that we have no plan to add any new ones (in
other words, new ones are meant to be created under refs/worktree/)?
+<<def_pseudoref,Pseudorefs>> are also specific to each worktree.
Prefix the last sentence with "Even though they are not refs, ",
perhaps?

Re: [PATCH v3 3/5] doc: stop calling root refs pseudorefs

Message details and related content
Andy Koppe [off-list ref] writes:
Several manual pages still use "pseudoref" in its old sense of any ref
outside of "refs/", which the glossary now calls a root ref. Pseudorefs
have since been restricted to FETCH_HEAD and MERGE_HEAD, which are not
considered root refs.

Replace "pseudoref" with "root ref" in those pages, giving HEAD and
ORIG_HEAD as examples. In git-worktree(1), say that both root refs and
pseudorefs are per-worktree, and drop the description of them as being
directly under $GIT_DIR, because that does not apply to the reftable
backend.
Thanks.  Clarification of this kind is very very much appreciated.
quoted hunk
diff --git a/Documentation/config/core.adoc b/Documentation/config/core.adoc
index bdc74d291c..8f8fe36a58 100644
--- a/Documentation/config/core.adoc
+++ b/Documentation/config/core.adoc
@@ -376,7 +376,7 @@ core.logAllRefUpdates::
 	`refs/heads/`), remote refs (i.e. under `refs/remotes/`),
 	note refs (i.e. under `refs/notes/`), and the symbolic ref `HEAD`.
 	If it is set to `always`, then a missing reflog is automatically
-	created for any ref under `refs/`.
+	created for any ref, including root refs such as `ORIG_HEAD`.
We are witnessing a grave mistake getting corrected here.  Nice.
quoted hunk
 `--include-root-refs`::
-	List root refs (`HEAD` and pseudorefs) apart from regular refs.
+	Also list root refs, such as `HEAD` or `ORIG_HEAD`.
Good.
quoted hunk
diff --git a/Documentation/git-ls-remote.adoc b/Documentation/git-ls-remote.adoc
index d71c4ab3e2..7ce7df3149 100644
--- a/Documentation/git-ls-remote.adoc
+++ b/Documentation/git-ls-remote.adoc
@@ -35,7 +35,7 @@ OPTIONS
 	with other git subcommands.
 
 --refs::
-	Do not show peeled tags or pseudorefs like `HEAD` in the output.
+	Do not show peeled tags or refs outside `refs/`.
Hmph, is there a reason for saying "refs outside `refs/`" (as
opposed to "root refs") here?

I _think_ (correct me if I am not remembering right) by default
ls-remote shows everything under refs/ (with peeled tags) and HEAD.
It is not even "normal refs including root refs", i.e. ORIG_HEAD is
not shown.  With "--refs", of course ORIG_HEAD is still not shown,
but it stops HEAD from getting shown.  So "do not show root refs" or
"do not show refs outside refs/" are equally slightly broader than
needed (meaning: with or without this option restricting further,
any root ref other than HEAD, like ORIG_HEAD, are not shown anyway).

Do we even say what are normally shown in this manual page?  Perhaps
we would want to update the description to say something like

    DESCRIPTION
    -----------
    Displays references available in a remote repository along with
    the associated commit IDs.  By default, all normal refs under
    `refs/` and `HEAD` are shown.  For each tag object, the tag
    itself and the object it tags (colloquially called `peeled`) are
    shown.

upfront, before going to the OPTIONS section to tell the users how
to tweak that default?
quoted hunk
@@ -118,7 +118,7 @@ line shows the name of the object the tag points at.
 EXAMPLES
 --------
 
-* List all references (including symbolics and pseudorefs), peeling
+* List all references (including `HEAD` and symbolic refs), peeling
   tags:
Here, mentioning symbolic refs as if they are anything special is
misleading, no?  Symbolic or non-symbolic, refs are refs.

On the other hand, "all references" should logically include
(normal) refs under `refs/` hierarchy plus "root refs", so "List all
references" is technically incorrect.  Perhaps give it a bit more
correction, e.g.,

    List all refs under `refs/` and `HEAD`, peeling tags.

which would make it in line with the updated DESCRIPTION suggested
above.
quoted hunk
diff --git a/Documentation/git-update-ref.adoc b/Documentation/git-update-ref.adoc
index 37a5019a8b..c775334230 100644
--- a/Documentation/git-update-ref.adoc
+++ b/Documentation/git-update-ref.adoc
@@ -163,13 +163,12 @@ still see a subset of the modifications.
 
 LOGGING UPDATES
 ---------------
-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.  Log lines are
-formatted as:
+If config parameter "core.logAllRefUpdates" is true and the ref is
+HEAD or one under "refs/heads/", "refs/remotes/" or "refs/notes/", or
+if 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.  Log lines are formatted as:
This mention of files and lines may be obsolete in the world with
reftable, but I am perfectly fine to leave the correction on that
front to another series, and concentrate only on "what is a ref?
what is a root ref?  and what is a pseudoref?" clarification and
nothing else.  And for that purpose, the updated text is a perfect
fit.
quoted hunk
diff --git a/Documentation/git-worktree.adoc b/Documentation/git-worktree.adoc
...
+In general, all root refs and pseudorefs are per-worktree and all refs
+starting with `refs/` are shared, except for refs with the following prefixes:
+
+ - `refs/bisect/`
+
+ - `refs/rewritten/`
+
+ - `refs/worktree/`
Good; the same comment on "are these frozen?" applies.
quoted hunk
diff --git a/Documentation/gitdatamodel.adoc b/Documentation/gitdatamodel.adoc
index 8d9be02036..2e44cc5003 100644
--- a/Documentation/gitdatamodel.adoc
+++ b/Documentation/gitdatamodel.adoc
@@ -211,15 +211,28 @@ 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::
Nice attention to the detail.
quoted hunk
     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`.
Good to get rid of this and adding a separate entry for root refs.
quoted hunk
+
+[[root-refs]]
+root refs::
+    Git may create references other than `HEAD` at the root of the hierarchy,
+    for example `ORIG_HEAD` or `REBASE_HEAD`.
Is this a good place to talk about their naming conventions?  At
least we want to have a reference to where we talk about it, I
think, just like you did one for pseudorefs below.
quoted hunk
+[[pseudorefs]]
+pseudorefs::
+    linkgit:git-fetch[1], linkgit:git-merge[1] and linkgit:git-pull[1]
+    create the so-called pseudorefs `FETCH_HEAD` and `MERGE_HEAD`.
+    They are not references as such, because they are not stored by the
+    ref backend, but they can nevertheless be used to refer to objects in
+    commands such as linkgit:git-log[1] and linkgit:git-diff[1].
+
+See linkgit:gitglossary[7] for further details on root refs and
+pseudorefs.
Great.  I do not see a need for so-called here.  As technical term,
pseudorefs, references, and root refs, all are officially sanctioned
words, and there is nothing so-called about them.
quoted hunk
diff --git a/Documentation/rev-list-options.adoc b/Documentation/rev-list-options.adoc
index bd9f345690..822604278a 100644
--- a/Documentation/rev-list-options.adoc
+++ b/Documentation/rev-list-options.adoc
@@ -350,8 +350,8 @@ Under `--pretty=reference`, this information will not be shown at all.
 
 `--merge`::
 	Show commits touching conflicted paths in the range `HEAD...<other>`,
-	where `<other>` is the first existing pseudoref in `MERGE_HEAD`,
-	`CHERRY_PICK_HEAD`, `REVERT_HEAD` or `REBASE_HEAD`. Only works
+	where `<other>` is the first of `MERGE_HEAD`, `CHERRY_PICK_HEAD`,
+	`REVERT_HEAD` or `REBASE_HEAD` that exists. Only works
Phrasing "existing pseudoref" in the original was wrong, but the
updated text feels a bit incomplete.  "... where <other> is the
first ref among X, Y, ... Z that exist" or something like that?

Overall, very welcome clarificaiton.

Re: [PATCH v3 4/5] refs: stop calling root refs pseudorefs

Message details and related content
Andy Koppe [off-list ref] writes:
quoted hunk
diff --git a/builtin/for-each-ref.c b/builtin/for-each-ref.c
index 4a2fc421db..6ff32e919c 100644
--- a/builtin/for-each-ref.c
+++ b/builtin/for-each-ref.c
@@ -48,7 +48,7 @@ int for_each_ref_core(int argc, const char **argv, const char *prefix, struct re
 		OPT_NO_CONTAINS(&filter.no_commit, N_("print only refs which don't contain the commit")),
 		OPT_BOOL(0, "ignore-case", &icase, N_("sorting and filtering are case insensitive")),
 		OPT_BOOL(0, "stdin", &from_stdin, N_("read reference patterns from stdin")),
-		OPT_BOOL(0, "include-root-refs", &include_root_refs, N_("also include HEAD ref and pseudorefs")),
+		OPT_BOOL(0, "include-root-refs", &include_root_refs, N_("also include root refs")),
 		OPT_END(),
Correct.  Not just HEAD, but all root refs are included, e.g., ORIG_HEAD.
-	die(_("--merge requires one of the pseudorefs MERGE_HEAD, CHERRY_PICK_HEAD, REVERT_HEAD or REBASE_HEAD"));
+	die(_("--merge requires one of MERGE_HEAD, CHERRY_PICK_HEAD, REVERT_HEAD or REBASE_HEAD"));
If we do not want to think deeply only on this one, just stop after
replacing pseudoref with root ref, but do not drop pseudoref and be
content.  Otherwise you would end up confusing users this way:

    let's see the --merge option I read recently about.

        $ git log --merge
        fatal: --merge requires one of MERGE_HEAD, ...

    oh, do I have to give one of these?

        $ git log --merge MERGE_HEAD
        fatal: ambiguous argument 'MERGE_HEAD': unknown revision or ...

    puzzled. why doesn't it work?

It might work to say "one of root refs to exist (e.g., MERGE_HEAD,
...)", or give it a more operational message, like this:

    --merge works only in the middle of a merge, rebase, etc.

THanks.
Keyboard shortcuts
hback out one level
jnext message in thread / next series-diff position
kprevious message in thread / previous series-diff position
ldrill in
Escclose help / fold thread tree
?toggle this help