From 7598cfd0cc1bcf55a09cc68347a3dcb215635c2a Mon Sep 17 00:00:00 2001 From: Alexandr Miloslavskiy Date: Thu, 2 Jul 2020 17:48:34 +0200 Subject: [PATCH] mingw_strbuf_realpath(): handle case of non existent last path component git often requests `strbuf_realpath(path + "/.git")`, where "./git" does not yet exist on disk. This causes the following to happen: 1. `mingw_strbuf_realpath()` fails 2. Non-mingw `strbuf_realpath()` does the work 3. Result of `strbuf_realpath()` is slightly different, for example it will not normalize the case of disk/folder names 4. `needs_work_tree_config()` becomes confused by these differences 5. clone adds `core.worktree` setting This in turn causes various problems, for example: 1. Repository folder can no longer be renamed/moved without breaking it 2. Using the repository on WSL (Windows Subsystem for Linux) doesn't work, because it has windows-style path saved This fixes https://github.com/git-for-windows/git/issues/2569 Co-Authored-By: Johannes Schindelin Signed-off-by: Alexandr Miloslavskiy --- compat/mingw.c | 32 ++++++++++++++++++++++++++++++++ t/t5601-clone.sh | 7 +++++++ 2 files changed, 39 insertions(+) diff --git a/compat/mingw.c b/compat/mingw.c index c8fd4538d7d440..431ade6e7bb52d 100644 --- a/compat/mingw.c +++ b/compat/mingw.c @@ -1293,6 +1293,7 @@ char *mingw_strbuf_realpath(struct strbuf *resolved, const char *path) HANDLE h; DWORD ret; int len; + const char *last_component = NULL; if (xutftowcs_path(wpath, path) < 0) return NULL; @@ -1300,6 +1301,30 @@ char *mingw_strbuf_realpath(struct strbuf *resolved, const char *path) h = CreateFileW(wpath, 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); + + /* + * strbuf_realpath() allows the last path component to not exist. If + * that is the case, now it's time to try without last component. + */ + if (h == INVALID_HANDLE_VALUE && + GetLastError() == ERROR_FILE_NOT_FOUND) { + /* cut last component off of `wpath` */ + wchar_t *p = wpath + wcslen(wpath); + + while (p != wpath) + if (*(--p) == L'/' || *p == L'\\') + break; /* found start of last component */ + + if (p != wpath && (last_component = find_last_dir_sep(path))) { + last_component++; /* skip directory separator */ + *p = L'\0'; + h = CreateFileW(wpath, 0, FILE_SHARE_READ | + FILE_SHARE_WRITE | FILE_SHARE_DELETE, + NULL, OPEN_EXISTING, + FILE_FLAG_BACKUP_SEMANTICS, NULL); + } + } + if (h == INVALID_HANDLE_VALUE) return NULL; @@ -1314,6 +1339,13 @@ char *mingw_strbuf_realpath(struct strbuf *resolved, const char *path) if (len < 0) return NULL; resolved->len = len; + + if (last_component) { + /* Use forward-slash, like `normalize_ntpath()` */ + strbuf_addch(resolved, '/'); + strbuf_addstr(resolved, last_component); + } + return resolved->buf; } diff --git a/t/t5601-clone.sh b/t/t5601-clone.sh index 84ea2a3eb707a7..23cac68388121e 100755 --- a/t/t5601-clone.sh +++ b/t/t5601-clone.sh @@ -68,6 +68,13 @@ test_expect_success 'clone respects GIT_WORK_TREE' ' ' +test_expect_success CASE_INSENSITIVE_FS 'core.worktree is not added due to path case' ' + + mkdir UPPERCASE && + git clone src "$(pwd)/uppercase" && + test "unset" = "$(git -C UPPERCASE config --default unset core.worktree)" +' + test_expect_success 'clone from hooks' ' test_create_repo r0 &&