public inbox for git@vger.kernel.org 
 help / color / mirror / Atom feed
From: "Johannes Schindelin via GitGitGadget" <gitgitgadget@gmail•com>
To: git@vger•kernel.org
Cc: Ben Knoble <ben.knoble@gmail•com>, Johannes Sixt <j6t@kdbg•org>,
	Karsten Blees <karsten.blees@gmail•com>,
	Johannes Schindelin <johannes.schindelin@gmx•de>
Subject: [PATCH v2 00/18] Support symbolic links on Windows
Date: Fri, 09 Jan 2026 20:04:57 +0000	[thread overview]
Message-ID: <pull.2018.v2.git.1767989115.gitgitgadget@gmail.com> (raw)
In-Reply-To: <pull.2018.git.1765980535.gitgitgadget@gmail.com>

This finally upstreams Git for Windows' support for Windows' branch of
symbolic links, which has been maturing since 2015. It is based off of
js/prep-symlink-windows.

Changes since v1:

 * Changed Karsten's email address to the current one.
 * The changes to do_lstat() are now reflected by the comment preceding that
   function.
 * The commit message mentioning some ELOOP logic when symlink support is
   disabled, which had no corresponding part in the commit's diff, was
   adjusted to no longer mention that long gone change.
 * Fixed the typo "woutl".
 * Dropped a misleading, stale comment about ENOSYS in the readlink()
   implementation (talking about a long-dropped part of the original patch).

Bill Zissimopoulos (1):
  mingw: compute the correct size for symlinks in `mingw_lstat()`

Johannes Schindelin (3):
  mingw: try to create symlinks without elevated permissions
  mingw: emulate `stat()` a little more faithfully
  mingw: special-case index entries for symlinks with buggy size

Karsten Blees (14):
  mingw: don't call `GetFileAttributes()` twice in `mingw_lstat()`
  mingw: implement `stat()` with symlink support
  mingw: drop the separate `do_lstat()` function
  mingw: let `mingw_lstat()` error early upon problems with reparse
    points
  mingw: teach dirent about symlinks
  mingw: factor out the retry logic
  mingw: change default of `core.symlinks` to false
  mingw: add symlink-specific error codes
  mingw: handle symlinks to directories in `mingw_unlink()`
  mingw: support renaming symlinks
  mingw: allow `mingw_chdir()` to change to symlink-resolved directories
  mingw: implement `readlink()`
  mingw: implement basic `symlink()` functionality (file symlinks only)
  mingw: add support for symlinks to directories

 compat/mingw-posix.h  |   6 +-
 compat/mingw.c        | 635 ++++++++++++++++++++++++++++++++----------
 compat/win32.h        |   6 +-
 compat/win32/dirent.c |   5 +-
 read-cache.c          |  11 +
 5 files changed, 507 insertions(+), 156 deletions(-)


base-commit: 1887b3dd06823575e37ad19b5827d467e126c6ed
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-2018%2Fdscho%2Fsymlinks-next-v2
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-2018/dscho/symlinks-next-v2
Pull-Request: https://github.com/gitgitgadget/git/pull/2018

Range-diff vs v1:

  1:  dae450dd0e !  1:  6ec4ff7457 mingw: don't call `GetFileAttributes()` twice in `mingw_lstat()`
     @@
       ## Metadata ##
     -Author: Karsten Blees <blees@dcon•de>
     +Author: Karsten Blees <karsten.blees@gmail•com>
      
       ## Commit message ##
          mingw: don't call `GetFileAttributes()` twice in `mingw_lstat()`
     @@ Commit message
          free, and also have a (wide char) buffer that can be modified. This
          makes it easy to avoid that extraneous Win32 API call.
      
     -    Signed-off-by: Karsten Blees <blees@dcon•de>
     +    Signed-off-by: Karsten Blees <karsten.blees@gmail•com>
          Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx•de>
      
       ## compat/mingw.c ##
     +@@ compat/mingw.c: static int has_valid_directory_prefix(wchar_t *wfilename)
     + }
     + 
     + /* We keep the do_lstat code in a separate function to avoid recursion.
     +- * When a path ends with a slash, the stat will fail with ENOENT. In
     +- * this case, we strip the trailing slashes and stat again.
     ++ * When a path ends with a slash, the call to `GetFileAttributedExW()`
     ++ * would fail. To prevent this, we strip any trailing slashes before that
     ++ * call.
     +  *
     +  * If follow is true then act like stat() and report on the link
     +  * target. Otherwise report on the link itself.
      @@ compat/mingw.c: static int do_lstat(int follow, const char *file_name, struct stat *buf)
       {
       	WIN32_FILE_ATTRIBUTE_DATA fdata;
  2:  c36848eda7 !  2:  5d83a8ab76 mingw: implement `stat()` with symlink support
     @@
       ## Metadata ##
     -Author: Karsten Blees <blees@dcon•de>
     +Author: Karsten Blees <karsten.blees@gmail•com>
      
       ## Commit message ##
          mingw: implement `stat()` with symlink support
     @@ Commit message
          permissions, and then calling `GetFileInformationByHandle()` on it. This
          way, all links are resolved by the Windows file system layer.
      
     -    If symlinks are disabled, use `mingw_lstat()` as before, but fail with
     -    `ELOOP` if a symlink would have to be resolved.
     -
     -    Signed-off-by: Karsten Blees <blees@dcon•de>
     +    Signed-off-by: Karsten Blees <karsten.blees@gmail•com>
          Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx•de>
      
       ## compat/mingw.c ##
  3:  aa0ca80bbb !  3:  d3953f1826 mingw: drop the separate `do_lstat()` function
     @@
       ## Metadata ##
     -Author: Karsten Blees <blees@dcon•de>
     +Author: Karsten Blees <karsten.blees@gmail•com>
      
       ## Commit message ##
          mingw: drop the separate `do_lstat()` function
     @@ Commit message
          the extra function and the old `mingw_stat()`-specific (`follow == 1`)
          logic.
      
     -    Signed-off-by: Karsten Blees <blees@dcon•de>
     +    Signed-off-by: Karsten Blees <karsten.blees@gmail•com>
          Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx•de>
      
       ## compat/mingw.c ##
     @@ compat/mingw.c: static int has_valid_directory_prefix(wchar_t *wfilename)
       }
       
      -/* We keep the do_lstat code in a separate function to avoid recursion.
     -- * When a path ends with a slash, the stat will fail with ENOENT. In
     -- * this case, we strip the trailing slashes and stat again.
     +- * When a path ends with a slash, the call to `GetFileAttributedExW()`
     +- * would fail. To prevent this, we strip any trailing slashes before that
     +- * call.
      - *
      - * If follow is true then act like stat() and report on the link
      - * target. Otherwise report on the link itself.
  4:  886044373b !  4:  a6355789c3 mingw: let `mingw_lstat()` error early upon problems with reparse points
     @@
       ## Metadata ##
     -Author: Karsten Blees <blees@dcon•de>
     +Author: Karsten Blees <karsten.blees@gmail•com>
      
       ## Commit message ##
          mingw: let `mingw_lstat()` error early upon problems with reparse points
     @@ Commit message
          `--color-moved -w`. That code was _not_ moved because a subsequent
          commit will move it to an altogether different function, anyway.
      
     -    Signed-off-by: Karsten Blees <blees@dcon•de>
     +    Signed-off-by: Karsten Blees <karsten.blees@gmail•com>
          Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx•de>
      
       ## compat/mingw.c ##
  5:  db1d156aa0 !  5:  8b7f5a8fc7 mingw: teach dirent about symlinks
     @@
       ## Metadata ##
     -Author: Karsten Blees <blees@dcon•de>
     +Author: Karsten Blees <karsten.blees@gmail•com>
      
       ## Commit message ##
          mingw: teach dirent about symlinks
     @@ Commit message
      
          Implement `DT_LNK` detection in dirent.c's `readdir()` function.
      
     -    Signed-off-by: Karsten Blees <blees@dcon•de>
     +    Signed-off-by: Karsten Blees <karsten.blees@gmail•com>
          Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx•de>
      
       ## compat/mingw.c ##
  6:  4c49a3d9bf =  6:  1159e86dc4 mingw: compute the correct size for symlinks in `mingw_lstat()`
  7:  ad74d540f2 !  7:  4aeccd6656 mingw: factor out the retry logic
     @@
       ## Metadata ##
     -Author: Karsten Blees <blees@dcon•de>
     +Author: Karsten Blees <karsten.blees@gmail•com>
      
       ## Commit message ##
          mingw: factor out the retry logic
     @@ Commit message
      
          In `mingw_rmdir()`, we include special error handling in the retry loop.
      
     -    Signed-off-by: Karsten Blees <blees@dcon•de>
     +    Signed-off-by: Karsten Blees <karsten.blees@gmail•com>
          Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx•de>
      
       ## compat/mingw.c ##
  8:  25313cea76 !  8:  86c0742748 mingw: change default of `core.symlinks` to false
     @@
       ## Metadata ##
     -Author: Karsten Blees <blees@dcon•de>
     +Author: Karsten Blees <karsten.blees@gmail•com>
      
       ## Commit message ##
          mingw: change default of `core.symlinks` to false
     @@ Commit message
          is true for the time being (an experiment to switch to BusyBox-w32
          failed due to the experimental nature of BusyBox-w32).
      
     -    Signed-off-by: Karsten Blees <blees@dcon•de>
     +    Signed-off-by: Karsten Blees <karsten.blees@gmail•com>
          Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx•de>
      
       ## compat/mingw.c ##
  9:  b698f4a002 !  9:  9a0093d34b mingw: add symlink-specific error codes
     @@
       ## Metadata ##
     -Author: Karsten Blees <blees@dcon•de>
     +Author: Karsten Blees <karsten.blees@gmail•com>
      
       ## Commit message ##
          mingw: add symlink-specific error codes
     @@ Commit message
          Let's handle a couple of symlink-related error codes that will become
          relevant with the upcoming support for symlinks on Windows.
      
     -    Signed-off-by: Karsten Blees <blees@dcon•de>
     +    Signed-off-by: Karsten Blees <karsten.blees@gmail•com>
          Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx•de>
      
       ## compat/mingw.c ##
 10:  282aba42e8 ! 10:  5dc90f9785 mingw: handle symlinks to directories in `mingw_unlink()`
     @@
       ## Metadata ##
     -Author: Karsten Blees <blees@dcon•de>
     +Author: Karsten Blees <karsten.blees@gmail•com>
      
       ## Commit message ##
          mingw: handle symlinks to directories in `mingw_unlink()`
      
          The `_wunlink()` and `DeleteFileW()` functions refuse to delete symlinks
     -    to directories on Windows; The error code woutl be `ERROR_ACCESS_DENIED`
     +    to directories on Windows; The error code would be `ERROR_ACCESS_DENIED`
          in that case. Take that error code as an indicator that we need to try
          `_wrmdir()` as well. In the best case, it will remove a symlink. In the
          worst case, it will fail with the same error code again.
      
     -    Signed-off-by: Karsten Blees <blees@dcon•de>
     +    Signed-off-by: Karsten Blees <karsten.blees@gmail•com>
          Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx•de>
      
       ## compat/mingw.c ##
 11:  5cb3b10500 ! 11:  3670a0a181 mingw: support renaming symlinks
     @@
       ## Metadata ##
     -Author: Karsten Blees <blees@dcon•de>
     +Author: Karsten Blees <karsten.blees@gmail•com>
      
       ## Commit message ##
          mingw: support renaming symlinks
     @@ Commit message
          Avoid the `_wrename()` call, and go with directly calling
          `MoveFileEx()`, with proper error handling of course.
      
     -    Signed-off-by: Karsten Blees <blees@dcon•de>
     +    Signed-off-by: Karsten Blees <karsten.blees@gmail•com>
          Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx•de>
      
       ## compat/mingw.c ##
 12:  49920839bb ! 12:  f79cf31dab mingw: allow `mingw_chdir()` to change to symlink-resolved directories
     @@
       ## Metadata ##
     -Author: Karsten Blees <blees@dcon•de>
     +Author: Karsten Blees <karsten.blees@gmail•com>
      
       ## Commit message ##
          mingw: allow `mingw_chdir()` to change to symlink-resolved directories
     @@ Commit message
          Windows is limited to only MAX_PATH (260) characters. Therefore using
          symlinks and long paths in combination may be problematic.
      
     -    Signed-off-by: Karsten Blees <blees@dcon•de>
     +    Signed-off-by: Karsten Blees <karsten.blees@gmail•com>
          Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx•de>
      
       ## compat/mingw.c ##
 13:  8fef8220f4 ! 13:  1ae9c63a89 mingw: implement `readlink()`
     @@
       ## Metadata ##
     -Author: Karsten Blees <blees@dcon•de>
     +Author: Karsten Blees <karsten.blees@gmail•com>
      
       ## Commit message ##
          mingw: implement `readlink()`
     @@ Commit message
          Implement `readlink()` by reading NTFS reparse points via the
          `read_reparse_point()` function that was introduced earlier to determine
          the length of symlink targets. Works for symlinks and directory
     -    junctions. If symlinks are disabled, fail with `ENOSYS`.
     +    junctions.
      
     -    Signed-off-by: Karsten Blees <blees@dcon•de>
     +    Signed-off-by: Karsten Blees <karsten.blees@gmail•com>
          Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx•de>
      
       ## compat/mingw-posix.h ##
 14:  1dd5f9d6cd ! 14:  4e0ac43ef6 mingw: implement basic `symlink()` functionality (file symlinks only)
     @@
       ## Metadata ##
     -Author: Karsten Blees <blees@dcon•de>
     +Author: Karsten Blees <karsten.blees@gmail•com>
      
       ## Commit message ##
          mingw: implement basic `symlink()` functionality (file symlinks only)
     @@ Commit message
          This implementation fails with `ENOSYS` if symlinks are disabled or
          unsupported.
      
     -    Signed-off-by: Karsten Blees <blees@dcon•de>
     +    Signed-off-by: Karsten Blees <karsten.blees@gmail•com>
          Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx•de>
      
       ## compat/mingw-posix.h ##
 15:  7b6dbc73f7 ! 15:  3d479fd47e mingw: add support for symlinks to directories
     @@
       ## Metadata ##
     -Author: Karsten Blees <blees@dcon•de>
     +Author: Karsten Blees <karsten.blees@gmail•com>
      
       ## Commit message ##
          mingw: add support for symlinks to directories
     @@ Commit message
          to directory or vice versa, or if the target directory is created in
          another process. It's the best Git can do, though.
      
     -    Signed-off-by: Karsten Blees <blees@dcon•de>
     +    Signed-off-by: Karsten Blees <karsten.blees@gmail•com>
          Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx•de>
      
       ## compat/mingw.c ##
 16:  d3b89c29b0 = 16:  fb6aa461da mingw: try to create symlinks without elevated permissions
 17:  2e73ab4221 = 17:  40c3f7f36e mingw: emulate `stat()` a little more faithfully
 18:  817f488523 = 18:  afcf2bbfcb mingw: special-case index entries for symlinks with buggy size

-- 
gitgitgadget

  parent reply	other threads:[~2026-01-09 20:05 UTC|newest]

Thread overview: 51+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-12-17 14:08 [PATCH 00/18] Support symbolic links on Windows Johannes Schindelin via GitGitGadget
2025-12-17 14:08 ` [PATCH 01/18] mingw: don't call `GetFileAttributes()` twice in `mingw_lstat()` Karsten Blees via GitGitGadget
2025-12-18 10:34   ` Johannes Sixt
2025-12-17 14:08 ` [PATCH 02/18] mingw: implement `stat()` with symlink support Karsten Blees via GitGitGadget
2025-12-18 10:44   ` Johannes Sixt
2026-01-09 20:04     ` Johannes Schindelin
2025-12-17 14:08 ` [PATCH 03/18] mingw: drop the separate `do_lstat()` function Karsten Blees via GitGitGadget
2025-12-18 10:48   ` Johannes Sixt
2025-12-17 14:08 ` [PATCH 04/18] mingw: let `mingw_lstat()` error early upon problems with reparse points Karsten Blees via GitGitGadget
2025-12-17 14:08 ` [PATCH 05/18] mingw: teach dirent about symlinks Karsten Blees via GitGitGadget
2025-12-17 14:08 ` [PATCH 06/18] mingw: compute the correct size for symlinks in `mingw_lstat()` Bill Zissimopoulos via GitGitGadget
2025-12-17 14:08 ` [PATCH 07/18] mingw: factor out the retry logic Karsten Blees via GitGitGadget
2025-12-17 14:08 ` [PATCH 08/18] mingw: change default of `core.symlinks` to false Karsten Blees via GitGitGadget
2025-12-17 14:08 ` [PATCH 09/18] mingw: add symlink-specific error codes Karsten Blees via GitGitGadget
2025-12-17 14:08 ` [PATCH 10/18] mingw: handle symlinks to directories in `mingw_unlink()` Karsten Blees via GitGitGadget
2025-12-18  2:49   ` Ben Knoble
2026-01-09 20:04     ` Johannes Schindelin
2025-12-17 14:08 ` [PATCH 11/18] mingw: support renaming symlinks Karsten Blees via GitGitGadget
2025-12-18 17:44   ` Johannes Sixt
2026-01-09 20:04     ` Johannes Schindelin
2025-12-17 14:08 ` [PATCH 12/18] mingw: allow `mingw_chdir()` to change to symlink-resolved directories Karsten Blees via GitGitGadget
2025-12-17 14:08 ` [PATCH 13/18] mingw: implement `readlink()` Karsten Blees via GitGitGadget
2025-12-18 18:13   ` Johannes Sixt
2026-01-09 20:04     ` Johannes Schindelin
2025-12-17 14:08 ` [PATCH 14/18] mingw: implement basic `symlink()` functionality (file symlinks only) Karsten Blees via GitGitGadget
2025-12-17 14:08 ` [PATCH 15/18] mingw: add support for symlinks to directories Karsten Blees via GitGitGadget
2025-12-17 14:08 ` [PATCH 16/18] mingw: try to create symlinks without elevated permissions Johannes Schindelin via GitGitGadget
2025-12-17 14:08 ` [PATCH 17/18] mingw: emulate `stat()` a little more faithfully Johannes Schindelin via GitGitGadget
2025-12-17 14:08 ` [PATCH 18/18] mingw: special-case index entries for symlinks with buggy size Johannes Schindelin via GitGitGadget
2025-12-18  0:00 ` [PATCH 00/18] Support symbolic links on Windows Junio C Hamano
2025-12-18 18:51 ` Johannes Sixt
2025-12-18 19:33   ` Karsten Blees
2026-01-09 20:04 ` Johannes Schindelin via GitGitGadget [this message]
2026-01-09 20:04   ` [PATCH v2 01/18] mingw: don't call `GetFileAttributes()` twice in `mingw_lstat()` Karsten Blees via GitGitGadget
2026-01-09 20:04   ` [PATCH v2 02/18] mingw: implement `stat()` with symlink support Karsten Blees via GitGitGadget
2026-01-09 20:05   ` [PATCH v2 03/18] mingw: drop the separate `do_lstat()` function Karsten Blees via GitGitGadget
2026-01-09 20:05   ` [PATCH v2 04/18] mingw: let `mingw_lstat()` error early upon problems with reparse points Karsten Blees via GitGitGadget
2026-01-09 20:05   ` [PATCH v2 05/18] mingw: teach dirent about symlinks Karsten Blees via GitGitGadget
2026-01-09 20:05   ` [PATCH v2 06/18] mingw: compute the correct size for symlinks in `mingw_lstat()` Bill Zissimopoulos via GitGitGadget
2026-01-09 20:05   ` [PATCH v2 07/18] mingw: factor out the retry logic Karsten Blees via GitGitGadget
2026-01-09 20:05   ` [PATCH v2 08/18] mingw: change default of `core.symlinks` to false Karsten Blees via GitGitGadget
2026-01-09 20:05   ` [PATCH v2 09/18] mingw: add symlink-specific error codes Karsten Blees via GitGitGadget
2026-01-09 20:05   ` [PATCH v2 10/18] mingw: handle symlinks to directories in `mingw_unlink()` Karsten Blees via GitGitGadget
2026-01-09 20:05   ` [PATCH v2 11/18] mingw: support renaming symlinks Karsten Blees via GitGitGadget
2026-01-09 20:05   ` [PATCH v2 12/18] mingw: allow `mingw_chdir()` to change to symlink-resolved directories Karsten Blees via GitGitGadget
2026-01-09 20:05   ` [PATCH v2 13/18] mingw: implement `readlink()` Karsten Blees via GitGitGadget
2026-01-09 20:05   ` [PATCH v2 14/18] mingw: implement basic `symlink()` functionality (file symlinks only) Karsten Blees via GitGitGadget
2026-01-09 20:05   ` [PATCH v2 15/18] mingw: add support for symlinks to directories Karsten Blees via GitGitGadget
2026-01-09 20:05   ` [PATCH v2 16/18] mingw: try to create symlinks without elevated permissions Johannes Schindelin via GitGitGadget
2026-01-09 20:05   ` [PATCH v2 17/18] mingw: emulate `stat()` a little more faithfully Johannes Schindelin via GitGitGadget
2026-01-09 20:05   ` [PATCH v2 18/18] mingw: special-case index entries for symlinks with buggy size Johannes Schindelin via GitGitGadget

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=pull.2018.v2.git.1767989115.gitgitgadget@gmail.com \
    --to=gitgitgadget@gmail$(echo .)com \
    --cc=ben.knoble@gmail$(echo .)com \
    --cc=git@vger$(echo .)kernel.org \
    --cc=j6t@kdbg$(echo .)org \
    --cc=johannes.schindelin@gmx$(echo .)de \
    --cc=karsten.blees@gmail$(echo .)com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox