Skip to content

Commit 9210c68

Browse files
committed
Merge branch 'mt/checkout-remove-nofollow'
When "git checkout" removes a path that does not exist in the commit it is checking out, it wasn't careful enough not to follow symbolic links, which has been corrected. * mt/checkout-remove-nofollow: checkout: don't follow symlinks when removing entries symlinks: update comment on threaded_check_leading_path()
2 parents 84d06cd + fab78a0 commit 9210c68

File tree

5 files changed

+39
-33
lines changed

5 files changed

+39
-33
lines changed

cache.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1659,7 +1659,7 @@ static inline void cache_def_clear(struct cache_def *cache)
16591659

16601660
int has_symlink_leading_path(const char *name, int len);
16611661
int threaded_has_symlink_leading_path(struct cache_def *, const char *, int);
1662-
int check_leading_path(const char *name, int len);
1662+
int check_leading_path(const char *name, int len, int warn_on_lstat_err);
16631663
int has_dirs_only_path(const char *name, int len, int prefix_len);
16641664
void invalidate_lstat_cache(void);
16651665
void schedule_dir_for_removal(const char *name, int len);

entry.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -530,7 +530,7 @@ void unlink_entry(const struct cache_entry *ce)
530530
submodule_move_head(ce->name, "HEAD", NULL,
531531
SUBMODULE_MOVE_HEAD_FORCE);
532532
}
533-
if (!check_leading_path(ce->name, ce_namelen(ce)))
533+
if (check_leading_path(ce->name, ce_namelen(ce), 1) >= 0)
534534
return;
535535
if (remove_or_warn(ce->ce_mode, ce->name))
536536
return;

symlinks.c

Lines changed: 24 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#include "cache.h"
22

3-
static int threaded_check_leading_path(struct cache_def *cache, const char *name, int len);
3+
static int threaded_check_leading_path(struct cache_def *cache, const char *name,
4+
int len, int warn_on_lstat_err);
45
static int threaded_has_dirs_only_path(struct cache_def *cache, const char *name, int len, int prefix_len);
56

67
/*
@@ -72,7 +73,7 @@ static int lstat_cache_matchlen(struct cache_def *cache,
7273
int prefix_len_stat_func)
7374
{
7475
int match_len, last_slash, last_slash_dir, previous_slash;
75-
int save_flags, ret;
76+
int save_flags, ret, saved_errno = 0;
7677
struct stat st;
7778

7879
if (cache->track_flags != track_flags ||
@@ -139,6 +140,7 @@ static int lstat_cache_matchlen(struct cache_def *cache,
139140

140141
if (ret) {
141142
*ret_flags = FL_LSTATERR;
143+
saved_errno = errno;
142144
if (errno == ENOENT)
143145
*ret_flags |= FL_NOENT;
144146
} else if (S_ISDIR(st.st_mode)) {
@@ -180,6 +182,8 @@ static int lstat_cache_matchlen(struct cache_def *cache,
180182
} else {
181183
reset_lstat_cache(cache);
182184
}
185+
if (saved_errno)
186+
errno = saved_errno;
183187
return match_len;
184188
}
185189

@@ -202,57 +206,47 @@ int threaded_has_symlink_leading_path(struct cache_def *cache, const char *name,
202206
return lstat_cache(cache, name, len, FL_SYMLINK|FL_DIR, USE_ONLY_LSTAT) & FL_SYMLINK;
203207
}
204208

205-
/*
206-
* Return non-zero if path 'name' has a leading symlink component
207-
*/
208209
int has_symlink_leading_path(const char *name, int len)
209210
{
210211
return threaded_has_symlink_leading_path(&default_cache, name, len);
211212
}
212213

213-
/*
214-
* Return zero if path 'name' has a leading symlink component or
215-
* if some leading path component does not exists.
216-
*
217-
* Return -1 if leading path exists and is a directory.
218-
*
219-
* Return path length if leading path exists and is neither a
220-
* directory nor a symlink.
221-
*/
222-
int check_leading_path(const char *name, int len)
214+
int check_leading_path(const char *name, int len, int warn_on_lstat_err)
223215
{
224-
return threaded_check_leading_path(&default_cache, name, len);
216+
return threaded_check_leading_path(&default_cache, name, len,
217+
warn_on_lstat_err);
225218
}
226219

227220
/*
228-
* Return zero if path 'name' has a leading symlink component or
229-
* if some leading path component does not exists.
221+
* Return zero if some leading path component of 'name' does not exist.
230222
*
231223
* Return -1 if leading path exists and is a directory.
232224
*
233-
* Return path length if leading path exists and is neither a
234-
* directory nor a symlink.
225+
* Return the length of a leading component if it either exists but it's not a
226+
* directory, or if we were unable to lstat() it. If warn_on_lstat_err is true,
227+
* also emit a warning for this error.
235228
*/
236-
static int threaded_check_leading_path(struct cache_def *cache, const char *name, int len)
229+
static int threaded_check_leading_path(struct cache_def *cache, const char *name,
230+
int len, int warn_on_lstat_err)
237231
{
238232
int flags;
239233
int match_len = lstat_cache_matchlen(cache, name, len, &flags,
240234
FL_SYMLINK|FL_NOENT|FL_DIR, USE_ONLY_LSTAT);
235+
int saved_errno = errno;
236+
241237
if (flags & FL_NOENT)
242238
return 0;
243239
else if (flags & FL_DIR)
244240
return -1;
245-
else
246-
return match_len;
241+
if (warn_on_lstat_err && (flags & FL_LSTATERR)) {
242+
char *path = xmemdupz(name, match_len);
243+
errno = saved_errno;
244+
warning_errno(_("failed to lstat '%s'"), path);
245+
free(path);
246+
}
247+
return match_len;
247248
}
248249

249-
/*
250-
* Return non-zero if all path components of 'name' exists as a
251-
* directory. If prefix_len > 0, we will test with the stat()
252-
* function instead of the lstat() function for a prefix length of
253-
* 'prefix_len', thus we then allow for symlinks in the prefix part as
254-
* long as those points to real existing directories.
255-
*/
256250
int has_dirs_only_path(const char *name, int len, int prefix_len)
257251
{
258252
return threaded_has_dirs_only_path(&default_cache, name, len, prefix_len);

t/t2021-checkout-overwrite.sh

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,4 +51,16 @@ test_expect_success SYMLINKS 'the symlink remained' '
5151
test -h a/b
5252
'
5353

54+
test_expect_success SYMLINKS 'checkout -f must not follow symlinks when removing entries' '
55+
git checkout -f start &&
56+
mkdir dir &&
57+
>dir/f &&
58+
git add dir/f &&
59+
git commit -m "add dir/f" &&
60+
mv dir untracked &&
61+
ln -s untracked dir &&
62+
git checkout -f HEAD~ &&
63+
test_path_is_file untracked/f
64+
'
65+
5466
test_done

unpack-trees.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2097,7 +2097,7 @@ static int verify_absent_1(const struct cache_entry *ce,
20972097
if (o->index_only || o->reset || !o->update)
20982098
return 0;
20992099

2100-
len = check_leading_path(ce->name, ce_namelen(ce));
2100+
len = check_leading_path(ce->name, ce_namelen(ce), 0);
21012101
if (!len)
21022102
return 0;
21032103
else if (len > 0) {

0 commit comments

Comments
 (0)