add reading commit hashes from the commit log - resolves #807

Wed, 25 Feb 2026 21:13:55 +0100

author
Mike Becker <universe@uap-core.de>
date
Wed, 25 Feb 2026 21:13:55 +0100
changeset 76
110a234a3260
parent 75
857af79337d5
child 77
43a1ba0e11e3

add reading commit hashes from the commit log - resolves #807

CHANGELOG file | annotate | diff | comparison | revisions
src/commit-data.h file | annotate | diff | comparison | revisions
src/heatmap.cpp file | annotate | diff | comparison | revisions
src/html.cpp file | annotate | diff | comparison | revisions
src/main.cpp file | annotate | diff | comparison | revisions
--- a/CHANGELOG	Fri Feb 06 18:44:47 2026 +0100
+++ b/CHANGELOG	Wed Feb 25 21:13:55 2026 +0100
@@ -2,6 +2,7 @@
 
 - Add --styles-and-script option to output the default CSS and Javascript
 - Add monthly summaries
+- Add reading commit hashes from the commit log
 
 Version 1.1.2 - 2025-12-15
 
--- a/src/commit-data.h	Fri Feb 06 18:44:47 2026 +0100
+++ b/src/commit-data.h	Wed Feb 25 21:13:55 2026 +0100
@@ -31,14 +31,24 @@
 #include <string>
 
 namespace fm {
+    struct commit_info final {
+        std::string hash;
+        std::string message;
+        commit_info(std::string_view hash, std::string_view message)
+            : hash{hash}, message {message} {}
+    };
+
     using commit_counts = std::unordered_map<
         std::string, // repository name
         unsigned>;
     using summaries_lists = std::unordered_map<
         std::string, // repository name
-        std::vector<std::string> >;
+        std::vector<commit_info> >;
     using tag_lists = std::unordered_map<
         std::string, // repository name
+        std::vector<commit_info> >;
+    using tag_summaries = std::unordered_map<
+        std::string, // repository name
         std::vector<std::string> >;
 
     struct commits final {
@@ -57,7 +67,7 @@
     };
 
     struct commit_summary final {
-        tag_lists tags_with_date;
+        tag_summaries tags_with_date;
         commit_counts commits;
 
         [[nodiscard]] unsigned count(const std::string &repo) const {
--- a/src/heatmap.cpp	Fri Feb 06 18:44:47 2026 +0100
+++ b/src/heatmap.cpp	Wed Feb 25 21:13:55 2026 +0100
@@ -37,16 +37,17 @@
     for (auto line: std::views::split(log, "\n"sv)) {
         if (line.empty()) continue;
 
-        // find all delimiters
+        // split the line by delimiter
         const auto line_view = std::string_view{line};
-        const auto pos_delim1 = line_view.find('#', 0);
-        const auto pos_delim2 = line_view.find('#', pos_delim1 + 1);
-        const auto pos_delim3 = line_view.find('#', pos_delim2 + 1);
+        auto parts = std::views::split(line_view, '#')
+                     | std::views::transform([](auto r) { return std::string_view(r); })
+                     | std::ranges::to<std::vector>();
 
-        std::string author{settings.map_author(line_view.substr(0, pos_delim1))};
-        std::string_view date_view{line_view.substr(pos_delim1+1, pos_delim2 - pos_delim1 - 1)};
-        std::string_view tags_view{line_view.substr(pos_delim2+1, pos_delim3 - pos_delim2 - 1)};
-        std::string_view summary_view{line_view.substr(pos_delim3+1)};
+        std::string_view hash_view{parts[0]};
+        std::string author{settings.map_author(parts[1])};
+        std::string_view date_view{parts[2]};
+        std::string_view tags_view{parts[3]};
+        std::string_view summary_view{parts[4]};
 
         int year = 0;
         unsigned int month = 0, day = 0;
@@ -60,10 +61,10 @@
                 m_heatmap[repo_key][author][chrono::year_month_day{
                     chrono::year{year}, chrono::month{month}, chrono::day{day}
                 }];
-        summaries[m_current_repo].emplace_back(summary_view);
+        summaries[m_current_repo].emplace_back(hash_view, summary_view);
         // special case: if the (only) tag is "tip", we do not add it
         if (!tags_view.empty() && tags_view != "tip") {
-            tags[m_current_repo].emplace_back(tags_view);
+            tags[m_current_repo].emplace_back(hash_view, tags_view);
         }
     }
 }
@@ -82,10 +83,10 @@
         }
         for (auto &&[reponame, tags]: commits.tags) {
             if (tags.empty()) continue;
-            std::string tag_list = tags.at(0);
+            std::string tag_list = tags.at(0).message;
             for (unsigned i = 1; i < tags.size(); ++i) {
                 tag_list.append(", ");
-                tag_list.append(tags.at(i));
+                tag_list.append(tags.at(i).message);
             }
             cs.tags_with_date[reponame].emplace_back(
                     std::format("{} on {}-{:02}-{:02}",
--- a/src/html.cpp	Fri Feb 06 18:44:47 2026 +0100
+++ b/src/html.cpp	Wed Feb 25 21:13:55 2026 +0100
@@ -92,7 +92,7 @@
         if (hide_repo_names) {
             for (const auto &tags_vector: tags | std::views::values) {
                 for (const auto &tag: tags_vector) {
-                    tags_json += escape_json(tag);
+                    tags_json += escape_json(tag.message);
                     tags_json += ' ';
                 }
             }
@@ -104,7 +104,7 @@
             for (const auto &[repo, tags_vector] : tags) {
                 tags_json += "\"" + escape_json(repo) + "\":\"";
                 for (const auto &tag: tags_vector) {
-                    tags_json += escape_json(tag);
+                    tags_json += escape_json(tag.message);
                     tags_json += ' ';
                 }
                 if (!tags_vector.empty()) {
@@ -120,7 +120,7 @@
         return tags_json;
     }
 
-    static std::string build_tag_array(fm::tag_lists tags, bool hide_repo_names) {
+    static std::string build_tag_summary(fm::tag_summaries tags, bool hide_repo_names) {
         std::string tags_json;
         if (hide_repo_names) {
             tags_json += '[';
@@ -482,7 +482,7 @@
                 commit_summary_json += '}';
                 commit_summary = std::format("data-commits=\"{}\"", encode(commit_summary_json));
             }
-            std::string tags = build_tag_array(summary.tags_with_date, hide_repo_names);
+            std::string tags = build_tag_summary(summary.tags_with_date, hide_repo_names);
             printf("<th scope=\"col\" title=\"Total commits: %u\" colspan=\"%d\" data-total=\"%u\" %s data-tags=\"%s\">%s</th>\n",
                 total, colspans[i], total,
                 commit_summary.c_str(),
@@ -535,10 +535,10 @@
     }
 
     // Build a JSON object of commit summaries
-    auto add_summaries = [](std::string &json, const std::vector<std::string> &summaries) static {
+    auto add_summaries = [](std::string &json, const std::vector<fm::commit_info> &summaries) static {
         // We have to iterate in reverse order to sort the summaries chronologically
         for (const auto &summary :  summaries | std::views::reverse) {
-            json += "\"" + escape_json(summary) + "\",";
+            json += "\"" + escape_json(summary.message) + "\",";
         }
         json.pop_back();
     };
--- a/src/main.cpp	Fri Feb 06 18:44:47 2026 +0100
+++ b/src/main.cpp	Wed Feb 25 21:13:55 2026 +0100
@@ -276,7 +276,7 @@
             proc.setbin(settings.hg);
             if (proc.exec_log({"log",
                 "--date", std::format("{0}-01-01 00:00:00 to {0}-12-31 23:59:59", report_year),
-                "--template", "{author}#{date|shortdate}#{tags}#{desc|firstline}\\n"})) {
+                "--template", "{node}#{author}#{date|shortdate}#{tags}#{desc|firstline}\\n"})) {
                 fprintf(stderr, "Reading commit log for repo '%s' failed!\n", repo.path.c_str());
                 return EXIT_FAILURE;
             }
@@ -289,7 +289,7 @@
                 "--decorate-refs=refs/tags/",
                 "--since", std::format("{0}-01-01 00:00:00", report_year),
                 "--until", std::format("{0}-12-31 23:59:59", report_year),
-                "--format=tformat:%an <%ae>#%cs#%(decorate:prefix=,suffix=,tag=,separator= )#%s"})) {
+                "--format=tformat:%H#%an <%ae>#%cs#%(decorate:prefix=,suffix=,tag=,separator= )#%s"})) {
                 fprintf(stderr, "Reading commit log for repo '%s' failed!\n", repo.path.c_str());
                 return EXIT_FAILURE;
             }

mercurial