diff options
context:
space:
mode:
-rw-r--r--cgitrc.5.txt21
-rw-r--r--custom/cgitrc9
-rw-r--r--source/cgit.c6
-rw-r--r--source/cgit.h4
-rw-r--r--source/shared.c33
-rw-r--r--source/ui-shared.c31
-rwxr-xr-xtests/t0203-dates.sh91
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