summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJunio C Hamano <gitster@pobox.com>2015-08-03 11:01:27 -0700
committerJunio C Hamano <gitster@pobox.com>2015-08-03 11:01:27 -0700
commitd939af12bd96db7ad3e671a0585ad8570aa7e9d3 (patch)
tree5772ac1192ed299db657ffcfbc71c318521db653
parent980a3d3dd742d534b394b04e578c2de5e3ee4793 (diff)
parente4f031e34b08e3217c10942e682920a6939308a0 (diff)
downloadgit-d939af12bd96db7ad3e671a0585ad8570aa7e9d3.tar.gz
Merge branch 'jk/date-mode-format'
Teach "git log" and friends a new "--date=format:..." option to format timestamps using system's strftime(3). * jk/date-mode-format: strbuf: make strbuf_addftime more robust introduce "format" date-mode convert "enum date_mode" into a struct show-branch: use DATE_RELATIVE instead of magic number
-rw-r--r--Documentation/rev-list-options.txt5
-rw-r--r--archive.c2
-rw-r--r--builtin/blame.c13
-rw-r--r--builtin/commit.c4
-rw-r--r--builtin/log.c4
-rw-r--r--builtin/shortlog.c2
-rw-r--r--builtin/show-branch.c3
-rw-r--r--cache.h37
-rw-r--r--commit.h2
-rw-r--r--date.c52
-rw-r--r--fast-import.c2
-rw-r--r--gettext.c1
-rw-r--r--http-backend.c2
-rw-r--r--log-tree.c2
-rw-r--r--pretty.c29
-rw-r--r--ref-filter.c6
-rw-r--r--reflog-walk.c4
-rw-r--r--reflog-walk.h4
-rw-r--r--refs.c4
-rw-r--r--revision.c4
-rw-r--r--revision.h2
-rw-r--r--sha1_name.c2
-rw-r--r--strbuf.c33
-rw-r--r--strbuf.h5
-rw-r--r--submodule.c2
-rwxr-xr-xt/t6300-for-each-ref.sh18
-rw-r--r--test-date.c4
-rw-r--r--test-revision-walking.c2
28 files changed, 172 insertions, 78 deletions
diff --git a/Documentation/rev-list-options.txt b/Documentation/rev-list-options.txt
index 77ac439234..a9b808fab3 100644
--- a/Documentation/rev-list-options.txt
+++ b/Documentation/rev-list-options.txt
@@ -727,6 +727,11 @@ format, often found in email messages.
+
`--date=raw` shows the date in the internal raw Git format `%s %z` format.
+
+`--date=format:...` feeds the format `...` to your system `strftime`.
+Use `--date=format:%c` to show the date in your system locale's
+preferred format. See the `strftime` manual for a complete list of
+format placeholders.
++
`--date=default` shows timestamps in the original time zone
(either committer's or author's).
diff --git a/archive.c b/archive.c
index 936a5945dd..01b0899b3f 100644
--- a/archive.c
+++ b/archive.c
@@ -34,7 +34,7 @@ static void format_subst(const struct commit *commit,
char *to_free = NULL;
struct strbuf fmt = STRBUF_INIT;
struct pretty_print_context ctx = {0};
- ctx.date_mode = DATE_NORMAL;
+ ctx.date_mode.type = DATE_NORMAL;
ctx.abbrev = DEFAULT_ABBREV;
if (src == buf->buf)
diff --git a/builtin/blame.c b/builtin/blame.c
index 272a222687..04e4864f8b 100644
--- a/builtin/blame.c
+++ b/builtin/blame.c
@@ -51,7 +51,7 @@ static int xdl_opts;
static int abbrev = -1;
static int no_whole_file_rename;
-static enum date_mode blame_date_mode = DATE_ISO8601;
+static struct date_mode blame_date_mode = { DATE_ISO8601 };
static size_t blame_date_width;
static struct string_list mailmap;
@@ -1828,7 +1828,7 @@ static const char *format_time(unsigned long time, const char *tz_str,
size_t time_width;
int tz;
tz = atoi(tz_str);
- time_str = show_date(time, tz, blame_date_mode);
+ time_str = show_date(time, tz, &blame_date_mode);
strbuf_addstr(&time_buf, time_str);
/*
* Add space paddings to time_buf to display a fixed width
@@ -2188,7 +2188,7 @@ static int git_blame_config(const char *var, const char *value, void *cb)
if (!strcmp(var, "blame.date")) {
if (!value)
return config_error_nonbool(var);
- blame_date_mode = parse_date_format(value);
+ parse_date_format(value, &blame_date_mode);
return 0;
}
@@ -2570,13 +2570,13 @@ parse_done:
if (cmd_is_annotate) {
output_option |= OUTPUT_ANNOTATE_COMPAT;
- blame_date_mode = DATE_ISO8601;
+ blame_date_mode.type = DATE_ISO8601;
} else {
blame_date_mode = revs.date_mode;
}
/* The maximum width used to show the dates */
- switch (blame_date_mode) {
+ switch (blame_date_mode.type) {
case DATE_RFC2822:
blame_date_width = sizeof("Thu, 19 Oct 2006 16:00:04 -0700");
break;
@@ -2605,6 +2605,9 @@ parse_done:
case DATE_NORMAL:
blame_date_width = sizeof("Thu Oct 19 16:00:04 2006 -0700");
break;
+ case DATE_STRFTIME:
+ blame_date_width = strlen(show_date(0, 0, &blame_date_mode)) + 1; /* add the null */
+ break;
}
blame_date_width -= 1; /* strip the null */
diff --git a/builtin/commit.c b/builtin/commit.c
index 254477fd1d..7314f33885 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -856,7 +856,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
_("%s"
"Date: %s"),
ident_shown++ ? "" : "\n",
- show_ident_date(&ai, DATE_NORMAL));
+ show_ident_date(&ai, DATE_MODE(NORMAL)));
if (!committer_ident_sufficiently_given())
status_printf_ln(s, GIT_COLOR_NORMAL,
@@ -1046,7 +1046,7 @@ static const char *find_author_by_nickname(const char *name)
commit = get_revision(&revs);
if (commit) {
struct pretty_print_context ctx = {0};
- ctx.date_mode = DATE_NORMAL;
+ ctx.date_mode.type = DATE_NORMAL;
strbuf_release(&buf);
format_commit_message(commit, "%aN <%aE>", &buf, &ctx);
clear_mailmap(&mailmap);
diff --git a/builtin/log.c b/builtin/log.c
index c851c7cc23..b50ef7510c 100644
--- a/builtin/log.c
+++ b/builtin/log.c
@@ -116,7 +116,7 @@ static void cmd_log_init_defaults(struct rev_info *rev)
DIFF_OPT_SET(&rev->diffopt, ALLOW_TEXTCONV);
if (default_date_mode)
- rev->date_mode = parse_date_format(default_date_mode);
+ parse_date_format(default_date_mode, &rev->date_mode);
rev->diffopt.touched_flags = 0;
}
@@ -956,7 +956,7 @@ static void make_cover_letter(struct rev_info *rev, int use_stdout,
msg = body;
pp.fmt = CMIT_FMT_EMAIL;
- pp.date_mode = DATE_RFC2822;
+ pp.date_mode.type = DATE_RFC2822;
pp_user_info(&pp, NULL, &sb, committer, encoding);
pp_title_line(&pp, &msg, &sb, encoding, need_8bit_cte);
pp_remainder(&pp, &msg, &sb, 0);
diff --git a/builtin/shortlog.c b/builtin/shortlog.c
index c0bab6aaa9..007cc66a03 100644
--- a/builtin/shortlog.c
+++ b/builtin/shortlog.c
@@ -138,7 +138,7 @@ void shortlog_add_commit(struct shortlog *log, struct commit *commit)
ctx.abbrev = log->abbrev;
ctx.subject = "";
ctx.after_subject = "";
- ctx.date_mode = DATE_NORMAL;
+ ctx.date_mode.type = DATE_NORMAL;
ctx.output_encoding = get_log_output_encoding();
pretty_print_commit(&ctx, commit, &ufbuf);
buffer = ufbuf.buf;
diff --git a/builtin/show-branch.c b/builtin/show-branch.c
index 323f857463..c87c46eb38 100644
--- a/builtin/show-branch.c
+++ b/builtin/show-branch.c
@@ -784,7 +784,8 @@ int cmd_show_branch(int ac, const char **av, const char *prefix)
else
msg++;
reflog_msg[i] = xstrfmt("(%s) %s",
- show_date(timestamp, tz, 1),
+ show_date(timestamp, tz,
+ DATE_MODE(RELATIVE)),
msg);
free(logmsg);
sprintf(nth_desc, "%s@{%d}", *av, base+i);
diff --git a/cache.h b/cache.h
index 6997b2cd47..6bb7119032 100644
--- a/cache.h
+++ b/cache.h
@@ -1049,18 +1049,30 @@ extern void *read_object_with_reference(const unsigned char *sha1,
extern struct object *peel_to_type(const char *name, int namelen,
struct object *o, enum object_type);
-enum date_mode {
- DATE_NORMAL = 0,
- DATE_RELATIVE,
- DATE_SHORT,
- DATE_LOCAL,
- DATE_ISO8601,
- DATE_ISO8601_STRICT,
- DATE_RFC2822,
- DATE_RAW
+struct date_mode {
+ enum date_mode_type {
+ DATE_NORMAL = 0,
+ DATE_RELATIVE,
+ DATE_SHORT,
+ DATE_LOCAL,
+ DATE_ISO8601,
+ DATE_ISO8601_STRICT,
+ DATE_RFC2822,
+ DATE_STRFTIME,
+ DATE_RAW
+ } type;
+ const char *strftime_fmt;
};
-const char *show_date(unsigned long time, int timezone, enum date_mode mode);
+/*
+ * Convenience helper for passing a constant type, like:
+ *
+ * show_date(t, tz, DATE_MODE(NORMAL));
+ */
+#define DATE_MODE(t) date_mode_from_type(DATE_##t)
+struct date_mode *date_mode_from_type(enum date_mode_type type);
+
+const char *show_date(unsigned long time, int timezone, const struct date_mode *mode);
void show_date_relative(unsigned long time, int tz, const struct timeval *now,
struct strbuf *timebuf);
int parse_date(const char *date, struct strbuf *out);
@@ -1070,7 +1082,7 @@ void datestamp(struct strbuf *out);
#define approxidate(s) approxidate_careful((s), NULL)
unsigned long approxidate_careful(const char *, int *);
unsigned long approxidate_relative(const char *date, const struct timeval *now);
-enum date_mode parse_date_format(const char *format);
+void parse_date_format(const char *format, struct date_mode *mode);
int date_overflows(unsigned long date);
#define IDENT_STRICT 1
@@ -1107,7 +1119,8 @@ extern int split_ident_line(struct ident_split *, const char *, int);
* the ident_split. It will also sanity-check the values and produce
* a well-known sentinel date if they appear bogus.
*/
-const char *show_ident_date(const struct ident_split *id, enum date_mode mode);
+const char *show_ident_date(const struct ident_split *id,
+ const struct date_mode *mode);
/*
* Compare split idents for equality or strict ordering. Note that we
diff --git a/commit.h b/commit.h
index 4983bdd3d3..5d58be0017 100644
--- a/commit.h
+++ b/commit.h
@@ -145,7 +145,7 @@ struct pretty_print_context {
const char *subject;
const char *after_subject;
int preserve_subject;
- enum date_mode date_mode;
+ struct date_mode date_mode;
unsigned date_mode_explicit:1;
int need_8bit_cte;
char *notes_message;
diff --git a/date.c b/date.c
index 733d1b29b1..8f9156909b 100644
--- a/date.c
+++ b/date.c
@@ -160,18 +160,27 @@ void show_date_relative(unsigned long time, int tz,
(diff + 183) / 365);
}
-const char *show_date(unsigned long time, int tz, enum date_mode mode)
+struct date_mode *date_mode_from_type(enum date_mode_type type)
+{
+ static struct date_mode mode;
+ if (type == DATE_STRFTIME)
+ die("BUG: cannot create anonymous strftime date_mode struct");
+ mode.type = type;
+ return &mode;
+}
+
+const char *show_date(unsigned long time, int tz, const struct date_mode *mode)
{
struct tm *tm;
static struct strbuf timebuf = STRBUF_INIT;
- if (mode == DATE_RAW) {
+ if (mode->type == DATE_RAW) {
strbuf_reset(&timebuf);
strbuf_addf(&timebuf, "%lu %+05d", time, tz);
return timebuf.buf;
}
- if (mode == DATE_RELATIVE) {
+ if (mode->type == DATE_RELATIVE) {
struct timeval now;
strbuf_reset(&timebuf);
@@ -180,7 +189,7 @@ const char *show_date(unsigned long time, int tz, enum date_mode mode)
return timebuf.buf;
}
- if (mode == DATE_LOCAL)
+ if (mode->type == DATE_LOCAL)
tz = local_tzoffset(time);
tm = time_to_tm(time, tz);
@@ -190,17 +199,17 @@ const char *show_date(unsigned long time, int tz, enum date_mode mode)
}
strbuf_reset(&timebuf);
- if (mode == DATE_SHORT)
+ if (mode->type == DATE_SHORT)
strbuf_addf(&timebuf, "%04d-%02d-%02d", tm->tm_year + 1900,
tm->tm_mon + 1, tm->tm_mday);
- else if (mode == DATE_ISO8601)
+ else if (mode->type == DATE_ISO8601)
strbuf_addf(&timebuf, "%04d-%02d-%02d %02d:%02d:%02d %+05d",
tm->tm_year + 1900,
tm->tm_mon + 1,
tm->tm_mday,
tm->tm_hour, tm->tm_min, tm->tm_sec,
tz);
- else if (mode == DATE_ISO8601_STRICT) {
+ else if (mode->type == DATE_ISO8601_STRICT) {
char sign = (tz >= 0) ? '+' : '-';
tz = abs(tz);
strbuf_addf(&timebuf, "%04d-%02d-%02dT%02d:%02d:%02d%c%02d:%02d",
@@ -209,11 +218,13 @@ const char *show_date(unsigned long time, int tz, enum date_mode mode)
tm->tm_mday,
tm->tm_hour, tm->tm_min, tm->tm_sec,
sign, tz / 100, tz % 100);
- } else if (mode == DATE_RFC2822)
+ } else if (mode->type == DATE_RFC2822)
strbuf_addf(&timebuf, "%.3s, %d %.3s %d %02d:%02d:%02d %+05d",
weekday_names[tm->tm_wday], tm->tm_mday,
month_names[tm->tm_mon], tm->tm_year + 1900,
tm->tm_hour, tm->tm_min, tm->tm_sec, tz);
+ else if (mode->type == DATE_STRFTIME)
+ strbuf_addftime(&timebuf, mode->strftime_fmt, tm);
else
strbuf_addf(&timebuf, "%.3s %.3s %d %02d:%02d:%02d %d%c%+05d",
weekday_names[tm->tm_wday],
@@ -221,7 +232,7 @@ const char *show_date(unsigned long time, int tz, enum date_mode mode)
tm->tm_mday,
tm->tm_hour, tm->tm_min, tm->tm_sec,
tm->tm_year + 1900,
- (mode == DATE_LOCAL) ? 0 : ' ',
+ (mode->type == DATE_LOCAL) ? 0 : ' ',
tz);
return timebuf.buf;
}
@@ -759,28 +770,31 @@ int parse_date(const char *date, struct strbuf *result)
return 0;
}
-enum date_mode parse_date_format(const char *format)
+void parse_date_format(const char *format, struct date_mode *mode)
{
if (!strcmp(format, "relative"))
- return DATE_RELATIVE;
+ mode->type = DATE_RELATIVE;
else if (!strcmp(format, "iso8601") ||
!strcmp(format, "iso"))
- return DATE_ISO8601;
+ mode->type = DATE_ISO8601;
else if (!strcmp(format, "iso8601-strict") ||
!strcmp(format, "iso-strict"))
- return DATE_ISO8601_STRICT;
+ mode->type = DATE_ISO8601_STRICT;
else if (!strcmp(format, "rfc2822") ||
!strcmp(format, "rfc"))
- return DATE_RFC2822;
+ mode->type = DATE_RFC2822;
else if (!strcmp(format, "short"))
- return DATE_SHORT;
+ mode->type = DATE_SHORT;
else if (!strcmp(format, "local"))
- return DATE_LOCAL;
+ mode->type = DATE_LOCAL;
else if (!strcmp(format, "default"))
- return DATE_NORMAL;
+ mode->type = DATE_NORMAL;
else if (!strcmp(format, "raw"))
- return DATE_RAW;
- else
+ mode->type = DATE_RAW;
+ else if (skip_prefix(format, "format:", &format)) {
+ mode->type = DATE_STRFTIME;
+ mode->strftime_fmt = xstrdup(format);
+ } else
die("unknown date format %s", format);
}
diff --git a/fast-import.c b/fast-import.c
index 6a0c0ac570..2ad4fee07e 100644
--- a/fast-import.c
+++ b/fast-import.c
@@ -423,7 +423,7 @@ static void write_crash_report(const char *err)
fprintf(rpt, "fast-import crash report:\n");
fprintf(rpt, " fast-import process: %"PRIuMAX"\n", (uintmax_t) getpid());
fprintf(rpt, " parent process : %"PRIuMAX"\n", (uintmax_t) getppid());
- fprintf(rpt, " at %s\n", show_date(time(NULL), 0, DATE_LOCAL));
+ fprintf(rpt, " at %s\n", show_date(time(NULL), 0, DATE_MODE(LOCAL)));
fputc('\n', rpt);
fputs("fatal: ", rpt);
diff --git a/gettext.c b/gettext.c
index 7378ba287f..a268a2c52c 100644
--- a/gettext.c
+++ b/gettext.c
@@ -162,6 +162,7 @@ void git_setup_gettext(void)
podir = GIT_LOCALE_PATH;
bindtextdomain("git", podir);
setlocale(LC_MESSAGES, "");
+ setlocale(LC_TIME, "");
init_gettext_charset("git");
textdomain("git");
}
diff --git a/http-backend.c b/http-backend.c
index 501bf797c0..b977c006a4 100644
--- a/http-backend.c
+++ b/http-backend.c
@@ -92,7 +92,7 @@ static void hdr_int(const char *name, uintmax_t value)
static void hdr_date(const char *name, unsigned long when)
{
- const char *value = show_date(when, 0, DATE_RFC2822);
+ const char *value = show_date(when, 0, DATE_MODE(RFC2822));
hdr_str(name, value);
}
diff --git a/log-tree.c b/log-tree.c
index e2f6de73ef..7b1b57aaf6 100644
--- a/log-tree.c
+++ b/log-tree.c
@@ -640,7 +640,7 @@ void show_log(struct rev_info *opt)
*/
show_reflog_message(opt->reflog_info,
opt->commit_format == CMIT_FMT_ONELINE,
- opt->date_mode,
+ &opt->date_mode,
opt->date_mode_explicit);
if (opt->commit_format == CMIT_FMT_ONELINE)
return;
diff --git a/pretty.c b/pretty.c
index 7b49304181..151c2ae312 100644
--- a/pretty.c
+++ b/pretty.c
@@ -399,7 +399,7 @@ static void add_rfc2047(struct strbuf *sb, const char *line, size_t len,
}
const char *show_ident_date(const struct ident_split *ident,
- enum date_mode mode)
+ const struct date_mode *mode)
{
unsigned long date = 0;
long tz = 0;
@@ -489,15 +489,15 @@ void pp_user_info(struct pretty_print_context *pp,
switch (pp->fmt) {
case CMIT_FMT_MEDIUM:
strbuf_addf(sb, "Date: %s\n",
- show_ident_date(&ident, pp->date_mode));
+ show_ident_date(&ident, &pp->date_mode));
break;
case CMIT_FMT_EMAIL:
strbuf_addf(sb, "Date: %s\n",
- show_ident_date(&ident, DATE_RFC2822));
+ show_ident_date(&ident, DATE_MODE(RFC2822)));
break;
case CMIT_FMT_FULLER:
strbuf_addf(sb, "%sDate: %s\n", what,
- show_ident_date(&ident, pp->date_mode));
+ show_ident_date(&ident, &pp->date_mode));
break;
default:
/* notin' */
@@ -671,7 +671,8 @@ static int mailmap_name(const char **email, size_t *email_len,
}
static size_t format_person_part(struct strbuf *sb, char part,
- const char *msg, int len, enum date_mode dmode)
+ const char *msg, int len,
+ const struct date_mode *dmode)
{
/* currently all placeholders have same length */
const int placeholder_len = 2;
@@ -711,16 +712,16 @@ static size_t format_person_part(struct strbuf *sb, char part,
strbuf_addstr(sb, show_ident_date(&s, dmode));
return placeholder_len;
case 'D': /* date, RFC2822 style */
- strbuf_addstr(sb, show_ident_date(&s, DATE_RFC2822));
+ strbuf_addstr(sb, show_ident_date(&s, DATE_MODE(RFC2822)));
return placeholder_len;
case 'r': /* date, relative */
- strbuf_addstr(sb, show_ident_date(&s, DATE_RELATIVE));
+ strbuf_addstr(sb, show_ident_date(&s, DATE_MODE(RELATIVE)));
return placeholder_len;
case 'i': /* date, ISO 8601-like */
- strbuf_addstr(sb, show_ident_date(&s, DATE_ISO8601));
+ strbuf_addstr(sb, show_ident_date(&s, DATE_MODE(ISO8601)));
return placeholder_len;
case 'I': /* date, ISO 8601 strict */
- strbuf_addstr(sb, show_ident_date(&s, DATE_ISO8601_STRICT));
+ strbuf_addstr(sb, show_ident_date(&s, DATE_MODE(ISO8601_STRICT)));
return placeholder_len;
}
@@ -933,7 +934,7 @@ static void rewrap_message_tail(struct strbuf *sb,
static int format_reflog_person(struct strbuf *sb,
char part,
struct reflog_walk_info *log,
- enum date_mode dmode)
+ const struct date_mode *dmode)
{
const char *ident;
@@ -1185,7 +1186,7 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */
if (c->pretty_ctx->reflog_info)
get_reflog_selector(sb,
c->pretty_ctx->reflog_info,
- c->pretty_ctx->date_mode,
+ &c->pretty_ctx->date_mode,
c->pretty_ctx->date_mode_explicit,
(placeholder[1] == 'd'));
return 2;
@@ -1200,7 +1201,7 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */
return format_reflog_person(sb,
placeholder[1],
c->pretty_ctx->reflog_info,
- c->pretty_ctx->date_mode);
+ &c->pretty_ctx->date_mode);
}
return 0; /* unknown %g placeholder */
case 'N':
@@ -1251,11 +1252,11 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */
case 'a': /* author ... */
return format_person_part(sb, placeholder[1],
msg + c->author.off, c->author.len,
- c->pretty_ctx->date_mode);
+ &c->pretty_ctx->date_mode);
case 'c': /* committer ... */
return format_person_part(sb, placeholder[1],
msg + c->committer.off, c->committer.len,
- c->pretty_ctx->date_mode);
+ &c->pretty_ctx->date_mode);
case 'e': /* encoding */
if (c->commit_encoding)
strbuf_addstr(sb, c->commit_encoding);
diff --git a/ref-filter.c b/ref-filter.c
index 6e09da88ca..f38dee4f60 100644
--- a/ref-filter.c
+++ b/ref-filter.c
@@ -362,7 +362,7 @@ static void grab_date(const char *buf, struct atom_value *v, const char *atomnam
char *zone;
unsigned long timestamp;
long tz;
- enum date_mode date_mode = DATE_NORMAL;
+ struct date_mode date_mode = { DATE_NORMAL };
const char *formatp;
/*
@@ -374,7 +374,7 @@ static void grab_date(const char *buf, struct atom_value *v, const char *atomnam
formatp = strchr(atomname, ':');
if (formatp != NULL) {
formatp++;
- date_mode = parse_date_format(formatp);
+ parse_date_format(formatp, &date_mode);
}
if (!eoemail)
@@ -385,7 +385,7 @@ static void grab_date(const char *buf, struct atom_value *v, const char *atomnam
tz = strtol(zone, NULL, 10);
if ((tz == LONG_MIN || tz == LONG_MAX) && errno == ERANGE)
goto bad;
- v->s = xstrdup(show_date(timestamp, tz, date_mode));
+ v->s = xstrdup(show_date(timestamp, tz, &date_mode));
v->ul = timestamp;
return;
bad:
diff --git a/reflog-walk.c b/reflog-walk.c
index 222de762eb..f8e743a23b 100644
--- a/reflog-walk.c
+++ b/reflog-walk.c
@@ -249,7 +249,7 @@ void fake_reflog_parent(struct reflog_walk_info *info, struct commit *commit)
void get_reflog_selector(struct strbuf *sb,
struct reflog_walk_info *reflog_info,
- enum date_mode dmode, int force_date,
+ const struct date_mode *dmode, int force_date,
int shorten)
{
struct commit_reflog *commit_reflog = reflog_info->last_commit_reflog;
@@ -311,7 +311,7 @@ const char *get_reflog_ident(struct reflog_walk_info *reflog_info)
}
void show_reflog_message(struct reflog_walk_info *reflog_info, int oneline,
- enum date_mode dmode, int force_date)
+ const struct date_mode *dmode, int force_date)
{
if (reflog_info && reflog_info->last_commit_reflog) {
struct commit_reflog *commit_reflog = reflog_info->last_commit_reflog;
diff --git a/reflog-walk.h b/reflog-walk.h
index a9bd60e32d..27886f793e 100644
--- a/reflog-walk.h
+++ b/reflog-walk.h
@@ -11,13 +11,13 @@ extern int add_reflog_for_walk(struct reflog_walk_info *info,
extern void fake_reflog_parent(struct reflog_walk_info *info,
struct commit *commit);
extern void show_reflog_message(struct reflog_walk_info *info, int,
- enum date_mode, int force_date);
+ const struct date_mode *, int force_date);
extern void get_reflog_message(struct strbuf *sb,
struct reflog_walk_info *reflog_info);
extern const char *get_reflog_ident(struct reflog_walk_info *reflog_info);
extern void get_reflog_selector(struct strbuf *sb,
struct reflog_walk_info *reflog_info,
- enum date_mode dmode, int force_date,
+ const struct date_mode *dmode, int force_date,
int shorten);
#endif
diff --git a/refs.c b/refs.c
index fb568d7a31..ce8cd8d450 100644
--- a/refs.c
+++ b/refs.c
@@ -3436,14 +3436,14 @@ static int read_ref_at_ent(unsigned char *osha1, unsigned char *nsha1,
hashcpy(cb->sha1, nsha1);
if (hashcmp(cb->osha1, nsha1))
warning("Log for ref %s has gap after %s.",
- cb->refname, show_date(cb->date, cb->tz, DATE_RFC2822));
+ cb->refname, show_date(cb->date, cb->tz, DATE_MODE(RFC2822)));
}
else if (cb->date == cb->at_time)
hashcpy(cb->sha1, nsha1);
else if (hashcmp(nsha1, cb->sha1))
warning("Log for ref %s unexpectedly ended on %s.",
cb->refname, show_date(cb->date, cb->tz,
- DATE_RFC2822));
+ DATE_MODE(RFC2822)));
hashcpy(cb->osha1, osha1);
hashcpy(cb->nsha1, nsha1);
cb->found_it = 1;
diff --git a/revision.c b/revision.c
index ab97ffd459..cf60c5d817 100644
--- a/revision.c
+++ b/revision.c
@@ -1996,10 +1996,10 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
} else if (!strcmp(arg, "--full-history")) {
revs->simplify_history = 0;
} else if (!strcmp(arg, "--relative-date")) {
- revs->date_mode = DATE_RELATIVE;
+ revs->date_mode.type = DATE_RELATIVE;
revs->date_mode_explicit = 1;
} else if ((argcount = parse_long_opt("date", argv, &optarg))) {
- revs->date_mode = parse_date_format(optarg);
+ parse_date_format(optarg, &revs->date_mode);
revs->date_mode_explicit = 1;
return argcount;
} else if (!strcmp(arg, "--log-size")) {
diff --git a/revision.h b/revision.h
index 0ea8b4e255..5bc9686846 100644
--- a/revision.h
+++ b/revision.h
@@ -146,7 +146,7 @@ struct rev_info {
track_first_time:1,
linear:1;
- enum date_mode date_mode;
+ struct date_mode date_mode;
unsigned int abbrev;
enum cmit_fmt commit_format;
diff --git a/sha1_name.c b/sha1_name.c
index e57513e610..da6874c15e 100644
--- a/sha1_name.c
+++ b/sha1_name.c
@@ -576,7 +576,7 @@ static int get_sha1_basic(const char *str, int len, unsigned char *sha1,
if (!(flags & GET_SHA1_QUIETLY)) {
warning("Log for '%.*s' only goes "
"back to %s.", len, str,
- show_date(co_time, co_tz, DATE_RFC2822));
+ show_date(co_time, co_tz, DATE_MODE(RFC2822)));
}
} else {
if (flags & GET_SHA1_QUIETLY) {
diff --git a/strbuf.c b/strbuf.c
index bbaf32eef6..cce5eed14a 100644
--- a/strbuf.c
+++ b/strbuf.c
@@ -710,3 +710,36 @@ char *xstrfmt(const char *fmt, ...)
return ret;
}
+
+void strbuf_addftime(struct strbuf *sb, const char *fmt, const struct tm *tm)
+{
+ size_t hint = 128;
+ size_t len;
+
+ if (!*fmt)
+ return;
+
+ strbuf_grow(sb, hint);
+ len = strftime(sb->buf + sb->len, sb->alloc - sb->len, fmt, tm);
+
+ if (!len) {
+ /*
+ * strftime reports "0" if it could not fit the result in the buffer.
+ * Unfortunately, it also reports "0" if the requested time string
+ * takes 0 bytes. So our strategy is to munge the format so that the
+ * output contains at least one character, and then drop the extra
+ * character before returning.
+ */
+ struct strbuf munged_fmt = STRBUF_INIT;
+ strbuf_addf(&munged_fmt, "%s ", fmt);
+ while (!len) {
+ hint *= 2;
+ strbuf_grow(sb, hint);
+ len = strftime(sb->buf + sb->len, sb->alloc - sb->len,
+ munged_fmt.buf, tm);
+ }
+ strbuf_release(&munged_fmt);
+ len--; /* drop munged space */
+ }
+ strbuf_setlen(sb, sb->len + len);
+}
diff --git a/strbuf.h b/strbuf.h
index 4a48c0031a..aef2794651 100644
--- a/strbuf.h
+++ b/strbuf.h
@@ -345,6 +345,11 @@ __attribute__((format (printf,2,0)))
extern void strbuf_vaddf(struct strbuf *sb, const char *fmt, va_list ap);
/**
+ * Add the time specified by `tm`, as formatted by `strftime`.
+ */
+extern void strbuf_addftime(struct strbuf *sb, const char *fmt, const struct tm *tm);
+
+/**
* Read a given size of data from a FILE* pointer to the buffer.
*
* NOTE: The buffer is rewound if the read fails. If -1 is returned,
diff --git a/submodule.c b/submodule.c
index 15e90d1c10..700bbf4fcb 100644
--- a/submodule.c
+++ b/submodule.c
@@ -993,7 +993,7 @@ static void print_commit(struct commit *commit)
{
struct strbuf sb = STRBUF_INIT;
struct pretty_print_context ctx = {0};
- ctx.date_mode = DATE_NORMAL;
+ ctx.date_mode.type = DATE_NORMAL;
format_commit_message(commit, " %h: %m %s", &sb, &ctx);
fprintf(stderr, "%s\n", sb.buf);
strbuf_release(&sb);
diff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh
index 24fc2ba55d..7c9bec7630 100755
--- a/t/t6300-for-each-ref.sh
+++ b/t/t6300-for-each-ref.sh
@@ -227,6 +227,24 @@ test_expect_success 'Check format "rfc2822" date fields output' '
test_cmp expected actual
'
+test_expect_success 'Check format of strftime date fields' '
+ echo "my date is 2006-07-03" >expected &&
+ git for-each-ref \
+ --format="%(authordate:format:my date is %Y-%m-%d)" \
+ refs/heads >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'exercise strftime with odd fields' '
+ echo >expected &&
+ git for-each-ref --format="%(authordate:format:)" refs/heads >actual &&
+ test_cmp expected actual &&
+ long="long format -- $_z40$_z40$_z40$_z40$_z40$_z40$_z40" &&
+ echo $long >expected &&
+ git for-each-ref --format="%(authordate:format:$long)" refs/heads >actual &&
+ test_cmp expected actual
+'
+
cat >expected <<\EOF
refs/heads/master
refs/remotes/origin/master
diff --git a/test-date.c b/test-date.c
index 94a6997a8f..63f373557e 100644
--- a/test-date.c
+++ b/test-date.c
@@ -29,7 +29,7 @@ static void parse_dates(char **argv, struct timeval *now)
parse_date(*argv, &result);
if (sscanf(result.buf, "%lu %d", &t, &tz) == 2)
printf("%s -> %s\n",
- *argv, show_date(t, tz, DATE_ISO8601));
+ *argv, show_date(t, tz, DATE_MODE(ISO8601)));
else
printf("%s -> bad\n", *argv);
}
@@ -41,7 +41,7 @@ static void parse_approxidate(char **argv, struct timeval *now)
for (; *argv; argv++) {
time_t t;
t = approxidate_relative(*argv, now);
- printf("%s -> %s\n", *argv, show_date(t, 0, DATE_ISO8601));
+ printf("%s -> %s\n", *argv, show_date(t, 0, DATE_MODE(ISO8601)));
}
}
diff --git a/test-revision-walking.c b/test-revision-walking.c
index 3ade02c72d..285f06b7ff 100644
--- a/test-revision-walking.c
+++ b/test-revision-walking.c
@@ -17,7 +17,7 @@ static void print_commit(struct commit *commit)
{
struct strbuf sb = STRBUF_INIT;
struct pretty_print_context ctx = {0};
- ctx.date_mode = DATE_NORMAL;
+ ctx.date_mode.type = DATE_NORMAL;
format_commit_message(commit, " %m %s", &sb, &ctx);
printf("%s\n", sb.buf);
strbuf_release(&sb);