Skip to content
Snippets Groups Projects
Unverified Commit 7480f4f9 authored by Eelco Dolstra's avatar Eelco Dolstra
Browse files

builtins.fetchgit: Support specifying commit hashes

This adds an argument "rev" specififying the Git commit hash. The
existing argument "rev" is renamed to "ref". The default value for
"ref" is "master". When specifying a hash, it's necessary to specify a
ref since we're not cloning the entire repository but only fetching a
specific ref.

Example usage:

  builtins.fetchgit {
    url = https://github.com/NixOS/nixpkgs.git;
    ref = "release-16.03";
    rev = "c1c0484041ab6f9c6858c8ade80a8477c9ae4442";
  };
parent 9f64cb89
No related branches found
No related tags found
No related merge requests found
...@@ -6,13 +6,27 @@ ...@@ -6,13 +6,27 @@
#include <sys/time.h> #include <sys/time.h>
#include <regex>
namespace nix { namespace nix {
Path exportGit(ref<Store> store, const std::string & uri, const std::string & rev) Path exportGit(ref<Store> store, const std::string & uri,
const std::string & ref, const std::string & rev)
{ {
if (!isUri(uri)) if (!isUri(uri))
throw EvalError(format("‘%s’ is not a valid URI") % uri); throw EvalError(format("‘%s’ is not a valid URI") % uri);
if (rev != "") {
std::regex revRegex("^[0-9a-fA-F]{40}$");
if (!std::regex_match(rev, revRegex))
throw Error("invalid Git revision ‘%s’", rev);
}
// FIXME: too restrictive, but better safe than sorry.
std::regex refRegex("^[0-9a-zA-Z][0-9a-zA-Z.-]+$");
if (!std::regex_match(ref, refRegex))
throw Error("invalid Git ref ‘%s’", ref);
Path cacheDir = getCacheDir() + "/nix/git"; Path cacheDir = getCacheDir() + "/nix/git";
if (!pathExists(cacheDir)) { if (!pathExists(cacheDir)) {
...@@ -22,7 +36,7 @@ Path exportGit(ref<Store> store, const std::string & uri, const std::string & re ...@@ -22,7 +36,7 @@ Path exportGit(ref<Store> store, const std::string & uri, const std::string & re
//Activity act(*logger, lvlInfo, format("fetching Git repository ‘%s’") % uri); //Activity act(*logger, lvlInfo, format("fetching Git repository ‘%s’") % uri);
std::string localRef = hashString(htSHA256, fmt("%s-%s", uri, rev)).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;
...@@ -33,7 +47,7 @@ Path exportGit(ref<Store> store, const std::string & uri, const std::string & re ...@@ -33,7 +47,7 @@ Path exportGit(ref<Store> store, const std::string & uri, const std::string & re
if (stat(localRefFile.c_str(), &st) != 0 || if (stat(localRefFile.c_str(), &st) != 0 ||
st.st_mtime < now - settings.tarballTtl) st.st_mtime < now - settings.tarballTtl)
{ {
runProgram("git", true, { "-C", cacheDir, "fetch", "--force", uri, rev + ":" + localRef }); runProgram("git", true, { "-C", cacheDir, "fetch", "--force", uri, ref + ":" + localRef });
struct timeval times[2]; struct timeval times[2];
times[0].tv_sec = now; times[0].tv_sec = now;
...@@ -44,7 +58,9 @@ Path exportGit(ref<Store> store, const std::string & uri, const std::string & re ...@@ -44,7 +58,9 @@ Path exportGit(ref<Store> store, const std::string & uri, const std::string & re
utimes(localRefFile.c_str(), times); utimes(localRefFile.c_str(), times);
} }
std::string commitHash = chomp(readFile(localRefFile)); // FIXME: check whether rev is an ancestor of ref.
std::string commitHash =
rev != "" ? rev : chomp(readFile(localRefFile));
printTalkative("using revision %s of repo ‘%s’", uri, commitHash); printTalkative("using revision %s of repo ‘%s’", uri, commitHash);
...@@ -81,7 +97,8 @@ static void prim_fetchgit(EvalState & state, const Pos & pos, Value * * args, Va ...@@ -81,7 +97,8 @@ static void prim_fetchgit(EvalState & state, const Pos & pos, Value * * args, Va
if (state.restricted) throw Error("‘fetchgit’ is not allowed in restricted mode"); if (state.restricted) throw Error("‘fetchgit’ is not allowed in restricted mode");
std::string url; std::string url;
std::string rev = "master"; std::string ref = "master";
std::string rev;
state.forceValue(*args[0]); state.forceValue(*args[0]);
...@@ -95,7 +112,10 @@ static void prim_fetchgit(EvalState & state, const Pos & pos, Value * * args, Va ...@@ -95,7 +112,10 @@ static void prim_fetchgit(EvalState & state, const Pos & pos, Value * * args, Va
PathSet context; PathSet context;
url = state.coerceToString(*attr.pos, *attr.value, context, false, false); url = state.coerceToString(*attr.pos, *attr.value, context, false, false);
if (hasPrefix(url, "/")) url = "file://" + url; if (hasPrefix(url, "/")) url = "file://" + url;
} else if (name == "rev") }
else if (name == "ref")
ref = state.forceStringNoCtx(*attr.value, *attr.pos);
else if (name == "rev")
rev = state.forceStringNoCtx(*attr.value, *attr.pos); rev = state.forceStringNoCtx(*attr.value, *attr.pos);
else else
throw EvalError("unsupported argument ‘%s’ to ‘fetchgit’, at %s", attr.name, *attr.pos); throw EvalError("unsupported argument ‘%s’ to ‘fetchgit’, at %s", attr.name, *attr.pos);
...@@ -107,7 +127,7 @@ static void prim_fetchgit(EvalState & state, const Pos & pos, Value * * args, Va ...@@ -107,7 +127,7 @@ static void prim_fetchgit(EvalState & state, const Pos & pos, Value * * args, Va
} else } else
url = state.forceStringNoCtx(*args[0], pos); url = state.forceStringNoCtx(*args[0], pos);
Path storePath = exportGit(state.store, url, rev); Path storePath = exportGit(state.store, url, ref, rev);
mkString(v, storePath, PathSet({storePath})); mkString(v, storePath, PathSet({storePath}));
} }
......
...@@ -8,7 +8,7 @@ namespace nix { ...@@ -8,7 +8,7 @@ namespace nix {
class Store; class Store;
Path exportGit(ref<Store> store, Path exportGit(ref<Store> store, const std::string & uri,
const std::string & uri, const std::string & rev); const std::string & ref, const std::string & rev = "");
} }
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment