From e38382895deef4c136b7425f480884d122f3c5ae Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 30 Oct 2017 11:49:03 +0100 Subject: [PATCH] builtins.fetchGit: Return an attrset with revision info This adds rev, shortRev and revCount attributes, equal to what Hydra provides. E.g. $ nix eval '(fetchGit https://github.com/NixOS/patchelf.git)' { outPath = "/nix/store/ghigrkw02l440g8vfxa9wj4c3zpfmw99-source"; rev = "29c085fd9d3fc972f75b3961905d6b4ecce7eb2b"; revCount = 303; shortRev = "29c085f"; } --- src/libexpr/parser.y | 2 +- src/libexpr/primops/fetchgit.cc | 58 ++++++++++++++++++++++++--------- src/libexpr/primops/fetchgit.hh | 12 +++++-- 3 files changed, 54 insertions(+), 18 deletions(-) diff --git a/src/libexpr/parser.y b/src/libexpr/parser.y index 669312bb7..eee315228 100644 --- a/src/libexpr/parser.y +++ b/src/libexpr/parser.y @@ -667,7 +667,7 @@ std::pair 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/fetchgit.cc b/src/libexpr/primops/fetchgit.cc index 18107f5cd..38bffd8db 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,11 +9,13 @@ #include +#include + using namespace std::string_literals; namespace nix { -Path exportGit(ref store, const std::string & uri, +GitInfo exportGit(ref store, const std::string & uri, const std::string & ref, const std::string & rev, const std::string & name) { @@ -56,36 +59,56 @@ Path exportGit(ref 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); - std::string storeLinkName = hashString(htSHA512, name + std::string("\0"s) + commitHash).to_string(Base32, false); + 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(name, tmpDir); + gitInfo.storePath = store->addToStore(name, tmpDir); - replaceSymlink(storePath, storeLink); + gitInfo.revCount = std::stoull(runProgram("git", true, { "-C", cacheDir, "rev-list", "--count", gitInfo.rev })); - return storePath; + nlohmann::json json; + json["storePath"] = gitInfo.storePath; + json["uri"] = uri; + json["name"] = name; + json["rev"] = gitInfo.rev; + json["revCount"] = gitInfo.revCount; + + writeFile(storeLink, json.dump()); + + return gitInfo; } static void prim_fetchGit(EvalState & state, const Pos & pos, Value * * args, Value & v) @@ -127,9 +150,14 @@ 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, name); + 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); diff --git a/src/libexpr/primops/fetchgit.hh b/src/libexpr/primops/fetchgit.hh index 4bf34b76f..056b6fcbe 100644 --- a/src/libexpr/primops/fetchgit.hh +++ b/src/libexpr/primops/fetchgit.hh @@ -2,13 +2,21 @@ #include -#include "ref.hh" +#include "util.hh" namespace nix { class Store; -Path exportGit(ref store, const std::string & uri, +struct GitInfo +{ + Path storePath; + std::string rev; + std::string shortRev; + uint64_t revCount = 0; +}; + +GitInfo exportGit(ref store, const std::string & uri, const std::string & ref, const std::string & rev = "", const std::string & name = "");