implement commit-tree command
This commit is contained in:
parent
f7e0105ba1
commit
aebdb21d84
|
|
@ -387,4 +387,4 @@ Makefile
|
||||||
|
|
||||||
.idea/
|
.idea/
|
||||||
|
|
||||||
cmake*/
|
cmake-build*/
|
||||||
|
|
@ -8,6 +8,14 @@ FetchContent_Declare(ZStrGitRepo
|
||||||
)
|
)
|
||||||
FetchContent_MakeAvailable(ZStrGitRepo) # defines INTERFACE target 'zstr::zstr'
|
FetchContent_MakeAvailable(ZStrGitRepo) # defines INTERFACE target 'zstr::zstr'
|
||||||
|
|
||||||
|
set(BUILD_TZ_LIB ON)# timezone support
|
||||||
|
|
||||||
|
FetchContent_Declare( date_src
|
||||||
|
GIT_REPOSITORY https://gitlab.com/JosephA1997/date.git
|
||||||
|
GIT_TAG "master"
|
||||||
|
)
|
||||||
|
FetchContent_MakeAvailable(date_src)
|
||||||
|
|
||||||
set(CMAKE_CXX_STANDARD 23)
|
set(CMAKE_CXX_STANDARD 23)
|
||||||
|
|
||||||
add_executable(basic-vcs src/main.cpp
|
add_executable(basic-vcs src/main.cpp
|
||||||
|
|
@ -24,7 +32,10 @@ add_executable(basic-vcs src/main.cpp
|
||||||
src/commands/hash-object.cpp
|
src/commands/hash-object.cpp
|
||||||
include/hash-object.h
|
include/hash-object.h
|
||||||
src/commands/clone.cpp
|
src/commands/clone.cpp
|
||||||
include/clone.h)
|
include/clone.h
|
||||||
|
src/commands/commit-tree.cpp
|
||||||
|
include/commit-tree.h
|
||||||
|
include/commit-tree.h)
|
||||||
|
|
||||||
target_link_libraries(basic-vcs zstr::zstr)
|
target_link_libraries(basic-vcs PRIVATE zstr::zstr date::date date::date-tz)
|
||||||
target_include_directories(basic-vcs PRIVATE include)
|
target_include_directories(basic-vcs PRIVATE include)
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
int commitTree(int argc, const char** argv);
|
||||||
|
|
@ -28,6 +28,7 @@ int catFile(const int argc, const char** argv)
|
||||||
const std::string file = input.substr(2);
|
const std::string file = input.substr(2);
|
||||||
|
|
||||||
zstr::ifstream inputFile("./.git/objects/" + dir + "/" + file, std::ofstream::binary);
|
zstr::ifstream inputFile("./.git/objects/" + dir + "/" + file, std::ofstream::binary);
|
||||||
|
|
||||||
if (!inputFile.is_open())
|
if (!inputFile.is_open())
|
||||||
{
|
{
|
||||||
std::cerr << "Could not open file\n";
|
std::cerr << "Could not open file\n";
|
||||||
|
|
@ -36,6 +37,7 @@ int catFile(const int argc, const char** argv)
|
||||||
|
|
||||||
const std::string output{std::istreambuf_iterator<char>(inputFile), std::istreambuf_iterator<char>()};
|
const std::string output{std::istreambuf_iterator<char>(inputFile), std::istreambuf_iterator<char>()};
|
||||||
std::cout << std::string_view(output).substr(output.find('\0') + 1);
|
std::cout << std::string_view(output).substr(output.find('\0') + 1);
|
||||||
|
|
||||||
inputFile.close();
|
inputFile.close();
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,127 @@
|
||||||
|
#include "commit-tree.h"
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <filesystem>
|
||||||
|
#include <fstream>
|
||||||
|
#include <string>
|
||||||
|
#include <string_view>
|
||||||
|
#include <chrono>
|
||||||
|
|
||||||
|
#include "sha1.hpp"
|
||||||
|
#include "zstr.hpp"
|
||||||
|
#include "date/tz.h"
|
||||||
|
|
||||||
|
int commitTree(int argc, const char** argv)
|
||||||
|
{
|
||||||
|
const std::string_view firstFlag = argv[3];
|
||||||
|
if (firstFlag != "-m" and firstFlag != "-p")
|
||||||
|
{
|
||||||
|
std::cerr << "\n\nplease use '-p' to specify the parent commit, or '-m' to specify initial commit message" << "\n\n";
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool initialCommit = false;
|
||||||
|
if (firstFlag == "-m")
|
||||||
|
initialCommit = true;
|
||||||
|
|
||||||
|
std::string commitHeader;
|
||||||
|
std::string commitBody;
|
||||||
|
std::string authorName;
|
||||||
|
std::string authorEmail;
|
||||||
|
std::string_view commitMessage;
|
||||||
|
std::string_view treeSha = argv[2];
|
||||||
|
|
||||||
|
commitBody.append("tree ");
|
||||||
|
commitBody.append(treeSha);
|
||||||
|
commitBody.append("\n");
|
||||||
|
|
||||||
|
if (!initialCommit)
|
||||||
|
{
|
||||||
|
const std::string_view parentSha = argv[4];
|
||||||
|
commitBody.append("parent ");
|
||||||
|
commitBody.append(parentSha).append("\n");
|
||||||
|
commitMessage = argv[6];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
commitMessage = argv[4];
|
||||||
|
}
|
||||||
|
|
||||||
|
// get author info
|
||||||
|
if (std::filesystem::exists("./.git/author.txt"))
|
||||||
|
{
|
||||||
|
std::ifstream authorInfo("./.git/author.txt");
|
||||||
|
if (!authorInfo)
|
||||||
|
{
|
||||||
|
std::cerr << "\n\n'/.git/author.txt exists but could not be opened\n\n";
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
//use getline() to include spaces between first and last name
|
||||||
|
std::getline(authorInfo, authorName);
|
||||||
|
std::getline(authorInfo, authorEmail);
|
||||||
|
authorInfo.close();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//use getline() to include spaces between first and last name
|
||||||
|
std::cout << "please enter the name of the author/commiter: ";
|
||||||
|
std::getline(std::cin, authorName);
|
||||||
|
std::cout << "please enter the email of the author/commiter: ";
|
||||||
|
std::getline(std::cin, authorEmail);
|
||||||
|
|
||||||
|
char input{};
|
||||||
|
do
|
||||||
|
{
|
||||||
|
std::cout << "would you like to save this information for future commits? (Y/N): ";
|
||||||
|
std::cin >> input;
|
||||||
|
input = std::toupper(input);
|
||||||
|
} while (input != 'Y' and input != 'N');
|
||||||
|
|
||||||
|
if (input == 'Y')
|
||||||
|
{
|
||||||
|
std::ofstream authorFile("./.git/author.txt");
|
||||||
|
if (!authorFile)
|
||||||
|
{
|
||||||
|
std::cerr << "\n\ncould not write author info to '/.git/author.txt'\n\n";
|
||||||
|
authorFile.close();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
authorFile << authorName << "\n" << authorEmail << "\n";
|
||||||
|
authorFile.close();
|
||||||
|
std::cout << "successfully wrote author info to '/.git/author.txt\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}// end get author info
|
||||||
|
|
||||||
|
auto timeZoneLocal = date::make_zoned(date::current_zone(), std::chrono::system_clock::now());
|
||||||
|
std::ostringstream tzOffset;
|
||||||
|
tzOffset << timeZoneLocal.get_info().offset;
|
||||||
|
|
||||||
|
//assume commiter and author are the same
|
||||||
|
commitBody.append("author " + authorName + " <" + authorEmail + "> " + std::to_string(timeZoneLocal.get_local_time().time_since_epoch().count()) + " " + tzOffset.str().substr(0,5) + "\n") ;//use substr() to exclude the char that defines the unit of measurement
|
||||||
|
commitBody.append("commiter " + authorName + " <" + authorEmail + "> " + std::to_string(timeZoneLocal.get_local_time().time_since_epoch().count()) + " " + tzOffset.str().substr(0,5) + "\n\n") ;
|
||||||
|
commitBody.append(commitMessage);
|
||||||
|
|
||||||
|
commitHeader.append("commit " + std::to_string(commitBody.size()) + '\0');
|
||||||
|
|
||||||
|
std::string commitObject = commitHeader + commitBody;
|
||||||
|
|
||||||
|
SHA1 checksum;
|
||||||
|
checksum.update(commitObject);
|
||||||
|
const std::string hash = checksum.final();
|
||||||
|
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(commitObject.c_str(), commitObject.size());
|
||||||
|
|
||||||
|
outputFile.close();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
@ -24,6 +24,7 @@ int hashObject(const int argc, const char** argv)
|
||||||
std::cerr << flag << "is an invalid flag, please use \"-w\"\n";
|
std::cerr << flag << "is an invalid flag, please use \"-w\"\n";
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::string path = argv[3];
|
const std::string path = argv[3];
|
||||||
std::ifstream inputFile("./" + path);
|
std::ifstream inputFile("./" + path);
|
||||||
if (!inputFile.is_open())
|
if (!inputFile.is_open())
|
||||||
|
|
@ -40,9 +41,14 @@ int hashObject(const int argc, const char** argv)
|
||||||
const std::string hash = checksum.final();
|
const std::string hash = checksum.final();
|
||||||
std::cout << hash << "\n";
|
std::cout << hash << "\n";
|
||||||
|
|
||||||
std::filesystem::create_directory("./.git/objects/" + hash.substr(0,2));
|
const std::string hashDir = "./.git/objects/" + hash.substr(0,2);
|
||||||
|
|
||||||
zstr::ofstream outputFile("./.git/objects/" + hash.substr(0,2) + "/" + hash.substr(2));
|
if (!std::filesystem::exists(hashDir))
|
||||||
|
{
|
||||||
|
std::filesystem::create_directory(hashDir);
|
||||||
|
}
|
||||||
|
|
||||||
|
zstr::ofstream outputFile(hashDir + "/" + hash.substr(2));
|
||||||
outputFile.write(objectData.c_str(), objectData.size());
|
outputFile.write(objectData.c_str(), objectData.size());
|
||||||
|
|
||||||
inputFile.close();
|
inputFile.close();
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,7 @@ int lsTree(const int argc, const char** argv)
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::string output{std::istreambuf_iterator<char>(inputFile), std::istreambuf_iterator<char>()};
|
const std::string output{std::istreambuf_iterator<char>(inputFile), std::istreambuf_iterator<char>()};
|
||||||
|
|
||||||
//start right after tree <size>\0
|
//start right after tree <size>\0
|
||||||
size_t startChar = output.find('\0') + 1;
|
size_t startChar = output.find('\0') + 1;
|
||||||
size_t nextNullChar = output.find('\0', startChar);
|
size_t nextNullChar = output.find('\0', startChar);
|
||||||
|
|
@ -59,16 +60,18 @@ int lsTree(const int argc, const char** argv)
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const auto& value : hash)
|
for (const auto& value : hash)
|
||||||
std::printf("%02x", static_cast<unsigned char>(value)); // simpler solution
|
std::printf("%02x", static_cast<unsigned char>(value)); // easy way to display raw bytes as hex chars
|
||||||
|
|
||||||
std::cout << " ";
|
std::cout << " ";
|
||||||
}
|
}
|
||||||
|
|
||||||
std::cout << name << "\n";
|
std::cout << name << "\n";
|
||||||
|
|
||||||
startChar = nextNullChar + 21;
|
startChar = nextNullChar + 21;
|
||||||
nextNullChar = output.find('\0', startChar);
|
nextNullChar = output.find('\0', startChar);
|
||||||
}
|
}
|
||||||
|
|
||||||
inputFile.close();
|
inputFile.close();
|
||||||
return 0;
|
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,27 +16,27 @@ struct node
|
||||||
std::string hash;
|
std::string hash;
|
||||||
};
|
};
|
||||||
|
|
||||||
uint8_t hexToRaw(char firstDigit, char secondDigit)
|
uint8_t hexToRaw(const char firstDigit, const char secondDigit)
|
||||||
{
|
{
|
||||||
uint8_t result = 0;
|
uint8_t result = 0;
|
||||||
switch (firstDigit)
|
switch (firstDigit)
|
||||||
{
|
{
|
||||||
case '0': result = 0; break;
|
case '0': result = 0x0; break;
|
||||||
case '1': result = 1 << 4; break;
|
case '1': result = 0x1 << 4; break;
|
||||||
case '2': result = 2 << 4; break;
|
case '2': result = 0x2 << 4; break;
|
||||||
case '3': result = 3 << 4; break;
|
case '3': result = 0x3 << 4; break;
|
||||||
case '4': result = 4 << 4; break;
|
case '4': result = 0x4 << 4; break;
|
||||||
case '5': result = 5 << 4; break;
|
case '5': result = 0x5 << 4; break;
|
||||||
case '6': result = 6 << 4; break;
|
case '6': result = 0x6 << 4; break;
|
||||||
case '7': result = 7 << 4; break;
|
case '7': result = 0x7 << 4; break;
|
||||||
case '8': result = 8 << 4; break;
|
case '8': result = 0x8 << 4; break;
|
||||||
case '9': result = 9 << 4; break;
|
case '9': result = 0x9 << 4; break;
|
||||||
case 'a': result = 10 << 4; break;
|
case 'a': result = 0xa << 4; break;
|
||||||
case 'b': result = 11 << 4; break;
|
case 'b': result = 0xb << 4; break;
|
||||||
case 'c': result = 12 << 4; break;
|
case 'c': result = 0xc << 4; break;
|
||||||
case 'd': result = 13 << 4; break;
|
case 'd': result = 0xd << 4; break;
|
||||||
case 'e': result = 14 << 4; break;
|
case 'e': result = 0xe << 4; break;
|
||||||
case 'f': result = 15 << 4; break;
|
case 'f': result = 0xf << 4; break;
|
||||||
default: result = 0; break;
|
default: result = 0; break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -72,7 +72,7 @@ std::string hashDir(const std::string& path)
|
||||||
for (const auto& object : std::filesystem::directory_iterator(path))
|
for (const auto& object : std::filesystem::directory_iterator(path))
|
||||||
{
|
{
|
||||||
if (object.is_directory())
|
if (object.is_directory())
|
||||||
{
|
{//recurse into the directory
|
||||||
nodes.emplace_back(object.path().filename().string(), 40000, hashDir(object.path().string()));
|
nodes.emplace_back(object.path().filename().string(), 40000, hashDir(object.path().string()));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
@ -82,7 +82,9 @@ std::string hashDir(const std::string& path)
|
||||||
|
|
||||||
SHA1 checksum;
|
SHA1 checksum;
|
||||||
checksum.update("blob " + std::to_string(fileContent.size()) + '\0' + fileContent);
|
checksum.update("blob " + std::to_string(fileContent.size()) + '\0' + fileContent);
|
||||||
|
|
||||||
nodes.emplace_back(object.path().filename(), 100644, checksum.final());
|
nodes.emplace_back(object.path().filename(), 100644, checksum.final());
|
||||||
|
|
||||||
file.close();
|
file.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -102,14 +104,12 @@ std::string hashDir(const std::string& path)
|
||||||
treeObject += static_cast<char>(byte);
|
treeObject += static_cast<char>(byte);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
auto temp = treeObject.size();
|
|
||||||
|
|
||||||
treeObject = "tree " + std::to_string(treeObject.size()) + '\0' + treeObject;
|
treeObject = "tree " + std::to_string(treeObject.size()) + '\0' + treeObject;
|
||||||
|
|
||||||
SHA1 checksum;
|
SHA1 checksum;
|
||||||
checksum.update(treeObject);
|
checksum.update(treeObject);
|
||||||
|
|
||||||
return checksum.final();
|
return checksum.final();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int writeTree(const char** argv)
|
int writeTree(const char** argv)
|
||||||
|
|
@ -125,16 +125,19 @@ int writeTree(const char** argv)
|
||||||
if (object.path().filename() == ".git") continue;
|
if (object.path().filename() == ".git") continue;
|
||||||
|
|
||||||
if (object.is_directory())
|
if (object.is_directory())
|
||||||
{
|
{//recurse into the directory
|
||||||
nodes.emplace_back(object.path().filename().string(), 40000, hashDir(object.path().string()));
|
nodes.emplace_back(object.path().filename().string(), 40000, hashDir(object.path().string()));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
std::ifstream file(object.path().string());
|
std::ifstream file(object.path().string());
|
||||||
std::string fileContent {std::istreambuf_iterator<char>(file), std::istreambuf_iterator<char>()};
|
std::string fileContent {std::istreambuf_iterator<char>(file), std::istreambuf_iterator<char>()};
|
||||||
|
|
||||||
SHA1 checksum;
|
SHA1 checksum;
|
||||||
checksum.update("blob " + std::to_string(fileContent.size()) + '\0' + fileContent);
|
checksum.update("blob " + std::to_string(fileContent.size()) + '\0' + fileContent);
|
||||||
|
|
||||||
nodes.emplace_back(object.path().filename(), 100644, checksum.final());
|
nodes.emplace_back(object.path().filename(), 100644, checksum.final());
|
||||||
|
|
||||||
file.close();
|
file.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -164,9 +167,14 @@ int writeTree(const char** argv)
|
||||||
const std::string hash = checksum.final();
|
const std::string hash = checksum.final();
|
||||||
std::cout << hash << "\n";
|
std::cout << hash << "\n";
|
||||||
|
|
||||||
std::filesystem::create_directory("./.git/objects/" + hash.substr(0,2));
|
const std::string hashDir = "./.git/objects/" + hash.substr(0,2);
|
||||||
|
|
||||||
zstr::ofstream outputFile("./.git/objects/" + hash.substr(0,2) + "/" + hash.substr(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.write(treeObject.c_str(), treeObject.size());
|
||||||
|
|
||||||
outputFile.close();
|
outputFile.close();
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include "commands.h"
|
#include "commands.h"
|
||||||
|
#include "commit-tree.h"
|
||||||
#include "../include/hash-object.h"
|
#include "../include/hash-object.h"
|
||||||
|
|
||||||
int main(const int argc, const char** argv)
|
int main(const int argc, const char** argv)
|
||||||
|
|
@ -34,6 +35,10 @@ int main(const int argc, const char** argv)
|
||||||
{
|
{
|
||||||
return writeTree(argv);
|
return writeTree(argv);
|
||||||
}
|
}
|
||||||
|
else if (command == "commit-tree")
|
||||||
|
{
|
||||||
|
return commitTree(argc, argv);
|
||||||
|
}
|
||||||
else if (command == "clone")
|
else if (command == "clone")
|
||||||
{
|
{
|
||||||
return clone(argc, argv);
|
return clone(argc, argv);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue