On Fri, Aug 20, 2010 at 01:35:32PM -0700, Junio C Hamano wrote: > > $ echo foo >po > $ git checkout pu > > should error out, as "po" is a directory that has tracked contents, and we > never said the untracked regular file "po" is trashable, but the above > sequence happily checks the branch out. Looks like this case is simply overlooked in verify_absent_1(). The following takes the existing lstat_cache() code and deals with the FL_ERR case, which is when there is a file in the way of the leading path. The patch below fixes the issue and passes the test suite, but it's lacking in various ways and I am probably breaking something in lstat_cache(), which I do not completely understand yet. So it's going to take some more work to fix this properly. Clemens cache.h | 1 + symlinks.c | 20 ++++++++++++++++++-- unpack-trees.c | 25 ++++++++++++++++++++++++- 3 files changed, 43 insertions(+), 3 deletions(-) diff --git a/cache.h b/cache.h index dd1b8f7..5a8a99d 100644 --- a/cache.h +++ b/cache.h @@ -850,6 +850,7 @@ struct cache_def { extern int has_symlink_leading_path(const char *name, int len); extern int threaded_has_symlink_leading_path(struct cache_def *, const char *, int); extern int has_symlink_or_noent_leading_path(const char *name, int len); +extern int find_leading_path(const char *name, int len, const char **path, int *path_len); extern int has_dirs_only_path(const char *name, int len, int prefix_len); extern void schedule_dir_for_removal(const char *name, int len); extern void remove_scheduled_dirs(void); diff --git a/symlinks.c b/symlinks.c index 8860120..3f78168 100644 --- a/symlinks.c +++ b/symlinks.c @@ -152,7 +152,7 @@ static int lstat_cache(struct cache_def *cache, const char *name, int len, * path types, FL_NOENT, FL_SYMLINK and FL_DIR, can be cached * for the moment! */ - save_flags = ret_flags & track_flags & (FL_NOENT|FL_SYMLINK); + save_flags = ret_flags & track_flags & (FL_NOENT|FL_SYMLINK|FL_ERR); if (save_flags && last_slash > 0 && last_slash <= PATH_MAX) { cache->path[last_slash] = '\0'; cache->len = last_slash; @@ -199,7 +199,7 @@ int has_symlink_leading_path(const char *name, int len) /* * Return non-zero if path 'name' has a leading symlink component or - * if some leading path component does not exists. + * if some leading path component does not exist. */ int has_symlink_or_noent_leading_path(const char *name, int len) { @@ -210,6 +210,22 @@ int has_symlink_or_noent_leading_path(const char *name, int len) } /* + * Stat for leading path. + */ +int find_leading_path(const char *name, int len, const char **path, int *path_len) +{ + struct cache_def *cache = &default_cache; /* FIXME */ + int flags = lstat_cache(cache, name, len, + FL_SYMLINK|FL_NOENT|FL_DIR|FL_ERR, USE_ONLY_LSTAT); + *path = cache->path; + *path_len = cache->len; + if (flags & FL_ERR) + return -1; + else + return flags & (FL_SYMLINK|FL_NOENT); +} + +/* * Return non-zero if all path components of 'name' exists as a * directory. If prefix_len > 0, we will test with the stat() * function instead of the lstat() function for a prefix length of diff --git a/unpack-trees.c b/unpack-trees.c index 8cf0da3..250ed7c 100644 --- a/unpack-trees.c +++ b/unpack-trees.c @@ -1016,12 +1016,35 @@ static int verify_absent_1(struct cache_entry *ce, const char *action, const char *error_msg) { struct stat st; + const char *path; + int path_len; + int ret; if (o->index_only || o->reset || !o->update) return 0; - if (has_symlink_or_noent_leading_path(ce->name, ce_namelen(ce))) + ret = find_leading_path(ce->name, ce_namelen(ce), &path, &path_len); + if (ret > 0) return 0; + else if (ret < 0) { + struct cache_entry *result; + + /* FIXME: respect ignores etc. as below */ + + /* + * The previous round may already have decided to + * delete this path, which is in a subdirectory that + * is being replaced with a blob. + */ + result = index_name_exists(&o->result, path, path_len, 0); + if (result) { + if (result->ce_flags & CE_REMOVE) + return 0; + } + + return o->gently ? -1 : + error(ERRORMSG(o, would_lose_untracked), path, action); + } if (!lstat(ce->name, &st)) { int dtype = ce_to_dtype(ce); -- 1.7.2.1.1.g202c