1
1
Fork 0
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:
Robert Hensing 2025-08-18 19:16:54 +02:00 committed by GitHub
commit aa0dc0d7e7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 217 additions and 13 deletions

View 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");
}

View file

@ -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',
)

View file

@ -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
'';
};
};

View file

@ -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});