From df292c791ab790340cc9e3577a073bcb9d1900ea Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Fri, 21 Mar 2008 15:53:00 -0700 Subject: Make "index_name_exists()" return the cache_entry it found This allows verify_absent() in unpack_trees() to use the hash chains rather than looking it up using the binary search. Perhaps more importantly, it's also going to be useful for the next phase, where we actually start looking at the cache entry when we do case-insensitive lookups and checking the result. Signed-off-by: Linus Torvalds Signed-off-by: Junio C Hamano --- unpack-trees.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'unpack-trees.c') diff --git a/unpack-trees.c b/unpack-trees.c index a59f47557a..ca4c845beb 100644 --- a/unpack-trees.c +++ b/unpack-trees.c @@ -538,6 +538,7 @@ static int verify_absent(struct cache_entry *ce, const char *action, if (!lstat(ce->name, &st)) { int cnt; int dtype = ce_to_dtype(ce); + struct cache_entry *result; if (o->dir && excluded(o->dir, ce->name, &dtype)) /* @@ -581,10 +582,9 @@ static int verify_absent(struct cache_entry *ce, const char *action, * delete this path, which is in a subdirectory that * is being replaced with a blob. */ - cnt = index_name_pos(&o->result, ce->name, strlen(ce->name)); - if (0 <= cnt) { - struct cache_entry *ce = o->result.cache[cnt]; - if (ce->ce_flags & CE_REMOVE) + result = index_name_exists(&o->result, ce->name, ce_namelen(ce)); + if (result) { + if (result->ce_flags & CE_REMOVE) return 0; } -- cgit v1.2.1 From cd2fef59edf72a1d9792d9cb72aae1e6f6c7b1d4 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Fri, 21 Mar 2008 15:55:19 -0700 Subject: Make hash_name_lookup able to do case-independent lookups Right now nobody uses it, but "index_name_exists()" gets a flag so you can enable it on a case-by-case basis. Signed-off-by: Linus Torvalds Signed-off-by: Junio C Hamano --- unpack-trees.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'unpack-trees.c') diff --git a/unpack-trees.c b/unpack-trees.c index ca4c845beb..bf7d8f6c5c 100644 --- a/unpack-trees.c +++ b/unpack-trees.c @@ -582,7 +582,7 @@ static int verify_absent(struct cache_entry *ce, const char *action, * delete this path, which is in a subdirectory that * is being replaced with a blob. */ - result = index_name_exists(&o->result, ce->name, ce_namelen(ce)); + result = index_name_exists(&o->result, ce->name, ce_namelen(ce), 0); if (result) { if (result->ce_flags & CE_REMOVE) return 0; -- cgit v1.2.1 From 32260ad5dbc3100ebb5e05432198888bfbe600f8 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Sat, 22 Mar 2008 09:35:59 -0700 Subject: Make branch merging aware of underlying case-insensitive filsystems If we find an unexpected file, see if that filename perhaps exists in a case-insensitive way in the index, and whether the file matches that. If so, ignore it as a known pre-existing file of a different name. Signed-off-by: Linus Torvalds Signed-off-by: Junio C Hamano --- unpack-trees.c | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) (limited to 'unpack-trees.c') diff --git a/unpack-trees.c b/unpack-trees.c index bf7d8f6c5c..95d3413ae5 100644 --- a/unpack-trees.c +++ b/unpack-trees.c @@ -520,6 +520,22 @@ static int verify_clean_subdirectory(struct cache_entry *ce, const char *action, return cnt; } +/* + * This gets called when there was no index entry for the tree entry 'dst', + * but we found a file in the working tree that 'lstat()' said was fine, + * and we're on a case-insensitive filesystem. + * + * See if we can find a case-insensitive match in the index that also + * matches the stat information, and assume it's that other file! + */ +static int icase_exists(struct unpack_trees_options *o, struct cache_entry *dst, struct stat *st) +{ + struct cache_entry *src; + + src = index_name_exists(o->src_index, dst->name, ce_namelen(dst), 1); + return src && !ie_match_stat(o->src_index, src, st, CE_MATCH_IGNORE_VALID); +} + /* * We do not want to remove or overwrite a working tree file that * is not tracked, unless it is ignored. @@ -540,6 +556,16 @@ static int verify_absent(struct cache_entry *ce, const char *action, int dtype = ce_to_dtype(ce); struct cache_entry *result; + /* + * It may be that the 'lstat()' succeeded even though + * target 'ce' was absent, because there is an old + * entry that is different only in case.. + * + * Ignore that lstat() if it matches. + */ + if (ignore_case && icase_exists(o, ce, &st)) + return 0; + if (o->dir && excluded(o->dir, ce->name, &dtype)) /* * ce->name is explicitly excluded, so it is Ok to -- cgit v1.2.1 From 1fa6ead492c81bffdbe336373e5b162d3b5ac6d3 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Sat, 22 Mar 2008 09:48:41 -0700 Subject: Make unpack-tree update removed files before any updated files This is immaterial on sane filesystems, but if you have a broken (aka case-insensitive) filesystem, and the objective is to remove the file 'abc' and replace it with the file 'Abc', then we must make sure to do the removal first. Otherwise, you'd first update the file 'Abc' - which would just overwrite the file 'abc' due to the broken case-insensitive filesystem - and then remove file 'abc' - which would now brokenly remove the just updated file 'Abc' on that broken filesystem. By doing removals first, this won't happen. Signed-off-by: Linus Torvalds Signed-off-by: Junio C Hamano --- unpack-trees.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'unpack-trees.c') diff --git a/unpack-trees.c b/unpack-trees.c index 95d3413ae5..feae846226 100644 --- a/unpack-trees.c +++ b/unpack-trees.c @@ -79,16 +79,21 @@ static int check_updates(struct unpack_trees_options *o) for (i = 0; i < index->cache_nr; i++) { struct cache_entry *ce = index->cache[i]; - if (ce->ce_flags & (CE_UPDATE | CE_REMOVE)) - display_progress(progress, ++cnt); if (ce->ce_flags & CE_REMOVE) { + display_progress(progress, ++cnt); if (o->update) unlink_entry(ce->name, last_symlink); remove_index_entry_at(&o->result, i); i--; continue; } + } + + for (i = 0; i < index->cache_nr; i++) { + struct cache_entry *ce = index->cache[i]; + if (ce->ce_flags & CE_UPDATE) { + display_progress(progress, ++cnt); ce->ce_flags &= ~CE_UPDATE; if (o->update) { errs |= checkout_entry(ce, &state, NULL); -- cgit v1.2.1 From c40641b77b0274186fd1b327d5dc3246f814aaaf Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Fri, 9 May 2008 09:21:07 -0700 Subject: Optimize symlink/directory detection This is the base for making symlink detection in the middle fo a pathname saner and (much) more efficient. Under various loads, we want to verify that the full path leading up to a filename is a real directory tree, and that when we successfully do an 'lstat()' on a filename, we don't get a false positive due to a symlink in the middle of the path that git should have seen as a symlink, not as a normal path component. The 'has_symlink_leading_path()' function already did this, and cached a single level of symlink information, but didn't cache the _lack_ of a symlink, so the normal behaviour was actually the wrong way around, and we ended up doing an 'lstat()' on each path component to check that it was a real directory. This caches the last detected full directory and symlink entries, and speeds up especially deep directory structures a lot by avoiding to lstat() all the directories leading up to each entry in the index. [ This can - and should - probably be extended upon so that we eventually never do a bare 'lstat()' on any path entries at *all* when checking the index, but always check the full path carefully. Right now we do not generally check the whole path for all our normal quick index revalidation. We should also make sure that we're careful about all the invalidation, ie when we remove a link and replace it by a directory we should invalidate the symlink cache if it matches (and vice versa for the directory cache). But regardless, the basic function needs to be sane to do that. The old 'has_symlink_leading_path()' was not capable enough - or indeed the code readable enough - to really do that sanely. So I'm pushing this as not just an optimization, but as a base for further work. ] Signed-off-by: Linus Torvalds Signed-off-by: Junio C Hamano --- unpack-trees.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) (limited to 'unpack-trees.c') diff --git a/unpack-trees.c b/unpack-trees.c index feae846226..1ab28fda45 100644 --- a/unpack-trees.c +++ b/unpack-trees.c @@ -26,11 +26,12 @@ static void add_entry(struct unpack_trees_options *o, struct cache_entry *ce, * directories, in case this unlink is the removal of the * last entry in the directory -- empty directories are removed. */ -static void unlink_entry(char *name, char *last_symlink) +static void unlink_entry(struct cache_entry *ce) { char *cp, *prev; + char *name = ce->name; - if (has_symlink_leading_path(name, last_symlink)) + if (has_symlink_leading_path(ce_namelen(ce), ce->name)) return; if (unlink(name)) return; @@ -58,7 +59,6 @@ static int check_updates(struct unpack_trees_options *o) { unsigned cnt = 0, total = 0; struct progress *progress = NULL; - char last_symlink[PATH_MAX]; struct index_state *index = &o->result; int i; int errs = 0; @@ -75,14 +75,13 @@ static int check_updates(struct unpack_trees_options *o) cnt = 0; } - *last_symlink = '\0'; for (i = 0; i < index->cache_nr; i++) { struct cache_entry *ce = index->cache[i]; if (ce->ce_flags & CE_REMOVE) { display_progress(progress, ++cnt); if (o->update) - unlink_entry(ce->name, last_symlink); + unlink_entry(ce); remove_index_entry_at(&o->result, i); i--; continue; @@ -97,7 +96,6 @@ static int check_updates(struct unpack_trees_options *o) ce->ce_flags &= ~CE_UPDATE; if (o->update) { errs |= checkout_entry(ce, &state, NULL); - *last_symlink = '\0'; } } } @@ -553,7 +551,7 @@ static int verify_absent(struct cache_entry *ce, const char *action, if (o->index_only || o->reset || !o->update) return 0; - if (has_symlink_leading_path(ce->name, NULL)) + if (has_symlink_leading_path(ce_namelen(ce), ce->name)) return 0; if (!lstat(ce->name, &st)) { -- cgit v1.2.1