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

"git rebase" breaks symbolic link .git/logs when used together with repo-tool #2967

Closed
1 task done
bossx91 opened this issue Jan 6, 2021 · 10 comments · Fixed by #3328
Closed
1 task done

"git rebase" breaks symbolic link .git/logs when used together with repo-tool #2967

bossx91 opened this issue Jan 6, 2021 · 10 comments · Fixed by #3328
Milestone

Comments

@bossx91
Copy link

bossx91 commented Jan 6, 2021

  • I was not able to find an open or closed issue matching what I'm seeing

Setup

  • Which version of Git for Windows are you using? Is it 32-bit or 64-bit?
$ git --version --build-options
git version 2.30.0.windows.1
cpu: x86_64
built from commit: 18da6dbba950f8cc7b7d07057f7c30bf7cf207b6
sizeof-long: 4
sizeof-size_t: 8
shell-path: /bin/sh

  • Which version of Windows are you running? Vista, 7, 8, 10? Is it 32-bit or 64-bit?
$ cmd.exe /c ver

Microsoft Windows [Version 10.0.19042.631]

  • What options did you set as part of the installation? Or did you choose the
    defaults?
# One of the following:
> type "C:\Program Files\Git\etc\install-options.txt"
> type "C:\Program Files (x86)\Git\etc\install-options.txt"
> type "%USERPROFILE%\AppData\Local\Programs\Git\etc\install-options.txt"
$ cat /etc/install-options.txt

Editor Option: VIM
Custom Editor Path:
Default Branch Option:
Path Option: Cmd
SSH Option: OpenSSH
Tortoise Option: false
CURL Option: OpenSSL
CRLF Option: CRLFAlways
Bash Terminal Option: MinTTY
Git Pull Behavior Option: Merge
Use Credential Manager: Core
Performance Tweaks FSCache: Enabled
Enable Symlinks: Enabled
Enable Pseudo Console Support: Disabled

  • Any other interesting things about your environment that might be related
    to the issue you're seeing?

Google's repo-tool is used.

Details

  • Which terminal/shell are you running Git from? e.g Bash/CMD/PowerShell/other

Git Bash

NOTE1: I've created a test environment on my github to show this issue. It consists of a repository for manifest used by repo tool and a repository for pseudo source code
NOTE2: repo-tool is needed to reproduce the issue but the issue itself is not with the repo-tool but with git for Windows, I think.

Run the following commands to see the issue:

repo init -u https://github.com/bossx91/test-repo-manifest -b main
repo sync
cd repo1
git checkout -t github/test
git rebase github/main
cd ..
repo sync
  • What did you expect to occur after running these commands?

I expected repo-tool sync succesfully.

  • What actually happened instead?

repo sync failed because symlink in repo1/.git/logs has been broken after git rebase github/main has been run.

  • How to see the issue more clearly?

Run the commands above until this point (included): git checkout -t github/test
Now, cd into .git under repo1 folder.
Run ls and see that "logs" is a symbolic link that points to the repo-tool's version of logs.
Run git rebase github/main inside "repo1".
Again cd into .git and see that "logs" is no longer a link but a folder (I suspect that this just copied the logs from where the link was potinng to before)

  • Additional information

This issue does not arise in Linux or WSL. In there, rebase command does not break the symbolic link, "logs" but instead updates the real logs folder.

  • What I've tried to solve this issue

export MSYS=winsymlinks:nativestrict
git config --system core.symlinks true
git config --global core.symlinks true
Running Git for Windows as admin

But none of the above have helped.

  • If the problem was occurring with a specific repository, can you provide the
    URL to that repository to help us with testing?

https://github.com/bossx91/test-repo-manifest - repository for manifest file for repo-tool
https://github.com/bossx91/test-repository1 - repository for pseudo source code

@dscho
Copy link
Member

dscho commented Jan 6, 2021

Do you happen to know whether that behavior is specific to Windows?

@dscho
Copy link
Member

dscho commented Jan 6, 2021

This issue does not arise in Linux or WSL.

Ah, I missed that in my first read-through.

repo sync

Any idea what this does? Maybe you can replicate this behavior via a .bat script, so that I do not have to install repo (which requires Python, I believe?)?

@bossx91
Copy link
Author

bossx91 commented Jan 6, 2021

Yes, you are correct it requires Python. To completely replicate this you would indeed need to install repo-tool.

I think this issue can be narrowed down to the root cause of why repo sync fails. It fails because symbolic link inside .git is not a symbolic link after git rebase is executed.

So I've created a simple .bat file that works on my system, hope it will work on yours too :) ( I am not so good with bat scripts). Anyway this kind of emulates what repo tool does at init stage.
When you done running it you will have the following:

  1. A folder called "repo" which will contain a folder called "test-repository1.git" with test-repository1's "logs" folder within it.
  2. A cloned git repository https://github.com/bossx91/test-repository1 and in .git in that repository it will create a symbolic link to the "logs" folder inside the "repo/test-repository1.git" folder which is how repo-tool does this roughly.

Then you can cd into the test-repository1/.git and ls -la. You will see that "logs" is a symbolic link (actually in windows terms it is a folder junction).
Then go back one step and run git rebase origin/main.
Go back into .git, now "logs" is no longer a symbolic link but a folder instead.

When repo sync is later run, it basically says that logs inside repo does not match with logs inside git repository thus the conflict arises. Hope this explains this a bit better :).

@bossx91
Copy link
Author

bossx91 commented Jan 6, 2021

.bat script is here, I coult not upload with .bat extension so I added .txt at the end.
setup_issue_2967.bat.txt

@thombet
Copy link

thombet commented Jun 17, 2021

I confirm the issue described by @bossx91: I have the same problem in the same conditions (WIndows 10 + Git Bash + Git-Repo). Did you manage to find a solution?

@dscho is there any chance that this issue is investigated?

Thanks!

@dscho
Copy link
Member

dscho commented Jun 18, 2021

@dscho is there any chance that this issue is investigated?

Yes, there is a chance. I don't know for sure, though, because I am not investigating it (I am busy elsewhere).

So if this was an underhanded question whether you can go right ahead investigating this: go right ahead.

@thombet
Copy link

thombet commented Jun 22, 2021

Nothing underhanded intended in my question: I was just wondering what was the status of this issue since it has been opened since January.
I hope my question was not misinterpreted: I didn't know how this project was maintained when I posted my question (since then I read the governance model 😄).

@dscho
Copy link
Member

dscho commented Jun 23, 2021

@thombet my suggestion stands: do feel free to investigate. That will be your best chance to see progress in this ticket.

@thombet
Copy link

thombet commented Jul 11, 2021

I followed your advice @dscho and I investigated this issue (by the way thanks for the very clear wiki and SDK: it was very easy to set up a debug environment 👍 ).

It is the first time I dive in the Git source code so I don't know if the information I share below are obvious or not.

The symlink .git/logs is removed during rebases by this call in builtin/rebase.c. The actual deletion occurs in the function mingw_rmdir in compat/mingw.c.
The full call stack is:

  • builtin/rebase.c init_basic_state()
  • refs.c delete_reflog()
  • refs.c refs_delete_reflog()
  • refs/files-backend.c files_delete_reflog()
  • dir.c remove_path()
  • dir.c rmdir()
  • compat/mingw.c mingw_rmdir()

As you probably know already the calls up to dir.c are the same as in Git so maybe this deletion of .git/logs is not the problem.
I don't know Git source code enough to know if this deletion is expected and I didn't debug Git on Linux to check if the folder is also deleted (and then recreated) on this system.

EDIT: I compiled Git in Cygwin (because the problem does not occur in Cygwin i.e. the rebase does not remove the symlink at all) and I debugged the same use case.
It turns out the call stack is a little bit different: rmdir() is resolved to lstat_cache_aware_rmdir() in Cygwin whereas it was resolved to mingw_rmdir() in Git Bash.
I checked in the code, and I found these 2 definitions of rmdir() in the code (these parts are identical both in Git and Git for Windows):

git-compat-util.h (link):

int lstat_cache_aware_rmdir(const char *path);
#if !defined(__MINGW32__) && !defined(_MSC_VER)
#define rmdir lstat_cache_aware_rmdir
#endif

and in mingw.h (link):

int mingw_rmdir(const char *path);
#define rmdir mingw_rmdir

So it looks like the definition in git-compat-util.h is used in Cygwin whereas the definition from mingw.h is used in Git Bash which is probably normal given the different architecture between Cygwin and Git Bash.
But I reached the limits of my knowledge in Git Bash here so I need your help @dscho :)

Are these different definitions of rmdir() expected?
If they are, could there be a bug in the way directory symbolic links are managed in Git Bash?

Thanks in advance!

@thombet
Copy link

thombet commented Jul 17, 2021

After some more investigation I think I found the root cause of the problem.
On Linux (and Cygwin) the function rmdir() does not remove a folder which is actually a symlink: instead it returns the error code ENOENT (more information here).
The Windows equivalent _rmdir() (or _wrmdir()) does not behave the same way: when the path passed to the function is a symlink, the link is removed.

I implemented a fix in mingw_rmdir(): _wrmdir() will be called only if the folder is not a symlink. If it is a symlink the function will return the same error code as Linux rmdir() and will exit.

I need to polish the code a bit and to write a test for this use case then I will create a pull request with this fix.

rimrul referenced this issue in thombet/git Jul 19, 2021
When performing a rebase, rmdir() is called on the folder .git/logs
which is supposed to exit without deleting anything in case .git/logs is
a symbolic link. The Windows equivalent to rmdir() (_rmdir) does not
behave the same and removes the folder if it is symlink which generates
issues especially when Repo is used.
This commit updates mingw_rmdir() so that its behavior is the same as
rmdir() in case of symbolic links.

Signed-off-by: Thomas Bétous <tomspycell@gmail.com>
@dscho dscho added this to the Next release milestone Jul 29, 2021
dscho added a commit to dscho/git that referenced this issue Jul 29, 2021
When performing a rebase, rmdir() is called on the folder .git/logs. On
Unix rmdir() exits without deleting anything in case .git/logs is a
symbolic link but the equivalent functions on Windows (_rmdir, _wrmdir
and RemoveDirectoryW) do not behave the same and remove the folder if it
is symlinked even if it is not empty.

This creates issues when folders in .git/ are symlinks which is
especially the case when git-repo[1] is used.

This commit updates mingw_rmdir() so that its behavior is the same as
Linux rmdir() in case of symbolic links.

This fixes git-for-windows#2967

[1]: git-repo is a python tool built on top of Git which helps manage
many Git repositories. It stores all the .git/ folders in a central
place by taking advantage of symbolic links.
More information: https://gerrit.googlesource.com/git-repo/

Signed-off-by: Thomas Bétous <tomspycell@gmail.com>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
dscho added a commit to dscho/git that referenced this issue Jul 29, 2021
When performing a rebase, rmdir() is called on the folder .git/logs. On
Unix rmdir() exits without deleting anything in case .git/logs is a
symbolic link but the equivalent functions on Windows (_rmdir, _wrmdir
and RemoveDirectoryW) do not behave the same and remove the folder if it
is symlinked even if it is not empty.

This creates issues when folders in .git/ are symlinks which is
especially the case when git-repo[1] is used.

This commit updates mingw_rmdir() so that its behavior is the same as
Linux rmdir() in case of symbolic links.

This fixes git-for-windows#2967

[1]: git-repo is a python tool built on top of Git which helps manage
many Git repositories. It stores all the .git/ folders in a central
place by taking advantage of symbolic links.
More information: https://gerrit.googlesource.com/git-repo/

Signed-off-by: Thomas Bétous <tomspycell@gmail.com>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
gitster pushed a commit to git/git that referenced this issue Jul 29, 2021
When performing a rebase, rmdir() is called on the folder .git/logs. On
Unix rmdir() exits without deleting anything in case .git/logs is a
symbolic link but the equivalent functions on Windows (_rmdir, _wrmdir
and RemoveDirectoryW) do not behave the same and remove the folder if it
is symlinked even if it is not empty.

This creates issues when folders in .git/ are symlinks which is
especially the case when git-repo[1] is used.

This commit updates mingw_rmdir() so that its behavior is the same as
Linux rmdir() in case of symbolic links.

This fixes git-for-windows#2967

[1] git-repo is a tool built on top of Git, which helps manage many
    Git repositories. It stores all the .git/ folders in a central
    place by taking advantage of symbolic links.

    More information: https://gerrit.googlesource.com/git-repo/

Signed-off-by: Thomas Bétous <tomspycell@gmail.com>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
dscho pushed a commit to dscho/git that referenced this issue Aug 2, 2021
When performing a rebase, rmdir() is called on the folder .git/logs. On
Unix rmdir() exits without deleting anything in case .git/logs is a
symbolic link but the equivalent functions on Windows (_rmdir, _wrmdir
and RemoveDirectoryW) do not behave the same and remove the folder if it
is symlinked even if it is not empty.

This creates issues when folders in .git/ are symlinks which is
especially the case when git-repo[1] is used: It replaces `.git/logs/`
with a symlink.

One such issue is that the _target_ of that symlink is removed e.g.
during a `git rebase`, where `delete_reflog("REBASE_HEAD")` will not
only try to remove `.git/logs/REBASE_HEAD` but then recursively try to
remove the parent directories until an error occurs, a technique that
obviously relies on `rmdir()` refusing to remove a symlink.

This was reported in git-for-windows#2967.

This commit updates mingw_rmdir() so that its behavior is the same as
Linux rmdir() in case of symbolic links.

To verify that Git does not regress on the reported issue, this patch
adds a regression test for the `git rebase` symptom, even if the same
`rmdir()` behavior is quite likely to cause potential problems in other
Git commands as well.

[1]: git-repo is a python tool built on top of Git which helps manage
many Git repositories. It stores all the .git/ folders in a central
place by taking advantage of symbolic links.
More information: https://gerrit.googlesource.com/git-repo/

Signed-off-by: Thomas Bétous <tomspycell@gmail.com>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
gitster pushed a commit to git/git that referenced this issue Aug 2, 2021
When performing a rebase, rmdir() is called on the folder .git/logs. On
Unix rmdir() exits without deleting anything in case .git/logs is a
symbolic link but the equivalent functions on Windows (_rmdir, _wrmdir
and RemoveDirectoryW) do not behave the same and remove the folder if it
is symlinked even if it is not empty.

This creates issues when folders in .git/ are symlinks which is
especially the case when git-repo[1] is used: It replaces `.git/logs/`
with a symlink.

One such issue is that the _target_ of that symlink is removed e.g.
during a `git rebase`, where `delete_reflog("REBASE_HEAD")` will not
only try to remove `.git/logs/REBASE_HEAD` but then recursively try to
remove the parent directories until an error occurs, a technique that
obviously relies on `rmdir()` refusing to remove a symlink.

This was reported in git-for-windows#2967.

This commit updates mingw_rmdir() so that its behavior is the same as
Linux rmdir() in case of symbolic links.

To verify that Git does not regress on the reported issue, this patch
adds a regression test for the `git rebase` symptom, even if the same
`rmdir()` behavior is quite likely to cause potential problems in other
Git commands as well.

[1]: git-repo is a python tool built on top of Git which helps manage
many Git repositories. It stores all the .git/ folders in a central
place by taking advantage of symbolic links.
More information: https://gerrit.googlesource.com/git-repo/

Signed-off-by: Thomas Bétous <tomspycell@gmail.com>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
dscho pushed a commit that referenced this issue Aug 11, 2021
When performing a rebase, rmdir() is called on the folder .git/logs. On
Unix rmdir() exits without deleting anything in case .git/logs is a
symbolic link but the equivalent functions on Windows (_rmdir, _wrmdir
and RemoveDirectoryW) do not behave the same and remove the folder if it
is symlinked even if it is not empty.

This creates issues when folders in .git/ are symlinks which is
especially the case when git-repo[1] is used: It replaces `.git/logs/`
with a symlink.

One such issue is that the _target_ of that symlink is removed e.g.
during a `git rebase`, where `delete_reflog("REBASE_HEAD")` will not
only try to remove `.git/logs/REBASE_HEAD` but then recursively try to
remove the parent directories until an error occurs, a technique that
obviously relies on `rmdir()` refusing to remove a symlink.

This was reported in #2967.

This commit updates mingw_rmdir() so that its behavior is the same as
Linux rmdir() in case of symbolic links.

To verify that Git does not regress on the reported issue, this patch
adds a regression test for the `git rebase` symptom, even if the same
`rmdir()` behavior is quite likely to cause potential problems in other
Git commands as well.

[1]: git-repo is a python tool built on top of Git which helps manage
many Git repositories. It stores all the .git/ folders in a central
place by taking advantage of symbolic links.
More information: https://gerrit.googlesource.com/git-repo/

Signed-off-by: Thomas Bétous <tomspycell@gmail.com>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
dscho pushed a commit that referenced this issue Aug 13, 2021
When performing a rebase, rmdir() is called on the folder .git/logs. On
Unix rmdir() exits without deleting anything in case .git/logs is a
symbolic link but the equivalent functions on Windows (_rmdir, _wrmdir
and RemoveDirectoryW) do not behave the same and remove the folder if it
is symlinked even if it is not empty.

This creates issues when folders in .git/ are symlinks which is
especially the case when git-repo[1] is used: It replaces `.git/logs/`
with a symlink.

One such issue is that the _target_ of that symlink is removed e.g.
during a `git rebase`, where `delete_reflog("REBASE_HEAD")` will not
only try to remove `.git/logs/REBASE_HEAD` but then recursively try to
remove the parent directories until an error occurs, a technique that
obviously relies on `rmdir()` refusing to remove a symlink.

This was reported in #2967.

This commit updates mingw_rmdir() so that its behavior is the same as
Linux rmdir() in case of symbolic links.

To verify that Git does not regress on the reported issue, this patch
adds a regression test for the `git rebase` symptom, even if the same
`rmdir()` behavior is quite likely to cause potential problems in other
Git commands as well.

[1]: git-repo is a python tool built on top of Git which helps manage
many Git repositories. It stores all the .git/ folders in a central
place by taking advantage of symbolic links.
More information: https://gerrit.googlesource.com/git-repo/

Signed-off-by: Thomas Bétous <tomspycell@gmail.com>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
dscho pushed a commit that referenced this issue Aug 18, 2021
When performing a rebase, rmdir() is called on the folder .git/logs. On
Unix rmdir() exits without deleting anything in case .git/logs is a
symbolic link but the equivalent functions on Windows (_rmdir, _wrmdir
and RemoveDirectoryW) do not behave the same and remove the folder if it
is symlinked even if it is not empty.

This creates issues when folders in .git/ are symlinks which is
especially the case when git-repo[1] is used: It replaces `.git/logs/`
with a symlink.

One such issue is that the _target_ of that symlink is removed e.g.
during a `git rebase`, where `delete_reflog("REBASE_HEAD")` will not
only try to remove `.git/logs/REBASE_HEAD` but then recursively try to
remove the parent directories until an error occurs, a technique that
obviously relies on `rmdir()` refusing to remove a symlink.

This was reported in #2967.

This commit updates mingw_rmdir() so that its behavior is the same as
Linux rmdir() in case of symbolic links.

To verify that Git does not regress on the reported issue, this patch
adds a regression test for the `git rebase` symptom, even if the same
`rmdir()` behavior is quite likely to cause potential problems in other
Git commands as well.

[1]: git-repo is a python tool built on top of Git which helps manage
many Git repositories. It stores all the .git/ folders in a central
place by taking advantage of symbolic links.
More information: https://gerrit.googlesource.com/git-repo/

Signed-off-by: Thomas Bétous <tomspycell@gmail.com>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
dscho pushed a commit that referenced this issue Aug 23, 2021
When performing a rebase, rmdir() is called on the folder .git/logs. On
Unix rmdir() exits without deleting anything in case .git/logs is a
symbolic link but the equivalent functions on Windows (_rmdir, _wrmdir
and RemoveDirectoryW) do not behave the same and remove the folder if it
is symlinked even if it is not empty.

This creates issues when folders in .git/ are symlinks which is
especially the case when git-repo[1] is used: It replaces `.git/logs/`
with a symlink.

One such issue is that the _target_ of that symlink is removed e.g.
during a `git rebase`, where `delete_reflog("REBASE_HEAD")` will not
only try to remove `.git/logs/REBASE_HEAD` but then recursively try to
remove the parent directories until an error occurs, a technique that
obviously relies on `rmdir()` refusing to remove a symlink.

This was reported in #2967.

This commit updates mingw_rmdir() so that its behavior is the same as
Linux rmdir() in case of symbolic links.

To verify that Git does not regress on the reported issue, this patch
adds a regression test for the `git rebase` symptom, even if the same
`rmdir()` behavior is quite likely to cause potential problems in other
Git commands as well.

[1]: git-repo is a python tool built on top of Git which helps manage
many Git repositories. It stores all the .git/ folders in a central
place by taking advantage of symbolic links.
More information: https://gerrit.googlesource.com/git-repo/

Signed-off-by: Thomas Bétous <tomspycell@gmail.com>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
git-for-windows-ci pushed a commit that referenced this issue Aug 24, 2021
When performing a rebase, rmdir() is called on the folder .git/logs. On
Unix rmdir() exits without deleting anything in case .git/logs is a
symbolic link but the equivalent functions on Windows (_rmdir, _wrmdir
and RemoveDirectoryW) do not behave the same and remove the folder if it
is symlinked even if it is not empty.

This creates issues when folders in .git/ are symlinks which is
especially the case when git-repo[1] is used: It replaces `.git/logs/`
with a symlink.

One such issue is that the _target_ of that symlink is removed e.g.
during a `git rebase`, where `delete_reflog("REBASE_HEAD")` will not
only try to remove `.git/logs/REBASE_HEAD` but then recursively try to
remove the parent directories until an error occurs, a technique that
obviously relies on `rmdir()` refusing to remove a symlink.

This was reported in #2967.

This commit updates mingw_rmdir() so that its behavior is the same as
Linux rmdir() in case of symbolic links.

To verify that Git does not regress on the reported issue, this patch
adds a regression test for the `git rebase` symptom, even if the same
`rmdir()` behavior is quite likely to cause potential problems in other
Git commands as well.

[1]: git-repo is a python tool built on top of Git which helps manage
many Git repositories. It stores all the .git/ folders in a central
place by taking advantage of symbolic links.
More information: https://gerrit.googlesource.com/git-repo/

Signed-off-by: Thomas Bétous <tomspycell@gmail.com>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
jiangxin pushed a commit to jiangxin/git-test-lib that referenced this issue Nov 6, 2023
When performing a rebase, rmdir() is called on the folder .git/logs. On
Unix rmdir() exits without deleting anything in case .git/logs is a
symbolic link but the equivalent functions on Windows (_rmdir, _wrmdir
and RemoveDirectoryW) do not behave the same and remove the folder if it
is symlinked even if it is not empty.

This creates issues when folders in .git/ are symlinks which is
especially the case when git-repo[1] is used: It replaces `.git/logs/`
with a symlink.

One such issue is that the _target_ of that symlink is removed e.g.
during a `git rebase`, where `delete_reflog("REBASE_HEAD")` will not
only try to remove `.git/logs/REBASE_HEAD` but then recursively try to
remove the parent directories until an error occurs, a technique that
obviously relies on `rmdir()` refusing to remove a symlink.

This was reported in git-for-windows/git#2967.

This commit updates mingw_rmdir() so that its behavior is the same as
Linux rmdir() in case of symbolic links.

To verify that Git does not regress on the reported issue, this patch
adds a regression test for the `git rebase` symptom, even if the same
`rmdir()` behavior is quite likely to cause potential problems in other
Git commands as well.

[1]: git-repo is a python tool built on top of Git which helps manage
many Git repositories. It stores all the .git/ folders in a central
place by taking advantage of symbolic links.
More information: https://gerrit.googlesource.com/git-repo/

Signed-off-by: Thomas Bétous <tomspycell@gmail.com>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
jiangxin pushed a commit to jiangxin/git-test-lib that referenced this issue Nov 29, 2023
When performing a rebase, rmdir() is called on the folder .git/logs. On
Unix rmdir() exits without deleting anything in case .git/logs is a
symbolic link but the equivalent functions on Windows (_rmdir, _wrmdir
and RemoveDirectoryW) do not behave the same and remove the folder if it
is symlinked even if it is not empty.

This creates issues when folders in .git/ are symlinks which is
especially the case when git-repo[1] is used: It replaces `.git/logs/`
with a symlink.

One such issue is that the _target_ of that symlink is removed e.g.
during a `git rebase`, where `delete_reflog("REBASE_HEAD")` will not
only try to remove `.git/logs/REBASE_HEAD` but then recursively try to
remove the parent directories until an error occurs, a technique that
obviously relies on `rmdir()` refusing to remove a symlink.

This was reported in git-for-windows/git#2967.

This commit updates mingw_rmdir() so that its behavior is the same as
Linux rmdir() in case of symbolic links.

To verify that Git does not regress on the reported issue, this patch
adds a regression test for the `git rebase` symptom, even if the same
`rmdir()` behavior is quite likely to cause potential problems in other
Git commands as well.

[1]: git-repo is a python tool built on top of Git which helps manage
many Git repositories. It stores all the .git/ folders in a central
place by taking advantage of symbolic links.
More information: https://gerrit.googlesource.com/git-repo/

Signed-off-by: Thomas Bétous <tomspycell@gmail.com>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
jiangxin pushed a commit to jiangxin/git-test-lib that referenced this issue Dec 1, 2023
When performing a rebase, rmdir() is called on the folder .git/logs. On
Unix rmdir() exits without deleting anything in case .git/logs is a
symbolic link but the equivalent functions on Windows (_rmdir, _wrmdir
and RemoveDirectoryW) do not behave the same and remove the folder if it
is symlinked even if it is not empty.

This creates issues when folders in .git/ are symlinks which is
especially the case when git-repo[1] is used: It replaces `.git/logs/`
with a symlink.

One such issue is that the _target_ of that symlink is removed e.g.
during a `git rebase`, where `delete_reflog("REBASE_HEAD")` will not
only try to remove `.git/logs/REBASE_HEAD` but then recursively try to
remove the parent directories until an error occurs, a technique that
obviously relies on `rmdir()` refusing to remove a symlink.

This was reported in git-for-windows/git#2967.

This commit updates mingw_rmdir() so that its behavior is the same as
Linux rmdir() in case of symbolic links.

To verify that Git does not regress on the reported issue, this patch
adds a regression test for the `git rebase` symptom, even if the same
`rmdir()` behavior is quite likely to cause potential problems in other
Git commands as well.

[1]: git-repo is a python tool built on top of Git which helps manage
many Git repositories. It stores all the .git/ folders in a central
place by taking advantage of symbolic links.
More information: https://gerrit.googlesource.com/git-repo/

Signed-off-by: Thomas Bétous <tomspycell@gmail.com>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
jiangxin pushed a commit to jiangxin/git-test-lib that referenced this issue Dec 4, 2023
When performing a rebase, rmdir() is called on the folder .git/logs. On
Unix rmdir() exits without deleting anything in case .git/logs is a
symbolic link but the equivalent functions on Windows (_rmdir, _wrmdir
and RemoveDirectoryW) do not behave the same and remove the folder if it
is symlinked even if it is not empty.

This creates issues when folders in .git/ are symlinks which is
especially the case when git-repo[1] is used: It replaces `.git/logs/`
with a symlink.

One such issue is that the _target_ of that symlink is removed e.g.
during a `git rebase`, where `delete_reflog("REBASE_HEAD")` will not
only try to remove `.git/logs/REBASE_HEAD` but then recursively try to
remove the parent directories until an error occurs, a technique that
obviously relies on `rmdir()` refusing to remove a symlink.

This was reported in git-for-windows/git#2967.

This commit updates mingw_rmdir() so that its behavior is the same as
Linux rmdir() in case of symbolic links.

To verify that Git does not regress on the reported issue, this patch
adds a regression test for the `git rebase` symptom, even if the same
`rmdir()` behavior is quite likely to cause potential problems in other
Git commands as well.

[1]: git-repo is a python tool built on top of Git which helps manage
many Git repositories. It stores all the .git/ folders in a central
place by taking advantage of symbolic links.
More information: https://gerrit.googlesource.com/git-repo/

Signed-off-by: Thomas Bétous <tomspycell@gmail.com>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
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 a pull request may close this issue.

4 participants
@dscho @thombet @bossx91 and others