ost << html::thead();
ost << html::tbody();
struct payload_t
{
std::ostream& ost; // NOLINT
const startgit::repository& repo; // NOLINT
} payload(ost, repo);
auto callback = +[](const char*, git_oid* objid, void* payload_p)
{
auto l_payload = *reinterpret_cast<payload_t*>(payload_p); // NOLINT
const auto tag = l_payload.repo.get().tag_lookup(git2wrap::oid(objid));
l_payload.ost << html::tr()
.add(html::td(" "))
.add(html::td(tag.get_name()))
.add(html::td(
long_to_string(tag.get_tagger().get_time().time)))
.add(html::td(tag.get_tagger().get_name()));
return 0;
};
repo.get().tag_foreach(callback, &payload);
for (const auto& tag : repo.get_tags()) {
ost << html::tr()
.add(html::td(" "))
.add(html::td(tag.get_name()))
.add(html::td(tag.get_time()))
.add(html::td(tag.get_name()));
}
ost << html::tbody();
ost << html::table();
}
void write_file_changes(std::ostream& ost, const git2wrap::diff& diff)
void write_file_changes(std::ostream& ost, const startgit::diff& diff)
{
using namespace hemplate; // NOLINT
const auto file_cb =
+[](const git_diff_delta* delta, float /* progress */, void* payload)
{
auto& l_ost = *reinterpret_cast<std::ostream*>(payload); // NOLINT
ost << html::b("Diffstat:");
ost << html::table() << html::tbody();
for (const auto& delta : diff.get_deltas()) {
static const char* marker = " ADMRC T ";
const std::string filename = delta->new_file.path;
l_ost << html::tr()
.add(html::td(std::string(1, marker[delta->status])))
.add(html::td().add(
html::a(filename).set("href", "#" + filename)))
.add(html::td("|"))
.add(html::td("..."));
const std::string link = std::format("#{}", delta->new_file.path);
return 0;
};
ost << html::tr()
.add(html::td(std::string(1, marker[delta->status])))
.add(html::td().add(
html::a(delta->new_file.path).set("href", link)))
.add(html::td("|"))
.add(html::td("..."));
}
ost << html::b("Diffstat:");
ost << html::table() << html::tbody();
diff.foreach(file_cb, nullptr, nullptr, nullptr, &ost);
ost << html::tbody() << html::table();
/*
ost << html::pre(
std::format("{} files changed, {} insertions(+), {} deletions(-)"));
*/
ost << html::span(
std::format("{} files changed, {} insertions(+), {} deletions(-)",
diff.get_files_changed(),
diff.get_insertions(),
diff.get_deletions()));
}
void write_file_diffs(std::ostream& ost, const git2wrap::diff& diff)
void write_file_diffs(std::ostream& ost, const startgit::diff& diff)
{
using namespace hemplate; // NOLINT
const auto file_cb =
+[](const git_diff_delta* delta, float /* progress */, void* payload)
{
auto& l_ost = *reinterpret_cast<std::ostream*>(payload); // NOLINT
for (const auto& delta : diff.get_deltas()) {
const auto new_link = std::format("../file/{}.html", delta->new_file.path);
const auto old_link = std::format("../file/{}.html", delta->old_file.path);
l_ost << html::h3().set("id", delta->new_file.path);
l_ost << "diff --git";
l_ost << " a/" << html::a(delta->new_file.path).set("href", new_link);
l_ost << " b/" << html::a(delta->old_file.path).set("href", old_link);
l_ost << html::h3();
return 0;
};
ost << html::h3().set("id", delta->new_file.path);
ost << "diff --git";
ost << " a/" << html::a(delta->new_file.path).set("href", new_link);
ost << " b/" << html::a(delta->old_file.path).set("href", old_link);
ost << html::h3();
const auto hunk_cb = +[](const git_diff_delta* /* delta */,
const git_diff_hunk* hunk,
void* payload)
{
auto& l_ost = *reinterpret_cast<std::ostream*>(payload); // NOLINT
const std::string header(hunk->header); // NOLINT
for (const auto& hunk : delta.get_hunks()) {
const std::string header(hunk->header); // NOLINT
l_ost << html::h4();
l_ost << std::format("@@ -{},{} +{},{} @@ ",
ost << html::h4();
ost << std::format("@@ -{},{} +{},{} @@ ",
hunk->new_start,
hunk->new_lines,
hunk->old_start,
hunk->old_lines);
xmlencode(l_ost, header.substr(header.rfind('@') + 2));
l_ost << html::h4();
return 0;
};
startgit::xmlencode(ost, header.substr(header.rfind('@') + 2));
ost << html::h4();
for (const auto& line : hunk.get_lines()) {
if (line.get_origin() == '-') {
ost << html::span().set(
"style", "color: var(--theme_green); white-space: pre;");
} else if (line.get_origin() == '+') {
ost << html::span().set("style",
"color: var(--theme_red); white-space: pre;");
} else {
ost << html::span().set("style", "white-space: pre;");
}
const auto line_cb = +[](const git_diff_delta* /* delta */,
const git_diff_hunk* /* hunk */,
const git_diff_line* line,
void* payload)
{
auto& l_ost = *reinterpret_cast<std::ostream*>(payload); // NOLINT
if (line->origin == '-') {
l_ost << html::span().set("style",
"color: var(--theme_green); white-space: pre;");
} else if (line->origin == '+') {
l_ost << html::span().set("style",
"color: var(--theme_red); white-space: pre;");
} else {
l_ost << html::span().set("style", "white-space: pre;");
startgit::xmlencode(ost, line.get_content());
ost << html::span();
}
}
xmlencode(l_ost, std::string(line->content, line->content_len));
l_ost << html::span();
return 0;
};
diff.foreach(file_cb, nullptr, hunk_cb, line_cb, &ost);
}
}
void write_commit_diff(std::ostream& ost, const git2wrap::commit& commit)
void write_commit_diff(std::ostream& ost, const startgit::commit& commit)
{
using namespace hemplate; // NOLINT
const auto ptree = commit.get_parentcount() > 0
? commit.get_parent().get_tree()
: git2wrap::tree(nullptr, nullptr);
git2wrap::diff_options opts;
git_diff_init_options(&opts, GIT_DIFF_OPTIONS_VERSION);
opts.flags = GIT_DIFF_DISABLE_PATHSPEC_MATCH | GIT_DIFF_IGNORE_SUBMODULES
| GIT_DIFF_INCLUDE_TYPECHANGE;
const auto diff =
git2wrap::diff::tree_to_tree(commit.get_tree(), ptree, &opts);
ost << html::table() << html::tbody();
const std::string cid = commit.get_id().get_hex_string(22);
const auto url = std::format("../commit/{}.html", commit.get_id());
ost << html::tr()
.add(html::td().add(html::b("commit")))
.add(html::td().add(html::a(cid).set(
"href", std::format("../commit/{}.html", cid))));
.add(html::td().add(html::a(commit.get_id()).set("href", url)));
if (commit.get_parentcount() > 0) {
const std::string pid = commit.get_parent().get_id().get_hex_string(22);
const auto purl = std::format("../commit/{}.html", commit.get_parent_id());
ost << html::tr()
.add(html::td().add(html::b("parent")))
.add(html::td().add(html::a(pid).set(
"href", std::format("../commit/{}.html", pid))));
.add(html::td().add(
html::a(commit.get_parent_id()).set("href", purl)));
}
const auto mailto = std::string("mailto:") + commit.get_author_email();
ost << html::tr();
ost << html::td().add(html::b("author"));
ost << html::td() << commit.get_author().get_name() << " <";
ost << html::a(commit.get_author().get_email())
.set("href",
std::string("mailto:") + commit.get_author().get_email());
ost << html::td() << commit.get_author_name() << " <";
ost << html::a(commit.get_author_email()).set("href", mailto);
ost << ">" << html::td();
ost << html::tr();
ost << html::tr()
.add(html::td().add(html::b("date")))
.add(html::td(long_to_string(commit.get_author().get_time())));
.add(html::td(commit.get_author_time()));
ost << html::tbody() << html::table();
ost << html::br() << html::p().set("style", "white-space: pre;");
xmlencode(ost, commit.get_message());
startgit::xmlencode(ost, commit.get_message());
ost << html::p();
write_file_changes(ost, diff);
write_file_changes(ost, commit.get_diff());
ost << html::hr();
write_file_diffs(ost, diff);
write_file_diffs(ost, commit.get_diff());
}
void write_footer(std::ostream& ost)