about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--doc/manual/release-notes/rl-1.12.xml2
-rw-r--r--src/libexpr/eval.cc2
-rw-r--r--src/libexpr/parser.y2
-rw-r--r--src/libexpr/primops.cc10
-rw-r--r--src/libexpr/primops/fetchgit.cc92
-rw-r--r--src/libexpr/primops/fetchgit.hh15
-rw-r--r--src/libstore/download.cc4
-rw-r--r--src/libstore/globals.hh2
8 files changed, 88 insertions, 41 deletions
diff --git a/doc/manual/release-notes/rl-1.12.xml b/doc/manual/release-notes/rl-1.12.xml
index adfbaa157f22..d8fc2bcf89b1 100644
--- a/doc/manual/release-notes/rl-1.12.xml
+++ b/doc/manual/release-notes/rl-1.12.xml
@@ -184,7 +184,7 @@
   </listitem>
 
   <listitem>
-    <para><function>builtins.fetchgit</function>.
+    <para><function>builtins.fetchGit</function>.
     (38539b943a060d9cdfc24d6e5d997c0885b8aa2f)</para>
   </listitem>
 
diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc
index 78f6b0010523..548537b72ced 100644
--- a/src/libexpr/eval.cc
+++ b/src/libexpr/eval.cc
@@ -149,7 +149,7 @@ string showType(const Value & v)
     switch (v.type) {
         case tInt: return "an integer";
         case tBool: return "a boolean";
-        case tString: return "a string";
+        case tString: return v.string.context ? "a string with context" : "a string";
         case tPath: return "a path";
         case tNull: return "null";
         case tAttrs: return "a set";
diff --git a/src/libexpr/parser.y b/src/libexpr/parser.y
index 669312bb7cff..eee31522830f 100644
--- a/src/libexpr/parser.y
+++ b/src/libexpr/parser.y
@@ -667,7 +667,7 @@ std::pair<bool, std::string> EvalState::resolveSearchPathElem(const SearchPathEl
         try {
             if (hasPrefix(elem.second, "git://") || hasSuffix(elem.second, ".git"))
                 // FIXME: support specifying revision/branch
-                res = { true, exportGit(store, elem.second, "master") };
+                res = { true, exportGit(store, elem.second, "master").storePath };
             else
                 res = { true, getDownloader()->downloadCached(store, elem.second, true) };
         } catch (DownloadError & e) {
diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc
index 6a50c99345e4..22925ba4de19 100644
--- a/src/libexpr/primops.cc
+++ b/src/libexpr/primops.cc
@@ -1907,11 +1907,11 @@ static void prim_compareVersions(EvalState & state, const Pos & pos, Value * * a
 
 
 void fetch(EvalState & state, const Pos & pos, Value * * args, Value & v,
-    const string & who, bool unpack)
+    const string & who, bool unpack, const std::string & defaultName)
 {
     string url;
     Hash expectedHash;
-    string name;
+    string name = defaultName;
 
     state.forceValue(*args[0]);
 
@@ -1937,7 +1937,7 @@ void fetch(EvalState & state, const Pos & pos, Value * * args, Value & v,
     } else
         url = state.forceStringNoCtx(*args[0], pos);
 
-    if (state.restricted && !expectedHash)
+    if (state.restricted)
         throw Error(format("'%1%' is not allowed in restricted mode") % who);
 
     Path res = getDownloader()->downloadCached(state.store, url, unpack, name, expectedHash);
@@ -1947,13 +1947,13 @@ void fetch(EvalState & state, const Pos & pos, Value * * args, Value & v,
 
 static void prim_fetchurl(EvalState & state, const Pos & pos, Value * * args, Value & v)
 {
-    fetch(state, pos, args, v, "fetchurl", false);
+    fetch(state, pos, args, v, "fetchurl", false, "");
 }
 
 
 static void prim_fetchTarball(EvalState & state, const Pos & pos, Value * * args, Value & v)
 {
-    fetch(state, pos, args, v, "fetchTarball", true);
+    fetch(state, pos, args, v, "fetchTarball", true, "source");
 }
 
 
diff --git a/src/libexpr/primops/fetchgit.cc b/src/libexpr/primops/fetchgit.cc
index 9db4adbc9da2..38bffd8dbdbc 100644
--- a/src/libexpr/primops/fetchgit.cc
+++ b/src/libexpr/primops/fetchgit.cc
@@ -1,3 +1,4 @@
+#include "fetchgit.hh"
 #include "primops.hh"
 #include "eval-inline.hh"
 #include "download.hh"
@@ -8,10 +9,15 @@
 
 #include <regex>
 
+#include <nlohmann/json.hpp>
+
+using namespace std::string_literals;
+
 namespace nix {
 
-Path exportGit(ref<Store> store, const std::string & uri,
-    const std::string & ref, const std::string & rev)
+GitInfo exportGit(ref<Store> store, const std::string & uri,
+    const std::string & ref, const std::string & rev,
+    const std::string & name)
 {
     if (rev != "") {
         std::regex revRegex("^[0-9a-fA-F]{40}$");
@@ -26,8 +32,6 @@ Path exportGit(ref<Store> store, const std::string & uri,
         runProgram("git", true, { "init", "--bare", cacheDir });
     }
 
-    //Activity act(*logger, lvlInfo, format("fetching Git repository '%s'") % uri);
-
     std::string localRef = hashString(htSHA256, fmt("%s-%s", uri, ref)).to_string(Base32, false);
 
     Path localRefFile = cacheDir + "/refs/heads/" + localRef;
@@ -39,7 +43,11 @@ Path exportGit(ref<Store> store, const std::string & uri,
     if (stat(localRefFile.c_str(), &st) != 0 ||
         st.st_mtime < now - settings.tarballTtl)
     {
-        runProgram("git", true, { "-C", cacheDir, "fetch", "--force", "--", uri, ref + ":" + localRef });
+        Activity act(*logger, lvlTalkative, actUnknown, fmt("fetching Git repository '%s'", uri));
+
+        // FIXME: git stderr messes up our progress indicator, so
+        // we're using --quiet for now. Should process its stderr.
+        runProgram("git", true, { "-C", cacheDir, "fetch", "--quiet", "--force", "--", uri, ref + ":" + localRef });
 
         struct timeval times[2];
         times[0].tv_sec = now;
@@ -51,46 +59,67 @@ Path exportGit(ref<Store> store, const std::string & uri,
     }
 
     // FIXME: check whether rev is an ancestor of ref.
-    std::string commitHash =
-        rev != "" ? rev : chomp(readFile(localRefFile));
+    GitInfo gitInfo;
+    gitInfo.rev = rev != "" ? rev : chomp(readFile(localRefFile));
+    gitInfo.shortRev = std::string(gitInfo.rev, 0, 7);
 
-    printTalkative("using revision %s of repo '%s'", uri, commitHash);
+    printTalkative("using revision %s of repo '%s'", uri, gitInfo.rev);
 
-    Path storeLink = cacheDir + "/" + commitHash + ".link";
+    std::string storeLinkName = hashString(htSHA512, name + std::string("\0"s) + gitInfo.rev).to_string(Base32, false);
+    Path storeLink = cacheDir + "/" + storeLinkName + ".link";
     PathLocks storeLinkLock({storeLink}, fmt("waiting for lock on '%1%'...", storeLink));
 
-    if (pathExists(storeLink)) {
-        auto storePath = readLink(storeLink);
-        store->addTempRoot(storePath);
-        if (store->isValidPath(storePath)) {
-            return storePath;
+    try {
+        // FIXME: doesn't handle empty lines
+        auto json = nlohmann::json::parse(readFile(storeLink));
+
+        assert(json["uri"] == uri && json["name"] == name && json["rev"] == gitInfo.rev);
+
+        gitInfo.storePath = json["storePath"];
+
+        if (store->isValidPath(gitInfo.storePath)) {
+            gitInfo.revCount = json["revCount"];
+            return gitInfo;
         }
+
+    } catch (SysError & e) {
+        if (e.errNo != ENOENT) throw;
     }
 
     // FIXME: should pipe this, or find some better way to extract a
     // revision.
-    auto tar = runProgram("git", true, { "-C", cacheDir, "archive", commitHash });
+    auto tar = runProgram("git", true, { "-C", cacheDir, "archive", gitInfo.rev });
 
     Path tmpDir = createTempDir();
     AutoDelete delTmpDir(tmpDir, true);
 
     runProgram("tar", true, { "x", "-C", tmpDir }, tar);
 
-    auto storePath = store->addToStore("git-export", tmpDir);
+    gitInfo.storePath = store->addToStore(name, tmpDir);
+
+    gitInfo.revCount = std::stoull(runProgram("git", true, { "-C", cacheDir, "rev-list", "--count", gitInfo.rev }));
+
+    nlohmann::json json;
+    json["storePath"] = gitInfo.storePath;
+    json["uri"] = uri;
+    json["name"] = name;
+    json["rev"] = gitInfo.rev;
+    json["revCount"] = gitInfo.revCount;
 
-    replaceSymlink(storePath, storeLink);
+    writeFile(storeLink, json.dump());
 
-    return storePath;
+    return gitInfo;
 }
 
-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)
 {
     // FIXME: cut&paste from fetch().
-    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 ref = "master";
     std::string rev;
+    std::string name = "source";
 
     state.forceValue(*args[0]);
 
@@ -99,18 +128,20 @@ static void prim_fetchgit(EvalState & state, const Pos & pos, Value * * args, Va
         state.forceAttrs(*args[0], pos);
 
         for (auto & attr : *args[0]->attrs) {
-            string name(attr.name);
-            if (name == "url") {
+            string n(attr.name);
+            if (n == "url") {
                 PathSet context;
                 url = state.coerceToString(*attr.pos, *attr.value, context, false, false);
                 if (hasPrefix(url, "/")) url = "file://" + url;
             }
-            else if (name == "ref")
+            else if (n == "ref")
                 ref = state.forceStringNoCtx(*attr.value, *attr.pos);
-            else if (name == "rev")
+            else if (n == "rev")
                 rev = state.forceStringNoCtx(*attr.value, *attr.pos);
+            else if (n == "name")
+                name = state.forceStringNoCtx(*attr.value, *attr.pos);
             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);
         }
 
         if (url.empty())
@@ -119,11 +150,16 @@ static void prim_fetchgit(EvalState & state, const Pos & pos, Value * * args, Va
     } else
         url = state.forceStringNoCtx(*args[0], pos);
 
-    Path storePath = exportGit(state.store, url, ref, rev);
+    auto gitInfo = exportGit(state.store, url, ref, rev, name);
 
-    mkString(v, storePath, PathSet({storePath}));
+    state.mkAttrs(v, 8);
+    mkString(*state.allocAttr(v, state.sOutPath), gitInfo.storePath, PathSet({gitInfo.storePath}));
+    mkString(*state.allocAttr(v, state.symbols.create("rev")), gitInfo.rev);
+    mkString(*state.allocAttr(v, state.symbols.create("shortRev")), gitInfo.shortRev);
+    mkInt(*state.allocAttr(v, state.symbols.create("revCount")), gitInfo.revCount);
+    v.attrs->sort();
 }
 
-static RegisterPrimOp r("__fetchgit", 1, prim_fetchgit);
+static RegisterPrimOp r("fetchGit", 1, prim_fetchGit);
 
 }
diff --git a/src/libexpr/primops/fetchgit.hh b/src/libexpr/primops/fetchgit.hh
index ff228f3b3c6a..056b6fcbe78d 100644
--- a/src/libexpr/primops/fetchgit.hh
+++ b/src/libexpr/primops/fetchgit.hh
@@ -2,13 +2,22 @@
 
 #include <string>
 
-#include "ref.hh"
+#include "util.hh"
 
 namespace nix {
 
 class Store;
 
-Path exportGit(ref<Store> store, const std::string & uri,
-    const std::string & ref, const std::string & rev = "");
+struct GitInfo
+{
+    Path storePath;
+    std::string rev;
+    std::string shortRev;
+    uint64_t revCount = 0;
+};
+
+GitInfo exportGit(ref<Store> store, const std::string & uri,
+    const std::string & ref, const std::string & rev = "",
+    const std::string & name = "");
 
 }
diff --git a/src/libstore/download.cc b/src/libstore/download.cc
index 608b8fd399b4..579a5e8c1b59 100644
--- a/src/libstore/download.cc
+++ b/src/libstore/download.cc
@@ -23,6 +23,8 @@
 #include <cmath>
 #include <random>
 
+using namespace std::string_literals;
+
 namespace nix {
 
 double getTime()
@@ -604,7 +606,7 @@ Path Downloader::downloadCached(ref<Store> store, const string & url_, bool unpa
     Path cacheDir = getCacheDir() + "/nix/tarballs";
     createDirs(cacheDir);
 
-    string urlHash = hashString(htSHA256, url).to_string(Base32, false);
+    string urlHash = hashString(htSHA256, name + std::string("\0"s) + url).to_string(Base32, false);
 
     Path dataFile = cacheDir + "/" + urlHash + ".info";
     Path fileLink = cacheDir + "/" + urlHash + "-file";
diff --git a/src/libstore/globals.hh b/src/libstore/globals.hh
index 880527322794..538273b546be 100644
--- a/src/libstore/globals.hh
+++ b/src/libstore/globals.hh
@@ -271,7 +271,7 @@ public:
         "Number of parallel HTTP connections.",
         {"binary-caches-parallel-connections"}};
 
-    Setting<bool> enableHttp2{this, true, "enable-http2",
+    Setting<bool> enableHttp2{this, true, "http2",
         "Whether to enable HTTP/2 support."};
 
     Setting<unsigned int> tarballTtl{this, 60 * 60, "tarball-ttl",