{"thread":{"id":"64636","subject":"[PATCH 0/5] Last preparations before upstreaming Git for Windows' symlink support","startedAt":"2025-12-16T15:33:52Z","lastAt":"2026-01-12T08:35:46Z","messageCount":21,"participants":["Johannes Schindelin via GitGitGadget","Karsten Blees via GitGitGadget","Patrick Steinhardt","Junio C Hamano","Johannes Schindelin"],"isPatch":true,"patchVersion":1,"patchTotal":5},"messages":[{"id":"532279","messageId":"pull.2017.git.1765899229.gitgitgadget@gmail.com","threadId":"64636","inReplyTo":null,"subject":"[PATCH 0/5] Last preparations before upstreaming Git for Windows' symlink support","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2025-12-16T15:33:44Z","receivedAt":"2025-12-16T15:33:52Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"After preparing Git's test suite for the upcoming support for symlinks on\nWindows, this patch series touches up a couple of code paths that might not\nseem to be related at first, but need to be adjusted for the symlink support\nto work as expected.\n\nThis is based on js/test-symlink-windows.\n\nJohannes Schindelin (2):\n  mingw: do resolve symlinks in `getcwd()`\n  init: do parse _all_ core.* settings early\n\nKarsten Blees (3):\n  strbuf_readlink(): avoid calling `readlink()` twice in corner-cases\n  strbuf_readlink(): support link targets that exceed PATH_MAX\n  trim_last_path_component(): avoid hard-coding the directory separator\n\n compat/mingw.c | 18 +++++++-----------\n environment.c  |  4 ++--\n environment.h  |  2 ++\n lockfile.c     |  4 ++--\n setup.c        |  2 +-\n strbuf.c       | 10 ++++------\n 6 files changed, 18 insertions(+), 22 deletions(-)\n\n\nbase-commit: 77dfd223aa5b180d69cb2da54f6a7859fb94e131\nPublished-As: https://github.com/gitgitgadget/git/releases/tag/pr-2017%2Fdscho%2Flast-preparations-before-mingw-symlinks-support-next-v1\nFetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-2017/dscho/last-preparations-before-mingw-symlinks-support-next-v1\nPull-Request: https://github.com/gitgitgadget/git/pull/2017\n-- \ngitgitgadget\n"},{"id":"532280","messageId":"1928738b464915e3fb796145688bbfcfcc0fee3c.1765899229.git.gitgitgadget@gmail.com","threadId":"64636","inReplyTo":"pull.2017.git.1765899229.gitgitgadget@gmail.com","subject":"[PATCH 1/5] mingw: do resolve symlinks in `getcwd()`","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2025-12-16T15:33:45Z","receivedAt":"2025-12-16T15:33:53Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nAs pointed out in https://github.com/git-for-windows/git/issues/1676,\nthe `git rev-parse --is-inside-work-tree` command currently fails when\nthe current directory's path contains symbolic links.\n\nThe underlying reason for this bug is that `getcwd()` is supposed to\nresolve symbolic links, but our `mingw_getcwd()` implementation did not.\n\nWe do have all the building blocks for that, though: the\n`GetFinalPathByHandleW()` function will resolve symbolic links. However,\nwe only called that function if `GetLongPathNameW()` failed, for\nhistorical reasons: the latter function was supported for a long time,\nbut the former API function was introduced only with Windows Vista, and\nwe used to support also Windows XP. With that support having been\ndropped, we are free to call the symbolic link-resolving function right\naway.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n compat/mingw.c | 18 +++++++-----------\n 1 file changed, 7 insertions(+), 11 deletions(-)\n\ndiff --git a/compat/mingw.c b/compat/mingw.c\nindex ba1b7b6dd1..7215b127cc 100644\n--- a/compat/mingw.c\n+++ b/compat/mingw.c\n@@ -1251,18 +1251,16 @@ char *mingw_getcwd(char *pointer, int len)\n {\n \twchar_t cwd[MAX_PATH], wpointer[MAX_PATH];\n \tDWORD ret = GetCurrentDirectoryW(ARRAY_SIZE(cwd), cwd);\n+\tHANDLE hnd;\n \n \tif (!ret || ret >= ARRAY_SIZE(cwd)) {\n \t\terrno = ret ? ENAMETOOLONG : err_win_to_posix(GetLastError());\n \t\treturn NULL;\n \t}\n-\tret = GetLongPathNameW(cwd, wpointer, ARRAY_SIZE(wpointer));\n-\tif (!ret && GetLastError() == ERROR_ACCESS_DENIED) {\n-\t\tHANDLE hnd = CreateFileW(cwd, 0,\n-\t\t\tFILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,\n-\t\t\tOPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);\n-\t\tif (hnd == INVALID_HANDLE_VALUE)\n-\t\t\treturn NULL;\n+\thnd = CreateFileW(cwd, 0,\n+\t\t\t  FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,\n+\t\t\t  OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);\n+\tif (hnd != INVALID_HANDLE_VALUE) {\n \t\tret = GetFinalPathNameByHandleW(hnd, wpointer, ARRAY_SIZE(wpointer), 0);\n \t\tCloseHandle(hnd);\n \t\tif (!ret || ret >= ARRAY_SIZE(wpointer))\n@@ -1271,13 +1269,11 @@ char *mingw_getcwd(char *pointer, int len)\n \t\t\treturn NULL;\n \t\treturn pointer;\n \t}\n-\tif (!ret || ret >= ARRAY_SIZE(wpointer))\n-\t\treturn NULL;\n-\tif (GetFileAttributesW(wpointer) == INVALID_FILE_ATTRIBUTES) {\n+\tif (GetFileAttributesW(cwd) == INVALID_FILE_ATTRIBUTES) {\n \t\terrno = ENOENT;\n \t\treturn NULL;\n \t}\n-\tif (xwcstoutf(pointer, wpointer, len) < 0)\n+\tif (xwcstoutf(pointer, cwd, len) < 0)\n \t\treturn NULL;\n \tconvert_slashes(pointer);\n \treturn pointer;\n-- \ngitgitgadget\n\n"},{"id":"532281","messageId":"31497b019886698aacebbbc6a464a7c0124f31c4.1765899229.git.gitgitgadget@gmail.com","threadId":"64636","inReplyTo":"pull.2017.git.1765899229.gitgitgadget@gmail.com","subject":"[PATCH 2/5] init: do parse _all_ core.* settings early","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2025-12-16T15:33:46Z","receivedAt":"2025-12-16T15:33:55Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nIn Git for Windows, `has_symlinks` is set to 0 by default. Therefore, we\nneed to parse the config setting `core.symlinks` to know if it has been\nset to `true`. In `git init`, we must do that before copying the\ntemplates because they might contain symbolic links.\n\nEven if the support for symbolic links on Windows has not made it to\nupstream Git yet, we really should make sure that all the `core.*`\nsettings are parsed before proceeding, as they might very well change\nthe behavior of `git init` in a way the user intended.\n\nThis fixes https://github.com/git-for-windows/git/issues/3414\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n environment.c | 4 ++--\n environment.h | 2 ++\n setup.c       | 2 +-\n 3 files changed, 5 insertions(+), 3 deletions(-)\n\ndiff --git a/environment.c b/environment.c\nindex a770b5921d..b65b85a01f 100644\n--- a/environment.c\n+++ b/environment.c\n@@ -324,8 +324,8 @@ next_name:\n \treturn (current & ~negative) | positive;\n }\n \n-static int git_default_core_config(const char *var, const char *value,\n-\t\t\t\t   const struct config_context *ctx, void *cb)\n+int git_default_core_config(const char *var, const char *value,\n+\t\t\t    const struct config_context *ctx, void *cb)\n {\n \t/* This needs a better name */\n \tif (!strcmp(var, \"core.filemode\")) {\ndiff --git a/environment.h b/environment.h\nindex 51898c99cd..e61f843fdb 100644\n--- a/environment.h\n+++ b/environment.h\n@@ -106,6 +106,8 @@ const char *strip_namespace(const char *namespaced_ref);\n \n int git_default_config(const char *, const char *,\n \t\t       const struct config_context *, void *);\n+int git_default_core_config(const char *var, const char *value,\n+\t\t\t    const struct config_context *ctx, void *cb);\n \n /*\n  * TODO: All the below state either explicitly or implicitly relies on\ndiff --git a/setup.c b/setup.c\nindex 7086741e6c..42e4e7a690 100644\n--- a/setup.c\n+++ b/setup.c\n@@ -2611,7 +2611,7 @@ int init_db(const char *git_dir, const char *real_git_dir,\n \t * have set up the repository format such that we can evaluate\n \t * includeIf conditions correctly in the case of re-initialization.\n \t */\n-\trepo_config(the_repository, platform_core_config, NULL);\n+\trepo_config(the_repository, git_default_core_config, NULL);\n \n \tsafe_create_dir(the_repository, git_dir, 0);\n \n-- \ngitgitgadget\n\n"},{"id":"532282","messageId":"dba281027a8fc49fc152a8d072761ea238aa54d4.1765899229.git.gitgitgadget@gmail.com","threadId":"64636","inReplyTo":"pull.2017.git.1765899229.gitgitgadget@gmail.com","subject":"[PATCH 3/5] strbuf_readlink(): avoid calling `readlink()` twice in corner-cases","fromName":"Karsten Blees via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2025-12-16T15:33:47Z","receivedAt":"2025-12-16T15:33:55Z","isPatch":true,"sender":{"key":"karsten.blees@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1111200?v=4"},"body":"From: Karsten Blees <blees@dcon.de>\n\nThe `strbuf_readlink()` function calls `readlink()`` twice if the hint\nargument specifies the exact size of the link target (e.g. by passing\nstat.st_size as returned by `lstat()`). This is necessary because\n`readlink(..., hint) == hint` could mean that the buffer was too small.\n\nUse `hint + 1` as buffer size to prevent this.\n\nSigned-off-by: Karsten Blees <blees@dcon.de>\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n strbuf.c | 6 +++---\n 1 file changed, 3 insertions(+), 3 deletions(-)\n\ndiff --git a/strbuf.c b/strbuf.c\nindex 6c3851a7f8..44a8f6a554 100644\n--- a/strbuf.c\n+++ b/strbuf.c\n@@ -578,12 +578,12 @@ int strbuf_readlink(struct strbuf *sb, const char *path, size_t hint)\n \twhile (hint < STRBUF_MAXLINK) {\n \t\tssize_t len;\n \n-\t\tstrbuf_grow(sb, hint);\n-\t\tlen = readlink(path, sb->buf, hint);\n+\t\tstrbuf_grow(sb, hint + 1);\n+\t\tlen = readlink(path, sb->buf, hint + 1);\n \t\tif (len < 0) {\n \t\t\tif (errno != ERANGE)\n \t\t\t\tbreak;\n-\t\t} else if (len < hint) {\n+\t\t} else if (len <= hint) {\n \t\t\tstrbuf_setlen(sb, len);\n \t\t\treturn 0;\n \t\t}\n-- \ngitgitgadget\n\n"},{"id":"532283","messageId":"db1feb2293d20532f9468ab63ede43d4fc620203.1765899229.git.gitgitgadget@gmail.com","threadId":"64636","inReplyTo":"pull.2017.git.1765899229.gitgitgadget@gmail.com","subject":"[PATCH 4/5] strbuf_readlink(): support link targets that exceed PATH_MAX","fromName":"Karsten Blees via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2025-12-16T15:33:48Z","receivedAt":"2025-12-16T15:33:56Z","isPatch":true,"sender":{"key":"karsten.blees@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1111200?v=4"},"body":"From: Karsten Blees <blees@dcon.de>\n\nThe `strbuf_readlink()` function refuses to read link targets that\nexceed PATH_MAX (even if a sufficient size was specified by the caller).\n\nAs some platforms (*cough* Windows *cough*) support longer paths, remove\nthis restriction (similar to `strbuf_getcwd()`).\n\nSigned-off-by: Karsten Blees <blees@dcon.de>\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n strbuf.c | 4 +---\n 1 file changed, 1 insertion(+), 3 deletions(-)\n\ndiff --git a/strbuf.c b/strbuf.c\nindex 44a8f6a554..fa4e30f112 100644\n--- a/strbuf.c\n+++ b/strbuf.c\n@@ -566,8 +566,6 @@ ssize_t strbuf_write(struct strbuf *sb, FILE *f)\n \treturn sb->len ? fwrite(sb->buf, 1, sb->len, f) : 0;\n }\n \n-#define STRBUF_MAXLINK (2*PATH_MAX)\n-\n int strbuf_readlink(struct strbuf *sb, const char *path, size_t hint)\n {\n \tsize_t oldalloc = sb->alloc;\n@@ -575,7 +573,7 @@ int strbuf_readlink(struct strbuf *sb, const char *path, size_t hint)\n \tif (hint < 32)\n \t\thint = 32;\n \n-\twhile (hint < STRBUF_MAXLINK) {\n+\tfor (;;) {\n \t\tssize_t len;\n \n \t\tstrbuf_grow(sb, hint + 1);\n-- \ngitgitgadget\n\n"},{"id":"532284","messageId":"3521180e0f73298be7258256289b5852fedf7750.1765899229.git.gitgitgadget@gmail.com","threadId":"64636","inReplyTo":"pull.2017.git.1765899229.gitgitgadget@gmail.com","subject":"[PATCH 5/5] trim_last_path_component(): avoid hard-coding the directory separator","fromName":"Karsten Blees via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2025-12-16T15:33:49Z","receivedAt":"2025-12-16T15:33:58Z","isPatch":true,"sender":{"key":"karsten.blees@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1111200?v=4"},"body":"From: Karsten Blees <blees@dcon.de>\n\nCurrently, this function hard-codes the directory separator as the\nforward slash.\n\nHowever, on Windows the backslash character is valid, too. And we want\nto call this function in the upcoming support for symlinks on Windows\nwith the symlink targets (which naturally use the canonical directory\nseparator on Windows, which is _not_ the forward slash).\n\nPrepare that function to be useful also in that context.\n\nSigned-off-by: Karsten Blees <blees@dcon.de>\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n lockfile.c | 4 ++--\n 1 file changed, 2 insertions(+), 2 deletions(-)\n\ndiff --git a/lockfile.c b/lockfile.c\nindex 1d5ed01682..67082a9caa 100644\n--- a/lockfile.c\n+++ b/lockfile.c\n@@ -19,14 +19,14 @@ static void trim_last_path_component(struct strbuf *path)\n \tint i = path->len;\n \n \t/* back up past trailing slashes, if any */\n-\twhile (i && path->buf[i - 1] == '/')\n+\twhile (i && is_dir_sep(path->buf[i - 1]))\n \t\ti--;\n \n \t/*\n \t * then go backwards until a slash, or the beginning of the\n \t * string\n \t */\n-\twhile (i && path->buf[i - 1] != '/')\n+\twhile (i && !is_dir_sep(path->buf[i - 1]))\n \t\ti--;\n \n \tstrbuf_setlen(path, i);\n-- \ngitgitgadget\n"},{"id":"532367","messageId":"aULBssdzMOw449HI@pks.im","threadId":"64636","inReplyTo":"1928738b464915e3fb796145688bbfcfcc0fee3c.1765899229.git.gitgitgadget@gmail.com","subject":"Re: [PATCH 1/5] mingw: do resolve symlinks in `getcwd()`","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-12-17T14:44:02Z","receivedAt":"2025-12-17T14:44:09Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"On Tue, Dec 16, 2025 at 03:33:45PM +0000, Johannes Schindelin via GitGitGadget wrote:\n> diff --git a/compat/mingw.c b/compat/mingw.c\n> index ba1b7b6dd1..7215b127cc 100644\n> --- a/compat/mingw.c\n> +++ b/compat/mingw.c\n> @@ -1251,18 +1251,16 @@ char *mingw_getcwd(char *pointer, int len)\n>  {\n>  \twchar_t cwd[MAX_PATH], wpointer[MAX_PATH];\n>  \tDWORD ret = GetCurrentDirectoryW(ARRAY_SIZE(cwd), cwd);\n> +\tHANDLE hnd;\n>  \n>  \tif (!ret || ret >= ARRAY_SIZE(cwd)) {\n>  \t\terrno = ret ? ENAMETOOLONG : err_win_to_posix(GetLastError());\n>  \t\treturn NULL;\n>  \t}\n> -\tret = GetLongPathNameW(cwd, wpointer, ARRAY_SIZE(wpointer));\n> -\tif (!ret && GetLastError() == ERROR_ACCESS_DENIED) {\n> -\t\tHANDLE hnd = CreateFileW(cwd, 0,\n> -\t\t\tFILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,\n> -\t\t\tOPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);\n> -\t\tif (hnd == INVALID_HANDLE_VALUE)\n> -\t\t\treturn NULL;\n> +\thnd = CreateFileW(cwd, 0,\n> +\t\t\t  FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,\n> +\t\t\t  OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);\n> +\tif (hnd != INVALID_HANDLE_VALUE) {\n>  \t\tret = GetFinalPathNameByHandleW(hnd, wpointer, ARRAY_SIZE(wpointer), 0);\n>  \t\tCloseHandle(hnd);\n>  \t\tif (!ret || ret >= ARRAY_SIZE(wpointer))\n\nOkay. Due to the change we now also try calling `GetFileAttributesW()`\nin case `CreateFileW()` fails, which wasn't the case before. But I'd\nconsider that to be a win -- if we cannot figure out the final path\nname, then we can at least return the unresolved current working\ndirectory.\n\nPatrick\n\n> @@ -1271,13 +1269,11 @@ char *mingw_getcwd(char *pointer, int len)\n>  \t\t\treturn NULL;\n>  \t\treturn pointer;\n>  \t}\n> -\tif (!ret || ret >= ARRAY_SIZE(wpointer))\n> -\t\treturn NULL;\n> -\tif (GetFileAttributesW(wpointer) == INVALID_FILE_ATTRIBUTES) {\n> +\tif (GetFileAttributesW(cwd) == INVALID_FILE_ATTRIBUTES) {\n>  \t\terrno = ENOENT;\n>  \t\treturn NULL;\n>  \t}\n> -\tif (xwcstoutf(pointer, wpointer, len) < 0)\n> +\tif (xwcstoutf(pointer, cwd, len) < 0)\n>  \t\treturn NULL;\n>  \tconvert_slashes(pointer);\n>  \treturn pointer;\n"},{"id":"532368","messageId":"aULB2TGj_qFFFvCu@pks.im","threadId":"64636","inReplyTo":"31497b019886698aacebbbc6a464a7c0124f31c4.1765899229.git.gitgitgadget@gmail.com","subject":"Re: [PATCH 2/5] init: do parse _all_ core.* settings early","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-12-17T14:44:41Z","receivedAt":"2025-12-17T14:44:47Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"On Tue, Dec 16, 2025 at 03:33:46PM +0000, Johannes Schindelin via GitGitGadget wrote:\n> diff --git a/setup.c b/setup.c\n> index 7086741e6c..42e4e7a690 100644\n> --- a/setup.c\n> +++ b/setup.c\n> @@ -2611,7 +2611,7 @@ int init_db(const char *git_dir, const char *real_git_dir,\n>  \t * have set up the repository format such that we can evaluate\n>  \t * includeIf conditions correctly in the case of re-initialization.\n>  \t */\n> -\trepo_config(the_repository, platform_core_config, NULL);\n> +\trepo_config(the_repository, git_default_core_config, NULL);\n>  \n>  \tsafe_create_dir(the_repository, git_dir, 0);\n\nTwo lines further down we call `create_default_files()`, and there we\nend up calling `repo_config(the_repository, git_default_config, NULL)`\nas one of the first things. We do so after copying templates though, so\nindeed this comes too late.\n\nWe also cannot really merge these two calls: we need to re-parse the\nconfiguration after having copied over the template, as the template may\ncontain a gitconfig file itself.\n\nFurthermore, `git_default_core_config()` already knows to call\n`platform_core_config()`, as well. So we're not losing any of that\ninformation, either.\n\nAll to say that this change makes sense to me and should be safe, as we\ndon't end up parsing _more_ configuration keys, we only parse a subset\nof it a bit earlier.\n\nPatrick\n"},{"id":"532369","messageId":"aULB3wCFGsbZbuSw@pks.im","threadId":"64636","inReplyTo":"db1feb2293d20532f9468ab63ede43d4fc620203.1765899229.git.gitgitgadget@gmail.com","subject":"Re: [PATCH 4/5] strbuf_readlink(): support link targets that exceed PATH_MAX","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-12-17T14:44:47Z","receivedAt":"2025-12-17T14:44:52Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"On Tue, Dec 16, 2025 at 03:33:48PM +0000, Karsten Blees via GitGitGadget wrote:\n> diff --git a/strbuf.c b/strbuf.c\n> index 44a8f6a554..fa4e30f112 100644\n> --- a/strbuf.c\n> +++ b/strbuf.c\n> @@ -566,8 +566,6 @@ ssize_t strbuf_write(struct strbuf *sb, FILE *f)\n>  \treturn sb->len ? fwrite(sb->buf, 1, sb->len, f) : 0;\n>  }\n>  \n> -#define STRBUF_MAXLINK (2*PATH_MAX)\n> -\n>  int strbuf_readlink(struct strbuf *sb, const char *path, size_t hint)\n>  {\n>  \tsize_t oldalloc = sb->alloc;\n> @@ -575,7 +573,7 @@ int strbuf_readlink(struct strbuf *sb, const char *path, size_t hint)\n>  \tif (hint < 32)\n>  \t\thint = 32;\n>  \n> -\twhile (hint < STRBUF_MAXLINK) {\n> +\tfor (;;) {\n>  \t\tssize_t len;\n>  \n>  \t\tstrbuf_grow(sb, hint + 1);\n\nThis makes me wonder whether we have a better way to figure out the\nactual size of the buffer that we ultimately need to allocate. But\nreading through readlink(3p) doesn't indicate anything, and I'm not sure\nwhether we can always rely on lstat(3p) to return the correct size for\nsymlink contents on all platforms.\n\nOne thing that _is_ noted though is that calling the function with a\nbuffer size larger than SSIZE_MAX is implementation-defined. It does\nmake me a bit uneasy in that light to grow indefinitely.\n\nWhich makes me wonder whether Windows has a limit for the symlink\ncontents that we could enforce in theory so that we can reasonably turn\nthis into a bounded loop again?\n\nPatrick\n"},{"id":"532391","messageId":"xmqqy0n0znib.fsf@gitster.g","threadId":"64636","inReplyTo":"db1feb2293d20532f9468ab63ede43d4fc620203.1765899229.git.gitgitgadget@gmail.com","subject":"Re: [PATCH 4/5] strbuf_readlink(): support link targets that exceed PATH_MAX","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-12-17T23:39:40Z","receivedAt":"2025-12-17T23:39:43Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"Karsten Blees via GitGitGadget\" <gitgitgadget@gmail.com> writes:\n\n> From: Karsten Blees <blees@dcon.de>\n>\n> The `strbuf_readlink()` function refuses to read link targets that\n> exceed PATH_MAX (even if a sufficient size was specified by the caller).\n>\n> As some platforms (*cough* Windows *cough*) support longer paths, remove\n> this restriction (similar to `strbuf_getcwd()`).\n>\n> Signed-off-by: Karsten Blees <blees@dcon.de>\n> Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n> ---\n>  strbuf.c | 4 +---\n>  1 file changed, 1 insertion(+), 3 deletions(-)\n\nWe've been bitten before by platforms that sets PATH_MAX too low\n(i.e., lower than what they comfortably support), so this is a\nwelcome change.\n\n> diff --git a/strbuf.c b/strbuf.c\n> index 44a8f6a554..fa4e30f112 100644\n> --- a/strbuf.c\n> +++ b/strbuf.c\n> @@ -566,8 +566,6 @@ ssize_t strbuf_write(struct strbuf *sb, FILE *f)\n>  \treturn sb->len ? fwrite(sb->buf, 1, sb->len, f) : 0;\n>  }\n>  \n> -#define STRBUF_MAXLINK (2*PATH_MAX)\n> -\n>  int strbuf_readlink(struct strbuf *sb, const char *path, size_t hint)\n>  {\n>  \tsize_t oldalloc = sb->alloc;\n> @@ -575,7 +573,7 @@ int strbuf_readlink(struct strbuf *sb, const char *path, size_t hint)\n>  \tif (hint < 32)\n>  \t\thint = 32;\n>  \n> -\twhile (hint < STRBUF_MAXLINK) {\n> +\tfor (;;) {\n>  \t\tssize_t len;\n>  \n>  \t\tstrbuf_grow(sb, hint + 1);\n\nI briefly wondered if this would cause us loop infinitely on a truly\nbroken platform, where readlink() somehow keeps returning negative,\nbut we only retry when we got ERANGE (which can be seen several\nlines below the postimage of hte patch), so we should be safe.\n\nThanks.\n"},{"id":"532529","messageId":"5778a03b-2e33-9224-e051-664c2d530fc3@gmx.de","threadId":"64636","inReplyTo":"aULB3wCFGsbZbuSw@pks.im","subject":"Re: [PATCH 4/5] strbuf_readlink(): support link targets that exceed PATH_MAX","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2025-12-19T08:50:15Z","receivedAt":"2025-12-19T08:50:21Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Patrick,\n\nOn Wed, 17 Dec 2025, Patrick Steinhardt wrote:\n\n> On Tue, Dec 16, 2025 at 03:33:48PM +0000, Karsten Blees via GitGitGadget wrote:\n> > diff --git a/strbuf.c b/strbuf.c\n> > index 44a8f6a554..fa4e30f112 100644\n> > --- a/strbuf.c\n> > +++ b/strbuf.c\n> > @@ -566,8 +566,6 @@ ssize_t strbuf_write(struct strbuf *sb, FILE *f)\n> >  \treturn sb->len ? fwrite(sb->buf, 1, sb->len, f) : 0;\n> >  }\n> >  \n> > -#define STRBUF_MAXLINK (2*PATH_MAX)\n> > -\n> >  int strbuf_readlink(struct strbuf *sb, const char *path, size_t hint)\n> >  {\n> >  \tsize_t oldalloc = sb->alloc;\n> > @@ -575,7 +573,7 @@ int strbuf_readlink(struct strbuf *sb, const char *path, size_t hint)\n> >  \tif (hint < 32)\n> >  \t\thint = 32;\n> >  \n> > -\twhile (hint < STRBUF_MAXLINK) {\n> > +\tfor (;;) {\n> >  \t\tssize_t len;\n> >  \n> >  \t\tstrbuf_grow(sb, hint + 1);\n> \n> This makes me wonder whether we have a better way to figure out the\n> actual size of the buffer that we ultimately need to allocate. But\n> reading through readlink(3p) doesn't indicate anything, and I'm not sure\n> whether we can always rely on lstat(3p) to return the correct size for\n> symlink contents on all platforms.\n> \n> One thing that _is_ noted though is that calling the function with a\n> buffer size larger than SSIZE_MAX is implementation-defined. It does\n> make me a bit uneasy in that light to grow indefinitely.\n> \n> Which makes me wonder whether Windows has a limit for the symlink\n> contents that we could enforce in theory so that we can reasonably turn\n> this into a bounded loop again?\n\nhttps://learn.microsoft.com/en-us/windows/win32/fileio/maximum-file-path-limitation\nsuggests that the maximum permissible target path should be 32,768. But\nthat's not _quite_ correct, as\n`../t/../Documentation/RelNotes/../../README.md` is a perfectly valid (if\nawkward) symlink target.\n\nStill, I would say that 32,768 would make for a fine (still insanely high,\nbut not so high as to allow malicious symlinks to cause memory problems)\nlimit.\n\nSound good?\nJohannes\n"},{"id":"532539","messageId":"aUU8O6ltrNj-FmjZ@pks.im","threadId":"64636","inReplyTo":"5778a03b-2e33-9224-e051-664c2d530fc3@gmx.de","subject":"Re: [PATCH 4/5] strbuf_readlink(): support link targets that exceed PATH_MAX","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-12-19T11:51:23Z","receivedAt":"2025-12-19T11:51:31Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"On Fri, Dec 19, 2025 at 09:50:15AM +0100, Johannes Schindelin wrote:\n> Hi Patrick,\n> \n> On Wed, 17 Dec 2025, Patrick Steinhardt wrote:\n> \n> > On Tue, Dec 16, 2025 at 03:33:48PM +0000, Karsten Blees via GitGitGadget wrote:\n> > > diff --git a/strbuf.c b/strbuf.c\n> > > index 44a8f6a554..fa4e30f112 100644\n> > > --- a/strbuf.c\n> > > +++ b/strbuf.c\n> > > @@ -566,8 +566,6 @@ ssize_t strbuf_write(struct strbuf *sb, FILE *f)\n> > >  \treturn sb->len ? fwrite(sb->buf, 1, sb->len, f) : 0;\n> > >  }\n> > >  \n> > > -#define STRBUF_MAXLINK (2*PATH_MAX)\n> > > -\n> > >  int strbuf_readlink(struct strbuf *sb, const char *path, size_t hint)\n> > >  {\n> > >  \tsize_t oldalloc = sb->alloc;\n> > > @@ -575,7 +573,7 @@ int strbuf_readlink(struct strbuf *sb, const char *path, size_t hint)\n> > >  \tif (hint < 32)\n> > >  \t\thint = 32;\n> > >  \n> > > -\twhile (hint < STRBUF_MAXLINK) {\n> > > +\tfor (;;) {\n> > >  \t\tssize_t len;\n> > >  \n> > >  \t\tstrbuf_grow(sb, hint + 1);\n> > \n> > This makes me wonder whether we have a better way to figure out the\n> > actual size of the buffer that we ultimately need to allocate. But\n> > reading through readlink(3p) doesn't indicate anything, and I'm not sure\n> > whether we can always rely on lstat(3p) to return the correct size for\n> > symlink contents on all platforms.\n> > \n> > One thing that _is_ noted though is that calling the function with a\n> > buffer size larger than SSIZE_MAX is implementation-defined. It does\n> > make me a bit uneasy in that light to grow indefinitely.\n> > \n> > Which makes me wonder whether Windows has a limit for the symlink\n> > contents that we could enforce in theory so that we can reasonably turn\n> > this into a bounded loop again?\n> \n> https://learn.microsoft.com/en-us/windows/win32/fileio/maximum-file-path-limitation\n> suggests that the maximum permissible target path should be 32,768. But\n> that's not _quite_ correct, as\n> `../t/../Documentation/RelNotes/../../README.md` is a perfectly valid (if\n> awkward) symlink target.\n> \n> Still, I would say that 32,768 would make for a fine (still insanely high,\n> but not so high as to allow malicious symlinks to cause memory problems)\n> limit.\n> \n> Sound good?\n> Johannes\n\nSounds good to me, thanks!\n\nPatrick\n"},{"id":"532821","messageId":"xmqqcy3wh8d1.fsf@gitster.g","threadId":"64636","inReplyTo":"aUU8O6ltrNj-FmjZ@pks.im","subject":"Re: [PATCH 4/5] strbuf_readlink(): support link targets that exceed PATH_MAX","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-12-30T05:00:26Z","receivedAt":"2025-12-30T05:00:29Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Patrick Steinhardt <ps@pks.im> writes:\n\n>> > This makes me wonder whether we have a better way to figure out the\n>> > actual size of the buffer that we ultimately need to allocate. But\n>> > reading through readlink(3p) doesn't indicate anything, and I'm not sure\n>> > whether we can always rely on lstat(3p) to return the correct size for\n>> > symlink contents on all platforms.\n>> > \n>> > One thing that _is_ noted though is that calling the function with a\n>> > buffer size larger than SSIZE_MAX is implementation-defined. It does\n>> > make me a bit uneasy in that light to grow indefinitely.\n>> > \n>> > Which makes me wonder whether Windows has a limit for the symlink\n>> > contents that we could enforce in theory so that we can reasonably turn\n>> > this into a bounded loop again?\n>> \n>> https://learn.microsoft.com/en-us/windows/win32/fileio/maximum-file-path-limitation\n>> suggests that the maximum permissible target path should be 32,768. But\n>> that's not _quite_ correct, as\n>> `../t/../Documentation/RelNotes/../../README.md` is a perfectly valid (if\n>> awkward) symlink target.\n>> \n>> Still, I would say that 32,768 would make for a fine (still insanely high,\n>> but not so high as to allow malicious symlinks to cause memory problems)\n>> limit.\n>> \n>> Sound good?\n>> Johannes\n>\n> Sounds good to me, thanks!\n\nAs this is a generic codepath in strbuf.c, platforms that do not\nhonor Microsoft's promise cited above can break the assumption made\nhere by going beyond 32k, no?\n\nI am OK if this infinite loop had our own \"we are growing the buffer\nvery long and still getting not-enough-buf error; let's give up\"\ntermination condition.\n\nIOW, a simpler alternative may be\n\n---- >8 ----\nSubject: strbuf_readlink(): do not trust PATH_MAX\n\nWe have been bitten before by platforms that sets PATH_MAX way too\nlow, far below the length of paths they comfortably support.  The\nstrbuf_readlink() limits the link targets to PATH_MAX, which is a\ncode path that is broken by such platforms.\n\nRaise the limit to 32kB, which matches the limit of a\nplatform with such a problem [*].\n\n * https://learn.microsoft.com/en-us/windows/win32/fileio/maximum-file-path-limitation\n\n strbuf.c | 6 +++++-\n 1 file changed, 5 insertions(+), 1 deletion(-)\n\ndiff --git c/strbuf.c w/strbuf.c\nindex 7fb7d12ac0..1c7659bcd2 100644\n--- c/strbuf.c\n+++ w/strbuf.c\n@@ -566,7 +566,11 @@ ssize_t strbuf_write(struct strbuf *sb, FILE *f)\n \treturn sb->len ? fwrite(sb->buf, 1, sb->len, f) : 0;\n }\n \n-#define STRBUF_MAXLINK (2*PATH_MAX)\n+/*\n+ * Do not use PATH_MAX, as some platforms sets it too low;\n+ * 32kB matches what Windows has as the real limit for a pathnname.\n+ */\n+#define STRBUF_MAXLINK (2 * (1 << 15))\n \n int strbuf_readlink(struct strbuf *sb, const char *path, size_t hint)\n {\n"},{"id":"533411","messageId":"pull.2017.v2.git.1767989109.gitgitgadget@gmail.com","threadId":"64636","inReplyTo":"pull.2017.git.1765899229.gitgitgadget@gmail.com","subject":"[PATCH v2 0/5] Last preparations before upstreaming Git for Windows' symlink support","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2026-01-09T20:05:04Z","receivedAt":"2026-01-09T20:05:22Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"After preparing Git's test suite for the upcoming support for symlinks on\nWindows, this patch series touches up a couple of code paths that might not\nseem to be related at first, but need to be adjusted for the symlink support\nto work as expected.\n\nThis is based on js/test-symlink-windows.\n\nChanges since v1:\n\n * Fixed Karsten's email address\n * Instead of allowing unlimited symlink target lengths, it is now increased\n   from 2*PATH_MAX to 32,767.\n\nJohannes Schindelin (3):\n  mingw: do resolve symlinks in `getcwd()`\n  init: do parse _all_ core.* settings early\n  strbuf_readlink(): support link targets that exceed 2*PATH_MAX\n\nKarsten Blees (2):\n  strbuf_readlink(): avoid calling `readlink()` twice in corner-cases\n  trim_last_path_component(): avoid hard-coding the directory separator\n\n compat/mingw.c | 18 +++++++-----------\n environment.c  |  4 ++--\n environment.h  |  2 ++\n lockfile.c     |  4 ++--\n setup.c        |  2 +-\n strbuf.c       |  8 ++++----\n 6 files changed, 18 insertions(+), 20 deletions(-)\n\n\nbase-commit: ef6dd000ad813fc34a05c4b9055578df13a2eaa6\nPublished-As: https://github.com/gitgitgadget/git/releases/tag/pr-2017%2Fdscho%2Flast-preparations-before-mingw-symlinks-support-next-v2\nFetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-2017/dscho/last-preparations-before-mingw-symlinks-support-next-v2\nPull-Request: https://github.com/gitgitgadget/git/pull/2017\n\nRange-diff vs v1:\n\n 1:  1928738b464 = 1:  eb95a74d6eb mingw: do resolve symlinks in `getcwd()`\n 2:  31497b01988 = 2:  9fee7bd16f5 init: do parse _all_ core.* settings early\n 3:  dba281027a8 ! 3:  7fe463d68aa strbuf_readlink(): avoid calling `readlink()` twice in corner-cases\n     @@\n       ## Metadata ##\n     -Author: Karsten Blees <blees@dcon.de>\n     +Author: Karsten Blees <karsten.blees@gmail.com>\n      \n       ## Commit message ##\n          strbuf_readlink(): avoid calling `readlink()` twice in corner-cases\n     @@ Commit message\n      \n          Use `hint + 1` as buffer size to prevent this.\n      \n     -    Signed-off-by: Karsten Blees <blees@dcon.de>\n     +    Signed-off-by: Karsten Blees <karsten.blees@gmail.com>\n          Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n      \n       ## strbuf.c ##\n 4:  db1feb2293d ! 4:  accb6d5f0ae strbuf_readlink(): support link targets that exceed PATH_MAX\n     @@\n       ## Metadata ##\n     -Author: Karsten Blees <blees@dcon.de>\n     +Author: Johannes Schindelin <Johannes.Schindelin@gmx.de>\n      \n       ## Commit message ##\n     -    strbuf_readlink(): support link targets that exceed PATH_MAX\n     +    strbuf_readlink(): support link targets that exceed 2*PATH_MAX\n      \n          The `strbuf_readlink()` function refuses to read link targets that\n     -    exceed PATH_MAX (even if a sufficient size was specified by the caller).\n     +    exceed 2*PATH_MAX (even if a sufficient size was specified by the\n     +    caller).\n      \n     -    As some platforms (*cough* Windows *cough*) support longer paths, remove\n     -    this restriction (similar to `strbuf_getcwd()`).\n     +    The reason that that limit is 2*PATH_MAX instead of PATH_MAX is that\n     +    the symlink targets do not need to be normalized. After running\n     +    `ln -s a/../a/../a/../a/../b c`, the target of the symlink `c` will not\n     +    be normalized to `b` but instead be much longer. As such, symlink\n     +    targets' lengths can far exceed PATH_MAX.\n      \n     -    Signed-off-by: Karsten Blees <blees@dcon.de>\n     +    They are frequently much longer than 2*PATH_MAX on Windows, which\n     +    actually supports paths up to 32,767 characters, but sets PATH_MAX to\n     +    260 for backwards compatibility. For full details, see\n     +    https://learn.microsoft.com/en-us/windows/win32/fileio/maximum-file-path-limitation\n     +\n     +    Let's just hard-code the limit used by `strbuf_readlink()` to 32,767 and\n     +    make it independent of the current platform's PATH_MAX.\n     +\n     +    Based-on-a-patch-by: Karsten Blees <karsten.blees@gmail.com>\n          Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n      \n       ## strbuf.c ##\n     @@ strbuf.c: ssize_t strbuf_write(struct strbuf *sb, FILE *f)\n       }\n       \n      -#define STRBUF_MAXLINK (2*PATH_MAX)\n     --\n     ++#define STRBUF_MAXLINK (32767)\n     + \n       int strbuf_readlink(struct strbuf *sb, const char *path, size_t hint)\n       {\n     - \tsize_t oldalloc = sb->alloc;\n     -@@ strbuf.c: int strbuf_readlink(struct strbuf *sb, const char *path, size_t hint)\n     - \tif (hint < 32)\n     - \t\thint = 32;\n     - \n     --\twhile (hint < STRBUF_MAXLINK) {\n     -+\tfor (;;) {\n     - \t\tssize_t len;\n     - \n     - \t\tstrbuf_grow(sb, hint + 1);\n 5:  3521180e0f7 ! 5:  9823cbb6df8 trim_last_path_component(): avoid hard-coding the directory separator\n     @@\n       ## Metadata ##\n     -Author: Karsten Blees <blees@dcon.de>\n     +Author: Karsten Blees <karsten.blees@gmail.com>\n      \n       ## Commit message ##\n          trim_last_path_component(): avoid hard-coding the directory separator\n     @@ Commit message\n      \n          Prepare that function to be useful also in that context.\n      \n     -    Signed-off-by: Karsten Blees <blees@dcon.de>\n     +    Signed-off-by: Karsten Blees <karsten.blees@gmail.com>\n          Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n      \n       ## lockfile.c ##\n\n-- \ngitgitgadget\n"},{"id":"533414","messageId":"eb95a74d6eb2198620d44f2877519a1a7ffef6d8.1767989109.git.gitgitgadget@gmail.com","threadId":"64636","inReplyTo":"pull.2017.v2.git.1767989109.gitgitgadget@gmail.com","subject":"[PATCH v2 1/5] mingw: do resolve symlinks in `getcwd()`","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2026-01-09T20:05:05Z","receivedAt":"2026-01-09T20:05:23Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nAs pointed out in https://github.com/git-for-windows/git/issues/1676,\nthe `git rev-parse --is-inside-work-tree` command currently fails when\nthe current directory's path contains symbolic links.\n\nThe underlying reason for this bug is that `getcwd()` is supposed to\nresolve symbolic links, but our `mingw_getcwd()` implementation did not.\n\nWe do have all the building blocks for that, though: the\n`GetFinalPathByHandleW()` function will resolve symbolic links. However,\nwe only called that function if `GetLongPathNameW()` failed, for\nhistorical reasons: the latter function was supported for a long time,\nbut the former API function was introduced only with Windows Vista, and\nwe used to support also Windows XP. With that support having been\ndropped, we are free to call the symbolic link-resolving function right\naway.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n compat/mingw.c | 18 +++++++-----------\n 1 file changed, 7 insertions(+), 11 deletions(-)\n\ndiff --git a/compat/mingw.c b/compat/mingw.c\nindex ba1b7b6dd1..7215b127cc 100644\n--- a/compat/mingw.c\n+++ b/compat/mingw.c\n@@ -1251,18 +1251,16 @@ char *mingw_getcwd(char *pointer, int len)\n {\n \twchar_t cwd[MAX_PATH], wpointer[MAX_PATH];\n \tDWORD ret = GetCurrentDirectoryW(ARRAY_SIZE(cwd), cwd);\n+\tHANDLE hnd;\n \n \tif (!ret || ret >= ARRAY_SIZE(cwd)) {\n \t\terrno = ret ? ENAMETOOLONG : err_win_to_posix(GetLastError());\n \t\treturn NULL;\n \t}\n-\tret = GetLongPathNameW(cwd, wpointer, ARRAY_SIZE(wpointer));\n-\tif (!ret && GetLastError() == ERROR_ACCESS_DENIED) {\n-\t\tHANDLE hnd = CreateFileW(cwd, 0,\n-\t\t\tFILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,\n-\t\t\tOPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);\n-\t\tif (hnd == INVALID_HANDLE_VALUE)\n-\t\t\treturn NULL;\n+\thnd = CreateFileW(cwd, 0,\n+\t\t\t  FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,\n+\t\t\t  OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);\n+\tif (hnd != INVALID_HANDLE_VALUE) {\n \t\tret = GetFinalPathNameByHandleW(hnd, wpointer, ARRAY_SIZE(wpointer), 0);\n \t\tCloseHandle(hnd);\n \t\tif (!ret || ret >= ARRAY_SIZE(wpointer))\n@@ -1271,13 +1269,11 @@ char *mingw_getcwd(char *pointer, int len)\n \t\t\treturn NULL;\n \t\treturn pointer;\n \t}\n-\tif (!ret || ret >= ARRAY_SIZE(wpointer))\n-\t\treturn NULL;\n-\tif (GetFileAttributesW(wpointer) == INVALID_FILE_ATTRIBUTES) {\n+\tif (GetFileAttributesW(cwd) == INVALID_FILE_ATTRIBUTES) {\n \t\terrno = ENOENT;\n \t\treturn NULL;\n \t}\n-\tif (xwcstoutf(pointer, wpointer, len) < 0)\n+\tif (xwcstoutf(pointer, cwd, len) < 0)\n \t\treturn NULL;\n \tconvert_slashes(pointer);\n \treturn pointer;\n-- \ngitgitgadget\n\n"},{"id":"533417","messageId":"9fee7bd16f52590c497124e81af495f76b162baf.1767989109.git.gitgitgadget@gmail.com","threadId":"64636","inReplyTo":"pull.2017.v2.git.1767989109.gitgitgadget@gmail.com","subject":"[PATCH v2 2/5] init: do parse _all_ core.* settings early","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2026-01-09T20:05:06Z","receivedAt":"2026-01-09T20:05:25Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nIn Git for Windows, `has_symlinks` is set to 0 by default. Therefore, we\nneed to parse the config setting `core.symlinks` to know if it has been\nset to `true`. In `git init`, we must do that before copying the\ntemplates because they might contain symbolic links.\n\nEven if the support for symbolic links on Windows has not made it to\nupstream Git yet, we really should make sure that all the `core.*`\nsettings are parsed before proceeding, as they might very well change\nthe behavior of `git init` in a way the user intended.\n\nThis fixes https://github.com/git-for-windows/git/issues/3414\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n environment.c | 4 ++--\n environment.h | 2 ++\n setup.c       | 2 +-\n 3 files changed, 5 insertions(+), 3 deletions(-)\n\ndiff --git a/environment.c b/environment.c\nindex a770b5921d..b65b85a01f 100644\n--- a/environment.c\n+++ b/environment.c\n@@ -324,8 +324,8 @@ next_name:\n \treturn (current & ~negative) | positive;\n }\n \n-static int git_default_core_config(const char *var, const char *value,\n-\t\t\t\t   const struct config_context *ctx, void *cb)\n+int git_default_core_config(const char *var, const char *value,\n+\t\t\t    const struct config_context *ctx, void *cb)\n {\n \t/* This needs a better name */\n \tif (!strcmp(var, \"core.filemode\")) {\ndiff --git a/environment.h b/environment.h\nindex 51898c99cd..e61f843fdb 100644\n--- a/environment.h\n+++ b/environment.h\n@@ -106,6 +106,8 @@ const char *strip_namespace(const char *namespaced_ref);\n \n int git_default_config(const char *, const char *,\n \t\t       const struct config_context *, void *);\n+int git_default_core_config(const char *var, const char *value,\n+\t\t\t    const struct config_context *ctx, void *cb);\n \n /*\n  * TODO: All the below state either explicitly or implicitly relies on\ndiff --git a/setup.c b/setup.c\nindex 7086741e6c..42e4e7a690 100644\n--- a/setup.c\n+++ b/setup.c\n@@ -2611,7 +2611,7 @@ int init_db(const char *git_dir, const char *real_git_dir,\n \t * have set up the repository format such that we can evaluate\n \t * includeIf conditions correctly in the case of re-initialization.\n \t */\n-\trepo_config(the_repository, platform_core_config, NULL);\n+\trepo_config(the_repository, git_default_core_config, NULL);\n \n \tsafe_create_dir(the_repository, git_dir, 0);\n \n-- \ngitgitgadget\n\n"},{"id":"533420","messageId":"7fe463d68aa58fd563053ee1cb87b2a8c1152957.1767989109.git.gitgitgadget@gmail.com","threadId":"64636","inReplyTo":"pull.2017.v2.git.1767989109.gitgitgadget@gmail.com","subject":"[PATCH v2 3/5] strbuf_readlink(): avoid calling `readlink()` twice in corner-cases","fromName":"Karsten Blees via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2026-01-09T20:05:07Z","receivedAt":"2026-01-09T20:05:27Z","isPatch":true,"sender":{"key":"karsten.blees@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1111200?v=4"},"body":"From: Karsten Blees <karsten.blees@gmail.com>\n\nThe `strbuf_readlink()` function calls `readlink()`` twice if the hint\nargument specifies the exact size of the link target (e.g. by passing\nstat.st_size as returned by `lstat()`). This is necessary because\n`readlink(..., hint) == hint` could mean that the buffer was too small.\n\nUse `hint + 1` as buffer size to prevent this.\n\nSigned-off-by: Karsten Blees <karsten.blees@gmail.com>\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n strbuf.c | 6 +++---\n 1 file changed, 3 insertions(+), 3 deletions(-)\n\ndiff --git a/strbuf.c b/strbuf.c\nindex 6c3851a7f8..44a8f6a554 100644\n--- a/strbuf.c\n+++ b/strbuf.c\n@@ -578,12 +578,12 @@ int strbuf_readlink(struct strbuf *sb, const char *path, size_t hint)\n \twhile (hint < STRBUF_MAXLINK) {\n \t\tssize_t len;\n \n-\t\tstrbuf_grow(sb, hint);\n-\t\tlen = readlink(path, sb->buf, hint);\n+\t\tstrbuf_grow(sb, hint + 1);\n+\t\tlen = readlink(path, sb->buf, hint + 1);\n \t\tif (len < 0) {\n \t\t\tif (errno != ERANGE)\n \t\t\t\tbreak;\n-\t\t} else if (len < hint) {\n+\t\t} else if (len <= hint) {\n \t\t\tstrbuf_setlen(sb, len);\n \t\t\treturn 0;\n \t\t}\n-- \ngitgitgadget\n\n"},{"id":"533422","messageId":"accb6d5f0aef9ab5de6da8e9d08ad59a19ef5157.1767989109.git.gitgitgadget@gmail.com","threadId":"64636","inReplyTo":"pull.2017.v2.git.1767989109.gitgitgadget@gmail.com","subject":"[PATCH v2 4/5] strbuf_readlink(): support link targets that exceed 2*PATH_MAX","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2026-01-09T20:05:08Z","receivedAt":"2026-01-09T20:05:29Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"From: Johannes Schindelin <Johannes.Schindelin@gmx.de>\n\nThe `strbuf_readlink()` function refuses to read link targets that\nexceed 2*PATH_MAX (even if a sufficient size was specified by the\ncaller).\n\nThe reason that that limit is 2*PATH_MAX instead of PATH_MAX is that\nthe symlink targets do not need to be normalized. After running\n`ln -s a/../a/../a/../a/../b c`, the target of the symlink `c` will not\nbe normalized to `b` but instead be much longer. As such, symlink\ntargets' lengths can far exceed PATH_MAX.\n\nThey are frequently much longer than 2*PATH_MAX on Windows, which\nactually supports paths up to 32,767 characters, but sets PATH_MAX to\n260 for backwards compatibility. For full details, see\nhttps://learn.microsoft.com/en-us/windows/win32/fileio/maximum-file-path-limitation\n\nLet's just hard-code the limit used by `strbuf_readlink()` to 32,767 and\nmake it independent of the current platform's PATH_MAX.\n\nBased-on-a-patch-by: Karsten Blees <karsten.blees@gmail.com>\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n strbuf.c | 2 +-\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\ndiff --git a/strbuf.c b/strbuf.c\nindex 44a8f6a554..ec2b7afbe6 100644\n--- a/strbuf.c\n+++ b/strbuf.c\n@@ -566,7 +566,7 @@ ssize_t strbuf_write(struct strbuf *sb, FILE *f)\n \treturn sb->len ? fwrite(sb->buf, 1, sb->len, f) : 0;\n }\n \n-#define STRBUF_MAXLINK (2*PATH_MAX)\n+#define STRBUF_MAXLINK (32767)\n \n int strbuf_readlink(struct strbuf *sb, const char *path, size_t hint)\n {\n-- \ngitgitgadget\n\n"},{"id":"533423","messageId":"9823cbb6df84eedfa40d7120d3949f70d2f960ba.1767989109.git.gitgitgadget@gmail.com","threadId":"64636","inReplyTo":"pull.2017.v2.git.1767989109.gitgitgadget@gmail.com","subject":"[PATCH v2 5/5] trim_last_path_component(): avoid hard-coding the directory separator","fromName":"Karsten Blees via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2026-01-09T20:05:09Z","receivedAt":"2026-01-09T20:05:30Z","isPatch":true,"sender":{"key":"karsten.blees@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1111200?v=4"},"body":"From: Karsten Blees <karsten.blees@gmail.com>\n\nCurrently, this function hard-codes the directory separator as the\nforward slash.\n\nHowever, on Windows the backslash character is valid, too. And we want\nto call this function in the upcoming support for symlinks on Windows\nwith the symlink targets (which naturally use the canonical directory\nseparator on Windows, which is _not_ the forward slash).\n\nPrepare that function to be useful also in that context.\n\nSigned-off-by: Karsten Blees <karsten.blees@gmail.com>\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n lockfile.c | 4 ++--\n 1 file changed, 2 insertions(+), 2 deletions(-)\n\ndiff --git a/lockfile.c b/lockfile.c\nindex 1d5ed01682..67082a9caa 100644\n--- a/lockfile.c\n+++ b/lockfile.c\n@@ -19,14 +19,14 @@ static void trim_last_path_component(struct strbuf *path)\n \tint i = path->len;\n \n \t/* back up past trailing slashes, if any */\n-\twhile (i && path->buf[i - 1] == '/')\n+\twhile (i && is_dir_sep(path->buf[i - 1]))\n \t\ti--;\n \n \t/*\n \t * then go backwards until a slash, or the beginning of the\n \t * string\n \t */\n-\twhile (i && path->buf[i - 1] != '/')\n+\twhile (i && !is_dir_sep(path->buf[i - 1]))\n \t\ti--;\n \n \tstrbuf_setlen(path, i);\n-- \ngitgitgadget\n"},{"id":"533511","messageId":"xmqqh5ssrdzu.fsf@gitster.g","threadId":"64636","inReplyTo":"pull.2017.v2.git.1767989109.gitgitgadget@gmail.com","subject":"Re: [PATCH v2 0/5] Last preparations before upstreaming Git for Windows' symlink support","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-01-11T04:04:53Z","receivedAt":"2026-01-11T04:04:56Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"Johannes Schindelin via GitGitGadget\" <gitgitgadget@gmail.com>\nwrites:\n\n> After preparing Git's test suite for the upcoming support for symlinks on\n> Windows, this patch series touches up a couple of code paths that might not\n> seem to be related at first, but need to be adjusted for the symlink support\n> to work as expected.\n>\n> This is based on js/test-symlink-windows.\n>\n> Changes since v1:\n>\n>  * Fixed Karsten's email address\n>  * Instead of allowing unlimited symlink target lengths, it is now increased\n>    from 2*PATH_MAX to 32,767.\n\nLooking good.\n\nLet's wait for a few days to see if others have further input, and\nthen mark the topic, together with the other windows-symlink topic\nthat comes on top of this one, for 'next'.\n\nThanks.\n"},{"id":"533566","messageId":"aWSyXfxOkpWMgX6D@pks.im","threadId":"64636","inReplyTo":"pull.2017.v2.git.1767989109.gitgitgadget@gmail.com","subject":"Re: [PATCH v2 0/5] Last preparations before upstreaming Git for Windows' symlink support","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2026-01-12T08:35:41Z","receivedAt":"2026-01-12T08:35:46Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"On Fri, Jan 09, 2026 at 08:05:04PM +0000, Johannes Schindelin via GitGitGadget wrote:\n> After preparing Git's test suite for the upcoming support for symlinks on\n> Windows, this patch series touches up a couple of code paths that might not\n> seem to be related at first, but need to be adjusted for the symlink support\n> to work as expected.\n> \n> This is based on js/test-symlink-windows.\n> \n> Changes since v1:\n> \n>  * Fixed Karsten's email address\n>  * Instead of allowing unlimited symlink target lengths, it is now increased\n>    from 2*PATH_MAX to 32,767.\n\nThanks, this version looks good to me based on the range-diff.\n\nPatrick\n"}]}