When 5102c617 (Add case insensitivity support for directories when using
git status, 2010-10-03) added directories to the name-hash there was
only a single hash table in which both real cache entries and leading
directory prefixes were registered. To distinguish between the two types
of entries, directories were stored with a trailing '/'.

2092678c (name-hash.c: fix endless loop with core.ignorecase=true,
2013-02-28), however, moved directories to a separate hash table
(index_state.dir_hash) but retained the now-redundant trailing '/', thus
callers still bear the burden of ensuring its presence before searching
via index_dir_exists(). Eliminate this redundancy by storing paths in
the dir-hash without the trailing '/'.

An important benefit of this change is that it eliminates undocumented
and dangerous behavior of dir.c:directory_exists_in_index_icase() in
which it assumes not only that it can validly access one character
beyond the end of its incoming directory argument, but also that that
character will unconditionally be a '/'. This perilous behavior was
"tolerated" because the string passed in by its lone caller always had a
'/' in that position, however, things broke [1] when 2eac2a4c (ls-files
-k: a directory only can be killed if the index has a non-directory,
2013-08-15) added a new caller which failed to respect the undocumented
assumption.

[1]: http://thread.gmane.org/gmane.comp.version-control.git/232727

Signed-off-by: Eric Sunshine <sunsh...@sunshineco.com>
---
 dir.c        | 2 +-
 name-hash.c  | 9 +++++----
 read-cache.c | 2 +-
 3 files changed, 7 insertions(+), 6 deletions(-)

diff --git a/dir.c b/dir.c
index 0080673..69d04a0 100644
--- a/dir.c
+++ b/dir.c
@@ -890,7 +890,7 @@ enum exist_status {
  */
 static enum exist_status directory_exists_in_index_icase(const char *dirname, 
int len)
 {
-       const struct cache_entry *ce = cache_dir_exists(dirname, len + 1);
+       const struct cache_entry *ce = cache_dir_exists(dirname, len);
        unsigned char endchar;
 
        if (!ce)
diff --git a/name-hash.c b/name-hash.c
index 5b01554..2bae75d 100644
--- a/name-hash.c
+++ b/name-hash.c
@@ -58,9 +58,9 @@ static struct dir_entry *hash_dir_entry(struct index_state 
*istate,
 {
        /*
         * Throw each directory component in the hash for quick lookup
-        * during a git status. Directory components are stored with their
+        * during a git status. Directory components are stored without their
         * closing slash.  Despite submodules being a directory, they never
-        * reach this point, because they are stored without a closing slash
+        * reach this point, because they are stored
         * in index_state.name_hash (as ordinary cache_entries).
         *
         * Note that the cache_entry stored with the dir_entry merely
@@ -78,6 +78,7 @@ static struct dir_entry *hash_dir_entry(struct index_state 
*istate,
                namelen--;
        if (namelen <= 0)
                return NULL;
+       namelen--;
 
        /* lookup existing entry for that directory */
        dir = find_dir_entry(istate, ce->name, namelen);
@@ -97,7 +98,7 @@ static struct dir_entry *hash_dir_entry(struct index_state 
*istate,
                }
 
                /* recursively add missing parent directories */
-               dir->parent = hash_dir_entry(istate, ce, namelen - 1);
+               dir->parent = hash_dir_entry(istate, ce, namelen);
        }
        return dir;
 }
@@ -237,7 +238,7 @@ struct cache_entry *index_dir_exists(struct index_state 
*istate, const char *nam
         * in the dir-hash, submodules are stored in the name-hash, so check
         * there, as well.
         */
-       ce = index_name_exists(istate, name, namelen - 1, 1);
+       ce = index_name_exists(istate, name, namelen, 1);
        if (ce && S_ISGITLINK(ce->ce_mode))
                return ce;
 
diff --git a/read-cache.c b/read-cache.c
index a59644a..8990b61 100644
--- a/read-cache.c
+++ b/read-cache.c
@@ -643,7 +643,7 @@ int add_to_index(struct index_state *istate, const char 
*path, struct stat *st,
                        if (*ptr == '/') {
                                struct cache_entry *foundce;
                                ++ptr;
-                               foundce = index_dir_exists(istate, ce->name, 
ptr - ce->name);
+                               foundce = index_dir_exists(istate, ce->name, 
ptr - ce->name - 1);
                                if (foundce) {
                                        memcpy((void *)startPtr, foundce->name 
+ (startPtr - ce->name), ptr - startPtr);
                                        startPtr = ptr;
-- 
1.8.4.457.g424cb08

--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to