Merge branch 'ap/dateformat'

* ap/dateformat:
  Add a test script for for-each-ref, including test of date formatting
  dateformat: parse %(xxdate) %(yydate:format) correctly
  Make for-each-ref's grab_date() support per-atom formatting
  Make for-each-ref allow atom names like "<name>:<something>"
  parse_date_format(): convert a format name to an enum date_mode
This commit is contained in:
Junio C Hamano 2007-10-03 03:03:22 -07:00
commit 5e5b2b98c8
6 changed files with 209 additions and 24 deletions

View File

@ -100,6 +100,11 @@ In any case, a field name that refers to a field inapplicable to
the object referred by the ref does not cause an error. It the object referred by the ref does not cause an error. It
returns an empty string instead. returns an empty string instead.
As a special case for the date-type fields, you may specify a format for
the date by adding one of `:default`, `:relative`, `:short`, `:local`,
`:iso8601` or `:rfc2822` to the end of the fieldname; e.g.
`%(taggerdate:relative)`.
EXAMPLES EXAMPLES
-------- --------

View File

@ -106,7 +106,16 @@ static int parse_atom(const char *atom, const char *ep)
/* Is the atom a valid one? */ /* Is the atom a valid one? */
for (i = 0; i < ARRAY_SIZE(valid_atom); i++) { for (i = 0; i < ARRAY_SIZE(valid_atom); i++) {
int len = strlen(valid_atom[i].name); int len = strlen(valid_atom[i].name);
if (len == ep - sp && !memcmp(valid_atom[i].name, sp, len)) /*
* If the atom name has a colon, strip it and everything after
* it off - it specifies the format for this entry, and
* shouldn't be used for checking against the valid_atom
* table.
*/
const char *formatp = strchr(sp, ':');
if (!formatp || ep < formatp)
formatp = ep;
if (len == formatp - sp && !memcmp(valid_atom[i].name, sp, len))
break; break;
} }
@ -349,12 +358,26 @@ static const char *copy_email(const char *buf)
return line; return line;
} }
static void grab_date(const char *buf, struct atom_value *v) static void grab_date(const char *buf, struct atom_value *v, const char *atomname)
{ {
const char *eoemail = strstr(buf, "> "); const char *eoemail = strstr(buf, "> ");
char *zone; char *zone;
unsigned long timestamp; unsigned long timestamp;
long tz; long tz;
enum date_mode date_mode = DATE_NORMAL;
const char *formatp;
/*
* We got here because atomname ends in "date" or "date<something>";
* it's not possible that <something> is not ":<format>" because
* parse_atom() wouldn't have allowed it, so we can assume that no
* ":" means no format is specified, and use the default.
*/
formatp = strchr(atomname, ':');
if (formatp != NULL) {
formatp++;
date_mode = parse_date_format(formatp);
}
if (!eoemail) if (!eoemail)
goto bad; goto bad;
@ -364,7 +387,7 @@ static void grab_date(const char *buf, struct atom_value *v)
tz = strtol(zone, NULL, 10); tz = strtol(zone, NULL, 10);
if ((tz == LONG_MIN || tz == LONG_MAX) && errno == ERANGE) if ((tz == LONG_MIN || tz == LONG_MAX) && errno == ERANGE)
goto bad; goto bad;
v->s = xstrdup(show_date(timestamp, tz, 0)); v->s = xstrdup(show_date(timestamp, tz, date_mode));
v->ul = timestamp; v->ul = timestamp;
return; return;
bad: bad:
@ -391,7 +414,7 @@ static void grab_person(const char *who, struct atom_value *val, int deref, stru
if (name[wholen] != 0 && if (name[wholen] != 0 &&
strcmp(name + wholen, "name") && strcmp(name + wholen, "name") &&
strcmp(name + wholen, "email") && strcmp(name + wholen, "email") &&
strcmp(name + wholen, "date")) prefixcmp(name + wholen, "date"))
continue; continue;
if (!wholine) if (!wholine)
wholine = find_wholine(who, wholen, buf, sz); wholine = find_wholine(who, wholen, buf, sz);
@ -403,8 +426,8 @@ static void grab_person(const char *who, struct atom_value *val, int deref, stru
v->s = copy_name(wholine); v->s = copy_name(wholine);
else if (!strcmp(name + wholen, "email")) else if (!strcmp(name + wholen, "email"))
v->s = copy_email(wholine); v->s = copy_email(wholine);
else if (!strcmp(name + wholen, "date")) else if (!prefixcmp(name + wholen, "date"))
grab_date(wholine, v); grab_date(wholine, v, name);
} }
/* For a tag or a commit object, if "creator" or "creatordate" is /* For a tag or a commit object, if "creator" or "creatordate" is
@ -424,8 +447,8 @@ static void grab_person(const char *who, struct atom_value *val, int deref, stru
if (deref) if (deref)
name++; name++;
if (!strcmp(name, "creatordate")) if (!prefixcmp(name, "creatordate"))
grab_date(wholine, v); grab_date(wholine, v, name);
else if (!strcmp(name, "creator")) else if (!strcmp(name, "creator"))
v->s = copy_line(wholine); v->s = copy_line(wholine);
} }

View File

@ -432,6 +432,7 @@ const char *show_date(unsigned long time, int timezone, enum date_mode mode);
int parse_date(const char *date, char *buf, int bufsize); int parse_date(const char *date, char *buf, int bufsize);
void datestamp(char *buf, int bufsize); void datestamp(char *buf, int bufsize);
unsigned long approxidate(const char *); unsigned long approxidate(const char *);
enum date_mode parse_date_format(const char *format);
extern const char *git_author_info(int); extern const char *git_author_info(int);
extern const char *git_committer_info(int); extern const char *git_committer_info(int);

20
date.c
View File

@ -584,6 +584,26 @@ int parse_date(const char *date, char *result, int maxlen)
return date_string(then, offset, result, maxlen); return date_string(then, offset, result, maxlen);
} }
enum date_mode parse_date_format(const char *format)
{
if (!strcmp(format, "relative"))
return DATE_RELATIVE;
else if (!strcmp(format, "iso8601") ||
!strcmp(format, "iso"))
return DATE_ISO8601;
else if (!strcmp(format, "rfc2822") ||
!strcmp(format, "rfc"))
return DATE_RFC2822;
else if (!strcmp(format, "short"))
return DATE_SHORT;
else if (!strcmp(format, "local"))
return DATE_LOCAL;
else if (!strcmp(format, "default"))
return DATE_NORMAL;
else
die("unknown date format %s", format);
}
void datestamp(char *buf, int bufsize) void datestamp(char *buf, int bufsize)
{ {
time_t now; time_t now;

View File

@ -1134,22 +1134,7 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
continue; continue;
} }
if (!strncmp(arg, "--date=", 7)) { if (!strncmp(arg, "--date=", 7)) {
if (!strcmp(arg + 7, "relative")) revs->date_mode = parse_date_format(arg + 7);
revs->date_mode = DATE_RELATIVE;
else if (!strcmp(arg + 7, "iso8601") ||
!strcmp(arg + 7, "iso"))
revs->date_mode = DATE_ISO8601;
else if (!strcmp(arg + 7, "rfc2822") ||
!strcmp(arg + 7, "rfc"))
revs->date_mode = DATE_RFC2822;
else if (!strcmp(arg + 7, "short"))
revs->date_mode = DATE_SHORT;
else if (!strcmp(arg + 7, "local"))
revs->date_mode = DATE_LOCAL;
else if (!strcmp(arg + 7, "default"))
revs->date_mode = DATE_NORMAL;
else
die("unknown date format %s", arg);
continue; continue;
} }
if (!strcmp(arg, "--log-size")) { if (!strcmp(arg, "--log-size")) {

151
t/t6300-for-each-ref.sh Normal file
View File

@ -0,0 +1,151 @@
#!/bin/sh
#
# Copyright (c) 2007 Andy Parkins
#
test_description='for-each-ref test'
. ./test-lib.sh
# Mon Jul 3 15:18:43 2006 +0000
datestamp=1151939923
setdate_and_increment () {
GIT_COMMITTER_DATE="$datestamp +0200"
datestamp=$(expr "$datestamp" + 1)
GIT_AUTHOR_DATE="$datestamp +0200"
datestamp=$(expr "$datestamp" + 1)
export GIT_COMMITTER_DATE GIT_AUTHOR_DATE
}
test_expect_success 'Create sample commit with known timestamp' '
setdate_and_increment &&
echo "Using $datestamp" > one &&
git add one &&
git commit -m "Initial" &&
setdate_and_increment &&
git tag -a -m "Tagging at $datestamp" testtag
'
test_expect_success 'Check atom names are valid' '
bad=
for token in \
refname objecttype objectsize objectname tree parent \
numparent object type author authorname authoremail \
authordate committer committername committeremail \
committerdate tag tagger taggername taggeremail \
taggerdate creator creatordate subject body contents
do
git for-each-ref --format="$token=%($token)" refs/heads || {
bad=$token
break
}
done
test -z "$bad"
'
test_expect_failure 'Check invalid atoms names are errors' '
git-for-each-ref --format="%(INVALID)" refs/heads
'
test_expect_success 'Check format specifiers are ignored in naming date atoms' '
git-for-each-ref --format="%(authordate)" refs/heads &&
git-for-each-ref --format="%(authordate:default) %(authordate)" refs/heads &&
git-for-each-ref --format="%(authordate) %(authordate:default)" refs/heads &&
git-for-each-ref --format="%(authordate:default) %(authordate:default)" refs/heads
'
test_expect_success 'Check valid format specifiers for date fields' '
git-for-each-ref --format="%(authordate:default)" refs/heads &&
git-for-each-ref --format="%(authordate:relative)" refs/heads &&
git-for-each-ref --format="%(authordate:short)" refs/heads &&
git-for-each-ref --format="%(authordate:local)" refs/heads &&
git-for-each-ref --format="%(authordate:iso8601)" refs/heads &&
git-for-each-ref --format="%(authordate:rfc2822)" refs/heads
'
test_expect_failure 'Check invalid format specifiers are errors' '
git-for-each-ref --format="%(authordate:INVALID)" refs/heads
'
cat >expected <<\EOF
'refs/heads/master' 'Mon Jul 3 17:18:43 2006 +0200' 'Mon Jul 3 17:18:44 2006 +0200'
'refs/tags/testtag' 'Mon Jul 3 17:18:45 2006 +0200'
EOF
test_expect_success 'Check unformatted date fields output' '
(git for-each-ref --shell --format="%(refname) %(committerdate) %(authordate)" refs/heads &&
git for-each-ref --shell --format="%(refname) %(taggerdate)" refs/tags) >actual &&
git diff expected actual
'
test_expect_success 'Check format "default" formatted date fields output' '
f=default &&
(git for-each-ref --shell --format="%(refname) %(committerdate:$f) %(authordate:$f)" refs/heads &&
git for-each-ref --shell --format="%(refname) %(taggerdate:$f)" refs/tags) >actual &&
git diff expected actual
'
# Don't know how to do relative check because I can't know when this script
# is going to be run and can't fake the current time to git, and hence can't
# provide expected output. Instead, I'll just make sure that "relative"
# doesn't exit in error
#
#cat >expected <<\EOF
#
#EOF
#
test_expect_success 'Check format "relative" date fields output' '
f=relative &&
(git for-each-ref --shell --format="%(refname) %(committerdate:$f) %(authordate:$f)" refs/heads &&
git for-each-ref --shell --format="%(refname) %(taggerdate:$f)" refs/tags) >actual
'
cat >expected <<\EOF
'refs/heads/master' '2006-07-03' '2006-07-03'
'refs/tags/testtag' '2006-07-03'
EOF
test_expect_success 'Check format "short" date fields output' '
f=short &&
(git for-each-ref --shell --format="%(refname) %(committerdate:$f) %(authordate:$f)" refs/heads &&
git for-each-ref --shell --format="%(refname) %(taggerdate:$f)" refs/tags) >actual &&
git diff expected actual
'
cat >expected <<\EOF
'refs/heads/master' 'Mon Jul 3 15:18:43 2006' 'Mon Jul 3 15:18:44 2006'
'refs/tags/testtag' 'Mon Jul 3 15:18:45 2006'
EOF
test_expect_success 'Check format "local" date fields output' '
f=local &&
(git for-each-ref --shell --format="%(refname) %(committerdate:$f) %(authordate:$f)" refs/heads &&
git for-each-ref --shell --format="%(refname) %(taggerdate:$f)" refs/tags) >actual &&
git diff expected actual
'
cat >expected <<\EOF
'refs/heads/master' '2006-07-03 17:18:43 +0200' '2006-07-03 17:18:44 +0200'
'refs/tags/testtag' '2006-07-03 17:18:45 +0200'
EOF
test_expect_success 'Check format "iso8601" date fields output' '
f=iso8601 &&
(git for-each-ref --shell --format="%(refname) %(committerdate:$f) %(authordate:$f)" refs/heads &&
git for-each-ref --shell --format="%(refname) %(taggerdate:$f)" refs/tags) >actual &&
git diff expected actual
'
cat >expected <<\EOF
'refs/heads/master' 'Mon, 3 Jul 2006 17:18:43 +0200' 'Mon, 3 Jul 2006 17:18:44 +0200'
'refs/tags/testtag' 'Mon, 3 Jul 2006 17:18:45 +0200'
EOF
test_expect_success 'Check format "rfc2822" date fields output' '
f=rfc2822 &&
(git for-each-ref --shell --format="%(refname) %(committerdate:$f) %(authordate:$f)" refs/heads &&
git for-each-ref --shell --format="%(refname) %(taggerdate:$f)" refs/tags) >actual &&
git diff expected actual
'
test_done