Depending upon the absence or presence of a trailing '/' on the incoming pathname, index_name_exists() checks either if a file is present in the index or if a directory is represented within the index. Each caller explicitly chooses the mode of operation by adding or removing a trailing '/' before invoking index_name_exists(). Since these two modes of operations are disjoint and have no code in common (one searches index_state.name_hash; the other dir_hash), they can be represented more naturally as distinct functions: one to search for a file, and one for a directory. Splitting index searching into two functions relieves callers of the artificial burden of having to add or remove a slash to select the mode of operation; instead they just call the desired function. A subsequent patch will take advantage of this benefit in order to eliminate the requirement that the incoming pathname for a directory search must have a trailing slash. Signed-off-by: Eric Sunshine <sunshine@xxxxxxxxxxxxxx> --- cache.h | 2 ++ dir.c | 7 ++++--- name-hash.c | 47 ++++++++++++++++++++++++----------------------- read-cache.c | 2 +- 4 files changed, 31 insertions(+), 27 deletions(-) diff --git a/cache.h b/cache.h index 9ef778a..03ec24c 100644 --- a/cache.h +++ b/cache.h @@ -314,6 +314,7 @@ extern void free_name_hash(struct index_state *istate); #define refresh_cache(flags) refresh_index(&the_index, (flags), NULL, NULL, NULL) #define ce_match_stat(ce, st, options) ie_match_stat(&the_index, (ce), (st), (options)) #define ce_modified(ce, st, options) ie_modified(&the_index, (ce), (st), (options)) +#define cache_dir_exists(name, namelen) index_dir_exists(&the_index, (name), (namelen)) #define cache_name_exists(name, namelen, igncase) index_name_exists(&the_index, (name), (namelen), (igncase)) #define cache_name_is_other(name, namelen) index_name_is_other(&the_index, (name), (namelen)) #define resolve_undo_clear() resolve_undo_clear_index(&the_index) @@ -463,6 +464,7 @@ extern int write_index(struct index_state *, int newfd); extern int discard_index(struct index_state *); extern int unmerged_index(const struct index_state *); extern int verify_path(const char *path); +extern struct cache_entry *index_dir_exists(struct index_state *istate, const char *name, int namelen); extern struct cache_entry *index_name_exists(struct index_state *istate, const char *name, int namelen, int igncase); extern int index_name_pos(const struct index_state *, const char *name, int namelen); #define ADD_CACHE_OK_TO_ADD 1 /* Ok to add */ diff --git a/dir.c b/dir.c index b439ff0..0080673 100644 --- a/dir.c +++ b/dir.c @@ -860,7 +860,8 @@ static struct dir_entry *dir_entry_new(const char *pathname, int len) static struct dir_entry *dir_add_name(struct dir_struct *dir, const char *pathname, int len) { - if (cache_name_exists(pathname, len, ignore_case)) + if ((len == 0 || pathname[len - 1] != '/') && + cache_name_exists(pathname, len, ignore_case)) return NULL; ALLOC_GROW(dir->entries, dir->nr+1, dir->alloc); @@ -885,11 +886,11 @@ enum exist_status { /* * Do not use the alphabetically sorted index to look up * the directory name; instead, use the case insensitive - * name hash. + * directory hash. */ static enum exist_status directory_exists_in_index_icase(const char *dirname, int len) { - const struct cache_entry *ce = cache_name_exists(dirname, len + 1, ignore_case); + const struct cache_entry *ce = cache_dir_exists(dirname, len + 1); unsigned char endchar; if (!ce) diff --git a/name-hash.c b/name-hash.c index 617c86c..5b01554 100644 --- a/name-hash.c +++ b/name-hash.c @@ -222,11 +222,35 @@ static int same_name(const struct cache_entry *ce, const char *name, int namelen return slow_same_name(name, namelen, ce->name, len); } +struct cache_entry *index_dir_exists(struct index_state *istate, const char *name, int namelen) +{ + struct cache_entry *ce; + struct dir_entry *dir; + + lazy_init_name_hash(istate); + dir = find_dir_entry(istate, name, namelen); + if (dir && dir->nr) + return dir->ce; + + /* + * It might be a submodule. Unlike plain directories, which are stored + * 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); + if (ce && S_ISGITLINK(ce->ce_mode)) + return ce; + + return NULL; +} + struct cache_entry *index_name_exists(struct index_state *istate, const char *name, int namelen, int icase) { unsigned int hash = hash_name(name, namelen); struct cache_entry *ce; + assert(namelen == 0 || name[namelen - 1] != '/'); + lazy_init_name_hash(istate); ce = lookup_hash(hash, &istate->name_hash); @@ -237,29 +261,6 @@ struct cache_entry *index_name_exists(struct index_state *istate, const char *na } ce = ce->next; } - - /* - * When looking for a directory (trailing '/'), it might be a - * submodule or a directory. Despite submodules being directories, - * they are stored in the name hash without a closing slash. - * When ignore_case is 1, directories are stored in a separate hash - * table *with* their closing slash. - * - * The side effect of this storage technique is we have need to - * lookup the directory in a separate hash table, and if not found - * remove the slash from name and perform the lookup again without - * the slash. If a match is made, S_ISGITLINK(ce->mode) will be - * true. - */ - if (icase && name[namelen - 1] == '/') { - struct dir_entry *dir = find_dir_entry(istate, name, namelen); - if (dir && dir->nr) - return dir->ce; - - ce = index_name_exists(istate, name, namelen - 1, icase); - if (ce && S_ISGITLINK(ce->ce_mode)) - return ce; - } return NULL; } diff --git a/read-cache.c b/read-cache.c index 885943a..a59644a 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_name_exists(istate, ce->name, ptr - ce->name, ignore_case); + foundce = index_dir_exists(istate, ce->name, ptr - ce->name); 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 majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html