git/list[1] front-page[2] threads[3] people[4] search[5] about
 

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

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

Previous: Andy KoppeNext: Andy Koppe
Message 20 of 34 in “root refs vs pseudorefs”
  1. 0/5 root refs vs pseudorefsAndy Koppe, Oct 22, 2026
  2. 1/5 doc: glossary: add root ref and clarify pseudorefsAndy Koppe, Oct 22, 2026
  3. Junio C HamanoOct 22, 2026
  4. Patrick SteinhardtOct 23, 2026
  5. Andy KoppeNov 1, 2026
  6. 2/5 doc: glossary: update per-worktree refsAndy Koppe, Oct 22, 2026
  7. Patrick SteinhardtOct 23, 2026
  8. Andy KoppeNov 1, 2026
  9. 3/5 doc: stop calling root refs pseudorefsAndy Koppe, Oct 22, 2026
  10. Patrick SteinhardtOct 23, 2026
  11. Andy KoppeNov 1, 2026
  12. 4/5 refs: stop calling root refs pseudorefsAndy Koppe, Oct 22, 2026
  13. Patrick SteinhardtOct 23, 2026
  14. Andy KoppeNov 1, 2026
  15. 5/5 t: stop calling root refs pseudorefsAndy Koppe, Oct 22, 2026
  16. Junio C HamanoOct 22, 2026
  17. Julia EvansOct 23, 2026
  18. Andy KoppeNov 1, 2026
  19. 0/5 root refs vs pseudorefsAndy Koppe, Nov 1, 2026
  20. Julia EvansNov 2, 2026
  21. Andy KoppeNov 2, 2026
  22. Julia EvansNov 2, 2026
  23. Andy KoppeNov 2, 2026
  24. Julia EvansNov 3, 2026
  25. Junio C HamanoNov 2, 2026
  26. 1/5 doc: glossary: add root ref and clarify pseudorefsAndy Koppe, Nov 1, 2026
  27. Patrick SteinhardtNov 3, 2026
  28. Junio C HamanoNov 3, 2026
  29. 2/5 doc: glossary: update per-worktree refsAndy Koppe, Nov 1, 2026
  30. Patrick SteinhardtNov 3, 2026
  31. 3/5 doc: stop calling root refs pseudorefsAndy Koppe, Nov 1, 2026
  32. Patrick SteinhardtNov 3, 2026
  33. 4/5 refs: stop calling root refs pseudorefsAndy Koppe, Nov 1, 2026
  34. 5/5 t: stop calling root refs pseudorefsAndy Koppe, Nov 1, 2026

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.