From 947de87d6755ea8e721294ef8d158582c30f2fee Mon Sep 17 00:00:00 2001 From: johnnyshields <27655+johnnyshields@users.noreply.github.com> Date: Thu, 7 May 2026 00:00:00 +0000 Subject: [PATCH 1/2] path.c: translate Windows paths recorded by Windows git on POSIX hosts When `git worktree add` is run from native Windows, git writes absolute paths into the worktree's `.git` file, into `/worktrees//gitdir`, and (when present) into `/commondir`, in `:/...` or `:\...` form. Reading those files back from a non-Windows-native build of git fails because neither form is meaningful on POSIX, so the worktree appears broken even though every byte of it is reachable - the most common scenario being a worktree on a Windows drive opened from inside WSL2 (where the Windows filesystem is mounted at `/mnt//`) or from Cygwin/MSYS (where it is `/cygdrive//`). Add a small helper `translate_windows_path()` that recognises this shape at the start of a path and rewrites it to the POSIX mount form appropriate for the current build (`/cygdrive//` on Cygwin, `/mnt//` everywhere else), converting any backslashes in the remainder to forward slashes. Call it at the three places where non-Windows-native git reads a recorded worktree-related path back from disk: * `read_gitfile_gently()` - the `gitdir:` line in a worktree's `.git` file. * `get_common_dir_noenv()` - the `commondir` file inside a worktree's git directory, which points at the main repo. * `get_linked_worktree()` - the `gitdir` file inside `/worktrees//`, which points at the worktree's `.git` link. Translation only happens for `:/` or `:\` where `` is a single ASCII letter; anything else is left alone. The helper is a no-op on `GIT_WINDOWS_NATIVE` builds, where the input is already in native form. On non-WSL Linux hosts the translation still produces a syntactically valid POSIX path; if the corresponding `/mnt//` mount does not exist, the next stat()/open() fails as it would have without translation - i.e. the change cannot make a working configuration stop working. Add a `translate_windows_path` subcommand to the path-utils test tool and cover it in `t/t0060-path-utils.sh`. The test fixtures pick the expected prefix from the CYGWIN prereq so the same suite passes on Linux and Cygwin builds. Signed-off-by: johnnyshields <27655+johnnyshields@users.noreply.github.com> --- path.c | 43 ++++++++++++++++++++++++++++++++++++++ path.h | 17 +++++++++++++++ setup.c | 16 ++++++++++++++ t/helper/test-path-utils.c | 9 ++++++++ t/t0060-path-utils.sh | 32 ++++++++++++++++++++++++++++ worktree.c | 17 +++++++++++++++ 6 files changed, 134 insertions(+) diff --git a/path.c b/path.c index d7e17bf17404de..675106382b2452 100644 --- a/path.c +++ b/path.c @@ -18,6 +18,49 @@ #include "lockfile.h" #include "exec-cmd.h" +int translate_windows_path(struct strbuf *path) +{ +#ifndef GIT_WINDOWS_NATIVE +#ifdef __CYGWIN__ + static const char drive_prefix[] = "/cygdrive/"; +#else + static const char drive_prefix[] = "/mnt/"; +#endif + const size_t drive_prefix_len = sizeof(drive_prefix) - 1; + const size_t expansion = drive_prefix_len + 1 - 2; + char drive; + size_t i; + + if (path->len < 3) + return 0; + if (!isalpha((unsigned char)path->buf[0])) + return 0; + if (path->buf[1] != ':') + return 0; + if (path->buf[2] != '/' && path->buf[2] != '\\') + return 0; + + drive = tolower((unsigned char)path->buf[0]); + + /* Rewrite ":" as "", then convert any + * backslashes in the remaining path to forward slashes. */ + strbuf_grow(path, expansion); + memmove(path->buf + 2 + expansion, path->buf + 2, path->len - 2 + 1); + memcpy(path->buf, drive_prefix, drive_prefix_len); + path->buf[drive_prefix_len] = drive; + path->len += expansion; + + for (i = drive_prefix_len + 1; i < path->len; i++) { + if (path->buf[i] == '\\') + path->buf[i] = '/'; + } + return 1; +#else + (void)path; + return 0; +#endif +} + static int get_st_mode_bits(const char *path, int *mode) { struct stat st; diff --git a/path.h b/path.h index 0434ba5e07e806..e92c294245ba03 100644 --- a/path.h +++ b/path.h @@ -6,6 +6,23 @@ struct strbuf; struct string_list; struct worktree; +/* + * Translate Windows-style absolute paths (`:/...` or `:\...`) recorded + * by git running on native Windows into the POSIX mount form used by the + * current build: + * + * * Cygwin / MSYS: `/cygdrive//...` + * * everything else: `/mnt//...` (suits WSL2, harmless elsewhere) + * + * Edits `path` in place; the strbuf may grow. Backslashes in the remainder + * are converted to forward slashes. Returns 1 if a translation occurred, + * 0 otherwise. + * + * No-op on native Windows builds, where the input is already in the native + * form. + */ +int translate_windows_path(struct strbuf *path); + /* * The result to all functions which return statically allocated memory may be * overwritten by another call to _any_ one of these functions. Consider using diff --git a/setup.c b/setup.c index 7ec4427368a2a7..c0518f374f50ae 100644 --- a/setup.c +++ b/setup.c @@ -336,6 +336,7 @@ int get_common_dir_noenv(struct strbuf *sb, const char *gitdir) data.len--; data.buf[data.len] = '\0'; strbuf_reset(&path); + translate_windows_path(&data); if (!is_absolute_path(data.buf)) strbuf_addf(&path, "%s/", gitdir); strbuf_addbuf(&path, &data); @@ -1009,6 +1010,21 @@ const char *read_gitfile_gently(const char *path, int *return_error_code) buf[len] = '\0'; dir = buf + 8; +#ifndef GIT_WINDOWS_NATIVE + { + struct strbuf translated = STRBUF_INIT; + strbuf_addstr(&translated, dir); + if (translate_windows_path(&translated)) { + char *new_buf = xstrfmt("gitdir: %s", translated.buf); + free(buf); + buf = new_buf; + len = strlen(buf); + dir = buf + 8; + } + strbuf_release(&translated); + } +#endif + if (!is_absolute_path(dir) && (slash = strrchr(path, '/'))) { size_t pathlen = slash+1 - path; dir = xstrfmt("%.*s%.*s", (int)pathlen, path, diff --git a/t/helper/test-path-utils.c b/t/helper/test-path-utils.c index 874542ec3462a5..fd7ee7d81e3c84 100644 --- a/t/helper/test-path-utils.c +++ b/t/helper/test-path-utils.c @@ -401,6 +401,15 @@ int cmd__path_utils(int argc, const char **argv) return 0; } + if (argc == 3 && !strcmp(argv[1], "translate_windows_path")) { + struct strbuf sb = STRBUF_INIT; + strbuf_addstr(&sb, argv[2]); + translate_windows_path(&sb); + puts(sb.buf); + strbuf_release(&sb); + return 0; + } + if (argc == 4 && !strcmp(argv[1], "relative_path")) { struct strbuf sb = STRBUF_INIT; const char *in, *prefix, *rel; diff --git a/t/t0060-path-utils.sh b/t/t0060-path-utils.sh index 8545cdfab559b4..5a07d2cd751789 100755 --- a/t/t0060-path-utils.sh +++ b/t/t0060-path-utils.sh @@ -611,4 +611,36 @@ test_expect_success !VALGRIND,RUNTIME_PREFIX,CAN_EXEC_IN_PWD '%(prefix)/ works' test_cmp expect actual ' +# translate_windows_path is a no-op when git is built for native Windows +# (the input is already in the native form). On Cygwin the mount root is +# /cygdrive/; everywhere else it is /mnt/. +if test_have_prereq CYGWIN +then + WSL_DRIVE_PREFIX=/cygdrive +else + WSL_DRIVE_PREFIX=/mnt +fi + +translate_windows_path() { + test_expect_success !MINGW "translate_windows_path: $1 => $2" " + echo '$2' >expect && + test-tool path-utils translate_windows_path '$1' >actual && + test_cmp expect actual + " +} + +translate_windows_path 'C:/foo/bar' "$WSL_DRIVE_PREFIX/c/foo/bar" +translate_windows_path 'C:\foo\bar' "$WSL_DRIVE_PREFIX/c/foo/bar" +translate_windows_path 'D:/repo/.git/worktrees/wt' "$WSL_DRIVE_PREFIX/d/repo/.git/worktrees/wt" +translate_windows_path 'Z:\path\with mixed/seps' "$WSL_DRIVE_PREFIX/z/path/with mixed/seps" +translate_windows_path 'c:/already-lower' "$WSL_DRIVE_PREFIX/c/already-lower" + +# Inputs that must NOT be translated: +translate_windows_path '/already/posix' '/already/posix' +translate_windows_path 'relative/path' 'relative/path' +translate_windows_path 'C:relative-no-separator' 'C:relative-no-separator' +translate_windows_path '1:/digit-prefix' '1:/digit-prefix' +translate_windows_path 'C' 'C' +translate_windows_path '' '' + test_done diff --git a/worktree.c b/worktree.c index d874e23b4e158a..dff2186eda14e3 100644 --- a/worktree.c +++ b/worktree.c @@ -155,6 +155,9 @@ struct worktree *get_linked_worktree(const char *id, strbuf_rtrim(&worktree_path); strbuf_strip_suffix(&worktree_path, "/.git"); + /* Worktree path may have been recorded by git running on Windows. */ + translate_windows_path(&worktree_path); + if (!is_absolute_path(worktree_path.buf)) { strbuf_strip_suffix(&path, "gitdir"); strbuf_addbuf(&path, &worktree_path); @@ -992,6 +995,20 @@ int should_prune_worktree(const char *id, struct strbuf *reason, char **wtpath, goto done; } path[len] = '\0'; + +#ifndef GIT_WINDOWS_NATIVE + { + struct strbuf translated = STRBUF_INIT; + strbuf_addstr(&translated, path); + if (translate_windows_path(&translated)) { + free(path); + path = strbuf_detach(&translated, NULL); + } else { + strbuf_release(&translated); + } + } +#endif + if (is_absolute_path(path)) { strbuf_addstr(&dotgit, path); } else { From 84281bf303c5a8434431fb7bc7a7e3ccb3e1594a Mon Sep 17 00:00:00 2001 From: johnnyshields <27655+johnnyshields@users.noreply.github.com> Date: Thu, 7 May 2026 00:00:00 +0000 Subject: [PATCH 2/2] path.c: translate worktree paths between Windows and WSL/Cygwin builds MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A `git worktree` created by git running under one runtime is in general not openable by git running under another, because the paths recorded in the worktree's metadata files (`/.git`, `/worktrees//gitdir`, `/commondir`) are written in the originating runtime's form. The two common cases: 1. Worktree created from WSL2 (or Cygwin/MSYS), opened by native Windows git. Recorded paths look like `/mnt/c/...` or `/cygdrive/c/...` — not parseable by Win32 APIs. 2. Worktree created from native Windows, opened by WSL2 / Cygwin / MSYS2 git. Recorded paths look like `C:/...` or `C:\...` — not valid POSIX paths. In either case the worktree appears broken even though every byte of it is reachable from the reader. Add a single helper `translate_windows_path()` that rewrites recorded paths to the form expected by the current build. Direction is selected at compile time, not at runtime: * On `GIT_WINDOWS_NATIVE` builds, `/mnt//...` or `/cygdrive//...` (where `` is a single ASCII letter followed by `/`, `\`, or end-of-string) are rewritten in place to `:/...`. * On other builds, `:/...` and `:\...` are rewritten to the mount form for this runtime: `//...` on MSYS2, `/cygdrive//...` on real Cygwin, `/mnt//...` everywhere else (the WSL2 default; harmless on hosts where `/mnt//` is not a Windows-drive mount, because the translated path simply fails to resolve, no worse than the unparseable input). Backslashes in the remainder are normalised to forward slashes. Multi-character segments (`/mnt/storage`, `/cygdrive/usr`) and digit-prefixed mounts pass through untouched, so legitimate POSIX paths under these prefixes are never disturbed. Wire the helper into the four sites that read recorded worktree path metadata: * `read_gitfile_gently()` — the `gitdir:` line in a worktree's `.git` file. * `get_common_dir_noenv()` — the `commondir` file inside a worktree's git directory. * `get_linked_worktree()` — the `gitdir` file inside `/worktrees//`. * `should_prune_worktree()` — re-reads the same file when deciding prunability; without translation, a cross-runtime worktree would be marked `prunable gitdir file points to non-existent location` even when the listing succeeded. Add tests: * `t/t0060-path-utils.sh` exercises the helper directly via a new `translate_windows_path` subcommand of `test-tool path-utils`, covering both translatable shapes and shapes that must remain untouched. The expected mount root is selected from `uname -s`, so the suite passes on Cygwin, MSYS2, and Linux/WSL builds. * `t/t0042-wsl-mnt-path.sh` (MINGW-gated) exercises all four read sites end-to-end using a real worktree whose recorded paths have been rewritten in `/mnt//` form, mimicking git running inside WSL2. --- path.c | 58 ++++++++++++++-- path.h | 22 +++--- setup.c | 2 - t/t0042-wsl-mnt-path.sh | 149 ++++++++++++++++++++++++++++++++++++++++ t/t0060-path-utils.sh | 24 +++---- worktree.c | 4 +- 6 files changed, 223 insertions(+), 36 deletions(-) create mode 100644 t/t0042-wsl-mnt-path.sh diff --git a/path.c b/path.c index 675106382b2452..67708ca94f67d9 100644 --- a/path.c +++ b/path.c @@ -20,8 +20,57 @@ int translate_windows_path(struct strbuf *path) { -#ifndef GIT_WINDOWS_NATIVE -#ifdef __CYGWIN__ +#ifdef GIT_WINDOWS_NATIVE + /* + * Windows-native: rewrite POSIX-mount paths (WSL `/mnt//`, + * Cygwin `/cygdrive//`, MSYS `//`) to drive form `:/...`. + * The single-letter + separator check below is what stops + * multi-character segments like `/mnt/storage` from matching. + */ + static const struct { + const char *prefix; + size_t prefix_len; + } posix_prefixes[] = { + { "/mnt/", 5 }, /* WSL */ + { "/cygdrive/", 10 }, /* Cygwin */ + { "/", 1 }, /* MSYS */ + }; + size_t i; + + if (path->len == 0) + return 0; + + for (i = 0; i < ARRAY_SIZE(posix_prefixes); i++) { + size_t pl = posix_prefixes[i].prefix_len; + char drive; + + if (path->len < pl + 1) + continue; + if (memcmp(path->buf, posix_prefixes[i].prefix, pl) != 0) + continue; + drive = path->buf[pl]; + if (!isalpha((unsigned char)drive)) + continue; + if (path->len > pl + 1 && path->buf[pl + 1] != '/' && path->buf[pl + 1] != '\\') + continue; + + /* "" (pl+1 bytes) -> ":" (2 bytes). */ + path->buf[0] = drive; + path->buf[1] = ':'; + memmove(path->buf + 2, path->buf + pl + 1, path->len - pl); + strbuf_setlen(path, path->len - (pl - 1)); + return 1; + } + return 0; +#else + /* + * POSIX: rewrite Windows-form `:/...` or `:\...` to this + * build's mount form (drive_prefix selected below), normalising + * any backslashes in the tail. + */ +#if defined(__MSYS__) + static const char drive_prefix[] = "/"; +#elif defined(__CYGWIN__) static const char drive_prefix[] = "/cygdrive/"; #else static const char drive_prefix[] = "/mnt/"; @@ -42,8 +91,6 @@ int translate_windows_path(struct strbuf *path) drive = tolower((unsigned char)path->buf[0]); - /* Rewrite ":" as "", then convert any - * backslashes in the remaining path to forward slashes. */ strbuf_grow(path, expansion); memmove(path->buf + 2 + expansion, path->buf + 2, path->len - 2 + 1); memcpy(path->buf, drive_prefix, drive_prefix_len); @@ -55,9 +102,6 @@ int translate_windows_path(struct strbuf *path) path->buf[i] = '/'; } return 1; -#else - (void)path; - return 0; #endif } diff --git a/path.h b/path.h index e92c294245ba03..987651d1247026 100644 --- a/path.h +++ b/path.h @@ -7,19 +7,21 @@ struct string_list; struct worktree; /* - * Translate Windows-style absolute paths (`:/...` or `:\...`) recorded - * by git running on native Windows into the POSIX mount form used by the - * current build: + * Translate worktree gitdir paths between native Windows and POSIX-mount + * forms, in the direction appropriate for the current build: * - * * Cygwin / MSYS: `/cygdrive//...` - * * everything else: `/mnt//...` (suits WSL2, harmless elsewhere) + * * Windows-native: `/mnt//`, `/cygdrive//`, or `//` -> `:/...` + * * MSYS: `:/` or `:\` -> `//...` + * * Cygwin: `:/` or `:\` -> `/cygdrive//...` + * * everything else (Linux/WSL/macOS/...): `:/` or `:\` -> `/mnt//...` * - * Edits `path` in place; the strbuf may grow. Backslashes in the remainder - * are converted to forward slashes. Returns 1 if a translation occurred, - * 0 otherwise. + * `` must be a single ASCII letter; multi-character segments + * (`/mnt/storage`) and digit-prefixed mounts pass through unchanged. + * Backslashes in the tail are normalised to forward slashes on the + * POSIX-direction translation. * - * No-op on native Windows builds, where the input is already in the native - * form. + * Edits `path` in place; may shrink (Windows direction) or grow (POSIX + * direction). Returns 1 if a translation occurred, 0 otherwise. */ int translate_windows_path(struct strbuf *path); diff --git a/setup.c b/setup.c index c0518f374f50ae..60fe062931fd70 100644 --- a/setup.c +++ b/setup.c @@ -1010,7 +1010,6 @@ const char *read_gitfile_gently(const char *path, int *return_error_code) buf[len] = '\0'; dir = buf + 8; -#ifndef GIT_WINDOWS_NATIVE { struct strbuf translated = STRBUF_INIT; strbuf_addstr(&translated, dir); @@ -1023,7 +1022,6 @@ const char *read_gitfile_gently(const char *path, int *return_error_code) } strbuf_release(&translated); } -#endif if (!is_absolute_path(dir) && (slash = strrchr(path, '/'))) { size_t pathlen = slash+1 - path; diff --git a/t/t0042-wsl-mnt-path.sh b/t/t0042-wsl-mnt-path.sh new file mode 100644 index 00000000000000..2ba403d8380726 --- /dev/null +++ b/t/t0042-wsl-mnt-path.sh @@ -0,0 +1,149 @@ +#!/bin/sh + +test_description='translate WSL/Cygwin /mnt// paths in worktree gitfiles + +Verify that `git worktree add` artefacts written from inside WSL2 or +Cygwin/MSYS - which use POSIX-mounted paths like `/mnt/c/...` or +`/cygdrive/c/...` - are still resolvable when read back from native +Windows git. +' + +GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main +export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME + +. ./test-lib.sh + +# Convert any drive-prefixed path Windows git might emit to a POSIX-mount +# form rooted at $1. Handles MSYS form (/c/foo), Windows forward-slash +# form (C:/foo), and Windows backslash form (C:\foo). $1 may be empty, +# in which case the result is the MSYS2 mount form (/c/foo). MINGW-only. +mount_form () { + prefix=$1 + path=$2 + case "$path" in + /[A-Za-z]/*) + echo "$path" | sed -E "s|^/([A-Za-z])/|$prefix/\\L\\1/|" + ;; + [A-Za-z]:/*) + echo "$path" | sed -E "s|^([A-Za-z]):/|$prefix/\\L\\1/|" + ;; + [A-Za-z]:'\'*) + echo "$path" | sed -E "s|^([A-Za-z]):.|$prefix/\\L\\1/|; s|\\\\|/|g" + ;; + *) + echo "$path" + ;; + esac +} + +to_mnt () { mount_form /mnt "$1"; } +to_cygdrive () { mount_form /cygdrive "$1"; } +to_msys () { mount_form "" "$1"; } + +test_expect_success MINGW 'setup main repo' ' + git init repo && + test_commit -C repo init +' + +test_expect_success MINGW 'read_gitfile_gently translates /mnt// gitdir' ' + test_when_finished "rm -rf wtlink actual" && + REAL=$(cd repo/.git && pwd) && + MNT=$(to_mnt "$REAL") && + + # Sanity: the path must actually start with /mnt/ - if it does not, + # the host shell did not give us a path with a drive prefix and the + # rest of the test would be silently meaningless. + case "$MNT" in + /mnt/*) : ok ;; + *) BUG "to_mnt produced $MNT from $REAL" ;; + esac && + + mkdir wtlink && + printf "gitdir: %s\n" "$MNT" >wtlink/.git && + + (cd wtlink && git rev-parse --git-dir) >actual && + test_path_is_dir "$(cat actual)" +' + +test_expect_success MINGW 'read_gitfile_gently translates /cygdrive// gitdir' ' + test_when_finished "rm -rf wtlink actual" && + REAL=$(cd repo/.git && pwd) && + CYG=$(to_cygdrive "$REAL") && + + mkdir wtlink && + printf "gitdir: %s\n" "$CYG" >wtlink/.git && + + (cd wtlink && git rev-parse --git-dir) >actual && + test_path_is_dir "$(cat actual)" +' + +test_expect_success MINGW 'read_gitfile_gently translates // MSYS2 gitdir' ' + test_when_finished "rm -rf wtlink actual" && + REAL=$(cd repo/.git && pwd) && + MSYS_PATH=$(to_msys "$REAL") && + + case "$MSYS_PATH" in + /[A-Za-z]/*) : ok ;; + *) BUG "to_msys produced $MSYS_PATH from $REAL" ;; + esac && + + mkdir wtlink && + printf "gitdir: %s\n" "$MSYS_PATH" >wtlink/.git && + + (cd wtlink && git rev-parse --git-dir) >actual && + test_path_is_dir "$(cat actual)" +' + +test_expect_success MINGW 'read_gitfile_gently leaves /mnt// alone' ' + test_when_finished "rm -rf wtlink" && + mkdir wtlink && + # "storage" is not a single drive letter, so this must not be + # translated. The path does not exist on Windows, so the open fails. + echo "gitdir: /mnt/storage/no/such/repo" >wtlink/.git && + + test_must_fail git -C wtlink rev-parse --git-dir 2>err && + test_grep "not a git repository" err +' + +test_expect_success MINGW 'get_linked_worktree finds worktree recorded with /mnt// path' ' + test_when_finished "rm -rf repo/wt repo/.git/worktrees/wt" && + + git -C repo worktree add --detach wt && + WT_REAL=$(cd repo/wt && pwd) && + WT_MNT=$(to_mnt "$WT_REAL") && + + # Overwrite the recorded worktree path with the WSL form, mimicking + # what `git worktree add` writes when run from inside WSL. + printf "%s/.git\n" "$WT_MNT" >repo/.git/worktrees/wt/gitdir && + + # `git worktree list` reads that file via get_linked_worktree. + # After translation the worktree must still be reachable: it must + # NOT be flagged prunable, and a git operation inside the worktree + # directory must succeed. + git -C repo worktree list --porcelain >list && + ! grep -q "^prunable" list && + (cd "$WT_REAL" && git rev-parse --is-inside-work-tree) +' + +test_expect_success MINGW 'get_common_dir_noenv translates /mnt// commondir' ' + test_when_finished "rm -rf wtdir wt actual" && + + REAL=$(cd repo/.git && pwd) && + MNT=$(to_mnt "$REAL") && + + # Build a synthetic linked-worktree gitdir that points at the main + # repo via a /mnt// commondir record. + mkdir wtdir && + echo "$(cd repo && git rev-parse HEAD)" >wtdir/HEAD && + echo "$MNT" >wtdir/commondir && + printf "%s/.git\n" "$(pwd)" >wtdir/gitdir && + + # rev-parse --git-common-dir on a checkout that points here should + # resolve through the translated commondir. + mkdir wt && + printf "gitdir: %s\n" "$(pwd)/wtdir" >wt/.git && + (cd wt && git rev-parse --git-common-dir) >actual && + test_path_is_dir "$(cat actual)" +' + +test_done diff --git a/t/t0060-path-utils.sh b/t/t0060-path-utils.sh index 5a07d2cd751789..016c330badbd9c 100755 --- a/t/t0060-path-utils.sh +++ b/t/t0060-path-utils.sh @@ -611,15 +611,11 @@ test_expect_success !VALGRIND,RUNTIME_PREFIX,CAN_EXEC_IN_PWD '%(prefix)/ works' test_cmp expect actual ' -# translate_windows_path is a no-op when git is built for native Windows -# (the input is already in the native form). On Cygwin the mount root is -# /cygdrive/; everywhere else it is /mnt/. -if test_have_prereq CYGWIN -then - WSL_DRIVE_PREFIX=/cygdrive -else - WSL_DRIVE_PREFIX=/mnt -fi +case "$(uname -s)" in +*CYGWIN*) MOUNT_PREFIX=/cygdrive ;; +*MSYS_NT*) MOUNT_PREFIX= ;; +*) MOUNT_PREFIX=/mnt ;; +esac translate_windows_path() { test_expect_success !MINGW "translate_windows_path: $1 => $2" " @@ -629,11 +625,11 @@ translate_windows_path() { " } -translate_windows_path 'C:/foo/bar' "$WSL_DRIVE_PREFIX/c/foo/bar" -translate_windows_path 'C:\foo\bar' "$WSL_DRIVE_PREFIX/c/foo/bar" -translate_windows_path 'D:/repo/.git/worktrees/wt' "$WSL_DRIVE_PREFIX/d/repo/.git/worktrees/wt" -translate_windows_path 'Z:\path\with mixed/seps' "$WSL_DRIVE_PREFIX/z/path/with mixed/seps" -translate_windows_path 'c:/already-lower' "$WSL_DRIVE_PREFIX/c/already-lower" +translate_windows_path 'C:/foo/bar' "$MOUNT_PREFIX/c/foo/bar" +translate_windows_path 'C:\foo\bar' "$MOUNT_PREFIX/c/foo/bar" +translate_windows_path 'D:/repo/.git/worktrees/wt' "$MOUNT_PREFIX/d/repo/.git/worktrees/wt" +translate_windows_path 'Z:\path\with mixed/seps' "$MOUNT_PREFIX/z/path/with mixed/seps" +translate_windows_path 'c:/already-lower' "$MOUNT_PREFIX/c/already-lower" # Inputs that must NOT be translated: translate_windows_path '/already/posix' '/already/posix' diff --git a/worktree.c b/worktree.c index dff2186eda14e3..c44ee6dd91235c 100644 --- a/worktree.c +++ b/worktree.c @@ -155,7 +155,7 @@ struct worktree *get_linked_worktree(const char *id, strbuf_rtrim(&worktree_path); strbuf_strip_suffix(&worktree_path, "/.git"); - /* Worktree path may have been recorded by git running on Windows. */ + /* Convert Windows path to POSIX path or vice-versa. */ translate_windows_path(&worktree_path); if (!is_absolute_path(worktree_path.buf)) { @@ -996,7 +996,6 @@ int should_prune_worktree(const char *id, struct strbuf *reason, char **wtpath, } path[len] = '\0'; -#ifndef GIT_WINDOWS_NATIVE { struct strbuf translated = STRBUF_INIT; strbuf_addstr(&translated, path); @@ -1007,7 +1006,6 @@ int should_prune_worktree(const char *id, struct strbuf *reason, char **wtpath, strbuf_release(&translated); } } -#endif if (is_absolute_path(path)) { strbuf_addstr(&dotgit, path);