Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Split git #4

Merged
merged 4 commits into from
May 29, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
89 changes: 55 additions & 34 deletions src/libutil/git.cc
Original file line number Diff line number Diff line change
Expand Up @@ -51,53 +51,74 @@ static void parse(ParseSink & sink, Source & source, const Path & path) {
else {
}
}
// Internal version, returns the perm.
unsigned int dumpGitInternal(const Path & path, Sink & sink, PathFilter & filter)

// TODO stream file into sink, rather than reading into vector
GitMode dumpGitBlob(const Path & path, const struct stat st, Sink & sink)
{
auto s = (format("blob %d\0%s"s) % std::to_string(st.st_size) % readFile(path)).str();

vector<uint8_t> v;
std::copy(s.begin(), s.end(), std::back_inserter(v));
sink(v.data(), v.size());
return st.st_mode & S_IXUSR
? GitMode::Executable
: GitMode::Regular;
}

GitMode dumpGitTree(const GitTree & entries, Sink & sink)
{
std::string s1 = "";
for (auto & i : entries) {
unsigned int mode;
switch (i.second.first) {
case GitMode::Directory: mode = 40000; break;
case GitMode::Executable: mode = 100755; break;
case GitMode::Regular: mode = 100644; break;
}
s1 += (format("%6d %s\0%s"s) % mode % i.first % i.second.second.hash).str();
}

std::string s2 = (format("tree %d\0%s"s) % s1.size() % s1).str();

vector<uint8_t> v;
std::copy(s2.begin(), s2.end(), std::back_inserter(v));
sink(v.data(), v.size());
return GitMode::Directory;
}

// Returns the perm in addition
std::pair<GitMode, Hash> dumpGitHashInternal(
std::function<std::unique_ptr<AbstractHashSink>()> makeHashSink,
const Path & path, PathFilter & filter)
{
auto hashSink = makeHashSink();
struct stat st;
GitMode perm;
if (lstat(path.c_str(), &st))
throw SysError(format("getting attributes of path '%1%'") % path);

if (S_ISREG(st.st_mode)) {
auto s = (format("blob %d\0%s"s) % std::to_string(st.st_size) % readFile(path)).str();

vector<unsigned char> v;
std::copy(s.begin(), s.end(), std::back_inserter(v));
sink(v.data(), v.size());
return st.st_mode & S_IXUSR
? 100755
: 100644;
}

else if (S_ISDIR(st.st_mode)) {
std::string s1 = "";

std::map<string, string> entries;
perm = dumpGitBlob(path, st, *hashSink);
} else if (S_ISDIR(st.st_mode)) {
GitTree entries;
for (auto & i : readDirectory(path))
entries[i.name] = i.name;

for (auto & i : entries)
if (filter(path + "/" + i.first)) {
HashSink hashSink(htSHA1);
unsigned int perm = dumpGitInternal(path + "/" + i.first, hashSink, filter);
auto hash = hashSink.finish().first;
s1 += (format("%6d %s\0%s"s) % perm % i.first % hash.hash).str();
if (filter(path + "/" + i.name)) {
entries[i.name] = dumpGitHashInternal(makeHashSink, path + "/" + i.name, filter);
}

std::string s2 = (format("tree %d\0%s"s) % s1.size() % s1).str();

vector<unsigned char> v;
std::copy(s2.begin(), s2.end(), std::back_inserter(v));
sink(v.data(), v.size());
return 40000;
perm = dumpGitTree(entries, *hashSink);
} else {
throw Error(format("file '%1%' has an unsupported type") % path);
}

else throw Error(format("file '%1%' has an unsupported type") % path);
auto hash = hashSink->finish().first;
return std::pair { perm, hash };
}

void dumpGit(const Path & path, Sink & sink, PathFilter & filter)
Hash dumpGitHash(
std::function<std::unique_ptr<AbstractHashSink>()> makeHashSink,
const Path & path, PathFilter & filter)
{
dumpGitInternal(path, sink, filter);
return dumpGitHashInternal(makeHashSink, path, filter).second;
}

}
16 changes: 15 additions & 1 deletion src/libutil/git.hh
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,20 @@ void restoreGit(const Path & path, Source & source);

void parseGit(ParseSink & sink, Source & source);

void dumpGit(const Path & path, Sink & sink, PathFilter & filter = defaultPathFilter);
// Dumps a single file to a sink
GitMode dumpGitBlob(const Path & path, const struct stat st, Sink & sink);

typedef std::map<string, std::pair<GitMode, Hash>> GitTree;

// Dumps a representation of a git tree to a sink
GitMode dumpGitTree(const GitTree & entries, Sink & sink);

// Recursively dumps path, hashing as we go
Hash dumpGitHash(
std::function<std::unique_ptr<AbstractHashSink>()>,
const Path & path,
PathFilter & filter = defaultPathFilter);

// N.B. There is no way to recursively dump to a sink, as that doesn't make
// sense with the git hash/data model where the information is Merklized.
}
9 changes: 5 additions & 4 deletions src/nix/hash.cc
Original file line number Diff line number Diff line change
Expand Up @@ -36,14 +36,17 @@ struct CmdHash : Command

std::string description() override
{
const char* d;
const char *d;
switch (mode) {
case FileIngestionMethod::Flat:
d = "print cryptographic hash of a regular file";
break;
case FileIngestionMethod::Recursive:
d = "print cryptographic hash of the NAR serialisation of a path";
break;
case FileIngestionMethod::Git:
d = "print cryptographic hash of the Git serialisation of a path";
break;
};
return d;
}
Expand Down Expand Up @@ -78,9 +81,7 @@ struct CmdHash : Command
break;
}
case FileIngestionMethod::Git:
auto hashSink = makeHashSink();
dumpGit(path, *hashSink);
h = hashSink->finish().first;
h = dumpGitHash(makeHashSink, path);
break;
}

Expand Down