stagit

cache support (-c option)

"Optionally the -c cachefile option can be used to cache the entries of
the log page up to the point of the last commit. The cachefile will store
the last commit id and the entries in the HTML table."

this caches the diffstat and commits, it is an expensive operation (twss).

Hiltjo Posthuma contact@arjunchoudhary.com

commit: 050a0fb parent: edcabd9
2 files changed, 130 insertions(+), 41 deletions(-)
Mstagit.1+17-3
Mstagit.c+113-38
M · stagit.1 +17, -3
 1@@ -1,4 +1,4 @@
 2-.Dd December 26, 2015
 3+.Dd May 1, 2016
 4 .Dt STAGIT 1
 5 .Os
 6 .Sh NAME
 7@@ -6,12 +6,26 @@
 8 .Nd static git page generator
 9 .Sh SYNOPSIS
10 .Nm
11-.Op Ar repodir
12+.Op Fl c Ar cachefile
13+.Ar repodir
14 .Sh DESCRIPTION
15 .Nm
16 writes HTML pages for the repository
17 .Ar repodir
18-to the current directory. The following files will be written:
19+to the current directory.
20+.Pp
21+Optionally the
22+.Fl c Ar cachefile
23+option can be used to cache the entries of the log page up to the point of
24+the last commit. The
25+.Ar cachefile
26+will store the last commit id and the entries in the HTML table. It is up
27+to the user to make sure the state of the
28+.Ar cachefile
29+is in sync with the history of the repository, for example a
30+git push \-\-force can screw this up.
31+.Pp
32+The following files will be written:
33 .Bl -tag -width Ds
34 .It atom.xml
35 Atom XML feed
M · stagit.c +113, -38
  1@@ -58,6 +58,12 @@ static char description[255];
  2 static char cloneurl[1024];
  3 static int haslicense, hasreadme, hassubmodules;
  4 
  5+/* cache */
  6+static git_oid lastoid;
  7+static char lastoidstr[GIT_OID_HEXSZ + 2]; /* id + newline + nul byte */
  8+static FILE *rcachefp, *wcachefp;
  9+static const char *cachefile;
 10+
 11 void
 12 deltainfo_free(struct deltainfo *di)
 13 {
 14@@ -530,13 +536,43 @@ printshowfile(FILE *fp, struct commitinfo *ci)
 15 	}
 16 }
 17 
 18+void
 19+writelogline(FILE *fp, struct commitinfo *ci)
 20+{
 21+	size_t len;
 22+
 23+	fputs("<tr><td>", fp);
 24+	if (ci->author)
 25+		printtimeshort(fp, &(ci->author->when));
 26+	fputs("</td><td>", fp);
 27+	if (ci->summary) {
 28+		fprintf(fp, "<a href=\"%scommit/%s.html\">", relpath, ci->oid);
 29+		if ((len = strlen(ci->summary)) > summarylen) {
 30+			xmlencode(fp, ci->summary, summarylen - 1);
 31+			fputs("…", fp);
 32+		} else {
 33+			xmlencode(fp, ci->summary, len);
 34+		}
 35+		fputs("</a>", fp);
 36+	}
 37+	fputs("</td><td>", fp);
 38+	if (ci->author)
 39+		xmlencode(fp, ci->author->name, strlen(ci->author->name));
 40+	fputs("</td><td class=\"num\">", fp);
 41+	fprintf(fp, "%zu", ci->filecount);
 42+	fputs("</td><td class=\"num\">", fp);
 43+	fprintf(fp, "+%zu", ci->addcount);
 44+	fputs("</td><td class=\"num\">", fp);
 45+	fprintf(fp, "-%zu", ci->delcount);
 46+	fputs("</td></tr>\n", fp);
 47+}
 48+
 49 int
 50 writelog(FILE *fp, const git_oid *oid)
 51 {
 52 	struct commitinfo *ci;
 53 	git_revwalk *w = NULL;
 54 	git_oid id;
 55-	size_t len;
 56 	char path[PATH_MAX];
 57 	FILE *fpfile;
 58 	int r;
 59@@ -546,40 +582,17 @@ writelog(FILE *fp, const git_oid *oid)
 60 	git_revwalk_sorting(w, GIT_SORT_TIME);
 61 	git_revwalk_simplify_first_parent(w);
 62 
 63-	fputs("<table id=\"log\"><thead>\n<tr><td>Date</td><td>Commit message</td>"
 64-		  "<td>Author</td><td class=\"num\">Files</td><td class=\"num\">+</td>"
 65-		  "<td class=\"num\">-</td></tr>\n</thead><tbody>\n", fp);
 66-
 67 	while (!git_revwalk_next(&id, w)) {
 68 		relpath = "";
 69 
 70+		if (cachefile && !memcmp(&id, &lastoid, sizeof(id)))
 71+			break;
 72 		if (!(ci = commitinfo_getbyoid(&id)))
 73 			break;
 74 
 75-		fputs("<tr><td>", fp);
 76-		if (ci->author)
 77-			printtimeshort(fp, &(ci->author->when));
 78-		fputs("</td><td>", fp);
 79-		if (ci->summary) {
 80-			fprintf(fp, "<a href=\"%scommit/%s.html\">", relpath, ci->oid);
 81-			if ((len = strlen(ci->summary)) > summarylen) {
 82-				xmlencode(fp, ci->summary, summarylen - 1);
 83-				fputs("…", fp);
 84-			} else {
 85-				xmlencode(fp, ci->summary, len);
 86-			}
 87-			fputs("</a>", fp);
 88-		}
 89-		fputs("</td><td>", fp);
 90-		if (ci->author)
 91-			xmlencode(fp, ci->author->name, strlen(ci->author->name));
 92-		fputs("</td><td class=\"num\">", fp);
 93-		fprintf(fp, "%zu", ci->filecount);
 94-		fputs("</td><td class=\"num\">", fp);
 95-		fprintf(fp, "+%zu", ci->addcount);
 96-		fputs("</td><td class=\"num\">", fp);
 97-		fprintf(fp, "-%zu", ci->delcount);
 98-		fputs("</td></tr>\n", fp);
 99+		writelogline(fp, ci);
100+		if (cachefile)
101+			writelogline(wcachefp, ci);
102 
103 		relpath = "../";
104 
105@@ -599,8 +612,6 @@ writelog(FILE *fp, const git_oid *oid)
106 		}
107 		commitinfo_free(ci);
108 	}
109-	fputs("</tbody></table>", fp);
110-
111 	git_revwalk_free(w);
112 
113 	relpath = "";
114@@ -1005,6 +1016,13 @@ joinpath(char *buf, size_t bufsiz, const char *path, const char *path2)
115 			path, path[0] && path[strlen(path) - 1] != '/' ? "/" : "", path2);
116 }
117 
118+void
119+usage(char *argv0)
120+{
121+	fprintf(stderr, "%s [-c cachefile] repodir\n", argv0);
122+	exit(1);
123+}
124+
125 int
126 main(int argc, char *argv[])
127 {
128@@ -1013,12 +1031,23 @@ main(int argc, char *argv[])
129 	const git_error *e = NULL;
130 	FILE *fp, *fpread;
131 	char path[PATH_MAX], repodirabs[PATH_MAX + 1], *p;
132-
133-	if (argc != 2) {
134-		fprintf(stderr, "%s <repodir>\n", argv[0]);
135-		return 1;
136+	char tmppath[64] = "cache.XXXXXXXXXXXX", buf[BUFSIZ];
137+	size_t n;
138+	int i, fd;
139+
140+	for (i = 1; i < argc; i++) {
141+		if (argv[i][0] != '-') {
142+			if (repodir)
143+				usage(argv[0]);
144+			repodir = argv[i];
145+		} else if (argv[i][1] == 'c') {
146+			if (i + 1 >= argc)
147+				usage(argv[0]);
148+			cachefile = argv[++i];
149+		}
150 	}
151-	repodir = argv[1];
152+	if (!repodir)
153+		usage(argv[0]);
154 
155 	if (!realpath(repodir, repodirabs))
156 		err(1, "realpath");
157@@ -1088,9 +1117,51 @@ main(int argc, char *argv[])
158 	/* log for HEAD */
159 	fp = efopen("log.html", "w");
160 	relpath = "";
161-	writeheader(fp, "Log");
162 	mkdir("commit", 0755);
163-	writelog(fp, head);
164+	writeheader(fp, "Log");
165+	fputs("<table id=\"log\"><thead>\n<tr><td>Date</td><td>Commit message</td>"
166+		  "<td>Author</td><td class=\"num\">Files</td><td class=\"num\">+</td>"
167+		  "<td class=\"num\">-</td></tr>\n</thead><tbody>\n", fp);
168+
169+	if (cachefile) {
170+		/* read from cache file (does not need to exist) */
171+		if ((rcachefp = fopen(cachefile, "r"))) {
172+			if (!fgets(lastoidstr, sizeof(lastoidstr), rcachefp))
173+				errx(1, "%s: no object id", cachefile);
174+			if (git_oid_fromstr(&lastoid, lastoidstr))
175+				errx(1, "%s: invalid object id", cachefile);
176+		}
177+
178+		/* write log to (temporary) cache */
179+		if ((fd = mkstemp(tmppath)) == -1)
180+			err(1, "mkstemp");
181+		if (!(wcachefp = fdopen(fd, "w")))
182+			err(1, "fdopen");
183+		/* write last commit id (HEAD) */
184+		git_oid_tostr(buf, sizeof(buf), head);
185+		fprintf(wcachefp, "%s\n", buf);
186+
187+		writelog(fp, head);
188+
189+		if (rcachefp) {
190+			/* append previous log to log.html and the new cache */
191+			while (!feof(rcachefp)) {
192+				n = fread(buf, 1, sizeof(buf), rcachefp);
193+				if (ferror(rcachefp))
194+					err(1, "fread");
195+				if (fwrite(buf, 1, n, fp) != n)
196+					err(1, "fwrite");
197+				if (fwrite(buf, 1, n, wcachefp) != n)
198+					err(1, "fwrite");
199+			}
200+			fclose(rcachefp);
201+		}
202+		fclose(wcachefp);
203+	} else {
204+		writelog(fp, head);
205+	}
206+
207+	fputs("</tbody></table>", fp);
208 	writefooter(fp);
209 	fclose(fp);
210 
211@@ -1113,6 +1184,10 @@ main(int argc, char *argv[])
212 	writeatom(fp);
213 	fclose(fp);
214 
215+	/* rename new cache file on success */
216+	if (cachefile && rename(tmppath, cachefile))
217+		err(1, "rename: '%s' to '%s'", tmppath, cachefile);
218+
219 	/* cleanup */
220 	git_repository_free(repo);
221 	git_libgit2_shutdown();