Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 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: 2 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ set(GIT2CPP_SRC
${GIT2CPP_SOURCE_DIR}/subcommand/commit_subcommand.hpp
${GIT2CPP_SOURCE_DIR}/subcommand/init_subcommand.cpp
${GIT2CPP_SOURCE_DIR}/subcommand/init_subcommand.hpp
${GIT2CPP_SOURCE_DIR}/subcommand/log_subcommand.cpp
${GIT2CPP_SOURCE_DIR}/subcommand/log_subcommand.hpp
${GIT2CPP_SOURCE_DIR}/subcommand/reset_subcommand.cpp
${GIT2CPP_SOURCE_DIR}/subcommand/reset_subcommand.hpp
${GIT2CPP_SOURCE_DIR}/subcommand/status_subcommand.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/clone_subcommand.hpp"
#include "subcommand/commit_subcommand.hpp"
#include "subcommand/init_subcommand.hpp"
#include "subcommand/log_subcommand.hpp"
#include "subcommand/reset_subcommand.hpp"
#include "subcommand/status_subcommand.hpp"

Expand All @@ -33,6 +34,7 @@ int main(int argc, char** argv)
clone_subcommand clone(lg2_obj, app);
commit_subcommand commit(lg2_obj, app);
reset_subcommand reset(lg2_obj, app);
log_subcommand log(lg2_obj, app);

app.require_subcommand(/* min */ 0, /* max */ 1);

Expand Down
101 changes: 101 additions & 0 deletions src/subcommand/log_subcommand.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
#include <format>
#include <git2.h>
#include <git2/revwalk.h>
#include <git2/types.h>
#include <string_view>

#include "log_subcommand.hpp"
#include "../wrapper/repository_wrapper.hpp"
#include "../wrapper/commit_wrapper.hpp"

log_subcommand::log_subcommand(const libgit2_object&, CLI::App& app)
{
auto *sub = app.add_subcommand("log", "Shows commit logs");

sub->add_flag("--format", m_format_flag, "Pretty-print the contents of the commit logs in a given format, where <format> can be one of full and fuller");
sub->add_option("-n,--max-count", m_max_count_flag, "Limit the output to <number> commits.");
// sub->add_flag("--oneline", m_oneline_flag, "This is a shorthand for --pretty=oneline --abbrev-commit used together.");

sub->callback([this]() { this->run(); });
};

void print_time(git_time intime, std::string prefix)
{
char sign, out[32];
struct tm *intm;
int offset, hours, minutes;
time_t t;

offset = intime.offset;
if (offset < 0) {
sign = '-';
offset = -offset;
}
else
{
sign = '+';
}

hours = offset / 60;
minutes = offset % 60;

t = (time_t)intime.time + (intime.offset * 60);

intm = gmtime(&t);
strftime(out, sizeof(out), "%a %b %e %T %Y", intm);

std::cout << prefix << out << " " << sign << std::format("{:02d}", hours) << std::format("{:02d}", minutes) <<std::endl;
}

void print_commit(const commit_wrapper& commit, std::string m_format_flag)
{
std::string buf = commit.commit_oid_tostr();

signature_wrapper author = signature_wrapper::get_commit_author(commit);
signature_wrapper committer = signature_wrapper::get_commit_committer(commit);

std::cout << "\033[0;33m" << "commit " << buf << "\033[0m" << std::endl;
if (m_format_flag=="fuller")
{
std::cout << "Author:\t " << author.name() << " " << author.email() << std::endl;
print_time(author.when(), "AuthorDate: ");
std::cout << "Commit:\t " << committer.name() << " " << committer.email() << std::endl;
print_time(committer.when(), "CommitDate: ");
}
else
{
std::cout << "Author:\t" << author.name() << " " << author.email() << std::endl;
if (m_format_flag=="full")
{
std::cout << "Commit:\t" << committer.name() << " " << committer.email() << std::endl;
}
else
{
print_time(author.when(), "Date:\t");
}
}
std::cout << "\n " << git_commit_message(commit) << "\n" << std::endl;
}

void log_subcommand::run()
{
auto directory = get_current_git_path();
auto bare = false;
auto repo = repository_wrapper::init(directory, bare);
// auto branch_name = repo.head().short_name();

git_revwalk* walker;
git_revwalk_new(&walker, repo);
git_revwalk_push_head(walker);

std::size_t i=0;
git_oid commit_oid;
while (!git_revwalk_next(&commit_oid, walker) && i<m_max_count_flag)
{
commit_wrapper commit = repo.find_commit(commit_oid);
print_commit(commit, m_format_flag);
i+=1;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
i+=1;
++i;

}

git_revwalk_free(walker);
}
21 changes: 21 additions & 0 deletions src/subcommand/log_subcommand.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#pragma once

#include <CLI/CLI.hpp>
#include <cstddef>
#include <limits>

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


class log_subcommand
{
public:

explicit log_subcommand(const libgit2_object&, CLI::App& app);
void run();

private:
std::string m_format_flag;
int m_max_count_flag=std::numeric_limits<int>::max();
// bool m_oneline_flag = false;
};
6 changes: 6 additions & 0 deletions src/wrapper/commit_wrapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,9 @@ const git_oid& commit_wrapper::oid() const
{
return *git_commit_id(p_resource);
}

std::string commit_wrapper::commit_oid_tostr() const
{
char buf[GIT_OID_SHA1_HEXSIZE + 1];
return git_oid_tostr(buf, sizeof(buf), &this->oid());
}
1 change: 1 addition & 0 deletions src/wrapper/commit_wrapper.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ class commit_wrapper : public wrapper_base<git_commit>
operator git_object*() const noexcept;

const git_oid& oid() const;
std::string commit_oid_tostr() const;

private:

Expand Down
36 changes: 35 additions & 1 deletion src/wrapper/signature_wrapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,48 @@

signature_wrapper::~signature_wrapper()
{
git_signature_free(p_resource);
if (m_ownership)
{
git_signature_free(p_resource);
}
p_resource=nullptr;
}

std::string_view signature_wrapper::name() const
{
return p_resource->name;
}

std::string_view signature_wrapper::email() const
{
return p_resource->email;
}

git_time signature_wrapper::when() const
{
return p_resource->when;
}

signature_wrapper::author_committer_signatures signature_wrapper::get_default_signature_from_env(repository_wrapper& rw)
{
signature_wrapper author;
signature_wrapper committer;
throw_if_error(git_signature_default_from_env(&(author.p_resource), &(committer.p_resource), rw));
return {std::move(author), std::move(committer)};
}

signature_wrapper signature_wrapper::get_commit_author(const commit_wrapper& cw)
{
signature_wrapper author;
author.p_resource = const_cast<git_signature*>(git_commit_author(cw));
author.m_ownership = false;
return author;
}

signature_wrapper signature_wrapper::get_commit_committer(const commit_wrapper& cw)
{
signature_wrapper committer;
committer.p_resource = const_cast<git_signature*>(git_commit_committer(cw));
committer.m_ownership = false;
return committer;
}
9 changes: 9 additions & 0 deletions src/wrapper/signature_wrapper.hpp
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
#pragma once

#include <utility>
#include <string_view>

#include <git2.h>

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

class commit_wrapper;
class repository_wrapper;

class signature_wrapper : public wrapper_base<git_signature>
Expand All @@ -18,9 +20,16 @@ class signature_wrapper : public wrapper_base<git_signature>
signature_wrapper(signature_wrapper&&) = default;
signature_wrapper& operator=(signature_wrapper&&) = default;

std::string_view name() const;
std::string_view email() const;
git_time when() const;

static author_committer_signatures get_default_signature_from_env(repository_wrapper&);
static signature_wrapper get_commit_author(const commit_wrapper&);
static signature_wrapper get_commit_committer(const commit_wrapper&);

private:

signature_wrapper() = default;
bool m_ownership=true;
};
54 changes: 54 additions & 0 deletions test/test_log.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import subprocess

import pytest

@pytest.mark.parametrize("format_flag", ["", "--format=full", "--format=fuller"])
def test_log(xtl_clone, git_config, git2cpp_path, tmp_path, monkeypatch, format_flag):
assert (tmp_path / "xtl").exists()
xtl_path = tmp_path / "xtl"

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

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_commit = [git2cpp_path, 'commit', "-m", "test commit"]
p_commit = subprocess.run(cmd_commit, cwd=xtl_path, text=True)
assert p_commit.returncode == 0

cmd_log = [git2cpp_path, 'log']
if format_flag != "":
cmd_log.append(format_flag)
p_log = subprocess.run(cmd_log, capture_output=True, cwd=xtl_path, text=True)
assert p_log.returncode == 0
assert "Jane Doe" in p_log.stdout
assert "test commit" in p_log.stdout

if format_flag == "":
assert "Commit" not in p_log.stdout
else:
assert "Commit" in p_log.stdout
if format_flag == "--format=full":
assert "Date" not in p_log.stdout
else:
assert "CommitDate" in p_log.stdout


@pytest.mark.parametrize("max_count_flag", ["", "-n", "--max-count"])
def test_max_count(xtl_clone, git_config, git2cpp_path, tmp_path, monkeypatch, max_count_flag):
assert (tmp_path / "xtl").exists()
xtl_path = tmp_path / "xtl"

cmd_log = [git2cpp_path, 'log']
if max_count_flag != "":
cmd_log.append(max_count_flag)
cmd_log.append("2")
p_log = subprocess.run(cmd_log, capture_output=True, cwd=xtl_path, text=True)
assert p_log.returncode == 0

if max_count_flag == "":
assert p_log.stdout.count("Author") > 2
else:
assert p_log.stdout.count("Author") == 2