Skip to content
Draft
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
4 changes: 4 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ set(GIT2CPP_SRC
${GIT2CPP_SOURCE_DIR}/subcommand/clone_subcommand.hpp
${GIT2CPP_SOURCE_DIR}/subcommand/commit_subcommand.cpp
${GIT2CPP_SOURCE_DIR}/subcommand/commit_subcommand.hpp
${GIT2CPP_SOURCE_DIR}/subcommand/config_subcommand.cpp
${GIT2CPP_SOURCE_DIR}/subcommand/config_subcommand.hpp
${GIT2CPP_SOURCE_DIR}/subcommand/fetch_subcommand.cpp
${GIT2CPP_SOURCE_DIR}/subcommand/fetch_subcommand.hpp
${GIT2CPP_SOURCE_DIR}/subcommand/init_subcommand.cpp
Expand Down Expand Up @@ -88,6 +90,8 @@ set(GIT2CPP_SRC
${GIT2CPP_SOURCE_DIR}/wrapper/branch_wrapper.hpp
${GIT2CPP_SOURCE_DIR}/wrapper/commit_wrapper.cpp
${GIT2CPP_SOURCE_DIR}/wrapper/commit_wrapper.hpp
${GIT2CPP_SOURCE_DIR}/wrapper/config_wrapper.cpp
${GIT2CPP_SOURCE_DIR}/wrapper/config_wrapper.hpp
${GIT2CPP_SOURCE_DIR}/wrapper/index_wrapper.cpp
${GIT2CPP_SOURCE_DIR}/wrapper/index_wrapper.hpp
${GIT2CPP_SOURCE_DIR}/wrapper/object_wrapper.cpp
Expand Down
2 changes: 2 additions & 0 deletions src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include "subcommand/checkout_subcommand.hpp"
#include "subcommand/clone_subcommand.hpp"
#include "subcommand/commit_subcommand.hpp"
#include "subcommand/config_subcommand.hpp"
#include "subcommand/fetch_subcommand.hpp"
#include "subcommand/init_subcommand.hpp"
#include "subcommand/log_subcommand.hpp"
Expand Down Expand Up @@ -40,6 +41,7 @@ int main(int argc, char** argv)
checkout_subcommand checkout(lg2_obj, app);
clone_subcommand clone(lg2_obj, app);
commit_subcommand commit(lg2_obj, app);
config_subcommand config(lg2_obj, app);
fetch_subcommand fetch(lg2_obj, app);
reset_subcommand reset(lg2_obj, app);
log_subcommand log(lg2_obj, app);
Expand Down
2 changes: 1 addition & 1 deletion src/subcommand/add_subcommand.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ add_subcommand::add_subcommand(const libgit2_object&, CLI::App& app)
{
auto *sub = app.add_subcommand("add", "Add file contents to the index");

sub->add_option("files", m_add_files, "Files to add");
sub->add_option("<files>", m_add_files, "Files to add");

sub->add_flag("-A,--all,--no-ignore-removal", m_all_flag, "");
// sub->add_flag("-n,--dryrun", dryrun_flag, "");
Expand Down
100 changes: 100 additions & 0 deletions src/subcommand/config_subcommand.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
#include <git2/config.h>
#include <git2/types.h>
#include <iostream>

#include <git2/remote.h>

#include "../utils/git_exception.hpp"
#include "../subcommand/config_subcommand.hpp"
#include "../wrapper/config_wrapper.hpp"
#include "../wrapper/repository_wrapper.hpp"

config_subcommand::config_subcommand(const libgit2_object&, CLI::App& app)
{
auto* config = app.add_subcommand("config", "Get and set repository or global options");
auto* list = config->add_subcommand("list", "List all variables set in config file, along with their values.");
auto* get = config->add_subcommand("get", "Emits the value of the specified key. If key is present multiple times in the configuration, emits the last value. If --all is specified, emits all values associated with key. Returns error code 1 if key is not present.");
auto* set = config->add_subcommand("set", "Set value for one or more config options. By default, this command refuses to write multi-valued config options. Passing --all will replace all multi-valued config options with the new value, whereas --value= will replace all config options whose values match the given pattern.");
auto* unset = config->add_subcommand("unset", "Unset value for one or more config options. By default, this command refuses to unset multi-valued keys. Passing --all will unset all multi-valued config options, whereas --value will unset all config options whose values match the given pattern.");

get->add_option("<name>", m_name, "");
set->add_option("<name>", m_name, "");
set->add_option("<value>", m_value, "");
unset->add_option("<name>", m_name, "");

// sub->add_flag("--local", m_local_flag, "");
// sub->add_flag("--global", m_global_flag, "");
// sub->add_flag("--system", m_system_flag, "");
// sub->add_flag("--worktree", m_worktree_flag, "");

list->callback([this]() { this->run_list(); });
get->callback([this]() { this->run_get(); });
set->callback([this]() { this->run_set(); });
unset->callback([this]() { this->run_unset(); });
}

void config_subcommand::run_list()
{
auto directory = get_current_git_path();
auto repo = repository_wrapper::open(directory);
auto cfg = repo.get_config();

git_config_iterator* iter;
throw_if_error(git_config_iterator_new(&iter, cfg));

git_config_entry* entry;
while (git_config_next(&entry, iter))
{
std::cout << entry->name << "=" << entry->value << std::endl;
}

git_config_iterator_free(iter);
}

void config_subcommand::run_get()
{
if (m_name.empty())
{
std::cout << "error: wrong number of arguments, should be 1" << std::endl;
return;
}

auto directory = get_current_git_path();
auto repo = repository_wrapper::open(directory);
auto cfg = repo.get_config();

git_config_entry* entry = cfg.get_entry(m_name);
std::cout << entry->value << std::endl;

git_config_entry_free(entry);
}

void config_subcommand::run_set()
{
if (m_name.empty() | m_value.empty())
{
std::cout << "error: wrong number of arguments, should be 2" << std::endl;
return;
}

auto directory = get_current_git_path();
auto repo = repository_wrapper::open(directory);
auto cfg = repo.get_config();

cfg.set_entry(m_name, m_value);
}

void config_subcommand::run_unset()
{
if (m_name.empty())
{
std::cout << "error: wrong number of arguments, should be 1" << std::endl;
return;
}

auto directory = get_current_git_path();
auto repo = repository_wrapper::open(directory);
auto cfg = repo.get_config();

cfg.delete_entry(m_name);
}
23 changes: 23 additions & 0 deletions src/subcommand/config_subcommand.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#pragma once

#include <CLI/CLI.hpp>

#include "../utils/common.hpp"

class config_subcommand
{
public:

explicit config_subcommand(const libgit2_object&, CLI::App& app);
void run_list();
void run_set();
void run_get();
void run_unset();

std::string m_name;
std::string m_value;
// bool m_local_flag = false;
// bool m_global_flag = false;
// bool m_system_flag = false;
// bool m_worktree_flag = false;
};
30 changes: 30 additions & 0 deletions src/wrapper/config_wrapper.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#include "../wrapper/config_wrapper.hpp"
#include "../utils/git_exception.hpp"

config_wrapper::config_wrapper(git_config* cfg)
: base_type(cfg)
{
}

config_wrapper::~config_wrapper()
{
git_config_free(p_resource);
p_resource=nullptr;
}

git_config_entry* config_wrapper::get_entry(std::string name)
{
git_config_entry* entry;
throw_if_error(git_config_get_entry(&entry, *this, name.c_str()));
return entry;
}

void config_wrapper::set_entry(std::string name, std::string value)
{
throw_if_error(git_config_set_string(*this, name.c_str(), value.c_str()));
}

void config_wrapper::delete_entry(std::string name)
{
throw_if_error(git_config_delete_entry(*this, name.c_str()));
}
29 changes: 29 additions & 0 deletions src/wrapper/config_wrapper.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#pragma once

#include <string>

#include <git2.h>

#include "../wrapper/wrapper_base.hpp"

class config_wrapper : public wrapper_base<git_config>
{
public:

using base_type = wrapper_base<git_config>;

~config_wrapper();

config_wrapper(config_wrapper&&) noexcept = default;
config_wrapper& operator=(config_wrapper&&) noexcept = default;

git_config_entry* get_entry(std::string name);
void set_entry(std::string name, std::string value);
void delete_entry(std::string name);

private:

config_wrapper(git_config* cfg);

friend class repository_wrapper;
};
11 changes: 11 additions & 0 deletions src/wrapper/repository_wrapper.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
#include <exception>
#include <iostream>

#include "../wrapper/repository_wrapper.hpp"
#include "config_wrapper.hpp"

repository_wrapper::~repository_wrapper()
{
Expand Down Expand Up @@ -341,3 +343,12 @@ std::vector<std::string> repository_wrapper::list_remotes() const
git_strarray_dispose(&remotes);
return result;
}


// Config
config_wrapper repository_wrapper::get_config()
{
git_config* cfg;
throw_if_error(git_repository_config(&cfg, *this));
return config_wrapper(cfg);
}
4 changes: 4 additions & 0 deletions src/wrapper/repository_wrapper.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include "../wrapper/annotated_commit_wrapper.hpp"
#include "../wrapper/branch_wrapper.hpp"
#include "../wrapper/commit_wrapper.hpp"
#include "../wrapper/config_wrapper.hpp"
#include "../wrapper/index_wrapper.hpp"
#include "../wrapper/object_wrapper.hpp"
#include "../wrapper/refs_wrapper.hpp"
Expand Down Expand Up @@ -92,6 +93,9 @@ class repository_wrapper : public wrapper_base<git_repository>
void set_remote_url(std::string_view name, std::string_view url, bool push = false);
std::vector<std::string> list_remotes() const;

// Config
config_wrapper get_config();

private:

repository_wrapper() = default;
Expand Down
15 changes: 12 additions & 3 deletions test/conftest.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import os
import subprocess
from pathlib import Path

import pytest
import subprocess


# Fixture to run test in current tmp_path
Expand All @@ -22,12 +23,20 @@ def git2cpp_path():
def xtl_clone(git2cpp_path, tmp_path, run_in_tmp_path):
url = "https://github.com/xtensor-stack/xtl.git"
clone_cmd = [git2cpp_path, "clone", url]
subprocess.run(clone_cmd, capture_output=True, cwd=tmp_path, text=True)
p = subprocess.run(clone_cmd, capture_output=True, cwd=tmp_path, text=True)
assert p.returncode == 0


@pytest.fixture
def git_config(monkeypatch):
def commit_env_config(monkeypatch):
monkeypatch.setenv("GIT_AUTHOR_NAME", "Jane Doe")
monkeypatch.setenv("GIT_AUTHOR_EMAIL", "jane.doe@blabla.com")
monkeypatch.setenv("GIT_COMMITTER_NAME", "Jane Doe")
monkeypatch.setenv("GIT_COMMITTER_EMAIL", "jane.doe@blabla.com")


# @pytest.fixture
# def dump_config(tmp_path, run_in_tmp_path):
# p = tmp_path / ".gitconfig"
# p.write_text("user.name=Jane Doe")
# p.write_text("core.bare=true")
18 changes: 11 additions & 7 deletions test/test_commit.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,29 +4,33 @@


@pytest.mark.parametrize("all_flag", ["", "-A", "--all", "--no-ignore-removal"])
def test_commit(xtl_clone, git_config, git2cpp_path, tmp_path, monkeypatch, all_flag):
def test_commit(
xtl_clone, commit_env_config, git2cpp_path, tmp_path, monkeypatch, all_flag
):
assert (tmp_path / "xtl").exists()
xtl_path = tmp_path / "xtl"

p = xtl_path / "mook_file.txt"
p.write_text('')
p.write_text("")

cmd_add = [git2cpp_path, 'add', "mook_file.txt"]
cmd_add = [git2cpp_path, "add", "mook_file.txt"]
p_add = subprocess.run(cmd_add, cwd=xtl_path, text=True)
assert p_add.returncode == 0

cmd_status = [git2cpp_path, 'status', "--long"]
cmd_status = [git2cpp_path, "status", "--long"]
p_status = subprocess.run(cmd_status, capture_output=True, cwd=xtl_path, text=True)
assert p_status.returncode == 0

assert "Changes to be committed" in p_status.stdout
assert "new file" in p_status.stdout

cmd_commit = [git2cpp_path, 'commit', "-m", "test commit"]
cmd_commit = [git2cpp_path, "commit", "-m", "test commit"]
p_commit = subprocess.run(cmd_commit, cwd=xtl_path, text=True)
assert p_commit.returncode == 0

cmd_status_2 = [git2cpp_path, 'status', "--long"]
p_status_2 = subprocess.run(cmd_status_2, capture_output=True, cwd=xtl_path, text=True)
cmd_status_2 = [git2cpp_path, "status", "--long"]
p_status_2 = subprocess.run(
cmd_status_2, capture_output=True, cwd=xtl_path, text=True
)
assert p_status_2.returncode == 0
assert "mook_file" not in p_status_2.stdout
Loading
Loading