Skip to content

Feat/implement tag #54

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
May 15, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .clang-format
Original file line number Diff line number Diff line change
@@ -1 +1 @@
InsertNewlineAtEOF: true
InsertNewlineAtEOF: true
103 changes: 49 additions & 54 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,56 +1,51 @@
cmake_minimum_required(VERSION 3.8.2)

#Set a name and a version number for your project:
project(cpp - project - template VERSION 0.0.1 LANGUAGES CXX)

set(CMAKE_AR / usr / bin / ar)

#Enable C language
enable_language(C)

#this needs to be in the top level CMakeLists.txt to enable tests
include(CTest)

set(BOOST_ENABLE_CMAKE ON) set(Boost_CMAKE_CXX_STANDARD 17) set(
CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED
ON)

set(BOOST_INCLUDE_LIBRARIES iostreams uuid algorithm)

include(FetchContent) FetchContent_Declare(
Boost GIT_REPOSITORY
https : // github.com/boostorg/boost.git
GIT_TAG boost -
1.83.0 GIT_SHALLOW
TRUE) FetchContent_MakeAvailable(Boost)

#compile the library
add_subdirectory(src)

#compile the application
add_subdirectory(app)

#optionally add doxygen target to generate documentation
option(BUILD_DOCS
"Enable building of "
"documentation (requires "
"Doxygen)" OFF) if (BUILD_DOCS)
find_package(Doxygen REQUIRED) set(
DOXYGEN_EXCLUDE_PATTERNS
"${CMAKE_SOURCE_DIR}/ext/*")
doxygen_add_docs(
doxygen ${
CMAKE_SOURCE_DIR} WORKING_DIRECTORY
${CMAKE_CURRENT_BINARY_DIR})
endif()

#compile the tests
option(BUILD_TESTS
"Enable "
"building of "
"documentation "
"(requires "
"Doxygen"
")" OFF) if (BUILD_TESTS)
add_subdirectory(
tests) endif()
# Set a name and a version number for your project:
project(
cpp-project-template
VERSION 0.0.1
LANGUAGES CXX)

set(CMAKE_AR /usr/bin/ar)

# Enable C language
enable_language(C)

# this needs to be in the top level CMakeLists.txt to enable tests
include(CTest)

set(BOOST_ENABLE_CMAKE ON)
set(Boost_CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

set(BOOST_INCLUDE_LIBRARIES iostreams uuid algorithm)

include(FetchContent)
FetchContent_Declare(
Boost
GIT_REPOSITORY https://github.com/boostorg/boost.git
GIT_TAG boost-1.83.0
GIT_SHALLOW TRUE)
FetchContent_MakeAvailable(Boost)

# compile the library
add_subdirectory(src)

# compile the application
add_subdirectory(app)

# optionally add doxygen target to generate documentation
option(BUILD_DOCS "Enable building of documentation (requires Doxygen)" OFF)
if(BUILD_DOCS)
find_package(Doxygen REQUIRED)
set(DOXYGEN_EXCLUDE_PATTERNS "${CMAKE_SOURCE_DIR}/ext/*")
doxygen_add_docs(doxygen ${CMAKE_SOURCE_DIR}
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
endif()

# compile the tests
option(BUILD_TESTS "Enable building of documentation (requires Doxygen)" OFF)
if(BUILD_TESTS)
add_subdirectory(tests)
endif()
3 changes: 3 additions & 0 deletions app/gyt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include "commands/log.h"
#include "commands/ls-tree.h"
#include "commands/show-ref.h"
#include "commands/tag.h"
#include <iostream>
#include <string>
#include <vector>
Expand Down Expand Up @@ -33,6 +34,8 @@ int main(int argc, char **argv) {
commands::checkout(args);
} else if (command == "show-ref") {
commands::showref(args);
} else if (command == "tag") {
commands::tag(args);
} else {
std::cerr << "Unknown command: " << command << "\n";
return -1;
Expand Down
10 changes: 10 additions & 0 deletions doc/case studies/Singleton.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Parser and singleton

`TagParser` is an example of a singleton class.
It was largely adapted from: https://stackoverflow.com/questions/75249277/is-singleton-with-static-unique-ptr-a-good-practice

It stores a reference of itself as a static member. The constructor constructs and sets the CmdLine arguments.

Some learning points:
- CmdLine, Arg classes has no default constructor, so they have to be constructed during the construction of `TagParser`, not after.
- Using a `static` reference of itself.
10 changes: 10 additions & 0 deletions include/commands/tag.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#ifndef TAG_H
#define TAG_H

#include <string>
#include <vector>
namespace commands {
void tag(std::vector<std::string> &args);
} // namespace commands

#endif // TAG_H
30 changes: 30 additions & 0 deletions include/parsers/TagParser.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#ifndef TAGPARSER_H
#define TAGPARSER_H

#include "tclap/CmdLine.h"
#include <string>
#include <vector>

class TagParser {
public:
static TagParser &get();
void parse(std::vector<std::string> &args);
std::string getTag();
bool isCommitSet() const;
std::string getCommit();

private:
// hides constructors to avoid accidental instantiation
TagParser();
TagParser(const TagParser &) = delete;
TagParser &operator=(const TagParser &) = delete;
TagParser(TagParser &&) = delete;
TagParser &operator=(TagParser &&) = delete;
~TagParser() = default;
// data members
TCLAP::CmdLine cmd;
TCLAP::UnlabeledValueArg<std::string> tagArg;
TCLAP::UnlabeledValueArg<std::string> commitArg;
};

#endif // TAGPARSER_H
2 changes: 1 addition & 1 deletion include/repository.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ namespace fs = std::filesystem;
class GitRepository {
public:
GitRepository(fs::path worktree, fs::path gitdir);
GitRepository(std::string path, bool force = false);
GitRepository(const std::string &path, bool force = false);

static std::optional<GitRepository> find(const fs::path &path = fs::path("."),
bool required = true);
Expand Down
1 change: 1 addition & 0 deletions include/util.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,5 @@ fs::perms get_unix_permissions(int mode);
std::string remove_file_prefix(const fs::path &path,
const fs::path &repo_prefix);
std::string resolve_ref(const fs::path &ref_path, GitRepository &repo);
fs::path get_commit_path(const std::string &sha);
#endif // UTIL_H
36 changes: 17 additions & 19 deletions run.sh
100644 → 100755
Original file line number Diff line number Diff line change
@@ -1,33 +1,31 @@
#!/ bin / bash
set - e
#!/bin/bash
set -e

#Set the default build type to Debug if no environment is specified
BUILD_TYPE = ${BUILD_TYPE : -Debug}
BUILD_TYPE=${BUILD_TYPE:-Debug}

#If the environment is specified as "prod", set the build type to Release
if["$1" == "-p"];
then BUILD_TYPE = Release fi
if [ "$1" == "-p" ];
then
BUILD_TYPE=Release
fi

#remove old build if any
if[-f "app/gyt"];
then rm -
rf app / gyt fi
if [ -f "app/gyt" ];
then
rm -rf app/gyt
fi

#Print the selected build type
echo "Selected build type: $BUILD_TYPE" echo
"Building the project... This will take a while to install "
"dependencies for the first time."
echo "Selected build type: $BUILD_TYPE"
echo "Building the project... This will take a while to install dependencies for the first time."

#Run CMake with the selected build type
cmake..-
DCMAKE_BUILD_TYPE = $BUILD_TYPE - DCMAKE_EXPORT_COMPILE_COMMANDS =
1 -
G Ninja
cmake .. -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_EXPORT_COMPILE_COMMANDS=1 -G Ninja

#Build the project
ninja
ninja

#symlink - so I can run it like gyt[arguments....]
sudo rm /
usr / local / bin / gyt sudo ln -
s "$(pwd)/app/gyt" / usr / local / bin / gyt
sudo rm /usr/local/bin/gyt
sudo ln -s "$(pwd)/app/gyt" /usr/local/bin/gyt
8 changes: 7 additions & 1 deletion src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,14 @@ target_link_libraries(object PRIVATE repository boost_libraries)
target_include_directories(object PUBLIC ../include)
target_compile_features(object PUBLIC cxx_std_17)

file(GLOB PARSER_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/parsers/*.cpp")
add_library(parsers ${PARSER_SOURCES})
target_link_libraries(parsers PRIVATE repository object boost_libraries)
target_include_directories(parsers PUBLIC ../include)
target_compile_features(parsers PUBLIC cxx_std_17)

file(GLOB COMMANDS_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/commands/*.cpp")
add_library(commands ${COMMANDS_SOURCES})
target_link_libraries(commands PRIVATE repository object boost_libraries)
target_link_libraries(commands PRIVATE parsers repository object boost_libraries)
target_include_directories(commands PUBLIC ../include)
target_compile_features(commands PUBLIC cxx_std_17)
36 changes: 36 additions & 0 deletions src/commands/tag.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
#include "commands/tag.h"
#include "object.h"
#include "parsers/TagParser.h"
#include "repository.h"
#include "util.h"
#include <optional>

namespace commands {
void tag(std::vector<std::string> &args) {
TagParser &parser = TagParser::get();
parser.parse(args);
// process args
std::optional<GitRepository> repo = GitRepository::find();
if (!repo) {
throw std::runtime_error("Not a git repository");
}
// resolve ref to find the commit hash
std::string commit = parser.isCommitSet() ? parser.getCommit()
: GitObject::find(*repo, "HEAD");
std::string tag = parser.getTag();
// ERROR: if the commit doesn't exist.
if (!fs::exists(repo->repo_path(get_commit_path(commit)))) {
std::string error_message = commit + ": not a valid commit";
throw std::runtime_error(error_message);
}
// ERROR: if the tag already exists
fs::path tag_path = repo->repo_path("refs/tags/" + tag);
if (fs::is_regular_file(tag_path)) {
std::string error_message = "tag '" + tag + "' already exists";
throw std::runtime_error(error_message);
}

// create a new file in refs/tags/{name}, and write the sha of the commit.
create_file(tag_path, commit + "\n");
}
} // namespace commands
4 changes: 1 addition & 3 deletions src/object.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,7 @@ GitObject::GitObject(const std::string &format) { this->format = format; }
void GitObject::init() {}

GitObject *GitObject::read(GitRepository &repo, const std::string &sha) {
std::string dir = sha.substr(0, 2);
std::string path = sha.substr(2);
fs::path file_path = fs::path("objects") / dir / path;
fs::path file_path = get_commit_path(sha);
fs::path paths = repo.file(file_path);

if (!fs::is_regular_file(paths)) {
Expand Down
28 changes: 28 additions & 0 deletions src/parsers/TagParser.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#include "parsers/TagParser.h"
#include <string>
#include <vector>

void TagParser::parse(std::vector<std::string> &args) {
cmd.reset();
cmd.parse(args);
}

std::string TagParser::getTag() { return tagArg.getValue(); }
bool TagParser::isCommitSet() const { return commitArg.isSet(); }
std::string TagParser::getCommit() { return commitArg.getValue(); }

TagParser &TagParser::get() {
static TagParser instance;
return instance;
}

TagParser::TagParser()
: cmd("tag", ' ', "0.1"),
tagArg("tagname", "Tag name", true, "HEAD", "tag name"),
commitArg("commit",
"Commit to tag to. If not specified, defaults to HEAD", false,
"HEAD", "commit to tag") {
cmd.ignoreUnmatched(true);
cmd.add(tagArg);
cmd.add(commitArg);
}
4 changes: 2 additions & 2 deletions src/repository.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

namespace fs = std::filesystem;

GitRepository::GitRepository(std::string path, bool force)
GitRepository::GitRepository(const std::string &path, bool force)
: worktree(fs::path(path)), gitdir(fs::path(path) / ".git") {
if (!force && !fs::is_directory(gitdir)) {
std::cerr << "Not a git repository: " << gitdir << "\n";
Expand Down Expand Up @@ -111,4 +111,4 @@ std::optional<GitRepository> GitRepository::find(const fs::path &path,
// TODO: update this to support refs
void GitRepository::update_head(const std::string &new_head) {
create_file(gitdir / "HEAD", new_head + "\n");
}
}
6 changes: 6 additions & 0 deletions src/util.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -169,3 +169,9 @@ std::string resolve_ref(const fs::path &ref_path, GitRepository &repo) {
}
return ref;
}

fs::path get_commit_path(const std::string &sha) {
std::string dir = sha.substr(0, 2);
std::string path = sha.substr(2);
return fs::path("objects") / dir / path;
}
Loading
Loading