commit 1f4d720bfa77f81ee9309b1b3276ecff59fe8a33
parent 0decaa4a8d589b1f65d910b3d300ec5b831b6c42
Author: Michael Neuper <michael@michaelneuper.com>
Date: Sun, 25 Oct 2026 21:20:07 +0200
render the README as the repository's front page
README.md is rendered with md4c (GitHub dialect) into index.html and
preferred over a plain README, which is shown as preformatted text. The
README link moves to the front of the menu.
Diffstat:
5 files changed, 144 insertions(+), 8 deletions(-)
diff --git a/.github/workflows/mirror.yml b/.github/workflows/mirror.yml
@@ -20,10 +20,10 @@ jobs:
steps:
- uses: actions/checkout@v7
- - name: Install libgit2
+ - name: Install libgit2 and md4c
run: |
sudo apt-get update
- sudo apt-get install -y --no-install-recommends libgit2-dev
+ sudo apt-get install -y --no-install-recommends libgit2-dev libmd4c-dev libmd4c-html0-dev
- name: Build stagit
run: make
diff --git a/Makefile b/Makefile
@@ -11,6 +11,7 @@ DOCPREFIX = ${PREFIX}/share/doc/${NAME}
# /opt/homebrew is Homebrew on Apple silicon.
LIBGIT_INC = -I/usr/local/include -I/opt/homebrew/include
LIBGIT_LIB = -L/usr/local/lib -L/opt/homebrew/lib -lgit2
+MD4C_LIB = -lmd4c-html
# use system flags.
STAGIT_CFLAGS = ${LIBGIT_INC} ${CFLAGS}
@@ -69,7 +70,7 @@ dist:
${OBJ}: ${HDR}
stagit: stagit.o ${COMPATOBJ}
- ${CC} -o $@ stagit.o ${COMPATOBJ} ${STAGIT_LDFLAGS}
+ ${CC} -o $@ stagit.o ${COMPATOBJ} ${STAGIT_LDFLAGS} ${MD4C_LIB}
stagit-index: stagit-index.o ${COMPATOBJ}
${CC} -o $@ stagit-index.o ${COMPATOBJ} ${STAGIT_LDFLAGS}
diff --git a/README.md b/README.md
@@ -6,6 +6,11 @@ mirror of my public GitHub repos. The upstream README is in `README`.
## Changes from upstream
+- Each repo opens on its README (`index.html`), with markdown rendered by
+ [md4c](https://github.com/mity/md4c). `README.md` is preferred over
+ `README`, which is shown as plain text. Repos without either open on the
+ log.
+- stagit-index links to each repo's front page instead of its log.
- cgit-flavoured `style.css`, and my own logo and favicon.
- The Makefile also finds libraries installed by Homebrew.
@@ -13,7 +18,7 @@ To see only the code changes: `git diff 1.3 -- '*.c' Makefile`.
## Building the site
-Needs libgit2 and the GitHub CLI (`brew install libgit2 gh`).
+Needs libgit2, md4c and the GitHub CLI (`brew install libgit2 md4c gh`).
./build.sh
python3 -m http.server -d public
@@ -78,3 +83,5 @@ Merge rather than rebase, so `main` never needs a force push. Conflicts in
existing commit page, so incremental output would go stale after a force
push or a description change. Netlify only uploads files whose contents
changed, so redeploys stay small.
+- Relative image links in READMEs don't work, because stagit only publishes
+ HTML views of files, not the files themselves.
diff --git a/stagit.c b/stagit.c
@@ -13,6 +13,7 @@
#include <unistd.h>
#include <git2.h>
+#include <md4c-html.h>
#include "compat.h"
@@ -69,7 +70,7 @@ static char cloneurl[1024];
static char *submodules;
static char *licensefiles[] = { "HEAD:LICENSE", "HEAD:LICENSE.md", "HEAD:LICENSE.txt", "HEAD:COPYING" };
static char *license;
-static char *readmefiles[] = { "HEAD:README", "HEAD:README.md" };
+static char *readmefiles[] = { "HEAD:README.md", "HEAD:README" };
static char *readme;
static long long nlogcommits = -1; /* -1 indicates not used */
@@ -535,15 +536,14 @@ writeheader(FILE *fp, const char *title)
fputs("</a></td></tr>", fp);
}
fputs("<tr><td></td><td>\n", fp);
+ if (readme)
+ fprintf(fp, "<a href=\"%sindex.html\">README</a> | ", relpath);
fprintf(fp, "<a href=\"%slog.html\">Log</a> | ", relpath);
fprintf(fp, "<a href=\"%sfiles.html\">Files</a> | ", relpath);
fprintf(fp, "<a href=\"%srefs.html\">Refs</a>", relpath);
if (submodules)
fprintf(fp, " | <a href=\"%sfile/%s.html\">Submodules</a>",
relpath, submodules);
- if (readme)
- fprintf(fp, " | <a href=\"%sfile/%s.html\">README</a>",
- relpath, readme);
if (license)
fprintf(fp, " | <a href=\"%sfile/%s.html\">LICENSE</a>",
relpath, license);
@@ -946,6 +946,40 @@ writeatom(FILE *fp, int all)
return 0;
}
+void
+writemarkdown(const MD_CHAR *s, MD_SIZE len, void *fp)
+{
+ fwrite(s, 1, len, fp);
+}
+
+void
+writereadme(FILE *fp)
+{
+ git_object *obj = NULL;
+ char spec[PATH_MAX];
+ const char *s, *ext;
+ size_t len;
+
+ snprintf(spec, sizeof(spec), "HEAD:%s", readme);
+ if (git_revparse_single(&obj, repo, spec))
+ return;
+ s = git_blob_rawcontent((git_blob *)obj);
+ len = git_blob_rawsize((git_blob *)obj);
+
+ fputs("<div id=\"readme\">\n", fp);
+ if ((ext = strrchr(readme, '.')) && !strcmp(ext, ".md")) {
+ if (md_html(s, len, writemarkdown, fp, MD_DIALECT_GITHUB, 0))
+ errx(1, "md_html: '%s'", readme);
+ } else {
+ fputs("<pre>", fp);
+ xmlencode(fp, s, len);
+ fputs("</pre>\n", fp);
+ }
+ fputs("</div>\n", fp);
+
+ git_object_free(obj);
+}
+
size_t
writeblob(git_object *obj, const char *fpath, const char *filename, size_t filesize)
{
@@ -1326,6 +1360,17 @@ main(int argc, char *argv[])
submodules = ".gitmodules";
git_object_free(obj);
+ /* README for HEAD as the front page */
+ if (readme) {
+ fp = efopen("index.html", "w");
+ relpath = "";
+ writeheader(fp, "");
+ writereadme(fp);
+ writefooter(fp);
+ checkfileerror(fp, "index.html", 'w');
+ fclose(fp);
+ }
+
/* log for HEAD */
fp = efopen("log.html", "w");
relpath = "";
diff --git a/style.css b/style.css
@@ -200,3 +200,86 @@ span.d,
pre a.d {
color: var(--del);
}
+
+/* README front page. */
+
+#readme {
+ max-width: 60em;
+ line-height: 1.5;
+}
+
+#readme h1,
+#readme h2,
+#readme h3,
+#readme h4 {
+ margin: 1.2em 0 0.5em;
+ font-weight: bold;
+ line-height: 1.25;
+}
+
+#readme h1:first-child,
+#readme h2:first-child {
+ margin-top: 0.5em;
+}
+
+#readme h1,
+#readme h2 {
+ padding-bottom: 0.2em;
+ border-bottom: 1px solid var(--rule);
+}
+
+#readme h1 {
+ font-size: 160%;
+}
+
+#readme h2 {
+ font-size: 130%;
+}
+
+#readme h3 {
+ font-size: 115%;
+}
+
+#readme h4 {
+ font-size: 100%;
+}
+
+#readme code {
+ padding: 0.1em 0.3em;
+ font: 9pt var(--mono);
+ background: var(--row);
+}
+
+#readme pre {
+ padding: 0.6em 0.8em;
+ overflow-x: auto;
+ background: var(--row);
+}
+
+#readme pre code {
+ padding: 0;
+ font: inherit;
+ background: none;
+}
+
+#readme img {
+ max-width: 100%;
+}
+
+#readme blockquote {
+ margin: 0;
+ padding: 0 1em;
+ color: var(--muted);
+ border-left: 3px solid var(--rule);
+}
+
+#readme th,
+#readme td {
+ padding: 0.2em 0.6em;
+ white-space: normal;
+ border: 1px solid var(--rule);
+}
+
+#readme th {
+ background: var(--row);
+}