| startgitStatic page generator for git repositories | 
| git clone git://git.dimitrijedobrota.com/startgit.git | 
| Log | Files | Refs | README | LICENSE | HACKING | CONTRIBUTING | CODE_OF_CONDUCT | BUILDING | 
| commit | 737b1d9858fa2c82ecd1cff6d9dfe14536a7e727 | 
| parent | 018e37d0b990bcd8557ad3db5c5c6e41d8eb2bee | 
| author | Dimitrije Dobrota < mail@dimitrijedobrota.com > | 
| date | Wed, 8 Jan 2025 22:18:39 +0100 | 
Proper decompositon and imporved safety
| M | CMakeLists.txt | | | +++ -- | 
| A | source/branch.cpp | | | ++++++++++++ | 
| A | source/branch.hpp | | | +++++++++++++++++++++++ | 
| D | source/lib.cpp | | | ------ | 
| D | source/lib.hpp | | | --------------------- | 
| M | source/main.cpp | | | +++++++++++++++++++++++++++++++++++++++++++ --------------------------------------- | 
| A | source/repository.cpp | | | +++++++++++++++++++++++++++++++++++++++++ | 
| A | source/repository.hpp | | | +++++++++++++++++++++++++++++++++++++++++ | 
| M | test/source/startgit_test.cpp | | | ++ ------ | 
9 files changed, 172 insertions(+), 80 deletions(-)
diff --git a/ CMakeLists.txt b/ CMakeLists.txt
          @@ -4,7 +4,7 @@ 
          include(cmake/prelude.cmake)
        
        
          project(
              startgit
              VERSION 0.1.4
              VERSION 0.1.5
              DESCRIPTION "Static page generator for git repositories"
              HOMEPAGE_URL "https://git.dimitrijedobrota.com/stargit.git"
              LANGUAGES CXX
        
        
          @@ -23,7 +23,8 @@ 
          find_package(poafloc 1 CONFIG REQUIRED)
        
        
          add_library(
              startgit_lib OBJECT
              source/lib.cpp
              source/branch.cpp
              source/repository.cpp
          )
          target_link_libraries(startgit_lib PUBLIC git2wrap::git2wrap hemplate::hemplate poafloc::poafloc)
        
        diff --git a/ source/branch.cpp b/ source/branch.cpp
@@ -0,0 +1,12 @@
#include "branch.hpp"
          namespace startgit
          {
          branch::branch(git2wrap::branch brnch)
              : m_branch(std::move(brnch))
              , m_name(m_branch.get_name())
          {
          }
          }  // namespace startgit
        
        diff --git a/ source/branch.hpp b/ source/branch.hpp
@@ -0,0 +1,23 @@
#pragma once
          #include <string>
          #include <git2wrap/branch.hpp>
          namespace startgit
          {
          class branch
          {
          public:
            explicit branch(git2wrap::branch brnch);
            const std::string& get_name() const { return m_name; }
          private:
            git2wrap::branch m_branch;
            std::string m_name;
          };
          }  // namespace startgit
        
        diff --git a/ source/lib.cpp b/ source/lib.cpp
@@ -1,6 +0,0 @@
#include "lib.hpp"
          library::library()
              : name {"startgit"}
          {
          }
        
        diff --git a/ source/lib.hpp b/ source/lib.hpp
@@ -1,21 +0,0 @@
#pragma once
          #include <string>
          /**
           * @brief The core implementation of the executable
           *
           * This class makes up the library part of the executable, which means that the
           * main logic is implemented here. This kind of separation makes it easy to
           * test the implementation for the executable, because the logic is nicely
           * separated from the command-line logic implemented in the main function.
           */
          struct library
          {
            /**
             * @brief Simply initializes the name member to the name of the project
             */
            library();
            std::string name;
          };
        
        diff --git a/ source/main.cpp b/ source/main.cpp
@@ -14,6 +14,8 @@
#include <hemplate/classes.hpp>
          #include <poafloc/poafloc.hpp>
          #include "repository.hpp"
          struct arguments_t
          {
            std::filesystem::path output_dir = ".";
        
        
          @@ -31,7 +33,8 @@ 
          std::string long_to_string(int64_t date)
        
        
          void write_header(std::ostream& ost,
                            const std::string& repo,
                            const std::string& branch,
                            const std::string& author)
                            const std::string& author,
                            const std::string& description)
          {
            using namespace hemplate;  // NOLINT
          
          @@ -45,7 +48,7 @@ 
          void write_header(std::ostream& ost,
        
        
                       .add(html::meta({{"charset", "UTF-8"}}))
                       .add(html::meta({{"name", "author"}, {"content", author}}))
                       .add(html::meta(
                           {{"name", "description"}, {"content", "Content of " + name}}))
                           {{"name", "description"}, {"content", description}}))
                       .add(
                           html::meta({{"content", "width=device-width, initial-scale=1"},
                                       {"name", "viewport"}}))
        
        
          @@ -100,9 +103,7 @@ 
          void write_commit_table(std::ostream& ost, git2wrap::revwalk& rwalk)
        
        
          }
          void write_repo_table(std::ostream& ost,
                                const arguments_t* args,
                                const std::string& description,
                                const std::string& owner)
                                const std::vector<startgit::repository>& repos)
          {
            using namespace hemplate;  // NOLINT
          
          @@ -116,14 +117,13 @@ 
          void write_repo_table(std::ostream& ost,
        
        
            ost << html::thead();
            ost << html::tbody();
            for (const auto& repo_path : args->repos) {
              const std::string repo_name = repo_path.stem().string();
            for (const auto& repo : repos) {
              ost << html::tr()
                         .add(html::td().add(html::a(repo_name).set(
                             "href", repo_name + "/master_log.html")))
                         .add(html::td(description))
                         .add(html::td(owner))
                         .add(html::td().add(
                             html::a(repo.get_name())
                                 .set("href", repo.get_name() + "/master_log.html")))
                         .add(html::td(repo.get_description()))
                         .add(html::td(repo.get_owner()))
                         .add(html::td(""));
            }
          
          @@ -153,7 +153,7 @@ 
          int parse_opt(int key, const char* arg, poafloc::Parser* parser)
        
        
                args->url = arg;
                break;
              case poafloc::ARG:
                args->repos.emplace_back(arg);
                args->repos.emplace_back(std::filesystem::canonical(arg));
                break;
              default:
                break;
        
        
          @@ -188,56 +188,61 @@ 
          int main(int argc, char* argv[])
        
        
              return 1;
            }
            using namespace git2wrap;  // NOLINT
            const libgit2 libgit;
            for (const auto& repo_path : args.repos) {
              try {
                const std::string repo_name = repo_path.stem().string();
                repository repo(nullptr);
            try {
              const git2wrap::libgit2 libgit;
              std::vector<startgit::repository> repos;
              // open all repositories
              for (const auto& repo_path : args.repos) {
                try {
                  repo = repository::open(
                      repo_path.c_str(), GIT_REPOSITORY_OPEN_NO_SEARCH, nullptr);
                  repos.emplace_back(repo_path);
                } catch (const git2wrap::error& err) {
                  std::cerr << std::format("Warning: {} is not a repository\n",
                                           repo_name);
                  continue;
                                           repo_path.string());
                }
              }
              for (const auto& repo : repos) {
                for (const auto& branch : repo.get_branches()) {
                  std::filesystem::create_directory(args.output_dir / repo.get_name());
                for (auto it = repo.branch_begin(GIT_BRANCH_LOCAL);
                     it != repo.branch_end();
                     ++it)
                {
                  const std::string filename = it->get_name() + "_log.html";
                  std::filesystem::create_directory(args.output_dir / repo_name);
                  std::ofstream ofs(args.output_dir / repo_name / filename);
                  const std::string filename = branch.get_name() + "_log.html";
                  std::ofstream ofs(args.output_dir / repo.get_name() / filename);
                  revwalk rwalk(repo);
                  git2wrap::revwalk rwalk(repo.get());
                  const object obj = repo.revparse(it->get_name().c_str());
                  const git2wrap::object obj =
                      repo.get().revparse(branch.get_name().c_str());
                  rwalk.push(obj.get_id());
                  write_header(ofs, repo_name, it->get_name(), "Dimitrije Dobrota");
                  write_header(ofs,
                               repo.get_name(),
                               branch.get_name(),
                               repo.get_owner(),
                               repo.get_description());
                  write_commit_table(ofs, rwalk);
                  write_footer(ofs);
                }
              } catch (const git2wrap::error& err) {
                std::cerr << std::format("({}:{}) Error {}/{}: {}\n",
                                         err.get_file(),
                                         err.get_line(),
                                         err.get_error(),
                                         err.get_klass(),
                                         err.get_message());
              }
              // Build repo index
              std::ofstream ofs(args.output_dir / "index.html");
              write_header(ofs, "Git repository", "~", "Dimitrije Dobrota");
              write_repo_table(ofs, &args, "Desc", "Own");
              write_header(ofs,
                           "Git repository",
                           "~",
                           "Dimitrije Dobrota",
                           "Collection of all public git repositories");
              write_repo_table(ofs, repos);
              write_footer(ofs);
            } catch (const git2wrap::error& err) {
              std::cerr << std::format("({}:{}) Error {}/{}: {}\n",
                                       err.get_file(),
                                       err.get_line(),
                                       err.get_error(),
                                       err.get_klass(),
                                       err.get_message());
            }
            return 0;
        
        diff --git a/ source/repository.cpp b/ source/repository.cpp
@@ -0,0 +1,41 @@
#include <fstream>
          #include "repository.hpp"
          namespace startgit
          {
          repository::repository(const std::filesystem::path& path)
              : m_repo(git2wrap::repository::open(
                    path.c_str(), GIT_REPOSITORY_OPEN_NO_SEARCH, nullptr))
          {
            m_name = path.stem().string();
            read_file(m_owner, path, "owner");
            read_file(m_description, path, "description");
            for (auto it = m_repo.branch_begin(GIT_BRANCH_LOCAL);
                 it != m_repo.branch_end();
                 ++it)
            {
              m_branches.emplace_back(it->dup());
            }
          }
          void repository::read_file(std::string& out,
                                     const std::filesystem::path& base,
                                     const char* file)
          {
            std::ifstream ifs(base / file);
            if (!ifs.is_open()) {
              ifs = base / ".git" / file;
            }
            if (ifs.is_open()) {
              std::getline(ifs, out, '\n');
            } else {
              out = "Unknown";
            }
          }
          }  // namespace startgit
        
        diff --git a/ source/repository.hpp b/ source/repository.hpp
@@ -0,0 +1,41 @@
#pragma once
          #include <filesystem>
          #include <string>
          #include <vector>
          #include <git2wrap/repository.hpp>
          #include "branch.hpp"
          namespace startgit
          {
          class repository
          {
          public:
            explicit repository(const std::filesystem::path& path);
            const git2wrap::repository& get() const { return m_repo; }
            const std::string& get_name() const { return m_name; }
            const std::string& get_owner() const { return m_owner; }
            const std::string& get_description() const { return m_description; }
            const auto& get_branches() const { return m_branches; }
          private:
            static void read_file(std::string& out,
                                  const std::filesystem::path& base,
                                  const char* file);
            git2wrap::repository m_repo;
            std::string m_name;
            std::string m_owner = "Unknown";
            std::string m_description;
            std::vector<branch> m_branches;
          };
          }  // namespace startgit
        
        diff --git a/ test/source/startgit_test.cpp b/ test/source/startgit_test.cpp
@@ -1,8 +1,4 @@
#include "lib.hpp"
          auto main() -> int
          int main()
          {
            auto const lib = library {};
            return lib.name == "startgit" ? 0 : 1;
            return 0;
          }