Skip to content

Commit

Permalink
Merge pull request #4 from obsidiansystems/split-git
Browse files Browse the repository at this point in the history
Split git
  • Loading branch information
Ericson2314 authored May 29, 2020
2 parents 854866b + 20c0a35 commit 319dcb4
Show file tree
Hide file tree
Showing 3 changed files with 75 additions and 39 deletions.
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

0 comments on commit 319dcb4

Please sign in to comment.