fix that commits were not listed per repository in the combined view

Tue, 15 Jul 2025 19:14:29 +0200

author
Mike Becker <universe@uap-core.de>
date
Tue, 15 Jul 2025 19:14:29 +0200
changeset 56
3d2720f95cfb
parent 54
586dcd606e47
child 57
4454fe0aed0d

fix that commits were not listed per repository in the combined view

src/commit-data.h file | annotate | diff | comparison | revisions
src/heatmap.cpp file | annotate | diff | comparison | revisions
src/heatmap.h file | annotate | diff | comparison | revisions
src/html.cpp file | annotate | diff | comparison | revisions
src/html.h file | annotate | diff | comparison | revisions
src/main.cpp file | annotate | diff | comparison | revisions
--- a/src/commit-data.h	Sat Jun 28 11:32:08 2025 +0200
+++ b/src/commit-data.h	Tue Jul 15 19:14:29 2025 +0200
@@ -25,10 +25,26 @@
 #ifndef COMMIT_DATA_H
 #define COMMIT_DATA_H
 
+#include <map>
+#include <numeric>
+#include <vector>
+#include <string>
+
 namespace fm {
     struct commits final {
-        std::vector<std::string> summaries;
-        [[nodiscard]] unsigned count() const {return summaries.size();}
+        std::map<
+            std::string, // repository name
+            std::vector<std::string> > summaries;
+
+        [[nodiscard]] unsigned count(const std::string &repo) const {
+            return summaries.at(repo).size();
+        }
+
+        [[nodiscard]] unsigned count() const {
+            return std::accumulate(
+                summaries.begin(), summaries.end(), 0u,
+                [](unsigned sum, const auto &pair) { return sum + pair.second.size(); });
+        }
     };
 }
 
--- a/src/heatmap.cpp	Sat Jun 28 11:32:08 2025 +0200
+++ b/src/heatmap.cpp	Tue Jul 15 19:14:29 2025 +0200
@@ -30,8 +30,9 @@
 
 namespace chrono = std::chrono;
 
-void fm::heatmap::add(const fm::settings &settings, const std::string &log) {
+void fm::heatmap::add(const settings &settings, const std::string &log) {
     using std::string_view_literals::operator ""sv;
+    const std::string repo_key = m_separate ? m_current_repo : "All Repositories";
 
     for (auto line: std::views::split(log, "\n"sv)) {
         if (line.empty()) continue;
@@ -48,11 +49,11 @@
         std::from_chars(date_parts[0].begin(), date_parts[0].end(), year);
         std::from_chars(date_parts[1].begin(), date_parts[1].end(), month);
         std::from_chars(date_parts[2].begin(), date_parts[2].end(), day);
-        auto &[summaries] = m_heatmap[m_current_repo][author][chrono::year_month_day{
+        auto &[summaries] = m_heatmap[repo_key][author][chrono::year_month_day{
             chrono::year{year}, chrono::month{month}, chrono::day{day}
         }];
         ++parts_iter;
-        summaries.emplace_back(std::string_view{*parts_iter});
+        summaries[m_current_repo].emplace_back(std::string_view{*parts_iter});
     }
 }
 
--- a/src/heatmap.h	Sat Jun 28 11:32:08 2025 +0200
+++ b/src/heatmap.h	Tue Jul 15 19:14:29 2025 +0200
@@ -45,8 +45,11 @@
                 std::chrono::year_month_day, // date
                 commits
     >>> m_heatmap;
-    std::string m_current_repo = "All Repositories";
+    std::string m_current_repo;
+    bool m_separate;
 public:
+    explicit heatmap(bool separate) noexcept : m_separate(separate) {}
+
     void set_repo(const std::string& repo) {
         m_current_repo.assign(repo);
     }
--- a/src/html.cpp	Sat Jun 28 11:32:08 2025 +0200
+++ b/src/html.cpp	Tue Jul 15 19:14:29 2025 +0200
@@ -187,13 +187,33 @@
                         const summaries = JSON.parse(this.dataset.summaries);
 
                         // Create popup content
-                        let content = `<span class="close-btn">×</span>
-                                      <h3>${date}: ${summaries.length} commit${summaries.length !== '1' ? 's' : ''}</h3>`;
-                        content += '<ul>';
-                        summaries.forEach(summary => {
-                            content += `<li>${summary}</li>`;
-                        });
-                        content += '</ul>';
+                        let content = '<span class="close-btn">×</span>';
+                        if (Array.isArray(summaries)) {
+                            content += `<h3>${date}: ${summaries.length} commit${summaries.length !== 1 ? 's' : ''}</h3>`;
+                            content += '<ul>';
+                            summaries.forEach(summary => {
+                                content += `<li>${summary}</li>`;
+                            });
+                            content += '</ul>';
+                        } else {
+                            const repos = Object.keys(summaries).sort();
+                            let total_commits = 0;
+                            for (const repo of repos) total_commits += summaries[repo].length;
+                            content += `<h3>${date}: ${total_commits} commit${total_commits !== 1 ? 's' : ''}</h3>`;
+                            for (const repo of repos) {
+                                const commits = summaries[repo];
+                                if (repos.length > 1) {
+                                    content += `<h4>${repo} (${commits.length} commit${commits.length !== 1 ? 's' : ''})</h4>`;
+                                } else {
+                                    content += `<h4>${repo}</h4>`;
+                                }
+                                content += '<ul>';
+                                commits.forEach(commit => {
+                                    content += `<li>${commit}</li>`;
+                                });
+                                content += '</ul>';
+                            }
+                        }
                         popup.innerHTML = content;
 
                         // Position popup near the cell
@@ -326,7 +346,7 @@
     puts("<td class=\"out-of-range\"></td>");
 }
 
-void html::cell(year_month_day ymd, const fm::commits &commits) {
+void html::cell(year_month_day ymd, bool hide_repo_names, const fm::commits &commits) {
     const char *color_class;
     if (commits.count() == 0) {
         color_class = "zero-commits";
@@ -351,25 +371,41 @@
         static_cast<unsigned>(ymd.month()),
         static_cast<unsigned>(ymd.day()));
 
-    // Build a JSON array of commit summaries
-    // We have to iterate in reverse order to sort the summaries chronologically
-    std::string summaries_json = "[";
-    for (const auto &summary :  commits.summaries | std::views::reverse) {
-        // Escape quotes in JSON
-        size_t pos = summary.find('\"');
-        if (pos == std::string::npos) {
-            summaries_json += "\"" + summary + "\",";
-        } else {
-            std::string escaped = summary;
-            do {
-                escaped.replace(pos, 1, "\\\"");
-                pos += 2;
-            } while ((pos = escaped.find('\"', pos)) != std::string::npos);
-            summaries_json += "\"" + escaped + "\",";
+    // Build a JSON object of commit summaries
+    auto escape_json = [](std::string str) static {
+        size_t pos = str.find('\"');
+        if (pos == std::string::npos) return str;
+        std::string escaped = std::move(str);
+        do {
+            escaped.replace(pos, 1, "\\\"");
+            pos += 2;
+        } while ((pos = escaped.find('\"', pos)) != std::string::npos);
+        return escaped;
+    };
+    auto add_summaries = [escape_json](std::string &json, const std::vector<std::string> &summaries) {
+        // 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.pop_back();
+    };
+    std::string summaries_json;
+    if (hide_repo_names) {
+        summaries_json += '[';
+        for (const auto &summaries: commits.summaries | std::views::values) {
+            add_summaries(summaries_json, summaries);
+        }
+        summaries_json += ']';
+    } else {
+        summaries_json += '{';
+        for (const auto &[repo, summaries] : commits.summaries) {
+            summaries_json += "\"" + escape_json(repo) + "\":[";
+            add_summaries(summaries_json, summaries);
+            summaries_json += "],";
+        }
+        summaries_json.pop_back();
+        summaries_json += '}';
     }
-    summaries_json.pop_back();
-    summaries_json += "]";
 
     indent();
     printf("<td class=\"%s\" title=\"%s: %u %s\" data-date=\"%s\" data-summaries=\"%s\"></td>\n",
--- a/src/html.h	Sat Jun 28 11:32:08 2025 +0200
+++ b/src/html.h	Tue Jul 15 19:14:29 2025 +0200
@@ -49,7 +49,7 @@
     void row_begin(unsigned int row);
     void row_end();
     void cell_out_of_range();
-    void cell(std::chrono::year_month_day ymd, const fm::commits &commits = {});
+    void cell(std::chrono::year_month_day ymd, bool hide_repo_names = false, const fm::commits &commits = {});
 
 }
 
--- a/src/main.cpp	Sat Jun 28 11:32:08 2025 +0200
+++ b/src/main.cpp	Tue Jul 15 19:14:29 2025 +0200
@@ -252,11 +252,9 @@
     year_month_day report_end{report_year, December, 31d};
 
     // read the commit logs
-    fm::heatmap heatmap;
+    fm::heatmap heatmap{settings.separate};
     for (auto &&repo : repos.list()) {
-        if (settings.separate) {
-            heatmap.set_repo(repo.name);
-        }
+        heatmap.set_repo(repo.name);
         proc.chdir(repo.path);
         if (repo.type == fm::HG) {
             proc.setbin(settings.hg);
@@ -325,7 +323,7 @@
                     if (find_result == entries.end()) {
                         html::cell(day_to_check);
                     } else {
-                        html::cell(day_to_check, find_result->second);
+                        html::cell(day_to_check, settings.separate, find_result->second);
                     }
                     // advance seven days and one column
                     day_to_check += days{7};

mercurial