Skip to content

Commit d6a58b7

Browse files
committed
Merge branch 'es/name-hash-no-trailing-slash-in-dirs'
Clean up the internal of the name-hash mechanism used to work around case insensitivity on some filesystems to cleanly fix a long-standing API glitch where the caller of cache_name_exists() that ask about a directory with a counted string was required to have '/' at one location past the end of the string. * es/name-hash-no-trailing-slash-in-dirs: dir: revert work-around for retired dangerous behavior name-hash: stop storing trailing '/' on paths in index_state.dir_hash employ new explicit "exists in index?" API name-hash: refactor polymorphic index_name_exists()
2 parents be98d91 + de372b1 commit d6a58b7

File tree

5 files changed

+50
-51
lines changed

5 files changed

+50
-51
lines changed

cache.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -314,6 +314,8 @@ extern void free_name_hash(struct index_state *istate);
314314
#define refresh_cache(flags) refresh_index(&the_index, (flags), NULL, NULL, NULL)
315315
#define ce_match_stat(ce, st, options) ie_match_stat(&the_index, (ce), (st), (options))
316316
#define ce_modified(ce, st, options) ie_modified(&the_index, (ce), (st), (options))
317+
#define cache_dir_exists(name, namelen) index_dir_exists(&the_index, (name), (namelen))
318+
#define cache_file_exists(name, namelen, igncase) index_file_exists(&the_index, (name), (namelen), (igncase))
317319
#define cache_name_exists(name, namelen, igncase) index_name_exists(&the_index, (name), (namelen), (igncase))
318320
#define cache_name_is_other(name, namelen) index_name_is_other(&the_index, (name), (namelen))
319321
#define resolve_undo_clear() resolve_undo_clear_index(&the_index)
@@ -463,6 +465,8 @@ extern int write_index(struct index_state *, int newfd);
463465
extern int discard_index(struct index_state *);
464466
extern int unmerged_index(const struct index_state *);
465467
extern int verify_path(const char *path);
468+
extern struct cache_entry *index_dir_exists(struct index_state *istate, const char *name, int namelen);
469+
extern struct cache_entry *index_file_exists(struct index_state *istate, const char *name, int namelen, int igncase);
466470
extern struct cache_entry *index_name_exists(struct index_state *istate, const char *name, int namelen, int igncase);
467471
extern int index_name_pos(const struct index_state *, const char *name, int namelen);
468472
#define ADD_CACHE_OK_TO_ADD 1 /* Ok to add */

dir.c

Lines changed: 8 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -860,7 +860,7 @@ static struct dir_entry *dir_entry_new(const char *pathname, int len)
860860

861861
static struct dir_entry *dir_add_name(struct dir_struct *dir, const char *pathname, int len)
862862
{
863-
if (cache_name_exists(pathname, len, ignore_case))
863+
if (cache_file_exists(pathname, len, ignore_case))
864864
return NULL;
865865

866866
ALLOC_GROW(dir->entries, dir->nr+1, dir->alloc);
@@ -885,11 +885,11 @@ enum exist_status {
885885
/*
886886
* Do not use the alphabetically sorted index to look up
887887
* the directory name; instead, use the case insensitive
888-
* name hash.
888+
* directory hash.
889889
*/
890890
static enum exist_status directory_exists_in_index_icase(const char *dirname, int len)
891891
{
892-
const struct cache_entry *ce = cache_name_exists(dirname, len + 1, ignore_case);
892+
const struct cache_entry *ce = cache_dir_exists(dirname, len);
893893
unsigned char endchar;
894894

895895
if (!ce)
@@ -1071,7 +1071,7 @@ static int get_index_dtype(const char *path, int len)
10711071
int pos;
10721072
const struct cache_entry *ce;
10731073

1074-
ce = cache_name_exists(path, len, 0);
1074+
ce = cache_file_exists(path, len, 0);
10751075
if (ce) {
10761076
if (!ce_uptodate(ce))
10771077
return DT_UNKNOWN;
@@ -1131,7 +1131,7 @@ static enum path_treatment treat_one_path(struct dir_struct *dir,
11311131
int dtype, struct dirent *de)
11321132
{
11331133
int exclude;
1134-
int has_path_in_index = !!cache_name_exists(path->buf, path->len, ignore_case);
1134+
int has_path_in_index = !!cache_file_exists(path->buf, path->len, ignore_case);
11351135

11361136
if (dtype == DT_UNKNOWN)
11371137
dtype = get_dtype(de, path->buf, path->len);
@@ -1160,21 +1160,9 @@ static enum path_treatment treat_one_path(struct dir_struct *dir,
11601160
*/
11611161
if ((dir->flags & DIR_COLLECT_KILLED_ONLY) &&
11621162
(dtype == DT_DIR) &&
1163-
!has_path_in_index) {
1164-
/*
1165-
* NEEDSWORK: directory_exists_in_index_icase()
1166-
* assumes that one byte past the given path is
1167-
* readable and has '/', which needs to be fixed, but
1168-
* until then, work it around in the caller.
1169-
*/
1170-
strbuf_addch(path, '/');
1171-
if (directory_exists_in_index(path->buf, path->len - 1) ==
1172-
index_nonexistent) {
1173-
strbuf_setlen(path, path->len - 1);
1174-
return path_none;
1175-
}
1176-
strbuf_setlen(path, path->len - 1);
1177-
}
1163+
!has_path_in_index &&
1164+
(directory_exists_in_index(path->buf, path->len) == index_nonexistent))
1165+
return path_none;
11781166

11791167
exclude = is_excluded(dir, path->buf, &dtype);
11801168

name-hash.c

Lines changed: 34 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -58,9 +58,9 @@ static struct dir_entry *hash_dir_entry(struct index_state *istate,
5858
{
5959
/*
6060
* Throw each directory component in the hash for quick lookup
61-
* during a git status. Directory components are stored with their
61+
* during a git status. Directory components are stored without their
6262
* closing slash. Despite submodules being a directory, they never
63-
* reach this point, because they are stored without a closing slash
63+
* reach this point, because they are stored
6464
* in index_state.name_hash (as ordinary cache_entries).
6565
*
6666
* 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,
7878
namelen--;
7979
if (namelen <= 0)
8080
return NULL;
81+
namelen--;
8182

8283
/* lookup existing entry for that directory */
8384
dir = find_dir_entry(istate, ce->name, namelen);
@@ -97,7 +98,7 @@ static struct dir_entry *hash_dir_entry(struct index_state *istate,
9798
}
9899

99100
/* recursively add missing parent directories */
100-
dir->parent = hash_dir_entry(istate, ce, namelen - 1);
101+
dir->parent = hash_dir_entry(istate, ce, namelen);
101102
}
102103
return dir;
103104
}
@@ -222,7 +223,29 @@ static int same_name(const struct cache_entry *ce, const char *name, int namelen
222223
return slow_same_name(name, namelen, ce->name, len);
223224
}
224225

225-
struct cache_entry *index_name_exists(struct index_state *istate, const char *name, int namelen, int icase)
226+
struct cache_entry *index_dir_exists(struct index_state *istate, const char *name, int namelen)
227+
{
228+
struct cache_entry *ce;
229+
struct dir_entry *dir;
230+
231+
lazy_init_name_hash(istate);
232+
dir = find_dir_entry(istate, name, namelen);
233+
if (dir && dir->nr)
234+
return dir->ce;
235+
236+
/*
237+
* It might be a submodule. Unlike plain directories, which are stored
238+
* in the dir-hash, submodules are stored in the name-hash, so check
239+
* there, as well.
240+
*/
241+
ce = index_file_exists(istate, name, namelen, 1);
242+
if (ce && S_ISGITLINK(ce->ce_mode))
243+
return ce;
244+
245+
return NULL;
246+
}
247+
248+
struct cache_entry *index_file_exists(struct index_state *istate, const char *name, int namelen, int icase)
226249
{
227250
unsigned int hash = hash_name(name, namelen);
228251
struct cache_entry *ce;
@@ -237,32 +260,16 @@ struct cache_entry *index_name_exists(struct index_state *istate, const char *na
237260
}
238261
ce = ce->next;
239262
}
240-
241-
/*
242-
* When looking for a directory (trailing '/'), it might be a
243-
* submodule or a directory. Despite submodules being directories,
244-
* they are stored in the name hash without a closing slash.
245-
* When ignore_case is 1, directories are stored in a separate hash
246-
* table *with* their closing slash.
247-
*
248-
* The side effect of this storage technique is we have need to
249-
* lookup the directory in a separate hash table, and if not found
250-
* remove the slash from name and perform the lookup again without
251-
* the slash. If a match is made, S_ISGITLINK(ce->mode) will be
252-
* true.
253-
*/
254-
if (icase && name[namelen - 1] == '/') {
255-
struct dir_entry *dir = find_dir_entry(istate, name, namelen);
256-
if (dir && dir->nr)
257-
return dir->ce;
258-
259-
ce = index_name_exists(istate, name, namelen - 1, icase);
260-
if (ce && S_ISGITLINK(ce->ce_mode))
261-
return ce;
262-
}
263263
return NULL;
264264
}
265265

266+
struct cache_entry *index_name_exists(struct index_state *istate, const char *name, int namelen, int icase)
267+
{
268+
if (namelen > 0 && name[namelen - 1] == '/')
269+
return index_dir_exists(istate, name, namelen - 1);
270+
return index_file_exists(istate, name, namelen, icase);
271+
}
272+
266273
static int free_dir_entry(void *entry, void *unused)
267274
{
268275
struct dir_entry *dir = entry;

read-cache.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -643,7 +643,7 @@ int add_to_index(struct index_state *istate, const char *path, struct stat *st,
643643
if (*ptr == '/') {
644644
struct cache_entry *foundce;
645645
++ptr;
646-
foundce = index_name_exists(istate, ce->name, ptr - ce->name, ignore_case);
646+
foundce = index_dir_exists(istate, ce->name, ptr - ce->name - 1);
647647
if (foundce) {
648648
memcpy((void *)startPtr, foundce->name + (startPtr - ce->name), ptr - startPtr);
649649
startPtr = ptr;
@@ -652,7 +652,7 @@ int add_to_index(struct index_state *istate, const char *path, struct stat *st,
652652
}
653653
}
654654

655-
alias = index_name_exists(istate, ce->name, ce_namelen(ce), ignore_case);
655+
alias = index_file_exists(istate, ce->name, ce_namelen(ce), ignore_case);
656656
if (alias && !ce_stage(alias) && !ie_match_stat(istate, alias, st, ce_option)) {
657657
/* Nothing changed, really */
658658
free(ce);

unpack-trees.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1357,7 +1357,7 @@ static int icase_exists(struct unpack_trees_options *o, const char *name, int le
13571357
{
13581358
const struct cache_entry *src;
13591359

1360-
src = index_name_exists(o->src_index, name, len, 1);
1360+
src = index_file_exists(o->src_index, name, len, 1);
13611361
return src && !ie_match_stat(o->src_index, src, st, CE_MATCH_IGNORE_VALID|CE_MATCH_IGNORE_SKIP_WORKTREE);
13621362
}
13631363

@@ -1403,7 +1403,7 @@ static int check_ok_to_remove(const char *name, int len, int dtype,
14031403
* delete this path, which is in a subdirectory that
14041404
* is being replaced with a blob.
14051405
*/
1406-
result = index_name_exists(&o->result, name, len, 0);
1406+
result = index_file_exists(&o->result, name, len, 0);
14071407
if (result) {
14081408
if (result->ce_flags & CE_REMOVE)
14091409
return 0;

0 commit comments

Comments
 (0)