Skip to content

Commit 5e738ae

Browse files
committed
Merge branch 'jj/icase-directory'
* jj/icase-directory: Support case folding in git fast-import when core.ignorecase=true Support case folding for git add when core.ignorecase=true Add case insensitivity support when using git ls-files Add case insensitivity support for directories when using git status Case insensitivity support for .gitignore via core.ignorecase Add string comparison functions that respect the ignore_case variable. Makefile & configure: add a NO_FNMATCH_CASEFOLD flag Makefile & configure: add a NO_FNMATCH flag Conflicts: Makefile config.mak.in configure.ac fast-import.c
2 parents b281796 + 50906e0 commit 5e738ae

File tree

8 files changed

+242
-27
lines changed

8 files changed

+242
-27
lines changed

Makefile

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,11 @@ all::
7070
#
7171
# Define NO_STRTOK_R if you don't have strtok_r in the C library.
7272
#
73+
# Define NO_FNMATCH if you don't have fnmatch in the C library.
74+
#
75+
# Define NO_FNMATCH_CASEFOLD if your fnmatch function doesn't have the
76+
# FNM_CASEFOLD GNU extension.
77+
#
7378
# Define NO_LIBGEN_H if you don't have libgen.h.
7479
#
7580
# Define NEEDS_LIBGEN if your libgen needs -lgen when linking
@@ -849,6 +854,7 @@ ifeq ($(uname_S),SunOS)
849854
NO_MKDTEMP = YesPlease
850855
NO_MKSTEMPS = YesPlease
851856
NO_REGEX = YesPlease
857+
NO_FNMATCH_CASEFOLD = YesPlease
852858
ifeq ($(uname_R),5.6)
853859
SOCKLEN_T = int
854860
NO_HSTRERROR = YesPlease
@@ -1055,6 +1061,7 @@ ifeq ($(uname_S),Windows)
10551061
NO_STRCASESTR = YesPlease
10561062
NO_STRLCPY = YesPlease
10571063
NO_STRTOK_R = YesPlease
1064+
NO_FNMATCH = YesPlease
10581065
NO_MEMMEM = YesPlease
10591066
# NEEDS_LIBICONV = YesPlease
10601067
NO_ICONV = YesPlease
@@ -1084,8 +1091,8 @@ ifeq ($(uname_S),Windows)
10841091
AR = compat/vcbuild/scripts/lib.pl
10851092
CFLAGS =
10861093
BASIC_CFLAGS = -nologo -I. -I../zlib -Icompat/vcbuild -Icompat/vcbuild/include -DWIN32 -D_CONSOLE -DHAVE_STRING_H -D_CRT_SECURE_NO_WARNINGS -D_CRT_NONSTDC_NO_DEPRECATE
1087-
COMPAT_OBJS = compat/msvc.o compat/fnmatch/fnmatch.o compat/winansi.o compat/win32/pthread.o compat/win32/syslog.o compat/win32/sys/poll.o
1088-
COMPAT_CFLAGS = -D__USE_MINGW_ACCESS -DNOGDI -DHAVE_STRING_H -DHAVE_ALLOCA_H -Icompat -Icompat/fnmatch -Icompat/regex -Icompat/fnmatch -Icompat/win32 -DSTRIP_EXTENSION=\".exe\"
1094+
COMPAT_OBJS = compat/msvc.o compat/winansi.o compat/win32/pthread.o compat/win32/syslog.o compat/win32/sys/poll.o
1095+
COMPAT_CFLAGS = -D__USE_MINGW_ACCESS -DNOGDI -DHAVE_STRING_H -DHAVE_ALLOCA_H -Icompat -Icompat/regex -Icompat/win32 -DSTRIP_EXTENSION=\".exe\"
10891096
BASIC_LDFLAGS = -IGNORE:4217 -IGNORE:4049 -NOLOGO -SUBSYSTEM:CONSOLE -NODEFAULTLIB:MSVCRT.lib
10901097
EXTLIBS = advapi32.lib shell32.lib wininet.lib ws2_32.lib
10911098
PTHREAD_LIBS =
@@ -1129,6 +1136,7 @@ ifneq (,$(findstring MINGW,$(uname_S)))
11291136
NO_STRCASESTR = YesPlease
11301137
NO_STRLCPY = YesPlease
11311138
NO_STRTOK_R = YesPlease
1139+
NO_FNMATCH = YesPlease
11321140
NO_MEMMEM = YesPlease
11331141
NEEDS_LIBICONV = YesPlease
11341142
OLD_ICONV = YesPlease
@@ -1152,9 +1160,9 @@ ifneq (,$(findstring MINGW,$(uname_S)))
11521160
NO_INET_PTON = YesPlease
11531161
NO_INET_NTOP = YesPlease
11541162
NO_POSIX_GOODIES = UnfortunatelyYes
1155-
COMPAT_CFLAGS += -D__USE_MINGW_ACCESS -DNOGDI -Icompat -Icompat/fnmatch -Icompat/win32
1163+
COMPAT_CFLAGS += -D__USE_MINGW_ACCESS -DNOGDI -Icompat -Icompat/win32
11561164
COMPAT_CFLAGS += -DSTRIP_EXTENSION=\".exe\"
1157-
COMPAT_OBJS += compat/mingw.o compat/fnmatch/fnmatch.o compat/winansi.o \
1165+
COMPAT_OBJS += compat/mingw.o compat/winansi.o \
11581166
compat/win32/pthread.o compat/win32/syslog.o \
11591167
compat/win32/sys/poll.o
11601168
EXTLIBS += -lws2_32
@@ -1364,6 +1372,17 @@ ifdef NO_STRTOK_R
13641372
COMPAT_CFLAGS += -DNO_STRTOK_R
13651373
COMPAT_OBJS += compat/strtok_r.o
13661374
endif
1375+
ifdef NO_FNMATCH
1376+
COMPAT_CFLAGS += -Icompat/fnmatch
1377+
COMPAT_CFLAGS += -DNO_FNMATCH
1378+
COMPAT_OBJS += compat/fnmatch/fnmatch.o
1379+
else
1380+
ifdef NO_FNMATCH_CASEFOLD
1381+
COMPAT_CFLAGS += -Icompat/fnmatch
1382+
COMPAT_CFLAGS += -DNO_FNMATCH_CASEFOLD
1383+
COMPAT_OBJS += compat/fnmatch/fnmatch.o
1384+
endif
1385+
endif
13671386
ifdef NO_SETENV
13681387
COMPAT_CFLAGS += -DNO_SETENV
13691388
COMPAT_OBJS += compat/setenv.o

config.mak.in

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@ NO_C99_FORMAT=@NO_C99_FORMAT@
4747
NO_HSTRERROR=@NO_HSTRERROR@
4848
NO_STRCASESTR=@NO_STRCASESTR@
4949
NO_STRTOK_R=@NO_STRTOK_R@
50+
NO_FNMATCH=@NO_FNMATCH@
51+
NO_FNMATCH_CASEFOLD=@NO_FNMATCH_CASEFOLD@
5052
NO_MEMMEM=@NO_MEMMEM@
5153
NO_STRLCPY=@NO_STRLCPY@
5254
NO_UINTMAX_T=@NO_UINTMAX_T@

configure.ac

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -830,6 +830,34 @@ GIT_CHECK_FUNC(strtok_r,
830830
[NO_STRTOK_R=YesPlease])
831831
AC_SUBST(NO_STRTOK_R)
832832
#
833+
# Define NO_FNMATCH if you don't have fnmatch
834+
GIT_CHECK_FUNC(fnmatch,
835+
[NO_FNMATCH=],
836+
[NO_FNMATCH=YesPlease])
837+
AC_SUBST(NO_FNMATCH)
838+
#
839+
# Define NO_FNMATCH_CASEFOLD if your fnmatch function doesn't have the
840+
# FNM_CASEFOLD GNU extension.
841+
AC_CACHE_CHECK([whether the fnmatch function supports the FNMATCH_CASEFOLD GNU extension],
842+
[ac_cv_c_excellent_fnmatch], [
843+
AC_EGREP_CPP(yippeeyeswehaveit,
844+
AC_LANG_PROGRAM([
845+
#include <fnmatch.h>
846+
],
847+
[#ifdef FNM_CASEFOLD
848+
yippeeyeswehaveit
849+
#endif
850+
]),
851+
[ac_cv_c_excellent_fnmatch=yes],
852+
[ac_cv_c_excellent_fnmatch=no])
853+
])
854+
if test $ac_cv_c_excellent_fnmatch = yes; then
855+
NO_FNMATCH_CASEFOLD=
856+
else
857+
NO_FNMATCH_CASEFOLD=YesPlease
858+
fi
859+
AC_SUBST(NO_FNMATCH_CASEFOLD)
860+
#
833861
# Define NO_MEMMEM if you don't have memmem.
834862
GIT_CHECK_FUNC(memmem,
835863
[NO_MEMMEM=],

dir.c

Lines changed: 87 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,22 @@ static int read_directory_recursive(struct dir_struct *dir, const char *path, in
1818
int check_only, const struct path_simplify *simplify);
1919
static int get_dtype(struct dirent *de, const char *path, int len);
2020

21+
/* helper string functions with support for the ignore_case flag */
22+
int strcmp_icase(const char *a, const char *b)
23+
{
24+
return ignore_case ? strcasecmp(a, b) : strcmp(a, b);
25+
}
26+
27+
int strncmp_icase(const char *a, const char *b, size_t count)
28+
{
29+
return ignore_case ? strncasecmp(a, b, count) : strncmp(a, b, count);
30+
}
31+
32+
int fnmatch_icase(const char *pattern, const char *string, int flags)
33+
{
34+
return fnmatch(pattern, string, flags | (ignore_case ? FNM_CASEFOLD : 0));
35+
}
36+
2137
static int common_prefix(const char **pathspec)
2238
{
2339
const char *path, *slash, *next;
@@ -91,16 +107,30 @@ static int match_one(const char *match, const char *name, int namelen)
91107
if (!*match)
92108
return MATCHED_RECURSIVELY;
93109

94-
for (;;) {
95-
unsigned char c1 = *match;
96-
unsigned char c2 = *name;
97-
if (c1 == '\0' || is_glob_special(c1))
98-
break;
99-
if (c1 != c2)
100-
return 0;
101-
match++;
102-
name++;
103-
namelen--;
110+
if (ignore_case) {
111+
for (;;) {
112+
unsigned char c1 = tolower(*match);
113+
unsigned char c2 = tolower(*name);
114+
if (c1 == '\0' || is_glob_special(c1))
115+
break;
116+
if (c1 != c2)
117+
return 0;
118+
match++;
119+
name++;
120+
namelen--;
121+
}
122+
} else {
123+
for (;;) {
124+
unsigned char c1 = *match;
125+
unsigned char c2 = *name;
126+
if (c1 == '\0' || is_glob_special(c1))
127+
break;
128+
if (c1 != c2)
129+
return 0;
130+
match++;
131+
name++;
132+
namelen--;
133+
}
104134
}
105135

106136

@@ -109,8 +139,8 @@ static int match_one(const char *match, const char *name, int namelen)
109139
* we need to match by fnmatch
110140
*/
111141
matchlen = strlen(match);
112-
if (strncmp(match, name, matchlen))
113-
return !fnmatch(match, name, 0) ? MATCHED_FNMATCH : 0;
142+
if (strncmp_icase(match, name, matchlen))
143+
return !fnmatch_icase(match, name, 0) ? MATCHED_FNMATCH : 0;
114144

115145
if (namelen == matchlen)
116146
return MATCHED_EXACTLY;
@@ -375,14 +405,14 @@ int excluded_from_list(const char *pathname,
375405
if (x->flags & EXC_FLAG_NODIR) {
376406
/* match basename */
377407
if (x->flags & EXC_FLAG_NOWILDCARD) {
378-
if (!strcmp(exclude, basename))
408+
if (!strcmp_icase(exclude, basename))
379409
return to_exclude;
380410
} else if (x->flags & EXC_FLAG_ENDSWITH) {
381411
if (x->patternlen - 1 <= pathlen &&
382-
!strcmp(exclude + 1, pathname + pathlen - x->patternlen + 1))
412+
!strcmp_icase(exclude + 1, pathname + pathlen - x->patternlen + 1))
383413
return to_exclude;
384414
} else {
385-
if (fnmatch(exclude, basename, 0) == 0)
415+
if (fnmatch_icase(exclude, basename, 0) == 0)
386416
return to_exclude;
387417
}
388418
}
@@ -397,14 +427,14 @@ int excluded_from_list(const char *pathname,
397427

398428
if (pathlen < baselen ||
399429
(baselen && pathname[baselen-1] != '/') ||
400-
strncmp(pathname, x->base, baselen))
430+
strncmp_icase(pathname, x->base, baselen))
401431
continue;
402432

403433
if (x->flags & EXC_FLAG_NOWILDCARD) {
404-
if (!strcmp(exclude, pathname + baselen))
434+
if (!strcmp_icase(exclude, pathname + baselen))
405435
return to_exclude;
406436
} else {
407-
if (fnmatch(exclude, pathname+baselen,
437+
if (fnmatch_icase(exclude, pathname+baselen,
408438
FNM_PATHNAME) == 0)
409439
return to_exclude;
410440
}
@@ -469,6 +499,39 @@ enum exist_status {
469499
index_gitdir
470500
};
471501

502+
/*
503+
* Do not use the alphabetically stored index to look up
504+
* the directory name; instead, use the case insensitive
505+
* name hash.
506+
*/
507+
static enum exist_status directory_exists_in_index_icase(const char *dirname, int len)
508+
{
509+
struct cache_entry *ce = index_name_exists(&the_index, dirname, len + 1, ignore_case);
510+
unsigned char endchar;
511+
512+
if (!ce)
513+
return index_nonexistent;
514+
endchar = ce->name[len];
515+
516+
/*
517+
* The cache_entry structure returned will contain this dirname
518+
* and possibly additional path components.
519+
*/
520+
if (endchar == '/')
521+
return index_directory;
522+
523+
/*
524+
* If there are no additional path components, then this cache_entry
525+
* represents a submodule. Submodules, despite being directories,
526+
* are stored in the cache without a closing slash.
527+
*/
528+
if (!endchar && S_ISGITLINK(ce->ce_mode))
529+
return index_gitdir;
530+
531+
/* This should never be hit, but it exists just in case. */
532+
return index_nonexistent;
533+
}
534+
472535
/*
473536
* The index sorts alphabetically by entry name, which
474537
* means that a gitlink sorts as '\0' at the end, while
@@ -478,7 +541,12 @@ enum exist_status {
478541
*/
479542
static enum exist_status directory_exists_in_index(const char *dirname, int len)
480543
{
481-
int pos = cache_name_pos(dirname, len);
544+
int pos;
545+
546+
if (ignore_case)
547+
return directory_exists_in_index_icase(dirname, len);
548+
549+
pos = cache_name_pos(dirname, len);
482550
if (pos < 0)
483551
pos = -pos-1;
484552
while (pos < active_nr) {

dir.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,4 +101,8 @@ extern int remove_dir_recursively(struct strbuf *path, int flag);
101101
/* tries to remove the path with empty directories along it, ignores ENOENT */
102102
extern int remove_path(const char *path);
103103

104+
extern int strcmp_icase(const char *a, const char *b);
105+
extern int strncmp_icase(const char *a, const char *b, size_t count);
106+
extern int fnmatch_icase(const char *pattern, const char *string, int flags);
107+
104108
#endif

fast-import.c

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,7 @@ Format of STDIN stream:
156156
#include "csum-file.h"
157157
#include "quote.h"
158158
#include "exec_cmd.h"
159+
#include "dir.h"
159160

160161
#define PACK_ID_BITS 16
161162
#define MAX_PACK_ID ((1<<PACK_ID_BITS)-1)
@@ -1478,7 +1479,7 @@ static int tree_content_set(
14781479
t = root->tree;
14791480
for (i = 0; i < t->entry_count; i++) {
14801481
e = t->entries[i];
1481-
if (e->name->str_len == n && !strncmp(p, e->name->str_dat, n)) {
1482+
if (e->name->str_len == n && !strncmp_icase(p, e->name->str_dat, n)) {
14821483
if (!slash1) {
14831484
if (!S_ISDIR(mode)
14841485
&& e->versions[1].mode == mode
@@ -1547,7 +1548,7 @@ static int tree_content_remove(
15471548
t = root->tree;
15481549
for (i = 0; i < t->entry_count; i++) {
15491550
e = t->entries[i];
1550-
if (e->name->str_len == n && !strncmp(p, e->name->str_dat, n)) {
1551+
if (e->name->str_len == n && !strncmp_icase(p, e->name->str_dat, n)) {
15511552
if (slash1 && !S_ISDIR(e->versions[1].mode))
15521553
/*
15531554
* If p names a file in some subdirectory, and a
@@ -1608,7 +1609,7 @@ static int tree_content_get(
16081609
t = root->tree;
16091610
for (i = 0; i < t->entry_count; i++) {
16101611
e = t->entries[i];
1611-
if (e->name->str_len == n && !strncmp(p, e->name->str_dat, n)) {
1612+
if (e->name->str_len == n && !strncmp_icase(p, e->name->str_dat, n)) {
16121613
if (!slash1) {
16131614
memcpy(leaf, e, sizeof(*leaf));
16141615
if (e->tree && is_null_sha1(e->versions[1].sha1))

0 commit comments

Comments
 (0)