builtins.fetchgit: Support importing a working tree
For example, you can write src = fetchgit ./.; and if ./. refers to an unclean working tree, that tree will be copied to the Nix store. This removes the need for "cleanSource".
This commit is contained in:
		
							parent
							
								
									197922ea4e
								
							
						
					
					
						commit
						72cd52c3cd
					
				
					 7 changed files with 54 additions and 17 deletions
				
			
		|  | @ -667,7 +667,7 @@ std::pair<bool, std::string> EvalState::resolveSearchPathElem(const SearchPathEl | ||||||
|         try { |         try { | ||||||
|             if (hasPrefix(elem.second, "git://") || hasSuffix(elem.second, ".git")) |             if (hasPrefix(elem.second, "git://") || hasSuffix(elem.second, ".git")) | ||||||
|                 // FIXME: support specifying revision/branch |                 // FIXME: support specifying revision/branch | ||||||
|                 res = { true, exportGit(store, elem.second, "master").storePath }; |                 res = { true, exportGit(store, elem.second).storePath }; | ||||||
|             else |             else | ||||||
|                 res = { true, getDownloader()->downloadCached(store, elem.second, true) }; |                 res = { true, getDownloader()->downloadCached(store, elem.second, true) }; | ||||||
|         } catch (DownloadError & e) { |         } catch (DownloadError & e) { | ||||||
|  |  | ||||||
|  | @ -16,9 +16,48 @@ using namespace std::string_literals; | ||||||
| namespace nix { | namespace nix { | ||||||
| 
 | 
 | ||||||
| GitInfo exportGit(ref<Store> store, const std::string & uri, | GitInfo exportGit(ref<Store> store, const std::string & uri, | ||||||
|     const std::string & ref, const std::string & rev, |     std::experimental::optional<std::string> ref, const std::string & rev, | ||||||
|     const std::string & name) |     const std::string & name) | ||||||
| { | { | ||||||
|  |     if (!ref && rev == "" && hasPrefix(uri, "/") && pathExists(uri + "/.git")) { | ||||||
|  | 
 | ||||||
|  |         bool clean = true; | ||||||
|  | 
 | ||||||
|  |         try { | ||||||
|  |             runProgram("git", true, { "-C", uri, "diff-index", "--quiet", "HEAD", "--" }); | ||||||
|  |         } catch (ExecError e) { | ||||||
|  |             if (!WIFEXITED(e.status) || WEXITSTATUS(e.status) != 1) throw; | ||||||
|  |             clean = false; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if (!clean) { | ||||||
|  | 
 | ||||||
|  |             /* This is an unclean working tree. So copy all tracked
 | ||||||
|  |                files. */ | ||||||
|  | 
 | ||||||
|  |             GitInfo gitInfo; | ||||||
|  |             gitInfo.rev = "0000000000000000000000000000000000000000"; | ||||||
|  |             gitInfo.shortRev = std::string(gitInfo.rev, 0, 7); | ||||||
|  | 
 | ||||||
|  |             auto files = tokenizeString<std::set<std::string>>( | ||||||
|  |                 runProgram("git", true, { "-C", uri, "ls-files", "-z" }), "\0"s); | ||||||
|  | 
 | ||||||
|  |             PathFilter filter = [&](const Path & p) -> bool { | ||||||
|  |                 assert(hasPrefix(p, uri)); | ||||||
|  |                 auto st = lstat(p); | ||||||
|  |                 if (S_ISDIR(st.st_mode)) return true; | ||||||
|  |                 std::string file(p, uri.size() + 1); | ||||||
|  |                 return files.count(file); | ||||||
|  |             }; | ||||||
|  | 
 | ||||||
|  |             gitInfo.storePath = store->addToStore("source", uri, true, htSHA256, filter); | ||||||
|  | 
 | ||||||
|  |             return gitInfo; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (!ref) ref = "master"; | ||||||
|  | 
 | ||||||
|     if (rev != "") { |     if (rev != "") { | ||||||
|         std::regex revRegex("^[0-9a-fA-F]{40}$"); |         std::regex revRegex("^[0-9a-fA-F]{40}$"); | ||||||
|         if (!std::regex_match(rev, revRegex)) |         if (!std::regex_match(rev, revRegex)) | ||||||
|  | @ -32,7 +71,7 @@ GitInfo exportGit(ref<Store> store, const std::string & uri, | ||||||
|         runProgram("git", true, { "init", "--bare", cacheDir }); |         runProgram("git", true, { "init", "--bare", cacheDir }); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     std::string localRef = hashString(htSHA256, fmt("%s-%s", uri, ref)).to_string(Base32, false); |     std::string localRef = hashString(htSHA256, fmt("%s-%s", uri, *ref)).to_string(Base32, false); | ||||||
| 
 | 
 | ||||||
|     Path localRefFile = cacheDir + "/refs/heads/" + localRef; |     Path localRefFile = cacheDir + "/refs/heads/" + localRef; | ||||||
| 
 | 
 | ||||||
|  | @ -47,7 +86,7 @@ GitInfo exportGit(ref<Store> store, const std::string & uri, | ||||||
| 
 | 
 | ||||||
|         // FIXME: git stderr messes up our progress indicator, so
 |         // FIXME: git stderr messes up our progress indicator, so
 | ||||||
|         // we're using --quiet for now. Should process its stderr.
 |         // we're using --quiet for now. Should process its stderr.
 | ||||||
|         runProgram("git", true, { "-C", cacheDir, "fetch", "--quiet", "--force", "--", uri, ref + ":" + localRef }); |         runProgram("git", true, { "-C", cacheDir, "fetch", "--quiet", "--force", "--", uri, *ref + ":" + localRef }); | ||||||
| 
 | 
 | ||||||
|         struct timeval times[2]; |         struct timeval times[2]; | ||||||
|         times[0].tv_sec = now; |         times[0].tv_sec = now; | ||||||
|  | @ -114,7 +153,7 @@ GitInfo exportGit(ref<Store> store, const std::string & uri, | ||||||
| static void prim_fetchGit(EvalState & state, const Pos & pos, Value * * args, Value & v) | static void prim_fetchGit(EvalState & state, const Pos & pos, Value * * args, Value & v) | ||||||
| { | { | ||||||
|     std::string url; |     std::string url; | ||||||
|     std::string ref = "master"; |     std::experimental::optional<std::string> ref; | ||||||
|     std::string rev; |     std::string rev; | ||||||
|     std::string name = "source"; |     std::string name = "source"; | ||||||
|     PathSet context; |     PathSet context; | ||||||
|  | @ -145,7 +184,7 @@ static void prim_fetchGit(EvalState & state, const Pos & pos, Value * * args, Va | ||||||
|     } else |     } else | ||||||
|         url = state.coerceToString(pos, *args[0], context, false, false); |         url = state.coerceToString(pos, *args[0], context, false, false); | ||||||
| 
 | 
 | ||||||
|     if (hasPrefix(url, "/")) url = "file://" + url; |     if (!isUri(url)) url = absPath(url); | ||||||
| 
 | 
 | ||||||
|     // FIXME: git externals probably can be used to bypass the URI
 |     // FIXME: git externals probably can be used to bypass the URI
 | ||||||
|     // whitelist. Ah well.
 |     // whitelist. Ah well.
 | ||||||
|  |  | ||||||
|  | @ -17,7 +17,8 @@ struct GitInfo | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| GitInfo exportGit(ref<Store> store, const std::string & uri, | GitInfo exportGit(ref<Store> store, const std::string & uri, | ||||||
|     const std::string & ref, const std::string & rev = "", |     std::experimental::optional<std::string> ref = {}, | ||||||
|  |     const std::string & rev = "", | ||||||
|     const std::string & name = ""); |     const std::string & name = ""); | ||||||
| 
 | 
 | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -29,7 +29,7 @@ const std::string narVersionMagic1 = "nix-archive-1"; | ||||||
| 
 | 
 | ||||||
| static string caseHackSuffix = "~nix~case~hack~"; | static string caseHackSuffix = "~nix~case~hack~"; | ||||||
| 
 | 
 | ||||||
| PathFilter defaultPathFilter; | PathFilter defaultPathFilter = [](const Path &) { return true; }; | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| static void dumpContents(const Path & path, size_t size, | static void dumpContents(const Path & path, size_t size, | ||||||
|  |  | ||||||
|  | @ -44,13 +44,6 @@ namespace nix { | ||||||
| 
 | 
 | ||||||
|      `+' denotes string concatenation. */ |      `+' denotes string concatenation. */ | ||||||
| 
 | 
 | ||||||
| struct PathFilter |  | ||||||
| { |  | ||||||
|     virtual ~PathFilter() { } |  | ||||||
|     virtual bool operator () (const Path & path) { return true; } |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| extern PathFilter defaultPathFilter; |  | ||||||
| 
 | 
 | ||||||
| void dumpPath(const Path & path, Sink & sink, | void dumpPath(const Path & path, Sink & sink, | ||||||
|     PathFilter & filter = defaultPathFilter); |     PathFilter & filter = defaultPathFilter); | ||||||
|  |  | ||||||
|  | @ -93,8 +93,6 @@ Hash hashFile(HashType ht, const Path & path); | ||||||
| 
 | 
 | ||||||
| /* Compute the hash of the given path.  The hash is defined as
 | /* Compute the hash of the given path.  The hash is defined as
 | ||||||
|    (essentially) hashString(ht, dumpPath(path)). */ |    (essentially) hashString(ht, dumpPath(path)). */ | ||||||
| struct PathFilter; |  | ||||||
| extern PathFilter defaultPathFilter; |  | ||||||
| typedef std::pair<Hash, unsigned long long> HashResult; | typedef std::pair<Hash, unsigned long long> HashResult; | ||||||
| HashResult hashPath(HashType ht, const Path & path, | HashResult hashPath(HashType ht, const Path & path, | ||||||
|     PathFilter & filter = defaultPathFilter); |     PathFilter & filter = defaultPathFilter); | ||||||
|  |  | ||||||
|  | @ -481,4 +481,10 @@ struct MaintainCount | ||||||
| std::pair<unsigned short, unsigned short> getWindowSize(); | std::pair<unsigned short, unsigned short> getWindowSize(); | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | /* Used in various places. */ | ||||||
|  | typedef std::function<bool(const Path & path)> PathFilter; | ||||||
|  | 
 | ||||||
|  | extern PathFilter defaultPathFilter; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| } | } | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue