diff --git a/stagit.1 b/stagit.1
index 7ee1249..d3cbb7e 100644
--- a/stagit.1
+++ b/stagit.1
@@ -1,4 +1,4 @@
-.Dd December 26, 2015
+.Dd May 1, 2016
.Dt STAGIT 1
.Os
.Sh NAME
@@ -6,12 +6,26 @@
.Nd static git page generator
.Sh SYNOPSIS
.Nm
-.Op Ar repodir
+.Op Fl c Ar cachefile
+.Ar repodir
.Sh DESCRIPTION
.Nm
writes HTML pages for the repository
.Ar repodir
-to the current directory. The following files will be written:
+to the current directory.
+.Pp
+Optionally the
+.Fl c Ar cachefile
+option can be used to cache the entries of the log page up to the point of
+the last commit. The
+.Ar cachefile
+will store the last commit id and the entries in the HTML table. It is up
+to the user to make sure the state of the
+.Ar cachefile
+is in sync with the history of the repository, for example a
+git push \-\-force can screw this up.
+.Pp
+The following files will be written:
.Bl -tag -width Ds
.It atom.xml
Atom XML feed
diff --git a/stagit.c b/stagit.c
index 7c52778..158447d 100644
--- a/stagit.c
+++ b/stagit.c
@@ -58,6 +58,12 @@ static char description[255];
static char cloneurl[1024];
static int haslicense, hasreadme, hassubmodules;
+/* cache */
+static git_oid lastoid;
+static char lastoidstr[GIT_OID_HEXSZ + 2]; /* id + newline + nul byte */
+static FILE *rcachefp, *wcachefp;
+static const char *cachefile;
+
void
deltainfo_free(struct deltainfo *di)
{
@@ -530,13 +536,43 @@ printshowfile(FILE *fp, struct commitinfo *ci)
}
}
+void
+writelogline(FILE *fp, struct commitinfo *ci)
+{
+ size_t len;
+
+ fputs("
", fp);
+ if (ci->author)
+ printtimeshort(fp, &(ci->author->when));
+ fputs(" | ", fp);
+ if (ci->summary) {
+ fprintf(fp, "", relpath, ci->oid);
+ if ((len = strlen(ci->summary)) > summarylen) {
+ xmlencode(fp, ci->summary, summarylen - 1);
+ fputs("…", fp);
+ } else {
+ xmlencode(fp, ci->summary, len);
+ }
+ fputs("", fp);
+ }
+ fputs(" | ", fp);
+ if (ci->author)
+ xmlencode(fp, ci->author->name, strlen(ci->author->name));
+ fputs(" | ", fp);
+ fprintf(fp, "%zu", ci->filecount);
+ fputs(" | ", fp);
+ fprintf(fp, "+%zu", ci->addcount);
+ fputs(" | ", fp);
+ fprintf(fp, "-%zu", ci->delcount);
+ fputs(" |
\n", fp);
+}
+
int
writelog(FILE *fp, const git_oid *oid)
{
struct commitinfo *ci;
git_revwalk *w = NULL;
git_oid id;
- size_t len;
char path[PATH_MAX];
FILE *fpfile;
int r;
@@ -546,40 +582,17 @@ writelog(FILE *fp, const git_oid *oid)
git_revwalk_sorting(w, GIT_SORT_TIME);
git_revwalk_simplify_first_parent(w);
- fputs("\nDate | Commit message | "
- "Author | Files | + | "
- "- |
\n\n", fp);
-
while (!git_revwalk_next(&id, w)) {
relpath = "";
+ if (cachefile && !memcmp(&id, &lastoid, sizeof(id)))
+ break;
if (!(ci = commitinfo_getbyoid(&id)))
break;
- fputs("", fp);
- if (ci->author)
- printtimeshort(fp, &(ci->author->when));
- fputs(" | ", fp);
- if (ci->summary) {
- fprintf(fp, "", relpath, ci->oid);
- if ((len = strlen(ci->summary)) > summarylen) {
- xmlencode(fp, ci->summary, summarylen - 1);
- fputs("…", fp);
- } else {
- xmlencode(fp, ci->summary, len);
- }
- fputs("", fp);
- }
- fputs(" | ", fp);
- if (ci->author)
- xmlencode(fp, ci->author->name, strlen(ci->author->name));
- fputs(" | ", fp);
- fprintf(fp, "%zu", ci->filecount);
- fputs(" | ", fp);
- fprintf(fp, "+%zu", ci->addcount);
- fputs(" | ", fp);
- fprintf(fp, "-%zu", ci->delcount);
- fputs(" |
\n", fp);
+ writelogline(fp, ci);
+ if (cachefile)
+ writelogline(wcachefp, ci);
relpath = "../";
@@ -599,8 +612,6 @@ writelog(FILE *fp, const git_oid *oid)
}
commitinfo_free(ci);
}
- fputs("
", fp);
-
git_revwalk_free(w);
relpath = "";
@@ -1005,6 +1016,13 @@ joinpath(char *buf, size_t bufsiz, const char *path, const char *path2)
path, path[0] && path[strlen(path) - 1] != '/' ? "/" : "", path2);
}
+void
+usage(char *argv0)
+{
+ fprintf(stderr, "%s [-c cachefile] repodir\n", argv0);
+ exit(1);
+}
+
int
main(int argc, char *argv[])
{
@@ -1013,12 +1031,23 @@ main(int argc, char *argv[])
const git_error *e = NULL;
FILE *fp, *fpread;
char path[PATH_MAX], repodirabs[PATH_MAX + 1], *p;
+ char tmppath[64] = "cache.XXXXXXXXXXXX", buf[BUFSIZ];
+ size_t n;
+ int i, fd;
- if (argc != 2) {
- fprintf(stderr, "%s \n", argv[0]);
- return 1;
+ for (i = 1; i < argc; i++) {
+ if (argv[i][0] != '-') {
+ if (repodir)
+ usage(argv[0]);
+ repodir = argv[i];
+ } else if (argv[i][1] == 'c') {
+ if (i + 1 >= argc)
+ usage(argv[0]);
+ cachefile = argv[++i];
+ }
}
- repodir = argv[1];
+ if (!repodir)
+ usage(argv[0]);
if (!realpath(repodir, repodirabs))
err(1, "realpath");
@@ -1088,9 +1117,51 @@ main(int argc, char *argv[])
/* log for HEAD */
fp = efopen("log.html", "w");
relpath = "";
- writeheader(fp, "Log");
mkdir("commit", 0755);
- writelog(fp, head);
+ writeheader(fp, "Log");
+ fputs("\nDate | Commit message | "
+ "Author | Files | + | "
+ "- |
\n\n", fp);
+
+ if (cachefile) {
+ /* read from cache file (does not need to exist) */
+ if ((rcachefp = fopen(cachefile, "r"))) {
+ if (!fgets(lastoidstr, sizeof(lastoidstr), rcachefp))
+ errx(1, "%s: no object id", cachefile);
+ if (git_oid_fromstr(&lastoid, lastoidstr))
+ errx(1, "%s: invalid object id", cachefile);
+ }
+
+ /* write log to (temporary) cache */
+ if ((fd = mkstemp(tmppath)) == -1)
+ err(1, "mkstemp");
+ if (!(wcachefp = fdopen(fd, "w")))
+ err(1, "fdopen");
+ /* write last commit id (HEAD) */
+ git_oid_tostr(buf, sizeof(buf), head);
+ fprintf(wcachefp, "%s\n", buf);
+
+ writelog(fp, head);
+
+ if (rcachefp) {
+ /* append previous log to log.html and the new cache */
+ while (!feof(rcachefp)) {
+ n = fread(buf, 1, sizeof(buf), rcachefp);
+ if (ferror(rcachefp))
+ err(1, "fread");
+ if (fwrite(buf, 1, n, fp) != n)
+ err(1, "fwrite");
+ if (fwrite(buf, 1, n, wcachefp) != n)
+ err(1, "fwrite");
+ }
+ fclose(rcachefp);
+ }
+ fclose(wcachefp);
+ } else {
+ writelog(fp, head);
+ }
+
+ fputs("
", fp);
writefooter(fp);
fclose(fp);
@@ -1113,6 +1184,10 @@ main(int argc, char *argv[])
writeatom(fp);
fclose(fp);
+ /* rename new cache file on success */
+ if (cachefile && rename(tmppath, cachefile))
+ err(1, "rename: '%s' to '%s'", tmppath, cachefile);
+
/* cleanup */
git_repository_free(repo);
git_libgit2_shutdown();