Merge branch 'jk/for-each-ref-multi-key-sort-fix'

"git branch" and other "for-each-ref" variants accepted multiple
--sort=<key> options in the increasing order of precedence, but it
had a few breakages around "--ignore-case" handling, and tie-breaking
with the refname, which have been fixed.

* jk/for-each-ref-multi-key-sort-fix:
  ref-filter: apply fallback refname sort only after all user sorts
  ref-filter: apply --ignore-case to all sorting keys
This commit is contained in:
Junio C Hamano 2020-05-08 14:25:04 -07:00
commit 6de1630898
6 changed files with 104 additions and 11 deletions

View File

@ -737,7 +737,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
*/
if (!sorting)
sorting = ref_default_sorting();
sorting->ignore_case = icase;
ref_sorting_icase_all(sorting, icase);
print_ref_list(&filter, sorting, &format);
print_columns(&output, colopts, NULL);
string_list_clear(&output, 0);

View File

@ -70,7 +70,7 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix)
if (!sorting)
sorting = ref_default_sorting();
sorting->ignore_case = icase;
ref_sorting_icase_all(sorting, icase);
filter.ignore_case = icase;
filter.name_patterns = argv;

View File

@ -485,7 +485,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
}
if (!sorting)
sorting = ref_default_sorting();
sorting->ignore_case = icase;
ref_sorting_icase_all(sorting, icase);
filter.ignore_case = icase;
if (cmdmode == 'l') {
int ret;

View File

@ -2295,7 +2295,7 @@ static int cmp_ref_sorting(struct ref_sorting *s, struct ref_array_item *a, stru
if (va->value < vb->value)
cmp = -1;
else if (va->value == vb->value)
cmp = cmp_fn(a->refname, b->refname);
cmp = 0;
else
cmp = 1;
}
@ -2314,7 +2314,16 @@ static int compare_refs(const void *a_, const void *b_, void *ref_sorting)
if (cmp)
return cmp;
}
return 0;
s = ref_sorting;
return s && s->ignore_case ?
strcasecmp(a->refname, b->refname) :
strcmp(a->refname, b->refname);
}
void ref_sorting_icase_all(struct ref_sorting *sorting, int flag)
{
for (; sorting; sorting = sorting->next)
sorting->ignore_case = !!flag;
}
void ref_array_sort(struct ref_sorting *sorting, struct ref_array *array)

View File

@ -114,6 +114,8 @@ void ref_array_clear(struct ref_array *array);
int verify_ref_format(struct ref_format *format);
/* Sort the given ref_array as per the ref_sorting provided */
void ref_array_sort(struct ref_sorting *sort, struct ref_array *array);
/* Set the ignore_case flag for all elements of a sorting list */
void ref_sorting_icase_all(struct ref_sorting *sorting, int flag);
/* Based on the given format and quote_style, fill the strbuf */
int format_ref_array_item(struct ref_array_item *info,
const struct ref_format *format,

View File

@ -650,17 +650,59 @@ test_atom refs/tags/signed-long contents "subject line
body contents
$sig"
sort >expected <<EOF
$(git rev-parse refs/tags/bogo) <committer@example.com> refs/tags/bogo
$(git rev-parse refs/tags/master) <committer@example.com> refs/tags/master
EOF
test_expect_success 'set up multiple-sort tags' '
for when in 100000 200000
do
for email in user1 user2
do
for ref in ref1 ref2
do
GIT_COMMITTER_DATE="@$when +0000" \
GIT_COMMITTER_EMAIL="$email@example.com" \
git tag -m "tag $ref-$when-$email" \
multi-$ref-$when-$email || return 1
done
done
done
'
test_expect_success 'Verify sort with multiple keys' '
git for-each-ref --format="%(objectname) %(taggeremail) %(refname)" --sort=objectname --sort=taggeremail \
refs/tags/bogo refs/tags/master > actual &&
cat >expected <<-\EOF &&
100000 <user1@example.com> refs/tags/multi-ref2-100000-user1
100000 <user1@example.com> refs/tags/multi-ref1-100000-user1
100000 <user2@example.com> refs/tags/multi-ref2-100000-user2
100000 <user2@example.com> refs/tags/multi-ref1-100000-user2
200000 <user1@example.com> refs/tags/multi-ref2-200000-user1
200000 <user1@example.com> refs/tags/multi-ref1-200000-user1
200000 <user2@example.com> refs/tags/multi-ref2-200000-user2
200000 <user2@example.com> refs/tags/multi-ref1-200000-user2
EOF
git for-each-ref \
--format="%(taggerdate:unix) %(taggeremail) %(refname)" \
--sort=-refname \
--sort=taggeremail \
--sort=taggerdate \
"refs/tags/multi-*" >actual &&
test_cmp expected actual
'
test_expect_success 'equivalent sorts fall back on refname' '
cat >expected <<-\EOF &&
100000 <user1@example.com> refs/tags/multi-ref1-100000-user1
100000 <user2@example.com> refs/tags/multi-ref1-100000-user2
100000 <user1@example.com> refs/tags/multi-ref2-100000-user1
100000 <user2@example.com> refs/tags/multi-ref2-100000-user2
200000 <user1@example.com> refs/tags/multi-ref1-200000-user1
200000 <user2@example.com> refs/tags/multi-ref1-200000-user2
200000 <user1@example.com> refs/tags/multi-ref2-200000-user1
200000 <user2@example.com> refs/tags/multi-ref2-200000-user2
EOF
git for-each-ref \
--format="%(taggerdate:unix) %(taggeremail) %(refname)" \
--sort=taggerdate \
"refs/tags/multi-*" >actual &&
test_cmp expected actual
'
test_expect_success 'do not dereference NULL upon %(HEAD) on unborn branch' '
test_when_finished "git checkout master" &&
@ -895,4 +937,44 @@ test_expect_success 'for-each-ref --ignore-case ignores case' '
test_cmp expect actual
'
test_expect_success 'for-each-ref --ignore-case works on multiple sort keys' '
# name refs numerically to avoid case-insensitive filesystem conflicts
nr=0 &&
for email in a A b B
do
for subject in a A b B
do
GIT_COMMITTER_EMAIL="$email@example.com" \
git tag -m "tag $subject" icase-$(printf %02d $nr) &&
nr=$((nr+1))||
return 1
done
done &&
git for-each-ref --ignore-case \
--format="%(taggeremail) %(subject) %(refname)" \
--sort=refname \
--sort=subject \
--sort=taggeremail \
refs/tags/icase-* >actual &&
cat >expect <<-\EOF &&
<a@example.com> tag a refs/tags/icase-00
<a@example.com> tag A refs/tags/icase-01
<A@example.com> tag a refs/tags/icase-04
<A@example.com> tag A refs/tags/icase-05
<a@example.com> tag b refs/tags/icase-02
<a@example.com> tag B refs/tags/icase-03
<A@example.com> tag b refs/tags/icase-06
<A@example.com> tag B refs/tags/icase-07
<b@example.com> tag a refs/tags/icase-08
<b@example.com> tag A refs/tags/icase-09
<B@example.com> tag a refs/tags/icase-12
<B@example.com> tag A refs/tags/icase-13
<b@example.com> tag b refs/tags/icase-10
<b@example.com> tag B refs/tags/icase-11
<B@example.com> tag b refs/tags/icase-14
<B@example.com> tag B refs/tags/icase-15
EOF
test_cmp expect actual
'
test_done