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 "fetchers.hh"
|
||||||
#include "cache.hh"
|
#include "cache.hh"
|
||||||
#include "globals.hh"
|
#include "globals.hh"
|
||||||
|
#include "processes.hh"
|
||||||
#include "tarfile.hh"
|
#include "tarfile.hh"
|
||||||
#include "store-api.hh"
|
#include "store-api.hh"
|
||||||
#include "url-parts.hh"
|
#include "url-parts.hh"
|
||||||
#include "pathlocks.hh"
|
#include "pathlocks.hh"
|
||||||
#include "util.hh"
|
#include "users.hh"
|
||||||
#include "git.hh"
|
#include "git.hh"
|
||||||
|
#include "logging.hh"
|
||||||
|
#include "finally.hh"
|
||||||
|
#include "file-descriptor.hh"
|
||||||
|
|
||||||
#include "fetch-settings.hh"
|
#include "fetch-settings.hh"
|
||||||
|
|
||||||
|
@ -62,7 +66,8 @@ bool isCacheFileWithinTtl(const struct stat & st)
|
||||||
Path getGitCachePath(std::string_view url, bool shallow = false)
|
Path getGitCachePath(std::string_view url, bool shallow = false)
|
||||||
{
|
{
|
||||||
auto cacheDir = getCacheDir() + "/nix/gitv3/"
|
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
|
// Create the repo if it does not exist
|
||||||
if (!pathExists(cacheDir)) {
|
if (!pathExists(cacheDir)) {
|
||||||
createDirs(dirOf(cacheDir));
|
createDirs(dirOf(cacheDir));
|
||||||
|
@ -233,7 +238,7 @@ Path fetchRevisionIntoCache(const std::string & gitUrl, const std::string & revi
|
||||||
options.push_back("--");
|
options.push_back("--");
|
||||||
options.push_back(gitUrl);
|
options.push_back(gitUrl);
|
||||||
options.push_back(revision + ":" + referencesForRevisionsPrefix + revision);
|
options.push_back(revision + ":" + referencesForRevisionsPrefix + revision);
|
||||||
runProgram("git", true, options, {}, true);
|
runProgram("git", true, options, true);
|
||||||
} catch (Error & e) {
|
} catch (Error & e) {
|
||||||
// Failing the fetch is always fatal. We do not want to continue with a partial repo.
|
// 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);
|
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
|
// Will be set to the full reference
|
||||||
std::string fullReference;
|
std::string fullReference;
|
||||||
try {
|
try {
|
||||||
runProgram("git", true, options, {}, true);
|
runProgram("git", true, options, true);
|
||||||
auto fetchHead = readFile(repoDir + "/FETCH_HEAD");
|
auto fetchHead = readFile(repoDir + "/FETCH_HEAD");
|
||||||
auto lines = tokenizeString<std::vector<std::string>>(fetchHead, "\n");
|
auto lines = tokenizeString<std::vector<std::string>>(fetchHead, "\n");
|
||||||
|
|
||||||
|
@ -500,7 +505,6 @@ std::vector<SubmoduleInfo> readGitmodules(
|
||||||
"--get",
|
"--get",
|
||||||
"submodule." + submoduleName + ".path"
|
"submodule." + submoduleName + ".path"
|
||||||
},
|
},
|
||||||
std::nullopt,
|
|
||||||
true
|
true
|
||||||
);
|
);
|
||||||
path = output.substr(0, output.find("\n"));
|
path = output.substr(0, output.find("\n"));
|
||||||
|
@ -518,7 +522,6 @@ std::vector<SubmoduleInfo> readGitmodules(
|
||||||
"--get",
|
"--get",
|
||||||
"submodule." + submoduleName + ".url"
|
"submodule." + submoduleName + ".url"
|
||||||
},
|
},
|
||||||
std::nullopt,
|
|
||||||
true
|
true
|
||||||
);
|
);
|
||||||
url = output.substr(0, output.find("\n"));
|
url = output.substr(0, output.find("\n"));
|
||||||
|
@ -557,7 +560,6 @@ std::vector<SubmoduleInfo> readGitmodules(
|
||||||
"git",
|
"git",
|
||||||
true,
|
true,
|
||||||
Strings{"-C", gitDir, "ls-tree", "--object-only", revision, path},
|
Strings{"-C", gitDir, "ls-tree", "--object-only", revision, path},
|
||||||
{},
|
|
||||||
true
|
true
|
||||||
);
|
);
|
||||||
auto line = output.substr(0, output.find("\n"));
|
auto line = output.substr(0, output.find("\n"));
|
||||||
|
@ -641,9 +643,13 @@ std::pair<Path, std::string> getLocalRepoContainingRevision(
|
||||||
Path repoDir = fetchRevisionIntoCache(gitUrl, revision, shallow);
|
Path repoDir = fetchRevisionIntoCache(gitUrl, revision, shallow);
|
||||||
PathLocks cacheDirLock({repoDir + ".lock"});
|
PathLocks cacheDirLock({repoDir + ".lock"});
|
||||||
runProgram("git", true, {"-C", repoDir, "read-tree", revision});
|
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"}));
|
auto treeHash = chomp(runProgram("git", true, {"-C", repoDir, "write-tree"}));
|
||||||
|
|
||||||
return {absPath(repoDir), treeHash};
|
return {absPath(repoDir), treeHash};
|
||||||
|
@ -658,12 +664,14 @@ void copyAllFilesFromRevision(
|
||||||
const Path & gitDir, const Path & targetDir, const std::string & revision
|
const Path & gitDir, const Path & targetDir, const std::string & revision
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
auto source = sinkToSource([&](Sink & sink) {
|
auto proc = runProgram2({
|
||||||
runProgram2(
|
.program = "git",
|
||||||
{.program = "git", .args = {"-C", gitDir, "archive", revision}, .standardOut = &sink}
|
.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
|
/// 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);
|
auto url2(url);
|
||||||
if (hasPrefix(url2.scheme, "git+")) {
|
if (url2.scheme.starts_with("git+")) {
|
||||||
url2.scheme = std::string(url2.scheme, 4);
|
url2.scheme = std::string(url2.scheme, 4);
|
||||||
}
|
}
|
||||||
url2.query.clear();
|
url2.query.clear();
|
||||||
|
|
||||||
Attrs attrs;
|
Attrs attrs;
|
||||||
attrs.emplace("type", "git");
|
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());
|
attrs.emplace("url", url2.to_string());
|
||||||
|
|
||||||
|
emplaceURLQueryIntoAttrs(
|
||||||
|
url, attrs, {"lastModified", "revCount"}, {"shallow", "submodules"}
|
||||||
|
);
|
||||||
|
|
||||||
return inputFromAttrs(attrs);
|
return inputFromAttrs(attrs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -896,10 +897,10 @@ struct GitInputScheme : InputScheme
|
||||||
|
|
||||||
args.push_back(destDir);
|
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"));
|
auto url = parseURL(getStrAttr(input.attrs, "url"));
|
||||||
if (url.scheme == "file" && !input.getRef() && !input.getRev()) {
|
if (url.scheme == "file" && !input.getRef() && !input.getRev()) {
|
||||||
|
@ -908,40 +909,73 @@ struct GitInputScheme : InputScheme
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
void markChangedFile(
|
void putFile(
|
||||||
const Input & input, std::string_view file, std::optional<std::string> commitMsg
|
const Input & input,
|
||||||
) override
|
const CanonPath & path,
|
||||||
|
std::string_view contents,
|
||||||
|
std::optional<std::string> commitMsg
|
||||||
|
) const override
|
||||||
{
|
{
|
||||||
auto sourcePath = getSourcePath(input);
|
auto root = getSourcePath(input);
|
||||||
assert(sourcePath);
|
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";
|
auto gitDir = ".git";
|
||||||
|
|
||||||
runProgram(
|
auto result = runProgram(RunOptions{
|
||||||
"git",
|
.program = "git",
|
||||||
true,
|
.args =
|
||||||
{"-C",
|
{"-C",
|
||||||
*sourcePath,
|
*root,
|
||||||
"--git-dir",
|
"--git-dir",
|
||||||
gitDir,
|
gitDir,
|
||||||
"add",
|
"check-ignore",
|
||||||
"--intent-to-add",
|
"--quiet",
|
||||||
"--",
|
std::string(path.rel())},
|
||||||
std::string(file)}
|
});
|
||||||
);
|
auto exitCode = WEXITSTATUS(result.first);
|
||||||
|
|
||||||
if (commitMsg) {
|
if (exitCode != 0) {
|
||||||
|
// The path is not `.gitignore`d, we can add the file.
|
||||||
runProgram(
|
runProgram(
|
||||||
"git",
|
"git",
|
||||||
true,
|
true,
|
||||||
{"-C",
|
{"-C",
|
||||||
*sourcePath,
|
*root,
|
||||||
"--git-dir",
|
"--git-dir",
|
||||||
gitDir,
|
gitDir,
|
||||||
"commit",
|
"add",
|
||||||
std::string(file),
|
"--intent-to-add",
|
||||||
"-m",
|
"--",
|
||||||
*commitMsg}
|
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
|
// Verify that the hash type is valid if a revision is specified
|
||||||
if (input.getRev().has_value()
|
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(
|
throw Error(
|
||||||
"Hash '%s' is not supported by Git. Supported types are sha1 and sha256.",
|
"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
|
// Add to store and return
|
||||||
auto storePath = store->addToStore(
|
auto storePath = store->addToStore(
|
||||||
name, tmpDir, FileIngestionMethod::Recursive, htSHA256, defaultPathFilter
|
name, tmpDir, FileIngestionMethod::Recursive, HashType::SHA256, defaultPathFilter
|
||||||
);
|
);
|
||||||
if (!isDirty) {
|
if (!isDirty) {
|
||||||
getCache()->add(
|
getCache()->add(
|
||||||
|
|
Loading…
Reference in a new issue