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
Thegitrevision to fetch. Defaults to the tip ofref.
ref
Thegitref to look for the requested revision under. This
is often a branch or tag name. Defaults to HEAD.By default, the
refvalue 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: Thegitrevision to fetch.In contrast to the Git revision syntax,
builtins.fetchGit’srevattribute 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 therefattribute’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
revattribute 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, thenrefwill 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
revattribute value (i.e., commit) should be a parent ofref, and along that line, the second one implies that if if therefis a branch reference thenrevneeds to belong to that branch (i.e,; the commit hash inrevhas to be a parent of that branch’s HEAD).None of that is honored though: in example 5 above,
refrefers to themainbranch, andrevis"5f45e9c854941c72deb9d36fb3e95e4feb4d698f"- the HEAD of thetopicbranch, 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
revattribute must denote a commit that exists in the branch or tag specified by therefattribute, since Nix doesn’t do a full clone of the remote repository by default (and the Git protocol doesn’t allow fetching arevwithout 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>