forked from lix-project/lix
Reapply changes from lix to git.cc
Change-Id: I8bdfb6022ab39316504aeb84424b920eeb19d2df
This commit is contained in:
parent
0e44ff99af
commit
1eff8ad0fd
|
@ -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(
|
||||
|
|
Loading…
Reference in a new issue