mirror of
https://github.com/NixOS/nix.git
synced 2025-11-09 20:16:03 +01:00
Merge pull request #13757 from fzakaria/issue-13215
libfetchers/git: add support for '.' in gitmodules
This commit is contained in:
commit
aa0dc0d7e7
4 changed files with 217 additions and 13 deletions
199
src/libfetchers-tests/git.cc
Normal file
199
src/libfetchers-tests/git.cc
Normal file
|
|
@ -0,0 +1,199 @@
|
|||
#include "nix/store/store-open.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='.'");
|
||||
|
||||
// TODO: Use dummy:// store with MemorySourceAccessor.
|
||||
Path storeTmpDir = createTempDir();
|
||||
auto storeTmpDirAutoDelete = AutoDelete(storeTmpDir, true);
|
||||
ref<Store> store = openStore(storeTmpDir);
|
||||
|
||||
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(store);
|
||||
|
||||
ASSERT_EQ(accessor->readFile(CanonPath("deps/sub/lib.txt")), "hello from submodule\n");
|
||||
}
|
||||
|
|
@ -41,6 +41,7 @@ subdir('nix-meson-build-support/common')
|
|||
sources = files(
|
||||
'access-tokens.cc',
|
||||
'git-utils.cc',
|
||||
'git.cc',
|
||||
'nix_api_fetchers.cc',
|
||||
'public-key.cc',
|
||||
)
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
buildPackages,
|
||||
stdenv,
|
||||
mkMesonExecutable,
|
||||
writableTmpDirAsHomeHook,
|
||||
|
||||
nix-fetchers,
|
||||
nix-fetchers-c,
|
||||
|
|
@ -57,18 +58,13 @@ mkMesonExecutable (finalAttrs: {
|
|||
runCommand "${finalAttrs.pname}-run"
|
||||
{
|
||||
meta.broken = !stdenv.hostPlatform.emulatorAvailable buildPackages;
|
||||
buildInputs = [ writableTmpDirAsHomeHook ];
|
||||
}
|
||||
(
|
||||
lib.optionalString stdenv.hostPlatform.isWindows ''
|
||||
export HOME="$PWD/home-dir"
|
||||
mkdir -p "$HOME"
|
||||
''
|
||||
+ ''
|
||||
export _NIX_TEST_UNIT_DATA=${resolvePath ./data}
|
||||
${stdenv.hostPlatform.emulator buildPackages} ${lib.getExe finalAttrs.finalPackage}
|
||||
touch $out
|
||||
''
|
||||
);
|
||||
''
|
||||
export _NIX_TEST_UNIT_DATA=${resolvePath ./data}
|
||||
${stdenv.hostPlatform.emulator buildPackages} ${lib.getExe finalAttrs.finalPackage}
|
||||
touch $out
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -739,8 +739,16 @@ struct GitInputScheme : InputScheme
|
|||
fetchers::Attrs attrs;
|
||||
attrs.insert_or_assign("type", "git");
|
||||
attrs.insert_or_assign("url", resolved);
|
||||
if (submodule.branch != "")
|
||||
attrs.insert_or_assign("ref", submodule.branch);
|
||||
if (submodule.branch != "") {
|
||||
// A special value of . is used to indicate that the name of the branch in the submodule
|
||||
// should be the same name as the current branch in the current repository.
|
||||
// https://git-scm.com/docs/gitmodules
|
||||
if (submodule.branch == ".") {
|
||||
attrs.insert_or_assign("ref", ref);
|
||||
} else {
|
||||
attrs.insert_or_assign("ref", submodule.branch);
|
||||
}
|
||||
}
|
||||
attrs.insert_or_assign("rev", submoduleRev.gitRev());
|
||||
attrs.insert_or_assign("exportIgnore", Explicit<bool>{exportIgnore});
|
||||
attrs.insert_or_assign("submodules", Explicit<bool>{true});
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue