Skip to content

Commit 006b2d3

Browse files
dschoGit for Windows Build Agent
authored and
Git for Windows Build Agent
committed
Merge pull request #2504 from dscho/access-repo-via-junction
Handle `git add <file>` where <file> traverses an NTFS junction
2 parents 0c298ab + fc0d027 commit 006b2d3

File tree

7 files changed

+112
-0
lines changed

7 files changed

+112
-0
lines changed

abspath.c

+3
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,9 @@ static char *strbuf_realpath_1(struct strbuf *resolved, const char *path,
9393
goto error_out;
9494
}
9595

96+
if (platform_strbuf_realpath(resolved, path))
97+
return resolved->buf;
98+
9699
strbuf_addstr(&remaining, path);
97100
get_root_part(resolved, &remaining);
98101

compat/mingw.c

+76
Original file line numberDiff line numberDiff line change
@@ -1238,6 +1238,82 @@ struct tm *localtime_r(const time_t *timep, struct tm *result)
12381238
}
12391239
#endif
12401240

1241+
char *mingw_strbuf_realpath(struct strbuf *resolved, const char *path)
1242+
{
1243+
wchar_t wpath[MAX_PATH];
1244+
HANDLE h;
1245+
DWORD ret;
1246+
int len;
1247+
const char *last_component = NULL;
1248+
char *append = NULL;
1249+
1250+
if (xutftowcs_path(wpath, path) < 0)
1251+
return NULL;
1252+
1253+
h = CreateFileW(wpath, 0,
1254+
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
1255+
OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
1256+
1257+
/*
1258+
* strbuf_realpath() allows the last path component to not exist. If
1259+
* that is the case, now it's time to try without last component.
1260+
*/
1261+
if (h == INVALID_HANDLE_VALUE &&
1262+
GetLastError() == ERROR_FILE_NOT_FOUND) {
1263+
/* cut last component off of `wpath` */
1264+
wchar_t *p = wpath + wcslen(wpath);
1265+
1266+
while (p != wpath)
1267+
if (*(--p) == L'/' || *p == L'\\')
1268+
break; /* found start of last component */
1269+
1270+
if (p != wpath && (last_component = find_last_dir_sep(path))) {
1271+
append = xstrdup(last_component + 1); /* skip directory separator */
1272+
/*
1273+
* Do not strip the trailing slash at the drive root, otherwise
1274+
* the path would be e.g. `C:` (which resolves to the
1275+
* _current_ directory on that drive).
1276+
*/
1277+
if (p[-1] == L':')
1278+
p[1] = L'\0';
1279+
else
1280+
*p = L'\0';
1281+
h = CreateFileW(wpath, 0, FILE_SHARE_READ |
1282+
FILE_SHARE_WRITE | FILE_SHARE_DELETE,
1283+
NULL, OPEN_EXISTING,
1284+
FILE_FLAG_BACKUP_SEMANTICS, NULL);
1285+
}
1286+
}
1287+
1288+
if (h == INVALID_HANDLE_VALUE) {
1289+
realpath_failed:
1290+
FREE_AND_NULL(append);
1291+
return NULL;
1292+
}
1293+
1294+
ret = GetFinalPathNameByHandleW(h, wpath, ARRAY_SIZE(wpath), 0);
1295+
CloseHandle(h);
1296+
if (!ret || ret >= ARRAY_SIZE(wpath))
1297+
goto realpath_failed;
1298+
1299+
len = wcslen(wpath) * 3;
1300+
strbuf_grow(resolved, len);
1301+
len = xwcstoutf(resolved->buf, normalize_ntpath(wpath), len);
1302+
if (len < 0)
1303+
goto realpath_failed;
1304+
resolved->len = len;
1305+
1306+
if (append) {
1307+
/* Use forward-slash, like `normalize_ntpath()` */
1308+
strbuf_complete(resolved, '/');
1309+
strbuf_addstr(resolved, append);
1310+
FREE_AND_NULL(append);
1311+
}
1312+
1313+
return resolved->buf;
1314+
1315+
}
1316+
12411317
char *mingw_getcwd(char *pointer, int len)
12421318
{
12431319
wchar_t cwd[MAX_PATH], wpointer[MAX_PATH];

compat/mingw.h

+3
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,9 @@ int mingw_is_mount_point(struct strbuf *path);
4343
#define PATH_SEP ';'
4444
char *mingw_query_user_email(void);
4545
#define query_user_email mingw_query_user_email
46+
struct strbuf;
47+
char *mingw_strbuf_realpath(struct strbuf *resolved, const char *path);
48+
#define platform_strbuf_realpath mingw_strbuf_realpath
4649

4750
/**
4851
* Verifies that the specified path is owned by the user running the

git-compat-util.h

+4
Original file line numberDiff line numberDiff line change
@@ -401,6 +401,10 @@ static inline int git_has_dir_sep(const char *path)
401401
#define query_user_email() NULL
402402
#endif
403403

404+
#ifndef platform_strbuf_realpath
405+
#define platform_strbuf_realpath(resolved, path) NULL
406+
#endif
407+
404408
#ifdef __TANDEM
405409
#include <floss.h(floss_execl,floss_execlp,floss_execv,floss_execvp)>
406410
#include <floss.h(floss_getpwuid)>

t/t0060-path-utils.sh

+8
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,14 @@ test_expect_success SYMLINKS 'real path works on symlinks' '
281281
test_cmp expect actual
282282
'
283283

284+
test_expect_success MINGW 'real path works near drive root' '
285+
# we need a non-existing path at the drive root; simply skip if C:/xyz exists
286+
if test ! -e C:/xyz
287+
then
288+
test C:/xyz = $(test-tool path-utils real_path C:/xyz)
289+
fi
290+
'
291+
284292
test_expect_success SYMLINKS 'prefix_path works with absolute paths to work tree symlinks' '
285293
ln -s target symlink &&
286294
echo "symlink" >expect &&

t/t3700-add.sh

+11
Original file line numberDiff line numberDiff line change
@@ -548,4 +548,15 @@ test_expect_success CASE_INSENSITIVE_FS 'path is case-insensitive' '
548548
git add "$downcased"
549549
'
550550

551+
test_expect_success MINGW 'can add files via NTFS junctions' '
552+
test_when_finished "cmd //c rmdir junction && rm -rf target" &&
553+
test_create_repo target &&
554+
cmd //c "mklink /j junction target" &&
555+
>target/via-junction &&
556+
git -C junction add "$(pwd)/junction/via-junction" &&
557+
echo via-junction >expect &&
558+
git -C target diff --cached --name-only >actual &&
559+
test_cmp expect actual
560+
'
561+
551562
test_done

t/t5601-clone.sh

+7
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,13 @@ test_expect_success 'clone respects GIT_WORK_TREE' '
7878
7979
'
8080

81+
test_expect_success CASE_INSENSITIVE_FS 'core.worktree is not added due to path case' '
82+
83+
mkdir UPPERCASE &&
84+
git clone src "$(pwd)/uppercase" &&
85+
test "unset" = "$(git -C UPPERCASE config --default unset core.worktree)"
86+
'
87+
8188
test_expect_success 'clone from hooks' '
8289
8390
test_create_repo r0 &&

0 commit comments

Comments
 (0)