aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorKjetil Orbekk <kj@orbekk.com>2022-01-29 14:22:55 -0500
committerKjetil Orbekk <kj@orbekk.com>2022-04-29 18:46:21 -0400
commit1203e489263fe867d0a1ae3fd9270f40c8a1c24e (patch)
tree8619e0d20750be438e3153ff5bcb034c7e41e951 /src
parentde54e1cd3fce6eb456048b6a346a0be2b88660ae (diff)
Store cached head in cached git repo
The previous head caching implementation stored two paths in the local cache; one for the cached git repo and another textfile containing the resolved HEAD ref. This commit instead stores the resolved HEAD by setting the HEAD ref in the local cache appropriately.
Diffstat (limited to 'src')
-rw-r--r--src/libfetchers/git.cc89
1 files changed, 58 insertions, 31 deletions
diff --git a/src/libfetchers/git.cc b/src/libfetchers/git.cc
index 00a7b85d8..968cd642a 100644
--- a/src/libfetchers/git.cc
+++ b/src/libfetchers/git.cc
@@ -10,6 +10,7 @@
#include "fetch-settings.hh"
#include <regex>
+#include <string.h>
#include <sys/time.h>
#include <sys/wait.h>
@@ -37,7 +38,19 @@ bool isCacheFileWithinTtl(const time_t now, const struct stat& st) {
return st.st_mtime + settings.tarballTtl > now;
}
-Path getCachePath(std::string key) {
+bool touchCacheFile(const Path& path, const time_t& touch_time)
+{
+ struct timeval times[2];
+ times[0].tv_sec = touch_time;
+ times[0].tv_usec = 0;
+ times[1].tv_sec = touch_time;
+ times[1].tv_usec = 0;
+
+ return lutimes(path.c_str(), times) == 0;
+}
+
+Path getCachePath(std::string key)
+{
return getCacheDir() + "/nix/gitv3/" +
hashString(htSHA256, key).to_string(Base32, false);
}
@@ -80,32 +93,42 @@ std::optional<std::string> readHead(const Path & path)
return std::nullopt;
}
-std::optional<std::string> readHeadCached(std::string actualUrl)
+// Persist the HEAD ref from the remote repo in the local cached repo.
+bool storeCachedHead(const std::string& actualUrl, const std::string& headRef)
+{
+ Path cacheDir = getCachePath(actualUrl);
+ try {
+ runProgram("git", true, { "-C", cacheDir, "symbolic-ref", "--", "HEAD", headRef });
+ } catch (ExecError &e) {
+ if (!WIFEXITED(e.status)) throw;
+ return false;
+ }
+ /* No need to touch refs/HEAD, because `git symbolic-ref` updates the mtime. */
+ return true;
+}
+
+std::optional<std::string> readHeadCached(const std::string& actualUrl)
{
// Create a cache path to store the branch of the HEAD ref. Append something
// in front of the URL to prevent collision with the repository itself.
- Path cachePath = getCachePath("<ref>|" + actualUrl);
+ Path cacheDir = getCachePath(actualUrl);
+ Path headRefFile = cacheDir + "/HEAD";
+
time_t now = time(0);
struct stat st;
std::optional<std::string> cachedRef;
- if (stat(cachePath.c_str(), &st) == 0 &&
- // The file may be empty, because writeFile() is not atomic.
- st.st_size > 0) {
-
- // The cached ref is persisted unconditionally (see below).
- cachedRef = readFile(cachePath);
- if (isCacheFileWithinTtl(now, st)) {
+ if (stat(headRefFile.c_str(), &st) == 0) {
+ cachedRef = readHead(cacheDir);
+ if (cachedRef != std::nullopt &&
+ *cachedRef != gitInitialBranch &&
+ isCacheFileWithinTtl(now, st)) {
debug("using cached HEAD ref '%s' for repo '%s'", *cachedRef, actualUrl);
return cachedRef;
}
}
auto ref = readHead(actualUrl);
-
if (ref) {
- debug("storing cached HEAD ref '%s' for repo '%s' at '%s'", *ref, actualUrl, cachePath);
- createDirs(dirOf(cachePath));
- writeFile(cachePath, *ref);
return ref;
}
@@ -438,15 +461,6 @@ struct GitInputScheme : InputScheme
}
}
- if (!input.getRef()) {
- auto head = isLocal ? readHead(actualUrl) : readHeadCached(actualUrl);
- if (!head) {
- warn("could not read HEAD ref from repo at '%s', using 'master'", actualUrl);
- head = "master";
- }
- input.attrs.insert_or_assign("ref", *head);
- }
-
const Attrs unlockedAttrs({
{"type", cacheType},
{"name", name},
@@ -457,14 +471,30 @@ struct GitInputScheme : InputScheme
Path repoDir;
if (isLocal) {
+ if (!input.getRef()) {
+ auto head = readHead(actualUrl);
+ if (!head) {
+ warn("could not read HEAD ref from repo at '%s', using 'master'", actualUrl);
+ head = "master";
+ }
+ input.attrs.insert_or_assign("ref", *head);
+ }
if (!input.getRev())
input.attrs.insert_or_assign("rev",
Hash::parseAny(chomp(runProgram("git", true, { "-C", actualUrl, "--git-dir", gitDir, "rev-parse", *input.getRef() })), htSHA1).gitRev());
repoDir = actualUrl;
-
} else {
+ const bool useHeadRef = !input.getRef();
+ if (useHeadRef) {
+ auto head = readHeadCached(actualUrl);
+ if (!head) {
+ warn("could not read HEAD ref from repo at '%s', using 'master'", actualUrl);
+ head = "master";
+ }
+ input.attrs.insert_or_assign("ref", *head);
+ }
if (auto res = getCache()->lookup(store, unlockedAttrs)) {
auto rev2 = Hash::parseAny(getStrAttr(res->first, "rev"), htSHA1);
@@ -538,13 +568,10 @@ struct GitInputScheme : InputScheme
warn("could not update local clone of Git repository '%s'; continuing with the most recent version", actualUrl);
}
- struct timeval times[2];
- times[0].tv_sec = now;
- times[0].tv_usec = 0;
- times[1].tv_sec = now;
- times[1].tv_usec = 0;
-
- utimes(localRefFile.c_str(), times);
+ if (!touchCacheFile(localRefFile, now))
+ warn("could not update mtime for file '%s': %s", localRefFile, strerror(errno));
+ if (useHeadRef && !storeCachedHead(actualUrl, *input.getRef()))
+ warn("could not update cached head '%s' for '%s'", *input.getRef(), actualUrl);
}
if (!input.getRev())