/* MIT License Copyright (c) 2026 Poul Sander Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #pragma once #include #include #include #include #include struct git_repository; // Thrown for any libgit2 failure or invalid lookup. Handlers translate this // into an HTTP error response. class GitError : public std::runtime_error { public: explicit GitError(const std::string &what) : std::runtime_error(what) {} }; struct TreeEntry { std::string name; // last path component std::string path; // full path relative to repo root std::string oid; // hex object id bool is_dir = false; // tree bool is_submodule = false; uint64_t size = 0; // blob size in bytes (0 for non-blobs) bool is_image = false; // blob whose name has an image extension }; struct CommitInfo { std::string oid; std::string short_oid; std::string summary; std::string body; // message past the summary, "" when there is none std::string author_name; std::string author_email; std::time_t time = 0; }; struct BlameLine { int number = 0; // 1-based line number std::string short_oid; // commit that last touched the line std::string author; std::time_t time = 0; std::string content; // the line text (without newline) }; struct TagInfo { std::string name; std::string target_oid; // commit the tag resolves to std::string short_oid; std::time_t time = 0; bool annotated = false; }; struct BranchInfo { std::string name; std::string target_oid; // commit the branch points at std::string short_oid; std::time_t time = 0; bool is_default = false; // true for the repository's HEAD branch }; struct TagDetail { std::string name; std::string target_oid; std::string short_oid; bool annotated = false; std::string tagger_name; std::time_t time = 0; std::string message; }; struct FileChange { char status = '?'; // A, M, D, R, C, T std::string path; std::string old_path; // set for renames/copies }; struct DiffLine { std::string css; // CSS class: d-add, d-del, d-hunk, d-file, d-ctx std::string content; // line text (with +/- prefix for code lines) }; struct CommitDetail { std::string oid; std::string short_oid; std::string author_name; std::string author_email; std::time_t time = 0; std::string message; std::vector parents; std::string first_parent; // "" for a root commit; used for "before" links std::vector files; std::vector diff; }; enum class ObjectKind { Blob, Tree, Commit, Tag, Unknown }; struct ObjectView { ObjectKind kind = ObjectKind::Unknown; std::string oid; std::string type_name; std::string body; // textual summary or blob preview bool is_binary = false; std::vector entries; // populated for trees }; // libgit2 process-wide init/shutdown. Construct exactly once in main(). class GitLibrary { public: GitLibrary(); ~GitLibrary(); GitLibrary(const GitLibrary &) = delete; GitLibrary &operator=(const GitLibrary &) = delete; }; // Wraps a single opened repository. Not thread safe: open one per request. class GitRepo { public: explicit GitRepo(const std::string &path); ~GitRepo(); GitRepo(const GitRepo &) = delete; GitRepo &operator=(const GitRepo &) = delete; // Short name of the default branch (HEAD), e.g. "main". std::string default_branch() const; // URL of the "origin" remote, or "" when there is no such remote. std::string remote_url() const; // Resolve a revision to a full commit oid. Empty id means HEAD. std::string resolve_commit(const std::string &id) const; // Directory listing of `path` ("" = root) at the given commit. std::vector list_tree(const std::string &commit_oid, const std::string &path) const; // Read a blob located at `path` within the commit tree. // Returns false if no such path exists. bool read_file(const std::string &commit_oid, const std::string &path, std::string &out, bool &is_binary) const; // First README* file at the root of the commit tree, or "" if none. std::string find_readme(const std::string &commit_oid) const; std::vector blame(const std::string &commit_oid, const std::string &path) const; std::vector log(const std::string &start_oid, int limit) const; // All tags, sorted by name. std::vector tags() const; // Names of tags whose target commit is `commit_oid`, sorted by name. std::vector tags_for_commit(const std::string &commit_oid) const; // Details of a single tag by short name (e.g. "v1.0"). TagDetail tag_detail(const std::string &name) const; // All local branches, sorted by name. std::vector branches() const; // Local branches whose history contains `commit_oid`, sorted by name. std::vector branches_for_commit(const std::string &commit_oid) const; // Commit metadata plus the diff against its first parent. CommitDetail commit_detail(const std::string &oid) const; // The commit's diff as a raw unified patch. std::string commit_patch(const std::string &oid) const; // Inspect an arbitrary object by (possibly abbreviated) oid. ObjectView inspect(const std::string &oid) const; // Writes a gzip-compressed tar archive of the commit's tree to `out_path`, // recursing into initialized submodules. `top_dir` is the directory placed // at the archive root (e.g. "repo_v1.0"). Throws GitError when a submodule // cannot be resolved or any other libgit2 lookup fails; the archive writer // throws ArchiveError on I/O failure. void write_archive(const std::string &commit_oid, const std::string &top_dir, const std::string &out_path) const; private: git_repository *repo_ = nullptr; };