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).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();