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 ofdiff --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