Repository: pre-commit/pre-commit

Issue:
Pre-commit install fails on Windows network mount drive
Hi, I'm trying to help some team members set up pre-commit on a Windows network mount drive, and they're encountering an issue with paths. Seems related to [this comment](https://github.com/pre-commit/pre-commit/issues/1610#issuecomment-719774326) in #1610, which seems to be a different issue than the one the original issue, as we are on the most recent version of pre-commit and git, and the fix that was merged in #1727 for that doesn't seem to address this issue. I also tried other versions of Git for Windows <2.25, and the issue still seemed to persist.

I tested the solution that @christopherdoyle proposed, and that does seems to fix this issue with network mount drives, though I saw that @asottile would prefer not to use `pathlib`. I am not able to propose a fix right now, but I wanted to raise this as an issue that still exists.

Full error below:

```
    ### version information
    
    ```
    pre-commit version: 2.10.1
    sys.version:
        3.9.1 | packaged by conda-forge | (default, Jan 26 2021, 01:29:07) [MSC v.1916 64 bit (AMD64)]
    sys.executable: C:\Users\roderick\.conda\envs\nmt\python.exe
    os.name: nt
    sys.platform: win32
    ```
    
    ### error information
    
    ```
    An unexpected error has occurred: ValueError: path is on mount 'S:', start on mount '\\\\MyServer\Directory'
    ```
    
    ```
    Traceback (most recent call last):
      File "C:\Users\roderick\.conda\envs\nmt\lib\site-packages\pre_commit\error_handler.py", line 65, in error_handler
        yield
      File "C:\Users\roderick\.conda\envs\nmt\lib\site-packages\pre_commit\main.py", line 333, in main
        _adjust_args_and_chdir(args)
      File "C:\Users\roderick\.conda\envs\nmt\lib\site-packages\pre_commit\main.py", line 153, in _adjust_args_and_chdir
        args.config = os.path.relpath(args.config)
      File "C:\Users\roderick\.conda\envs\nmt\lib\ntpath.py", line 703, in relpath
        raise ValueError("path is on mount %r, start on mount %r" % (
    ValueError: path is on mount 'S:', start on mount '\\\\MyServer\Directory'
```

```


Discussion:
can you provide a reproduction and/or more information?  might need to use the debugger a bit to see what `git` is telling us and why we can't deal with that
Same issue here using a substed drive. Here I use a substed drive `W:` pointing to a subfolder on `D:`
```
An unexpected error has occurred: ValueError: path is on mount 'W:', start on mount 'D:'
```

Output of `git rev-parse --show-toplevel` is the path to the original subfolder (not `W:`)


pre-commit no longer uses `git rev-parse --show-toplevel`, so I'm confused why you're still seeing these issues
Sorry, I did the comment based on old versions. 

Taking a look at the code I'd guess that the problem is caused by the `os.path.realpath` on `git.get_root` function. It converts a substed or mounted drive to its original path.

instead of guessing can you show me in a terminal?  as far as I know realpath does not translate across mounts
Sorry. I'm sure `realpath` convert between substed drives. What I was saying is that I'm guessing that this is the root of the problem (I don't know that if it's just a case of replacing `realpath` by `abspath`).

![image](https://user-images.githubusercontent.com/2694778/108636787-55ce4600-7466-11eb-8927-8a4c8fe304ce.png)

Hi @asottile, sorry I'm new to this and not exactly sure what you want me to do. 

I can reproduce this bug by running `pre-commit run`. I cloned the `pre-commit` repo and set up a test environment, and I can reproduce the error by running the `main.py` script as well, but I don't exactly how to see what `git` is saying, as you suggested. What I can see is that this block:
```
try: 
    root = os.path.realpath(
         cmd_output('git', 'rev-parse', '--show-cdup')[1].strip(),
     )
```
in the `git.get_root()` function returns the path as `\\\\MyServer\Directory` whereas `args.config` is located at `S:\path\to\.pre-commit-config.yaml`
Just to add that we're seeing the same error using Python 3.8.6

With Python 3.6.8, all works as expected.
From the docs (https://docs.python.org/3/library/os.path.html#os.path.realpath):

> os.path.realpath(path)
Return the canonical path of the specified filename, eliminating any symbolic links encountered in the path (if they are supported by the operating system).
>
>Note When symbolic link cycles occur, the returned path will be one member of the cycle, but no guarantee is made about which member that will be.
Changed in version 3.6: Accepts a path-like object.
>
> Changed in version 3.8: Symbolic links and junctions are now resolved on Windows.

Resolving patch:
diff --git a/pre_commit/git.py b/pre_commit/git.py
--- a/pre_commit/git.py
+++ b/pre_commit/git.py
@@ -52,10 +52,10 @@ def get_root() -> str:
     # "rev-parse --show-cdup" to get the appropriate path, but must perform
     # an extra check to see if we are in the .git directory.
     try:
-        root = os.path.realpath(
+        root = os.path.abspath(
             cmd_output('git', 'rev-parse', '--show-cdup')[1].strip(),
         )
-        git_dir = os.path.realpath(get_git_dir())
+        git_dir = os.path.abspath(get_git_dir())
     except CalledProcessError:
         raise FatalError(
             'git failed. Is it installed, and are you in a Git repository '


Test patch:
diff --git a/tests/main_test.py b/tests/main_test.py
--- a/tests/main_test.py
+++ b/tests/main_test.py
@@ -7,7 +7,9 @@
 import pre_commit.constants as C
 from pre_commit import main
 from pre_commit.errors import FatalError
+from pre_commit.util import cmd_output
 from testing.auto_namedtuple import auto_namedtuple
+from testing.util import cwd
 
 
 @pytest.mark.parametrize(
@@ -54,6 +56,17 @@ def test_adjust_args_and_chdir_relative_things(in_git_dir):
     assert args.files == [os.path.join('foo', 'f1'), os.path.join('foo', 'f2')]
 
 
+@pytest.mark.skipif(os.name != 'nt', reason='windows feature')
+def test_install_on_subst(in_git_dir, store):  # pragma: posix no cover
+    assert not os.path.exists('Z:')
+    cmd_output('subst', 'Z:', str(in_git_dir))
+    try:
+        with cwd('Z:'):
+            test_adjust_args_and_chdir_noop('Z:\\')
+    finally:
+        cmd_output('subst', '/d', 'Z:')
+
+
 def test_adjust_args_and_chdir_non_relative_config(in_git_dir):
     in_git_dir.join('foo').ensure_dir().chdir()