1{
2 config,
3 lib,
4 stdenvNoCC,
5 writeText,
6 git,
7 git-lfs,
8 cacert,
9}:
10
11let
12 urlToName =
13 {
14 url,
15 rev,
16 append,
17 }:
18 let
19 shortRev = lib.sources.shortRev rev;
20 appendShort = lib.optionalString ((builtins.match "[a-f0-9]*" rev) != null) "-${shortRev}";
21 in
22 "${lib.sources.urlToName url}${if append == "" then appendShort else append}";
23in
24
25lib.makeOverridable (
26 lib.fetchers.withNormalizedHash { } (
27 # NOTE Please document parameter additions or changes in
28 # ../../../doc/build-helpers/fetchers.chapter.md
29 {
30 url,
31 tag ? null,
32 rev ? null,
33 name ? urlToName {
34 inherit url;
35 rev = lib.revOrTag rev tag;
36 # when rootDir is specified, avoid invalidating the result when rev changes
37 append = if rootDir != "" then "-${lib.strings.sanitizeDerivationName rootDir}" else "";
38 },
39 leaveDotGit ? deepClone || fetchTags,
40 outputHash ? lib.fakeHash,
41 outputHashAlgo ? null,
42 fetchSubmodules ? true,
43 deepClone ? false,
44 branchName ? null,
45 sparseCheckout ? lib.optional (rootDir != "") rootDir,
46 nonConeMode ? rootDir != "",
47 nativeBuildInputs ? [ ],
48 # Shell code executed before the file has been fetched. This, in
49 # particular, can do things like set NIX_PREFETCH_GIT_CHECKOUT_HOOK to
50 # run operations between the checkout completing and deleting the .git
51 # directory.
52 preFetch ? "",
53 # Shell code executed after the file has been fetched
54 # successfully. This can do things like check or transform the file.
55 postFetch ? "",
56 preferLocalBuild ? true,
57 fetchLFS ? false,
58 # Shell code to build a netrc file for BASIC auth
59 netrcPhase ? null,
60 # Impure env vars (https://nixos.org/nix/manual/#sec-advanced-attributes)
61 # needed for netrcPhase
62 netrcImpureEnvVars ? [ ],
63 meta ? { },
64 allowedRequisites ? null,
65 # fetch all tags after tree (useful for git describe)
66 fetchTags ? false,
67 # make this subdirectory the root of the result
68 rootDir ? "",
69 # GIT_CONFIG_GLOBAL (as a file)
70 gitConfigFile ? config.gitConfigFile,
71 }:
72
73 /*
74 NOTE:
75 fetchgit has one problem: git fetch only works for refs.
76 This is because fetching arbitrary (maybe dangling) commits creates garbage collection risks
77 and checking whether a commit belongs to a ref is expensive. This may
78 change in the future when some caching is added to git (?)
79 Usually refs are either tags (refs/tags/*) or branches (refs/heads/*)
80 Cloning branches will make the hash check fail when there is an update.
81 But not all patches we want can be accessed by tags.
82
83 The workaround is getting the last n commits so that it's likely that they
84 still contain the hash we want.
85
86 for now : increase depth iteratively (TODO)
87
88 real fix: ask git folks to add a
89 git fetch $HASH contained in $BRANCH
90 facility because checking that $HASH is contained in $BRANCH is less
91 expensive than fetching --depth $N.
92 Even if git folks implemented this feature soon it may take years until
93 server admins start using the new version?
94 */
95
96 assert nonConeMode -> (sparseCheckout != [ ]);
97 assert fetchTags -> leaveDotGit;
98 assert rootDir != "" -> !leaveDotGit;
99
100 let
101 revWithTag =
102 let
103 warningMsg = "fetchgit requires one of either `rev` or `tag` to be provided (not both).";
104 otherIsNull = other: lib.assertMsg (other == null) warningMsg;
105 in
106 if tag != null then
107 assert (otherIsNull rev);
108 "refs/tags/${tag}"
109 else if rev != null then
110 assert (otherIsNull tag);
111 rev
112 else
113 # FIXME fetching HEAD if no rev or tag is provided is problematic at best
114 "HEAD";
115 in
116
117 if builtins.isString sparseCheckout then
118 # Changed to throw on 2023-06-04
119 throw
120 "Please provide directories/patterns for sparse checkout as a list of strings. Passing a (multi-line) string is not supported any more."
121 else
122 stdenvNoCC.mkDerivation {
123 inherit name;
124
125 builder = ./builder.sh;
126 fetcher = ./nix-prefetch-git;
127
128 nativeBuildInputs = [
129 git
130 cacert
131 ]
132 ++ lib.optionals fetchLFS [ git-lfs ]
133 ++ nativeBuildInputs;
134
135 inherit outputHash outputHashAlgo;
136 outputHashMode = "recursive";
137
138 # git-sparse-checkout(1) says:
139 # > When the --stdin option is provided, the directories or patterns are read
140 # > from standard in as a newline-delimited list instead of from the arguments.
141 sparseCheckout = builtins.concatStringsSep "\n" sparseCheckout;
142
143 inherit
144 url
145 leaveDotGit
146 fetchLFS
147 fetchSubmodules
148 deepClone
149 branchName
150 nonConeMode
151 preFetch
152 postFetch
153 fetchTags
154 rootDir
155 gitConfigFile
156 ;
157 rev = revWithTag;
158
159 postHook =
160 if netrcPhase == null then
161 null
162 else
163 ''
164 ${netrcPhase}
165 # required that git uses the netrc file
166 mv {,.}netrc
167 export NETRC=$PWD/.netrc
168 export HOME=$PWD
169 '';
170
171 impureEnvVars =
172 lib.fetchers.proxyImpureEnvVars
173 ++ netrcImpureEnvVars
174 ++ [
175 "GIT_PROXY_COMMAND"
176 "NIX_GIT_SSL_CAINFO"
177 "SOCKS_SERVER"
178
179 # This is a parameter intended to be set by setup hooks or preFetch
180 # scripts that want per-URL control over HTTP proxies used by Git
181 # (if per-URL control isn't needed, `http_proxy` etc. will
182 # suffice). It must be a whitespace-separated (with backslash as an
183 # escape character) list of pairs like this:
184 #
185 # http://domain1/path1 proxy1 https://domain2/path2 proxy2
186 #
187 # where the URLs are as documented in the `git-config` manual page
188 # under `http.<url>.*`, and the proxies are as documented on the
189 # same page under `http.proxy`.
190 "FETCHGIT_HTTP_PROXIES"
191 ];
192
193 inherit preferLocalBuild meta allowedRequisites;
194
195 passthru = {
196 gitRepoUrl = url;
197 inherit tag;
198 };
199 }
200 )
201)