diff --git a/src/libfetchers-tests/git-utils.cc b/src/libfetchers-tests/git-utils.cc index 762e39ad6eae..cdd920e540b1 100644 --- a/src/libfetchers-tests/git-utils.cc +++ b/src/libfetchers-tests/git-utils.cc @@ -48,7 +48,7 @@ class GitUtilsTest : public ::testing::Test ref openRepo() { - return GitRepo::openRepo(tmpDir, true, false); + return GitRepo::openRepo(tmpDir, {.create = true}); } std::string getRepoName() const diff --git a/src/libfetchers/git-utils.cc b/src/libfetchers/git-utils.cc index fecceeffffe0..068b620adf6f 100644 --- a/src/libfetchers/git-utils.cc +++ b/src/libfetchers/git-utils.cc @@ -203,16 +203,19 @@ static git_packbuilder_progress PACKBUILDER_PROGRESS_CHECK_INTERRUPT = &packBuil } // extern "C" -static void initRepoAtomically(std::filesystem::path & path, bool bare) +static void initRepoAtomically(std::filesystem::path & path, GitRepo::Options options) { if (pathExists(path.string())) return; + if (!options.create) + throw Error("Git repository %s does not exist.", path); + std::filesystem::path tmpDir = createTempDir(path.parent_path()); AutoDelete delTmpDir(tmpDir, true); Repository tmpRepo; - if (git_repository_init(Setter(tmpRepo), tmpDir.string().c_str(), bare)) + if (git_repository_init(Setter(tmpRepo), tmpDir.string().c_str(), options.bare)) throw Error("creating Git repository %s: %s", path, git_error_last()->message); try { std::filesystem::rename(tmpDir, path); @@ -234,7 +237,7 @@ struct GitRepoImpl : GitRepo, std::enable_shared_from_this /** Location of the repository on disk. */ std::filesystem::path path; - bool bare; + Options options; /** * libgit2 repository. Note that new objects are not written to disk, @@ -255,18 +258,18 @@ struct GitRepoImpl : GitRepo, std::enable_shared_from_this */ git_odb_backend * packBackend = nullptr; - GitRepoImpl(std::filesystem::path _path, bool create, bool bare, bool packfilesOnly = false) + GitRepoImpl(std::filesystem::path _path, Options _options) : path(std::move(_path)) - , bare(bare) + , options(_options) { initLibGit2(); - initRepoAtomically(path, bare); + initRepoAtomically(path, options); if (git_repository_open(Setter(repo), path.string().c_str())) throw Error("opening Git repository %s: %s", path, git_error_last()->message); ObjectDb odb; - if (packfilesOnly) { + if (options.packfilesOnly) { /* Create a fresh object database because by default the repo also loose object backends. We are not using any of those for the tarball cache, but libgit2 still does a bunch of unnecessary @@ -295,7 +298,7 @@ struct GitRepoImpl : GitRepo, std::enable_shared_from_this if (git_odb_add_backend(odb.get(), mempackBackend, 999)) throw Error("adding mempack backend to Git object database: %s", git_error_last()->message); - if (packfilesOnly) { + if (options.packfilesOnly) { if (git_repository_set_odb(repo.get(), odb.get())) throw Error("setting Git object database: %s", git_error_last()->message); } @@ -366,7 +369,7 @@ struct GitRepoImpl : GitRepo, std::enable_shared_from_this { // TODO: as an optimization, it would be nice to include `this` in the pool. return Pool(std::numeric_limits::max(), [this]() -> ref { - return make_ref(path, false, bare); + return make_ref(path, options); }); } @@ -712,9 +715,9 @@ struct GitRepoImpl : GitRepo, std::enable_shared_from_this } }; -ref GitRepo::openRepo(const std::filesystem::path & path, bool create, bool bare, bool packfilesOnly) +ref GitRepo::openRepo(const std::filesystem::path & path, GitRepo::Options options) { - return make_ref(path, create, bare, packfilesOnly); + return make_ref(path, options); } /** @@ -1428,7 +1431,7 @@ namespace fetchers { ref Settings::getTarballCache() const { static auto repoDir = std::filesystem::path(getCacheDir()) / "tarball-cache"; - return GitRepo::openRepo(repoDir, /*create=*/true, /*bare=*/true, /*packfilesOnly=*/true); + return GitRepo::openRepo(repoDir, {.create = true, .bare = true, .packfilesOnly = true}); } } // namespace fetchers @@ -1442,7 +1445,7 @@ GitRepo::WorkdirInfo GitRepo::getCachedWorkdirInfo(const std::filesystem::path & if (i != cache->end()) return i->second; } - auto workdirInfo = GitRepo::openRepo(path)->getWorkdirInfo(); + auto workdirInfo = GitRepo::openRepo(path, {})->getWorkdirInfo(); _cache.lock()->emplace(path, workdirInfo); return workdirInfo; } diff --git a/src/libfetchers/git.cc b/src/libfetchers/git.cc index 4f5247861d88..3bdb38b91ebb 100644 --- a/src/libfetchers/git.cc +++ b/src/libfetchers/git.cc @@ -637,11 +637,6 @@ struct GitInputScheme : InputScheme url); } - // If we don't check here for the path existence, then we can give libgit2 any directory - // and it will initialize them as git directories. - if (!pathExists(path)) { - throw Error("The path '%s' does not exist.", path); - } repoInfo.location = std::filesystem::absolute(path); } else { if (url.scheme == "file") @@ -703,7 +698,7 @@ struct GitInputScheme : InputScheme if (auto res = cache->lookup(key)) return getIntAttr(*res, "lastModified"); - auto lastModified = GitRepo::openRepo(repoDir)->getLastModified(rev); + auto lastModified = GitRepo::openRepo(repoDir, {})->getLastModified(rev); cache->upsert(key, {{"lastModified", lastModified}}); @@ -726,7 +721,7 @@ struct GitInputScheme : InputScheme Activity act( *logger, lvlChatty, actUnknown, fmt("getting Git revision count of '%s'", repoInfo.locationToArg())); - auto revCount = GitRepo::openRepo(repoDir)->getRevCount(rev); + auto revCount = GitRepo::openRepo(repoDir, {})->getRevCount(rev); cache->upsert(key, Attrs{{"revCount", revCount}}); @@ -737,7 +732,7 @@ struct GitInputScheme : InputScheme { auto head = std::visit( overloaded{ - [&](const std::filesystem::path & path) { return GitRepo::openRepo(path)->getWorkdirRef(); }, + [&](const std::filesystem::path & path) { return GitRepo::openRepo(path, {})->getWorkdirRef(); }, [&](const ParsedURL & url) { return readHeadCached(url.to_string(), shallow); }}, repoInfo.location); if (!head) { @@ -795,7 +790,7 @@ struct GitInputScheme : InputScheme if (auto repoPath = repoInfo.getPath()) { repoDir = *repoPath; if (!input.getRev()) - input.attrs.insert_or_assign("rev", GitRepo::openRepo(repoDir)->resolveRef(ref).gitRev()); + input.attrs.insert_or_assign("rev", GitRepo::openRepo(repoDir, {})->resolveRef(ref).gitRev()); } else { auto repoUrl = std::get(repoInfo.location); std::filesystem::path cacheDir = getCachePath(repoUrl.to_string(), shallow); @@ -805,7 +800,7 @@ struct GitInputScheme : InputScheme std::filesystem::create_directories(cacheDir.parent_path()); PathLocks cacheDirLock({cacheDir.string()}); - auto repo = GitRepo::openRepo(cacheDir, true, true); + auto repo = GitRepo::openRepo(cacheDir, {.create = true, .bare = true}); // We need to set the origin so resolving submodule URLs works repo->setRemote("origin", repoUrl.to_string()); @@ -876,7 +871,7 @@ struct GitInputScheme : InputScheme // the remainder } - auto repo = GitRepo::openRepo(repoDir); + auto repo = GitRepo::openRepo(repoDir, {}); auto isShallow = repo->isShallow(); @@ -963,7 +958,7 @@ struct GitInputScheme : InputScheme for (auto & submodule : repoInfo.workdirInfo.submodules) repoInfo.workdirInfo.files.insert(submodule.path); - auto repo = GitRepo::openRepo(repoPath, false, false); + auto repo = GitRepo::openRepo(repoPath, {}); auto exportIgnore = getExportIgnoreAttr(input); @@ -1003,7 +998,7 @@ struct GitInputScheme : InputScheme } if (!repoInfo.workdirInfo.isDirty) { - auto repo = GitRepo::openRepo(repoPath); + auto repo = GitRepo::openRepo(repoPath, {}); if (auto ref = repo->getWorkdirRef()) input.attrs.insert_or_assign("ref", *ref); diff --git a/src/libfetchers/include/nix/fetchers/git-utils.hh b/src/libfetchers/include/nix/fetchers/git-utils.hh index 5c79f256e864..24a7b80087f8 100644 --- a/src/libfetchers/include/nix/fetchers/git-utils.hh +++ b/src/libfetchers/include/nix/fetchers/git-utils.hh @@ -32,8 +32,14 @@ struct GitRepo { virtual ~GitRepo() {} - static ref - openRepo(const std::filesystem::path & path, bool create = false, bool bare = false, bool packfilesOnly = false); + struct Options + { + bool create = false; + bool bare = false; + bool packfilesOnly = false; + }; + + static ref openRepo(const std::filesystem::path & path, Options options); virtual uint64_t getRevCount(const Hash & rev) = 0;