diff --git a/src/libstore/daemon.cc b/src/libstore/daemon.cc index f1afdff699e..117ee8c3585 100644 --- a/src/libstore/daemon.cc +++ b/src/libstore/daemon.cc @@ -8,6 +8,7 @@ #include "archive.hh" #include "derivations.hh" #include "args.hh" +#include "git.hh" namespace nix::daemon { @@ -358,7 +359,8 @@ static void performOp(TunnelLogger * logger, ref store, std::string s, baseName; FileIngestionMethod method; { - bool fixed, recursive; + bool fixed; + unsigned char recursive; from >> baseName >> fixed /* obsolete */ >> recursive >> s; method = FileIngestionMethod { recursive }; /* Compatibility hack. */ @@ -372,20 +374,31 @@ static void performOp(TunnelLogger * logger, ref store, TeeSource savedNAR(from); RetrieveRegularNARSink savedRegular; - if (method == FileIngestionMethod::Recursive) { + switch (method) { + case FileIngestionMethod::Recursive: { /* Get the entire NAR dump from the client and save it to a string so that we can pass it to addToStoreFromDump(). */ ParseSink sink; /* null sink; just parse the NAR */ parseDump(sink, savedNAR); - } else + break; + } + case FileIngestionMethod::Git: { + ParseSink sink; + parseGit(sink, savedNAR, store->storeDir, store->storeDir); + break; + } + case FileIngestionMethod::Flat: { parseDump(savedRegular, from); + break; + } + } logger->startWork(); if (!savedRegular.regular) throw Error("regular file expected"); auto path = store->addToStoreFromDump( - method == FileIngestionMethod::Recursive ? *savedNAR.data : savedRegular.s, + method == FileIngestionMethod::Flat ? savedRegular.s : *savedNAR.data, baseName, method, hashAlgo); diff --git a/src/libstore/local-store.cc b/src/libstore/local-store.cc index 39efa650b25..14c816e7282 100644 --- a/src/libstore/local-store.cc +++ b/src/libstore/local-store.cc @@ -1017,7 +1017,10 @@ void LocalStore::addToStore(const ValidPathInfo & info, Source & source, return n; }); - restorePath(realPath, wrapperSource); + if (hasPrefix(info.ca, "fixed:git:")) + restoreGit(realPath, wrapperSource, realStoreDir, storeDir); + else + restorePath(realPath, wrapperSource); auto hashResult = hashSink->finish(); @@ -1046,6 +1049,9 @@ void LocalStore::addToStore(const ValidPathInfo & info, Source & source, StorePath LocalStore::addToStoreFromDump(const string & dump, const string & name, FileIngestionMethod method, HashType hashAlgo, RepairFlag repair) { + if (method == FileIngestionMethod::Git && hashAlgo != htSHA1) + throw Error("git ingestion must use sha1 hash"); + Hash h = hashString(hashAlgo, dump); auto dstPath = makeFixedOutputPath(method, h, name); @@ -1079,7 +1085,7 @@ StorePath LocalStore::addToStoreFromDump(const string & dump, const string & nam } case FileIngestionMethod::Git: { StringSource source(dump); - restoreGit(realPath, source); + restoreGit(realPath, source, realStoreDir, storeDir); break; } } @@ -1118,14 +1124,35 @@ StorePath LocalStore::addToStore(const string & name, const Path & _srcPath, { Path srcPath(absPath(_srcPath)); + if (method == FileIngestionMethod::Git && hashAlgo != htSHA1) + throw Error("git ingestion must use sha1 hash"); + /* Read the whole path into memory. This is not a very scalable method for very large paths, but `copyPath' is mainly used for small files. */ StringSink sink; - if (method == FileIngestionMethod::Recursive) + switch (method) { + case FileIngestionMethod::Recursive: { dumpPath(srcPath, sink, filter); - else + break; + } + case FileIngestionMethod::Git: { + // recursively add to store if path is a directory + struct stat st; + if (lstat(srcPath.c_str(), &st)) + throw SysError(format("getting attributes of path '%1%'") % srcPath); + if (S_ISDIR(st.st_mode)) + for (auto & i : readDirectory(srcPath)) + addToStore(i.name, srcPath + "/" + i.name, method, hashAlgo, filter, repair); + + dumpGit(hashAlgo, srcPath, sink, filter); + break; + } + case FileIngestionMethod::Flat: { sink.s = make_ref(readFile(srcPath)); + break; + } + } return addToStoreFromDump(*sink.s, name, method, hashAlgo, repair); } diff --git a/src/libstore/remote-store.cc b/src/libstore/remote-store.cc index 4425ab9f07c..fbc161cc92c 100644 --- a/src/libstore/remote-store.cc +++ b/src/libstore/remote-store.cc @@ -8,6 +8,7 @@ #include "derivations.hh" #include "pool.hh" #include "finally.hh" +#include "git.hh" #include #include @@ -488,17 +489,28 @@ StorePath RemoteStore::addToStore(const string & name, const Path & _srcPath, { if (repair) throw Error("repairing is not supported when building through the Nix daemon"); - if (method == FileIngestionMethod::Git) throw Error("cannot remotely add to store using the git file ingestion method"); - - auto conn(getConnection()); + if (method == FileIngestionMethod::Git && hashAlgo != htSHA1) + throw Error("git ingestion must use sha1 hash"); Path srcPath(absPath(_srcPath)); + // recursively add to store if path is a directory + if (method == FileIngestionMethod::Git) { + struct stat st; + if (lstat(srcPath.c_str(), &st)) + throw SysError(format("getting attributes of path '%1%'") % srcPath); + if (S_ISDIR(st.st_mode)) + for (auto & i : readDirectory(srcPath)) + addToStore(i.name, srcPath + "/" + i.name, method, hashAlgo, filter, repair); + } + + auto conn(getConnection()); + conn->to << wopAddToStore << name << ((hashAlgo == htSHA256 && method == FileIngestionMethod::Recursive) ? 0 : 1) /* backwards compatibility hack */ - << (method == FileIngestionMethod::Recursive ? 1 : 0) + << (uint8_t) method << printHashType(hashAlgo); try { @@ -507,7 +519,10 @@ StorePath RemoteStore::addToStore(const string & name, const Path & _srcPath, connections->incCapacity(); { Finally cleanup([&]() { connections->decCapacity(); }); - dumpPath(srcPath, conn->to, filter); + if (method == FileIngestionMethod::Git) + dumpGit(hashAlgo, srcPath, conn->to, filter); + else + dumpPath(srcPath, conn->to, filter); } conn->to.warn = false; conn.processStderr(); diff --git a/src/libstore/store-api.cc b/src/libstore/store-api.cc index f880cd5034c..39c4009b5a0 100644 --- a/src/libstore/store-api.cc +++ b/src/libstore/store-api.cc @@ -178,6 +178,9 @@ StorePath Store::makeFixedOutputPath( const StorePathSet & references, bool hasSelfReference) const { + if (method == FileIngestionMethod::Git && hash.type != htSHA1) + throw Error("Git file ingestion must use sha1 hash"); + if (hash.type == htSHA256 && method == FileIngestionMethod::Recursive) { return makeStorePath(makeType(*this, "source", references, hasSelfReference), hash, name); } else { @@ -206,9 +209,21 @@ StorePath Store::makeTextPath(std::string_view name, const Hash & hash, std::pair Store::computeStorePathForPath(std::string_view name, const Path & srcPath, FileIngestionMethod method, HashType hashAlgo, PathFilter & filter) const { - Hash h = method == FileIngestionMethod::Recursive - ? hashPath(hashAlgo, srcPath, filter).first - : hashFile(hashAlgo, srcPath); + Hash h; + switch (method) { + case FileIngestionMethod::Recursive: { + h = hashPath(hashAlgo, srcPath, filter).first; + break; + } + case FileIngestionMethod::Git: { + h = hashGit(hashAlgo, srcPath, filter).first; + break; + } + case FileIngestionMethod::Flat: { + h = hashFile(hashAlgo, srcPath); + break; + } + } return std::make_pair(makeFixedOutputPath(method, h, name), h); } @@ -838,6 +853,8 @@ Strings ValidPathInfo::shortRefs() const std::string makeFixedOutputCA(FileIngestionMethod method, const Hash & hash) { + if (method == FileIngestionMethod::Git && hash.type != htSHA1) + throw Error("git file ingestion must use sha1 hashes"); return "fixed:" + ingestionMethodPrefix(method) + hash.to_string(); } diff --git a/src/libutil/fs-sink.hh b/src/libutil/fs-sink.hh index d3d1e43b79f..efd13685df3 100644 --- a/src/libutil/fs-sink.hh +++ b/src/libutil/fs-sink.hh @@ -13,11 +13,15 @@ struct ParseSink virtual void createDirectory(const Path & path) { }; virtual void createRegularFile(const Path & path) { }; + virtual void createExecutableFile(const Path & path) { }; virtual void isExecutable() { }; virtual void preallocateContents(unsigned long long size) { }; virtual void receiveContents(unsigned char * data, unsigned int len) { }; virtual void createSymlink(const Path & path, const string & target) { }; + + virtual void copyFile(const Path & source) { }; + virtual void copyDirectory(const Path & source, const Path & destination) { }; }; struct RestoreSink : ParseSink @@ -39,6 +43,13 @@ struct RestoreSink : ParseSink if (!fd) throw SysError(format("creating file '%1%'") % p); } + void createExecutableFile(const Path & path) + { + Path p = dstPath + path; + fd = open(p.c_str(), O_CREAT | O_EXCL | O_WRONLY | O_CLOEXEC, 0777); + if (!fd) throw SysError(format("creating file '%1%'") % p); + } + void isExecutable() { struct stat st; @@ -73,6 +84,31 @@ struct RestoreSink : ParseSink Path p = dstPath + path; nix::createSymlink(target, p); } + + void copyFile(const Path & source) + { + FdSink sink(fd.get()); + readFile(source, sink); + } + + void copyDirectory(const Path & source, const Path & destination) + { + Path p = dstPath + destination; + createDirectory(destination); + for (auto & i : readDirectory(source)) { + struct stat st; + Path entry = source + "/" + i.name; + if (lstat(entry.c_str(), &st)) + throw SysError(format("getting attributes of path '%1%'") % entry); + if (S_ISREG(st.st_mode)) { + createRegularFile(destination + "/" + i.name); + copyFile(entry); + } else if (S_ISDIR(st.st_mode)) + copyDirectory(entry, destination + "/" + i.name); + else + throw Error(format("Unknown file: %s") % entry); + } + } }; diff --git a/src/libutil/git.cc b/src/libutil/git.cc index 7e8f25b3e14..3e613949a26 100644 --- a/src/libutil/git.cc +++ b/src/libutil/git.cc @@ -16,40 +16,127 @@ #include "hash.hh" #include "git.hh" +#include "serialise.hh" using namespace std::string_literals; namespace nix { -static void parse(ParseSink & sink, Source & source, const Path & path); +static void parse(ParseSink & sink, Source & source, const Path & path, const Path & realStoreDir, const Path & storeDir); // Converts a Path to a ParseSink -void restoreGit(const Path & path, Source & source) { - +void restoreGit(const Path & path, Source & source, const Path & realStoreDir, const Path & storeDir) { RestoreSink sink; sink.dstPath = path; - parseGit(sink, source); - + parseGit(sink, source, realStoreDir, storeDir); } -void parseGit(ParseSink & sink, Source & source) { - parse(sink, source, ""); +void parseGit(ParseSink & sink, Source & source, const Path & realStoreDir, const Path & storeDir) +{ + parse(sink, source, "", realStoreDir, storeDir); } -static void parse(ParseSink & sink, Source & source, const Path & path) { - uint8_t buf[4]; - - std::basic_string_view buf_v { - (const uint8_t *) & buf, - std::size(buf) - }; - source(buf_v); - if (buf_v.compare((const uint8_t *)"blob")) { - uint8_t space; - source(& space, 1); - } - else { +static string getStringUntil(Source & source, char byte) +{ + string s; + unsigned char n[1]; + source(n, 1); + while (*n != byte) { + s += *n; + source(n, 1); } + return s; +} + +static string getString(Source & source, int n) +{ + std::vector v(n); + source(v.data(), n); + return std::string(v.begin(), v.end()); +} + +// Unfortunately, no access to libstore headers here. +static string getStoreEntry(const Path & storeDir, Hash hash, string name) +{ + Hash hash1 = hashString(htSHA256, "fixed:out:git:" + hash.to_string(Base16) + ":"); + Hash hash2 = hashString(htSHA256, "output:out:" + hash1.to_string(Base16) + ":" + storeDir + ":" + name); + Hash hash3 = compressHash(hash2, 20); + + return hash3.to_string(Base32, false) + "-" + name; +} + +static void parse(ParseSink & sink, Source & source, const Path & path, const Path & realStoreDir, const Path & storeDir) +{ + auto type = getString(source, 5); + + if (type == "blob ") { + sink.createRegularFile(path); + + unsigned long long size = std::stoi(getStringUntil(source, 0)); + + sink.preallocateContents(size); + + unsigned long long left = size; + std::vector buf(65536); + + while (left) { + checkInterrupt(); + auto n = buf.size(); + if ((unsigned long long)n > left) n = left; + source(buf.data(), n); + sink.receiveContents(buf.data(), n); + left -= n; + } + } else if (type == "tree ") { + unsigned long long size = std::stoi(getStringUntil(source, 0)); + unsigned long long left = size; + + sink.createDirectory(path); + + while (left) { + string perms = getStringUntil(source, ' '); + left -= perms.size(); + left -= 1; + + int perm = std::stoi(perms); + if (perm != 100644 && perm != 100755 && perm != 644 && perm != 755 && perm != 40000) + throw Error(format("Unknown Git permission: %d") % perm); + + string name = getStringUntil(source, 0); + left -= name.size(); + left -= 1; + + string hashs = getString(source, 20); + left -= 20; + + Hash hash(htSHA1); + std::copy(hashs.begin(), hashs.end(), hash.hash); + + string entryName = getStoreEntry(storeDir, hash, name); + Path entry = absPath(realStoreDir + "/" + entryName); + + struct stat st; + if (lstat(entry.c_str(), &st)) + throw SysError(format("getting attributes of path '%1%'") % entry); + + if (S_ISREG(st.st_mode)) { + if (perm == 40000) + throw SysError(format("file is a file but expected to be a directory '%1%'") % entry); + + if (perm == 100755 || perm == 755) + sink.createExecutableFile(path + "/" + name); + else + sink.createRegularFile(path + "/" + name); + + sink.copyFile(entry); + } else if (S_ISDIR(st.st_mode)) { + if (perm != 40000) + throw SysError(format("file is a directory but expected to be a file '%1%'") % entry); + + sink.copyDirectory(realStoreDir + "/" + entryName, path + "/" + name); + } else throw Error(format("file '%1%' has an unsupported type") % entry); + } + } else throw Error("input doesn't look like a Git object"); } // TODO stream file into sink, rather than reading into vector @@ -75,7 +162,7 @@ GitMode dumpGitTree(const GitTree & entries, Sink & sink) 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(); + s1 += (format("%06d %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(); @@ -86,39 +173,45 @@ GitMode dumpGitTree(const GitTree & entries, Sink & sink) return GitMode::Directory; } -// Returns the perm in addition -std::pair dumpGitHashInternal( - std::function()> makeHashSink, - const Path & path, PathFilter & filter) +static std::pair dumpGitHashInternal(HashType ht, const Path & path, PathFilter & filter); + +static GitMode dumpGitInternal(HashType ht, const Path & path, Sink & sink, 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)) { - perm = dumpGitBlob(path, st, *hashSink); - } else if (S_ISDIR(st.st_mode)) { + if (S_ISREG(st.st_mode)) + perm = dumpGitBlob(path, st, sink); + else if (S_ISDIR(st.st_mode)) { GitTree entries; for (auto & i : readDirectory(path)) - if (filter(path + "/" + i.name)) { - entries[i.name] = dumpGitHashInternal(makeHashSink, path + "/" + i.name, filter); - } - perm = dumpGitTree(entries, *hashSink); - } else { - throw Error(format("file '%1%' has an unsupported type") % path); - } + if (filter(path + "/" + i.name)) + entries[i.name] = dumpGitHashInternal(ht, path + "/" + i.name, filter); + perm = dumpGitTree(entries, sink); + } else throw Error(format("file '%1%' has an unsupported type") % path); + + return perm; +} + +static std::pair dumpGitHashInternal(HashType ht, const Path & path, PathFilter & filter) +{ + auto hashSink = new HashSink(ht); + auto perm = dumpGitInternal(ht, path, *hashSink, filter); auto hash = hashSink->finish().first; return std::pair { perm, hash }; } -Hash dumpGitHash( - std::function()> makeHashSink, - const Path & path, PathFilter & filter) +Hash dumpGitHash(HashType ht, const Path & path, PathFilter & filter) +{ + return dumpGitHashInternal(ht, path, filter).second; +} + +void dumpGit(HashType ht, const Path & path, Sink & sink, PathFilter & filter) { - return dumpGitHashInternal(makeHashSink, path, filter).second; + dumpGitInternal(ht, path, sink, filter); } } diff --git a/src/libutil/git.hh b/src/libutil/git.hh index 7b11f72be29..06a56d55b70 100644 --- a/src/libutil/git.hh +++ b/src/libutil/git.hh @@ -12,9 +12,9 @@ enum struct GitMode { Regular, }; -void restoreGit(const Path & path, Source & source); +void restoreGit(const Path & path, Source & source, const Path & realStoreDir, const Path & storeDir); -void parseGit(ParseSink & sink, Source & source); +void parseGit(ParseSink & sink, Source & source, const Path & realStoreDir, const Path & storeDir); // Dumps a single file to a sink GitMode dumpGitBlob(const Path & path, const struct stat st, Sink & sink); @@ -25,11 +25,8 @@ typedef std::map> GitTree; GitMode dumpGitTree(const GitTree & entries, Sink & sink); // Recursively dumps path, hashing as we go -Hash dumpGitHash( - std::function()>, - const Path & path, - PathFilter & filter = defaultPathFilter); +Hash dumpGitHash(HashType ht, const Path & path, PathFilter & filter = defaultPathFilter); + +void dumpGit(HashType ht, const Path & path, Sink & sink, 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. } diff --git a/src/libutil/hash.cc b/src/libutil/hash.cc index 7caee1da7f8..b3f3db23178 100644 --- a/src/libutil/hash.cc +++ b/src/libutil/hash.cc @@ -6,6 +6,7 @@ #include "hash.hh" #include "archive.hh" +#include "git.hh" #include "util.hh" #include "istringstream_nocopy.hh" @@ -307,6 +308,13 @@ HashResult hashPath( return sink.finish(); } +HashResult hashGit( + HashType ht, const Path & path, PathFilter & filter) +{ + HashSink sink(ht); + dumpGit(ht, path, sink, filter); + return sink.finish(); +} Hash compressHash(const Hash & hash, unsigned int newSize) { diff --git a/src/libutil/hash.hh b/src/libutil/hash.hh index ea9fca3e74f..1af94c2b47c 100644 --- a/src/libutil/hash.hh +++ b/src/libutil/hash.hh @@ -110,6 +110,9 @@ typedef std::pair HashResult; HashResult hashPath(HashType ht, const Path & path, PathFilter & filter = defaultPathFilter); +HashResult hashGit(HashType ht, const Path & path, + PathFilter & filter = defaultPathFilter); + /* Compress a hash to the specified number of bytes by cyclically XORing bytes together. */ Hash compressHash(const Hash & hash, unsigned int newSize); diff --git a/src/nix/add-to-store.cc b/src/nix/add-to-store.cc index f43f774c1c8..c96a16cebc8 100644 --- a/src/nix/add-to-store.cc +++ b/src/nix/add-to-store.cc @@ -2,6 +2,7 @@ #include "common-args.hh" #include "store-api.hh" #include "archive.hh" +#include "git.hh" using namespace nix; @@ -9,6 +10,7 @@ struct CmdAddToStore : MixDryRun, StoreCommand { Path path; std::optional namePart; + bool git = false; CmdAddToStore() { @@ -21,6 +23,12 @@ struct CmdAddToStore : MixDryRun, StoreCommand .labels = {"name"}, .handler = {&namePart}, }); + + addFlag({ + .longName = "git", + .description = "treat path as a git object", + .handler = {&this->git, true}, + }); } std::string description() override @@ -40,19 +48,23 @@ struct CmdAddToStore : MixDryRun, StoreCommand { if (!namePart) namePart = baseNameOf(path); + auto ingestionMethod = git ? FileIngestionMethod::Git : FileIngestionMethod::Recursive; + StringSink sink; dumpPath(path, sink); auto narHash = hashString(htSHA256, *sink.s); + auto hash = git ? dumpGitHash(htSHA1, path) : narHash; - ValidPathInfo info(store->makeFixedOutputPath(FileIngestionMethod::Recursive, narHash, *namePart)); + ValidPathInfo info(store->makeFixedOutputPath(ingestionMethod, hash, *namePart)); info.narHash = narHash; info.narSize = sink.s->size(); - info.ca = makeFixedOutputCA(FileIngestionMethod::Recursive, info.narHash); + info.ca = makeFixedOutputCA(ingestionMethod, hash); if (!dryRun) { - auto source = StringSource { *sink.s }; - store->addToStore(info, source); + auto addedPath = store->addToStore(*namePart, path, ingestionMethod, git ? htSHA1 : htSHA256); + if (addedPath != info.path) + throw Error(format("Added path %s does not match calculated path %s; something has changed") % addedPath.to_string() % info.path.to_string()); } logger->stdout("%s", store->printStorePath(info.path)); diff --git a/src/nix/hash.cc b/src/nix/hash.cc index 3a8c82da17b..41a66676967 100644 --- a/src/nix/hash.cc +++ b/src/nix/hash.cc @@ -57,34 +57,29 @@ struct CmdHash : Command { for (auto path : paths) { - auto makeHashSink = [&]() -> std::unique_ptr { - std::unique_ptr t; - if (modulus) - t = std::make_unique(ht, *modulus); - else - t = std::make_unique(ht); - return t; - }; + std::unique_ptr hashSink; + if (modulus) + hashSink = std::make_unique(ht, *modulus); + else + hashSink = std::make_unique(ht); Hash h; switch (mode) { case FileIngestionMethod::Flat: { - auto hashSink = makeHashSink(); readFile(path, *hashSink); - h = hashSink->finish().first; break; } case FileIngestionMethod::Recursive: { - auto hashSink = makeHashSink(); dumpPath(path, *hashSink); - h = hashSink->finish().first; break; } case FileIngestionMethod::Git: - h = dumpGitHash(makeHashSink, path); + dumpGit(ht, path, *hashSink); break; } + h = hashSink->finish().first; + if (truncate && h.hashSize > 20) h = compressHash(h, 20); logger->stdout(h.to_string(base, base == SRI)); } diff --git a/tests/git.sh b/tests/git.sh new file mode 100644 index 00000000000..e700dcc785c --- /dev/null +++ b/tests/git.sh @@ -0,0 +1,36 @@ +source common.sh + +try () { + hash=$(nix hash-git --base16 --type sha1 $TEST_ROOT/hash-path) + if test "$hash" != "$1"; then + echo "git hash, expected $1, got $hash" + exit 1 + fi +} + +rm -rf $TEST_ROOT/hash-path +mkdir $TEST_ROOT/hash-path +echo "Hello World" > $TEST_ROOT/hash-path/hello + +try "117c62a8c5e01758bd284126a6af69deab9dbbe2" + +rm -rf $TEST_ROOT/dummy1 +echo Hello World! > $TEST_ROOT/dummy1 +path1=$(nix add-to-store --git $TEST_ROOT/dummy1) +hash1=$(nix-store -q --hash $path1) +test "$hash1" = "sha256:1brffhvj2c0z6x8qismd43m0iy8dsgfmy10bgg9w11szway2wp9v" + +rm -rf $TEST_ROOT/dummy2 +mkdir -p $TEST_ROOT/dummy2 +echo Hello World! > $TEST_ROOT/dummy2/hello +path2=$(nix add-to-store --git $TEST_ROOT/dummy2) +hash2=$(nix-store -q --hash $path2) +test "$hash2" = "sha256:1vhv7zxam7x277q0y0jcypm7hwhccbzss81vkdgf0ww5sm2am4y0" + +rm -rf $TEST_ROOT/dummy3 +mkdir -p $TEST_ROOT/dummy3 +mkdir -p $TEST_ROOT/dummy3/hello +echo Hello World! > $TEST_ROOT/dummy3/hello/hello +path3=$(nix add-to-store --git $TEST_ROOT/dummy3) +hash3=$(nix-store -q --hash $path3) +test "$hash3" = "sha256:1i2x80840igikhbyy7nqf08ymx3a6n83x1fzyrxvddf0sdl5nqvp" diff --git a/tests/hash.sh b/tests/hash.sh index 33b5bef7595..4cfc9790101 100644 --- a/tests/hash.sh +++ b/tests/hash.sh @@ -85,18 +85,3 @@ try3() { try3 sha1 "800d59cfcd3c05e900cb4e214be48f6b886a08df" "vw46m23bizj4n8afrc0fj19wrp7mj3c0" "gA1Zz808BekAy04hS+SPa4hqCN8=" try3 sha256 "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad" "1b8m03r63zqhnjf7l5wnldhh7c134ap5vpj0850ymkq1iyzicy5s" "ungWv48Bz+pBQUDeXa4iI7ADYaOWF3qctBD/YfIAFa0=" try3 sha512 "204a8fc6dda82f0a0ced7beb8e08a41657c16ef468b228a8279be331a703c33596fd15c13b1b07f9aa1d3bea57789ca031ad85c7a71dd70354ec631238ca3445" "12k9jiq29iyqm03swfsgiw5mlqs173qazm3n7daz43infy12pyrcdf30fkk3qwv4yl2ick8yipc2mqnlh48xsvvxl60lbx8vp38yji0" "IEqPxt2oLwoM7XvrjgikFlfBbvRosiioJ5vjMacDwzWW/RXBOxsH+aodO+pXeJygMa2Fx6cd1wNU7GMSOMo0RQ==" - -# Git. -try4 () { - hash=$(nix hash-git --base16 --type sha1 $TEST_ROOT/hash-path) - if test "$hash" != "$1"; then - echo "git hash, expected $1, got $hash" - exit 1 - fi -} - -rm -rf $TEST_ROOT/hash-path -mkdir $TEST_ROOT/hash-path -echo "Hello World" > $TEST_ROOT/hash-path/hello - -try4 "117c62a8c5e01758bd284126a6af69deab9dbbe2" diff --git a/tests/local.mk b/tests/local.mk index 56e5640ca33..35a80a16a5a 100644 --- a/tests/local.mk +++ b/tests/local.mk @@ -30,7 +30,8 @@ nix_tests = \ nix-copy-ssh.sh \ post-hook.sh \ function-trace.sh \ - recursive.sh + recursive.sh \ + git.sh # parallel.sh install-tests += $(foreach x, $(nix_tests), tests/$(x))