Skip to content

Commit 358c26f

Browse files
committed
fetchTree: shallow git fetching by default
Motivation: make git fetching more efficient for most repos by default
1 parent 60936f2 commit 358c26f

File tree

3 files changed

+78
-6
lines changed

3 files changed

+78
-6
lines changed
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
---
2+
synopsis: "`fetchTree` now fetches git repositories shallowly by default"
3+
prs: 10028
4+
---
5+
6+
`builtins.fetchTree` now clones git repositories shallowly by default, which reduces network traffic and disk usage significantly in many cases.
7+
8+
Previously, the default behavior was to clone the full history of a specific tag or branch (eg. `ref`) and only afterwards extract the files of one specific revision.
9+
10+
From now on, the `ref` and `allRefs` arguments will be ignored, except if shallow cloning is disabled by setting `shallow = false`.
11+
12+
The defaults for `builtins.fetchGit` remain unchanged. Here, shallow cloning has to be enabled manually by passing `shallow = true`.

src/libexpr/primops/fetchTree.cc

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,11 @@ static void fetchTree(
138138
attrs.emplace("exportIgnore", Explicit<bool>{true});
139139
}
140140

141+
// fetchTree should fetch git repos with shallow = true by default
142+
if (type == "git" && !params.isFetchGit && !attrs.contains("shallow")) {
143+
attrs.emplace("shallow", Explicit<bool>{true});
144+
}
145+
141146
if (!params.allowNameArgument)
142147
if (auto nameIter = attrs.find("name"); nameIter != attrs.end())
143148
state.error<EvalError>(
@@ -321,6 +326,8 @@ static RegisterPrimOp primop_fetchTree({
321326
322327
- `ref` (String, optional)
323328
329+
By default, this has no effect. This becomes relevant only once `shallow` cloning is disabled.
330+
324331
A [Git reference](https://git-scm.com/book/en/v2/Git-Internals-Git-References), such as a branch or tag name.
325332
326333
Default: `"HEAD"`
@@ -334,8 +341,9 @@ static RegisterPrimOp primop_fetchTree({
334341
- `shallow` (Bool, optional)
335342
336343
Make a shallow clone when fetching the Git tree.
344+
When this is enabled, the options `ref` and `allRefs` have no effect anymore.
337345
338-
Default: `false`
346+
Default: `true`
339347
340348
- `submodules` (Bool, optional)
341349
@@ -345,8 +353,11 @@ static RegisterPrimOp primop_fetchTree({
345353
346354
- `allRefs` (Bool, optional)
347355
348-
If set to `true`, always fetch the entire repository, even if the latest commit is still in the cache.
349-
Otherwise, only the latest commit is fetched if it is not already cached.
356+
By default, this has no effect. This becomes relevant only once `shallow` cloning is disabled.
357+
358+
Whether to fetch all references (eg. branches and tags) of the repository.
359+
With this argument being true, it's possible to load a `rev` from *any* `ref`.
360+
(Without setting this option, only `rev`s from the specified `ref` are supported).
350361
351362
Default: `false`
352363
@@ -600,6 +611,8 @@ static RegisterPrimOp primop_fetchGit({
600611
601612
[Git reference]: https://git-scm.com/book/en/v2/Git-Internals-Git-References
602613
614+
This option has no effect once `shallow` cloning is enabled.
615+
603616
By default, the `ref` value is prefixed with `refs/heads/`.
604617
As of 2.3.0, Nix will not prefix `refs/heads/` if `ref` starts with `refs/`.
605618
@@ -617,13 +630,15 @@ static RegisterPrimOp primop_fetchGit({
617630
- `shallow` (default: `false`)
618631
619632
Make a shallow clone when fetching the Git tree.
620-
633+
When this is enabled, the options `ref` and `allRefs` have no effect anymore.
621634
- `allRefs`
622635
623-
Whether to fetch all references of the repository.
624-
With this argument being true, it's possible to load a `rev` from *any* `ref`
636+
Whether to fetch all references (eg. branches and tags) of the repository.
637+
With this argument being true, it's possible to load a `rev` from *any* `ref`.
625638
(by default only `rev`s from the specified `ref` are supported).
626639
640+
This option has no effect once `shallow` cloning is enabled.
641+
627642
- `verifyCommit` (default: `true` if `publicKey` or `publicKeys` are provided, otherwise `false`)
628643
629644
Whether to check `rev` for a signature matching `publicKey` or `publicKeys`.
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
{
2+
description = "fetchTree fetches git repos shallowly by default";
3+
script = ''
4+
# purge nix git cache to make sure we start with a clean slate
5+
client.succeed("rm -rf ~/.cache/nix")
6+
7+
# add two commits to the repo:
8+
# - one with a large file (2M)
9+
# - another one making the file small again
10+
client.succeed(f"""
11+
dd if=/dev/urandom of={repo.path}/thailand bs=1M count=2 \
12+
&& {repo.git} add thailand \
13+
&& {repo.git} commit -m 'commit1' \
14+
&& echo 'ThaigerSprint' > {repo.path}/thailand \
15+
&& {repo.git} add thailand \
16+
&& {repo.git} commit -m 'commit2' \
17+
&& {repo.git} push origin main
18+
""")
19+
20+
# memoize the revision
21+
commit2_rev = client.succeed(f"""
22+
{repo.git} rev-parse HEAD
23+
""").strip()
24+
25+
# construct the fetcher call
26+
fetchGit_expr = f"""
27+
builtins.fetchTree {{
28+
type = "git";
29+
url = "{repo.remote}";
30+
rev = "{commit2_rev}";
31+
}}
32+
"""
33+
34+
# fetch the repo via nix
35+
fetched1 = client.succeed(f"""
36+
nix eval --impure --raw --expr '({fetchGit_expr}).outPath'
37+
""")
38+
39+
# check that the size of ~/.cache/nix is less than 1M
40+
cache_size = client.succeed("""
41+
du -s ~/.cache/nix
42+
""").strip().split()[0]
43+
assert int(cache_size) < 1024, f"cache size is {cache_size}K which is larger than 1M"
44+
'';
45+
}

0 commit comments

Comments
 (0)