Skip to content

Commit

Permalink
ref-filter.c: sort formatted dates by byte value
Browse files Browse the repository at this point in the history
Update the handling of date fields in the ref sorting functions of
'ref-filter.c' so that date atoms specified with a format string (such as in
'git for-each-ref --sort=creatordate:<something>') are sorted by the
formatted string value rather than by the numeric timestamp. Currently, date
fields are always sorted by timestamp, regardless of whether formatting
information is included in the '--sort' key.

Leaving the default (unformatted) date sorting unchanged, sorting by the
formatted date string adds some flexibility to 'for-each-ref' by allowing
for behavior like "sort by year, then by refname within each year" or "sort
by time of day". Because the inclusion of a format string previously had no
effect on sort behavior, this change likely will not affect existing usage
of 'for-each-ref' and other ref listing commands.

Additionally, update documentation & tests to document the new sorting
mechanism.

Signed-off-by: Victoria Dye <[email protected]>
  • Loading branch information
vdye committed Feb 7, 2024
1 parent 235986b commit 563b254
Show file tree
Hide file tree
Showing 3 changed files with 72 additions and 3 deletions.
8 changes: 5 additions & 3 deletions Documentation/git-for-each-ref.txt
Original file line number Diff line number Diff line change
Expand Up @@ -359,9 +359,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
returns an empty string instead.

As a special case for the date-type fields, you may specify a format for
the date by adding `:` followed by date format name (see the
values the `--date` option to linkgit:git-rev-list[1] takes).
As a special case for the date-type fields, you may specify a format for the
date by adding `:` followed by date format name (see the values the `--date`
option to linkgit:git-rev-list[1] takes). If this formatting is provided in
a `--sort` key, references will be sorted according to the byte-value of the
formatted string rather than the numeric value of the underlying timestamp.

Some atoms like %(align) and %(if) always require a matching %(end).
We call them "opening atoms" and sometimes denote them as %($open).
Expand Down
6 changes: 6 additions & 0 deletions ref-filter.c
Original file line number Diff line number Diff line change
Expand Up @@ -1611,6 +1611,12 @@ static void grab_date(const char *buf, struct atom_value *v, const char *atomnam
if (formatp) {
formatp++;
parse_date_format(formatp, &date_mode);

/*
* If this is a sort field and a format was specified, we'll
* want to compare formatted date by string value.
*/
v->atom->type = FIELD_STR;
}

if (!eoemail)
Expand Down
61 changes: 61 additions & 0 deletions t/t6300-for-each-ref.sh
Original file line number Diff line number Diff line change
Expand Up @@ -1356,6 +1356,67 @@ test_expect_success '--no-sort without subsequent --sort prints expected refs' '
test_cmp expected actual
'

test_expect_success 'set up custom date sorting' '
# Dates:
# - Wed Feb 07 2024 21:34:20 +0000
# - Tue Dec 14 1999 00:05:22 +0000
# - Fri Jun 04 2021 11:26:51 +0000
# - Mon Jan 22 2007 16:44:01 GMT+0000
i=1 &&
for when in 1707341660 945129922 1622806011 1169484241
do
GIT_COMMITTER_DATE="@$when +0000" \
GIT_COMMITTER_EMAIL="[email protected]" \
git tag -m "tag $when" custom-dates-$i &&
i=$(($i+1)) || return 1
done
'

test_expect_success 'sort by date defaults to full timestamp' '
cat >expected <<-\EOF &&
945129922 refs/tags/custom-dates-2
1169484241 refs/tags/custom-dates-4
1622806011 refs/tags/custom-dates-3
1707341660 refs/tags/custom-dates-1
EOF
git for-each-ref \
--format="%(creatordate:unix) %(refname)" \
--sort=creatordate \
"refs/tags/custom-dates-*" >actual &&
test_cmp expected actual
'

test_expect_success 'sort by time only (no date)' '
cat >expected <<-\EOF &&
00:05:22 refs/tags/custom-dates-2
11:26:51 refs/tags/custom-dates-3
16:44:01 refs/tags/custom-dates-4
21:34:20 refs/tags/custom-dates-1
EOF
git for-each-ref \
--format="%(creatordate:format:%H:%M:%S) %(refname)" \
--sort="creatordate:format:%H:%M:%S" \
"refs/tags/custom-dates-*" >actual &&
test_cmp expected actual
'

test_expect_success 'sort by month name' '
cat >expected <<-\EOF &&
December refs/tags/custom-dates-2
February refs/tags/custom-dates-1
January refs/tags/custom-dates-4
June refs/tags/custom-dates-3
EOF
git for-each-ref \
--format="%(creatordate:format:%B) %(refname)" \
--sort="creatordate:format:%B" \
"refs/tags/custom-dates-*" >actual &&
test_cmp expected actual
'

test_expect_success 'do not dereference NULL upon %(HEAD) on unborn branch' '
test_when_finished "git checkout main" &&
git for-each-ref --format="%(HEAD) %(refname:short)" refs/heads/ >actual &&
Expand Down

0 comments on commit 563b254

Please sign in to comment.