We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Pre-commit does not always automatically re-apply patch when stopped with Ctrl-C
As mentioned in this issue:

> if you can reproduce your findings where it doesn't re-apply I'd be more interested in that

_Originally posted by @asottile in https://github.com/pre-commit/pre-commit/issues/2251#issuecomment-1043529759_

----

I reproduced it once, although unfortunately I can't share the config since it is from a private repository in my organization, I do have a log which might provide a clue where this is happening.

In this case, the affected repo uses around 35 hooks, some custom and some from various upstreams. I was doing a `git commit --amend` but forgot to stage some changes, so I canceled to try a `git commit --amend -a` instead, when I hit the issue.

### version information

```
pre-commit version: 2.17.0
git --version: git version 2.32.0
sys.version:
    3.6.8 (default, Dec  2 2020, 12:54:58) 
    [GCC Apple LLVM 12.0.0 (clang-1200.0.32.27)]
sys.executable: /Users/ichamberlain/.pyenv/versions/3.6.8/envs/i95-py3/bin/python
os.name: posix
sys.platform: darwin
```

### error information

```
Interrupted (^C): KeyboardInterrupt: 
```

```
Traceback (most recent call last):
  File "/Users/ichamberlain/.pyenv/versions/3.6.8/envs/i95-py3/lib/python3.6/site-packages/pre_commit/error_handler.py", line 70, in error_handler
    yield
  File "/Users/ichamberlain/.pyenv/versions/3.6.8/envs/i95-py3/lib/python3.6/site-packages/pre_commit/main.py", line 375, in main
    args=args.rest[1:],
  File "/Users/ichamberlain/.pyenv/versions/3.6.8/envs/i95-py3/lib/python3.6/site-packages/pre_commit/commands/hook_impl.py", line 237, in hook_impl
    return retv | run(config, store, ns)
  File "/Users/ichamberlain/.pyenv/versions/3.6.8/envs/i95-py3/lib/python3.6/site-packages/pre_commit/commands/run.py", line 398, in run
    exit_stack.enter_context(staged_files_only(store.directory))
  File "/Users/ichamberlain/.pyenv/versions/3.6.8/lib/python3.6/contextlib.py", line 330, in enter_context
    result = _cm_type.__enter__(cm)
  File "/Users/ichamberlain/.pyenv/versions/3.6.8/lib/python3.6/contextlib.py", line 81, in __enter__
    return next(self.gen)
  File "/Users/ichamberlain/.pyenv/versions/3.6.8/envs/i95-py3/lib/python3.6/site-packages/pre_commit/staged_files_only.py", line 98, in staged_files_only
    with _intent_to_add_cleared(), _unstaged_changes_cleared(patch_dir):
  File "/Users/ichamberlain/.pyenv/versions/3.6.8/lib/python3.6/contextlib.py", line 81, in __enter__
    return next(self.gen)
  File "/Users/ichamberlain/.pyenv/versions/3.6.8/envs/i95-py3/lib/python3.6/site-packages/pre_commit/staged_files_only.py", line 67, in _unstaged_changes_cleared
    cmd_output_b(*_CHECKOUT_CMD, env=no_checkout_env)
  File "/Users/ichamberlain/.pyenv/versions/3.6.8/envs/i95-py3/lib/python3.6/site-packages/pre_commit/util.py", line 150, in cmd_output_b
    stdout_b, stderr_b = proc.communicate()
  File "/Users/ichamberlain/.pyenv/versions/3.6.8/lib/python3.6/subprocess.py", line 863, in communicate
    stdout, stderr = self._communicate(input, endtime, timeout)
  File "/Users/ichamberlain/.pyenv/versions/3.6.8/lib/python3.6/subprocess.py", line 1534, in _communicate
    ready = selector.select(timeout)
  File "/Users/ichamberlain/.pyenv/versions/3.6.8/lib/python3.6/selectors.py", line 376, in select
    fd_event_list = self._poll.poll(timeout)
KeyboardInterrupt
```

--- 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/staged_files_only.py`
Content:
```
1 from __future__ import annotations
2 
3 import contextlib
4 import logging
5 import os.path
6 import time
7 from typing import Generator
8 
9 from pre_commit import git
10 from pre_commit.util import CalledProcessError
11 from pre_commit.util import cmd_output
12 from pre_commit.util import cmd_output_b
13 from pre_commit.xargs import xargs
14 
15 
16 logger = logging.getLogger('pre_commit')
17 
18 # without forcing submodule.recurse=0, changes in nested submodules will be
19 # discarded if `submodule.recurse=1` is configured
20 # we choose this instead of `--no-recurse-submodules` because it works on
21 # versions of git before that option was added to `git checkout`
22 _CHECKOUT_CMD = ('git', '-c', 'submodule.recurse=0', 'checkout', '--', '.')
23 
24 
25 def _git_apply(patch: str) -> None:
26     args = ('apply', '--whitespace=nowarn', patch)
27     try:
28         cmd_output_b('git', *args)
29     except CalledProcessError:
30         # Retry with autocrlf=false -- see #570
31         cmd_output_b('git', '-c', 'core.autocrlf=false', *args)
32 
33 
34 @contextlib.contextmanager
35 def _intent_to_add_cleared() -> Generator[None, None, None]:
36     intent_to_add = git.intent_to_add_files()
37     if intent_to_add:
38         logger.warning('Unstaged intent-to-add files detected.')
39 
40         xargs(('git', 'rm', '--cached', '--'), intent_to_add)
41         try:
42             yield
43         finally:
44             xargs(('git', 'add', '--intent-to-add', '--'), intent_to_add)
45     else:
46         yield
47 
48 
49 @contextlib.contextmanager
50 def _unstaged_changes_cleared(patch_dir: str) -> Generator[None, None, None]:
51     tree = cmd_output('git', 'write-tree')[1].strip()
52     retcode, diff_stdout_binary, _ = cmd_output_b(
53         'git', 'diff-index', '--ignore-submodules', '--binary',
54         '--exit-code', '--no-color', '--no-ext-diff', tree, '--',
55         retcode=None,
56     )
57     if retcode and diff_stdout_binary.strip():
58         patch_filename = f'patch{int(time.time())}-{os.getpid()}'
59         patch_filename = os.path.join(patch_dir, patch_filename)
60         logger.warning('Unstaged files detected.')
61         logger.info(f'Stashing unstaged files to {patch_filename}.')
62         # Save the current unstaged changes as a patch
63         os.makedirs(patch_dir, exist_ok=True)
64         with open(patch_filename, 'wb') as patch_file:
65             patch_file.write(diff_stdout_binary)
66 
67         # prevent recursive post-checkout hooks (#1418)
68         no_checkout_env = dict(os.environ, _PRE_COMMIT_SKIP_POST_CHECKOUT='1')
69         cmd_output_b(*_CHECKOUT_CMD, env=no_checkout_env)
70 
71         try:
72             yield
73         finally:
74             # Try to apply the patch we saved
75             try:
76                 _git_apply(patch_filename)
77             except CalledProcessError:
78                 logger.warning(
79                     'Stashed changes conflicted with hook auto-fixes... '
80                     'Rolling back fixes...',
81                 )
82                 # We failed to apply the patch, presumably due to fixes made
83                 # by hooks.
84                 # Roll back the changes made by hooks.
85                 cmd_output_b(*_CHECKOUT_CMD, env=no_checkout_env)
86                 _git_apply(patch_filename)
87 
88             logger.info(f'Restored changes from {patch_filename}.')
89     else:
90         # There weren't any staged files so we don't need to do anything
91         # special
92         yield
93 
94 
95 @contextlib.contextmanager
96 def staged_files_only(patch_dir: str) -> Generator[None, None, None]:
97     """Clear any unstaged changes from the git working directory inside this
98     context.
99     """
100     with _intent_to_add_cleared(), _unstaged_changes_cleared(patch_dir):
101         yield
102
```
--- 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/staged_files_only.py b/pre_commit/staged_files_only.py
--- a/pre_commit/staged_files_only.py
+++ b/pre_commit/staged_files_only.py
@@ -66,9 +66,9 @@
 
         # prevent recursive post-checkout hooks (#1418)
         no_checkout_env = dict(os.environ, _PRE_COMMIT_SKIP_POST_CHECKOUT='1')
-        cmd_output_b(*_CHECKOUT_CMD, env=no_checkout_env)
 
         try:
+            cmd_output_b(*_CHECKOUT_CMD, env=no_checkout_env)
             yield
         finally:
             # Try to apply the patch we saved

{"golden_diff": "diff --git a/pre_commit/staged_files_only.py b/pre_commit/staged_files_only.py\n--- a/pre_commit/staged_files_only.py\n+++ b/pre_commit/staged_files_only.py\n@@ -66,9 +66,9 @@\n \n         # prevent recursive post-checkout hooks (#1418)\n         no_checkout_env = dict(os.environ, _PRE_COMMIT_SKIP_POST_CHECKOUT='1')\n-        cmd_output_b(*_CHECKOUT_CMD, env=no_checkout_env)\n \n         try:\n+            cmd_output_b(*_CHECKOUT_CMD, env=no_checkout_env)\n             yield\n         finally:\n             # Try to apply the patch we saved\n", "issue": "Pre-commit does not always automatically re-apply patch when stopped with Ctrl-C\nAs mentioned in this issue:\r\n\r\n> if you can reproduce your findings where it doesn't re-apply I'd be more interested in that\r\n\r\n_Originally posted by @asottile in https://github.com/pre-commit/pre-commit/issues/2251#issuecomment-1043529759_\r\n\r\n----\r\n\r\nI reproduced it once, although unfortunately I can't share the config since it is from a private repository in my organization, I do have a log which might provide a clue where this is happening.\r\n\r\nIn this case, the affected repo uses around 35 hooks, some custom and some from various upstreams. I was doing a `git commit --amend` but forgot to stage some changes, so I canceled to try a `git commit --amend -a` instead, when I hit the issue.\r\n\r\n### version information\r\n\r\n```\r\npre-commit version: 2.17.0\r\ngit --version: git version 2.32.0\r\nsys.version:\r\n    3.6.8 (default, Dec  2 2020, 12:54:58) \r\n    [GCC Apple LLVM 12.0.0 (clang-1200.0.32.27)]\r\nsys.executable: /Users/ichamberlain/.pyenv/versions/3.6.8/envs/i95-py3/bin/python\r\nos.name: posix\r\nsys.platform: darwin\r\n```\r\n\r\n### error information\r\n\r\n```\r\nInterrupted (^C): KeyboardInterrupt: \r\n```\r\n\r\n```\r\nTraceback (most recent call last):\r\n  File \"/Users/ichamberlain/.pyenv/versions/3.6.8/envs/i95-py3/lib/python3.6/site-packages/pre_commit/error_handler.py\", line 70, in error_handler\r\n    yield\r\n  File \"/Users/ichamberlain/.pyenv/versions/3.6.8/envs/i95-py3/lib/python3.6/site-packages/pre_commit/main.py\", line 375, in main\r\n    args=args.rest[1:],\r\n  File \"/Users/ichamberlain/.pyenv/versions/3.6.8/envs/i95-py3/lib/python3.6/site-packages/pre_commit/commands/hook_impl.py\", line 237, in hook_impl\r\n    return retv | run(config, store, ns)\r\n  File \"/Users/ichamberlain/.pyenv/versions/3.6.8/envs/i95-py3/lib/python3.6/site-packages/pre_commit/commands/run.py\", line 398, in run\r\n    exit_stack.enter_context(staged_files_only(store.directory))\r\n  File \"/Users/ichamberlain/.pyenv/versions/3.6.8/lib/python3.6/contextlib.py\", line 330, in enter_context\r\n    result = _cm_type.__enter__(cm)\r\n  File \"/Users/ichamberlain/.pyenv/versions/3.6.8/lib/python3.6/contextlib.py\", line 81, in __enter__\r\n    return next(self.gen)\r\n  File \"/Users/ichamberlain/.pyenv/versions/3.6.8/envs/i95-py3/lib/python3.6/site-packages/pre_commit/staged_files_only.py\", line 98, in staged_files_only\r\n    with _intent_to_add_cleared(), _unstaged_changes_cleared(patch_dir):\r\n  File \"/Users/ichamberlain/.pyenv/versions/3.6.8/lib/python3.6/contextlib.py\", line 81, in __enter__\r\n    return next(self.gen)\r\n  File \"/Users/ichamberlain/.pyenv/versions/3.6.8/envs/i95-py3/lib/python3.6/site-packages/pre_commit/staged_files_only.py\", line 67, in _unstaged_changes_cleared\r\n    cmd_output_b(*_CHECKOUT_CMD, env=no_checkout_env)\r\n  File \"/Users/ichamberlain/.pyenv/versions/3.6.8/envs/i95-py3/lib/python3.6/site-packages/pre_commit/util.py\", line 150, in cmd_output_b\r\n    stdout_b, stderr_b = proc.communicate()\r\n  File \"/Users/ichamberlain/.pyenv/versions/3.6.8/lib/python3.6/subprocess.py\", line 863, in communicate\r\n    stdout, stderr = self._communicate(input, endtime, timeout)\r\n  File \"/Users/ichamberlain/.pyenv/versions/3.6.8/lib/python3.6/subprocess.py\", line 1534, in _communicate\r\n    ready = selector.select(timeout)\r\n  File \"/Users/ichamberlain/.pyenv/versions/3.6.8/lib/python3.6/selectors.py\", line 376, in select\r\n    fd_event_list = self._poll.poll(timeout)\r\nKeyboardInterrupt\r\n```\n", "before_files": [{"content": "from __future__ import annotations\n\nimport contextlib\nimport logging\nimport os.path\nimport time\nfrom typing import Generator\n\nfrom pre_commit import git\nfrom pre_commit.util import CalledProcessError\nfrom pre_commit.util import cmd_output\nfrom pre_commit.util import cmd_output_b\nfrom pre_commit.xargs import xargs\n\n\nlogger = logging.getLogger('pre_commit')\n\n# without forcing submodule.recurse=0, changes in nested submodules will be\n# discarded if `submodule.recurse=1` is configured\n# we choose this instead of `--no-recurse-submodules` because it works on\n# versions of git before that option was added to `git checkout`\n_CHECKOUT_CMD = ('git', '-c', 'submodule.recurse=0', 'checkout', '--', '.')\n\n\ndef _git_apply(patch: str) -> None:\n    args = ('apply', '--whitespace=nowarn', patch)\n    try:\n        cmd_output_b('git', *args)\n    except CalledProcessError:\n        # Retry with autocrlf=false -- see #570\n        cmd_output_b('git', '-c', 'core.autocrlf=false', *args)\n\n\n@contextlib.contextmanager\ndef _intent_to_add_cleared() -> Generator[None, None, None]:\n    intent_to_add = git.intent_to_add_files()\n    if intent_to_add:\n        logger.warning('Unstaged intent-to-add files detected.')\n\n        xargs(('git', 'rm', '--cached', '--'), intent_to_add)\n        try:\n            yield\n        finally:\n            xargs(('git', 'add', '--intent-to-add', '--'), intent_to_add)\n    else:\n        yield\n\n\n@contextlib.contextmanager\ndef _unstaged_changes_cleared(patch_dir: str) -> Generator[None, None, None]:\n    tree = cmd_output('git', 'write-tree')[1].strip()\n    retcode, diff_stdout_binary, _ = cmd_output_b(\n        'git', 'diff-index', '--ignore-submodules', '--binary',\n        '--exit-code', '--no-color', '--no-ext-diff', tree, '--',\n        retcode=None,\n    )\n    if retcode and diff_stdout_binary.strip():\n        patch_filename = f'patch{int(time.time())}-{os.getpid()}'\n        patch_filename = os.path.join(patch_dir, patch_filename)\n        logger.warning('Unstaged files detected.')\n        logger.info(f'Stashing unstaged files to {patch_filename}.')\n        # Save the current unstaged changes as a patch\n        os.makedirs(patch_dir, exist_ok=True)\n        with open(patch_filename, 'wb') as patch_file:\n            patch_file.write(diff_stdout_binary)\n\n        # prevent recursive post-checkout hooks (#1418)\n        no_checkout_env = dict(os.environ, _PRE_COMMIT_SKIP_POST_CHECKOUT='1')\n        cmd_output_b(*_CHECKOUT_CMD, env=no_checkout_env)\n\n        try:\n            yield\n        finally:\n            # Try to apply the patch we saved\n            try:\n                _git_apply(patch_filename)\n            except CalledProcessError:\n                logger.warning(\n                    'Stashed changes conflicted with hook auto-fixes... '\n                    'Rolling back fixes...',\n                )\n                # We failed to apply the patch, presumably due to fixes made\n                # by hooks.\n                # Roll back the changes made by hooks.\n                cmd_output_b(*_CHECKOUT_CMD, env=no_checkout_env)\n                _git_apply(patch_filename)\n\n            logger.info(f'Restored changes from {patch_filename}.')\n    else:\n        # There weren't any staged files so we don't need to do anything\n        # special\n        yield\n\n\n@contextlib.contextmanager\ndef staged_files_only(patch_dir: str) -> Generator[None, None, None]:\n    \"\"\"Clear any unstaged changes from the git working directory inside this\n    context.\n    \"\"\"\n    with _intent_to_add_cleared(), _unstaged_changes_cleared(patch_dir):\n        yield\n", "path": "pre_commit/staged_files_only.py"}], "after_files": [{"content": "from __future__ import annotations\n\nimport contextlib\nimport logging\nimport os.path\nimport time\nfrom typing import Generator\n\nfrom pre_commit import git\nfrom pre_commit.util import CalledProcessError\nfrom pre_commit.util import cmd_output\nfrom pre_commit.util import cmd_output_b\nfrom pre_commit.xargs import xargs\n\n\nlogger = logging.getLogger('pre_commit')\n\n# without forcing submodule.recurse=0, changes in nested submodules will be\n# discarded if `submodule.recurse=1` is configured\n# we choose this instead of `--no-recurse-submodules` because it works on\n# versions of git before that option was added to `git checkout`\n_CHECKOUT_CMD = ('git', '-c', 'submodule.recurse=0', 'checkout', '--', '.')\n\n\ndef _git_apply(patch: str) -> None:\n    args = ('apply', '--whitespace=nowarn', patch)\n    try:\n        cmd_output_b('git', *args)\n    except CalledProcessError:\n        # Retry with autocrlf=false -- see #570\n        cmd_output_b('git', '-c', 'core.autocrlf=false', *args)\n\n\n@contextlib.contextmanager\ndef _intent_to_add_cleared() -> Generator[None, None, None]:\n    intent_to_add = git.intent_to_add_files()\n    if intent_to_add:\n        logger.warning('Unstaged intent-to-add files detected.')\n\n        xargs(('git', 'rm', '--cached', '--'), intent_to_add)\n        try:\n            yield\n        finally:\n            xargs(('git', 'add', '--intent-to-add', '--'), intent_to_add)\n    else:\n        yield\n\n\n@contextlib.contextmanager\ndef _unstaged_changes_cleared(patch_dir: str) -> Generator[None, None, None]:\n    tree = cmd_output('git', 'write-tree')[1].strip()\n    retcode, diff_stdout_binary, _ = cmd_output_b(\n        'git', 'diff-index', '--ignore-submodules', '--binary',\n        '--exit-code', '--no-color', '--no-ext-diff', tree, '--',\n        retcode=None,\n    )\n    if retcode and diff_stdout_binary.strip():\n        patch_filename = f'patch{int(time.time())}-{os.getpid()}'\n        patch_filename = os.path.join(patch_dir, patch_filename)\n        logger.warning('Unstaged files detected.')\n        logger.info(f'Stashing unstaged files to {patch_filename}.')\n        # Save the current unstaged changes as a patch\n        os.makedirs(patch_dir, exist_ok=True)\n        with open(patch_filename, 'wb') as patch_file:\n            patch_file.write(diff_stdout_binary)\n\n        # prevent recursive post-checkout hooks (#1418)\n        no_checkout_env = dict(os.environ, _PRE_COMMIT_SKIP_POST_CHECKOUT='1')\n\n        try:\n            cmd_output_b(*_CHECKOUT_CMD, env=no_checkout_env)\n            yield\n        finally:\n            # Try to apply the patch we saved\n            try:\n                _git_apply(patch_filename)\n            except CalledProcessError:\n                logger.warning(\n                    'Stashed changes conflicted with hook auto-fixes... '\n                    'Rolling back fixes...',\n                )\n                # We failed to apply the patch, presumably due to fixes made\n                # by hooks.\n                # Roll back the changes made by hooks.\n                cmd_output_b(*_CHECKOUT_CMD, env=no_checkout_env)\n                _git_apply(patch_filename)\n\n            logger.info(f'Restored changes from {patch_filename}.')\n    else:\n        # There weren't any staged files so we don't need to do anything\n        # special\n        yield\n\n\n@contextlib.contextmanager\ndef staged_files_only(patch_dir: str) -> Generator[None, None, None]:\n    \"\"\"Clear any unstaged changes from the git working directory inside this\n    context.\n    \"\"\"\n    with _intent_to_add_cleared(), _unstaged_changes_cleared(patch_dir):\n        yield\n", "path": "pre_commit/staged_files_only.py"}]}