Skip to contentSkip to conversation

date: add --date=elapsed and %aX/%cX placeholders

PATCH New Israel Roldan via GitGitGadget 1 participant 1 message Jul 30 Documentation

lore
Share & tools
lore
#0 lore raw

PATCH 1/1 date: add --date=elapsed and %aX/%cX placeholders

From: Israel Roldan <[email protected]>

The --date=human format and the %cr/%ch placeholders both produce a localized English phrase that hides the precise amount of time that has passed. A commit shown as "2 days ago" may in reality be 1 day 3 hours, or 1 day 23 hours. Switching to %ct and a calculator solves the problem but is not glance-friendly.

Add an uptime(1)-style elapsed-time format:

  --date=elapsed          --date=elapsed-pad
  39s                     39s
  8h 7m 48s               08h 07m 48s
  1y 11M 5d 13h 5m 13s    01y 11M 05d 13h 05m 13s

The placeholder letter is X because the obvious choice E is already used for the mailmap-respecting email variants (%aE, %cE, %gE).

---
[RFC PATCH] date: add --date=elapsed and %aX/%cX placeholders

Motivation
==========

The --date=human format and the %cr/%ch placeholders both produce a localized English phrase that hides the precise amount of time that has passed. A commit shown as "2 days ago" may in reality be 1 day 3 hours, or 1 day 23 hours. Switching to %ct and a calculator solves the problem but is not glance-friendly.

The closest external tool that solves the same problem is git-log-compact by Kyle J. McKay (2016), which the maintainer declined to merge into Git core because it was a heavyweight change to the oneline output rather than a date format.

What this patch adds
====================

A new date format --date=elapsed and --date=elapsed-pad modelled on uptime(1):

seconds --date=elapsed --date=elapsed-pad
39 "39s" "39s"
984 "16m 24s" "16m 24s"
29268 "8h 7m 48s" "08h 07m 48s"
3024000 "1M 5d 0s" "01M 05d 00h 00m 00s"
60527113 "1y 11M 5d 13h 5m 13s" "01y 11M 05d 13h 05m 13s"

The padded variant zero-pads every emitted unit to at least two digits.

A commit timestamp that is in the future is reported as "0s", not the localized phrase "in the future" that show_date_relative() emits.

Months are fixed at 30 days and years at 365 days, matching the uptime(1) convention. They are consistent, and the same approximations are already in use by Git's own show_date_relative() for the relative-time format.

Two new pretty-format placeholders, %aX (author) and %cX (committer):

git log --format='%h (%aX) %s'
13c7afec21 (1d 2h 9s) The 8th batch
8c7817c361 (1y 3d 19h 5m 10s) Merge branch 'rj/meson-libexecdir-fix' 0e521a41b5 (11y 17d 21h 8m 40s) Merge branch 'et/http-proxyauth'

The placeholders mirror %ch and %cr, which also ignore the caller's --date= and emit a fixed shape.

Open questions
==============

Q1. The placeholder letter. The natural mnemonic is E (Elapsed), but %aE, %cE and %gE are already taken (mailmap-respecting email variants). This patch uses X as a free letter. Would the maintainer prefer a different letter? I am not attached to X.

Q2. Should --date=elapsed-pad be its own format name, or a separate --date=elapsed plus a flag? Which mirrors --date=human / --date=human-local / --date=iso / --date=iso-local.

Q3. The "future timestamp" behaviour. This patch clamps a future timestamp to "0s". An alternative would be to keep the relative format's "in the future" wording.

Q4. %aX/%cX are hard-coded compact; padded output requires %ad/%cd together with --date=elapsed-pad. Should a paired %aY/%cY for padded also exist, or is the asymmetry acceptable?

Patch
=====

The patch modifies the existing date_mode_type enum, adds a new show_date_elapsed() helper in date.c, wires it through show_date() and parse_date_format(), exposes two placeholders via format_person_part() in pretty.c, handles the new format in builtin/blame.c for column width, updates the documentation, and adds tests for both the date helper and the pretty-format placeholder equivalence.

The full patch is attached below.

Testing
=======

$ make DEVELOPER=1 && cd t &&
prove --no-color ./t0006-date.sh ./t4205-log-pretty-formats.sh ./t0006-date.sh ................ ok ./t4205-log-pretty-formats.sh .. ok All tests successful.

The full test suite (1054 test files, 33832 tests) also passes on this machine.

Thanks for your time. I am happy to iterate on any of the open questions before posting a non-RFC version.

Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-git-2368%2Fairvzxf%2Ffeature%2Fdate-elapsed-v1
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-git-2368/airvzxf/feature/date-elapsed-v1
Pull-Request: https://github.com/git/git/pull/2368

 Documentation/pretty-formats.adoc      |  4 ++
 Documentation/rev-list-options.adoc    | 13 ++++
 builtin/blame.c                        |  8 +++
 contrib/completion/git-completion.bash |  2 +-
 date.c                                 | 88 +++++++++++++++++++++++++-
 date.h                                 |  4 +-
 pretty.c                               |  3 +
 t/helper/test-date.c                   | 19 +++++-
 t/t0006-date.sh                        | 43 +++++++++++++
 t/t4205-log-pretty-formats.sh          |  6 ++
 10 files changed, 186 insertions(+), 4 deletions(-)
Documentation/pretty-formats.adoc +4 -0
index 2ae0eb11a9..9ae882b160 100644
@@ -233,6 +233,8 @@ The placeholders are:
 +%as+:: author date, short format (`YYYY-MM-DD`)
 +%ah+:: author date, human style (like the `--date=human` option of
 	linkgit:git-rev-list[1])
++%aX+:: author date, elapsed (like the `--date=elapsed` option of
+	linkgit:git-rev-list[1])
 +%cn+:: committer name
 +%cN+:: committer name (respecting .mailmap, see
 	linkgit:git-shortlog[1] or linkgit:git-blame[1])
@@ -251,6 +253,8 @@ The placeholders are:
 +%cs+:: committer date, short format (`YYYY-MM-DD`)
 +%ch+:: committer date, human style (like the `--date=human` option of
 	linkgit:git-rev-list[1])
++%cX+:: committer date, elapsed (like the `--date=elapsed` option of
+	linkgit:git-rev-list[1])
 +%d+:: ref names, like the --decorate option of linkgit:git-log[1]
 +%D+:: ref names without the " (", ")" wrapping.
 +%(count)+:: the number of a patch within a patch series. Used only in
Documentation/rev-list-options.adoc +13 -0
index fd831f0ec6..937c6096c5 100644
@@ -1174,6 +1174,19 @@ omitted.
 1970).  As with `--raw`, this is always in UTC and therefore `-local`
 has no effect.
 
+`--date=elapsed` shows the elapsed time between the commit and now,
+in a compact, script-friendly, `uptime(1)`-style format. Only the
+non-zero leading units are emitted, separated by single spaces. For
+example "39s", "16m 24s", "8h 7m 48s", "12M 30d 13h 25m 43s",
+"1y 11M 5d 13h 5m 13s". A commit timestamp that is in the future
+is reported as "0s". Months are fixed at 30 days and years at 365
+days, matching the `uptime(1)` convention.
+
+`--date=elapsed-pad` is like `--date=elapsed`, but every emitted
+unit is at least two digits wide, zero-padded on the left, so
+that columns line up across rows.  For example "16m 24s",
+"08h 07m 48s", "01M 05d 00h 00m 00s", "01y 11M 05d 13h 05m 13s".
+
 `--date=format:<format>` feeds the _<format>_ to your system `strftime`,
 except for `%s`, `%z`, and `%Z`, which are handled internally.
 Use `--date=format:%c` to show the date in your system locale's
builtin/blame.c +8 -0
index 48d5251c6d..f3fd866e82 100644
@@ -1123,6 +1123,14 @@ parse_done:
 		/* If the year is shown, no time is shown */
 		blame_date_width = sizeof("Thu Oct 19 16:00");
 		break;
+	case DATE_ELAPSED:
+	case DATE_ELAPSED_PAD:
+		/*
+		 * Maximum display width of the elapsed time format,
+		 * e.g. "99y 99M 99d 99h 99m 99s" -- a safe upper bound.
+		 */
+		blame_date_width = sizeof("99y 99M 99d 99h 99m 99s");
+		break;
 	case DATE_NORMAL:
 		blame_date_width = sizeof("Thu Oct 19 16:00:04 2006 -0700");
 		break;
contrib/completion/git-completion.bash +1 -1
index e875787710..4db014c7c0 100644
@@ -2231,7 +2231,7 @@ __git_log_show_options="
 __git_diff_merges_opts="off none on first-parent 1 separate m combined c dense-combined cc remerge r"
 
 __git_log_pretty_formats="oneline short medium full fuller reference email raw format: tformat: mboxrd"
-__git_log_date_formats="relative iso8601 iso8601-strict rfc2822 short local default human raw unix auto: format:"
+__git_log_date_formats="relative iso8601 iso8601-strict rfc2822 short local default human raw unix elapsed auto: format:"
 
 # Complete porcelain (i.e. not git-rev-list) options and at least some
 # option arguments accepted by git-log.  Note that this same set of options
date.c +87 -1

112 lines not on this page · raw message

date.h +3 -1
index 0747864fd7..eb7f1fb6ad 100644
@@ -17,7 +17,9 @@ enum date_mode_type {
 	DATE_RFC2822,
 	DATE_STRFTIME,
 	DATE_RAW,
-	DATE_UNIX
+	DATE_UNIX,
+	DATE_ELAPSED,
+	DATE_ELAPSED_PAD
 };
 
 struct date_mode {
pretty.c +3 -0
index d8a9f370f6..9ba274c64a 100644
@@ -848,6 +848,9 @@ static size_t format_person_part(struct strbuf *sb, char part,
 	case 'h':	/* date, human */
 		strbuf_addstr(sb, show_ident_date(&s, DATE_MODE(HUMAN)));
 		return placeholder_len;
+	case 'X':	/* date, elapsed */
+		strbuf_addstr(sb, show_ident_date(&s, DATE_MODE(ELAPSED)));
+		return placeholder_len;
 	case 's':
 		strbuf_addstr(sb, show_ident_date(&s, DATE_MODE(SHORT)));
 		return placeholder_len;
t/helper/test-date.c +18 -1
index 87d2ad6fca..13101201d0 100644
@@ -11,7 +11,9 @@ static const char *const usage_msg = "\n"
 "  test-tool date timestamp [date]...\n"
 "  test-tool date getnanos [start-nanos]\n"
 "  test-tool date is64bit\n"
-"  test-tool date time_t-is64bit\n";
+"  test-tool date time_t-is64bit\n"
+"  test-tool date elapsed [time_t]...\n"
+"  test-tool date elapsed-pad [time_t]...\n";
 
 static void show_relative_dates(const char **argv)
 {
@@ -33,6 +35,17 @@ static void show_human_dates(const char **argv)
 	}
 }
 
+static void show_elapsed_dates(const char **argv, int pad)
+{
+	for (; *argv; argv++) {
+		time_t t = atoi(*argv);
+		struct date_mode mode = DATE_MODE(ELAPSED);
+		if (pad)
+			mode = DATE_MODE(ELAPSED_PAD);
+		printf("%s -> %s\n", *argv, show_date(t, 0, mode));
+	}
+}
+
 static void show_dates(const char **argv, const char *format)
 {
 	struct date_mode mode = DATE_MODE_INIT;
@@ -115,6 +128,10 @@ int cmd__date(int argc UNUSED, const char **argv)
 		show_relative_dates(argv+1);
 	else if (!strcmp(*argv, "human"))
 		show_human_dates(argv+1);
+	else if (!strcmp(*argv, "elapsed"))
+		show_elapsed_dates(argv+1, 0);
+	else if (!strcmp(*argv, "elapsed-pad"))
+		show_elapsed_dates(argv+1, 1);
 	else if (skip_prefix(*argv, "show:", &x))
 		show_dates(argv+1, x);
 	else if (!strcmp(*argv, "parse"))
t/t0006-date.sh +43 -0

47 lines not on this page · raw message

t/t4205-log-pretty-formats.sh +6 -0
index 4be5c51489..307be52b8f 100755
@@ -557,6 +557,12 @@ test_expect_success '--date=human %ad%cd is the same as %ah%ch' '
 	test_cmp expected actual
 '
 
+test_expect_success '--date=elapsed %ad%cd is the same as %aX%cX' '
+	git log --format=%ad%n%cd --date=elapsed >expected &&
+	git log --format=%aX%n%cX >actual &&
+	test_cmp expected actual
+'
+
 # get new digests (with no abbreviations)
 test_expect_success 'set up log decoration tests' '
 	head1=$(git rev-parse --verify HEAD~0) &&

base-commit: 13c7afec212fc97ce257d15601659314c6673d6c

Signature · 1 line
gitgitgadget