Skip to content

Commit

Permalink
Merge branch 'jj/icase-directory'
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
gitster committed Dec 4, 2010
2 parents b281796 + 50906e0 commit 5e738ae
Show file tree
Hide file tree
Showing 8 changed files with 242 additions and 27 deletions.
27 changes: 23 additions & 4 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,11 @@ all::
#
# Define NO_STRTOK_R if you don't have strtok_r in the C library.
#
# Define NO_FNMATCH if you don't have fnmatch in the C library.
#
# Define NO_FNMATCH_CASEFOLD if your fnmatch function doesn't have the
# FNM_CASEFOLD GNU extension.
#
# Define NO_LIBGEN_H if you don't have libgen.h.
#
# Define NEEDS_LIBGEN if your libgen needs -lgen when linking
Expand Down Expand Up @@ -849,6 +854,7 @@ ifeq ($(uname_S),SunOS)
NO_MKDTEMP = YesPlease
NO_MKSTEMPS = YesPlease
NO_REGEX = YesPlease
NO_FNMATCH_CASEFOLD = YesPlease
ifeq ($(uname_R),5.6)
SOCKLEN_T = int
NO_HSTRERROR = YesPlease
Expand Down Expand Up @@ -1055,6 +1061,7 @@ ifeq ($(uname_S),Windows)
NO_STRCASESTR = YesPlease
NO_STRLCPY = YesPlease
NO_STRTOK_R = YesPlease
NO_FNMATCH = YesPlease
NO_MEMMEM = YesPlease
# NEEDS_LIBICONV = YesPlease
NO_ICONV = YesPlease
Expand Down Expand Up @@ -1084,8 +1091,8 @@ ifeq ($(uname_S),Windows)
AR = compat/vcbuild/scripts/lib.pl
CFLAGS =
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
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
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\"
COMPAT_OBJS = compat/msvc.o compat/winansi.o compat/win32/pthread.o compat/win32/syslog.o compat/win32/sys/poll.o
COMPAT_CFLAGS = -D__USE_MINGW_ACCESS -DNOGDI -DHAVE_STRING_H -DHAVE_ALLOCA_H -Icompat -Icompat/regex -Icompat/win32 -DSTRIP_EXTENSION=\".exe\"
BASIC_LDFLAGS = -IGNORE:4217 -IGNORE:4049 -NOLOGO -SUBSYSTEM:CONSOLE -NODEFAULTLIB:MSVCRT.lib
EXTLIBS = advapi32.lib shell32.lib wininet.lib ws2_32.lib
PTHREAD_LIBS =
Expand Down Expand Up @@ -1129,6 +1136,7 @@ ifneq (,$(findstring MINGW,$(uname_S)))
NO_STRCASESTR = YesPlease
NO_STRLCPY = YesPlease
NO_STRTOK_R = YesPlease
NO_FNMATCH = YesPlease
NO_MEMMEM = YesPlease
NEEDS_LIBICONV = YesPlease
OLD_ICONV = YesPlease
Expand All @@ -1152,9 +1160,9 @@ ifneq (,$(findstring MINGW,$(uname_S)))
NO_INET_PTON = YesPlease
NO_INET_NTOP = YesPlease
NO_POSIX_GOODIES = UnfortunatelyYes
COMPAT_CFLAGS += -D__USE_MINGW_ACCESS -DNOGDI -Icompat -Icompat/fnmatch -Icompat/win32
COMPAT_CFLAGS += -D__USE_MINGW_ACCESS -DNOGDI -Icompat -Icompat/win32
COMPAT_CFLAGS += -DSTRIP_EXTENSION=\".exe\"
COMPAT_OBJS += compat/mingw.o compat/fnmatch/fnmatch.o compat/winansi.o \
COMPAT_OBJS += compat/mingw.o compat/winansi.o \
compat/win32/pthread.o compat/win32/syslog.o \
compat/win32/sys/poll.o
EXTLIBS += -lws2_32
Expand Down Expand Up @@ -1364,6 +1372,17 @@ ifdef NO_STRTOK_R
COMPAT_CFLAGS += -DNO_STRTOK_R
COMPAT_OBJS += compat/strtok_r.o
endif
ifdef NO_FNMATCH
COMPAT_CFLAGS += -Icompat/fnmatch
COMPAT_CFLAGS += -DNO_FNMATCH
COMPAT_OBJS += compat/fnmatch/fnmatch.o
else
ifdef NO_FNMATCH_CASEFOLD
COMPAT_CFLAGS += -Icompat/fnmatch
COMPAT_CFLAGS += -DNO_FNMATCH_CASEFOLD
COMPAT_OBJS += compat/fnmatch/fnmatch.o
endif
endif
ifdef NO_SETENV
COMPAT_CFLAGS += -DNO_SETENV
COMPAT_OBJS += compat/setenv.o
Expand Down
2 changes: 2 additions & 0 deletions config.mak.in
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ NO_C99_FORMAT=@NO_C99_FORMAT@
NO_HSTRERROR=@NO_HSTRERROR@
NO_STRCASESTR=@NO_STRCASESTR@
NO_STRTOK_R=@NO_STRTOK_R@
NO_FNMATCH=@NO_FNMATCH@
NO_FNMATCH_CASEFOLD=@NO_FNMATCH_CASEFOLD@
NO_MEMMEM=@NO_MEMMEM@
NO_STRLCPY=@NO_STRLCPY@
NO_UINTMAX_T=@NO_UINTMAX_T@
Expand Down
28 changes: 28 additions & 0 deletions configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -830,6 +830,34 @@ GIT_CHECK_FUNC(strtok_r,
[NO_STRTOK_R=YesPlease])
AC_SUBST(NO_STRTOK_R)
#
# Define NO_FNMATCH if you don't have fnmatch
GIT_CHECK_FUNC(fnmatch,
[NO_FNMATCH=],
[NO_FNMATCH=YesPlease])
AC_SUBST(NO_FNMATCH)
#
# Define NO_FNMATCH_CASEFOLD if your fnmatch function doesn't have the
# FNM_CASEFOLD GNU extension.
AC_CACHE_CHECK([whether the fnmatch function supports the FNMATCH_CASEFOLD GNU extension],
[ac_cv_c_excellent_fnmatch], [
AC_EGREP_CPP(yippeeyeswehaveit,
AC_LANG_PROGRAM([
#include <fnmatch.h>
],
[#ifdef FNM_CASEFOLD
yippeeyeswehaveit
#endif
]),
[ac_cv_c_excellent_fnmatch=yes],
[ac_cv_c_excellent_fnmatch=no])
])
if test $ac_cv_c_excellent_fnmatch = yes; then
NO_FNMATCH_CASEFOLD=
else
NO_FNMATCH_CASEFOLD=YesPlease
fi
AC_SUBST(NO_FNMATCH_CASEFOLD)
#
# Define NO_MEMMEM if you don't have memmem.
GIT_CHECK_FUNC(memmem,
[NO_MEMMEM=],
Expand Down
106 changes: 87 additions & 19 deletions dir.c
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,22 @@ static int read_directory_recursive(struct dir_struct *dir, const char *path, in
int check_only, const struct path_simplify *simplify);
static int get_dtype(struct dirent *de, const char *path, int len);

/* helper string functions with support for the ignore_case flag */
int strcmp_icase(const char *a, const char *b)
{
return ignore_case ? strcasecmp(a, b) : strcmp(a, b);
}

int strncmp_icase(const char *a, const char *b, size_t count)
{
return ignore_case ? strncasecmp(a, b, count) : strncmp(a, b, count);
}

int fnmatch_icase(const char *pattern, const char *string, int flags)
{
return fnmatch(pattern, string, flags | (ignore_case ? FNM_CASEFOLD : 0));
}

static int common_prefix(const char **pathspec)
{
const char *path, *slash, *next;
Expand Down Expand Up @@ -91,16 +107,30 @@ static int match_one(const char *match, const char *name, int namelen)
if (!*match)
return MATCHED_RECURSIVELY;

for (;;) {
unsigned char c1 = *match;
unsigned char c2 = *name;
if (c1 == '\0' || is_glob_special(c1))
break;
if (c1 != c2)
return 0;
match++;
name++;
namelen--;
if (ignore_case) {
for (;;) {
unsigned char c1 = tolower(*match);
unsigned char c2 = tolower(*name);
if (c1 == '\0' || is_glob_special(c1))
break;
if (c1 != c2)
return 0;
match++;
name++;
namelen--;
}
} else {
for (;;) {
unsigned char c1 = *match;
unsigned char c2 = *name;
if (c1 == '\0' || is_glob_special(c1))
break;
if (c1 != c2)
return 0;
match++;
name++;
namelen--;
}
}


Expand All @@ -109,8 +139,8 @@ static int match_one(const char *match, const char *name, int namelen)
* we need to match by fnmatch
*/
matchlen = strlen(match);
if (strncmp(match, name, matchlen))
return !fnmatch(match, name, 0) ? MATCHED_FNMATCH : 0;
if (strncmp_icase(match, name, matchlen))
return !fnmatch_icase(match, name, 0) ? MATCHED_FNMATCH : 0;

if (namelen == matchlen)
return MATCHED_EXACTLY;
Expand Down Expand Up @@ -375,14 +405,14 @@ int excluded_from_list(const char *pathname,
if (x->flags & EXC_FLAG_NODIR) {
/* match basename */
if (x->flags & EXC_FLAG_NOWILDCARD) {
if (!strcmp(exclude, basename))
if (!strcmp_icase(exclude, basename))
return to_exclude;
} else if (x->flags & EXC_FLAG_ENDSWITH) {
if (x->patternlen - 1 <= pathlen &&
!strcmp(exclude + 1, pathname + pathlen - x->patternlen + 1))
!strcmp_icase(exclude + 1, pathname + pathlen - x->patternlen + 1))
return to_exclude;
} else {
if (fnmatch(exclude, basename, 0) == 0)
if (fnmatch_icase(exclude, basename, 0) == 0)
return to_exclude;
}
}
Expand All @@ -397,14 +427,14 @@ int excluded_from_list(const char *pathname,

if (pathlen < baselen ||
(baselen && pathname[baselen-1] != '/') ||
strncmp(pathname, x->base, baselen))
strncmp_icase(pathname, x->base, baselen))
continue;

if (x->flags & EXC_FLAG_NOWILDCARD) {
if (!strcmp(exclude, pathname + baselen))
if (!strcmp_icase(exclude, pathname + baselen))
return to_exclude;
} else {
if (fnmatch(exclude, pathname+baselen,
if (fnmatch_icase(exclude, pathname+baselen,
FNM_PATHNAME) == 0)
return to_exclude;
}
Expand Down Expand Up @@ -469,6 +499,39 @@ enum exist_status {
index_gitdir
};

/*
* Do not use the alphabetically stored index to look up
* the directory name; instead, use the case insensitive
* name hash.
*/
static enum exist_status directory_exists_in_index_icase(const char *dirname, int len)
{
struct cache_entry *ce = index_name_exists(&the_index, dirname, len + 1, ignore_case);
unsigned char endchar;

if (!ce)
return index_nonexistent;
endchar = ce->name[len];

/*
* The cache_entry structure returned will contain this dirname
* and possibly additional path components.
*/
if (endchar == '/')
return index_directory;

/*
* If there are no additional path components, then this cache_entry
* represents a submodule. Submodules, despite being directories,
* are stored in the cache without a closing slash.
*/
if (!endchar && S_ISGITLINK(ce->ce_mode))
return index_gitdir;

/* This should never be hit, but it exists just in case. */
return index_nonexistent;
}

/*
* The index sorts alphabetically by entry name, which
* means that a gitlink sorts as '\0' at the end, while
Expand All @@ -478,7 +541,12 @@ enum exist_status {
*/
static enum exist_status directory_exists_in_index(const char *dirname, int len)
{
int pos = cache_name_pos(dirname, len);
int pos;

if (ignore_case)
return directory_exists_in_index_icase(dirname, len);

pos = cache_name_pos(dirname, len);
if (pos < 0)
pos = -pos-1;
while (pos < active_nr) {
Expand Down
4 changes: 4 additions & 0 deletions dir.h
Original file line number Diff line number Diff line change
Expand Up @@ -101,4 +101,8 @@ extern int remove_dir_recursively(struct strbuf *path, int flag);
/* tries to remove the path with empty directories along it, ignores ENOENT */
extern int remove_path(const char *path);

extern int strcmp_icase(const char *a, const char *b);
extern int strncmp_icase(const char *a, const char *b, size_t count);
extern int fnmatch_icase(const char *pattern, const char *string, int flags);

#endif
7 changes: 4 additions & 3 deletions fast-import.c
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,7 @@ Format of STDIN stream:
#include "csum-file.h"
#include "quote.h"
#include "exec_cmd.h"
#include "dir.h"

#define PACK_ID_BITS 16
#define MAX_PACK_ID ((1<<PACK_ID_BITS)-1)
Expand Down Expand Up @@ -1478,7 +1479,7 @@ static int tree_content_set(
t = root->tree;
for (i = 0; i < t->entry_count; i++) {
e = t->entries[i];
if (e->name->str_len == n && !strncmp(p, e->name->str_dat, n)) {
if (e->name->str_len == n && !strncmp_icase(p, e->name->str_dat, n)) {
if (!slash1) {
if (!S_ISDIR(mode)
&& e->versions[1].mode == mode
Expand Down Expand Up @@ -1547,7 +1548,7 @@ static int tree_content_remove(
t = root->tree;
for (i = 0; i < t->entry_count; i++) {
e = t->entries[i];
if (e->name->str_len == n && !strncmp(p, e->name->str_dat, n)) {
if (e->name->str_len == n && !strncmp_icase(p, e->name->str_dat, n)) {
if (slash1 && !S_ISDIR(e->versions[1].mode))
/*
* If p names a file in some subdirectory, and a
Expand Down Expand Up @@ -1608,7 +1609,7 @@ static int tree_content_get(
t = root->tree;
for (i = 0; i < t->entry_count; i++) {
e = t->entries[i];
if (e->name->str_len == n && !strncmp(p, e->name->str_dat, n)) {
if (e->name->str_len == n && !strncmp_icase(p, e->name->str_dat, n)) {
if (!slash1) {
memcpy(leaf, e, sizeof(*leaf));
if (e->tree && is_null_sha1(e->versions[1].sha1))
Expand Down
Loading

0 comments on commit 5e738ae

Please sign in to comment.