diff options
author | Clemens Buchacher <drizzd@aon.at> | 2010-10-09 15:53:00 +0200 |
---|---|---|
committer | Junio C Hamano <gitster@pobox.com> | 2010-10-13 14:34:09 -0700 |
commit | f66caaf9c8e0feb840a439a58e165b963aec79cf (patch) | |
tree | 0c0f99ba5a023b7330b949045ba188e3a7cc1fde | |
parent | 4856ff2a195e558c82ca3cab636c4d4fbef148e3 (diff) | |
download | git-f66caaf9c8e0feb840a439a58e165b963aec79cf.tar.gz |
do not overwrite files in leading path
If the work tree contains an untracked file x, and
unpack-trees wants to checkout a path x/*, the
file x is removed unconditionally.
Instead, apply the same checks that are normally
used for untracked files, and abort if the file
cannot be removed.
Signed-off-by: Clemens Buchacher <drizzd@aon.at>
-rw-r--r-- | cache.h | 2 | ||||
-rw-r--r-- | symlinks.c | 21 | ||||
-rwxr-xr-x | t/t7607-merge-overwrite.sh | 2 | ||||
-rw-r--r-- | unpack-trees.c | 16 |
4 files changed, 31 insertions, 10 deletions
@@ -859,7 +859,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 check_leading_path(const char *name, int 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 b7343ebd62..3cacebd91a 100644 --- a/symlinks.c +++ b/symlinks.c @@ -209,15 +209,26 @@ int has_symlink_leading_path(const char *name, int len) } /* - * Return non-zero if path 'name' has a leading symlink component or + * Return zero if path 'name' has a leading symlink component or * if some leading path component does not exists. + * + * Return -1 if leading path exists and is a directory. + * + * Return path length if leading path exists and is neither a + * directory nor a symlink. */ -int has_symlink_or_noent_leading_path(const char *name, int len) +int check_leading_path(const char *name, int len) { struct cache_def *cache = &default_cache; /* FIXME */ - return lstat_cache(cache, name, len, - FL_SYMLINK|FL_NOENT|FL_DIR, USE_ONLY_LSTAT) & - (FL_SYMLINK|FL_NOENT); + int flags; + int match_len = lstat_cache_matchlen(cache, name, len, &flags, + FL_SYMLINK|FL_NOENT|FL_DIR, USE_ONLY_LSTAT); + if (flags & (FL_SYMLINK|FL_NOENT)) + return 0; + else if (flags & FL_DIR) + return -1; + else + return match_len; } /* diff --git a/t/t7607-merge-overwrite.sh b/t/t7607-merge-overwrite.sh index 77fcaa2e31..e49dd8023c 100755 --- a/t/t7607-merge-overwrite.sh +++ b/t/t7607-merge-overwrite.sh @@ -100,7 +100,7 @@ test_expect_success 'will not overwrite untracked subtree' ' test_cmp important sub/f/important ' -test_expect_failure 'will not overwrite untracked file in leading path' ' +test_expect_success 'will not overwrite untracked file in leading path' ' git reset --hard c0 && rm -rf sub && cp important sub && diff --git a/unpack-trees.c b/unpack-trees.c index df1c9209d4..6816113420 100644 --- a/unpack-trees.c +++ b/unpack-trees.c @@ -182,7 +182,7 @@ static void display_error_msgs(struct unpack_trees_options *o) */ static void unlink_entry(struct cache_entry *ce) { - if (has_symlink_or_noent_leading_path(ce->name, ce_namelen(ce))) + if (!check_leading_path(ce->name, ce_namelen(ce))) return; if (remove_or_warn(ce->ce_mode, ce->name)) return; @@ -1194,18 +1194,28 @@ static int verify_absent_1(struct cache_entry *ce, enum unpack_trees_error_types error_type, struct unpack_trees_options *o) { + int len; struct stat st; if (o->index_only || o->reset || !o->update) return 0; - if (has_symlink_or_noent_leading_path(ce->name, ce_namelen(ce))) + len = check_leading_path(ce->name, ce_namelen(ce)); + if (!len) return 0; + else if (len > 0) { + char path[PATH_MAX + 1]; + memcpy(path, ce->name, len); + path[len] = 0; + lstat(path, &st); - if (!lstat(ce->name, &st)) + return check_ok_to_remove(path, len, DT_UNKNOWN, NULL, &st, + error_type, o); + } else if (!lstat(ce->name, &st)) return check_ok_to_remove(ce->name, ce_namelen(ce), ce_to_dtype(ce), ce, &st, error_type, o); + return 0; } |