184 lines
5.1 KiB
C++
184 lines
5.1 KiB
C++
#include "write-tree.h"
|
|
|
|
#include <iostream>
|
|
#include <filesystem>
|
|
#include <fstream>
|
|
#include <string>
|
|
#include <string_view>
|
|
|
|
#include "sha1.hpp"
|
|
#include "zstr.hpp"
|
|
|
|
struct node
|
|
{
|
|
std::string name;
|
|
int mode;
|
|
std::string hash;
|
|
};
|
|
|
|
uint8_t hexToRaw(const char firstDigit, const char secondDigit)
|
|
{
|
|
uint8_t result = 0;
|
|
switch (firstDigit)
|
|
{
|
|
case '0': result = 0x0; break;
|
|
case '1': result = 0x1 << 4; break;
|
|
case '2': result = 0x2 << 4; break;
|
|
case '3': result = 0x3 << 4; break;
|
|
case '4': result = 0x4 << 4; break;
|
|
case '5': result = 0x5 << 4; break;
|
|
case '6': result = 0x6 << 4; break;
|
|
case '7': result = 0x7 << 4; break;
|
|
case '8': result = 0x8 << 4; break;
|
|
case '9': result = 0x9 << 4; break;
|
|
case 'a': result = 0xa << 4; break;
|
|
case 'b': result = 0xb << 4; break;
|
|
case 'c': result = 0xc << 4; break;
|
|
case 'd': result = 0xd << 4; break;
|
|
case 'e': result = 0xe << 4; break;
|
|
case 'f': result = 0xf << 4; break;
|
|
default: result = 0; break;
|
|
}
|
|
|
|
switch (secondDigit)
|
|
{
|
|
case '0': result |= 0x0; break;
|
|
case '1': result |= 0x1; break;
|
|
case '2': result |= 0x2; break;
|
|
case '3': result |= 0x3; break;
|
|
case '4': result |= 0x4; break;
|
|
case '5': result |= 0x5; break;
|
|
case '6': result |= 0x6; break;
|
|
case '7': result |= 0x7; break;
|
|
case '8': result |= 0x8; break;
|
|
case '9': result |= 0x9; break;
|
|
case 'a': result |= 0xa; break;
|
|
case 'b': result |= 0xb; break;
|
|
case 'c': result |= 0xc; break;
|
|
case 'd': result |= 0xd; break;
|
|
case 'e': result |= 0xe; break;
|
|
case 'f': result |= 0xf; break;
|
|
default: result = 0; break;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
std::string hashDir(const std::string& path)
|
|
{
|
|
|
|
std::vector<node> nodes;
|
|
|
|
for (const auto& object : std::filesystem::directory_iterator(path))
|
|
{
|
|
if (object.is_directory())
|
|
{//recurse into the directory
|
|
nodes.emplace_back(object.path().filename().string(), 40000, hashDir(object.path().string()));
|
|
}
|
|
else
|
|
{
|
|
std::ifstream file(object.path().string());
|
|
std::string fileContent {std::istreambuf_iterator<char>(file), std::istreambuf_iterator<char>()};
|
|
|
|
SHA1 checksum;
|
|
checksum.update("blob " + std::to_string(fileContent.size()) + '\0' + fileContent);
|
|
|
|
nodes.emplace_back(object.path().filename(), 100644, checksum.final());
|
|
|
|
file.close();
|
|
}
|
|
}
|
|
|
|
std::sort(nodes.begin(), nodes.end(),
|
|
[](const auto& first, const auto& second){return first.name < second.name;});
|
|
|
|
std::string treeObject;
|
|
|
|
for (const auto&[name, mode, hash] : nodes)
|
|
{
|
|
treeObject += std::to_string(mode) + " " + name + '\0';
|
|
for (int i = 0; i < 39; i += 2)
|
|
{
|
|
uint8_t byte = hexToRaw(hash.at(i), hash.at(i+1));
|
|
|
|
treeObject += static_cast<char>(byte);
|
|
}
|
|
}
|
|
|
|
treeObject = "tree " + std::to_string(treeObject.size()) + '\0' + treeObject;
|
|
|
|
SHA1 checksum;
|
|
checksum.update(treeObject);
|
|
return checksum.final();
|
|
}
|
|
|
|
int writeTree(const char** argv)
|
|
{
|
|
if (!std::filesystem::exists("./.git/objects"))
|
|
std::filesystem::create_directory("./.git/objects");
|
|
|
|
std::vector<node> nodes;
|
|
|
|
|
|
for (const auto& object : std::filesystem::directory_iterator("./"))
|
|
{
|
|
if (object.path().filename() == ".git") continue;
|
|
|
|
if (object.is_directory())
|
|
{//recurse into the directory
|
|
nodes.emplace_back(object.path().filename().string(), 40000, hashDir(object.path().string()));
|
|
}
|
|
else
|
|
{
|
|
std::ifstream file(object.path().string());
|
|
std::string fileContent {std::istreambuf_iterator<char>(file), std::istreambuf_iterator<char>()};
|
|
|
|
SHA1 checksum;
|
|
checksum.update("blob " + std::to_string(fileContent.size()) + '\0' + fileContent);
|
|
|
|
nodes.emplace_back(object.path().filename(), 100644, checksum.final());
|
|
|
|
file.close();
|
|
}
|
|
|
|
}
|
|
|
|
std::sort(nodes.begin(), nodes.end(),
|
|
[](const auto& first, const auto& second){return first.name < second.name;});
|
|
|
|
std::string treeObject;
|
|
|
|
for (const auto&[name, mode, hash] : nodes)
|
|
{
|
|
treeObject += std::to_string(mode) + " " + name + '\0';
|
|
for (int i = 0; i < 39; i += 2)
|
|
{
|
|
uint8_t byte = hexToRaw(hash.at(i), hash.at(i+1));
|
|
|
|
treeObject += static_cast<char>(byte);
|
|
}
|
|
}
|
|
|
|
treeObject = "tree " + std::to_string(treeObject.size()) + '\0' + treeObject;
|
|
|
|
SHA1 checksum;
|
|
checksum.update(treeObject);
|
|
|
|
const std::string hash = checksum.final();
|
|
std::cout << hash << "\n";
|
|
|
|
const std::string hashDir = "./.git/objects/" + hash.substr(0,2);
|
|
|
|
if (!std::filesystem::exists(hashDir))
|
|
{
|
|
std::filesystem::create_directory(hashDir);
|
|
}
|
|
|
|
zstr::ofstream outputFile(hashDir + "/" + hash.substr(2));
|
|
outputFile.write(treeObject.c_str(), treeObject.size());
|
|
|
|
outputFile.close();
|
|
|
|
return 0;
|
|
}
|