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
84264void 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