Update: made a comment of recapping all the builtins.fetchGit
oddities in Nix issue #5128.
When builtins.fetchGit
is called with an attribute set, the following optional attributes can be specified:
rev
Thegit
revision to fetch. Defaults to the tip ofref
.
ref
Thegit
ref to look for the requested revision under. This
is often a branch or tag name. Defaults to HEAD.By default, the
ref
value is prefixed withrefs/heads/
.
As of Nix 2.3.0 Nix will not prefixrefs/heads/
ifref
starts withrefs/
.
I think my understanding of Git revisions and references is solid, and these descriptions in the Nix manual seem to be misleading - and even mutually exclusive in certain cases even. However, I don’t know all of the Git functions / tools that Nix is calling underneath that may impose special semantics, so what contradictions I believe I found below may not be contraditictions at all.
All the examples below are carried out on the toraritte/my-project repo that looks like this:
-BRANCH- -COMMIT MESSAGE--- -SHORT- -----------LONG------------------------
(topic) smarties cereal 5f45e9c 5f45e9c854941c72deb9d36fb3e95e4feb4d698f
Say, I hate stress 6692c9c 6692c9c6e231b1dfd5594dd59b32001b70060f19
(main) More to say c277976 c277976fce0b2b32b954a66d4345730b5b08f1db
Add sentence abb0819 abb08192ed875ef73fa66029994aa2f6700befd0
init e67cb07 e67cb07f9ddb0ecd0f88fcf36093d8d8bf928b75
Figure 1: The table is a combination of the outputs of git log --pretty=oneline
and git v --color=always --all
-
rev
: Thegit
revision to fetch.In contrast to the Git revision syntax,
builtins.fetchGit
’srev
attribute only accepts “the full SHA-1 object name (40-byte hexadecimal string)”; it doesn’t even accept an abbreviated SHA-1 hash.Example 1
nix-repl> builtins.fetchGit { url = "https://github.com/toraritte/my-project"; rev = "HEAD~2"; } error: hash 'HEAD~2' has wrong length for hash type 'sha1'
Example 2
nix-repl> builtins.fetchGit { url = "https://github.com/toraritte/my-project"; rev = "5f45e9c"; } error: hash '5f45e9c' has wrong length for hash type 'sha1'
Example 3
nix-repl> builtins.fetchGit { url = "https://github.com/toraritte/my-project"; rev = "5f45e9c854941c72deb9d36fb3e95e4feb4d698f"; } { lastModified = 1658846311; lastModifiedDate = "20220726143831"; narHash = "sha256-Yph6e...eoDAh/W6xaG+j5oFAui80c1FMYaGPTY="; outPath = "/nix/store/w...xrfyh1yj60v4phaf49ccyjd0-source"; rev = "5f45e9c854941c72deb9d36fb3e95e4feb4d698f"; revCount = 5; shortRev = "5f45e9c"; submodules = false; }
-
rev
: Defaults to the tip ofref
.A Git reference is an alias for the SHA-1 hash of a particular singular Git object, therefore “tip of
ref
” is meaningless. If theref
attribute’s value is a branch reference1 (e.g.,/refs/heads/main
), that will still only resolve to a single commit.As for
builtins.fetchGit
, from what I’ve seen innix repl
,-
when the
rev
attribute is not provided, it defaults toref
.
Example 4nix-repl> builtins.fetchGit { url = "https://github.com/toraritte/my-project"; ref = "main"; } { lastModified = 1658846059; lastModifiedDate = "20220726143419"; narHash = "sha256-FQUE8ek9uoy...LH+yv4S4="; outPath = "/nix/store/3ra7y3v...8r2d5p7k4irmiwrp-source"; rev = "c277976fce0b2b32b954a66d4345730b5b08f1db"; revCount = 3; shortRev = "c277976"; submodules = false; } $ git show-ref refs/heads/main c277976fce0b2b32b954a66d4345730b5b08f1db refs/heads/main
-
Otherwise, if
rev
(i.e., a specific commit hash) is present, thenref
will simply be ignored.
Example 5nix-repl> builtins.fetchGit { url = "https://github.com/toraritte/my-project"; ref = "main"; _______ rev = "5f45e9c854941c72deb9d36fb3e95e4feb4d698f"; ^^^^^^^ } { lastModified = 1658846311; lastModifiedDate = "20220726143831"; narHash = "sha256-Yph6eC...Ah/W6xaG+j5oFAui80c1FMYaGPTY="; outPath = "/nix/store/wa...yj60v4phaf49ccyjd0-source"; _______ rev = "5f45e9c854941c72deb9d36fb3e95e4feb4d698f"; ^^^^^^^ revCount = 5; shortRev = "5f45e9c"; submodules = false; } $ git show-ref refs/heads/main _______ c277976fce0b2b32b954a66d4345730b5b08f1db refs/heads/main ^^^^^^^
-
-
ref
: The git ref to look for the requested revision under. This is often a branch or tag name.I presume the first sentence states that the povided
rev
attribute value (i.e., commit) should be a parent ofref
, and along that line, the second one implies that if if theref
is a branch reference thenrev
needs to belong to that branch (i.e,; the commit hash inrev
has to be a parent of that branch’s HEAD).None of that is honored though: in example 5 above,
ref
refers to themain
branch, andrev
is"5f45e9c854941c72deb9d36fb3e95e4feb4d698f"
- the HEAD of thetopic
branch, which commit is not even part ofmain
:* 5f45e9c (HEAD -> topic, origin/topic) smarties cereal * ....... Say, I hate stress * c277976 (tag: miez, origin/main, main) More to say * ....... Add sentence * ....... init
-
ref
: Defaults to HEAD.Have nothing on this one.2
[2]: Was only able to find where ths is done for GitHub/Gitlab/SourceHut in
github.cc
(see), but no joy forgit.cc
…
7.5.11 nix flake
in the Nix manual also states the same with an added explanation/justification (emphasis mine):
The
rev
attribute must denote a commit that exists in the branch or tag specified by theref
attribute, since Nix doesn’t do a full clone of the remote repository by default (and the Git protocol doesn’t allow fetching arev
without a knownref
). The default is the commit currently pointed to byref
.
-
Does Nix flakes use the same C++ functions to fetch as
builtins.fetchGit
? If yes then I don’t think that restriction is honored. -
Which Git protocol is mentioned? There are revisions that don’t need an anchor (i.e., a commit hash or a Git reference), such as
:/<text>
and:\[<n>:\]<path>