Tue, 15 Jul 2025 19:14:29 +0200
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};