Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

compat/mingw: rename the symlink, not the target #5437

Merged
merged 1 commit into from
Feb 21, 2025

Conversation

EliahKagan
Copy link

This fixes #5436. I've tried to write the commit message (6a994b2) in such a way that it would be suitable both here and upstream, but I do not know if I succeeded at that. As noted in the commit message, and in the issue, existing tests are able to detect the bug this is fixing, so at least for now I have not written any tests.

The code this modifies is also present upstream, but my understanding of the contribution guidelines is that I should open this PR here rather than submitting a patch to the Git mailing list, because the effect of this change is entirely Windows-specific. If this instead needs to be submitted to the mailing list, then I could try and do that. The change here is also rather simple, and I would certainly have no objection to it being integrated in some other way that doesn't involve merging this in any form, if that is more convenient.

Since 183ea3e [1], a new technique is used on Windows to rename files,
where supported. The first step of this technique is to open the file
with `CreateFileW`. At that time, `FILE_ATTRIBUTE_NORMAL` was passed as
the value of the `dwFlagsAndAttributes` argument. In b30404d [2], this
was improved by passing `FILE_FLAG_BACKUP_SEMANTICS`, to support
directories as well as regular files.

However, neither value of `dwFlagsAndAttributes` is sufficient to open
a symbolic link with the correct semantics to rename it. Symlinks on
Windows are reparse points. Attempting to open a reparse point with
`CreateFileW` dereferences the reparse point and opens the target
instead, unless `FILE_FLAG_OPEN_REPARSE_POINT` is included in
`dwFlagsAndAttributes`. This is documented for that flag and in the
"Symbolic Link Behavior" section of the `CreateFileW` docs [3].

This produces a regression where attempting to rename a symlink on
Windows renames its target to the intended new name and location of the
symlink. For example, if `symlink` points to `file`, then running

    git mv symlink symlink-renamed

leaves `symlink` in place and unchanged, but renames `file` to
`symlink-renamed` [4].

This regression is detectable by existing tests in `t7001-mv.sh`, but
the tests must be run by a Windows user with the ability to create
symlinks, and the `ln -s` command used to create the initial symlink
must also be able to create a real symlink (such as by setting the
`MSYS` environment variable to `winsymlinks:nativestrict`). Then
these two tests fail if the regression is present, and pass otherwise:

    38 - git mv should overwrite file with a symlink
    39 - check moved symlink

Let's fix this, so that renaming a symlink again renames the symlink
itself and leaves the target unchanged, by passing

    FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT

as the `dwFlagsAndAttributes` argument. This is sufficient (and safe)
because including `FILE_FLAG_OPEN_REPARSE_POINT` causes no harm even
when used to open a file or directory that is not a reparse point. In
that case, as noted in [3], this flag is simply ignored.

[1]: git-for-windows@183ea3e
[2]: git-for-windows@b30404d
[3]: https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilew
[4]: git-for-windows#5436

Signed-off-by: Eliah Kagan <eliah.kagan@gmail.com>
Copy link
Member

@dscho dscho left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wow, what an excellent Pull Request! Thank you so much!

@dscho dscho added this to the Next release milestone Feb 21, 2025
@dscho
Copy link
Member

dscho commented Feb 21, 2025

I verified manually that this fixes it by running:

cd t && MSYS=winsymlinks:nativestrict sh t7001-mv.sh -iVx

With the version from current main (which is identical to v2.48.1.windows.1), it fails with:

[...]
not ok 38 - git mv should overwrite file with a symlink"
#
#               rm -fr .git &&
#               git init &&
#               echo 1 >moved &&
#               test_ln_s_add moved symlink &&
#               git add moved &&
#               test_must_fail git mv symlink moved &&
#               git mv -f symlink moved &&
#               test_path_is_missing symlink &&
#               git update-index --refresh &&
#               git diff-files --quiet
#
1..38

With this patch, it succeeds:

[...]
ok 38 - git mv should overwrite file with a symlink
ok 39 - check moved symlink
ok 40 - setup submodule
ok 41 - git mv cannot move a submodule in a file
ok 42 - git mv moves a submodule with a .git directory and no .gitmodules
ok 43 - git mv moves a submodule with a .git directory and .gitmodules
ok 44 - git mv moves a submodule with gitfile
ok 45 - mv does not complain when no .gitmodules file is found
ok 46 - mv will error out on a modified .gitmodules file unless staged
ok 47 - mv issues a warning when section is not found in .gitmodules
ok 48 - mv --dry-run does not touch the submodule or .gitmodules
ok 49 - checking out a commit before submodule moved needs manual updates
ok 50 - mv -k does not accidentally destroy submodules
ok 51 - moving a submodule in nested directories
ok 52 - moving nested submodules
not ok 53 - nonsense mv triggers assertion failure and partially updated index # TODO known breakage
# still have 1 known breakage(s)
# passed all remaining 52 test(s)
1..53

@dscho dscho merged commit 3003038 into git-for-windows:main Feb 21, 2025
49 checks passed
git-for-windows-ci pushed a commit that referenced this pull request Feb 21, 2025
This fixes yet another fall-out from the `mingw_rename()` change that was introduced in v2.48.0.
git-for-windows-ci pushed a commit that referenced this pull request Feb 21, 2025
This fixes yet another fall-out from the `mingw_rename()` change that was introduced in v2.48.0.
git-for-windows-ci pushed a commit that referenced this pull request Feb 22, 2025
This fixes yet another fall-out from the `mingw_rename()` change that was introduced in v2.48.0.
@dscho
Copy link
Member

dscho commented Feb 25, 2025

/add relnote bug A change in upstream Git v2.48.0 broke renaming symlinks, which was fixed.

The workflow run was started

github-actions bot pushed a commit to git-for-windows/build-extra that referenced this pull request Feb 25, 2025
A change in upstream Git v2.48.0 broke renaming symlinks, which [was
fixed](git-for-windows/git#5437).

Signed-off-by: gitforwindowshelper[bot] <gitforwindowshelper-bot@users.noreply.github.com>
dscho added a commit that referenced this pull request Feb 25, 2025
This fixes yet another fall-out from the `mingw_rename()` change that was introduced in v2.48.0.
dscho added a commit that referenced this pull request Feb 25, 2025
This fixes yet another fall-out from the `mingw_rename()` change that was introduced in v2.48.0.
git-for-windows-ci pushed a commit that referenced this pull request Feb 25, 2025
This fixes yet another fall-out from the `mingw_rename()` change that was introduced in v2.48.0.
git-for-windows-ci pushed a commit that referenced this pull request Feb 26, 2025
This fixes yet another fall-out from the `mingw_rename()` change that was introduced in v2.48.0.
dscho added a commit that referenced this pull request Feb 26, 2025
This fixes yet another fall-out from the `mingw_rename()` change that was introduced in v2.48.0.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

git mv on a symlink dereferences it and renames the target in 2.48.1
2 participants