Split tarball-specific logic from GitFileSystemObjectSink

This commit is contained in:
Eelco Dolstra 2024-07-29 14:26:25 +02:00
parent 5e83c0427f
commit e0012b97ab
5 changed files with 37 additions and 20 deletions

View file

@ -486,6 +486,24 @@ struct GitRepoImpl : GitRepo, std::enable_shared_from_this<GitRepoImpl>
return narHash; return narHash;
} }
Hash dereferenceSingletonDirectory(const Hash & oid_) override
{
auto oid = hashToOID(oid_);
/* If the root directory contains */
auto _tree = lookupObject(*this, oid, GIT_OBJECT_TREE);
auto tree = (const git_tree *) &*_tree;
if (git_tree_entrycount(tree) == 1) {
auto entry = git_tree_entry_byindex(tree, 0);
auto mode = git_tree_entry_filemode(entry);
if (mode == GIT_FILEMODE_BLOB || mode == GIT_FILEMODE_TREE)
oid = *git_tree_entry_id(entry);
}
return toHash(oid);
}
}; };
ref<GitRepo> GitRepo::openRepo(const std::filesystem::path & path, bool create, bool bare) ref<GitRepo> GitRepo::openRepo(const std::filesystem::path & path, bool create, bool bare)
@ -991,21 +1009,6 @@ struct GitFileSystemObjectSinkImpl : GitFileSystemObjectSink
auto [oid, _name] = popBuilder(); auto [oid, _name] = popBuilder();
/* If the root directory contains a single entry that is a
directory or a non-executable regular file, return that as
the top-level object. We don't do this for executables
because they don't have a tree hash in the Git object
model. */
auto _tree = lookupObject(*repo, oid, GIT_OBJECT_TREE);
auto tree = (const git_tree *) &*_tree;
if (git_tree_entrycount(tree) == 1) {
auto entry = git_tree_entry_byindex(tree, 0);
auto mode = git_tree_entry_filemode(entry);
if (mode == GIT_FILEMODE_BLOB || mode == GIT_FILEMODE_TREE)
oid = *git_tree_entry_id(entry);
}
return toHash(oid); return toHash(oid);
} }
}; };

View file

@ -98,6 +98,17 @@ struct GitRepo
* serialisation. This is memoised on-disk. * serialisation. This is memoised on-disk.
*/ */
virtual Hash treeHashToNarHash(const Hash & treeHash) = 0; virtual Hash treeHashToNarHash(const Hash & treeHash) = 0;
/**
* If the specified Git object is a directory with a single entry
* that is a directory or a non-executable regular file, return
* the ID of that object.
*
* Note: We don't do this for executable files because they don't
* have a tree hash in the Git object model that distinguishes
* them from non-executable files.
*/
virtual Hash dereferenceSingletonDirectory(const Hash & oid) = 0;
}; };
ref<GitRepo> getTarballCache(); ref<GitRepo> getTarballCache();

View file

@ -255,11 +255,12 @@ struct GitArchiveInputScheme : InputScheme
}); });
TarArchive archive { *source }; TarArchive archive { *source };
auto parseSink = getTarballCache()->getFileSystemObjectSink(); auto tarballCache = getTarballCache();
auto parseSink = tarballCache->getFileSystemObjectSink();
auto lastModified = unpackTarfileToSink(archive, *parseSink); auto lastModified = unpackTarfileToSink(archive, *parseSink);
TarballInfo tarballInfo { TarballInfo tarballInfo {
.treeHash = parseSink->sync(), .treeHash = tarballCache->dereferenceSingletonDirectory(parseSink->sync()),
.lastModified = lastModified .lastModified = lastModified
}; };

View file

@ -164,7 +164,8 @@ DownloadTarballResult downloadTarball(
TarArchive{path}; TarArchive{path};
}) })
: TarArchive{*source}; : TarArchive{*source};
auto parseSink = getTarballCache()->getFileSystemObjectSink(); auto tarballCache = getTarballCache();
auto parseSink = tarballCache->getFileSystemObjectSink();
auto lastModified = unpackTarfileToSink(archive, *parseSink); auto lastModified = unpackTarfileToSink(archive, *parseSink);
auto res(_res->lock()); auto res(_res->lock());
@ -177,7 +178,8 @@ DownloadTarballResult downloadTarball(
infoAttrs = cached->value; infoAttrs = cached->value;
} else { } else {
infoAttrs.insert_or_assign("etag", res->etag); infoAttrs.insert_or_assign("etag", res->etag);
infoAttrs.insert_or_assign("treeHash", parseSink->sync().gitRev()); infoAttrs.insert_or_assign("treeHash",
tarballCache->dereferenceSingletonDirectory(parseSink->sync()).gitRev());
infoAttrs.insert_or_assign("lastModified", uint64_t(lastModified)); infoAttrs.insert_or_assign("lastModified", uint64_t(lastModified));
if (res->immutableUrl) if (res->immutableUrl)
infoAttrs.insert_or_assign("immutableUrl", *res->immutableUrl); infoAttrs.insert_or_assign("immutableUrl", *res->immutableUrl);

View file

@ -77,7 +77,7 @@ TEST_F(GitUtilsTest, sink_basic)
// sink->createHardlink("foo-1.1/links/foo-2", CanonPath("foo-1.1/hello")); // sink->createHardlink("foo-1.1/links/foo-2", CanonPath("foo-1.1/hello"));
auto result = sink->sync(); auto result = repo->dereferenceSingletonDirectory(sink->sync());
auto accessor = repo->getAccessor(result, false); auto accessor = repo->getAccessor(result, false);
auto entries = accessor->readDirectory(CanonPath::root); auto entries = accessor->readDirectory(CanonPath::root);
ASSERT_EQ(entries.size(), 5); ASSERT_EQ(entries.size(), 5);