Skip to content

Commit fcb281d

Browse files
committed
mingw: implement a platform-specific strbuf_realpath()
There is a Win32 API function to resolve symbolic links, and we can use that instead of resolving them manually. Even better, this function also resolves NTFS junction points (which are somewhat similar to bind mounts). This fixes git-for-windows#2481. Signed-off-by: Johannes Schindelin <[email protected]>
1 parent c735978 commit fcb281d

File tree

4 files changed

+73
-1
lines changed

4 files changed

+73
-1
lines changed

compat/mingw.c

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1097,6 +1097,69 @@ struct tm *localtime_r(const time_t *timep, struct tm *result)
10971097
return NULL;
10981098
}
10991099

1100+
char *mingw_strbuf_realpath(struct strbuf *resolved, const char *path)
1101+
{
1102+
wchar_t wpath[MAX_PATH];
1103+
HANDLE h;
1104+
DWORD ret;
1105+
int len;
1106+
const char *last_component = NULL;
1107+
1108+
if (xutftowcs_path(wpath, path) < 0)
1109+
return NULL;
1110+
1111+
h = CreateFileW(wpath, 0,
1112+
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
1113+
OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
1114+
1115+
/*
1116+
* strbuf_realpath() allows the last path component to not exist. If
1117+
* that is the case, now it's time to try without last component.
1118+
*/
1119+
if (h == INVALID_HANDLE_VALUE &&
1120+
GetLastError() == ERROR_FILE_NOT_FOUND) {
1121+
/* cut last component off of `wpath` */
1122+
wchar_t *p = wpath + wcslen(wpath);
1123+
1124+
while (p != wpath)
1125+
if (*(--p) == L'/' || *p == L'\\')
1126+
break; /* found start of last component */
1127+
1128+
if (p != wpath && (last_component = find_last_dir_sep(path))) {
1129+
last_component++; /* skip directory separator */
1130+
*p = L'\0';
1131+
h = CreateFileW(wpath, 0, FILE_SHARE_READ |
1132+
FILE_SHARE_WRITE | FILE_SHARE_DELETE,
1133+
NULL, OPEN_EXISTING,
1134+
FILE_FLAG_BACKUP_SEMANTICS, NULL);
1135+
}
1136+
}
1137+
1138+
if (h == INVALID_HANDLE_VALUE)
1139+
return NULL;
1140+
1141+
ret = GetFinalPathNameByHandleW(h, wpath, ARRAY_SIZE(wpath), 0);
1142+
CloseHandle(h);
1143+
if (!ret || ret >= ARRAY_SIZE(wpath))
1144+
return NULL;
1145+
1146+
len = wcslen(wpath) * 3;
1147+
strbuf_grow(resolved, len);
1148+
len = xwcstoutf(resolved->buf, normalize_ntpath(wpath), len);
1149+
if (len < 0)
1150+
return NULL;
1151+
resolved->len = len;
1152+
1153+
if (last_component) {
1154+
/* Use forward-slash, like `normalize_ntpath()` */
1155+
strbuf_addch(resolved, '/');
1156+
strbuf_addstr(resolved, last_component);
1157+
}
1158+
1159+
return resolved->buf;
1160+
1161+
}
1162+
11001163
char *mingw_getcwd(char *pointer, int len)
11011164
{
11021165
wchar_t cwd[MAX_PATH], wpointer[MAX_PATH];

compat/mingw.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -446,6 +446,8 @@ static inline void convert_slashes(char *path)
446446
#define PATH_SEP ';'
447447
char *mingw_query_user_email(void);
448448
#define query_user_email mingw_query_user_email
449+
char *mingw_strbuf_realpath(struct strbuf *resolved, const char *path);
450+
#define platform_strbuf_realpath mingw_strbuf_realpath
449451
#if !defined(__MINGW64_VERSION_MAJOR) && (!defined(_MSC_VER) || _MSC_VER < 1800)
450452
#define PRIuMAX "I64u"
451453
#define PRId64 "I64d"

t/t3700-add.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -469,7 +469,7 @@ test_expect_success CASE_INSENSITIVE_FS 'path is case-insensitive' '
469469
git add "$downcased"
470470
'
471471

472-
test_expect_failure MINGW 'can add files via NTFS junctions' '
472+
test_expect_success MINGW 'can add files via NTFS junctions' '
473473
test_when_finished "cmd //c rmdir junction && rm -rf target" &&
474474
test_create_repo target &&
475475
cmd //c "mklink /j junction target" &&

t/t5601-clone.sh

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,13 @@ test_expect_success 'clone respects GIT_WORK_TREE' '
7171
7272
'
7373

74+
test_expect_success CASE_INSENSITIVE_FS 'core.worktree is not added due to path case' '
75+
76+
mkdir UPPERCASE &&
77+
git clone src "$(pwd)/uppercase" &&
78+
test "unset" = "$(git -C UPPERCASE config --default unset core.worktree)"
79+
'
80+
7481
test_expect_success 'clone from hooks' '
7582
7683
test_create_repo r0 &&

0 commit comments

Comments
 (0)