From 318f182df575407dc11e25343c3488d172da07f5 Mon Sep 17 00:00:00 2001 From: Bart Trojanowski Date: Mon, 18 May 2026 14:34:51 -0400 Subject: [PATCH 1/4] git wip save -t --- src/cmd_save.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/cmd_save.cpp b/src/cmd_save.cpp index 3715559..e78dd02 100644 --- a/src/cmd_save.cpp +++ b/src/cmd_save.cpp @@ -48,6 +48,9 @@ int SaveCmd::run(int argc, char *argv[]) { } else if (a == "--all" || a == "-a") { add_untracked = true; add_ignored = true; + } else if (a == "--only-tracked" || a == "-t") { + add_untracked = false; + add_ignored = false; } else if (a == "--gpg-sign") { gpg_sign = true; } else if (a == "--no-gpg-sign") { @@ -62,6 +65,7 @@ int SaveCmd::run(int argc, char *argv[]) { std::println(" -i, --ignored # enable capture of changes to ignored files"); std::println(" -I, --no-ignored # disable capture of changes to ignored files"); std::println(" -a, --all # same as --untracked and --ignored"); + std::println(" -t, --only-tracked # same as --no-untracked and --no-ignored"); std::println(" --gpg-sign # enable signing of commits"); std::println(" --no-gpg-sign # disable signing of commits"); std::println(" ... # filter on changes to specific file(s)\n"); From cfb8bf9669a739fd8ed77085c008ef74ebb08695 Mon Sep 17 00:00:00 2001 From: Bart Trojanowski Date: Mon, 18 May 2026 14:42:35 -0400 Subject: [PATCH 2/4] git wip save reads defaults from gitconfig --- src/CMakeLists.txt | 1 + src/cmd_save.cpp | 145 +++++++++++++++++++++++------------ test/cli/CMakeLists.txt | 2 +- test/cli/test_save_config.sh | 143 ++++++++++++++++++++++++++++++++++ 4 files changed, 243 insertions(+), 48 deletions(-) create mode 100755 test/cli/test_save_config.sh diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index b3e679b..6435247 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -22,6 +22,7 @@ endif() add_executable(git-wip color.cpp + config.cpp main.cpp cmd_delete.cpp cmd_list.cpp diff --git a/src/cmd_save.cpp b/src/cmd_save.cpp index e78dd02..a7e5c78 100644 --- a/src/cmd_save.cpp +++ b/src/cmd_save.cpp @@ -1,4 +1,5 @@ #include "cmd_save.hpp" +#include "config.hpp" #include "git_guards.hpp" #include "git_helpers.hpp" #include "string_helpers.hpp" @@ -13,17 +14,91 @@ #include #include +// Helper to parse git-wip.save spec into add_untracked/add_ignored flags +static void parse_save_spec(const std::string &spec, bool &add_untracked, bool &add_ignored) { + if (spec == "tracked") { + add_untracked = false; + add_ignored = false; + } else if (spec == "untracked") { + add_untracked = true; + add_ignored = false; + } else if (spec == "ignored") { + add_untracked = false; + add_ignored = true; + } else if (spec == "all") { + add_untracked = true; + add_ignored = true; + } else { + spdlog::warn("git-wip.save: unknown value '{}', ignoring", spec); + } +} + int SaveCmd::run(int argc, char *argv[]) { // ----------------------------------------------------------------------- - // 1. Parse arguments + // 1. Check for --help early (before opening repo) + // ----------------------------------------------------------------------- + for (int i = 1; i < argc; ++i) { + std::string a = argv[i]; + if (a == "--help" || a == "-h") { + std::println("Usage: git-wip save [] [--editor|-e] [--[no-]untracked|-u|-U] [--[no-]ignored|-i|-I] [--[no-]gpg-sign] [-- ...]\n"); + // - # + std::println(" # use this message (defaults to \"WIP\")"); + std::println(" -e, --editor # queit when there are no changes (called from editor)"); + std::println(" -u, --untracked # enable capture of changes to untracked files"); + std::println(" -U, --no-untracked # disable capture of changes to untracked files"); + std::println(" -i, --ignored # enable capture of changes to ignored files"); + std::println(" -I, --no-ignored # disable capture of changes to ignored files"); + std::println(" -a, --all # same as --untracked and --ignored"); + std::println(" -t, --only-tracked # same as --no-untracked and --no-ignored"); + std::println(" --gpg-sign # enable signing of commits"); + std::println(" --no-gpg-sign # disable signing of commits"); + std::println(" ... # filter on changes to specific file(s)\n"); + return 0; + } + } + + // ----------------------------------------------------------------------- + // 2. Open repository + // ----------------------------------------------------------------------- + GitLibGuard git_lib_guard; + + GitRepoGuard repo_guard; + if (git_repository_open_ext(repo_guard.ptr(), ".", 0, nullptr) < 0) { + spdlog::error("not a git repository: {}", git_error_str()); + return 1; + } + git_repository *repo = repo_guard.get(); + + // ----------------------------------------------------------------------- + // 3. Load defaults from git config + // + // [git-wip] + // save = tracked | untracked | ignored | all + // gpg-sign = true | false // ----------------------------------------------------------------------- bool editor_mode = false; bool add_untracked = false; bool add_ignored = false; - bool gpg_sign = false; // TODO: not implemented yet + bool gpg_sign = false; std::string message = "WIP"; std::vector files; + { + Config cfg(repo); + if (auto spec = cfg.get_string("git-wip.save"); spec) { + parse_save_spec(*spec, add_untracked, add_ignored); + } + if (auto val = cfg.get_bool("git-wip.gpg-sign"); val) { + gpg_sign = *val; + } + } + + spdlog::debug("save: config defaults: untracked={} ignored={} gpg_sign={}", + add_untracked, add_ignored, gpg_sign); + + // ----------------------------------------------------------------------- + // 4. Parse command line arguments (override config defaults) + // ----------------------------------------------------------------------- std::vector args; for (int i = 1; i < argc; ++i) args.emplace_back(argv[i]); @@ -56,19 +131,7 @@ int SaveCmd::run(int argc, char *argv[]) { } else if (a == "--no-gpg-sign") { gpg_sign = false; } else if (a == "--help" || a == "-h") { - std::println("Usage: git-wip save [] [--editor|-e] [--[no-]untracked|-u|-U] [--[no-]ignored|-i|-I] [--[no-]gpg-sign] [-- ...]\n"); - // - # - std::println(" # use this message (defaults to \"WIP\")"); - std::println(" -e, --editor # queit when there are no changes (called from editor)"); - std::println(" -u, --untracked # enable capture of changes to untracked files"); - std::println(" -U, --no-untracked # disable capture of changes to untracked files"); - std::println(" -i, --ignored # enable capture of changes to ignored files"); - std::println(" -I, --no-ignored # disable capture of changes to ignored files"); - std::println(" -a, --all # same as --untracked and --ignored"); - std::println(" -t, --only-tracked # same as --no-untracked and --no-ignored"); - std::println(" --gpg-sign # enable signing of commits"); - std::println(" --no-gpg-sign # disable signing of commits"); - std::println(" ... # filter on changes to specific file(s)\n"); + // Already handled above, but keep for completeness return 0; } else if (!a.empty() && a[0] == '-') { spdlog::error("git-wip save: unknown option '{}'", a); @@ -81,31 +144,19 @@ int SaveCmd::run(int argc, char *argv[]) { } } - spdlog::debug("save: message='{}' editor={} untracked={} ignored={} gpg_sign={} files={}", + spdlog::debug("save: final: message='{}' editor={} untracked={} ignored={} gpg_sign={} files={}", message, editor_mode, add_untracked, add_ignored, gpg_sign, files.size()); if (gpg_sign) - spdlog::warn("git-wip sign --gpg-sign is not implemented yet"); - - // ----------------------------------------------------------------------- - // 2. Open repository - // ----------------------------------------------------------------------- - GitLibGuard git_lib_guard; - - GitRepoGuard repo_guard; - if (git_repository_open_ext(repo_guard.ptr(), ".", 0, nullptr) < 0) { - spdlog::error("not a git repository: {}", git_error_str()); - return 1; - } - git_repository *repo = repo_guard.get(); + spdlog::warn("git-wip save --gpg-sign is not implemented yet"); // ----------------------------------------------------------------------- - // 2b. Normalise file paths to be relative to the workdir root. + // 5. Normalise file paths to be relative to the workdir root. // - // git_index_add_all() resolves pathspecs against the workdir root, - // but the paths supplied on the command line are relative to the - // current working directory (which may be a subdirectory). Convert - // each path: cwd/file → absolute → workdir-relative. + // git_index_add_all() resolves pathspecs against the workdir root, + // but the paths supplied on the command line are relative to the + // current working directory (which may be a subdirectory). Convert + // each path: cwd/file → absolute → workdir-relative. // ----------------------------------------------------------------------- if (!files.empty()) { const char *workdir_cstr = git_repository_workdir(repo); @@ -131,7 +182,7 @@ int SaveCmd::run(int argc, char *argv[]) { } // ----------------------------------------------------------------------- - // 3. Resolve branch names + // 6. Resolve branch names // ----------------------------------------------------------------------- auto bn = resolve_branch_names(repo); if (!bn) { @@ -143,12 +194,12 @@ int SaveCmd::run(int argc, char *argv[]) { spdlog::debug("save: work_branch='{}' wip_ref='{}'", bn->work_branch, bn->wip_ref); // ----------------------------------------------------------------------- - // 4. Ensure reflog directory exists for the wip branch + // 7. Ensure reflog directory exists for the wip branch // ----------------------------------------------------------------------- ensure_reflog_dir(repo, bn->wip_ref); // ----------------------------------------------------------------------- - // 5. Resolve work_last + // 8. Resolve work_last // ----------------------------------------------------------------------- auto work_last = resolve_oid(repo, bn->work_ref); if (!work_last) { @@ -160,7 +211,7 @@ int SaveCmd::run(int argc, char *argv[]) { spdlog::debug("save: work_last={}", oid_to_hex(&*work_last)); // ----------------------------------------------------------------------- - // 6. Determine wip_parent + // 9. Determine wip_parent // ----------------------------------------------------------------------- auto wip_last = resolve_oid(repo, bn->wip_ref); // nullopt if no wip branch yet @@ -177,13 +228,13 @@ int SaveCmd::run(int argc, char *argv[]) { spdlog::debug("save: wip_parent={}", oid_to_hex(&*wip_parent)); // ----------------------------------------------------------------------- - // 7. Build new tree (in-memory; never writes to the on-disk index) + // 10. Build new tree (in-memory; never writes to the on-disk index) // - // Steps: - // a) Load the parent commit's tree into the repo's in-memory index - // b) Stage changes from the working directory on top - // c) Write the tree object to the ODB - // d) Restore the real on-disk index + // Steps: + // a) Load the parent commit's tree into the repo's in-memory index + // b) Stage changes from the working directory on top + // c) Write the tree object to the ODB + // d) Restore the real on-disk index // ----------------------------------------------------------------------- git_oid new_tree_oid{}; { @@ -248,7 +299,7 @@ int SaveCmd::run(int argc, char *argv[]) { } // ----------------------------------------------------------------------- - // 8. Check for changes + // 11. Check for changes // ----------------------------------------------------------------------- { GitCommitGuard parent_commit; @@ -266,7 +317,7 @@ int SaveCmd::run(int argc, char *argv[]) { spdlog::debug("save: has changes, creating commit"); // ----------------------------------------------------------------------- - // 9. Create the WIP commit + // 12. Create the WIP commit // ----------------------------------------------------------------------- GitTreeGuard new_tree_obj; if (git_tree_lookup(new_tree_obj.ptr(), repo, &new_tree_oid) < 0) { @@ -299,7 +350,7 @@ int SaveCmd::run(int argc, char *argv[]) { spdlog::debug("save: new_wip={}", oid_to_hex(&new_commit_oid)); // ----------------------------------------------------------------------- - // 10. Update the wip ref + // 13. Update the wip ref // ----------------------------------------------------------------------- { std::string reflog_msg = "git-wip: " + first_line(message.c_str()); diff --git a/test/cli/CMakeLists.txt b/test/cli/CMakeLists.txt index 58b614a..bcfd52e 100644 --- a/test/cli/CMakeLists.txt +++ b/test/cli/CMakeLists.txt @@ -9,7 +9,7 @@ set(LEGACY_TEST_TREE "${CMAKE_CURRENT_BINARY_DIR}/test-artifacts") set(GIT_WIP_BIN "$") -foreach(TEST_NAME IN ITEMS test_legacy test_spaces test_status test_status2 test_status_ref test_save_file test_save_subdir test_list test_delete test_help test_log) +foreach(TEST_NAME IN ITEMS test_legacy test_spaces test_status test_status2 test_status_ref test_save_file test_save_subdir test_save_config test_list test_delete test_help test_log) add_test( NAME "cli/${TEST_NAME}" COMMAND "${CMAKE_CURRENT_SOURCE_DIR}/${TEST_NAME}.sh" diff --git a/test/cli/test_save_config.sh b/test/cli/test_save_config.sh new file mode 100755 index 0000000..3afd166 --- /dev/null +++ b/test/cli/test_save_config.sh @@ -0,0 +1,143 @@ +#!/usr/bin/env bash +source "$(dirname "$0")/lib.sh" + +# Test that git config options work for git-wip save + +create_test_repo +RUN "echo 1 >README" +RUN git add README +RUN git commit -m "\"initial commit\"" + +# ----------------------------------------------------------------------------- +# Test 1: git-wip.save = untracked +# ----------------------------------------------------------------------------- +note "Test 1: git-wip.save = untracked" + +RUN git config git-wip.save untracked + +# Create an untracked file +RUN "echo content >UNTRACKED" + +# Without the config, untracked files would NOT be captured. +# With git-wip.save=untracked, they should be captured. +RUN "$GIT_WIP" save "\"test untracked config\"" + +# Verify untracked file was captured +RUN git show wip/master:UNTRACKED +EXP_text "content" + +# Clean up for next test +RUN git update-ref -d refs/wip/master +RUN rm UNTRACKED + +# ----------------------------------------------------------------------------- +# Test 2: git-wip.save = tracked (explicit default) +# ----------------------------------------------------------------------------- +note "Test 2: git-wip.save = tracked" + +RUN git config git-wip.save tracked + +# Create untracked file +RUN "echo content >UNTRACKED2" + +# Make a change to tracked file +RUN "echo 2 >README" + +RUN "$GIT_WIP" save "\"test tracked config\"" + +# Verify tracked file was captured +RUN git show wip/master:README +EXP_text "2" + +# Verify untracked file was NOT captured +_RUN git show wip/master:UNTRACKED2 +EXP_grep "not in" + +# Clean up +RUN git update-ref -d refs/wip/master +RUN rm UNTRACKED2 +RUN "echo 1 >README" + +# ----------------------------------------------------------------------------- +# Test 3: Command line overrides config +# ----------------------------------------------------------------------------- +note "Test 3: Command line overrides config" + +# Config says tracked only +RUN git config git-wip.save tracked + +# Create untracked file +RUN "echo override >OVERRIDE" +RUN "echo 3 >README" + +# But command line says --untracked +RUN "$GIT_WIP" save "\"override test\"" --untracked + +# Verify untracked file WAS captured (command line wins) +RUN git show wip/master:OVERRIDE +EXP_text "override" + +# Clean up +RUN git update-ref -d refs/wip/master +RUN rm OVERRIDE +RUN "echo 1 >README" + +# ----------------------------------------------------------------------------- +# Test 4: --no-untracked overrides git-wip.save = untracked +# ----------------------------------------------------------------------------- +note "Test 4: --no-untracked overrides config" + +RUN git config git-wip.save untracked + +# Create untracked file +RUN "echo should-not-capture >NOCAPTURE" +RUN "echo 4 >README" + +# Command line explicitly disables untracked +RUN "$GIT_WIP" save "\"no-untracked test\"" --no-untracked + +# Verify untracked file was NOT captured +_RUN git show wip/master:NOCAPTURE +EXP_grep "not in" + +# Verify tracked file was captured +RUN git show wip/master:README +EXP_text "4" + +# Clean up +RUN git update-ref -d refs/wip/master +RUN rm NOCAPTURE +RUN "echo 1 >README" + +# ----------------------------------------------------------------------------- +# Test 5: git-wip.save = all +# ----------------------------------------------------------------------------- +note "Test 5: git-wip.save = all" + +RUN git config git-wip.save all + +# Create files +RUN "echo untracked >UNTRACKED_ALL" +RUN "echo 5 >README" + +# Create an ignored file +RUN "echo ignored >IGNORED" +RUN "echo IGNORED >.gitignore" +RUN git add .gitignore +RUN git commit -m "\"add gitignore\"" + +# Save with config=all +RUN "$GIT_WIP" save "\"all test\"" + +# Verify both untracked and ignored were captured +RUN git show wip/master:UNTRACKED_ALL +EXP_text "untracked" + +RUN git show wip/master:IGNORED +EXP_text "ignored" + +# Clean up +RUN git update-ref -d refs/wip/master +RUN rm UNTRACKED_ALL IGNORED + +echo "OK: $TEST_NAME" From 09e9cd8fd3ed3b6c89e3cbd34f43a7b319fd286d Mon Sep 17 00:00:00 2001 From: Bart Trojanowski Date: Mon, 18 May 2026 14:45:58 -0400 Subject: [PATCH 3/4] update docs --- AGENTS.md | 20 +++++++++++++++++++- README.md | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 1 deletion(-) diff --git a/AGENTS.md b/AGENTS.md index 31cef02..40c3f63 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -217,6 +217,7 @@ RUN "$GIT_WIP" save "message with spaces" # WRONG — splits into tokens src/ main.cpp # arg dispatch; no-args → save "WIP" command.hpp # abstract Command base class + config.hpp/cpp # generic git config wrapper (libgit2) git_guards.hpp # RAII wrappers for libgit2 handles + git_error_str() cmd_save.hpp/cpp # save command cmd_log.hpp/cpp # log command @@ -230,6 +231,7 @@ test/cli/ test_status2.sh # status after work-branch advance test_save_file.sh # save with explicit file arguments test_log.sh # log command tests + test_save_config.sh # git config options for save command CMakeLists.txt # registers each test_*.sh with ctest ``` @@ -404,7 +406,23 @@ The vim plugin runs `git wip -h` to check if git-wip is installed. This should: | log | cmd_log.hpp | cmd_log.cpp | **Implemented** — libgit2 range, spawns `git log` | | status | cmd_status.hpp | cmd_status.cpp | **Implemented** — libgit2 revwalk, `-l`/`-f` flags | | delete | cmd_delete.hpp | cmd_delete.cpp | **Implemented** — delete one/current/cleanup orphaned wip refs | -| config | — | — | Not implemented | + +### config.hpp/cpp — generic git config wrapper + +`Config` is a generic wrapper around libgit2's `git_config` API. It provides +typed accessors for reading configuration values from `.gitconfig`: + +```cpp +Config cfg(repo); +auto str_val = cfg.get_string("git-wip.save"); // std::optional +auto bool_val = cfg.get_bool("git-wip.gpg-sign"); // std::optional +auto int_val = cfg.get_int("some.key"); // std::optional +auto i64_val = cfg.get_int64("some.key"); // std::optional +``` + +Each accessor returns `std::nullopt` if the key is not found or on error. +Commands use `Config` to read their specific settings and interpret the values +themselves. ### git_guards.hpp — RAII wrappers diff --git a/README.md b/README.md index ce506ca..8ea2a81 100644 --- a/README.md +++ b/README.md @@ -211,6 +211,38 @@ does not exist). --- +## Configuration + +`git-wip` reads settings from your `.gitconfig` under the `[git-wip]` section. +These provide defaults that can be overridden by command-line flags. + +```ini +[git-wip] + save = untracked # which files to capture: tracked | untracked | ignored | all + gpg-sign = false # whether to GPG-sign WIP commits +``` + +### `git-wip.save` + +Controls which files are captured by `git wip save`: + +| Value | Description | +|---|---| +| `tracked` | Only tracked files (default behaviour) | +| `untracked` | Tracked files plus untracked files (equivalent to `-u`) | +| `ignored` | Tracked files plus ignored files (equivalent to `-i`) | +| `all` | Everything: tracked, untracked, and ignored (equivalent to `-a`) | + +Command-line flags (`--untracked`, `--no-untracked`, `--ignored`, `--no-ignored`, +`--all`, `--only-tracked`) override this setting. + +### `git-wip.gpg-sign` + +Boolean. If `true`, WIP commits will be GPG-signed (not yet implemented). +`--gpg-sign` and `--no-gpg-sign` override this setting. + +--- + ## Building Requires: a C++23 compiler, CMake ≥ 3.26, Ninja, and `libgit2-dev`. From 710ec0430937149b04ff5da6567450c5f55dcd09 Mon Sep 17 00:00:00 2001 From: Bart Trojanowski Date: Mon, 18 May 2026 14:48:07 -0400 Subject: [PATCH 4/4] missing config .cpp .hpp --- src/config.cpp | 93 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/config.hpp | 51 +++++++++++++++++++++++++++ 2 files changed, 144 insertions(+) create mode 100644 src/config.cpp create mode 100644 src/config.hpp diff --git a/src/config.cpp b/src/config.cpp new file mode 100644 index 0000000..efb70a4 --- /dev/null +++ b/src/config.cpp @@ -0,0 +1,93 @@ +#include "config.hpp" +#include "spdlog/spdlog.h" + +// --------------------------------------------------------------------------- +// Config implementation — generic libgit2 config wrapper +// --------------------------------------------------------------------------- + +Config::Config(git_repository *repo) { + if (git_repository_config(&m_cfg, repo) < 0) { + spdlog::debug("config: cannot open git config"); + m_cfg = nullptr; + } +} + +Config::Config(Config &&other) noexcept : m_cfg(other.m_cfg) { + other.m_cfg = nullptr; +} + +Config &Config::operator=(Config &&other) noexcept { + if (this != &other) { + if (m_cfg) git_config_free(m_cfg); + m_cfg = other.m_cfg; + other.m_cfg = nullptr; + } + return *this; +} + +Config::~Config() { + if (m_cfg) git_config_free(m_cfg); +} + +std::optional Config::get_string(const char *key) const { + if (!m_cfg) return std::nullopt; + + git_config_entry *entry = nullptr; + int rc = git_config_get_entry(&entry, m_cfg, key); + if (rc == 0 && entry && entry->value) { + std::string value = entry->value; + git_config_entry_free(entry); + spdlog::debug("config: {} = '{}'", key, value); + return value; + } + if (rc != GIT_ENOTFOUND) { + spdlog::debug("config: error reading {}", key); + } + return std::nullopt; +} + +std::optional Config::get_bool(const char *key) const { + if (!m_cfg) return std::nullopt; + + int value = 0; + int rc = git_config_get_bool(&value, m_cfg, key); + if (rc == 0) { + bool result = (value != 0); + spdlog::debug("config: {} = {}", key, result); + return result; + } + if (rc != GIT_ENOTFOUND) { + spdlog::debug("config: error reading {}", key); + } + return std::nullopt; +} + +std::optional Config::get_int(const char *key) const { + if (!m_cfg) return std::nullopt; + + int value = 0; + int rc = git_config_get_int32(&value, m_cfg, key); + if (rc == 0) { + spdlog::debug("config: {} = {}", key, value); + return value; + } + if (rc != GIT_ENOTFOUND) { + spdlog::debug("config: error reading {}", key); + } + return std::nullopt; +} + +std::optional Config::get_int64(const char *key) const { + if (!m_cfg) return std::nullopt; + + int64_t value = 0; + int rc = git_config_get_int64(&value, m_cfg, key); + if (rc == 0) { + spdlog::debug("config: {} = {}", key, value); + return value; + } + if (rc != GIT_ENOTFOUND) { + spdlog::debug("config: error reading {}", key); + } + return std::nullopt; +} diff --git a/src/config.hpp b/src/config.hpp new file mode 100644 index 0000000..fa7ef5c --- /dev/null +++ b/src/config.hpp @@ -0,0 +1,51 @@ +#pragma once + +#include +#include +#include + +// --------------------------------------------------------------------------- +// Config — generic wrapper around libgit2's git config API +// +// Provides typed accessors for reading configuration values from .gitconfig. +// Commands use this to read their specific settings (e.g., git-wip.save). +// +// Example usage: +// +// Config cfg(repo); +// auto save_spec = cfg.get_string("git-wip.save"); +// auto gpg_sign = cfg.get_bool("git-wip.gpg-sign"); +// +// --------------------------------------------------------------------------- + +class Config { +public: + // Construct from an open repository; opens the merged config view. + explicit Config(git_repository *repo); + + // Non-copyable, movable + Config(const Config &) = delete; + Config &operator=(const Config &) = delete; + Config(Config &&other) noexcept; + Config &operator=(Config &&other) noexcept; + + ~Config(); + + // Returns true if the config was successfully opened + bool valid() const { return m_cfg != nullptr; } + + // Get a string value; returns nullopt if not found or on error + std::optional get_string(const char *key) const; + + // Get a boolean value; returns nullopt if not found or on error + std::optional get_bool(const char *key) const; + + // Get an integer value; returns nullopt if not found or on error + std::optional get_int(const char *key) const; + + // Get an int64 value; returns nullopt if not found or on error + std::optional get_int64(const char *key) const; + +private: + git_config *m_cfg = nullptr; +};