Commit d28eec26 authored by Eric Sunshine's avatar Eric Sunshine Committed by Junio C Hamano

name-hash: stop storing trailing '/' on paths in index_state.dir_hash

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 continue to bear the burden of ensuring the slash's
presence before searching the index for a directory. 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

[1]: Eric Sunshine's avatarEric Sunshine <[email protected]>
Signed-off-by: default avatarJunio C Hamano <[email protected]>
parent ebbd7439
......@@ -889,7 +889,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)
......@@ -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,
if (namelen <= 0)
return NULL;
/* 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_file_exists(istate, name, namelen - 1, 1);
ce = index_file_exists(istate, name, namelen, 1);
if (ce && S_ISGITLINK(ce->ce_mode))
return ce;
......@@ -265,7 +266,7 @@ struct cache_entry *index_file_exists(struct index_state *istate, const char *na
struct cache_entry *index_name_exists(struct index_state *istate, const char *name, int namelen, int icase)
if (namelen > 0 && name[namelen - 1] == '/')
return index_dir_exists(istate, name, namelen);
return index_dir_exists(istate, name, namelen - 1);
return index_file_exists(istate, name, namelen, icase);
......@@ -643,7 +643,7 @@ int add_to_index(struct index_state *istate, const char *path, struct stat *st,
if (*ptr == '/') {
struct cache_entry *foundce;
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;
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment