GitRepoImpl: Move exportIgnore into a filtering accessor

This commit is contained in:
Robert Hensing 2023-12-11 19:32:18 +01:00
parent cd5e752fa7
commit 467c62a96e

View file

@ -1,5 +1,6 @@
#include "git-utils.hh" #include "git-utils.hh"
#include "input-accessor.hh" #include "input-accessor.hh"
#include "filtering-input-accessor.hh"
#include "cache.hh" #include "cache.hh"
#include "finally.hh" #include "finally.hh"
#include "processes.hh" #include "processes.hh"
@ -465,16 +466,17 @@ ref<GitRepo> GitRepo::openRepo(const CanonPath & path, bool create, bool bare)
return make_ref<GitRepoImpl>(path, create, bare); return make_ref<GitRepoImpl>(path, create, bare);
} }
/**
* Raw git tree input accessor.
*/
struct GitInputAccessor : InputAccessor struct GitInputAccessor : InputAccessor
{ {
ref<GitRepoImpl> repo; ref<GitRepoImpl> repo;
Tree root; Tree root;
bool exportIgnore;
GitInputAccessor(ref<GitRepoImpl> repo_, const Hash & rev, bool exportIgnore) GitInputAccessor(ref<GitRepoImpl> repo_, const Hash & rev)
: repo(repo_) : repo(repo_)
, root(peelObject<Tree>(*repo, lookupObject(*repo, hashToOID(rev)).get(), GIT_OBJECT_TREE)) , root(peelObject<Tree>(*repo, lookupObject(*repo, hashToOID(rev)).get(), GIT_OBJECT_TREE))
, exportIgnore(exportIgnore)
{ {
} }
@ -503,7 +505,7 @@ struct GitInputAccessor : InputAccessor
return Stat { .type = tDirectory }; return Stat { .type = tDirectory };
auto entry = lookup(path); auto entry = lookup(path);
if (!entry || isExportIgnored(path)) if (!entry)
return std::nullopt; return std::nullopt;
auto mode = git_tree_entry_filemode(entry); auto mode = git_tree_entry_filemode(entry);
@ -538,12 +540,6 @@ struct GitInputAccessor : InputAccessor
for (size_t n = 0; n < count; ++n) { for (size_t n = 0; n < count; ++n) {
auto entry = git_tree_entry_byindex(tree.get(), n); auto entry = git_tree_entry_byindex(tree.get(), n);
if (exportIgnore) {
if (isExportIgnored(path + git_tree_entry_name(entry))) {
continue;
}
}
// FIXME: add to cache // FIXME: add to cache
res.emplace(std::string(git_tree_entry_name(entry)), DirEntry{}); res.emplace(std::string(git_tree_entry_name(entry)), DirEntry{});
} }
@ -573,33 +569,6 @@ struct GitInputAccessor : InputAccessor
std::unordered_map<CanonPath, TreeEntry> lookupCache; std::unordered_map<CanonPath, TreeEntry> lookupCache;
bool isExportIgnored(const CanonPath & path) {
if (!exportIgnore)
return false;
const char *exportIgnoreEntry = nullptr;
// GIT_ATTR_CHECK_INDEX_ONLY:
// > It will use index only for creating archives or for a bare repo
// > (if an index has been specified for the bare repo).
// -- https://github.com/libgit2/libgit2/blob/HEAD/include/git2/attr.h#L113C62-L115C48
if (git_attr_get(&exportIgnoreEntry,
*repo,
GIT_ATTR_CHECK_INDEX_ONLY,
std::string(path.rel()).c_str(),
"export-ignore")) {
if (git_error_last()->klass == GIT_ENOTFOUND)
return false;
else
throw Error("looking up '%s': %s", showPath(path), git_error_last()->message);
}
else {
// Official git will silently reject export-ignore lines that have
// values. We do the same.
return GIT_ATTR_IS_TRUE(exportIgnoreEntry);
}
}
/* Recursively look up 'path' relative to the root. */ /* Recursively look up 'path' relative to the root. */
git_tree_entry * lookup(const CanonPath & path) git_tree_entry * lookup(const CanonPath & path)
{ {
@ -613,10 +582,6 @@ struct GitInputAccessor : InputAccessor
throw Error("looking up '%s': %s", showPath(path), git_error_last()->message); throw Error("looking up '%s': %s", showPath(path), git_error_last()->message);
} }
if (entry && isExportIgnored(path)) {
entry.reset();
}
i = lookupCache.emplace(path, std::move(entry)).first; i = lookupCache.emplace(path, std::move(entry)).first;
} }
@ -692,6 +657,46 @@ struct GitInputAccessor : InputAccessor
} }
}; };
struct GitExportIgnoreInputAccessor : FilteringInputAccessor {
ref<GitRepoImpl> repo;
GitExportIgnoreInputAccessor(ref<GitRepoImpl> repo, ref<InputAccessor> next)
: FilteringInputAccessor(next, [&](const CanonPath & path) {
return RestrictedPathError(fmt("'%s' does not exist because it was fetched with exportIgnore enabled", path));
})
, repo(repo)
{ }
bool isExportIgnored(const CanonPath & path) {
const char *exportIgnoreEntry = nullptr;
// GIT_ATTR_CHECK_INDEX_ONLY:
// > It will use index only for creating archives or for a bare repo
// > (if an index has been specified for the bare repo).
// -- https://github.com/libgit2/libgit2/blob/HEAD/include/git2/attr.h#L113C62-L115C48
if (git_attr_get(&exportIgnoreEntry,
*repo,
GIT_ATTR_CHECK_INDEX_ONLY,
std::string(path.rel()).c_str(),
"export-ignore")) {
if (git_error_last()->klass == GIT_ENOTFOUND)
return false;
else
throw Error("looking up '%s': %s", showPath(path), git_error_last()->message);
}
else {
// Official git will silently reject export-ignore lines that have
// values. We do the same.
return GIT_ATTR_IS_TRUE(exportIgnoreEntry);
}
}
bool isAllowed(const CanonPath & path) override {
return !isExportIgnored(path);
}
};
ref<GitInputAccessor> GitRepoImpl::getRawAccessor(const Hash & rev) ref<GitInputAccessor> GitRepoImpl::getRawAccessor(const Hash & rev)
{ {
auto self = ref<GitRepoImpl>(shared_from_this()); auto self = ref<GitRepoImpl>(shared_from_this());
@ -700,7 +705,14 @@ ref<GitInputAccessor> GitRepoImpl::getRawAccessor(const Hash & rev)
ref<InputAccessor> GitRepoImpl::getAccessor(const Hash & rev, bool exportIgnore) ref<InputAccessor> GitRepoImpl::getAccessor(const Hash & rev, bool exportIgnore)
{ {
return make_ref<GitInputAccessor>(ref<GitRepoImpl>(shared_from_this()), rev, exportIgnore); auto self = ref<GitRepoImpl>(shared_from_this());
ref<GitInputAccessor> rawGitAccessor = getRawAccessor(rev);
if (exportIgnore) {
return make_ref<GitExportIgnoreInputAccessor>(self, rawGitAccessor);
}
else {
return rawGitAccessor;
}
} }
std::vector<std::tuple<GitRepoImpl::Submodule, Hash>> GitRepoImpl::getSubmodules(const Hash & rev, bool exportIgnore) std::vector<std::tuple<GitRepoImpl::Submodule, Hash>> GitRepoImpl::getSubmodules(const Hash & rev, bool exportIgnore)