We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Destructive operation on submodules with `submodule.recurse = true` git setting
### describe your issue

I added pre-commit to an ansible playbook repository that does extensive usage of git submodules for roles.

I had two submodules with extensive changes:

```shell
$ gst
On branch master
Your branch is ahead of 'origin/master' by 3 commits.
  (use "git push" to publish your local commits)
...
        modified:   roles/dnscrypt-proxy (modified content) <-- submodule with extensive (non-commited) changes
        modified:   roles/syncthing (modified content) <-- another submodule with extensive (non-commited) changes
...
```

Upon doing a commit I received an error (check ~/.cache/pre-commit/pre-commit.log section) due to having some problems in my repo and its submodules.
Notice that I cold not even run `git status` afterwards:
```shell
$ gst
fatal: not a git repository: roles/matomo/../../.git/modules/roles/matomo
```
After fixing the problems:
```shell
$ rm -rf roles/matomo/ .git/modules/roles/matomo/
$ git submodule update
Cloning into '/home/apoc/w/posg-ops/roles/matomo'...
Submodule path 'roles/matomo': checked out '######'
fatal: could not get a repository handle for submodule 'roles/php'
$ rm -rf roles/php/ .git/modules/roles/php
$ git submodule update
Cloning into '/home/apoc/w/posg-ops/roles/php'...
Submodule path 'roles/php': checked out '######'
```
and running `git apply` to have my changes back:
```shell
$ git apply /home/a666/.cache/pre-commit/patch1632709290-1076629
```

I noticed that my two submodules now had no changes in them... 
And unfortunately I see nothing in pre-commit cache area to help me recover them.

### pre-commit --version

pre-commit 2.15.0

### .pre-commit-config.yaml

```yaml
---
repos:
  - repo: https://github.com/pre-commit/pre-commit-hooks
    rev: v4.0.1
    hooks:
      - id: trailing-whitespace
      - id: end-of-file-fixer
      - id: check-yaml
      - id: check-toml
      - id: check-added-large-files

  - repo: https://github.com/editorconfig-checker/editorconfig-checker.python
    rev: 2.3.54
    hooks:
      - id: editorconfig-checker

  - repo: https://github.com/antonbabenko/pre-commit-terraform
    rev: v1.51.0
    hooks:
      - id: terraform_validate
      - id: terraform_fmt
      - id: checkov
      - id: terrascan
      - id: terraform_tfsec
```


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

### version information

```
pre-commit version: 2.15.0
sys.version:
    3.9.7 (default, Aug 31 2021, 13:28:12)
    [GCC 11.1.0]
sys.executable: /home/a666/.local/pipx/venvs/pre-commit/bin/python
os.name: posix
sys.platform: linux
```

### error information

```
An unexpected error has occurred: CalledProcessError: command: ('/usr/lib/git-core/git', 'checkout', '--', '.')
return code: 255
expected return code: 0
stdout: (none)
stderr:
    Migrating git directory of 'roles/fail2ban' from
    '/home/a666/ops/roles/fail2ban/.git' to
    '/home/a666/ops/.git/modules/roles/fail2ban'
    fatal: not a git repository: ../../.git/modules/roles/matomo
    error: Submodule 'roles/matomo' could not be updated.
    Migrating git directory of 'roles/openproject' from
    '/home/a666/ops/roles/openproject/.git' to
    '/home/a666/ops/.git/modules/roles/openproject'
    Migrating git directory of 'roles/oryhydra' from
    '/home/a666/ops/roles/oryhydra/.git' to
    '/home/a666/ops/.git/modules/roles/oryhydra'
    fatal: not a git repository: ../../.git/modules/roles/php
    error: Submodule 'roles/php' could not be updated.

```

```
Traceback (most recent call last):
  File "/home/a666/.local/pipx/venvs/pre-commit/lib/python3.9/site-packages/pre_commit/error_handler.py", line 65, in error_handler
    yield
  File "/home/a666/.local/pipx/venvs/pre-commit/lib/python3.9/site-packages/pre_commit/main.py", line 368, in main
    return hook_impl(
  File "/home/a666/.local/pipx/venvs/pre-commit/lib/python3.9/site-packages/pre_commit/commands/hook_impl.py", line 237, in hook_impl
    return retv | run(config, store, ns)
  File "/home/a666/.local/pipx/venvs/pre-commit/lib/python3.9/site-packages/pre_commit/commands/run.py", line 399, in run
    exit_stack.enter_context(staged_files_only(store.directory))
  File "/usr/lib/python3.9/contextlib.py", line 448, in enter_context
    result = _cm_type.__enter__(cm)
  File "/usr/lib/python3.9/contextlib.py", line 119, in __enter__
    return next(self.gen)
  File "/home/a666/.local/pipx/venvs/pre-commit/lib/python3.9/site-packages/pre_commit/staged_files_only.py", line 92, in staged_files_only
    with _intent_to_add_cleared(), _unstaged_changes_cleared(patch_dir):
  File "/usr/lib/python3.9/contextlib.py", line 119, in __enter__
    return next(self.gen)
  File "/home/a666/.local/pipx/venvs/pre-commit/lib/python3.9/site-packages/pre_commit/staged_files_only.py", line 61, in _unstaged_changes_cleared
    cmd_output_b('git', 'checkout', '--', '.', env=no_checkout_env)
  File "/home/a666/.local/pipx/venvs/pre-commit/lib/python3.9/site-packages/pre_commit/util.py", line 154, in cmd_output_b
    raise CalledProcessError(returncode, cmd, retcode, stdout_b, stderr_b)
pre_commit.util.CalledProcessError: command: ('/usr/lib/git-core/git', 'checkout', '--', '.')
return code: 255
expected return code: 0
stdout: (none)
stderr:
    Migrating git directory of 'roles/fail2ban' from
    '/home/a666/ops/roles/fail2ban/.git' to
    '/home/a666/ops/.git/modules/roles/fail2ban'
    fatal: not a git repository: ../../.git/modules/roles/matomo
    error: Submodule 'roles/matomo' could not be updated.
    Migrating git directory of 'roles/openproject' from
    '/home/a666/ops/roles/openproject/.git' to
    '/home/a666/ops/.git/modules/roles/openproject'
    Migrating git directory of 'roles/oryhydra' from
    '/home/a666/ops/roles/oryhydra/.git' to
    '/home/a666/ops/.git/modules/roles/oryhydra'
    fatal: not a git repository: ../../.git/modules/roles/php
    error: Submodule 'roles/php' could not be updated.
```


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

{"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@@ -13,6 +13,12 @@\n \n logger = 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 \n def _git_apply(patch: str) -> None:\n     args = ('apply', '--whitespace=nowarn', patch)\n@@ -58,7 +64,7 @@\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('git', 'checkout', '--', '.', env=no_checkout_env)\n+        cmd_output_b(*_CHECKOUT_CMD, env=no_checkout_env)\n \n         try:\n             yield\n@@ -74,7 +80,7 @@\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('git', 'checkout', '--', '.', env=no_checkout_env)\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", "issue": "Destructive operation on submodules with `submodule.recurse = true` git setting\n### describe your issue\n\nI added pre-commit to an ansible playbook repository that does extensive usage of git submodules for roles.\r\n\r\nI had two submodules with extensive changes:\r\n\r\n```shell\r\n$ gst\r\nOn branch master\r\nYour branch is ahead of 'origin/master' by 3 commits.\r\n  (use \"git push\" to publish your local commits)\r\n...\r\n        modified:   roles/dnscrypt-proxy (modified content) <-- submodule with extensive (non-commited) changes\r\n        modified:   roles/syncthing (modified content) <-- another submodule with extensive (non-commited) changes\r\n...\r\n```\r\n\r\nUpon doing a commit I received an error (check ~/.cache/pre-commit/pre-commit.log section) due to having some problems in my repo and its submodules.\r\nNotice that I cold not even run `git status` afterwards:\r\n```shell\r\n$ gst\r\nfatal: not a git repository: roles/matomo/../../.git/modules/roles/matomo\r\n```\r\nAfter fixing the problems:\r\n```shell\r\n$ rm -rf roles/matomo/ .git/modules/roles/matomo/\r\n$ git submodule update\r\nCloning into '/home/apoc/w/posg-ops/roles/matomo'...\r\nSubmodule path 'roles/matomo': checked out '######'\r\nfatal: could not get a repository handle for submodule 'roles/php'\r\n$ rm -rf roles/php/ .git/modules/roles/php\r\n$ git submodule update\r\nCloning into '/home/apoc/w/posg-ops/roles/php'...\r\nSubmodule path 'roles/php': checked out '######'\r\n```\r\nand running `git apply` to have my changes back:\r\n```shell\r\n$ git apply /home/a666/.cache/pre-commit/patch1632709290-1076629\r\n```\r\n\r\nI noticed that my two submodules now had no changes in them... \r\nAnd unfortunately I see nothing in pre-commit cache area to help me recover them.\n\n### pre-commit --version\n\npre-commit 2.15.0\n\n### .pre-commit-config.yaml\n\n```yaml\n---\r\nrepos:\r\n  - repo: https://github.com/pre-commit/pre-commit-hooks\r\n    rev: v4.0.1\r\n    hooks:\r\n      - id: trailing-whitespace\r\n      - id: end-of-file-fixer\r\n      - id: check-yaml\r\n      - id: check-toml\r\n      - id: check-added-large-files\r\n\r\n  - repo: https://github.com/editorconfig-checker/editorconfig-checker.python\r\n    rev: 2.3.54\r\n    hooks:\r\n      - id: editorconfig-checker\r\n\r\n  - repo: https://github.com/antonbabenko/pre-commit-terraform\r\n    rev: v1.51.0\r\n    hooks:\r\n      - id: terraform_validate\r\n      - id: terraform_fmt\r\n      - id: checkov\r\n      - id: terrascan\r\n      - id: terraform_tfsec\n```\n\n\n### ~/.cache/pre-commit/pre-commit.log (if present)\n\n### version information\r\n\r\n```\r\npre-commit version: 2.15.0\r\nsys.version:\r\n    3.9.7 (default, Aug 31 2021, 13:28:12)\r\n    [GCC 11.1.0]\r\nsys.executable: /home/a666/.local/pipx/venvs/pre-commit/bin/python\r\nos.name: posix\r\nsys.platform: linux\r\n```\r\n\r\n### error information\r\n\r\n```\r\nAn unexpected error has occurred: CalledProcessError: command: ('/usr/lib/git-core/git', 'checkout', '--', '.')\r\nreturn code: 255\r\nexpected return code: 0\r\nstdout: (none)\r\nstderr:\r\n    Migrating git directory of 'roles/fail2ban' from\r\n    '/home/a666/ops/roles/fail2ban/.git' to\r\n    '/home/a666/ops/.git/modules/roles/fail2ban'\r\n    fatal: not a git repository: ../../.git/modules/roles/matomo\r\n    error: Submodule 'roles/matomo' could not be updated.\r\n    Migrating git directory of 'roles/openproject' from\r\n    '/home/a666/ops/roles/openproject/.git' to\r\n    '/home/a666/ops/.git/modules/roles/openproject'\r\n    Migrating git directory of 'roles/oryhydra' from\r\n    '/home/a666/ops/roles/oryhydra/.git' to\r\n    '/home/a666/ops/.git/modules/roles/oryhydra'\r\n    fatal: not a git repository: ../../.git/modules/roles/php\r\n    error: Submodule 'roles/php' could not be updated.\r\n\r\n```\r\n\r\n```\r\nTraceback (most recent call last):\r\n  File \"/home/a666/.local/pipx/venvs/pre-commit/lib/python3.9/site-packages/pre_commit/error_handler.py\", line 65, in error_handler\r\n    yield\r\n  File \"/home/a666/.local/pipx/venvs/pre-commit/lib/python3.9/site-packages/pre_commit/main.py\", line 368, in main\r\n    return hook_impl(\r\n  File \"/home/a666/.local/pipx/venvs/pre-commit/lib/python3.9/site-packages/pre_commit/commands/hook_impl.py\", line 237, in hook_impl\r\n    return retv | run(config, store, ns)\r\n  File \"/home/a666/.local/pipx/venvs/pre-commit/lib/python3.9/site-packages/pre_commit/commands/run.py\", line 399, in run\r\n    exit_stack.enter_context(staged_files_only(store.directory))\r\n  File \"/usr/lib/python3.9/contextlib.py\", line 448, in enter_context\r\n    result = _cm_type.__enter__(cm)\r\n  File \"/usr/lib/python3.9/contextlib.py\", line 119, in __enter__\r\n    return next(self.gen)\r\n  File \"/home/a666/.local/pipx/venvs/pre-commit/lib/python3.9/site-packages/pre_commit/staged_files_only.py\", line 92, in staged_files_only\r\n    with _intent_to_add_cleared(), _unstaged_changes_cleared(patch_dir):\r\n  File \"/usr/lib/python3.9/contextlib.py\", line 119, in __enter__\r\n    return next(self.gen)\r\n  File \"/home/a666/.local/pipx/venvs/pre-commit/lib/python3.9/site-packages/pre_commit/staged_files_only.py\", line 61, in _unstaged_changes_cleared\r\n    cmd_output_b('git', 'checkout', '--', '.', env=no_checkout_env)\r\n  File \"/home/a666/.local/pipx/venvs/pre-commit/lib/python3.9/site-packages/pre_commit/util.py\", line 154, in cmd_output_b\r\n    raise CalledProcessError(returncode, cmd, retcode, stdout_b, stderr_b)\r\npre_commit.util.CalledProcessError: command: ('/usr/lib/git-core/git', 'checkout', '--', '.')\r\nreturn code: 255\r\nexpected return code: 0\r\nstdout: (none)\r\nstderr:\r\n    Migrating git directory of 'roles/fail2ban' from\r\n    '/home/a666/ops/roles/fail2ban/.git' to\r\n    '/home/a666/ops/.git/modules/roles/fail2ban'\r\n    fatal: not a git repository: ../../.git/modules/roles/matomo\r\n    error: Submodule 'roles/matomo' could not be updated.\r\n    Migrating git directory of 'roles/openproject' from\r\n    '/home/a666/ops/roles/openproject/.git' to\r\n    '/home/a666/ops/.git/modules/roles/openproject'\r\n    Migrating git directory of 'roles/oryhydra' from\r\n    '/home/a666/ops/roles/oryhydra/.git' to\r\n    '/home/a666/ops/.git/modules/roles/oryhydra'\r\n    fatal: not a git repository: ../../.git/modules/roles/php\r\n    error: Submodule 'roles/php' could not be updated.\r\n```\r\n\n", "before_files": [{"content": "import 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\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('git', 'checkout', '--', '.', 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('git', 'checkout', '--', '.', 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": "import 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"}]}