This is a follow up patch to one I posted mid last year specifically for
making .gitignore entries be processed in a case insensitive manner. I
have used the patch with success since then, but I was unable to make
the updates requested by list members.
Over the past month, I have been able to clean up the code and extend
the core.ignorecase=true support to better handle git status, ls-files,
log, add, and fast-import.
git status and add both use an update made to name-hash.c where
directories, specifically names with a trailing slash, can be looked up
in a case insensitive manner. After trying a myriad of solutions, this
seemed to be the cleanest. Does anyone see a problem with embedding the
directory names in the same hash as the file names? I couldn't find one,
especially since I append a slash to each directory name.
The new directory_exists_in_index_icase() duplicates some of the
functionality in directory_exists_in_index(). I don't understand some of
the functionality I just mindlessly cloned. Can anyone clue me in on
what directory_exists_in_index() achieves in the case of S_ISGITLINK()?
Updating match_one() bought case insensitive matching support for git
ls-files? Are there other side effects I haven't found yet?
The git add path case folding functionality is a somewhat radical
departure from what Git does now. It is described in detail in patch 6.
Does anyone have any concerns?
Joshua Jensen (7):
Add string comparison functions that respect the ignore_case
variable.
Case insensitivity support for .gitignore via core.ignorecase
Add case insensitivity support for directories when using git status
Add case insensitivity support when using git ls-files
Add support for case insensitive directory and file lookups to git
log
Support case folding for git add when core.ignorecase=true
Support case folding in git fast-import when core.ignorecase=true
dir.c | 110 +++++++++++++++++++++++++++++++++++++++++++++++----------
dir.h | 5 +++
fast-import.c | 3 +-
name-hash.c | 44 ++++++++++++++++++++++-
read-cache.c | 16 ++++++++
tree-diff.c | 9 +++--
6 files changed, 162 insertions(+), 25 deletions(-)
This patch also affects any other commands that use tree-diff.c.
Signed-off-by: Joshua Jensen <redacted>
---
tree-diff.c | 9 +++++----
1 files changed, 5 insertions(+), 4 deletions(-)
@@ -114,7 +115,7 @@ static int tree_entry_interesting(struct tree_desc *desc, const char *base, intif(baselen>=matchlen){/* If it doesn't match, move along... */-if(strncmp(base,match,matchlen))+if(strncmp_icase(base,match,matchlen))continue;/*
@@ -131,7 +132,7 @@ static int tree_entry_interesting(struct tree_desc *desc, const char *base, int}/* Does the base match? */-if(strncmp(base,match,baselen))+if(strncmp_icase(base,match,baselen))continue;match+=baselen;
Multiple locations within this patch series alter a case sensitive
string comparison call such as strcmp() to be a call to a string
comparison call that selects case comparison based on the global
ignore_case variable. Behaviorally, when core.ignorecase=false, the
*_icase() versions are functionally equivalent to their C runtime
counterparts. When core.ignorecase=true, the *_icase() versions perform
a case insensitive comparison.
Like Linus' earlier ignorecase patch, these may ignore filename
conventions on certain file systems. By isolating filename comparisons
to certain functions, support for those filename conventions may be more
easily met.
Signed-off-by: Joshua Jensen <redacted>
---
dir.c | 35 +++++++++++++++++++++++++++++++++++
dir.h | 5 +++++
2 files changed, 40 insertions(+), 0 deletions(-)
@@ -18,6 +18,41 @@ static int read_directory_recursive(struct dir_struct *dir, const char *path, inintcheck_only,conststructpath_simplify*simplify);staticintget_dtype(structdirent*de,constchar*path,intlen);+/* helper string functions with support for the ignore_case flag */+intstrcmp_icase(constchar*a,constchar*b)+{+returnignore_case?strcasecmp(a,b):strcmp(a,b);+}++intstrncmp_icase(constchar*a,constchar*b,size_tcount)+{+returnignore_case?strncasecmp(a,b,count):strncmp(a,b,count);+}++intfnmatch_icase(constchar*pattern,constchar*string,intflags)+{+returnfnmatch(pattern,string,flags|(ignore_case?FNM_CASEFOLD:0));+}++intmemcmp_icase(constchar*a,constchar*b,size_tcount)+{+if(ignore_case){+intlowera=0;+intlowerb=0;+while(--count){+lowera=tolower(*a++);+lowerb=tolower(*b++);+if(lowera!=lowerb)+break;+}+returnlowera-lowerb;++return0;+}else{+returnmemcmp(a,b,count);+}+}+staticintcommon_prefix(constchar**pathspec){constchar*path,*slash,*next;
@@ -100,4 +100,9 @@ extern int remove_dir_recursively(struct strbuf *path, int flag);/* tries to remove the path with empty directories along it, ignores ENOENT */externintremove_path(constchar*path);+externintstrcmp_icase(constchar*a,constchar*b);+externintstrncmp_icase(constchar*a,constchar*b,size_tcount);+externintfnmatch_icase(constchar*pattern,constchar*string,intflags);+externintmemcmp_icase(constchar*a,constchar*b,size_tcount);+#endif
When using a case preserving but case insensitive file system, directory
case can differ but still refer to the same physical directory. git
status reports the directory with the alternate case as an Untracked
file. (That is, when mydir/filea.txt is added to the repository and
then the directory on disk is renamed from mydir/ to MyDir/, git status
shows MyDir/ as being untracked.)
Support has been added in name-hash.c for hashing directories with a
terminating slash into the name hash. When index_name_exists() is called
with a directory (a name with a terminating slash), the name is not
found via the normal cache_name_compare() call, but it is found in the
slow_same_name() function.
Additionally, in dir.c, directory_exists_in_index_icase() allows newly
added directories deeper in the directory chain to be identified.
Ultimately, it would be better if the file list was read in case
insensitive alphabetical order from disk, but this change seems to
suffice for now.
The end result is the directory is looked up in a case insensitive
manner and does not show in the Untracked files list.
Signed-off-by: Joshua Jensen <redacted>
---
dir.c | 25 ++++++++++++++++++++++++-
name-hash.c | 44 +++++++++++++++++++++++++++++++++++++++++++-
2 files changed, 67 insertions(+), 2 deletions(-)
@@ -32,6 +32,30 @@ static unsigned int hash_name(const char *name, int namelen)returnhash;}+staticvoidhash_index_entry_directories(structindex_state*istate,structcache_entry*ce)+{+/* throw each directory component in the hash for quick lookup during a git status */+unsignedinthash;+void**pos;++constchar*ptr=ce->name;+while(*ptr){+while(*ptr&&*ptr!='/')+++ptr;+if(*ptr=='/'){+++ptr;+hash=hash_name(ce->name,ptr-ce->name);+if(!lookup_hash(hash,&istate->name_hash)){+pos=insert_hash(hash,ce,&istate->name_hash);+if(pos){+ce->next=*pos;+*pos=ce;+}+}+}+}+}+staticvoidhash_index_entry(structindex_state*istate,structcache_entry*ce){void**pos;
This is especially beneficial when using Windows and Perforce and the
git-p4 bridge. Internally, Perforce preserves a given file's full path
including its case at the time it was added to the Perforce repository.
When syncing a file down via Perforce, missing directories are created,
if necessary, using the case as stored with the filename. Unfortunately,
two files in the same directory can have differing cases for their
respective paths, such as /diRa/file1.c and /DirA/file2.c. Depending on
sync order, DirA/ may get created instead of diRa/.
It is possible to handle directory names in a case insensitive manner
without this patch, but it is highly inconvenient, requiring each
character to be specified like so: [Bb][Uu][Ii][Ll][Dd]. With this patch, the
gitignore exclusions honor the core.ignorecase=true configuration
setting and make the process less error prone. The above is specified
like so: Build
Signed-off-by: Joshua Jensen <redacted>
---
dir.c | 12 ++++++------
1 files changed, 6 insertions(+), 6 deletions(-)
When MyDir/ABC/filea.txt is added to Git, the disk directory MyDir/ABC/
is renamed to mydir/aBc/, and then mydir/aBc/fileb.txt is added, the
index will contain MyDir/ABC/filea.txt and mydir/aBc/fileb.txt. Although
the earlier portions of this patch series account for those differences
in case, this patch makes the pathing consistent by folding the case of
newly added files against the first file added with that path.
In read-cache.c's add_to_index(), the index_name_exists() support used
for git status's case insensitive directory lookups is used to find the
proper directory case according to what the user already checked in.
That is, MyDir/ABC/'s case is used to alter the stored path for
fileb.txt to MyDir/ABC/fileb.txt (instead of mydir/aBc/fileb.txt).
This is especially important when cloning a repository to a case
sensitive file system. MyDir/ABC/ and mydir/aBc/ exist in the same
directory on a Windows machine, but on Linux, the files exist in two
separate directories. The update to add_to_index(), in effect, treats a
Windows file system as case sensitive by making path case consistent.
Signed-off-by: Joshua Jensen <redacted>
---
read-cache.c | 16 ++++++++++++++++
1 files changed, 16 insertions(+), 0 deletions(-)
When mydir/filea.txt is added, mydir/ is renamed to MyDir/, and
MyDir/fileb.txt is added, running git ls-files mydir only shows
mydir/filea.txt. Running git ls-files MyDir shows MyDir/fileb.txt.
Running git ls-files mYdIR shows nothing.
With this patch running git ls-files for mydir, MyDir, and mYdIR shows
mydir/filea.txt and MyDir/fileb.txt.
Wildcards are not handled case insensitively in this patch. Example:
MyDir/aBc/file.txt is added. git ls-files MyDir/a* works fine, but git
ls-files mydir/a* does not.
Signed-off-by: Joshua Jensen <redacted>
---
dir.c | 38 ++++++++++++++++++++++++++------------
1 files changed, 26 insertions(+), 12 deletions(-)
Joshua Jensen (7):
Add string comparison functions that respect the ignore_case
variable.
Case insensitivity support for .gitignore via core.ignorecase
Add case insensitivity support for directories when using git status
Add case insensitivity support when using git ls-files
Add support for case insensitive directory and file lookups to git
log
Support case folding for git add when core.ignorecase=true
Support case folding in git fast-import when core.ignorecase=true
Would this patch series be better sent to the msysGit mailing list,
given that it addresses Windows (and Mac OS X, I suppose) case
preserving but case insensitive file system issues? I posted here
first, because it builds on some core.ignorecase functionality Linus wrote.
Thanks.
Josh