Skip to content

Commit 7e064cc

Browse files
dschomjcheetham
authored andcommitted
Merge branch 'dont-clean-junctions'
This topic branch teaches `git clean` to respect NTFS junctions and Unix bind mounts: it will now stop at those boundaries. Signed-off-by: Johannes Schindelin <[email protected]>
2 parents 0c218e6 + a29dc8a commit 7e064cc

File tree

7 files changed

+107
-0
lines changed

7 files changed

+107
-0
lines changed

builtin/clean.c

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,10 @@ static const char *msg_remove = N_("Removing %s\n");
3838
static const char *msg_would_remove = N_("Would remove %s\n");
3939
static const char *msg_skip_git_dir = N_("Skipping repository %s\n");
4040
static const char *msg_would_skip_git_dir = N_("Would skip repository %s\n");
41+
#ifndef CAN_UNLINK_MOUNT_POINTS
42+
static const char *msg_skip_mount_point = N_("Skipping mount point %s\n");
43+
static const char *msg_would_skip_mount_point = N_("Would skip mount point %s\n");
44+
#endif
4145
static const char *msg_warn_remove_failed = N_("failed to remove %s");
4246
static const char *msg_warn_lstat_failed = N_("could not lstat %s\n");
4347
static const char *msg_skip_cwd = N_("Refusing to remove current working directory\n");
@@ -179,6 +183,29 @@ static int remove_dirs(struct strbuf *path, const char *prefix, int force_flag,
179183
goto out;
180184
}
181185

186+
if (is_mount_point(path)) {
187+
#ifndef CAN_UNLINK_MOUNT_POINTS
188+
if (!quiet) {
189+
quote_path(path->buf, prefix, &quoted, 0);
190+
printf(dry_run ?
191+
_(msg_would_skip_mount_point) :
192+
_(msg_skip_mount_point), quoted.buf);
193+
}
194+
*dir_gone = 0;
195+
#else
196+
if (!dry_run && unlink(path->buf)) {
197+
int saved_errno = errno;
198+
quote_path(path->buf, prefix, &quoted, 0);
199+
errno = saved_errno;
200+
warning_errno(_(msg_warn_remove_failed), quoted.buf);
201+
*dir_gone = 0;
202+
ret = -1;
203+
}
204+
#endif
205+
206+
goto out;
207+
}
208+
182209
dir = opendir(path->buf);
183210
if (!dir) {
184211
/* an empty dir could be removed even if it is unreadble */

compat/mingw.c

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2520,6 +2520,28 @@ pid_t waitpid(pid_t pid, int *status, int options)
25202520
return -1;
25212521
}
25222522

2523+
int mingw_is_mount_point(struct strbuf *path)
2524+
{
2525+
WIN32_FIND_DATAW findbuf = { 0 };
2526+
HANDLE handle;
2527+
wchar_t wfilename[MAX_PATH];
2528+
int wlen = xutftowcs_path(wfilename, path->buf);
2529+
if (wlen < 0)
2530+
die(_("could not get long path for '%s'"), path->buf);
2531+
2532+
/* remove trailing slash, if any */
2533+
if (wlen > 0 && wfilename[wlen - 1] == L'/')
2534+
wfilename[--wlen] = L'\0';
2535+
2536+
handle = FindFirstFileW(wfilename, &findbuf);
2537+
if (handle == INVALID_HANDLE_VALUE)
2538+
return 0;
2539+
FindClose(handle);
2540+
2541+
return (findbuf.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) &&
2542+
(findbuf.dwReserved0 == IO_REPARSE_TAG_MOUNT_POINT);
2543+
}
2544+
25232545
int xutftowcsn(wchar_t *wcs, const char *utfs, size_t wcslen, int utflen)
25242546
{
25252547
int upos = 0, wpos = 0;

compat/mingw.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -449,6 +449,10 @@ static inline void convert_slashes(char *path)
449449
if (*path == '\\')
450450
*path = '/';
451451
}
452+
struct strbuf;
453+
int mingw_is_mount_point(struct strbuf *path);
454+
#define is_mount_point mingw_is_mount_point
455+
#define CAN_UNLINK_MOUNT_POINTS 1
452456
#define PATH_SEP ';'
453457
char *mingw_query_user_email(void);
454458
#define query_user_email mingw_query_user_email

git-compat-util.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -575,6 +575,10 @@ static inline int git_has_dir_sep(const char *path)
575575
#define has_dir_sep(path) git_has_dir_sep(path)
576576
#endif
577577

578+
#ifndef is_mount_point
579+
#define is_mount_point is_mount_point_via_stat
580+
#endif
581+
578582
#ifndef query_user_email
579583
#define query_user_email() NULL
580584
#endif

path.c

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1313,6 +1313,45 @@ char *strip_path_suffix(const char *path, const char *suffix)
13131313
return offset == -1 ? NULL : xstrndup(path, offset);
13141314
}
13151315

1316+
int is_mount_point_via_stat(struct strbuf *path)
1317+
{
1318+
size_t len = path->len;
1319+
unsigned int current_dev;
1320+
struct stat st;
1321+
1322+
if (!strcmp("/", path->buf))
1323+
return 1;
1324+
1325+
strbuf_addstr(path, "/.");
1326+
if (lstat(path->buf, &st)) {
1327+
/*
1328+
* If we cannot access the current directory, we cannot say
1329+
* that it is a bind mount.
1330+
*/
1331+
strbuf_setlen(path, len);
1332+
return 0;
1333+
}
1334+
current_dev = st.st_dev;
1335+
1336+
/* Now look at the parent directory */
1337+
strbuf_addch(path, '.');
1338+
if (lstat(path->buf, &st)) {
1339+
/*
1340+
* If we cannot access the parent directory, we cannot say
1341+
* that it is a bind mount.
1342+
*/
1343+
strbuf_setlen(path, len);
1344+
return 0;
1345+
}
1346+
strbuf_setlen(path, len);
1347+
1348+
/*
1349+
* If the device ID differs between current and parent directory,
1350+
* then it is a bind mount.
1351+
*/
1352+
return current_dev != st.st_dev;
1353+
}
1354+
13161355
int daemon_avoid_alias(const char *p)
13171356
{
13181357
int sl, ndot;

path.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,7 @@ int normalize_path_copy_len(char *dst, const char *src, int *prefix_len);
193193
int normalize_path_copy(char *dst, const char *src);
194194
int longest_ancestor_length(const char *path, struct string_list *prefixes);
195195
char *strip_path_suffix(const char *path, const char *suffix);
196+
int is_mount_point_via_stat(struct strbuf *path);
196197
int daemon_avoid_alias(const char *path);
197198

198199
/*

t/t7300-clean.sh

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -789,4 +789,14 @@ test_expect_success 'traverse into directories that may have ignored entries' '
789789
)
790790
'
791791

792+
test_expect_success MINGW 'clean does not traverse mount points' '
793+
mkdir target &&
794+
>target/dont-clean-me &&
795+
git init with-mountpoint &&
796+
cmd //c "mklink /j with-mountpoint\\mountpoint target" &&
797+
git -C with-mountpoint clean -dfx &&
798+
test_path_is_missing with-mountpoint/mountpoint &&
799+
test_path_is_file target/dont-clean-me
800+
'
801+
792802
test_done

0 commit comments

Comments
 (0)