Implement shallow fetching

This commit is contained in:
Eelco Dolstra 2023-11-15 14:43:30 +01:00
parent 5dd4ae8687
commit 7ab91e7238
3 changed files with 31 additions and 24 deletions

View file

@ -364,7 +364,8 @@ struct GitRepoImpl : GitRepo, std::enable_shared_from_this<GitRepoImpl>
void fetch( void fetch(
const std::string & url, const std::string & url,
const std::string & refspec) override const std::string & refspec,
bool shallow) override
{ {
Activity act(*logger, lvlTalkative, actFetchTree, fmt("fetching Git repository '%s'", url)); Activity act(*logger, lvlTalkative, actFetchTree, fmt("fetching Git repository '%s'", url));
@ -380,6 +381,7 @@ struct GitRepoImpl : GitRepo, std::enable_shared_from_this<GitRepoImpl>
}; };
git_fetch_options opts = GIT_FETCH_OPTIONS_INIT; git_fetch_options opts = GIT_FETCH_OPTIONS_INIT;
opts.depth = shallow ? 1 : GIT_FETCH_DEPTH_FULL;
opts.callbacks.payload = &act; opts.callbacks.payload = &act;
opts.callbacks.sideband_progress = sidebandProgressCallback; opts.callbacks.sideband_progress = sidebandProgressCallback;
opts.callbacks.transfer_progress = transferProgressCallback; opts.callbacks.transfer_progress = transferProgressCallback;

View file

@ -75,7 +75,8 @@ struct GitRepo
virtual void fetch( virtual void fetch(
const std::string & url, const std::string & url,
const std::string & refspec) = 0; const std::string & refspec,
bool shallow) = 0;
/** /**
* Verify that commit `rev` is signed by one of the keys in * Verify that commit `rev` is signed by one of the keys in

View file

@ -219,9 +219,6 @@ struct GitInputScheme : InputScheme
|| name == "publicKeys") || name == "publicKeys")
experimentalFeatureSettings.require(Xp::VerifiedFetches); experimentalFeatureSettings.require(Xp::VerifiedFetches);
maybeGetBoolAttr(attrs, "shallow");
maybeGetBoolAttr(attrs, "submodules");
maybeGetBoolAttr(attrs, "allRefs");
maybeGetBoolAttr(attrs, "verifyCommit"); maybeGetBoolAttr(attrs, "verifyCommit");
if (auto ref = maybeGetStrAttr(attrs, "ref")) { if (auto ref = maybeGetStrAttr(attrs, "ref")) {
@ -234,6 +231,9 @@ struct GitInputScheme : InputScheme
auto url = fixGitURL(getStrAttr(attrs, "url")); auto url = fixGitURL(getStrAttr(attrs, "url"));
parseURL(url); parseURL(url);
input.attrs["url"] = url; input.attrs["url"] = url;
getShallowAttr(input);
getSubmodulesAttr(input);
getAllRefsAttr(input);
return input; return input;
} }
@ -243,8 +243,10 @@ struct GitInputScheme : InputScheme
if (url.scheme != "git") url.scheme = "git+" + url.scheme; if (url.scheme != "git") url.scheme = "git+" + url.scheme;
if (auto rev = input.getRev()) url.query.insert_or_assign("rev", rev->gitRev()); if (auto rev = input.getRev()) url.query.insert_or_assign("rev", rev->gitRev());
if (auto ref = input.getRef()) url.query.insert_or_assign("ref", *ref); if (auto ref = input.getRef()) url.query.insert_or_assign("ref", *ref);
if (maybeGetBoolAttr(input.attrs, "shallow").value_or(false)) if (getShallowAttr(input))
url.query.insert_or_assign("shallow", "1"); url.query.insert_or_assign("shallow", "1");
if (getSubmodulesAttr(input))
url.query.insert_or_assign("submodules", "1");
if (maybeGetBoolAttr(input.attrs, "verifyCommit").value_or(false)) if (maybeGetBoolAttr(input.attrs, "verifyCommit").value_or(false))
url.query.insert_or_assign("verifyCommit", "1"); url.query.insert_or_assign("verifyCommit", "1");
auto publicKeys = getPublicKeys(input.attrs); auto publicKeys = getPublicKeys(input.attrs);
@ -319,10 +321,6 @@ struct GitInputScheme : InputScheme
struct RepoInfo struct RepoInfo
{ {
bool shallow = false;
bool submodules = false;
bool allRefs = false;
/* Whether this is a local, non-bare repository. */ /* Whether this is a local, non-bare repository. */
bool isLocal = false; bool isLocal = false;
@ -347,11 +345,21 @@ struct GitInputScheme : InputScheme
std::string gitDir = ".git"; std::string gitDir = ".git";
}; };
bool getShallowAttr(const Input & input) const
{
return maybeGetBoolAttr(input.attrs, "shallow").value_or(false);
}
bool getSubmodulesAttr(const Input & input) const bool getSubmodulesAttr(const Input & input) const
{ {
return maybeGetBoolAttr(input.attrs, "submodules").value_or(false); return maybeGetBoolAttr(input.attrs, "submodules").value_or(false);
} }
bool getAllRefsAttr(const Input & input) const
{
return maybeGetBoolAttr(input.attrs, "allRefs").value_or(false);
}
RepoInfo getRepoInfo(const Input & input) const RepoInfo getRepoInfo(const Input & input) const
{ {
auto checkHashType = [&](const std::optional<Hash> & hash) auto checkHashType = [&](const std::optional<Hash> & hash)
@ -363,11 +371,7 @@ struct GitInputScheme : InputScheme
if (auto rev = input.getRev()) if (auto rev = input.getRev())
checkHashType(rev); checkHashType(rev);
RepoInfo repoInfo { RepoInfo repoInfo;
.shallow = maybeGetBoolAttr(input.attrs, "shallow").value_or(false),
.submodules = getSubmodulesAttr(input),
.allRefs = maybeGetBoolAttr(input.attrs, "allRefs").value_or(false)
};
// file:// URIs are normally not cloned (but otherwise treated the // file:// URIs are normally not cloned (but otherwise treated the
// same as remote URIs, i.e. we don't use the working tree or // same as remote URIs, i.e. we don't use the working tree or
@ -501,7 +505,7 @@ struct GitInputScheme : InputScheme
if (auto rev = input.getRev()) { if (auto rev = input.getRev()) {
doFetch = !repo->hasObject(*rev); doFetch = !repo->hasObject(*rev);
} else { } else {
if (repoInfo.allRefs) { if (getAllRefsAttr(input)) {
doFetch = true; doFetch = true;
} else { } else {
/* If the local ref is older than tarball-ttl seconds, do a /* If the local ref is older than tarball-ttl seconds, do a
@ -514,7 +518,7 @@ struct GitInputScheme : InputScheme
if (doFetch) { if (doFetch) {
try { try {
auto fetchRef = repoInfo.allRefs auto fetchRef = getAllRefsAttr(input)
? "refs/*" ? "refs/*"
: ref.compare(0, 5, "refs/") == 0 : ref.compare(0, 5, "refs/") == 0
? ref ? ref
@ -522,7 +526,7 @@ struct GitInputScheme : InputScheme
? ref ? ref
: "refs/heads/" + ref; : "refs/heads/" + ref;
repo->fetch(repoInfo.url, fmt("%s:%s", fetchRef, fetchRef)); repo->fetch(repoInfo.url, fmt("%s:%s", fetchRef, fetchRef), getShallowAttr(input));
} catch (Error & e) { } catch (Error & e) {
if (!pathExists(localRefFile)) throw; if (!pathExists(localRefFile)) throw;
logError(e.info()); logError(e.info());
@ -556,7 +560,7 @@ struct GitInputScheme : InputScheme
auto isShallow = repo->isShallow(); auto isShallow = repo->isShallow();
if (isShallow && !repoInfo.shallow) if (isShallow && !getShallowAttr(input))
throw Error("'%s' is a shallow Git repository, but shallow repositories are only allowed when `shallow = true;` is specified", repoInfo.url); throw Error("'%s' is a shallow Git repository, but shallow repositories are only allowed when `shallow = true;` is specified", repoInfo.url);
// FIXME: check whether rev is an ancestor of ref? // FIXME: check whether rev is an ancestor of ref?
@ -568,7 +572,7 @@ struct GitInputScheme : InputScheme
{"lastModified", getLastModified(repoInfo, repoDir, rev)}, {"lastModified", getLastModified(repoInfo, repoDir, rev)},
}); });
if (!repoInfo.shallow) if (!getShallowAttr(input))
infoAttrs.insert_or_assign("revCount", infoAttrs.insert_or_assign("revCount",
getRevCount(repoInfo, repoDir, rev)); getRevCount(repoInfo, repoDir, rev));
@ -581,7 +585,7 @@ struct GitInputScheme : InputScheme
/* If the repo has submodules, fetch them and return a mounted /* If the repo has submodules, fetch them and return a mounted
input accessor consisting of the accessor for the top-level input accessor consisting of the accessor for the top-level
repo and the accessors for the submodules. */ repo and the accessors for the submodules. */
if (repoInfo.submodules) { if (getSubmodulesAttr(input)) {
std::map<CanonPath, nix::ref<InputAccessor>> mounts; std::map<CanonPath, nix::ref<InputAccessor>> mounts;
for (auto & [submodule, submoduleRev] : repo->getSubmodules(rev)) { for (auto & [submodule, submoduleRev] : repo->getSubmodules(rev)) {
@ -607,7 +611,7 @@ struct GitInputScheme : InputScheme
} }
assert(!origRev || origRev == rev); assert(!origRev || origRev == rev);
if (!repoInfo.shallow) if (!getShallowAttr(input))
input.attrs.insert_or_assign("revCount", getIntAttr(infoAttrs, "revCount")); input.attrs.insert_or_assign("revCount", getIntAttr(infoAttrs, "revCount"));
input.attrs.insert_or_assign("lastModified", getIntAttr(infoAttrs, "lastModified")); input.attrs.insert_or_assign("lastModified", getIntAttr(infoAttrs, "lastModified"));
@ -619,7 +623,7 @@ struct GitInputScheme : InputScheme
RepoInfo & repoInfo, RepoInfo & repoInfo,
Input && input) const Input && input) const
{ {
if (repoInfo.submodules) if (getSubmodulesAttr(input))
/* Create mountpoints for the submodules. */ /* Create mountpoints for the submodules. */
for (auto & submodule : repoInfo.workdirInfo.submodules) for (auto & submodule : repoInfo.workdirInfo.submodules)
repoInfo.workdirInfo.files.insert(submodule.path); repoInfo.workdirInfo.files.insert(submodule.path);
@ -630,7 +634,7 @@ struct GitInputScheme : InputScheme
/* If the repo has submodules, return a mounted input accessor /* If the repo has submodules, return a mounted input accessor
consisting of the accessor for the top-level repo and the consisting of the accessor for the top-level repo and the
accessors for the submodule workdirs. */ accessors for the submodule workdirs. */
if (repoInfo.submodules && !repoInfo.workdirInfo.submodules.empty()) { if (getSubmodulesAttr(input) && !repoInfo.workdirInfo.submodules.empty()) {
std::map<CanonPath, nix::ref<InputAccessor>> mounts; std::map<CanonPath, nix::ref<InputAccessor>> mounts;
for (auto & submodule : repoInfo.workdirInfo.submodules) { for (auto & submodule : repoInfo.workdirInfo.submodules) {