We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN 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'
```

```

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `pre_commit/git.py`
Content:
```
1 import logging
2 import os.path
3 import sys
4 from typing import Dict
5 from typing import List
6 from typing import MutableMapping
7 from typing import Optional
8 from typing import Set
9 
10 from pre_commit.errors import FatalError
11 from pre_commit.util import CalledProcessError
12 from pre_commit.util import cmd_output
13 from pre_commit.util import cmd_output_b
14 
15 
16 logger = logging.getLogger(__name__)
17 
18 
19 def zsplit(s: str) -> List[str]:
20     s = s.strip('\0')
21     if s:
22         return s.split('\0')
23     else:
24         return []
25 
26 
27 def no_git_env(
28         _env: Optional[MutableMapping[str, str]] = None,
29 ) -> Dict[str, str]:
30     # Too many bugs dealing with environment variables and GIT:
31     # https://github.com/pre-commit/pre-commit/issues/300
32     # In git 2.6.3 (maybe others), git exports GIT_WORK_TREE while running
33     # pre-commit hooks
34     # In git 1.9.1 (maybe others), git exports GIT_DIR and GIT_INDEX_FILE
35     # while running pre-commit hooks in submodules.
36     # GIT_DIR: Causes git clone to clone wrong thing
37     # GIT_INDEX_FILE: Causes 'error invalid object ...' during commit
38     _env = _env if _env is not None else os.environ
39     return {
40         k: v for k, v in _env.items()
41         if not k.startswith('GIT_') or
42         k in {
43             'GIT_EXEC_PATH', 'GIT_SSH', 'GIT_SSH_COMMAND', 'GIT_SSL_CAINFO',
44             'GIT_SSL_NO_VERIFY',
45         }
46     }
47 
48 
49 def get_root() -> str:
50     # Git 2.25 introduced a change to "rev-parse --show-toplevel" that exposed
51     # underlying volumes for Windows drives mapped with SUBST.  We use
52     # "rev-parse --show-cdup" to get the appropriate path, but must perform
53     # an extra check to see if we are in the .git directory.
54     try:
55         root = os.path.realpath(
56             cmd_output('git', 'rev-parse', '--show-cdup')[1].strip(),
57         )
58         git_dir = os.path.realpath(get_git_dir())
59     except CalledProcessError:
60         raise FatalError(
61             'git failed. Is it installed, and are you in a Git repository '
62             'directory?',
63         )
64     if os.path.samefile(root, git_dir):
65         raise FatalError(
66             'git toplevel unexpectedly empty! make sure you are not '
67             'inside the `.git` directory of your repository.',
68         )
69     return root
70 
71 
72 def get_git_dir(git_root: str = '.') -> str:
73     opts = ('--git-common-dir', '--git-dir')
74     _, out, _ = cmd_output('git', 'rev-parse', *opts, cwd=git_root)
75     for line, opt in zip(out.splitlines(), opts):
76         if line != opt:  # pragma: no branch (git < 2.5)
77             return os.path.normpath(os.path.join(git_root, line))
78     else:
79         raise AssertionError('unreachable: no git dir')
80 
81 
82 def get_remote_url(git_root: str) -> str:
83     _, out, _ = cmd_output('git', 'config', 'remote.origin.url', cwd=git_root)
84     return out.strip()
85 
86 
87 def is_in_merge_conflict() -> bool:
88     git_dir = get_git_dir('.')
89     return (
90         os.path.exists(os.path.join(git_dir, 'MERGE_MSG')) and
91         os.path.exists(os.path.join(git_dir, 'MERGE_HEAD'))
92     )
93 
94 
95 def parse_merge_msg_for_conflicts(merge_msg: bytes) -> List[str]:
96     # Conflicted files start with tabs
97     return [
98         line.lstrip(b'#').strip().decode()
99         for line in merge_msg.splitlines()
100         # '#\t' for git 2.4.1
101         if line.startswith((b'\t', b'#\t'))
102     ]
103 
104 
105 def get_conflicted_files() -> Set[str]:
106     logger.info('Checking merge-conflict files only.')
107     # Need to get the conflicted files from the MERGE_MSG because they could
108     # have resolved the conflict by choosing one side or the other
109     with open(os.path.join(get_git_dir('.'), 'MERGE_MSG'), 'rb') as f:
110         merge_msg = f.read()
111     merge_conflict_filenames = parse_merge_msg_for_conflicts(merge_msg)
112 
113     # This will get the rest of the changes made after the merge.
114     # If they resolved the merge conflict by choosing a mesh of both sides
115     # this will also include the conflicted files
116     tree_hash = cmd_output('git', 'write-tree')[1].strip()
117     merge_diff_filenames = zsplit(
118         cmd_output(
119             'git', 'diff', '--name-only', '--no-ext-diff', '-z',
120             '-m', tree_hash, 'HEAD', 'MERGE_HEAD',
121         )[1],
122     )
123     return set(merge_conflict_filenames) | set(merge_diff_filenames)
124 
125 
126 def get_staged_files(cwd: Optional[str] = None) -> List[str]:
127     return zsplit(
128         cmd_output(
129             'git', 'diff', '--staged', '--name-only', '--no-ext-diff', '-z',
130             # Everything except for D
131             '--diff-filter=ACMRTUXB',
132             cwd=cwd,
133         )[1],
134     )
135 
136 
137 def intent_to_add_files() -> List[str]:
138     _, stdout, _ = cmd_output(
139         'git', 'status', '--ignore-submodules', '--porcelain', '-z',
140     )
141     parts = list(reversed(zsplit(stdout)))
142     intent_to_add = []
143     while parts:
144         line = parts.pop()
145         status, filename = line[:3], line[3:]
146         if status[0] in {'C', 'R'}:  # renames / moves have an additional arg
147             parts.pop()
148         if status[1] == 'A':
149             intent_to_add.append(filename)
150     return intent_to_add
151 
152 
153 def get_all_files() -> List[str]:
154     return zsplit(cmd_output('git', 'ls-files', '-z')[1])
155 
156 
157 def get_changed_files(old: str, new: str) -> List[str]:
158     return zsplit(
159         cmd_output(
160             'git', 'diff', '--name-only', '--no-ext-diff', '-z',
161             f'{old}...{new}',
162         )[1],
163     )
164 
165 
166 def head_rev(remote: str) -> str:
167     _, out, _ = cmd_output('git', 'ls-remote', '--exit-code', remote, 'HEAD')
168     return out.split()[0]
169 
170 
171 def has_diff(*args: str, repo: str = '.') -> bool:
172     cmd = ('git', 'diff', '--quiet', '--no-ext-diff', *args)
173     return cmd_output_b(*cmd, cwd=repo, retcode=None)[0] == 1
174 
175 
176 def has_core_hookpaths_set() -> bool:
177     _, out, _ = cmd_output_b('git', 'config', 'core.hooksPath', retcode=None)
178     return bool(out.strip())
179 
180 
181 def init_repo(path: str, remote: str) -> None:
182     if os.path.isdir(remote):
183         remote = os.path.abspath(remote)
184 
185     env = no_git_env()
186     # avoid the user's template so that hooks do not recurse
187     cmd_output_b('git', 'init', '--template=', path, env=env)
188     cmd_output_b('git', 'remote', 'add', 'origin', remote, cwd=path, env=env)
189 
190 
191 def commit(repo: str = '.') -> None:
192     env = no_git_env()
193     name, email = 'pre-commit', 'asottile+pre-commit@umich.edu'
194     env['GIT_AUTHOR_NAME'] = env['GIT_COMMITTER_NAME'] = name
195     env['GIT_AUTHOR_EMAIL'] = env['GIT_COMMITTER_EMAIL'] = email
196     cmd = ('git', 'commit', '--no-edit', '--no-gpg-sign', '-n', '-minit')
197     cmd_output_b(*cmd, cwd=repo, env=env)
198 
199 
200 def git_path(name: str, repo: str = '.') -> str:
201     _, out, _ = cmd_output('git', 'rev-parse', '--git-path', name, cwd=repo)
202     return os.path.join(repo, out.strip())
203 
204 
205 def check_for_cygwin_mismatch() -> None:
206     """See https://github.com/pre-commit/pre-commit/issues/354"""
207     if sys.platform in ('cygwin', 'win32'):  # pragma: no cover (windows)
208         is_cygwin_python = sys.platform == 'cygwin'
209         try:
210             toplevel = get_root()
211         except FatalError:  # skip the check if we're not in a git repo
212             return
213         is_cygwin_git = toplevel.startswith('/')
214 
215         if is_cygwin_python ^ is_cygwin_git:
216             exe_type = {True: '(cygwin)', False: '(windows)'}
217             logger.warn(
218                 f'pre-commit has detected a mix of cygwin python / git\n'
219                 f'This combination is not supported, it is likely you will '
220                 f'receive an error later in the program.\n'
221                 f'Make sure to use cygwin git+python while using cygwin\n'
222                 f'These can be installed through the cygwin installer.\n'
223                 f' - python {exe_type[is_cygwin_python]}\n'
224                 f' - git {exe_type[is_cygwin_git]}\n',
225             )
226
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

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 @@
     # "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 '

{"golden_diff": "diff --git a/pre_commit/git.py b/pre_commit/git.py\n--- a/pre_commit/git.py\n+++ b/pre_commit/git.py\n@@ -52,10 +52,10 @@\n     # \"rev-parse --show-cdup\" to get the appropriate path, but must perform\n     # an extra check to see if we are in the .git directory.\n     try:\n-        root = os.path.realpath(\n+        root = os.path.abspath(\n             cmd_output('git', 'rev-parse', '--show-cdup')[1].strip(),\n         )\n-        git_dir = os.path.realpath(get_git_dir())\n+        git_dir = os.path.abspath(get_git_dir())\n     except CalledProcessError:\n         raise FatalError(\n             'git failed. Is it installed, and are you in a Git repository '\n", "issue": "Pre-commit install fails on Windows network mount drive\nHi, 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.\r\n\r\nI 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.\r\n\r\nFull error below:\r\n\r\n```\r\n    ### version information\r\n    \r\n    ```\r\n    pre-commit version: 2.10.1\r\n    sys.version:\r\n        3.9.1 | packaged by conda-forge | (default, Jan 26 2021, 01:29:07) [MSC v.1916 64 bit (AMD64)]\r\n    sys.executable: C:\\Users\\roderick\\.conda\\envs\\nmt\\python.exe\r\n    os.name: nt\r\n    sys.platform: win32\r\n    ```\r\n    \r\n    ### error information\r\n    \r\n    ```\r\n    An unexpected error has occurred: ValueError: path is on mount 'S:', start on mount '\\\\\\\\MyServer\\Directory'\r\n    ```\r\n    \r\n    ```\r\n    Traceback (most recent call last):\r\n      File \"C:\\Users\\roderick\\.conda\\envs\\nmt\\lib\\site-packages\\pre_commit\\error_handler.py\", line 65, in error_handler\r\n        yield\r\n      File \"C:\\Users\\roderick\\.conda\\envs\\nmt\\lib\\site-packages\\pre_commit\\main.py\", line 333, in main\r\n        _adjust_args_and_chdir(args)\r\n      File \"C:\\Users\\roderick\\.conda\\envs\\nmt\\lib\\site-packages\\pre_commit\\main.py\", line 153, in _adjust_args_and_chdir\r\n        args.config = os.path.relpath(args.config)\r\n      File \"C:\\Users\\roderick\\.conda\\envs\\nmt\\lib\\ntpath.py\", line 703, in relpath\r\n        raise ValueError(\"path is on mount %r, start on mount %r\" % (\r\n    ValueError: path is on mount 'S:', start on mount '\\\\\\\\MyServer\\Directory'\r\n```\r\n\r\n```\n", "before_files": [{"content": "import logging\nimport os.path\nimport sys\nfrom typing import Dict\nfrom typing import List\nfrom typing import MutableMapping\nfrom typing import Optional\nfrom typing import Set\n\nfrom pre_commit.errors import FatalError\nfrom pre_commit.util import CalledProcessError\nfrom pre_commit.util import cmd_output\nfrom pre_commit.util import cmd_output_b\n\n\nlogger = logging.getLogger(__name__)\n\n\ndef zsplit(s: str) -> List[str]:\n    s = s.strip('\\0')\n    if s:\n        return s.split('\\0')\n    else:\n        return []\n\n\ndef no_git_env(\n        _env: Optional[MutableMapping[str, str]] = None,\n) -> Dict[str, str]:\n    # Too many bugs dealing with environment variables and GIT:\n    # https://github.com/pre-commit/pre-commit/issues/300\n    # In git 2.6.3 (maybe others), git exports GIT_WORK_TREE while running\n    # pre-commit hooks\n    # In git 1.9.1 (maybe others), git exports GIT_DIR and GIT_INDEX_FILE\n    # while running pre-commit hooks in submodules.\n    # GIT_DIR: Causes git clone to clone wrong thing\n    # GIT_INDEX_FILE: Causes 'error invalid object ...' during commit\n    _env = _env if _env is not None else os.environ\n    return {\n        k: v for k, v in _env.items()\n        if not k.startswith('GIT_') or\n        k in {\n            'GIT_EXEC_PATH', 'GIT_SSH', 'GIT_SSH_COMMAND', 'GIT_SSL_CAINFO',\n            'GIT_SSL_NO_VERIFY',\n        }\n    }\n\n\ndef get_root() -> str:\n    # Git 2.25 introduced a change to \"rev-parse --show-toplevel\" that exposed\n    # underlying volumes for Windows drives mapped with SUBST.  We use\n    # \"rev-parse --show-cdup\" to get the appropriate path, but must perform\n    # an extra check to see if we are in the .git directory.\n    try:\n        root = os.path.realpath(\n            cmd_output('git', 'rev-parse', '--show-cdup')[1].strip(),\n        )\n        git_dir = os.path.realpath(get_git_dir())\n    except CalledProcessError:\n        raise FatalError(\n            'git failed. Is it installed, and are you in a Git repository '\n            'directory?',\n        )\n    if os.path.samefile(root, git_dir):\n        raise FatalError(\n            'git toplevel unexpectedly empty! make sure you are not '\n            'inside the `.git` directory of your repository.',\n        )\n    return root\n\n\ndef get_git_dir(git_root: str = '.') -> str:\n    opts = ('--git-common-dir', '--git-dir')\n    _, out, _ = cmd_output('git', 'rev-parse', *opts, cwd=git_root)\n    for line, opt in zip(out.splitlines(), opts):\n        if line != opt:  # pragma: no branch (git < 2.5)\n            return os.path.normpath(os.path.join(git_root, line))\n    else:\n        raise AssertionError('unreachable: no git dir')\n\n\ndef get_remote_url(git_root: str) -> str:\n    _, out, _ = cmd_output('git', 'config', 'remote.origin.url', cwd=git_root)\n    return out.strip()\n\n\ndef is_in_merge_conflict() -> bool:\n    git_dir = get_git_dir('.')\n    return (\n        os.path.exists(os.path.join(git_dir, 'MERGE_MSG')) and\n        os.path.exists(os.path.join(git_dir, 'MERGE_HEAD'))\n    )\n\n\ndef parse_merge_msg_for_conflicts(merge_msg: bytes) -> List[str]:\n    # Conflicted files start with tabs\n    return [\n        line.lstrip(b'#').strip().decode()\n        for line in merge_msg.splitlines()\n        # '#\\t' for git 2.4.1\n        if line.startswith((b'\\t', b'#\\t'))\n    ]\n\n\ndef get_conflicted_files() -> Set[str]:\n    logger.info('Checking merge-conflict files only.')\n    # Need to get the conflicted files from the MERGE_MSG because they could\n    # have resolved the conflict by choosing one side or the other\n    with open(os.path.join(get_git_dir('.'), 'MERGE_MSG'), 'rb') as f:\n        merge_msg = f.read()\n    merge_conflict_filenames = parse_merge_msg_for_conflicts(merge_msg)\n\n    # This will get the rest of the changes made after the merge.\n    # If they resolved the merge conflict by choosing a mesh of both sides\n    # this will also include the conflicted files\n    tree_hash = cmd_output('git', 'write-tree')[1].strip()\n    merge_diff_filenames = zsplit(\n        cmd_output(\n            'git', 'diff', '--name-only', '--no-ext-diff', '-z',\n            '-m', tree_hash, 'HEAD', 'MERGE_HEAD',\n        )[1],\n    )\n    return set(merge_conflict_filenames) | set(merge_diff_filenames)\n\n\ndef get_staged_files(cwd: Optional[str] = None) -> List[str]:\n    return zsplit(\n        cmd_output(\n            'git', 'diff', '--staged', '--name-only', '--no-ext-diff', '-z',\n            # Everything except for D\n            '--diff-filter=ACMRTUXB',\n            cwd=cwd,\n        )[1],\n    )\n\n\ndef intent_to_add_files() -> List[str]:\n    _, stdout, _ = cmd_output(\n        'git', 'status', '--ignore-submodules', '--porcelain', '-z',\n    )\n    parts = list(reversed(zsplit(stdout)))\n    intent_to_add = []\n    while parts:\n        line = parts.pop()\n        status, filename = line[:3], line[3:]\n        if status[0] in {'C', 'R'}:  # renames / moves have an additional arg\n            parts.pop()\n        if status[1] == 'A':\n            intent_to_add.append(filename)\n    return intent_to_add\n\n\ndef get_all_files() -> List[str]:\n    return zsplit(cmd_output('git', 'ls-files', '-z')[1])\n\n\ndef get_changed_files(old: str, new: str) -> List[str]:\n    return zsplit(\n        cmd_output(\n            'git', 'diff', '--name-only', '--no-ext-diff', '-z',\n            f'{old}...{new}',\n        )[1],\n    )\n\n\ndef head_rev(remote: str) -> str:\n    _, out, _ = cmd_output('git', 'ls-remote', '--exit-code', remote, 'HEAD')\n    return out.split()[0]\n\n\ndef has_diff(*args: str, repo: str = '.') -> bool:\n    cmd = ('git', 'diff', '--quiet', '--no-ext-diff', *args)\n    return cmd_output_b(*cmd, cwd=repo, retcode=None)[0] == 1\n\n\ndef has_core_hookpaths_set() -> bool:\n    _, out, _ = cmd_output_b('git', 'config', 'core.hooksPath', retcode=None)\n    return bool(out.strip())\n\n\ndef init_repo(path: str, remote: str) -> None:\n    if os.path.isdir(remote):\n        remote = os.path.abspath(remote)\n\n    env = no_git_env()\n    # avoid the user's template so that hooks do not recurse\n    cmd_output_b('git', 'init', '--template=', path, env=env)\n    cmd_output_b('git', 'remote', 'add', 'origin', remote, cwd=path, env=env)\n\n\ndef commit(repo: str = '.') -> None:\n    env = no_git_env()\n    name, email = 'pre-commit', 'asottile+pre-commit@umich.edu'\n    env['GIT_AUTHOR_NAME'] = env['GIT_COMMITTER_NAME'] = name\n    env['GIT_AUTHOR_EMAIL'] = env['GIT_COMMITTER_EMAIL'] = email\n    cmd = ('git', 'commit', '--no-edit', '--no-gpg-sign', '-n', '-minit')\n    cmd_output_b(*cmd, cwd=repo, env=env)\n\n\ndef git_path(name: str, repo: str = '.') -> str:\n    _, out, _ = cmd_output('git', 'rev-parse', '--git-path', name, cwd=repo)\n    return os.path.join(repo, out.strip())\n\n\ndef check_for_cygwin_mismatch() -> None:\n    \"\"\"See https://github.com/pre-commit/pre-commit/issues/354\"\"\"\n    if sys.platform in ('cygwin', 'win32'):  # pragma: no cover (windows)\n        is_cygwin_python = sys.platform == 'cygwin'\n        try:\n            toplevel = get_root()\n        except FatalError:  # skip the check if we're not in a git repo\n            return\n        is_cygwin_git = toplevel.startswith('/')\n\n        if is_cygwin_python ^ is_cygwin_git:\n            exe_type = {True: '(cygwin)', False: '(windows)'}\n            logger.warn(\n                f'pre-commit has detected a mix of cygwin python / git\\n'\n                f'This combination is not supported, it is likely you will '\n                f'receive an error later in the program.\\n'\n                f'Make sure to use cygwin git+python while using cygwin\\n'\n                f'These can be installed through the cygwin installer.\\n'\n                f' - python {exe_type[is_cygwin_python]}\\n'\n                f' - git {exe_type[is_cygwin_git]}\\n',\n            )\n", "path": "pre_commit/git.py"}], "after_files": [{"content": "import logging\nimport os.path\nimport sys\nfrom typing import Dict\nfrom typing import List\nfrom typing import MutableMapping\nfrom typing import Optional\nfrom typing import Set\n\nfrom pre_commit.errors import FatalError\nfrom pre_commit.util import CalledProcessError\nfrom pre_commit.util import cmd_output\nfrom pre_commit.util import cmd_output_b\n\n\nlogger = logging.getLogger(__name__)\n\n\ndef zsplit(s: str) -> List[str]:\n    s = s.strip('\\0')\n    if s:\n        return s.split('\\0')\n    else:\n        return []\n\n\ndef no_git_env(\n        _env: Optional[MutableMapping[str, str]] = None,\n) -> Dict[str, str]:\n    # Too many bugs dealing with environment variables and GIT:\n    # https://github.com/pre-commit/pre-commit/issues/300\n    # In git 2.6.3 (maybe others), git exports GIT_WORK_TREE while running\n    # pre-commit hooks\n    # In git 1.9.1 (maybe others), git exports GIT_DIR and GIT_INDEX_FILE\n    # while running pre-commit hooks in submodules.\n    # GIT_DIR: Causes git clone to clone wrong thing\n    # GIT_INDEX_FILE: Causes 'error invalid object ...' during commit\n    _env = _env if _env is not None else os.environ\n    return {\n        k: v for k, v in _env.items()\n        if not k.startswith('GIT_') or\n        k in {\n            'GIT_EXEC_PATH', 'GIT_SSH', 'GIT_SSH_COMMAND', 'GIT_SSL_CAINFO',\n            'GIT_SSL_NO_VERIFY',\n        }\n    }\n\n\ndef get_root() -> str:\n    # Git 2.25 introduced a change to \"rev-parse --show-toplevel\" that exposed\n    # underlying volumes for Windows drives mapped with SUBST.  We use\n    # \"rev-parse --show-cdup\" to get the appropriate path, but must perform\n    # an extra check to see if we are in the .git directory.\n    try:\n        root = os.path.abspath(\n            cmd_output('git', 'rev-parse', '--show-cdup')[1].strip(),\n        )\n        git_dir = os.path.abspath(get_git_dir())\n    except CalledProcessError:\n        raise FatalError(\n            'git failed. Is it installed, and are you in a Git repository '\n            'directory?',\n        )\n    if os.path.samefile(root, git_dir):\n        raise FatalError(\n            'git toplevel unexpectedly empty! make sure you are not '\n            'inside the `.git` directory of your repository.',\n        )\n    return root\n\n\ndef get_git_dir(git_root: str = '.') -> str:\n    opts = ('--git-common-dir', '--git-dir')\n    _, out, _ = cmd_output('git', 'rev-parse', *opts, cwd=git_root)\n    for line, opt in zip(out.splitlines(), opts):\n        if line != opt:  # pragma: no branch (git < 2.5)\n            return os.path.normpath(os.path.join(git_root, line))\n    else:\n        raise AssertionError('unreachable: no git dir')\n\n\ndef get_remote_url(git_root: str) -> str:\n    _, out, _ = cmd_output('git', 'config', 'remote.origin.url', cwd=git_root)\n    return out.strip()\n\n\ndef is_in_merge_conflict() -> bool:\n    git_dir = get_git_dir('.')\n    return (\n        os.path.exists(os.path.join(git_dir, 'MERGE_MSG')) and\n        os.path.exists(os.path.join(git_dir, 'MERGE_HEAD'))\n    )\n\n\ndef parse_merge_msg_for_conflicts(merge_msg: bytes) -> List[str]:\n    # Conflicted files start with tabs\n    return [\n        line.lstrip(b'#').strip().decode()\n        for line in merge_msg.splitlines()\n        # '#\\t' for git 2.4.1\n        if line.startswith((b'\\t', b'#\\t'))\n    ]\n\n\ndef get_conflicted_files() -> Set[str]:\n    logger.info('Checking merge-conflict files only.')\n    # Need to get the conflicted files from the MERGE_MSG because they could\n    # have resolved the conflict by choosing one side or the other\n    with open(os.path.join(get_git_dir('.'), 'MERGE_MSG'), 'rb') as f:\n        merge_msg = f.read()\n    merge_conflict_filenames = parse_merge_msg_for_conflicts(merge_msg)\n\n    # This will get the rest of the changes made after the merge.\n    # If they resolved the merge conflict by choosing a mesh of both sides\n    # this will also include the conflicted files\n    tree_hash = cmd_output('git', 'write-tree')[1].strip()\n    merge_diff_filenames = zsplit(\n        cmd_output(\n            'git', 'diff', '--name-only', '--no-ext-diff', '-z',\n            '-m', tree_hash, 'HEAD', 'MERGE_HEAD',\n        )[1],\n    )\n    return set(merge_conflict_filenames) | set(merge_diff_filenames)\n\n\ndef get_staged_files(cwd: Optional[str] = None) -> List[str]:\n    return zsplit(\n        cmd_output(\n            'git', 'diff', '--staged', '--name-only', '--no-ext-diff', '-z',\n            # Everything except for D\n            '--diff-filter=ACMRTUXB',\n            cwd=cwd,\n        )[1],\n    )\n\n\ndef intent_to_add_files() -> List[str]:\n    _, stdout, _ = cmd_output(\n        'git', 'status', '--ignore-submodules', '--porcelain', '-z',\n    )\n    parts = list(reversed(zsplit(stdout)))\n    intent_to_add = []\n    while parts:\n        line = parts.pop()\n        status, filename = line[:3], line[3:]\n        if status[0] in {'C', 'R'}:  # renames / moves have an additional arg\n            parts.pop()\n        if status[1] == 'A':\n            intent_to_add.append(filename)\n    return intent_to_add\n\n\ndef get_all_files() -> List[str]:\n    return zsplit(cmd_output('git', 'ls-files', '-z')[1])\n\n\ndef get_changed_files(old: str, new: str) -> List[str]:\n    return zsplit(\n        cmd_output(\n            'git', 'diff', '--name-only', '--no-ext-diff', '-z',\n            f'{old}...{new}',\n        )[1],\n    )\n\n\ndef head_rev(remote: str) -> str:\n    _, out, _ = cmd_output('git', 'ls-remote', '--exit-code', remote, 'HEAD')\n    return out.split()[0]\n\n\ndef has_diff(*args: str, repo: str = '.') -> bool:\n    cmd = ('git', 'diff', '--quiet', '--no-ext-diff', *args)\n    return cmd_output_b(*cmd, cwd=repo, retcode=None)[0] == 1\n\n\ndef has_core_hookpaths_set() -> bool:\n    _, out, _ = cmd_output_b('git', 'config', 'core.hooksPath', retcode=None)\n    return bool(out.strip())\n\n\ndef init_repo(path: str, remote: str) -> None:\n    if os.path.isdir(remote):\n        remote = os.path.abspath(remote)\n\n    env = no_git_env()\n    # avoid the user's template so that hooks do not recurse\n    cmd_output_b('git', 'init', '--template=', path, env=env)\n    cmd_output_b('git', 'remote', 'add', 'origin', remote, cwd=path, env=env)\n\n\ndef commit(repo: str = '.') -> None:\n    env = no_git_env()\n    name, email = 'pre-commit', 'asottile+pre-commit@umich.edu'\n    env['GIT_AUTHOR_NAME'] = env['GIT_COMMITTER_NAME'] = name\n    env['GIT_AUTHOR_EMAIL'] = env['GIT_COMMITTER_EMAIL'] = email\n    cmd = ('git', 'commit', '--no-edit', '--no-gpg-sign', '-n', '-minit')\n    cmd_output_b(*cmd, cwd=repo, env=env)\n\n\ndef git_path(name: str, repo: str = '.') -> str:\n    _, out, _ = cmd_output('git', 'rev-parse', '--git-path', name, cwd=repo)\n    return os.path.join(repo, out.strip())\n\n\ndef check_for_cygwin_mismatch() -> None:\n    \"\"\"See https://github.com/pre-commit/pre-commit/issues/354\"\"\"\n    if sys.platform in ('cygwin', 'win32'):  # pragma: no cover (windows)\n        is_cygwin_python = sys.platform == 'cygwin'\n        try:\n            toplevel = get_root()\n        except FatalError:  # skip the check if we're not in a git repo\n            return\n        is_cygwin_git = toplevel.startswith('/')\n\n        if is_cygwin_python ^ is_cygwin_git:\n            exe_type = {True: '(cygwin)', False: '(windows)'}\n            logger.warn(\n                f'pre-commit has detected a mix of cygwin python / git\\n'\n                f'This combination is not supported, it is likely you will '\n                f'receive an error later in the program.\\n'\n                f'Make sure to use cygwin git+python while using cygwin\\n'\n                f'These can be installed through the cygwin installer.\\n'\n                f' - python {exe_type[is_cygwin_python]}\\n'\n                f' - git {exe_type[is_cygwin_git]}\\n',\n            )\n", "path": "pre_commit/git.py"}]}