1
0
Fork 0
mirror of https://github.com/NixOS/nix synced 2025-06-24 22:11:15 +02:00
This commit is contained in:
Jörg Thalheim 2025-06-23 10:36:00 +00:00 committed by GitHub
commit aa2880bd6e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -47,6 +47,7 @@ template<> struct hash<git_oid>
} }
std::ostream & operator << (std::ostream & str, const git_oid & oid) std::ostream & operator << (std::ostream & str, const git_oid & oid)
{ {
str << git_oid_tostr_s(&oid); str << git_oid_tostr_s(&oid);
@ -58,6 +59,78 @@ bool operator == (const git_oid & oid1, const git_oid & oid2)
return git_oid_equal(&oid1, &oid2); return git_oid_equal(&oid1, &oid2);
} }
namespace {
int matchesDotPlusGit(const std::string& str) {
// String must have at least 4 characters (at least one '.' plus "git")
if (str.size() < 4) {
return 0;
}
// Count consecutive dots at the beginning
size_t dotCount = 0;
while (dotCount < str.size() && str[dotCount] == '.') {
dotCount++;
}
// Must have at least one dot
if (dotCount == 0) {
return 0;
}
// After the dots, check if the remaining string is exactly "git"
if ((str.size() == dotCount + 3) &&
(str[dotCount] == 'g') &&
(str[dotCount + 1] == 'i') &&
(str[dotCount + 2] == 't')) {
return dotCount;
}
return 0;
}
std::string escapeDotGit(const std::string& filename) {
// Check if this filename matches the pattern of dots followed by "git"
int dotCount = matchesDotPlusGit(filename);
if (dotCount == 0) {
// Not a dot+git pattern, return as is
return filename;
}
std::string result(dotCount * 2, '.'); // String with 2*dotCount dots
result += "git";
return result;
}
std::string unescapeDotGit(const std::string filename) {
// Check if this filename matches the pattern of dots followed by "git"
int dotCount = matchesDotPlusGit(filename);
// Ensure dots are even for unescaping (must be divisible by 2)
if (dotCount == 0 || dotCount % 2 != 0) {
// Can't unescape an odd number of dots, return as is
return filename;
}
// Create a new string with half the dots plus "git"
std::string result(dotCount / 2, '.'); // String with dotCount/2 dots
result += "git";
return result;
}
const git_tree_entry* gitTreebuilderGet(git_treebuilder *bld, std::string name)
{
auto escapedName = escapeDotGit(name);
return git_treebuilder_get(bld, escapedName.c_str());
}
const std::string gitTreeEntryName(const git_tree_entry *entry)
{
auto escapedName = git_tree_entry_name(entry);
return unescapeDotGit(escapedName);
}
}
namespace nix { namespace nix {
struct GitSourceAccessor; struct GitSourceAccessor;
@ -767,7 +840,7 @@ struct GitSourceAccessor : SourceAccessor
for (size_t n = 0; n < count; ++n) { for (size_t n = 0; n < count; ++n) {
auto entry = git_tree_entry_byindex(tree.get(), n); auto entry = git_tree_entry_byindex(tree.get(), n);
// FIXME: add to cache // FIXME: add to cache
res.emplace(std::string(git_tree_entry_name(entry)), DirEntry{}); res.emplace(std::string(gitTreeEntryName(entry)), DirEntry{});
} }
return res; return res;
@ -828,7 +901,7 @@ struct GitSourceAccessor : SourceAccessor
if (git_tree_entry_dup(Setter(copy), entry)) if (git_tree_entry_dup(Setter(copy), entry))
throw Error("dupping tree entry: %s", git_error_last()->message); throw Error("dupping tree entry: %s", git_error_last()->message);
auto entryName = std::string_view(git_tree_entry_name(entry)); auto entryName = gitTreeEntryName(entry);
if (entryName == name) if (entryName == name)
res = copy.get(); res = copy.get();
@ -999,6 +1072,7 @@ struct GitExportIgnoreSourceAccessor : CachingFilteringSourceAccessor {
return !isExportIgnored(path); return !isExportIgnored(path);
} }
}; };
struct GitFileSystemObjectSinkImpl : GitFileSystemObjectSink struct GitFileSystemObjectSinkImpl : GitFileSystemObjectSink
@ -1019,7 +1093,7 @@ struct GitFileSystemObjectSinkImpl : GitFileSystemObjectSink
Tree prevTree = nullptr; Tree prevTree = nullptr;
if (!pendingDirs.empty() && if (!pendingDirs.empty() &&
(entry = git_treebuilder_get(pendingDirs.back().builder.get(), name.c_str()))) (entry = gitTreebuilderGet(pendingDirs.back().builder.get(), name)))
{ {
/* Clone a tree that we've already finished. This happens /* Clone a tree that we've already finished. This happens
if a tarball has directory entries that are not if a tarball has directory entries that are not
@ -1057,7 +1131,8 @@ struct GitFileSystemObjectSinkImpl : GitFileSystemObjectSink
{ {
assert(!pendingDirs.empty()); assert(!pendingDirs.empty());
auto & pending = pendingDirs.back(); auto & pending = pendingDirs.back();
if (git_treebuilder_insert(nullptr, pending.builder.get(), name.c_str(), &oid, mode)) auto escapedName = escapeDotGit(name);
if (git_treebuilder_insert(nullptr, pending.builder.get(), escapedName.c_str(), &oid, mode))
throw Error("adding a file to a tree builder: %s", git_error_last()->message); throw Error("adding a file to a tree builder: %s", git_error_last()->message);
}; };
@ -1188,7 +1263,7 @@ struct GitFileSystemObjectSinkImpl : GitFileSystemObjectSink
for (auto & c : CanonPath(relTargetLeft)) { for (auto & c : CanonPath(relTargetLeft)) {
if (auto builder = std::get_if<git_treebuilder *>(&curDir)) { if (auto builder = std::get_if<git_treebuilder *>(&curDir)) {
assert(*builder); assert(*builder);
if (!(entry = git_treebuilder_get(*builder, std::string(c).c_str()))) if (!(entry = gitTreebuilderGet(*builder, std::string(c))))
throw Error("cannot find hard link target '%s' for path '%s'", target, path); throw Error("cannot find hard link target '%s' for path '%s'", target, path);
curDir = *git_tree_entry_id(entry); curDir = *git_tree_entry_id(entry);
} else if (auto oid = std::get_if<git_oid>(&curDir)) { } else if (auto oid = std::get_if<git_oid>(&curDir)) {