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

mingw: treat junction points the same as symlinks #437

Closed
wants to merge 1 commit into from

Conversation

dscho
Copy link
Member

@dscho dscho commented Sep 24, 2015

We forgot to handle them in the same way. Pointed out by Ed Thomson.

Signed-off-by: Johannes Schindelin [email protected]

We forgot to handle them in the same way. Pointed out by Ed Thomson.

Signed-off-by: Johannes Schindelin <[email protected]>
@dscho
Copy link
Member Author

dscho commented Sep 24, 2015

This is now a proper Pull Request to be more visible (and have more eyeballs on it).

@dscho
Copy link
Member Author

dscho commented Sep 25, 2015

MSys2 does report those junction points as symlinks ;-)

Not always...I just tried mklink /j xtemp ?\Volume{...}\Temp (where Volume{...} is a drive without a drive letter). Worked like a charm and MSys2 says its a directory.

Yeah, I think I would like MSys2's behavior to be replicated in Git for Windows. Any idea of the top of your head how they do it? If not, no big deal, I'll try to dig into it.

@elieux
Copy link

elieux commented Sep 30, 2015

However, in my understanding, junctions are just special forms of mount points

@kblees, what makes you think that? I've always thought of junctions as being a restricted form of symlinks and I'm curious about the reasoning behind your opinion.

@kblees
Copy link

kblees commented Oct 1, 2015

However, in my understanding, junctions are just special forms of mount points

@kblees, what makes you think that? I've always thought of junctions as being a restricted form of symlinks and I'm curious about the reasoning behind your opinion.

Because mount points and junctions are both implemented via IO_REPARSE_TAG_MOUNT_POINT reparse points, fsutil reparsepoint query identifies both as mount points, and they are handled by the same low level file system filter driver. Reparse points created via mountvol C:\mount \\?\Volume{...}\ and mklink /j C:\junction \\?\Volume{...}\ are almost indistinguishable.

Mount points and junctions cannot point to files, cannot point to network locations and cannot be relative. Windows Explorer treats them like normal directories (e.g. if you drag them around, Explorer moves or copies the content, while for symlinks it just moves / copies the link).

Thus I think junctions have much more in common with mount points (or bind mounts on Linux) than with symlinks. That Cygwin/MSys2 report some junctions as symlinks, but others as normal directories is quite a strange inconsistency IMO.

@elieux
Copy link

elieux commented Oct 1, 2015

Thanks. I'm pretty convinced by the evidence you presented. (Not that it affects anything. (: )

@dscho
Copy link
Member Author

dscho commented Oct 1, 2015

@kblees Do you think that this Pull Request is misguided, based on the evidence you presented? I guess I really should find out how Cygwin/MSys determines whether an IO_REPARSE_MOUNT_POINT indicates a symlink...

@dra27
Copy link

dra27 commented Oct 1, 2015

I don't know they do it, but they should do it by checking the ReparseTag member of the reparse point for IO_REPARSE_TAG_SYMLINK (see, for example, https://github.com/dra27/ocaml/blob/559747ad661265244d1c67f20dd342f4a000816d/otherlibs/win32unix/readlink.c#L58)

@dscho
Copy link
Member Author

dscho commented Oct 1, 2015

@dra27 please note that if that were true, my patch would not even be needed: https://github.com/git-for-windows/git/pull/437/files#diff-94a5535c71aaa5811b4b56854fe25547L12 (note how the current code already tests for IO_REPARSE_TAG_SYMLINK).

@dra27
Copy link

dra27 commented Oct 1, 2015

Sorry, what I meant is that that's the only thing they should be doing. Junctions are a weird - and now deprecated in favour of directory symbolic links - Windows 2000 oddity. IIRC, Cygwin (and therefore MSYS2) tried at some point to do some weird tricks with them to emulate symbolic links which may be why some still show up in directory listings as symbolic links. But it would seem better (to me at least) to treat them as actual directories, not symbolic links.

@dscho
Copy link
Member Author

dscho commented Oct 1, 2015

Junctions are a weird - and now deprecated in favour of directory symbolic links - Windows 2000 oddity

Yet it is possible for everybody to create junction points, but symbolic links are only for the Gods.

I think we still have to handle them the same as Cygwin does, for the same reason as Cygwin does it: users rely on them.

@kblees
Copy link

kblees commented Oct 1, 2015

@kblees Do you think that this Pull Request is misguided, based on the evidence you presented?

My opinion: If we need to implement junctions as symlinks to make git work (e.g. to find the real_path of the work-tree on startup), so be it. However, we shouldn't do it just to follow questionable Cygwin/MSys2 behaviour.

I guess I really should find out how Cygwin/MSys determines whether an IO_REPARSE_MOUNT_POINT indicates a symlink...

If the SubsituteName starts with \??\Volume{, MSys2 considers it a mount point (i.e. reported as normal directory), otherwise a junction (i.e. reported as symlink).

https://github.com/Alexpux/Cygwin/blob/msys2-master/winsup/cygwin/fhandler_disk_file.cc#L203
https://github.com/Alexpux/Cygwin/blob/msys2-master/winsup/cygwin/path.cc#L2459
https://github.com/Alexpux/Cygwin/blob/msys2-master/winsup/cygwin/globals.cc#L148

@dra27
Copy link

dra27 commented Oct 1, 2015

@dscho - but it's very flaky support, surely? So the user creates a directory junction in a working directory which git then interprets as a symbolic link. What happens for that same user if they switch between branches where the junction or shouldn't exist? git would delete the junction (correctly) and then try to checkout a symbolic link and so create a SYMLINKD (or fail if the user isn't a god!). So what was a JUNCTION to start with at very best becomes a SYMLINKD or more likely a mess.
Surely supporting them in this way brings more hassle than it's worth?

@dscho
Copy link
Member Author

dscho commented Oct 1, 2015

@dra27 look at it from a maintainer's point of view. What is the best solution to prevent reports like "I ran ls -l and it reports it as symlink, but it was not replaced by a directory as on Linux when I switched branches, and now it ate all my cookies and my puppy is dead!"? Right, the best solution is to not let that situation happen.

Of course we cannot fix everything. If we switch back to the branch where the path referred to a symlink, with core.symlinks = false, we will write a plain file with the target path. Big deal. But at least we will not clutter an unrelated directory for which the user created a junction point.

@dscho
Copy link
Member Author

dscho commented Oct 1, 2015

If the SubsituteName starts with ??\Volume{, MSys2 considers it a mount point (i.e. reported as normal directory), otherwise a junction (i.e. reported as symlink).

@kblees thanks for the analysis!

I am mildly in favor of doing the expensive thing and extend file_attr_to_st_mode() to take the path, too, and call readlink() in case of IO_REPARSE_MOUNT_POINT to figure out whether it is a volume mount point. In that case, I would also change finddata2dirent() to call file_attr_to_st_mode() to determine the d_type, to avoid duplicate logic.

What is your opinion about this strategy?

@kblees
Copy link

kblees commented Oct 1, 2015

I would also change finddata2dirent() to call file_attr_to_st_mode() to determine the d_type

IIRC neither dirent.c nor fscache.c have the full path when calculating d_type, just the basename, so this would involve some additional path mangling...

I just checked xcopy /b and robocopy /sl ... both affect symlinks only, not junctions, very much like Windows Explorer. And very much not like MSys2 cp -P.

The more I think about this, the more I'm convinced that the junction handling in Cygwin/MSys2 is an outright bug, not just a weirdness.

As this is Git for Windows, not msysgit (@dscho I know you like that name 😄), we shouldn't use MSys2 as a measure for 'correct' junction handling. Maybe the smartest thing would be to fix this in MSys2?

@dscho
Copy link
Member Author

dscho commented Oct 4, 2015

@ethomson I am tending to agree, after reflecting about this for a while: we do not create junction points in Git for Windows at all, so maybe we should continue to treat them as if they were real directories? Would you agree?

@ethomson
Copy link
Member

ethomson commented Oct 4, 2015

so maybe we should continue to treat them as if they were real directories? Would you agree?

I do not. Since Git will not create junction points (only the user will), then the user has explicitly tried to split their working tree across two locations. If Git treats junction points as directories, then this approach seems clever and workable, and it will write through the junction point into the other location. Yay!

This works fine until the user checks out a commit that doesn't contain that directory. Git will then remove it from the working tree. Meaning that it will remove the junction, and not what the junction points to. If the user switches back to a commit that does have the directory, it will recreate the directory as a directory and now those contents exist in two places.

That's quite surprising.

I do believe that you need to treat them specially, and not like a directory. One alternative is to write through them as if they were a directory, so that you can try to give them some deeper meaning, as you might a unix mount point, and refuse to delete them altogether and just leave them empty on-disk.

@dscho
Copy link
Member Author

dscho commented Oct 4, 2015

@ethomson do I understand correctly that you would prefer it if our rmdir() would refuse to remove junction points? That should be easy to accomplish.

@ethomson
Copy link
Member

ethomson commented Oct 4, 2015

No - but also, I don't want to get into the behavior of the rmdir function, I want to stick to high-level concepts because I care about "how checkout behaves", since Git for Windows is used as the benchmark for whether other Git implementations are "right" or not.

I think that fundamentally, there are three types of reparse points that we're interested in:

  1. Symbolic links, those of type IO_REPARSE_TAG_SYMLINK. I don't think there's much argument that an NTFS symbolic link maps most intelligently to a POSIX symbolic link, excepting the disappointing permissions problems.

  2. Those of type IO_REPARSE_TAG_MOUNT_POINT, which I have always broken down as:

    a. Junction points a/k/a "Directory Junctions" that point to another location on the filesystem, by path. These were historically used as symlinks before Vista introduced proper symlinks (above), and a lot of old school people still use them in place of symlinks.

    b. Mount points, that actually point to a volume.

After re-reading @kblees 's opinions, my takeaway is that I do understand the utility of allowing a volume to be mounted in the working tree. So I understand treating 2b as if it were a unix mount point, thus writing to it as if it's a directory (though if you're going to do that, let's please keep it around and not delete it from the working tree; that's particularly cruel.)

That said, I think 2a is different scenario. It's basically a symlink into the NT namespace, and acts like a symlink (in that it can be dangling). It's been historically used to enable symlink support from when Windows lacked symlinks.

@kblees
Copy link

kblees commented Oct 5, 2015

@ethomson The problem with your 2a case is that we'd have to sub-divide it into three categories:

2.a.I. Junctions on local disks referring to local paths by drive letter + path.

2.a.II. Junctions on local disks referring to local paths by volume name + path. As MSys2 doesn't recognise volume names as valid path names, these would have to be treated as normal directories for the sake of scripted commands.

2.a.III. Junctions on remote disks referring to remote paths by drive letter or volume name + path. As the remote path may not be accessible over the network other than by letting the server resolve the junction locally, we'd have to treat these as normal directories as well.

The only class of junctions that could possibly be treated as symlinks is 2.a.I.. In particular, this means that git on C:\repo would behave differently from git on \\localhost\C$\repo. This distinction is quite arbitrary and completely intransparent to end users, especially those working with git-cmd (i.e. users who are well familiar with typical windows commands that treat junctions as normal directories).

@dscho I just checked Linux's behaviour for bind mounts: Both stat and lstat report the bind-mounted directory as a normal directory. rmdir fails with EBUSY (i.e. checking out a branch that would remove a bind-mounted directory succeeds and leaves the directory intact; checking out a branch that would change a bind-mounted directory into a regular file fails with error message). So handling IO_REPARSE_TAG_MOUNT_POINT specially in mingw_rmdir would IMO be the best solution.

@dscho
Copy link
Member Author

dscho commented Oct 5, 2015

@kblees hmm. How do we want to deal with the legion of developers who have used junction points as symlink replacements since pre-Vista times and continue now because It Just Works For Them? We cannot practically educate them all, can we?

@kblees
Copy link

kblees commented Oct 5, 2015

How do we want to deal with the legion of developers who have used junction points as symlink replacements since pre-Vista times and continue now because It Just Works For Them?

Git for Windows has always treated junctions as normal directories, so I don't see why keeping it that way would change anything for these developers...

Although in my experience, people used Good Old .LNK files before Vista, because creating junctions would require login to the file server as well as installing additional tools (Windows Resource Kit or Sysinternals' junction.exe), while shortcuts Just Worked, even across network shares. So I would guess the "legion of developers" using junctions is quite small...

since Git for Windows is used as the benchmark for whether other Git implementations are "right" or not.

AFAIK, JGit / EGit already supports symlinks [1], via the NIO2 API introduced in Java 7 (which also treats junctions as normal directories, not symlinks). I would guess that eclipse folks would be very reluctant to write a Windows-specific JNI DLL, just to follow Git for Windows' "special" junction handling.

[1] https://bugs.eclipse.org/bugs/show_bug.cgi?id=354367#c41

@ethomson
Copy link
Member

ethomson commented Oct 5, 2015

Git for Windows has always treated junctions as normal directories,

Because libgit2 has never done this, and the goal here is to get alternate implementations on the same page. Using history as the definition of correctness is not going to succeed here.

Similarly, comparing Unix to Windows, and talking about how msys2 or cygwin behaves only defines the functionality for Git for Windows. We should be talking about how all Git implementations behave on Windows, because if we're not consistent with each other, then we're in serious trouble.

@ethomson
Copy link
Member

ethomson commented Oct 5, 2015

Anyway. You keep saying "normal directories". Is that really what you mean, where we delete junction points and recreate directories in their place? I don't see a good solution that involves the status quo. I think what we can either:

  1. Never delete junction points in the working directory, or
  2. Always delete them and recreate them.

Or some combination of the two, but never should we fall into the "normal directory" behavior. It's quite terrible.

I just want to be clear about what we're talking about.

@dscho
Copy link
Member Author

dscho commented Oct 5, 2015

@kblees there seems to be another voice not quite agreeing with the notion that junction points are a totally wrong choice for emulating symlinks: git-for-windows/msys2-runtime#13

@ethomson
Copy link
Member

ethomson commented Oct 5, 2015

God, though, isn't creating symlinks / junction points a whole different kettle of pain?

@kblees
Copy link

kblees commented Oct 5, 2015

You keep saying "normal directories". Is that really what you mean, where we delete junction points and recreate directories in their place?

No, trying to delete junction points should probably fail, e.g. by rmdir failing with EBUSY.

We should be talking about how all Git implementations behave on Windows, because if we're not consistent with each other, then we're in serious trouble.

First and foremost, we should be consistent with the behaviour of Windows. Otherwise we could just implement symlinks via plain files, as Cygwin/MSys2 does by default. Additionally, we should consider whether it is feasible or even possible to change junction handling in whatever direction.

  • Git for Windows (currently) treats junctions as directories. Special handling in rmdir would be simple. Treating junctions as symlinks would require adding expensive checks in performance-critical code sections.
  • Windows (Explorer / command line tools, i.e. shell for some Git for Windows users) treats junctions as directories. Changing this is impossible.
  • Cygwin/MSys2 (i.e. scripting platform of Git for Windows, and shell for some Git for Windows users) treats some junctions as directories, and others (2.a.I.) as symlinks. Treating all junctions as directories would be simple (remove a few lines of code).
  • Java / JGit / EGit treats junctions as directories (don't know whether it will delete them, though). Treating junctions as symlinks would require writing a Windows-specific JNI DLL. My gut feeling from the thread I linked above is that such a change will not be accepted.
  • libgit2 treats some junctions as directories, and others as symlinks (2.a.I. and, AFAICT from current sources, 2.a.III.). Treating junctions as directories would be simple (remove some lines of code). Fixing the 2.a.III. case - I don't know.

@kblees
Copy link

kblees commented Oct 5, 2015

@kblees there seems to be another voice not quite agreeing with the notion that junction points are a totally wrong choice for emulating symlinks: git-for-windows/msys2-runtime#13

AFAICT from a first glance, this code doesn't handle the 2.a.III. case correctly either (i.e. it will allow creating junctions to mapped network drives, which doesn't work at all).

@Karlson2k
Copy link

There are no strict equivalence for symlinks on Win32. So we have to use what's possible to use.
Anyway Windows and Unix/Linux are different:

Windows Unix/Linux
drive mountpoint
file file
directory directory
mountpoint mountpoint
file hardlink file hardlink
dir symlink symlink
file symlink symlink
N/A * symlink to non-existing object
directory junction symlink

* can be ether dir symlink or file symlink, but can't automatically change type depending on which target type is created later (file or directory).

Patch is good but incomplete: directory junction on remote systems (network paths/drives) are resolved transparently by remote host and should not be resolved locally as path are specific for drive's host system.

So we have some limitation, like unavailability of junction on network paths but all we know that Win32 port of Git has some limitation.
If some tools can't read windows' version of symlinks (like directory junction) this is a problem of that tool, not related to git itself.

@ethomson
Copy link
Member

I'm sorry, but I still fail to see any practical application for this. Links inside the worktree only make sense if they are relative, which means that real Windows symlinks are the only option.

There's nothing stopping someone from creating a junction point to an absolute path inside the working tree.

Junctions cannot point to a network host

They need only point to an absolute path, no? Does that not mean that they can point to volumes that aren't mounted, disks that aren't connected, and if you NET USE a drive, yes, they can point to a network drive.

But the network drive was merely an example. A junction can point to a target that doesn't exist. (Because regardless of what Explorer shows you, they're a soft link.)

@kblees
Copy link

kblees commented Oct 12, 2015

I'm sorry, but I still fail to see any practical application for this. Links inside the worktree only make sense if they are relative, which means that real Windows symlinks are the only option.

There's nothing stopping someone from creating a junction point to an absolute path inside the working tree.

This doesn't answer the question how such links should be useful to anyone.

And how would you distinguish the legitimate use of junctions from your case that a user accidentally created a junction when she actually meant to create a symlink?

Junctions cannot point to a network host

They need only point to an absolute path, no? Does that not mean that they can point to volumes that aren't mounted, disks that aren't connected, and if you NET USE a drive, yes, they can point to a network drive.

No. The low level driver responsible for resolving mount points cannot access network drives.

@ethomson
Copy link
Member

This doesn't answer the question how such links should be useful to anyone.

Well, I can think of myriad ways that a symbolic link or a junction point might be useful. But I'll tell you mine, which is that we have a particular component in our build system that must emit its output in a bin directory alongside the sources. We create a junction point from the bin directory in our worktree to our actual binary directory.

Obviously, we do not want git to try to do anything with this junction point, so we .gitignore it. Whether this is a symlink or a directory is what prompted this issue.

And how would you distinguish the legitimate use of junctions from your case that a user accidentally created a junction when she actually meant to create a symlink?

I don't understand why there would be a difference based on intent.

No. The low level driver responsible for resolving mount points cannot access network drives.

Yes, indeed you are correct. If you do create a junction point whose target is a network path, you have a dangling link regardless of whether the network path is available or not. Which was still my greater point: it's a soft link.

@kblees
Copy link

kblees commented Oct 12, 2015

And how would you distinguish the legitimate use of junctions from your case that a user accidentally created a junction when she actually meant to create a symlink?

I don't understand why there would be a difference based on intent.

Currently, Git treats junctions like normal directories, and there are people relying on this functionality (I've given two examples above, but there are many more [1]).

If we change Git's behaviour to treat all junctions as if they were symlinks, the existing functionality will obviously break. IOW we would need another way to tell Git: this junction should be treated like a directory, and that junction should be treated like a symlink.

Currently, this distinction is easy: if you want Git to recurse into a link target's content, use a junction (or bind mount on Unix); if you want Git to record the link itself, use a symlink.

we have a particular component in our build system that must emit its output in a bin directory alongside the sources. We create a junction point from the bin directory in our worktree to our actual binary directory.

Why does it have to be a junction point? Wouldn't you get the same effect with a symlink (aka mklink /d)?

Obviously, we do not want git to try to do anything with this junction point, so we .gitignore it. Whether this is a symlink or a directory is what prompted this issue.

Do you mean that you do not even track the link with Git (i.e. you never git add <junction> anywhere)? Then I honestly don't understand the problem. Or is this just about having bin or bin/ in .gitignore?

[1] https://www.google.com/search?q=git+follow+symlinks

@ethomson
Copy link
Member

Currently, Git treats junctions like normal directories, and there are people relying on this functionality (I've given two examples above, but there are many more [1]).

They should not be, because your implementation is not well defined. Again:

  1. You delete them and recreate directories in their place. So you are not treating them like a Unix mount point.
  2. You can write through a junction point into a different place in the working directory. This means that checkout is no longer well defined and will check out a tree that is immediately dirty.

This is absolutely not an implementation that a user could sensibly rely upon.

If we change Git's behaviour to treat all junctions as if they were symlinks, the existing functionality will obviously break

We do not need two modes. This should be a breaking change. It is already broken and needs to be fixed.

@elieux
Copy link

elieux commented Oct 13, 2015

I'm following this discussion and although I don't have much of an opinion, I do have some questions (possibly useful for others, as well).

  1. Disallow removing junction points via rmdir().
  2. Extend the current patch in this Pull Request to be careful to emulate MSys2's behavior: volume mounts are treated as if they were directories, other junction points are interpreted as symbolic links.

Following this concept, would/could Git remove a non-mount junction point during an update?

You can write through a junction point into a different place in the working directory. This means that checkout is no longer well defined and will check out a tree that is immediately dirty.

Can't I do the same with bind mounts?

@kblees
Copy link

kblees commented Oct 13, 2015

  1. You delete them and recreate directories in their place.

Not if you protect them from deletion, as recommended by microsoft [1]. And I already agreed that we should not delete mount points in rmdir().

[1] https://support.microsoft.com/en-us/kb/205524

2 . You can write through a junction point into a different place in the working directory.

You can do the same thing with Unix bind mounts. Your alternative of tracking the junction point's absolute Windows path in the Git repository is not useful at all. And you still fail to see the valid use case of junctions pointing outside the working directory.

This should be a breaking change.

Let me recap:

  • You request that (some) junction points be treated like symlinks, although junction points and symlinks are quite different.
  • You do not even need this feature, as you .gitignore the junction point in your use case anyway.
  • You do not care that this breaks Git for other people who use junction points correctly (as mount points, which is what they actually are).
  • You do not care that this is inconsistent with Windows behaviour for junction points.
  • You do not care that this is inconsistent with other Git implementations (namely JGit / EGit, as well as Cygwin/Msys2 once the junction bug is fixed).
  • You consistently fail to address the more sophisticated problems of your proposal, such as the absolute path issue, and the inconsistency that junction points on network drives would have to be treated differently than local junction points.

Do you realize that this is quite an arrogant position?

@ethomson
Copy link
Member

If you're going to recap, please don't put words into my mouth. Indeed I am not interested in this for my particular example. (You asked for an example and I provided one, which happened to be the issue that led me to research this fascinating topic.)

I thought it was quite obvious that I am interested in building a solution that both of our users benefit from. Git for Windows - right or wrong - is considered the Gold Standard by which the other implementations are judged.

As I said in my first comment:

Git for Windows is used as the benchmark for whether other Git implementations are "right" or not.

And subsequently:

The goal here is to get alternate implementations on the same page.

And while, yes, my initial suggestion was simply to treat junction points as symbolic links, my very first comment in this thread was:

One alternative is to write through them as if they were a directory, so that you can try to give them some deeper meaning, as you might a unix mount point, and refuse to delete them altogether and just leave them empty on-disk.

My position is very simply that the current behavior is not right, and we need a different one. I don't believe that's arrogant.

Perhaps I was unclear in my last comment - when I stated that "your implementation is not well defined" I was objecting to the current behavior and how it really must change. I was not objecting some proposed implementation.

Whatever we do with junction points, we need to not treat them like normal directories. We need to treat them as junction points, whatever we define that to be. Which I think that you agree with:

And I already agreed that we should not delete mount points in rmdir().

But obviously there are other questions like:

what is the behavior when a junction point is dangling, because it points to a network host that is offline, or the target directory does not exist?

(Note the edit - as you point out, if your link target is a network share, the link will be dangling.) The broader point remains: what do you do with dangling links when you're trying to check out into them?

@kblees
Copy link

kblees commented Oct 13, 2015

@ethompson I truly apologize for misunderstanding you so thoroughly.

IMO we should treat all mount points the same way, including all junction points, like this:

  • Prevent implicit deletion of mount points / junction points through Git (in mingw_rmdir()).
    • Git commands that would remove the mount point directory will succeed but leave the directory alone.
    • Git commands that would replace the directory with a file will fail.
    • Protecting mount points / junction points from explicit deletion (e.g. via Windows' rmdir command) would still require modifying the ACL.
  • Prevent reading the content of mount points / junction points in readlink.
    • This would prevent accidentally trying to manually resolve remote mount points locally, as in lockfile.c::resolve_symlink().
    • ...and prevent accidentally adding a link to an absolute Windows/NT path to a Git repository.
  • Otherwise treat mount points / junction points like normal directories.
    • Using junctions pointing inside the same worktree may exhibit strange behaviour (same as with Unix bind mounts).
    • Using junctions pointing at an external location will work as if the external content was present in the work tree.
    • Using dangling mount points should ideally result in an error. This may require upstream changes, as e.g. git-status doesn't seem to check for errors at all, so any failures from opendir() or readdir() would currently appear as an empty directory.

This would have the following advantages:

  • It doesn't break the existing use case of junctions / bind mounts as a means to achieve 'follow symlinks' behaviour.
  • It is reasonably consistent with Windows.
  • It is reasonably consistent with Unix.
  • It is possible (and AFAICT relatively simple) to implement the same behaviour in other Git implementations (JGit, libgit2, Cygwin/MSys2).

@ethomson
Copy link
Member

Apologies for the delay in getting back to this. I've been thinking a bit about the dangling junction case.

You mentioned that reading a directory might show this as an empty directory. I wonder if that might be a nice behavior. It's what one would expect on Unix if the drive had not been mounted (it would appear as an empty directory). It may also be less expensive for some implementations to handle things this way - perhaps avoiding a stat-like call. Plus it may be particularly nice for people in managed languages (eg jgit) who do not have the luxury of (easy) syscalls.

Note that I haven't actually implemented this, so this is all just in my head, but I'm curious about your thoughts.

Also - @dscho This differs from your suggestion, above. Are you happy with this?

@dscho
Copy link
Member Author

dscho commented May 12, 2016

Sorry for the long delay. I do agree with what @kblees wrote and will try to come up with the appropriate changes. Some time ;-)

@whoisj
Copy link

whoisj commented Dec 19, 2016

Symlinks are becoming more interesting in Windows (starting with RS2 update - coming to a PC near you):

https://blogs.windows.com/buildingapps/2016/12/02/symlinks-windows-10/#t0ZkyKCrUSyMAVF4.97

Per the Windows Developer Blog, they'll no longer require elevation to create, modify, or destroy so they will become useful to Git on Windows. 😄

@dscho
Copy link
Member Author

dscho commented Mar 8, 2019

I just got reminded of this old PR of mine, and was totally surprised that I had failed to close it yet!

In the meantime, the wisdom of @kblees and @ethomson managed to convince me that my approach was misguided.

To address the remaining issues (such as not traversing junction points or bind mounts in git clean), I opened this PR a while ago (but it still needs some work): #1976

@dscho dscho closed this Mar 8, 2019
@dscho dscho deleted the junction-point branch March 8, 2019 14:00
dscho pushed a commit to dscho/git that referenced this pull request Oct 7, 2021
…-fsmonitor-take2

Replace pre-V4 of FSMonitor with V4 using GFW experimental commits
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.

8 participants