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(
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));
@ -380,6 +381,7 @@ struct GitRepoImpl : GitRepo, std::enable_shared_from_this<GitRepoImpl>
};
git_fetch_options opts = GIT_FETCH_OPTIONS_INIT;
opts.depth = shallow ? 1 : GIT_FETCH_DEPTH_FULL;
opts.callbacks.payload = &act;
opts.callbacks.sideband_progress = sidebandProgressCallback;
opts.callbacks.transfer_progress = transferProgressCallback;

View file

@ -75,7 +75,8 @@ struct GitRepo
virtual void fetch(
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

View file

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