Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
195 changes: 190 additions & 5 deletions src/subcommand/log_subcommand.cpp
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
#include <format>
#include <git2.h>
#include <git2/revwalk.h>
#include <git2/oid.h>
#include <git2/refs.h>
#include <git2/types.h>
#include <sstream>
#include <string_view>
#include <vector>

#include <termcolor/termcolor.hpp>

Expand Down Expand Up @@ -50,15 +53,181 @@ void print_time(git_time intime, std::string prefix)
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::vector<std::string> get_tags_for_commit(repository_wrapper& repo, const git_oid* commit_oid)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The commit_oid parameter should be passed by reference.

{
std::vector<std::string> tags;
git_strarray tag_names = {0};

if (git_tag_list(&tag_names, repo) != 0)
{
return tags;
}

for (size_t i = 0; i < tag_names.count; i++)
{
std::string tag_name = tag_names.strings[i];
std::string ref_name = "refs/tags/" + std::string(tag_name);

reference_wrapper tag_ref = repo.find_reference(ref_name);
object_wrapper peeled = tag_ref.peel<object_wrapper>();

if (git_oid_equal(&peeled.oid(), commit_oid))
{
tags.push_back(std::string(tag_name));
}
}

git_strarray_dispose(&tag_names); // TODO: refactor git_strarray_wrapper to use it here
return tags;
}

std::vector<std::string> get_branches_for_commit(repository_wrapper& repo, git_branch_t type, const git_oid& commit_oid, const std::string exclude_branch)
{
std::vector<std::string> branches;

auto branch_iter = repo.iterate_branches(type);
while (auto branch = branch_iter.next())
{
const git_oid* branch_target = nullptr;
git_reference* ref = branch.value();

if (git_reference_type(ref) == GIT_REFERENCE_DIRECT)
{
branch_target = git_reference_target(ref);
}
else if (git_reference_type(ref) == GIT_REFERENCE_SYMBOLIC)
{
git_reference* resolved = nullptr;
if (git_reference_resolve(&resolved, ref) == 0)
{
branch_target = git_reference_target(resolved);
git_reference_free(resolved);
}
}

if (branch_target && git_oid_equal(branch_target, &commit_oid))
{
std::string branch_name;
branch_name = branch->name();
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
std::string branch_name;
branch_name = branch->name();
std::string branch_name = branch->name();

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

I wrote it this way because it's not happy with the one line option:
error: conversion from 'std::string_view' {aka 'std::basic_string_view<char>'} to non-scalar type 'std::string' {aka 'std::__cxx11::basic_string<char>'} requested

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

How about

std::string branch_name(branch->name());

instead, which works for me on macos?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I missed that the return type of branch::name was std::string_view. In that case the correct solution is the line from Ian.

if (type == GIT_BRANCH_LOCAL)
{
if (branch_name != exclude_branch)
{
branches.push_back(branch_name);
}
}
else
{
branches.push_back(branch_name);
}
}
}

return branches;
}

struct commit_refs
{
std::string head_branch;
std::vector<std::string> tags;
std::vector<std::string> local_branches;
std::vector<std::string> remote_branches;

bool has_refs() const {
return !head_branch.empty() || !tags.empty() ||
!local_branches.empty() || !remote_branches.empty();
}
};

commit_refs get_refs_for_commit(repository_wrapper& repo, const git_oid& commit_oid)
{
commit_refs refs;

if (!repo.is_head_unborn())
{
auto head = repo.head();
auto head_taget = head.target();
if (git_oid_equal(head_taget, &commit_oid))
{
refs.head_branch = head.short_name();
}
}

refs.tags = get_tags_for_commit(repo, &commit_oid);
refs.local_branches = get_branches_for_commit(repo, GIT_BRANCH_LOCAL, commit_oid, refs.head_branch);
refs.remote_branches = get_branches_for_commit(repo, GIT_BRANCH_REMOTE, commit_oid, "");

return refs;
}

void print_refs(const commit_refs& refs)
{
if (!refs.has_refs())
{
return;
}

std::cout << " (";

bool first = true;

if (!refs.head_branch.empty())
{
std::cout << termcolor::bold << termcolor::cyan << "HEAD" << termcolor::reset
<< termcolor::yellow << " -> " << termcolor::reset
<< termcolor::bold << termcolor::green << refs.head_branch << termcolor::reset
<< termcolor::yellow;
first = false;
}

for (const auto& tag :refs.tags)
{
if (!first)
{
std::cout << ", ";
}
std::cout << termcolor::bold << "tag: " << tag << termcolor::reset << termcolor::yellow;
first = false;
}

for (const auto& remote : refs.remote_branches)
{
if (!first)
{
std::cout << ", ";
}
std::cout << termcolor::bold << termcolor::red << remote << termcolor::reset << termcolor::yellow;
first = false;
}

for (const auto& local : refs.local_branches)
{
if (!first)
{
std::cout << ", ";
}
std::cout << termcolor::bold << termcolor::green << local << termcolor::reset << termcolor::yellow;
first = false;
}

std::cout << ")" << termcolor::reset;
}

void print_commit(repository_wrapper& repo, 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);

stream_colour_fn colour = termcolor::yellow;
std::cout << colour << "commit " << buf << termcolor::reset << std::endl;
std::cout << colour << "commit " << buf;

commit_refs refs = get_refs_for_commit(repo, commit.oid());
print_refs(refs);

std::cout << termcolor::reset << std::endl;

if (m_format_flag=="fuller")
{
std::cout << "Author:\t " << author.name() << " " << author.email() << std::endl;
Expand All @@ -78,7 +247,19 @@ void print_commit(const commit_wrapper& commit, std::string m_format_flag)
print_time(author.when(), "Date:\t");
}
}
std::cout << "\n " << commit.message() << "\n" << std::endl;

std::string message = commit.message();
while (!message.empty() && message.back() == '\n')
{
message.pop_back();
}
std::istringstream message_stream(message);
std::string line;
while (std::getline(message_stream, line))
{
std::cout << "\n " << line;
}
std::cout << std::endl;
}

void log_subcommand::run()
Expand All @@ -102,8 +283,12 @@ void log_subcommand::run()
git_oid commit_oid;
while (!walker.next(commit_oid) && i<m_max_count_flag)
{
if (i != 0)
{
std::cout << std::endl;
}
commit_wrapper commit = repo.find_commit(commit_oid);
print_commit(commit, m_format_flag);
print_commit(repo, commit, m_format_flag);
++i;
}

Expand Down
Loading