Reapply changes from lix to git.cc

Change-Id: I8bdfb6022ab39316504aeb84424b920eeb19d2df
This commit is contained in:
Zebreus 2024-08-26 23:02:13 +02:00
parent 0e44ff99af
commit 1eff8ad0fd

View file

@ -1,12 +1,16 @@
#include "fetchers.hh"
#include "cache.hh"
#include "globals.hh"
#include "processes.hh"
#include "tarfile.hh"
#include "store-api.hh"
#include "url-parts.hh"
#include "pathlocks.hh"
#include "util.hh"
#include "users.hh"
#include "git.hh"
#include "logging.hh"
#include "finally.hh"
#include "file-descriptor.hh"
#include "fetch-settings.hh"
@ -62,7 +66,8 @@ bool isCacheFileWithinTtl(const struct stat & st)
Path getGitCachePath(std::string_view url, bool shallow = false)
{
auto cacheDir = getCacheDir() + "/nix/gitv3/"
+ hashString(htSHA256, url).to_string(Base32, false) + (shallow ? "_shallow" : "");
+ hashString(HashType::SHA256, url).to_string(Base::Base32, false)
+ (shallow ? "_shallow" : "");
// Create the repo if it does not exist
if (!pathExists(cacheDir)) {
createDirs(dirOf(cacheDir));
@ -233,7 +238,7 @@ Path fetchRevisionIntoCache(const std::string & gitUrl, const std::string & revi
options.push_back("--");
options.push_back(gitUrl);
options.push_back(revision + ":" + referencesForRevisionsPrefix + revision);
runProgram("git", true, options, {}, true);
runProgram("git", true, options, true);
} catch (Error & e) {
// Failing the fetch is always fatal. We do not want to continue with a partial repo.
throw Error("failed to fetch revision '%s' from '%s'", revision, gitUrl);
@ -308,7 +313,7 @@ std::string fetchAndResolveReferenceIntoCache(
// Will be set to the full reference
std::string fullReference;
try {
runProgram("git", true, options, {}, true);
runProgram("git", true, options, true);
auto fetchHead = readFile(repoDir + "/FETCH_HEAD");
auto lines = tokenizeString<std::vector<std::string>>(fetchHead, "\n");
@ -500,7 +505,6 @@ std::vector<SubmoduleInfo> readGitmodules(
"--get",
"submodule." + submoduleName + ".path"
},
std::nullopt,
true
);
path = output.substr(0, output.find("\n"));
@ -518,7 +522,6 @@ std::vector<SubmoduleInfo> readGitmodules(
"--get",
"submodule." + submoduleName + ".url"
},
std::nullopt,
true
);
url = output.substr(0, output.find("\n"));
@ -557,7 +560,6 @@ std::vector<SubmoduleInfo> readGitmodules(
"git",
true,
Strings{"-C", gitDir, "ls-tree", "--object-only", revision, path},
{},
true
);
auto line = output.substr(0, output.find("\n"));
@ -641,9 +643,13 @@ std::pair<Path, std::string> getLocalRepoContainingRevision(
Path repoDir = fetchRevisionIntoCache(gitUrl, revision, shallow);
PathLocks cacheDirLock({repoDir + ".lock"});
runProgram("git", true, {"-C", repoDir, "read-tree", revision});
runProgram(
"git", true, Strings{"-C", repoDir, "apply", "--cached", "--binary", "-"}, dirtyDiff
);
// TODO: This previously piped stdin. That feature was removed in INSERT_COMMIT. For now this
// uses a temp file
auto [fd, diffFilePath] = createTempFile();
writeFull(fd.get(), dirtyDiff.value());
runProgram("git", true, {"-C", repoDir, "apply", "--cached", "--binary", diffFilePath}, true);
auto treeHash = chomp(runProgram("git", true, {"-C", repoDir, "write-tree"}));
return {absPath(repoDir), treeHash};
@ -658,12 +664,14 @@ void copyAllFilesFromRevision(
const Path & gitDir, const Path & targetDir, const std::string & revision
)
{
auto source = sinkToSource([&](Sink & sink) {
runProgram2(
{.program = "git", .args = {"-C", gitDir, "archive", revision}, .standardOut = &sink}
);
auto proc = runProgram2({
.program = "git",
.args = {"-C", gitDir, "archive", revision},
.captureStdout = true,
});
unpackTarfile(*source, targetDir);
Finally const _wait([&] { proc.wait(); });
unpackTarfile(*proc.getStdout(), targetDir);
}
/// Place the tree of a git repo at a given revision at a given path
@ -779,26 +787,19 @@ struct GitInputScheme : InputScheme
}
auto url2(url);
if (hasPrefix(url2.scheme, "git+")) {
if (url2.scheme.starts_with("git+")) {
url2.scheme = std::string(url2.scheme, 4);
}
url2.query.clear();
Attrs attrs;
attrs.emplace("type", "git");
for (auto & [name, value] : url.query) {
if (name == "rev" || name == "ref") {
attrs.emplace(name, value);
} else if (name == "shallow" || name == "submodules") {
attrs.emplace(name, Explicit<bool>{value == "1"});
} else {
url2.query.emplace(name, value);
}
}
attrs.emplace("url", url2.to_string());
emplaceURLQueryIntoAttrs(
url, attrs, {"lastModified", "revCount"}, {"shallow", "submodules"}
);
return inputFromAttrs(attrs);
}
@ -896,10 +897,10 @@ struct GitInputScheme : InputScheme
args.push_back(destDir);
runProgram("git", true, args, {}, true);
runProgram("git", true, args, true);
}
std::optional<Path> getSourcePath(const Input & input) override
std::optional<Path> getSourcePath(const Input & input) const override
{
auto url = parseURL(getStrAttr(input.attrs, "url"));
if (url.scheme == "file" && !input.getRef() && !input.getRev()) {
@ -908,40 +909,73 @@ struct GitInputScheme : InputScheme
return {};
}
void markChangedFile(
const Input & input, std::string_view file, std::optional<std::string> commitMsg
) override
void putFile(
const Input & input,
const CanonPath & path,
std::string_view contents,
std::optional<std::string> commitMsg
) const override
{
auto sourcePath = getSourcePath(input);
assert(sourcePath);
auto root = getSourcePath(input);
if (!root) {
throw Error(
"cannot commit '%s' to Git repository '%s' because it's not a working tree",
path,
input.to_string()
);
}
writeFile((CanonPath(*root) + path).abs(), contents);
auto gitDir = ".git";
runProgram(
"git",
true,
{"-C",
*sourcePath,
"--git-dir",
gitDir,
"add",
"--intent-to-add",
"--",
std::string(file)}
);
auto result = runProgram(RunOptions{
.program = "git",
.args =
{"-C",
*root,
"--git-dir",
gitDir,
"check-ignore",
"--quiet",
std::string(path.rel())},
});
auto exitCode = WEXITSTATUS(result.first);
if (commitMsg) {
if (exitCode != 0) {
// The path is not `.gitignore`d, we can add the file.
runProgram(
"git",
true,
{"-C",
*sourcePath,
*root,
"--git-dir",
gitDir,
"commit",
std::string(file),
"-m",
*commitMsg}
"add",
"--intent-to-add",
"--",
std::string(path.rel())}
);
if (commitMsg) {
auto [_fd, msgPath] = createTempFile("nix-msg");
AutoDelete const _delete{msgPath};
writeFile(msgPath, *commitMsg);
runProgram(
"git",
true,
{"-C",
*root,
"--git-dir",
gitDir,
"commit",
std::string(path.rel()),
"-F",
msgPath},
true
);
}
}
}
@ -962,11 +996,13 @@ struct GitInputScheme : InputScheme
{
// Verify that the hash type is valid if a revision is specified
if (input.getRev().has_value()
&& !(input.getRev()->type == htSHA1 || input.getRev()->type == htSHA256))
&& !(
input.getRev()->type == HashType::SHA1 || input.getRev()->type == HashType::SHA256
))
{
throw Error(
"Hash '%s' is not supported by Git. Supported types are sha1 and sha256.",
input.getRev()->to_string(Base16, true)
input.getRev()->to_string(Base::Base16, true)
);
}
@ -1036,7 +1072,7 @@ struct GitInputScheme : InputScheme
// Add to store and return
auto storePath = store->addToStore(
name, tmpDir, FileIngestionMethod::Recursive, htSHA256, defaultPathFilter
name, tmpDir, FileIngestionMethod::Recursive, HashType::SHA256, defaultPathFilter
);
if (!isDirty) {
getCache()->add(