We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Add GIT_HTTP_PROXY_AUTHMETHOD to kept environment variables
### describe your issue

On the cluster I work on, there’s a proxy. It’s… Let’s just say that proxy is being a pain, and to make it work we have to set the environment variable GIT_HTTP_PROXY_AUTHMETHOD.

In pre_commit/git.py however, only a small subset of variables are kept, and that one is not among them. So, sure, I can (and did) edit the script to keep that one too (and it works now) but it’s not ideal.

### pre-commit --version

pre-commit 2.17.0

### .pre-commit-config.yaml

```yaml
Not relevant
```


### ~/.cache/pre-commit/pre-commit.log (if present)

_No response_

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

{"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@@ -43,6 +43,7 @@\n         k in {\n             'GIT_EXEC_PATH', 'GIT_SSH', 'GIT_SSH_COMMAND', 'GIT_SSL_CAINFO',\n             'GIT_SSL_NO_VERIFY', 'GIT_CONFIG_COUNT',\n+            'GIT_HTTP_PROXY_AUTHMETHOD',\n         }\n     }\n", "issue": "Add GIT_HTTP_PROXY_AUTHMETHOD to kept environment variables\n### describe your issue\n\nOn the cluster I work on, there\u2019s a proxy. It\u2019s\u2026 Let\u2019s just say that proxy is being a pain, and to make it work we have to set the environment variable GIT_HTTP_PROXY_AUTHMETHOD.\r\n\r\nIn pre_commit/git.py however, only a small subset of variables are kept, and that one is not among them. So, sure, I can (and did) edit the script to keep that one too (and it works now) but it\u2019s not ideal.\n\n### pre-commit --version\n\npre-commit 2.17.0\n\n### .pre-commit-config.yaml\n\n```yaml\nNot relevant\n```\n\n\n### ~/.cache/pre-commit/pre-commit.log (if present)\n\n_No response_\n", "before_files": [{"content": "from __future__ import annotations\n\nimport logging\nimport os.path\nimport sys\nfrom typing import MutableMapping\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\nlogger = logging.getLogger(__name__)\n\n# see #2046\nNO_FS_MONITOR = ('-c', 'core.useBuiltinFSMonitor=false')\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: MutableMapping[str, str] | None = 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.startswith(('GIT_CONFIG_KEY_', 'GIT_CONFIG_VALUE_')) or\n        k in {\n            'GIT_EXEC_PATH', 'GIT_SSH', 'GIT_SSH_COMMAND', 'GIT_SSL_CAINFO',\n            'GIT_SSL_NO_VERIFY', 'GIT_CONFIG_COUNT',\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: str | None = 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    diff_cmd = ('git', 'diff', '--name-only', '--no-ext-diff', '-z')\n    try:\n        _, out, _ = cmd_output(*diff_cmd, f'{old}...{new}')\n    except CalledProcessError:  # pragma: no cover (new git)\n        # on newer git where old and new do not have a merge base git fails\n        # so we try a full diff (this is what old git did for us!)\n        _, out, _ = cmd_output(*diff_cmd, f'{old}..{new}')\n\n    return zsplit(out)\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    git = ('git', *NO_FS_MONITOR)\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": "from __future__ import annotations\n\nimport logging\nimport os.path\nimport sys\nfrom typing import MutableMapping\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\nlogger = logging.getLogger(__name__)\n\n# see #2046\nNO_FS_MONITOR = ('-c', 'core.useBuiltinFSMonitor=false')\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: MutableMapping[str, str] | None = 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.startswith(('GIT_CONFIG_KEY_', 'GIT_CONFIG_VALUE_')) or\n        k in {\n            'GIT_EXEC_PATH', 'GIT_SSH', 'GIT_SSH_COMMAND', 'GIT_SSL_CAINFO',\n            'GIT_SSL_NO_VERIFY', 'GIT_CONFIG_COUNT',\n            'GIT_HTTP_PROXY_AUTHMETHOD',\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: str | None = 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    diff_cmd = ('git', 'diff', '--name-only', '--no-ext-diff', '-z')\n    try:\n        _, out, _ = cmd_output(*diff_cmd, f'{old}...{new}')\n    except CalledProcessError:  # pragma: no cover (new git)\n        # on newer git where old and new do not have a merge base git fails\n        # so we try a full diff (this is what old git did for us!)\n        _, out, _ = cmd_output(*diff_cmd, f'{old}..{new}')\n\n    return zsplit(out)\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    git = ('git', *NO_FS_MONITOR)\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"}]}