stagit

static git page - forked from git.codemadness.org/stagit
git clone git://src.gearsix.net/stagit
Log | Files | Refs | Atom | README | LICENSE

commit d0fd6f19ba2e5a51e9e090f0ac6d4b0d5902b6d1
parent ed29611947530f1a1542a9532710b2145149c799
Author: gearsix <gearsix@tuta.io>
Date:   Wed,  7 Apr 2021 10:55:21 +0100

upstream merge

Squashed commit of the following:

commit 295e4b8cb95114bb74b582c7332bc4c171f36dd3
Author: Hiltjo Posthuma <hiltjo@codemadness.org>
Date:   Thu Mar 25 18:13:13 2021 +0100

    add function to print a single line, ignoring \r and \n

    This can happen when there is no newline at end of file in the diff which is
    served by libgit2 as:

    "\n\ No newline at end of file\n".

commit 995f7d5c5d8e396b06e70b1497ac96df63ffec36
Author: Hiltjo Posthuma <hiltjo@codemadness.org>
Date:   Fri Mar 19 11:29:53 2021 +0100

    add meta viewport on stagit-index too

    Patch by Oscar Benedito, thanks!

commit f46405850133e43dcae95e0a41b74bcca7b10027
Author: Hiltjo Posthuma <hiltjo@codemadness.org>
Date:   Sun Mar 14 16:23:58 2021 +0100

    bump version to 0.9.5

commit c4d5fecc40e51ab4667315bd11dabd2023e357f3
Author: Hiltjo Posthuma <hiltjo@codemadness.org>
Date:   Fri Mar 5 12:47:08 2021 +0100

    LICENSE: update

commit 5ced189f1993fc17ae683f0a542218db7be7267b
Author: Hiltjo Posthuma <hiltjo@codemadness.org>
Date:   Fri Mar 5 12:44:48 2021 +0100

    change STAGIT_BASEURL to an -u option and also update the example script

commit 7968c0bc9c0172bd654e1f87d8194aef7fb69865
Author: Hiltjo Posthuma <hiltjo@codemadness.org>
Date:   Fri Mar 5 11:51:21 2021 +0100

    add $STAGIT_BASEURL environment variable to make Atom links absolute

    With feedback from adc, thanks!

commit d1c528fb5ad81c876f07a69e1b759764f69cb9de
Author: Hiltjo Posthuma <hiltjo@codemadness.org>
Date:   Fri Mar 5 11:50:16 2021 +0100

    README: mention tags.xml feature

commit 722f8364601d2b6ee2439b42cd75750f6aac90ed
Author: Hiltjo Posthuma <hiltjo@codemadness.org>
Date:   Sat Jan 9 14:59:53 2021 +0100

    micro-optimization: fputc (function) -> putc (macro/inline function)

commit 5044ddeea3c77fea97daa62d51593d73b0e08413
Author: Hiltjo Posthuma <hiltjo@codemadness.org>
Date:   Fri Jan 8 14:32:33 2021 +0100

    LICENSE: bump year

commit e1c0aebde443979a524a944027b81f84f4323ff3
Author: Hiltjo Posthuma <hiltjo@codemadness.org>
Date:   Sat Nov 28 12:28:05 2020 +0100

    fix warning with libgit2 v0.99+, remain compatible with older versions

    git_blob_rawsize now returns with git_object_size_t (unsigned). This was
    git_off_t (signed).

    In my current version 1.1.0:
    	types.h:typedef uint64_t git_object_size_t;

    v0.28.5:
    https://libgit2.org/libgit2/#v0.28.5/group/blob/git_blob_rawsize

    changed from v0.99 onwards:
    https://libgit2.org/libgit2/#v0.99.0/group/blob/git_blob_rawsize

    Fix: use size_t to remain compatible (with a possible warning in older
    versions), since git_object_size_t is a new defined type.
    This assumes size_t is atleast uint32_t / uint64_t size.

    Adapted from a patch by Augustin Fabre <augustin@augfab.fr>, thanks!

commit 66df204c440de3b0cf3442d3a0c719016cdcf9c6
Author: Oscar Benedito <oscar@oscarbenedito.com>
Date:   Mon Nov 16 23:24:32 2020 +0100

    add abbreviated commit hash to submodule file

commit a63645a5ea4e60523c0024f69c627f586b601d82
Author: Hiltjo Posthuma <hiltjo@codemadness.org>
Date:   Sun Nov 15 21:11:46 2020 +0100

    add meta viewport for scaling on mobile

    Patch by Augustin Fabre <augustin@augfab.fr>

commit ae41add24a87027343e3a6f7eea19f3902af4a12
Author: Hiltjo Posthuma <hiltjo@codemadness.org>
Date:   Sun Nov 15 21:11:10 2020 +0100

    style.css: improve contrast

    https://webaim.org/resources/contrastchecker/?fcolor=555555&bcolor=FFFFFF

    Patch by Augustin Fabre <augustin@augfab.fr> and adapted.

commit fc5ef41165df39d6def252e5230a63cc6839bfc1
Author: Hiltjo Posthuma <hiltjo@codemadness.org>
Date:   Sun Nov 15 20:59:34 2020 +0100

    use size_t to count lines

commit 4f60446c011b45e862540c97b684c62fd8dc3c60
Author: Hiltjo Posthuma <hiltjo@codemadness.org>
Date:   Sun Nov 15 20:58:58 2020 +0100

    avoid shadowed `name' global variable

    by Augustin Fabre <augustin@augfab.fr>

commit 9467f347a2224ac95b96ef5c74d50a4e2aad5241
Author: Hiltjo Posthuma <hiltjo@codemadness.org>
Date:   Sun Nov 15 20:58:41 2020 +0100

    refs_cmp: remove unneeded cast

commit 3e7865f8f9ef87f622a7a94e7ae70355753ee66a
Author: Hiltjo Posthuma <hiltjo@codemadness.org>
Date:   Sun Nov 15 20:58:02 2020 +0100

    use LEN() macros for arrays

    from Augustin Fabre <augustin@augfab.fr>

commit 75555cd99ee4d5df765f7dd6f0d09f2f925be725
Author: Hiltjo Posthuma <hiltjo@codemadness.org>
Date:   Mon Aug 17 16:09:33 2020 +0200

    bump version to 0.9.4

commit 5334f3e0009bb7d5835c3bad60db507bfd146930
Author: Hiltjo Posthuma <hiltjo@codemadness.org>
Date:   Mon Aug 10 16:09:46 2020 +0200

    fix a small memleak in writeatom()

    non-tag references were not freed.

commit dc0709f6f4f7e256e27272cb0b8611715caf1f3b
Author: Hiltjo Posthuma <hiltjo@codemadness.org>
Date:   Sat Aug 8 20:01:05 2020 +0200

    stagit-index: remove unveil support for argv

    This can reach the unveil argument limits and it is not a good case for using
    unveil.

commit 174a763058f9a90831ab5a2aeb1c9bfbecdabf48
Author: kst <nil@krj.st>
Date:   Wed Aug 5 22:11:18 2020 +0000

    fix submodule lookup in bare repos

    git_submodule_lookup does not work without a working tree [1], so the
    current approach fails to recognize any submodules in bare repos.

    Instead, notice that

    	$ git ls-tree HEAD

    lists any submodules as commit objects regardless of a working tree.
    This is the only instance commit object is used in a tree, so we will
    use this to check for submodules.

    [1]: https://github.com/libgit2/libgit2/pull/4305/files

commit f05e6b0fcb3b874180970d06ebcde05fb5aea470
Author: Hiltjo Posthuma <hiltjo@codemadness.org>
Date:   Mon Jul 20 14:15:12 2020 +0200

    regression: do not show unset or empty tags

commit d80a163acd47df2bd9ab145be6b249814aa9eceb
Author: Hiltjo Posthuma <hiltjo@codemadness.org>
Date:   Sun Jul 19 16:41:10 2020 +0200

    refactor get reference, add another feed for tags/releases

    A separate Atom feed is helpful to ports maintainers to monitor new
    tags/releases.

commit 693c06448972f049d74addbd4942365cd37d92e4
Author: Hiltjo Posthuma <hiltjo@codemadness.org>
Date:   Sun Jul 19 14:07:54 2020 +0200

    sort branches and tags by time (descending)

    In general version tags are done in chronological order, so this will have a
    better sorting for tagged (versioned) releases.

    Request from Caltlgin Stsodaat and others, thanks!

Diffstat:
MLICENSE | 3+--
MMakefile | 2+-
MREADME | 3++-
Mexample_create.sh | 2+-
Mexample_post-receive.sh | 2+-
Mstagit-index.c | 7++-----
Mstagit.1 | 12++++++++++--
Mstagit.c | 390++++++++++++++++++++++++++++++++++++++++++++++++++-----------------------------
Mstyle.css | 6+++---
9 files changed, 267 insertions(+), 160 deletions(-)

diff --git a/LICENSE b/LICENSE @@ -1,7 +1,6 @@ MIT/X Consortium License -(c) 2015-2019 Hiltjo Posthuma <hiltjo@codemadness.org> -(c) 2015-2016 Dimitris Papastamos <sin@2f30.org> +(c) 2015-2021 Hiltjo Posthuma <hiltjo@codemadness.org> Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), diff --git a/Makefile b/Makefile @@ -1,7 +1,7 @@ .POSIX: NAME = stagit -VERSION = 0.9.3 +VERSION = 0.9.5 # paths PREFIX = /usr/local diff --git a/README b/README @@ -173,7 +173,8 @@ Features - Show references: local branches and tags. - Detect README and LICENSE file from HEAD and link it as a webpage. - Detect submodules (.gitmodules file) from HEAD and link it as a webpage. -- Atom feed log (atom.xml). +- Atom feed of the commit log (atom.xml). +- Atom feed of the tags/refs (tags.xml). - Make index page for multiple repositories with stagit-index. - After generating the pages (relatively slow) serving the files is very fast, simple and requires little resources (because the content is static), only diff --git a/example_create.sh b/example_create.sh @@ -31,7 +31,7 @@ for dir in "${reposdir}/"*/; do mkdir -p "${curdir}/${d}" cd "${curdir}/${d}" || continue - stagit -c ".cache" "${reposdir}/${r}" + stagit -c ".cache" -u "https://git.codemadness.nl/$d/" "${reposdir}/${r}" # symlinks ln -sf log.html index.html diff --git a/example_post-receive.sh b/example_post-receive.sh @@ -64,7 +64,7 @@ fi stagit-index "${reposdir}/"*/ > "${destdir}/index.html" # make pages. -stagit -c "${cachefile}" "${reposdir}/${r}" +stagit -c "${cachefile}" -u "https://git.codemadness.nl/$d/" "${reposdir}/${r}" ln -sf log.html index.html ln -sf ../style.css style.css diff --git a/stagit-index.c b/stagit-index.c @@ -43,7 +43,7 @@ xmlencode(FILE *fp, const char *s, size_t len) case '\'': fputs("&#39;" , fp); break; case '&': fputs("&amp;", fp); break; case '"': fputs("&quot;", fp); break; - default: fputc(*s, fp); + default: putc(*s, fp); } } } @@ -68,6 +68,7 @@ writeheader(FILE *fp) fputs("<!DOCTYPE html>\n" "<html>\n<head>\n" "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\" />\n" + "<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n" "<title>", fp); xmlencode(fp, description, strlen(description)); fprintf(fp, "</title>\n<link rel=\"icon\" type=\"image/png\" href=\"%sfavicon.png\" />\n", rootpath); @@ -159,10 +160,6 @@ main(int argc, char *argv[]) git_libgit2_init(); #ifdef __OpenBSD__ - for (i = 1; i < argc; i++) - if (unveil(argv[i], "r") == -1) - err(1, "unveil: %s", argv[i]); - if (pledge("stdio rpath", NULL) == -1) err(1, "pledge"); #endif diff --git a/stagit.1 b/stagit.1 @@ -1,4 +1,4 @@ -.Dd February 6, 2019 +.Dd March 5, 2021 .Dt STAGIT 1 .Os .Sh NAME @@ -8,6 +8,7 @@ .Nm .Op Fl c Ar cachefile .Op Fl l Ar commits +.Op Fl u Ar baseurl .Ar repodir .Sh DESCRIPTION .Nm @@ -31,6 +32,9 @@ Write a maximum number of .Ar commits to the log.html file only. However the commit files are written as usual. +.It Fl u Ar baseurl +Base URL to make links in the Atom feeds absolute. +For example: "https://git.codemadness.org/stagit/". .El .Pp The options @@ -42,7 +46,9 @@ cannot be used at the same time. The following files will be written: .Bl -tag -width Ds .It atom.xml -Atom XML feed +Atom XML feed of the last 100 commits. +.It tags.xml +Atom XML feed of the tags. .It files.html List of files in the latest tree, linking to the file. .It log.html @@ -101,6 +107,8 @@ favicon image. .It style.css CSS stylesheet. .El +.Sh EXIT STATUS +.Ex -std .Sh SEE ALSO .Xr stagit-index 1 .Sh AUTHORS diff --git a/stagit.c b/stagit.c @@ -16,6 +16,8 @@ #include "compat.h" +#define LEN(s) (sizeof(s)/sizeof(*s)) + struct deltainfo { git_patch *patch; @@ -48,9 +50,16 @@ struct commitinfo { size_t ndeltas; }; +/* reference and associated data for sorting */ +struct referenceinfo { + struct git_reference *ref; + struct commitinfo *ci; +}; + static git_repository *repo; static const char *rootpath = "/"; +static const char *baseurl = ""; /* base URL to make absolute RSS/Atom URI */ static const char *relpath = ""; static const char *repodir; @@ -243,13 +252,110 @@ err: return NULL; } +int +refs_cmp(const void *v1, const void *v2) +{ + const struct referenceinfo *r1 = v1, *r2 = v2; + time_t t1, t2; + int r; + + if ((r = git_reference_is_tag(r1->ref) - git_reference_is_tag(r2->ref))) + return r; + + t1 = r1->ci->author ? r1->ci->author->when.time : 0; + t2 = r2->ci->author ? r2->ci->author->when.time : 0; + if ((r = t1 > t2 ? -1 : (t1 == t2 ? 0 : 1))) + return r; + + return strcmp(git_reference_shorthand(r1->ref), + git_reference_shorthand(r2->ref)); +} + +int +getrefs(struct referenceinfo **pris, size_t *prefcount) +{ + struct referenceinfo *ris = NULL; + struct commitinfo *ci = NULL; + git_reference_iterator *it = NULL; + const git_oid *id = NULL; + git_object *obj = NULL; + git_reference *dref = NULL, *r, *ref = NULL; + size_t i, refcount; + + *pris = NULL; + *prefcount = 0; + + if (git_reference_iterator_new(&it, repo)) + return -1; + + for (refcount = 0; !git_reference_next(&ref, it); ) { + if (!git_reference_is_branch(ref) && !git_reference_is_tag(ref)) { + git_reference_free(ref); + ref = NULL; + continue; + } + + switch (git_reference_type(ref)) { + case GIT_REF_SYMBOLIC: + if (git_reference_resolve(&dref, ref)) + goto err; + r = dref; + break; + case GIT_REF_OID: + r = ref; + break; + default: + continue; + } + if (!git_reference_target(r) || + git_reference_peel(&obj, r, GIT_OBJ_ANY)) + goto err; + if (!(id = git_object_id(obj))) + goto err; + if (!(ci = commitinfo_getbyoid(id))) + break; + + if (!(ris = reallocarray(ris, refcount + 1, sizeof(*ris)))) + err(1, "realloc"); + ris[refcount].ci = ci; + ris[refcount].ref = r; + refcount++; + + git_object_free(obj); + obj = NULL; + git_reference_free(dref); + dref = NULL; + } + git_reference_iterator_free(it); + + /* sort by type, date then shorthand name */ + qsort(ris, refcount, sizeof(*ris), refs_cmp); + + *pris = ris; + *prefcount = refcount; + + return 0; + +err: + git_object_free(obj); + git_reference_free(dref); + commitinfo_free(ci); + for (i = 0; i < refcount; i++) { + commitinfo_free(ris[i].ci); + git_reference_free(ris[i].ref); + } + free(ris); + + return -1; +} + FILE * -efopen(const char *name, const char *flags) +efopen(const char *filename, const char *flags) { FILE *fp; - if (!(fp = fopen(name, flags))) - err(1, "fopen: '%s'", name); + if (!(fp = fopen(filename, flags))) + err(1, "fopen: '%s'", filename); return fp; } @@ -267,7 +373,27 @@ xmlencode(FILE *fp, const char *s, size_t len) case '\'': fputs("&#39;", fp); break; case '&': fputs("&amp;", fp); break; case '"': fputs("&quot;", fp); break; - default: fputc(*s, fp); + default: putc(*s, fp); + } + } +} + +/* Escape characters below as HTML 2.0 / XML 1.0, ignore printing '\n', '\r' */ +void +xmlencodeline(FILE *fp, const char *s, size_t len) +{ + size_t i; + + for (i = 0; *s && i < len; s++, i++) { + switch(*s) { + case '<': fputs("&lt;", fp); break; + case '>': fputs("&gt;", fp); break; + case '\'': fputs("&#39;", fp); break; + case '&': fputs("&amp;", fp); break; + case '"': fputs("&quot;", fp); break; + case '\r': break; /* ignore CR */ + case '\n': break; /* ignore LF */ + default: putc(*s, fp); } } } @@ -345,6 +471,7 @@ writeheader(FILE *fp, const char *title) fputs("<!DOCTYPE html>\n" "<html>\n<head>\n" "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\" />\n" + "<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n" "<title>", fp); xmlencode(fp, title, strlen(title)); if (title[0] && strippedname[0]) @@ -395,18 +522,18 @@ writefooter(FILE *fp) fputs("</div>\n</body>\n</html>\n", fp); } -int +size_t writeblobhtml(FILE *fp, const git_blob *blob) { - size_t n = 0, i, prev; - const char *nfmt = "<a href=\"#l%d\" class=\"line\" id=\"l%d\">%7d</a> "; + size_t n = 0, i, len, prev; + const char *nfmt = "<a href=\"#l%zu\" class=\"line\" id=\"l%zu\">%7zu</a> "; const char *s = git_blob_rawcontent(blob); - git_off_t len = git_blob_rawsize(blob); + len = git_blob_rawsize(blob); fputs("<pre id=\"blob\">\n", fp); if (len > 0) { - for (i = 0, prev = 0; i < (size_t)len; i++) { + for (i = 0, prev = 0; i < len; i++) { if (s[i] != '\n') continue; n++; @@ -446,12 +573,12 @@ printcommit(FILE *fp, struct commitinfo *ci) xmlencode(fp, ci->author->email, strlen(ci->author->email)); fputs("</a>&gt;\n<b>Date:</b> ", fp); printtime(fp, &(ci->author->when)); - fputc('\n', fp); + putc('\n', fp); } if (ci->msg) { - fputc('\n', fp); + putc('\n', fp); xmlencode(fp, ci->msg, strlen(ci->msg)); - fputc('\n', fp); + putc('\n', fp); } } @@ -570,8 +697,9 @@ printshowfile(FILE *fp, struct commitinfo *ci) fprintf(fp, "<a href=\"#h%zu-%zu-%zu\" id=\"h%zu-%zu-%zu\" class=\"d\">-", i, j, k, i, j, k); else - fputc(' ', fp); - xmlencode(fp, line->content, line->content_len); + putc(' ', fp); + xmlencodeline(fp, line->content, line->content_len); + putc('\n', fp); if (line->old_lineno == -1 || line->new_lineno == -1) fputs("</a>", fp); } @@ -676,7 +804,7 @@ err: } void -printcommitatom(FILE *fp, struct commitinfo *ci) +printcommitatom(FILE *fp, struct commitinfo *ci, const char *tag) { fputs("<entry>\n", fp); @@ -693,11 +821,16 @@ printcommitatom(FILE *fp, struct commitinfo *ci) } if (ci->summary) { fputs("<title type=\"text\">", fp); + if (tag && tag[0]) { + fputs("[", fp); + xmlencode(fp, tag, strlen(tag)); + fputs("] ", fp); + } xmlencode(fp, ci->summary, strlen(ci->summary)); fputs("</title>\n", fp); } - fprintf(fp, "<link rel=\"alternate\" type=\"text/html\" href=\"commit/%s.html\" />\n", - ci->oid); + fprintf(fp, "<link rel=\"alternate\" type=\"text/html\" href=\"%scommit/%s.html\" />\n", + baseurl, ci->oid); if (ci->author) { fputs("<author>\n<name>", fp); @@ -718,18 +851,20 @@ printcommitatom(FILE *fp, struct commitinfo *ci) xmlencode(fp, ci->author->email, strlen(ci->author->email)); fputs("&gt;\nDate: ", fp); printtime(fp, &(ci->author->when)); - fputc('\n', fp); + putc('\n', fp); } if (ci->msg) { - fputc('\n', fp); + putc('\n', fp); xmlencode(fp, ci->msg, strlen(ci->msg)); } fputs("\n</content>\n</entry>\n", fp); } int -writeatom(FILE *fp) +writeatom(FILE *fp, int all) { + struct referenceinfo *ris = NULL; + size_t refcount = 0; struct commitinfo *ci; git_revwalk *w = NULL; git_oid id; @@ -742,29 +877,42 @@ writeatom(FILE *fp) xmlencode(fp, description, strlen(description)); fputs("</subtitle>\n", fp); - git_revwalk_new(&w, repo); - git_revwalk_push_head(w); - git_revwalk_simplify_first_parent(w); - - for (i = 0; i < m && !git_revwalk_next(&id, w); i++) { - if (!(ci = commitinfo_getbyoid(&id))) - break; - printcommitatom(fp, ci); - commitinfo_free(ci); + /* all commits or only tags? */ + if (all) { + git_revwalk_new(&w, repo); + git_revwalk_push_head(w); + git_revwalk_simplify_first_parent(w); + for (i = 0; i < m && !git_revwalk_next(&id, w); i++) { + if (!(ci = commitinfo_getbyoid(&id))) + break; + printcommitatom(fp, ci, ""); + commitinfo_free(ci); + } + git_revwalk_free(w); + } else if (getrefs(&ris, &refcount) != -1) { + /* references: tags */ + for (i = 0; i < refcount; i++) { + if (git_reference_is_tag(ris[i].ref)) + printcommitatom(fp, ris[i].ci, + git_reference_shorthand(ris[i].ref)); + + commitinfo_free(ris[i].ci); + git_reference_free(ris[i].ref); + } + free(ris); } - git_revwalk_free(w); fputs("</feed>\n", fp); return 0; } -int -writeblob(git_object *obj, const char *fpath, const char *filename, git_off_t filesize) +size_t +writeblob(git_object *obj, const char *fpath, const char *filename, size_t filesize) { char tmp[PATH_MAX] = "", *d; const char *p; - int lc = 0; + size_t lc = 0; FILE *fp; if (strlcpy(tmp, fpath, sizeof(tmp)) >= sizeof(tmp)) @@ -784,7 +932,7 @@ writeblob(git_object *obj, const char *fpath, const char *filename, git_off_t fi writeheader(fp, filename); fputs("<p> ", fp); xmlencode(fp, filename, strlen(filename)); - fprintf(fp, " (%juB)", (uintmax_t)filesize); + fprintf(fp, " (%zuB)", filesize); fputs("</p><hr/>", fp); if (git_blob_is_binary((git_blob *)obj)) { @@ -848,13 +996,11 @@ int writefilestree(FILE *fp, git_tree *tree, const char *path) { const git_tree_entry *entry = NULL; - git_submodule *module = NULL; git_object *obj = NULL; - git_off_t filesize; const char *entryname; - char filepath[PATH_MAX], entrypath[PATH_MAX]; - size_t count, i; - int lc, r, ret; + char filepath[PATH_MAX], entrypath[PATH_MAX], oid[8]; + size_t count, i, lc, filesize; + int r, ret; count = git_tree_entrycount(tree); for (i = 0; i < count; i++) { @@ -896,17 +1042,20 @@ writefilestree(FILE *fp, git_tree *tree, const char *path) xmlencode(fp, entrypath, strlen(entrypath)); fputs("</a></td><td class=\"num\" align=\"right\">", fp); if (lc > 0) - fprintf(fp, "%dL", lc); + fprintf(fp, "%zuL", lc); else - fprintf(fp, "%juB", (uintmax_t)filesize); + fprintf(fp, "%zuB", filesize); fputs("</td></tr>\n", fp); git_object_free(obj); - } else if (!git_submodule_lookup(&module, repo, entryname)) { + } else if (git_tree_entry_type(entry) == GIT_OBJ_COMMIT) { + /* commit object in tree is a submodule */ fprintf(fp, "<tr><td>m---------</td><td><a href=\"%sfile/.gitmodules.html\">", relpath); xmlencode(fp, entrypath, strlen(entrypath)); - git_submodule_free(module); - fputs("</a></td><td class=\"num\" align=\"right\"></td></tr>\n", fp); + fputs("</a> @ ", fp); + git_oid_tostr(oid, sizeof(oid), git_tree_entry_id(entry)); + xmlencode(fp, oid, strlen(oid)); + fputs("</td><td class=\"num\" align=\"right\"></td></tr>\n", fp); } } @@ -938,115 +1087,58 @@ writefiles(FILE *fp, const git_oid *id) } int -refs_cmp(const void *v1, const void *v2) -{ - git_reference *r1 = (*(git_reference **)v1); - git_reference *r2 = (*(git_reference **)v2); - int r; - - if ((r = git_reference_is_branch(r1) - git_reference_is_branch(r2))) - return r; - - return strcmp(git_reference_shorthand(r1), - git_reference_shorthand(r2)); -} - -int writerefs(FILE *fp) { + struct referenceinfo *ris = NULL; struct commitinfo *ci; - const git_oid *id = NULL; - git_object *obj = NULL; - git_reference *dref = NULL, *r, *ref = NULL; - git_reference_iterator *it = NULL; - git_reference **refs = NULL; size_t count, i, j, refcount; const char *titles[] = { "Branches", "Tags" }; const char *ids[] = { "branches", "tags" }; - const char *name; + const char *s; - if (git_reference_iterator_new(&it, repo)) + if (getrefs(&ris, &refcount) == -1) return -1; - for (refcount = 0; !git_reference_next(&ref, it); refcount++) { - if (!(refs = reallocarray(refs, refcount + 1, sizeof(git_reference *)))) - err(1, "realloc"); - refs[refcount] = ref; - } - git_reference_iterator_free(it); - - /* sort by type then shorthand name */ - qsort(refs, refcount, sizeof(git_reference *), refs_cmp); - - for (j = 0; j < 2; j++) { - for (i = 0, count = 0; i < refcount; i++) { - if (!(git_reference_is_branch(refs[i]) && j == 0) && - !(git_reference_is_tag(refs[i]) && j == 1)) - continue; - - switch (git_reference_type(refs[i])) { - case GIT_REF_SYMBOLIC: - if (git_reference_resolve(&dref, refs[i])) - goto err; - r = dref; - break; - case GIT_REF_OID: - r = refs[i]; - break; - default: - continue; - } - if (!git_reference_target(r) || - git_reference_peel(&obj, r, GIT_OBJ_ANY)) - goto err; - if (!(id = git_object_id(obj))) - goto err; - if (!(ci = commitinfo_getbyoid(id))) - break; - - /* print header if it has an entry (first). */ - if (++count == 1) { - fprintf(fp, "<h2>%s</h2><table id=\"%s\">" - "<thead>\n<tr><td><b>Name</b></td>" - "<td><b>Last commit date</b></td>" - "<td><b>Author</b></td>\n</tr>\n" - "</thead><tbody>\n", - titles[j], ids[j]); - } - - relpath = ""; - name = git_reference_shorthand(r); - - fputs("<tr><td>", fp); - xmlencode(fp, name, strlen(name)); - fputs("</td><td>", fp); - if (ci->author) - printtimeshort(fp, &(ci->author->when)); - fputs("</td><td>", fp); - if (ci->author) - xmlencode(fp, ci->author->name, strlen(ci->author->name)); - fputs("</td></tr>\n", fp); - - relpath = "../"; + for (i = 0, j = 0, count = 0; i < refcount; i++) { + if (j == 0 && git_reference_is_tag(ris[i].ref)) { + if (count) + fputs("</tbody></table><br/>\n", fp); + count = 0; + j = 1; + } - commitinfo_free(ci); - git_object_free(obj); - obj = NULL; - git_reference_free(dref); - dref = NULL; + /* print header if it has an entry (first). */ + if (++count == 1) { + fprintf(fp, "<h2>%s</h2><table id=\"%s\">" + "<thead>\n<tr><td><b>Name</b></td>" + "<td><b>Last commit date</b></td>" + "<td><b>Author</b></td>\n</tr>\n" + "</thead><tbody>\n", + titles[j], ids[j]); } - /* table footer */ - if (count) - fputs("</tbody></table><br/>", fp); - } -err: - git_object_free(obj); - git_reference_free(dref); + ci = ris[i].ci; + s = git_reference_shorthand(ris[i].ref); + + fputs("<tr><td>", fp); + xmlencode(fp, s, strlen(s)); + fputs("</td><td>", fp); + if (ci->author) + printtimeshort(fp, &(ci->author->when)); + fputs("</td><td>", fp); + if (ci->author) + xmlencode(fp, ci->author->name, strlen(ci->author->name)); + fputs("</td></tr>\n", fp); + } + /* table footer */ + if (count) + fputs("</tbody></table><br/>\n", fp); - for (i = 0; i < refcount; i++) - git_reference_free(refs[i]); - free(refs); + for (i = 0; i < refcount; i++) { + commitinfo_free(ris[i].ci); + git_reference_free(ris[i].ref); + } + free(ris); return 0; } @@ -1054,7 +1146,8 @@ err: void usage(char *argv0) { - fprintf(stderr, "%s [-c cachefile | -l commits] repodir\n", argv0); + fprintf(stderr, "%s [-c cachefile | -l commits] " + "[-u baseurl] repodir\n", argv0); exit(1); } @@ -1087,6 +1180,10 @@ main(int argc, char *argv[]) if (argv[i][0] == '\0' || *p != '\0' || nlogcommits <= 0 || errno) usage(argv[0]); + } else if (argv[i][1] == 'u') { + if (i + 1 >= argc) + usage(argv[0]); + baseurl = argv[++i]; } } if (!repodir) @@ -1164,7 +1261,7 @@ main(int argc, char *argv[]) } /* check LICENSE */ - for (i = 0; i < sizeof(licensefiles) / sizeof(*licensefiles) && !license; i++) { + for (i = 0; i < LEN(licensefiles) && !license; i++) { if (!git_revparse_single(&obj, repo, licensefiles[i]) && git_object_type(obj) == GIT_OBJ_BLOB) license = licensefiles[i] + strlen("HEAD:"); @@ -1172,7 +1269,7 @@ main(int argc, char *argv[]) } /* check README */ - for (i = 0; i < sizeof(readmefiles) / sizeof(*readmefiles) && !readme; i++) { + for (i = 0; i < LEN(readmefiles) && !readme; i++) { if (!git_revparse_single(&obj, repo, readmefiles[i]) && git_object_type(obj) == GIT_OBJ_BLOB) readme = readmefiles[i] + strlen("HEAD:"); @@ -1254,7 +1351,12 @@ main(int argc, char *argv[]) /* Atom feed */ fp = efopen("atom.xml", "w"); - writeatom(fp); + writeatom(fp, 1); + fclose(fp); + + /* Atom feed for tags / releases */ + fp = efopen("tags.xml", "w"); + writeatom(fp, 0); fclose(fp); /* rename new cache file on success */ diff --git a/style.css b/style.css @@ -29,7 +29,7 @@ a.line { } #blob a { - color: #777; + color: #555; } #blob a:hover { @@ -70,12 +70,12 @@ td.num { } .desc { - color: #777; + color: #555; } hr { border: 0; - border-top: 1px solid #777; + border-top: 1px solid #555; height: 1px; }