Skip to content

Commit 73e494f

Browse files
committed
Merge branch 'nd/for-each-ref-ignore-case'
"git branch --list" and friends learned "--ignore-case" option to optionally sort branches and tags case insensitively. * nd/for-each-ref-ignore-case: tag, branch, for-each-ref: add --ignore-case for sorting and filtering
2 parents 0f30315 + 3bb16a8 commit 73e494f

File tree

10 files changed

+112
-17
lines changed

10 files changed

+112
-17
lines changed

Documentation/git-branch.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,10 @@ OPTIONS
118118
default to color output.
119119
Same as `--color=never`.
120120

121+
-i::
122+
--ignore-case::
123+
Sorting and filtering branches are case insensitive.
124+
121125
--column[=<options>]::
122126
--no-column::
123127
Display branch listing in columns. See configuration variable

Documentation/git-for-each-ref.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,9 @@ OPTIONS
7979
Only list refs which contain the specified commit (HEAD if not
8080
specified).
8181

82+
--ignore-case::
83+
Sorting and filtering refs are case insensitive.
84+
8285
FIELD NAMES
8386
-----------
8487

Documentation/git-tag.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,10 @@ OPTIONS
108108
variable if it exists, or lexicographic order otherwise. See
109109
linkgit:git-config[1].
110110

111+
-i::
112+
--ignore-case::
113+
Sorting and filtering tags are case insensitive.
114+
111115
--column[=<options>]::
112116
--no-column::
113117
Display tag listing in columns. See configuration variable

builtin/branch.c

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -512,15 +512,6 @@ static void print_ref_list(struct ref_filter *filter, struct ref_sorting *sortin
512512
if (filter->verbose)
513513
maxwidth = calc_maxwidth(&array, strlen(remote_prefix));
514514

515-
/*
516-
* If no sorting parameter is given then we default to sorting
517-
* by 'refname'. This would give us an alphabetically sorted
518-
* array with the 'HEAD' ref at the beginning followed by
519-
* local branches 'refs/heads/...' and finally remote-tacking
520-
* branches 'refs/remotes/...'.
521-
*/
522-
if (!sorting)
523-
sorting = ref_default_sorting();
524515
ref_array_sort(sorting, &array);
525516

526517
for (i = 0; i < array.nr; i++)
@@ -645,6 +636,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
645636
const char *new_upstream = NULL;
646637
enum branch_track track;
647638
struct ref_filter filter;
639+
int icase = 0;
648640
static struct ref_sorting *sorting = NULL, **sorting_tail = &sorting;
649641

650642
struct option options[] = {
@@ -686,6 +678,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
686678
OPTION_CALLBACK, 0, "points-at", &filter.points_at, N_("object"),
687679
N_("print only branches of the object"), 0, parse_opt_object_name
688680
},
681+
OPT_BOOL('i', "ignore-case", &icase, N_("sorting and filtering are case insensitive")),
689682
OPT_END(),
690683
};
691684

@@ -723,6 +716,8 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
723716

724717
if (filter.abbrev == -1)
725718
filter.abbrev = DEFAULT_ABBREV;
719+
filter.ignore_case = icase;
720+
726721
finalize_colopts(&colopts, -1);
727722
if (filter.verbose) {
728723
if (explicitly_enable_column(colopts))
@@ -744,6 +739,16 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
744739
if ((filter.kind & FILTER_REFS_BRANCHES) && filter.detached)
745740
filter.kind |= FILTER_REFS_DETACHED_HEAD;
746741
filter.name_patterns = argv;
742+
/*
743+
* If no sorting parameter is given then we default to sorting
744+
* by 'refname'. This would give us an alphabetically sorted
745+
* array with the 'HEAD' ref at the beginning followed by
746+
* local branches 'refs/heads/...' and finally remote-tacking
747+
* branches 'refs/remotes/...'.
748+
*/
749+
if (!sorting)
750+
sorting = ref_default_sorting();
751+
sorting->ignore_case = icase;
747752
print_ref_list(&filter, sorting);
748753
print_columns(&output, colopts, NULL);
749754
string_list_clear(&output, 0);

builtin/for-each-ref.c

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix)
1818
int i;
1919
const char *format = "%(objectname) %(objecttype)\t%(refname)";
2020
struct ref_sorting *sorting = NULL, **sorting_tail = &sorting;
21-
int maxcount = 0, quote_style = 0;
21+
int maxcount = 0, quote_style = 0, icase = 0;
2222
struct ref_array array;
2323
struct ref_filter filter;
2424

@@ -43,6 +43,7 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix)
4343
OPT_MERGED(&filter, N_("print only refs that are merged")),
4444
OPT_NO_MERGED(&filter, N_("print only refs that are not merged")),
4545
OPT_CONTAINS(&filter.with_commit, N_("print only refs which contain the commit")),
46+
OPT_BOOL(0, "ignore-case", &icase, N_("sorting and filtering are case insensitive")),
4647
OPT_END(),
4748
};
4849

@@ -63,6 +64,8 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix)
6364

6465
if (!sorting)
6566
sorting = ref_default_sorting();
67+
sorting->ignore_case = icase;
68+
filter.ignore_case = icase;
6669

6770
/* for warn_ambiguous_refs */
6871
git_config(git_default_config, NULL);

builtin/tag.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -335,6 +335,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
335335
struct ref_filter filter;
336336
static struct ref_sorting *sorting = NULL, **sorting_tail = &sorting;
337337
const char *format = NULL;
338+
int icase = 0;
338339
struct option options[] = {
339340
OPT_CMDMODE('l', "list", &cmdmode, N_("list tag names"), 'l'),
340341
{ OPTION_INTEGER, 'n', NULL, &filter.lines, N_("n"),
@@ -370,6 +371,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
370371
N_("print only tags of the object"), 0, parse_opt_object_name
371372
},
372373
OPT_STRING( 0 , "format", &format, N_("format"), N_("format to use for the output")),
374+
OPT_BOOL('i', "ignore-case", &icase, N_("sorting and filtering are case insensitive")),
373375
OPT_END()
374376
};
375377

@@ -401,6 +403,8 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
401403
}
402404
if (!sorting)
403405
sorting = ref_default_sorting();
406+
sorting->ignore_case = icase;
407+
filter.ignore_case = icase;
404408
if (cmdmode == 'l') {
405409
int ret;
406410
if (column_active(colopts)) {

ref-filter.c

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1231,8 +1231,14 @@ static int commit_contains(struct ref_filter *filter, struct commit *commit)
12311231
* matches a pattern "refs/heads/mas") or a wildcard (e.g. the same ref
12321232
* matches "refs/heads/mas*", too).
12331233
*/
1234-
static int match_pattern(const char **patterns, const char *refname)
1234+
static int match_pattern(const struct ref_filter *filter, const char *refname)
12351235
{
1236+
const char **patterns = filter->name_patterns;
1237+
unsigned flags = 0;
1238+
1239+
if (filter->ignore_case)
1240+
flags |= WM_CASEFOLD;
1241+
12361242
/*
12371243
* When no '--format' option is given we need to skip the prefix
12381244
* for matching refs of tags and branches.
@@ -1243,7 +1249,7 @@ static int match_pattern(const char **patterns, const char *refname)
12431249
skip_prefix(refname, "refs/", &refname));
12441250

12451251
for (; *patterns; patterns++) {
1246-
if (!wildmatch(*patterns, refname, 0, NULL))
1252+
if (!wildmatch(*patterns, refname, flags, NULL))
12471253
return 1;
12481254
}
12491255
return 0;
@@ -1255,9 +1261,15 @@ static int match_pattern(const char **patterns, const char *refname)
12551261
* matches a pattern "refs/heads/" but not "refs/heads/m") or a
12561262
* wildcard (e.g. the same ref matches "refs/heads/m*", too).
12571263
*/
1258-
static int match_name_as_path(const char **pattern, const char *refname)
1264+
static int match_name_as_path(const struct ref_filter *filter, const char *refname)
12591265
{
1266+
const char **pattern = filter->name_patterns;
12601267
int namelen = strlen(refname);
1268+
unsigned flags = WM_PATHNAME;
1269+
1270+
if (filter->ignore_case)
1271+
flags |= WM_CASEFOLD;
1272+
12611273
for (; *pattern; pattern++) {
12621274
const char *p = *pattern;
12631275
int plen = strlen(p);
@@ -1280,8 +1292,8 @@ static int filter_pattern_match(struct ref_filter *filter, const char *refname)
12801292
if (!*filter->name_patterns)
12811293
return 1; /* No pattern always matches */
12821294
if (filter->match_as_path)
1283-
return match_name_as_path(filter->name_patterns, refname);
1284-
return match_pattern(filter->name_patterns, refname);
1295+
return match_name_as_path(filter, refname);
1296+
return match_pattern(filter, refname);
12851297
}
12861298

12871299
/*
@@ -1536,18 +1548,20 @@ static int cmp_ref_sorting(struct ref_sorting *s, struct ref_array_item *a, stru
15361548
struct atom_value *va, *vb;
15371549
int cmp;
15381550
cmp_type cmp_type = used_atom[s->atom].type;
1551+
int (*cmp_fn)(const char *, const char *);
15391552

15401553
get_ref_atom_value(a, s->atom, &va);
15411554
get_ref_atom_value(b, s->atom, &vb);
1555+
cmp_fn = s->ignore_case ? strcasecmp : strcmp;
15421556
if (s->version)
15431557
cmp = versioncmp(va->s, vb->s);
15441558
else if (cmp_type == FIELD_STR)
1545-
cmp = strcmp(va->s, vb->s);
1559+
cmp = cmp_fn(va->s, vb->s);
15461560
else {
15471561
if (va->ul < vb->ul)
15481562
cmp = -1;
15491563
else if (va->ul == vb->ul)
1550-
cmp = strcmp(a->refname, b->refname);
1564+
cmp = cmp_fn(a->refname, b->refname);
15511565
else
15521566
cmp = 1;
15531567
}

ref-filter.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ struct ref_sorting {
2929
struct ref_sorting *next;
3030
int atom; /* index into used_atom array (internal) */
3131
unsigned reverse : 1,
32+
ignore_case : 1,
3233
version : 1;
3334
};
3435

@@ -62,6 +63,7 @@ struct ref_filter {
6263

6364
unsigned int with_commit_tag_algo : 1,
6465
match_as_path : 1,
66+
ignore_case : 1,
6567
detached : 1;
6668
unsigned int kind,
6769
lines;

t/t3203-branch-output.sh

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,11 @@ test_expect_success 'git branch --list -v pattern shows branch summaries' '
8989
awk "{print \$NF}" <tmp >actual &&
9090
test_cmp expect actual
9191
'
92+
test_expect_success 'git branch --ignore-case --list -v pattern shows branch summaries' '
93+
git branch --list --ignore-case -v BRANCH* >tmp &&
94+
awk "{print \$NF}" <tmp >actual &&
95+
test_cmp expect actual
96+
'
9297

9398
test_expect_success 'git branch -v pattern does not show branch summaries' '
9499
test_must_fail git branch -v branch*
@@ -196,4 +201,28 @@ test_expect_success 'local-branch symrefs shortened properly' '
196201
test_cmp expect actual
197202
'
198203

204+
test_expect_success 'sort branches, ignore case' '
205+
(
206+
git init sort-icase &&
207+
cd sort-icase &&
208+
test_commit initial &&
209+
git branch branch-one &&
210+
git branch BRANCH-two &&
211+
git branch --list | awk "{print \$NF}" >actual &&
212+
cat >expected <<-\EOF &&
213+
BRANCH-two
214+
branch-one
215+
master
216+
EOF
217+
test_cmp expected actual &&
218+
git branch --list -i | awk "{print \$NF}" >actual &&
219+
cat >expected <<-\EOF &&
220+
branch-one
221+
BRANCH-two
222+
master
223+
EOF
224+
test_cmp expected actual
225+
)
226+
'
227+
199228
test_done

t/t7004-tag.sh

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,30 @@ test_expect_success 'listing all tags in an empty tree should output nothing' '
2727
test $(git tag | wc -l) -eq 0
2828
'
2929

30+
test_expect_success 'sort tags, ignore case' '
31+
(
32+
git init sort &&
33+
cd sort &&
34+
test_commit initial &&
35+
git tag tag-one &&
36+
git tag TAG-two &&
37+
git tag -l >actual &&
38+
cat >expected <<-\EOF &&
39+
TAG-two
40+
initial
41+
tag-one
42+
EOF
43+
test_cmp expected actual &&
44+
git tag -l -i >actual &&
45+
cat >expected <<-\EOF &&
46+
initial
47+
tag-one
48+
TAG-two
49+
EOF
50+
test_cmp expected actual
51+
)
52+
'
53+
3054
test_expect_success 'looking for a tag in an empty tree should fail' \
3155
'! (tag_exists mytag)'
3256

@@ -81,6 +105,9 @@ test_expect_success 'listing all tags if one exists should output that tag' '
81105
test_expect_success 'listing a tag using a matching pattern should succeed' \
82106
'git tag -l mytag'
83107

108+
test_expect_success 'listing a tag with --ignore-case' \
109+
'test $(git tag -l --ignore-case MYTAG) = mytag'
110+
84111
test_expect_success \
85112
'listing a tag using a matching pattern should output that tag' \
86113
'test $(git tag -l mytag) = mytag'

0 commit comments

Comments
 (0)