mirror of
https://github.com/NixOS/nix
synced 2025-06-25 10:41:16 +02:00
GitSourceAccessor: Make thread-safe
This commit is contained in:
parent
4751cbef63
commit
4bce2d723d
1 changed files with 56 additions and 37 deletions
|
@ -665,28 +665,40 @@ ref<GitRepo> GitRepo::openRepo(const std::filesystem::path & path, bool create,
|
||||||
|
|
||||||
struct GitSourceAccessor : SourceAccessor
|
struct GitSourceAccessor : SourceAccessor
|
||||||
{
|
{
|
||||||
|
struct State
|
||||||
|
{
|
||||||
ref<GitRepoImpl> repo;
|
ref<GitRepoImpl> repo;
|
||||||
Object root;
|
Object root;
|
||||||
std::optional<lfs::Fetch> lfsFetch = std::nullopt;
|
std::optional<lfs::Fetch> lfsFetch = std::nullopt;
|
||||||
|
};
|
||||||
|
|
||||||
|
Sync<State> state_;
|
||||||
|
|
||||||
GitSourceAccessor(ref<GitRepoImpl> repo_, const Hash & rev, bool smudgeLfs)
|
GitSourceAccessor(ref<GitRepoImpl> repo_, const Hash & rev, bool smudgeLfs)
|
||||||
: repo(repo_)
|
: state_{
|
||||||
, root(peelToTreeOrBlob(lookupObject(*repo, hashToOID(rev)).get()))
|
State {
|
||||||
|
.repo = repo_,
|
||||||
|
.root = peelToTreeOrBlob(lookupObject(*repo_, hashToOID(rev)).get()),
|
||||||
|
.lfsFetch = smudgeLfs ? std::make_optional(lfs::Fetch(*repo_, hashToOID(rev))) : std::nullopt,
|
||||||
|
}
|
||||||
|
}
|
||||||
{
|
{
|
||||||
if (smudgeLfs)
|
|
||||||
lfsFetch = std::make_optional(lfs::Fetch(*repo, hashToOID(rev)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string readBlob(const CanonPath & path, bool symlink)
|
std::string readBlob(const CanonPath & path, bool symlink)
|
||||||
{
|
{
|
||||||
const auto blob = getBlob(path, symlink);
|
auto state(state_.lock());
|
||||||
|
|
||||||
if (lfsFetch) {
|
const auto blob = getBlob(*state, path, symlink);
|
||||||
if (lfsFetch->shouldFetch(path)) {
|
|
||||||
|
if (state->lfsFetch) {
|
||||||
|
if (state->lfsFetch->shouldFetch(path)) {
|
||||||
StringSink s;
|
StringSink s;
|
||||||
try {
|
try {
|
||||||
|
// FIXME: do we need to hold the state lock while
|
||||||
|
// doing this?
|
||||||
auto contents = std::string((const char *) git_blob_rawcontent(blob.get()), git_blob_rawsize(blob.get()));
|
auto contents = std::string((const char *) git_blob_rawcontent(blob.get()), git_blob_rawsize(blob.get()));
|
||||||
lfsFetch->fetch(contents, path, s, [&s](uint64_t size){ s.s.reserve(size); });
|
state->lfsFetch->fetch(contents, path, s, [&s](uint64_t size){ s.s.reserve(size); });
|
||||||
} catch (Error & e) {
|
} catch (Error & e) {
|
||||||
e.addTrace({}, "while smudging git-lfs file '%s'", path);
|
e.addTrace({}, "while smudging git-lfs file '%s'", path);
|
||||||
throw;
|
throw;
|
||||||
|
@ -705,15 +717,18 @@ struct GitSourceAccessor : SourceAccessor
|
||||||
|
|
||||||
bool pathExists(const CanonPath & path) override
|
bool pathExists(const CanonPath & path) override
|
||||||
{
|
{
|
||||||
return path.isRoot() ? true : (bool) lookup(path);
|
auto state(state_.lock());
|
||||||
|
return path.isRoot() ? true : (bool) lookup(*state, path);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<Stat> maybeLstat(const CanonPath & path) override
|
std::optional<Stat> maybeLstat(const CanonPath & path) override
|
||||||
{
|
{
|
||||||
if (path.isRoot())
|
auto state(state_.lock());
|
||||||
return Stat { .type = git_object_type(root.get()) == GIT_OBJECT_TREE ? tDirectory : tRegular };
|
|
||||||
|
|
||||||
auto entry = lookup(path);
|
if (path.isRoot())
|
||||||
|
return Stat { .type = git_object_type(state->root.get()) == GIT_OBJECT_TREE ? tDirectory : tRegular };
|
||||||
|
|
||||||
|
auto entry = lookup(*state, path);
|
||||||
if (!entry)
|
if (!entry)
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
|
|
||||||
|
@ -741,6 +756,8 @@ struct GitSourceAccessor : SourceAccessor
|
||||||
|
|
||||||
DirEntries readDirectory(const CanonPath & path) override
|
DirEntries readDirectory(const CanonPath & path) override
|
||||||
{
|
{
|
||||||
|
auto state(state_.lock());
|
||||||
|
|
||||||
return std::visit(overloaded {
|
return std::visit(overloaded {
|
||||||
[&](Tree tree) {
|
[&](Tree tree) {
|
||||||
DirEntries res;
|
DirEntries res;
|
||||||
|
@ -758,7 +775,7 @@ struct GitSourceAccessor : SourceAccessor
|
||||||
[&](Submodule) {
|
[&](Submodule) {
|
||||||
return DirEntries();
|
return DirEntries();
|
||||||
}
|
}
|
||||||
}, getTree(path));
|
}, getTree(*state, path));
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string readLink(const CanonPath & path) override
|
std::string readLink(const CanonPath & path) override
|
||||||
|
@ -772,7 +789,9 @@ struct GitSourceAccessor : SourceAccessor
|
||||||
*/
|
*/
|
||||||
std::optional<Hash> getSubmoduleRev(const CanonPath & path)
|
std::optional<Hash> getSubmoduleRev(const CanonPath & path)
|
||||||
{
|
{
|
||||||
auto entry = lookup(path);
|
auto state(state_.lock());
|
||||||
|
|
||||||
|
auto entry = lookup(*state, path);
|
||||||
|
|
||||||
if (!entry || git_tree_entry_type(entry) != GIT_OBJECT_COMMIT)
|
if (!entry || git_tree_entry_type(entry) != GIT_OBJECT_COMMIT)
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
|
@ -783,7 +802,7 @@ struct GitSourceAccessor : SourceAccessor
|
||||||
std::unordered_map<CanonPath, TreeEntry> lookupCache;
|
std::unordered_map<CanonPath, TreeEntry> lookupCache;
|
||||||
|
|
||||||
/* 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(State & state, const CanonPath & path)
|
||||||
{
|
{
|
||||||
auto i = lookupCache.find(path);
|
auto i = lookupCache.find(path);
|
||||||
if (i != lookupCache.end()) return i->second.get();
|
if (i != lookupCache.end()) return i->second.get();
|
||||||
|
@ -793,7 +812,7 @@ struct GitSourceAccessor : SourceAccessor
|
||||||
|
|
||||||
auto name = path.baseName().value();
|
auto name = path.baseName().value();
|
||||||
|
|
||||||
auto parentTree = lookupTree(*parent);
|
auto parentTree = lookupTree(state, *parent);
|
||||||
if (!parentTree) return nullptr;
|
if (!parentTree) return nullptr;
|
||||||
|
|
||||||
auto count = git_tree_entrycount(parentTree->get());
|
auto count = git_tree_entrycount(parentTree->get());
|
||||||
|
@ -822,29 +841,29 @@ struct GitSourceAccessor : SourceAccessor
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<Tree> lookupTree(const CanonPath & path)
|
std::optional<Tree> lookupTree(State & state, const CanonPath & path)
|
||||||
{
|
{
|
||||||
if (path.isRoot()) {
|
if (path.isRoot()) {
|
||||||
if (git_object_type(root.get()) == GIT_OBJECT_TREE)
|
if (git_object_type(state.root.get()) == GIT_OBJECT_TREE)
|
||||||
return dupObject<Tree>((git_tree *) &*root);
|
return dupObject<Tree>((git_tree *) &*state.root);
|
||||||
else
|
else
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto entry = lookup(path);
|
auto entry = lookup(state, path);
|
||||||
if (!entry || git_tree_entry_type(entry) != GIT_OBJECT_TREE)
|
if (!entry || git_tree_entry_type(entry) != GIT_OBJECT_TREE)
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
|
|
||||||
Tree tree;
|
Tree tree;
|
||||||
if (git_tree_entry_to_object((git_object * *) (git_tree * *) Setter(tree), *repo, entry))
|
if (git_tree_entry_to_object((git_object * *) (git_tree * *) Setter(tree), *state.repo, entry))
|
||||||
throw Error("looking up directory '%s': %s", showPath(path), git_error_last()->message);
|
throw Error("looking up directory '%s': %s", showPath(path), git_error_last()->message);
|
||||||
|
|
||||||
return tree;
|
return tree;
|
||||||
}
|
}
|
||||||
|
|
||||||
git_tree_entry * need(const CanonPath & path)
|
git_tree_entry * need(State & state, const CanonPath & path)
|
||||||
{
|
{
|
||||||
auto entry = lookup(path);
|
auto entry = lookup(state, path);
|
||||||
if (!entry)
|
if (!entry)
|
||||||
throw Error("'%s' does not exist", showPath(path));
|
throw Error("'%s' does not exist", showPath(path));
|
||||||
return entry;
|
return entry;
|
||||||
|
@ -852,16 +871,16 @@ struct GitSourceAccessor : SourceAccessor
|
||||||
|
|
||||||
struct Submodule { };
|
struct Submodule { };
|
||||||
|
|
||||||
std::variant<Tree, Submodule> getTree(const CanonPath & path)
|
std::variant<Tree, Submodule> getTree(State & state, const CanonPath & path)
|
||||||
{
|
{
|
||||||
if (path.isRoot()) {
|
if (path.isRoot()) {
|
||||||
if (git_object_type(root.get()) == GIT_OBJECT_TREE)
|
if (git_object_type(state.root.get()) == GIT_OBJECT_TREE)
|
||||||
return dupObject<Tree>((git_tree *) &*root);
|
return dupObject<Tree>((git_tree *) &*state.root);
|
||||||
else
|
else
|
||||||
throw Error("Git root object '%s' is not a directory", *git_object_id(root.get()));
|
throw Error("Git root object '%s' is not a directory", *git_object_id(state.root.get()));
|
||||||
}
|
}
|
||||||
|
|
||||||
auto entry = need(path);
|
auto entry = need(state, path);
|
||||||
|
|
||||||
if (git_tree_entry_type(entry) == GIT_OBJECT_COMMIT)
|
if (git_tree_entry_type(entry) == GIT_OBJECT_COMMIT)
|
||||||
return Submodule();
|
return Submodule();
|
||||||
|
@ -870,16 +889,16 @@ struct GitSourceAccessor : SourceAccessor
|
||||||
throw Error("'%s' is not a directory", showPath(path));
|
throw Error("'%s' is not a directory", showPath(path));
|
||||||
|
|
||||||
Tree tree;
|
Tree tree;
|
||||||
if (git_tree_entry_to_object((git_object * *) (git_tree * *) Setter(tree), *repo, entry))
|
if (git_tree_entry_to_object((git_object * *) (git_tree * *) Setter(tree), *state.repo, entry))
|
||||||
throw Error("looking up directory '%s': %s", showPath(path), git_error_last()->message);
|
throw Error("looking up directory '%s': %s", showPath(path), git_error_last()->message);
|
||||||
|
|
||||||
return tree;
|
return tree;
|
||||||
}
|
}
|
||||||
|
|
||||||
Blob getBlob(const CanonPath & path, bool expectSymlink)
|
Blob getBlob(State & state, const CanonPath & path, bool expectSymlink)
|
||||||
{
|
{
|
||||||
if (!expectSymlink && git_object_type(root.get()) == GIT_OBJECT_BLOB)
|
if (!expectSymlink && git_object_type(state.root.get()) == GIT_OBJECT_BLOB)
|
||||||
return dupObject<Blob>((git_blob *) &*root);
|
return dupObject<Blob>((git_blob *) &*state.root);
|
||||||
|
|
||||||
auto notExpected = [&]()
|
auto notExpected = [&]()
|
||||||
{
|
{
|
||||||
|
@ -892,7 +911,7 @@ struct GitSourceAccessor : SourceAccessor
|
||||||
|
|
||||||
if (path.isRoot()) notExpected();
|
if (path.isRoot()) notExpected();
|
||||||
|
|
||||||
auto entry = need(path);
|
auto entry = need(state, path);
|
||||||
|
|
||||||
if (git_tree_entry_type(entry) != GIT_OBJECT_BLOB)
|
if (git_tree_entry_type(entry) != GIT_OBJECT_BLOB)
|
||||||
notExpected();
|
notExpected();
|
||||||
|
@ -907,7 +926,7 @@ struct GitSourceAccessor : SourceAccessor
|
||||||
}
|
}
|
||||||
|
|
||||||
Blob blob;
|
Blob blob;
|
||||||
if (git_tree_entry_to_object((git_object * *) (git_blob * *) Setter(blob), *repo, entry))
|
if (git_tree_entry_to_object((git_object * *) (git_blob * *) Setter(blob), *state.repo, entry))
|
||||||
throw Error("looking up file '%s': %s", showPath(path), git_error_last()->message);
|
throw Error("looking up file '%s': %s", showPath(path), git_error_last()->message);
|
||||||
|
|
||||||
return blob;
|
return blob;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue