diff options
| -rw-r--r-- | cgitrc.5.txt | 21 | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| -rw-r--r-- | custom/cgitrc | 9 | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| -rw-r--r-- | source/cgit.c | 6 | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| -rw-r--r-- | source/cgit.h | 4 | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| -rw-r--r-- | source/shared.c | 33 | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| -rw-r--r-- | source/ui-shared.c | 31 | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| -rwxr-xr-x | tests/t0203-dates.sh | 91 | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
7 files changed, 188 insertions, 7 deletions
diff --git a/cgitrc.5.txt b/cgitrc.5.txt index dba9751..d066dcc 100644 --- a/cgitrc.5.txt +++ b/cgitrc.5.txt @@ -131,6 +131,19 @@ css:: Default value: "/cgit.css". May be given multiple times, each css URL path is added in the head section of the document in turn. +date-format:: + Format used for the calendar dates in the age columns of the log, refs + and index pages, both when "enable-relative-dates" is off and when a + log entry is too old to be shown as an age. Accepts the same names as + git's --date option: "default", "human", "iso" (or "iso8601"), + "iso-strict" (or "iso8601-strict"), "raw", "relative", "rfc" (or + "rfc2822"), "short" and "unix", each of which may carry a "-local" + suffix, plus "format:<strftime>" for an arbitrary layout. An + unrecognised value is ignored. Dates elsewhere, such as the author and + committer lines on the commit page, are always ISO 8601 and are not + affected, nor are the exact timestamps shown on hover. Default + value: "short". See also: "local-time". + email-filter:: Specifies a command which will be invoked to format names and email address of committers, authors, and taggers, as represented in various @@ -210,6 +223,12 @@ enable-log-linecount:: and removed lines for each commit on the repository log page. Default value: "0". +enable-relative-dates:: + Flag which, when set to "1", shows the age columns of the log, refs and + index pages as an elapsed time such as "3 days" rather than a calendar + date. When set to "0" those columns always show a date, formatted + according to "date-format". Default value: "1". + enable-remote-branches:: Flag which, when set to "1", will make cgit display remote branches in the summary and refs views. Default value: "0". See also: @@ -266,7 +285,7 @@ js:: local-time:: Flag which, if set to "1", makes cgit print commit and tag times in the - servers timezone. Default value: "0". + servers timezone. Default value: "0". See also: "date-format". logo:: Url which specifies the source of an image which will be used as a logo diff --git a/custom/cgitrc b/custom/cgitrc index fd4ac34..d456301 100644 --- a/custom/cgitrc +++ b/custom/cgitrc @@ -213,6 +213,10 @@ enable-log-filecount=0 # Default is 0. enable-log-linecount=0 +# Show ages in the log, refs and index columns as an elapsed time such as +# "3 days" rather than a calendar date. Values are 0 or 1. Default is 1. +enable-relative-dates=1 + # Display remote branches in summary and refs views. Values are 0 or 1. # Default is 0. enable-remote-branches=0 @@ -384,6 +388,11 @@ side-by-side-diffs=0 # Default is 0. local-time=0 +# Format for the calendar dates in the age columns. Accepts the names git +# takes for --date, such as short, iso8601, rfc2822, human and unix, each of +# which may carry a -local suffix, plus format:<strftime>. Default is short. +date-format=short + # Ordering of commits in the log view. Leaving this unset uses the git log # default. Values are date or topo. Default is unset. #commit-sort=date diff --git a/source/cgit.c b/source/cgit.c index c824da6..412d5f3 100644 --- a/source/cgit.c +++ b/source/cgit.c @@ -199,6 +199,8 @@ static void config_cb(const char *name, const char *value) ctx.cfg.enable_log_filecount = atoi(value); else if (!strcmp(name, "enable-log-linecount")) ctx.cfg.enable_log_linecount = atoi(value); + else if (!strcmp(name, "enable-relative-dates")) + ctx.cfg.enable_relative_dates = atoi(value); else if (!strcmp(name, "enable-remote-branches")) ctx.cfg.enable_remote_branches = atoi(value); else if (!strcmp(name, "enable-subject-links")) @@ -311,6 +313,8 @@ static void config_cb(const char *name, const char *value) ctx.cfg.clone_url = strdup_first_line(value); else if (!strcmp(name, "local-time")) ctx.cfg.local_time = atoi(value); + else if (!strcmp(name, "date-format")) + cgit_parse_date_format(value, &ctx.cfg.date_mode); else if (!strcmp(name, "commit-sort")) { if (!strcmp(value, "date")) ctx.cfg.commit_sort = 1; @@ -412,6 +416,8 @@ static void prepare_context(void) ctx.cfg.logo = "/cgit.png"; ctx.cfg.favicon = "/favicon.ico"; ctx.cfg.local_time = 0; + ctx.cfg.date_mode = date_mode_from_type(DATE_SHORT); + ctx.cfg.enable_relative_dates = 1; ctx.cfg.enable_http_clone = 1; ctx.cfg.enable_index_owner = 1; ctx.cfg.enable_tree_linenumbers = 1; diff --git a/source/cgit.h b/source/cgit.h index 78129b5..c41854e 100644 --- a/source/cgit.h +++ b/source/cgit.h @@ -7,6 +7,7 @@ #include <archive.h> #include <commit.h> +#include <date.h> #include <diffcore.h> #include <diff.h> #include <environment.h> @@ -217,6 +218,7 @@ struct cgit_config { char *repository_sort; char *virtual_root; /* Always ends with '/'. */ char *strict_export; + struct date_mode date_mode; int cache_size; int cache_dynamic_ttl; int cache_repo_ttl; @@ -237,6 +239,7 @@ struct cgit_config { int enable_commit_graph; int enable_log_filecount; int enable_log_linecount; + int enable_relative_dates; int enable_remote_branches; int enable_subject_links; int enable_html_serving; @@ -382,6 +385,7 @@ extern void cgit_parse_url(const char *url); extern const char *cgit_repobasename(const char *reponame); extern int cgit_parse_snapshots_mask(const char *str); +extern void cgit_parse_date_format(const char *format, struct date_mode *mode); extern const struct object_id *cgit_snapshot_get_sig(const char *ref, const struct cgit_snapshot_format *f); extern const unsigned cgit_snapshot_format_bit(const struct cgit_snapshot_format *f); diff --git a/source/shared.c b/source/shared.c index 59f1c8e..058d938 100644 --- a/source/shared.c +++ b/source/shared.c @@ -439,6 +439,39 @@ int cgit_parse_snapshots_mask(const char *str) return rv; } +/* + * Git's parse_date_format() dies on anything it does not recognize, which + * would turn a typo in cgitrc into a failed request, so gate it behind the + * set of formats cgit documents and leave the mode alone otherwise. + */ +void cgit_parse_date_format(const char *format, struct date_mode *mode) +{ + static const char * const names[] = { + "default", "human", "iso", "iso-strict", "iso8601", + "iso8601-strict", "local", "raw", "relative", "rfc", + "rfc2822", "short", "unix", + }; + const char *rest; + size_t i; + + if (skip_prefix(format, "format:", &rest)) { + // an empty strftime format would render every date blank + if (*rest) + parse_date_format(format, mode); + return; + } + + for (i = 0; i < ARRAY_SIZE(names); i++) { + if (!skip_prefix(format, names[i], &rest)) + continue; + // "iso" also prefixes "iso8601", so keep looking on a partial hit + if (*rest && strcmp(rest, "-local")) + continue; + parse_date_format(format, mode); + return; + } +} + typedef struct { char * name; char * value; diff --git a/source/ui-shared.c b/source/ui-shared.c index ace06b2..d023243 100644 --- a/source/ui-shared.c +++ b/source/ui-shared.c @@ -672,6 +672,28 @@ const struct date_mode cgit_date_mode(enum date_mode_type type) return mode; } +/* + * The mode date-format selects, used wherever an age column falls back to a + * calendar date. local-time still forces local rendering on top of it. + */ +static const struct date_mode cgit_configured_date_mode(void) +{ + struct date_mode mode = ctx.cfg.date_mode; + + if (ctx.cfg.local_time) + mode.local = 1; + return mode; +} + +static void print_abs_date(time_t t, int tz) +{ + html("<span title='"); + html_attr(show_date(t, tz, cgit_date_mode(DATE_ISO8601))); + html("'>"); + html_txt(show_date(t, tz, cgit_configured_date_mode())); + html("</span>"); +} + static void print_rel_date(time_t t, int tz, double value, const char *class, const char *suffix) { @@ -691,12 +713,9 @@ void cgit_print_age(time_t t, int tz, time_t max_relative) if (secs < 0) secs = 0; - if (secs > max_relative && max_relative >= 0) { - html("<span title='"); - html_attr(show_date(t, tz, cgit_date_mode(DATE_ISO8601))); - html("'>"); - html_txt(show_date(t, tz, cgit_date_mode(DATE_SHORT))); - html("</span>"); + if (!ctx.cfg.enable_relative_dates || + (secs > max_relative && max_relative >= 0)) { + print_abs_date(t, tz); return; } diff --git a/tests/t0203-dates.sh b/tests/t0203-dates.sh new file mode 100755 index 0000000..1bd6253 --- /dev/null +++ b/tests/t0203-dates.sh @@ -0,0 +1,91 @@ +#!/bin/sh + +test_description='Check the date display options' +. ./setup.sh + +# An old commit and a fresh one, so a single log page exercises both the +# relative rendering and the calendar fallback past the two week cutoff. +test_expect_success 'set up a dated repo' ' + mkrepo repos/dated 1 && + ( + cd repos/dated && + GIT_AUTHOR_DATE="@1112911993 +0000" \ + GIT_COMMITTER_DATE="@1112911993 +0000" \ + git commit --allow-empty -m old && + GIT_AUTHOR_DATE="@$(date +%s) +0000" \ + GIT_COMMITTER_DATE="@$(date +%s) +0000" \ + git commit --allow-empty -m fresh + ) +' + +# Each argument is written as one extra config line. +daterc() { + { + echo "virtual-root=/" + echo "cache-size=0" + printf '%s\n' "$@" + echo "repo.url=dated" + echo "repo.path=$PWD/repos/dated/.git" + } >daterc +} + +dateq() { CGIT_CONFIG="$PWD/daterc" QUERY_STRING="$1" cgit; } + +test_expect_success 'relative dates are on by default' ' + daterc && + dateq "url=dated/refs/" >tmp && + grep "class=.age-" tmp +' + +test_expect_success 'enable-relative-dates=0 shows a calendar date' ' + daterc "enable-relative-dates=0" && + dateq "url=dated/refs/" >tmp && + ! grep "class=.age-" tmp && + grep "[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]<" tmp +' + +test_expect_success 'the index and log columns follow the same flag' ' + daterc "enable-relative-dates=0" && + dateq "" >tmp && + ! grep "class=.age-" tmp && + dateq "url=dated/log/" >tmp && + ! grep "class=.age-" tmp +' + +test_expect_success 'date-format selects the calendar format' ' + daterc "enable-relative-dates=0" "date-format=iso8601" && + dateq "url=dated/refs/" >tmp && + grep ">[0-9-]* [0-9][0-9]:[0-9][0-9]:[0-9][0-9] +0000<" tmp +' + +test_expect_success 'date-format accepts a strftime format' ' + daterc "enable-relative-dates=0" "date-format=format:%Y/%m/%d" && + dateq "url=dated/refs/" >tmp && + grep ">[0-9][0-9][0-9][0-9]/[0-9][0-9]/[0-9][0-9]<" tmp +' + +test_expect_success 'an unknown date-format falls back to short' ' + daterc "enable-relative-dates=0" "date-format=nonsense" && + dateq "url=dated/refs/" >tmp && + grep "[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]<" tmp && + daterc "enable-relative-dates=0" "date-format=format:" && + dateq "url=dated/refs/" >tmp && + grep "[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]<" tmp +' + +test_expect_success 'date-format still formats the log fallback' ' + daterc "date-format=rfc2822" && + dateq "url=dated/log/" >tmp && + grep "class=.age-" tmp && + grep ">[A-Z][a-z][a-z], [0-9]* Apr 2005 " tmp +' + +test_expect_success 'date-format leaves the commit page on ISO 8601' ' + daterc "date-format=rfc2822" && + sha=$(git -C repos/dated rev-parse HEAD~1) && + dateq "url=dated/commit/&id=$sha" >tmp && + grep "2005-04-07 [0-9:]* +0000" tmp && + ! grep "Apr 2005" tmp +' + +test_done |
