Skip to content

Commit 36281b0

Browse files
committed
Merge pull request #2504 from dscho/access-repo-via-junction
Handle `git add <file>` where <file> traverses an NTFS junction
2 parents 74873b9 + 16dd42f commit 36281b0

File tree

6 files changed

+90
-0
lines changed

6 files changed

+90
-0
lines changed

abspath.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,9 @@ static char *strbuf_realpath_1(struct strbuf *resolved, const char *path,
9191
goto error_out;
9292
}
9393

94+
if (platform_strbuf_realpath(resolved, path))
95+
return resolved->buf;
96+
9497
strbuf_addstr(&remaining, path);
9598
get_root_part(resolved, &remaining);
9699

compat/mingw.c

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

1083+
char *mingw_strbuf_realpath(struct strbuf *resolved, const char *path)
1084+
{
1085+
wchar_t wpath[MAX_PATH];
1086+
HANDLE h;
1087+
DWORD ret;
1088+
int len;
1089+
const char *last_component = NULL;
1090+
1091+
if (xutftowcs_path(wpath, path) < 0)
1092+
return NULL;
1093+
1094+
h = CreateFileW(wpath, 0,
1095+
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
1096+
OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
1097+
1098+
/*
1099+
* strbuf_realpath() allows the last path component to not exist. If
1100+
* that is the case, now it's time to try without last component.
1101+
*/
1102+
if (h == INVALID_HANDLE_VALUE &&
1103+
GetLastError() == ERROR_FILE_NOT_FOUND) {
1104+
/* cut last component off of `wpath` */
1105+
wchar_t *p = wpath + wcslen(wpath);
1106+
1107+
while (p != wpath)
1108+
if (*(--p) == L'/' || *p == L'\\')
1109+
break; /* found start of last component */
1110+
1111+
if (p != wpath && (last_component = find_last_dir_sep(path))) {
1112+
last_component++; /* skip directory separator */
1113+
*p = L'\0';
1114+
h = CreateFileW(wpath, 0, FILE_SHARE_READ |
1115+
FILE_SHARE_WRITE | FILE_SHARE_DELETE,
1116+
NULL, OPEN_EXISTING,
1117+
FILE_FLAG_BACKUP_SEMANTICS, NULL);
1118+
}
1119+
}
1120+
1121+
if (h == INVALID_HANDLE_VALUE)
1122+
return NULL;
1123+
1124+
ret = GetFinalPathNameByHandleW(h, wpath, ARRAY_SIZE(wpath), 0);
1125+
CloseHandle(h);
1126+
if (!ret || ret >= ARRAY_SIZE(wpath))
1127+
return NULL;
1128+
1129+
len = wcslen(wpath) * 3;
1130+
strbuf_grow(resolved, len);
1131+
len = xwcstoutf(resolved->buf, normalize_ntpath(wpath), len);
1132+
if (len < 0)
1133+
return NULL;
1134+
resolved->len = len;
1135+
1136+
if (last_component) {
1137+
/* Use forward-slash, like `normalize_ntpath()` */
1138+
strbuf_addch(resolved, '/');
1139+
strbuf_addstr(resolved, last_component);
1140+
}
1141+
1142+
return resolved->buf;
1143+
1144+
}
1145+
10831146
char *mingw_getcwd(char *pointer, int len)
10841147
{
10851148
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
@@ -449,6 +449,8 @@ int mingw_is_mount_point(struct strbuf *path);
449449
#define PATH_SEP ';'
450450
char *mingw_query_user_email(void);
451451
#define query_user_email mingw_query_user_email
452+
char *mingw_strbuf_realpath(struct strbuf *resolved, const char *path);
453+
#define platform_strbuf_realpath mingw_strbuf_realpath
452454
#if !defined(__MINGW64_VERSION_MAJOR) && (!defined(_MSC_VER) || _MSC_VER < 1800)
453455
#define PRIuMAX "I64u"
454456
#define PRId64 "I64d"

git-compat-util.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -409,6 +409,10 @@ static inline int git_has_dir_sep(const char *path)
409409
#define query_user_email() NULL
410410
#endif
411411

412+
#ifndef platform_strbuf_realpath
413+
#define platform_strbuf_realpath(resolved, path) NULL
414+
#endif
415+
412416
#ifdef __TANDEM
413417
#include <floss.h(floss_execl,floss_execlp,floss_execv,floss_execvp)>
414418
#include <floss.h(floss_getpwuid)>

t/t3700-add.sh

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -423,4 +423,15 @@ test_expect_success CASE_INSENSITIVE_FS 'path is case-insensitive' '
423423
git add "$downcased"
424424
'
425425

426+
test_expect_success MINGW 'can add files via NTFS junctions' '
427+
test_when_finished "cmd //c rmdir junction && rm -rf target" &&
428+
test_create_repo target &&
429+
cmd //c "mklink /j junction target" &&
430+
>target/via-junction &&
431+
git -C junction add "$(pwd)/junction/via-junction" &&
432+
echo via-junction >expect &&
433+
git -C target diff --cached --name-only >actual &&
434+
test_cmp expect actual
435+
'
436+
426437
test_done

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)