203 lines
5.9 KiB
C++
203 lines
5.9 KiB
C++
#include "nix/store/store-open.hh"
|
|
#include "nix/store/globals.hh"
|
|
#include "nix/store/dummy-store.hh"
|
|
#include "nix/fetchers/fetch-settings.hh"
|
|
#include "nix/fetchers/fetchers.hh"
|
|
#include "nix/fetchers/git-utils.hh"
|
|
|
|
#include <git2.h>
|
|
#include <gtest/gtest.h>
|
|
|
|
#include <filesystem>
|
|
|
|
namespace {
|
|
|
|
template<class T, void (*F)(T *)>
|
|
struct Deleter
|
|
{
|
|
void operator()(T * p) const
|
|
{
|
|
if (p)
|
|
F(p);
|
|
}
|
|
};
|
|
|
|
template<class T, void (*F)(T *)>
|
|
using Handle = std::unique_ptr<T, Deleter<T, F>>;
|
|
|
|
using RepoHandle = Handle<git_repository, git_repository_free>;
|
|
using IndexHandle = Handle<git_index, git_index_free>;
|
|
using TreeHandle = Handle<git_tree, git_tree_free>;
|
|
using SigHandle = Handle<git_signature, git_signature_free>;
|
|
using RefHandle = Handle<git_reference, git_reference_free>;
|
|
using CommitHandle = Handle<git_commit, git_commit_free>;
|
|
using SubmoduleHandle = Handle<git_submodule, git_submodule_free>;
|
|
|
|
#define CHECK_LIBGIT(expr) ASSERT_TRUE((expr) >= 0) << git_error_last()
|
|
|
|
static void commitAll(git_repository * repo, const char * msg)
|
|
{
|
|
IndexHandle idx;
|
|
{
|
|
git_index * raw = nullptr;
|
|
CHECK_LIBGIT(git_repository_index(&raw, repo));
|
|
idx.reset(raw);
|
|
}
|
|
CHECK_LIBGIT(git_index_add_all(idx.get(), nullptr, 0, nullptr, nullptr));
|
|
CHECK_LIBGIT(git_index_write(idx.get()));
|
|
|
|
git_oid treeId{};
|
|
CHECK_LIBGIT(git_index_write_tree(&treeId, idx.get()));
|
|
TreeHandle tree;
|
|
{
|
|
git_tree * raw = nullptr;
|
|
CHECK_LIBGIT(git_tree_lookup(&raw, repo, &treeId));
|
|
tree.reset(raw);
|
|
}
|
|
|
|
SigHandle sig;
|
|
{
|
|
git_signature * raw = nullptr;
|
|
CHECK_LIBGIT(git_signature_now(&raw, "you", "you@example.com"));
|
|
sig.reset(raw);
|
|
}
|
|
|
|
git_oid commitId{};
|
|
if (git_repository_is_empty(repo) == 1) {
|
|
CHECK_LIBGIT(git_commit_create_v(&commitId, repo, "HEAD", sig.get(), sig.get(), nullptr, msg, tree.get(), 0));
|
|
CHECK_LIBGIT(git_reference_create(nullptr, repo, "refs/heads/main", &commitId, true, nullptr));
|
|
CHECK_LIBGIT(git_repository_set_head(repo, "refs/heads/main"));
|
|
} else {
|
|
RefHandle head;
|
|
{
|
|
git_reference * raw = nullptr;
|
|
CHECK_LIBGIT(git_repository_head(&raw, repo));
|
|
head.reset(raw);
|
|
}
|
|
CommitHandle parent;
|
|
{
|
|
git_commit * raw = nullptr;
|
|
CHECK_LIBGIT(git_commit_lookup(&raw, repo, git_reference_target(head.get())));
|
|
parent.reset(raw);
|
|
}
|
|
const git_commit * parents[] = {parent.get()};
|
|
CHECK_LIBGIT(git_commit_create(
|
|
&commitId,
|
|
repo,
|
|
"HEAD",
|
|
sig.get(),
|
|
sig.get(),
|
|
/*message_encoding=*/nullptr,
|
|
msg,
|
|
tree.get(),
|
|
/*parent_count=*/1,
|
|
&parents[0]));
|
|
}
|
|
}
|
|
|
|
} // namespace
|
|
|
|
using namespace nix;
|
|
|
|
class GitTest : public ::testing::Test
|
|
{
|
|
std::unique_ptr<AutoDelete> delTmpDir;
|
|
|
|
protected:
|
|
std::filesystem::path tmpDir;
|
|
|
|
void SetUp() override
|
|
{
|
|
tmpDir = createTempDir();
|
|
delTmpDir = std::make_unique<AutoDelete>(tmpDir, /*recursive=*/true);
|
|
nix::initLibStore(/*loadConfig=*/false);
|
|
git_libgit2_init();
|
|
}
|
|
|
|
void TearDown() override
|
|
{
|
|
delTmpDir.reset();
|
|
}
|
|
};
|
|
|
|
// Regression test for https://github.com/NixOS/nix/issues/13215
|
|
TEST_F(GitTest, submodulePeriodSupport)
|
|
{
|
|
auto storePath = tmpDir / "store";
|
|
auto repoPath = tmpDir / "repo";
|
|
auto submodulePath = tmpDir / "submodule";
|
|
|
|
// Set up our git directories: one top level and a submodule
|
|
// the submodule in the .gitmodules has the branch listed as '.'
|
|
|
|
// 1) Create sub repo
|
|
{
|
|
git_repository * raw = nullptr;
|
|
CHECK_LIBGIT(git_repository_init(&raw, submodulePath.string().c_str(), /*is_bare=*/0));
|
|
RepoHandle sub(raw);
|
|
writeFile(submodulePath / "lib.txt", "hello from submodule\n");
|
|
commitAll(sub.get(), "init sub");
|
|
}
|
|
|
|
// 2) Create super repo
|
|
RepoHandle super;
|
|
{
|
|
git_repository * raw = nullptr;
|
|
CHECK_LIBGIT(git_repository_init(&raw, repoPath.string().c_str(), /*is_bare=*/0));
|
|
super.reset(raw);
|
|
}
|
|
|
|
writeFile(repoPath / "README.md", "# super\n");
|
|
commitAll(super.get(), "init super");
|
|
|
|
// 3) Add submodule at deps/sub
|
|
{
|
|
git_repository * raw = nullptr;
|
|
git_clone_options cloneOpts = GIT_CLONE_OPTIONS_INIT;
|
|
// clone from local subPath into superPath/deps/sub
|
|
CHECK_LIBGIT(
|
|
git_clone(&raw, submodulePath.string().c_str(), (repoPath / "deps" / "sub").string().c_str(), &cloneOpts));
|
|
RepoHandle sub(raw);
|
|
}
|
|
|
|
// 4) Add submodule and set branch="."
|
|
SubmoduleHandle sm;
|
|
{
|
|
git_submodule * raw = nullptr;
|
|
CHECK_LIBGIT(git_submodule_add_setup(
|
|
&raw,
|
|
super.get(),
|
|
"../submodule",
|
|
"deps/sub",
|
|
/*use_gitlink=*/1));
|
|
sm.reset(raw);
|
|
}
|
|
CHECK_LIBGIT(git_submodule_set_branch(super.get(), git_submodule_name(sm.get()), /*branch=*/"."));
|
|
CHECK_LIBGIT(git_submodule_sync(sm.get()));
|
|
|
|
// 5) Finalize now that the worktree exists; libgit2 can read its HEAD OID
|
|
CHECK_LIBGIT(git_submodule_add_finalize(sm.get()));
|
|
// 6) Commit the addition in super
|
|
commitAll(super.get(), "Add submodule with branch='.'");
|
|
|
|
auto store = [] {
|
|
auto cfg = make_ref<DummyStoreConfig>(StoreReference::Params{});
|
|
cfg->readOnly = false;
|
|
return cfg->openStore();
|
|
}();
|
|
|
|
auto settings = fetchers::Settings{};
|
|
auto input = fetchers::Input::fromAttrs(
|
|
settings,
|
|
{
|
|
{"url", "file://" + repoPath.string()},
|
|
{"submodules", Explicit{true}},
|
|
{"type", "git"},
|
|
{"ref", "main"},
|
|
});
|
|
|
|
auto [accessor, i] = input.getAccessor(settings, *store);
|
|
|
|
ASSERT_EQ(accessor->readFile(CanonPath("deps/sub/lib.txt")), "hello from submodule\n");
|
|
}
|