basic-vcs/src/commands/write-tree.cpp

199 lines
5.6 KiB
C++

#include "write-tree.h"
#include <iostream>
#include <filesystem>
#include <fstream>
#include <string>
#include <string_view>
#include <vector>
#include <algorithm>
#include <iterator>
#include <cstdint>
#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;
std::string blob = "blob " + std::to_string(fileContent.size());
blob.append("\0");
blob.append(fileContent);
checksum.update(blob);
nodes.emplace_back(object.path().filename().string(), 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);
}
}
std::string header{ "tree " + std::to_string(treeObject.size()) };
header.push_back('\0');
std::string finalObject = header + treeObject;
SHA1 checksum;
checksum.update(finalObject);
return checksum.final();
}
int writeTree(const char** argv)
{
if (!std::filesystem::exists(std::filesystem::current_path() / ".git" / "objects"))
std::filesystem::create_directory(std::filesystem::current_path() / ".git" / "objects");
std::vector<node> nodes;
for (const auto& object : std::filesystem::directory_iterator(std::filesystem::current_path()))
{
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().string(), 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 < 40; i += 2)
{
uint8_t byte = hexToRaw(hash.at(i), hash.at(i+1));
treeObject += static_cast<char>(byte);
}
}
std::string header {"tree " + std::to_string(treeObject.size())};
header.push_back('\0');
std::string finalObject = header + treeObject;
SHA1 checksum;
checksum.update(finalObject);
const std::string hash = checksum.final();
std::cout << hash << "\n";
const std::filesystem::path hashDir {std::filesystem::current_path() / ".git" / "objects" / hash.substr(0,2)};
const std::filesystem::path objectFullPath {hashDir / hash.substr(2)};
if (!std::filesystem::exists(hashDir))
{
std::filesystem::create_directory(hashDir);
}
zstr::ofstream outputFile(objectFullPath.string());
outputFile.write(finalObject.c_str(), finalObject.size());
outputFile.close();
return 0;
}