Skip to content

Commit 1ba9701

Browse files
authored
Add tracking info for log subcommand (#106)
* Add tracking info * address review comments * address review comments
1 parent 1a6fb5b commit 1ba9701

File tree

2 files changed

+383
-5
lines changed

2 files changed

+383
-5
lines changed

src/subcommand/log_subcommand.cpp

Lines changed: 189 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
#include <format>
22
#include <git2.h>
3-
#include <git2/revwalk.h>
3+
#include <git2/oid.h>
4+
#include <git2/refs.h>
45
#include <git2/types.h>
6+
#include <sstream>
57
#include <string_view>
8+
#include <vector>
69

710
#include <termcolor/termcolor.hpp>
811

@@ -50,15 +53,180 @@ void print_time(git_time intime, std::string prefix)
5053
std::cout << prefix << out << " " << sign << std::format("{:02d}", hours) << std::format("{:02d}", minutes) <<std::endl;
5154
}
5255

53-
void print_commit(const commit_wrapper& commit, std::string m_format_flag)
56+
std::vector<std::string> get_tags_for_commit(repository_wrapper& repo, const git_oid& commit_oid)
57+
{
58+
std::vector<std::string> tags;
59+
git_strarray tag_names = {0};
60+
61+
if (git_tag_list(&tag_names, repo) != 0)
62+
{
63+
return tags;
64+
}
65+
66+
for (size_t i = 0; i < tag_names.count; i++)
67+
{
68+
std::string tag_name = tag_names.strings[i];
69+
std::string ref_name = "refs/tags/" + std::string(tag_name);
70+
71+
reference_wrapper tag_ref = repo.find_reference(ref_name);
72+
object_wrapper peeled = tag_ref.peel<object_wrapper>();
73+
74+
if (git_oid_equal(&peeled.oid(), &commit_oid))
75+
{
76+
tags.push_back(std::string(tag_name));
77+
}
78+
}
79+
80+
git_strarray_dispose(&tag_names); // TODO: refactor git_strarray_wrapper to use it here
81+
return tags;
82+
}
83+
84+
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)
85+
{
86+
std::vector<std::string> branches;
87+
88+
auto branch_iter = repo.iterate_branches(type);
89+
while (auto branch = branch_iter.next())
90+
{
91+
const git_oid* branch_target = nullptr;
92+
git_reference* ref = branch.value();
93+
94+
if (git_reference_type(ref) == GIT_REFERENCE_DIRECT)
95+
{
96+
branch_target = git_reference_target(ref);
97+
}
98+
else if (git_reference_type(ref) == GIT_REFERENCE_SYMBOLIC)
99+
{
100+
git_reference* resolved = nullptr;
101+
if (git_reference_resolve(&resolved, ref) == 0)
102+
{
103+
branch_target = git_reference_target(resolved);
104+
git_reference_free(resolved);
105+
}
106+
}
107+
108+
if (branch_target && git_oid_equal(branch_target, &commit_oid))
109+
{
110+
std::string branch_name(branch->name());
111+
if (type == GIT_BRANCH_LOCAL)
112+
{
113+
if (branch_name != exclude_branch)
114+
{
115+
branches.push_back(branch_name);
116+
}
117+
}
118+
else
119+
{
120+
branches.push_back(branch_name);
121+
}
122+
}
123+
}
124+
125+
return branches;
126+
}
127+
128+
struct commit_refs
129+
{
130+
std::string head_branch;
131+
std::vector<std::string> tags;
132+
std::vector<std::string> local_branches;
133+
std::vector<std::string> remote_branches;
134+
135+
bool has_refs() const {
136+
return !head_branch.empty() || !tags.empty() ||
137+
!local_branches.empty() || !remote_branches.empty();
138+
}
139+
};
140+
141+
commit_refs get_refs_for_commit(repository_wrapper& repo, const git_oid& commit_oid)
142+
{
143+
commit_refs refs;
144+
145+
if (!repo.is_head_unborn())
146+
{
147+
auto head = repo.head();
148+
auto head_taget = head.target();
149+
if (git_oid_equal(head_taget, &commit_oid))
150+
{
151+
refs.head_branch = head.short_name();
152+
}
153+
}
154+
155+
refs.tags = get_tags_for_commit(repo, commit_oid);
156+
refs.local_branches = get_branches_for_commit(repo, GIT_BRANCH_LOCAL, commit_oid, refs.head_branch);
157+
refs.remote_branches = get_branches_for_commit(repo, GIT_BRANCH_REMOTE, commit_oid, "");
158+
159+
return refs;
160+
}
161+
162+
void print_refs(const commit_refs& refs)
163+
{
164+
if (!refs.has_refs())
165+
{
166+
return;
167+
}
168+
169+
std::cout << " (";
170+
171+
bool first = true;
172+
173+
if (!refs.head_branch.empty())
174+
{
175+
std::cout << termcolor::bold << termcolor::cyan << "HEAD" << termcolor::reset
176+
<< termcolor::yellow << " -> " << termcolor::reset
177+
<< termcolor::bold << termcolor::green << refs.head_branch << termcolor::reset
178+
<< termcolor::yellow;
179+
first = false;
180+
}
181+
182+
for (const auto& tag :refs.tags)
183+
{
184+
if (!first)
185+
{
186+
std::cout << ", ";
187+
}
188+
std::cout << termcolor::bold << "tag: " << tag << termcolor::reset << termcolor::yellow;
189+
first = false;
190+
}
191+
192+
for (const auto& remote : refs.remote_branches)
193+
{
194+
if (!first)
195+
{
196+
std::cout << ", ";
197+
}
198+
std::cout << termcolor::bold << termcolor::red << remote << termcolor::reset << termcolor::yellow;
199+
first = false;
200+
}
201+
202+
for (const auto& local : refs.local_branches)
203+
{
204+
if (!first)
205+
{
206+
std::cout << ", ";
207+
}
208+
std::cout << termcolor::bold << termcolor::green << local << termcolor::reset << termcolor::yellow;
209+
first = false;
210+
}
211+
212+
std::cout << ")" << termcolor::reset;
213+
}
214+
215+
void print_commit(repository_wrapper& repo, const commit_wrapper& commit, std::string m_format_flag)
54216
{
55217
std::string buf = commit.commit_oid_tostr();
56218

57219
signature_wrapper author = signature_wrapper::get_commit_author(commit);
58220
signature_wrapper committer = signature_wrapper::get_commit_committer(commit);
59221

60222
stream_colour_fn colour = termcolor::yellow;
61-
std::cout << colour << "commit " << buf << termcolor::reset << std::endl;
223+
std::cout << colour << "commit " << buf;
224+
225+
commit_refs refs = get_refs_for_commit(repo, commit.oid());
226+
print_refs(refs);
227+
228+
std::cout << termcolor::reset << std::endl;
229+
62230
if (m_format_flag=="fuller")
63231
{
64232
std::cout << "Author:\t " << author.name() << " " << author.email() << std::endl;
@@ -78,7 +246,19 @@ void print_commit(const commit_wrapper& commit, std::string m_format_flag)
78246
print_time(author.when(), "Date:\t");
79247
}
80248
}
81-
std::cout << "\n " << commit.message() << "\n" << std::endl;
249+
250+
std::string message = commit.message();
251+
while (!message.empty() && message.back() == '\n')
252+
{
253+
message.pop_back();
254+
}
255+
std::istringstream message_stream(message);
256+
std::string line;
257+
while (std::getline(message_stream, line))
258+
{
259+
std::cout << "\n " << line;
260+
}
261+
std::cout << std::endl;
82262
}
83263

84264
void log_subcommand::run()
@@ -102,8 +282,12 @@ void log_subcommand::run()
102282
git_oid commit_oid;
103283
while (!walker.next(commit_oid) && i<m_max_count_flag)
104284
{
285+
if (i != 0)
286+
{
287+
std::cout << std::endl;
288+
}
105289
commit_wrapper commit = repo.find_commit(commit_oid);
106-
print_commit(commit, m_format_flag);
290+
print_commit(repo, commit, m_format_flag);
107291
++i;
108292
}
109293

0 commit comments

Comments
 (0)