echasnovski/mini.nvim

[mini.diff] doesn't track beyond junction link in Windows

Opened this issue ยท 14 comments

Contributing guidelines

Module(s)

mini.diff

Description

A file beyond junction link on Windows isn't tracked for changes

folder
  .git
  jlink
    file

while Neogit status tracks the file

Neovim version

NVIM v0.10.3

Steps to reproduce

Create this file structure on Windows:

folder
  .git
  jlink
    file

and open the file beyond junction

Expected behavior

Track files beyond junction link on Windows

Actual behavior

Currently, doesn't track

Thanks for the issue!

It is the first time I hear about junction links on Windows. As far as I can tell, it is an equivalent of symbolic link in Linux. If that is the case, where is the source of the linked file? Right now all the data for the file is taken after resolving symlinks, so if the original file is somewhere outside, then it indeed won't recognize it as part of the current Git repository.

I can reproduce similar case with symbolic link on Linux if I link the file completely outside of the repo. I don't quite remember now, but I am pretty sure that the current behavior was justified for some cases. I'll have to think about it.

I can reproduce similar case with symbolic link on Linux if I link the file completely outside of the repo. I don't quite remember now, but I am pretty sure that the current behavior was justified for some cases. I'll have to think about it.

Ah, I remembered now. It is to work in exactly reversed case: linking files from the Git repo to somewhere outside of it. Like with stow. If links are not resolved, then the file does not appear as tracked by Git.

This indeed breaks in the case from this issue: linking files from outside Git repo into it. Not sure which behavior is more reasonable. I think that the current behavior is more reasonable, but maybe it warrants the extra option.

One thing that is indeed not great is that 'mini.git' does not follow symlinks. So making them equivalent by default might be reasonable.

On Windows junction is a symlink to another folder. I can add a file inside that folder to git, while on Linux git rejects adding a file beyond symlink folder, but you can add symlink itself.
I would suggest resolving symlinks for files but not for folders, this way it can work for both cases.
What do you think?

On Windows junction is a symlink to another folder. I can add a file inside that folder to git, while on Linux git rejects adding a file beyond symlink folder, but you can add symlink itself.

I am not sure this is 100% the case. I manage my '~/.config/nvim' as a symlink from '~/dotfiles/neovim/.config/nvim'. Adding and removing files works as expected, but maybe because '~/dotfiles/neovim/.config/nvim' is a Git submodule.

I would suggest resolving symlinks for files but not for folders, this way it can work for both cases.
What do you think?

That's probably off the table because requires extra complexity. It is either resolve all symlinks (via vim.loop.fs_realpath()) or not.

Just as a data point, would you mind checking something for me on Windows, please?
Could you replace this line in the your installed version of 'mini.nvim' / 'mini.diff' with the following:

H.get_buf_realpath = function(buf_id) return vim.api.nvim_buf_get_name(buf_id) end

Does it solve this issue with junction links?

@echasnovski well, it doesn't ๐Ÿค”

@echasnovski well, it doesn't ๐Ÿค”

Very strange, as it seems to (and indeed should) change the behavior on Linux. Are you double sure that you applied the change in the actual plugin location, i.e. which has effect on current interactive session?

It's git rev-parse --path-format=absolute --git-dir from git_start_watching_index that fails with exit code 128:
fatal: not a git repository (or any of the parent directories): .git

It's git rev-parse --path-format=absolute --git-dir from git_start_watching_index that fails with exit code 128:
fatal: not a git repository (or any of the parent directories): .git

From what directory does it run? I.e. the cwd from this line? And does path match the path without symlink resolution (i.e. 'folder/jlink/file')?

Can you see the same behavior when running git rev-parse --path-format=absolute --git-dir from inside 'folder/jlink'?

It's git rev-parse --path-format=absolute --git-dir from git_start_watching_index that fails with exit code 128:
fatal: not a git repository (or any of the parent directories): .git

From what directory does it run? I.e. the cwd from this line? And does path match the path without symlink resolution (i.e. 'folder/jlink/file')?

Yes. Yes, the path matches (after removing symlink resolution)

Can you see the same behavior when running git rev-parse --path-format=absolute --git-dir from inside 'folder/jlink'?

fatal: not a git repository (or any of the parent directories): .git is the output of running inside 'folder/jlink'

fatal: not a git repository (or any of the parent directories): .git is the output of running inside 'folder/jlink'

Sorry, then I am completely unaware why it is like that and how to fix this. The goal of that call is to find the '.git' directory which is an indicator of whether a file is inside a Git directory. If you can find a way to do that which is reasonable and concise, I'll consider it.

I'll take a look at Neogit code to see if there is any mention of cases like that, but if there is nothing I am afraid I'll close this issue after that. Mostly because there is currently no way to find a solution without Windows users volunteers. Probably add a special label also to maybe revisit if/when I have an access to Windows.

O'll help you with the debug if you want

O'll help you with the debug if you want

All explicit directions I'd thought of are already asked, I am afraid. What remains is to "just make it work". Basically make start watching index and all the rest of machinery work in the use case from this issue while still working for more common setups.

OK, so I've tested with symlinked directory on Linux, because I previously only did that with symlinked files. And there is indeed the behavior you describe. Here the setup I used:

  • Create 'repo' and 'outside-dir' directories, 'repo/file' and 'outside-dir/outside-dir/file' files.
  • Inside 'repo':
    • git init
    • git add *
    • git commit -m 'Initial commit'
    • Now 'repo/file' should be tracked.
  • Inside 'repo':
    • ln -s /abs/path/to/outside-dir /abs/path/to/repo/inside-dir
    • git add *
    • git commit -m 'Add symlinked directory'.
    • Now 'outside-dir' is "tracked" in 'repo' repository as 'inside-dir'.
  • Modify 'repo/inside-dir/file' and execute git status. It doesn't show any changes to the file.
  • Running git rev-parse --path-format=absolute --git-dir from inside 'repo/inside-dir' indeed shows fatal: not a git repository (or any parent up to mount point /)\nStopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set). error.

So it seems that Git itself doesn't really work with directories symlinked inside the repo. I also tried 'NeogitOrg/neogit' and it also doesn't detect changes in 'repo/inside-dir/file'. And it seems more or less reasonable, after thinking about it.

So, @edshamis, I have several questions:

  • Is the described setup (on Linux) similar to what you have for this issue (on Windows)?
  • If yes, what is the use case for symlinking directory/file from outside of the repo into it? As far as I can understand, this doesn't really work with the whole concept of version control management. Like, what if repo is deleted and cloned again? Isn't it more reasonable to have files/directories symlinked from inside repo outside?