We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
always_run + no `files` still crashes on `KeyError: files`
I was under the impression `files` was completely ignored for `always_run`, I guess not!

Here's a small reproduction:

```yaml
-   repo: local
    hooks:
    -   id: foo
        name: foo
        always_run: true
        entry: bash -c 'echo hello && exit 1'
        language: system
```

```
$ ./venv-pre_commit/bin/pre-commit run foo
An unexpected error has occurred: KeyError: u'files'
Check the log at ~/.pre-commit/pre-commit.log
```

```
$ cat ~/.pre-commit/pre-commit.log 
An unexpected error has occurred: KeyError: u'files'
Traceback (most recent call last):
  File "/tmp/foo/pre-commit/pre_commit/error_handler.py", line 48, in error_handler
    yield
  File "/tmp/foo/pre-commit/pre_commit/main.py", line 226, in main
    return run(runner, args)
  File "/tmp/foo/pre-commit/pre_commit/commands/run.py", line 235, in run
    return _run_hooks(repo_hooks, args, environ)
  File "/tmp/foo/pre-commit/pre_commit/commands/run.py", line 155, in _run_hooks
    retval |= _run_single_hook(hook, repo, args, skips, cols)
  File "/tmp/foo/pre-commit/pre_commit/commands/run.py", line 60, in _run_single_hook
    filenames = get_filenames(args, hook['files'], hook['exclude'])
KeyError: u'files'
```


--- 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/commands/run.py`
Content:
```
1 from __future__ import print_function
2 from __future__ import unicode_literals
3 
4 import logging
5 import os
6 import subprocess
7 import sys
8 
9 from pre_commit import color
10 from pre_commit import git
11 from pre_commit import output
12 from pre_commit.output import get_hook_message
13 from pre_commit.staged_files_only import staged_files_only
14 from pre_commit.util import cmd_output
15 from pre_commit.util import noop_context
16 
17 
18 logger = logging.getLogger('pre_commit')
19 
20 
21 def _get_skips(environ):
22     skips = environ.get('SKIP', '')
23     return {skip.strip() for skip in skips.split(',') if skip.strip()}
24 
25 
26 def _hook_msg_start(hook, verbose):
27     return '{}{}'.format(
28         '[{}] '.format(hook['id']) if verbose else '',
29         hook['name'],
30     )
31 
32 
33 def get_changed_files(new, old):
34     return cmd_output(
35         'git', 'diff', '--name-only', '{}...{}'.format(old, new),
36     )[1].splitlines()
37 
38 
39 def get_filenames(args, include_expr, exclude_expr):
40     if args.origin and args.source:
41         getter = git.get_files_matching(
42             lambda: get_changed_files(args.origin, args.source),
43         )
44     elif args.files:
45         getter = git.get_files_matching(lambda: args.files)
46     elif args.all_files:
47         getter = git.get_all_files_matching
48     elif git.is_in_merge_conflict():
49         getter = git.get_conflicted_files_matching
50     else:
51         getter = git.get_staged_files_matching
52     return getter(include_expr, exclude_expr)
53 
54 
55 SKIPPED = 'Skipped'
56 NO_FILES = '(no files to check)'
57 
58 
59 def _run_single_hook(hook, repo, args, skips, cols):
60     filenames = get_filenames(args, hook['files'], hook['exclude'])
61     if hook['id'] in skips:
62         output.write(get_hook_message(
63             _hook_msg_start(hook, args.verbose),
64             end_msg=SKIPPED,
65             end_color=color.YELLOW,
66             use_color=args.color,
67             cols=cols,
68         ))
69         return 0
70     elif not filenames and not hook['always_run']:
71         output.write(get_hook_message(
72             _hook_msg_start(hook, args.verbose),
73             postfix=NO_FILES,
74             end_msg=SKIPPED,
75             end_color=color.TURQUOISE,
76             use_color=args.color,
77             cols=cols,
78         ))
79         return 0
80 
81     # Print the hook and the dots first in case the hook takes hella long to
82     # run.
83     output.write(get_hook_message(
84         _hook_msg_start(hook, args.verbose), end_len=6, cols=cols,
85     ))
86     sys.stdout.flush()
87 
88     diff_before = cmd_output('git', 'diff', retcode=None, encoding=None)
89     retcode, stdout, stderr = repo.run_hook(
90         hook,
91         tuple(filenames) if hook['pass_filenames'] else (),
92     )
93     diff_after = cmd_output('git', 'diff', retcode=None, encoding=None)
94 
95     file_modifications = diff_before != diff_after
96 
97     # If the hook makes changes, fail the commit
98     if file_modifications:
99         retcode = 1
100 
101     if retcode:
102         retcode = 1
103         print_color = color.RED
104         pass_fail = 'Failed'
105     else:
106         retcode = 0
107         print_color = color.GREEN
108         pass_fail = 'Passed'
109 
110     output.write_line(color.format_color(pass_fail, print_color, args.color))
111 
112     if (stdout or stderr or file_modifications) and (retcode or args.verbose):
113         output.write_line('hookid: {}\n'.format(hook['id']))
114 
115         # Print a message if failing due to file modifications
116         if file_modifications:
117             output.write('Files were modified by this hook.')
118 
119             if stdout or stderr:
120                 output.write_line(' Additional output:')
121 
122             output.write_line()
123 
124         for out in (stdout, stderr):
125             assert type(out) is bytes, type(out)
126             if out.strip():
127                 output.write_line(out.strip(), logfile_name=hook['log_file'])
128         output.write_line()
129 
130     return retcode
131 
132 
133 def _compute_cols(hooks, verbose):
134     """Compute the number of columns to display hook messages.  The widest
135     that will be displayed is in the no files skipped case:
136 
137         Hook name...(no files to check) Skipped
138 
139     or in the verbose case
140 
141         Hook name [hookid]...(no files to check) Skipped
142     """
143     if hooks:
144         name_len = max(len(_hook_msg_start(hook, verbose)) for hook in hooks)
145     else:
146         name_len = 0
147 
148     cols = name_len + 3 + len(NO_FILES) + 1 + len(SKIPPED)
149     return max(cols, 80)
150 
151 
152 def _run_hooks(repo_hooks, args, environ):
153     """Actually run the hooks."""
154     skips = _get_skips(environ)
155     cols = _compute_cols([hook for _, hook in repo_hooks], args.verbose)
156     retval = 0
157     for repo, hook in repo_hooks:
158         retval |= _run_single_hook(hook, repo, args, skips, cols)
159     if (
160             retval and
161             args.show_diff_on_failure and
162             subprocess.call(('git', 'diff', '--quiet')) != 0
163     ):
164         print('All changes made by hooks:')
165         subprocess.call(('git', 'diff'))
166     return retval
167 
168 
169 def get_repo_hooks(runner):
170     for repo in runner.repositories:
171         for _, hook in repo.hooks:
172             yield (repo, hook)
173 
174 
175 def _has_unmerged_paths(runner):
176     _, stdout, _ = runner.cmd_runner.run(['git', 'ls-files', '--unmerged'])
177     return bool(stdout.strip())
178 
179 
180 def _has_unstaged_config(runner):
181     retcode, _, _ = runner.cmd_runner.run(
182         ('git', 'diff', '--exit-code', runner.config_file_path),
183         retcode=None,
184     )
185     # be explicit, other git errors don't mean it has an unstaged config.
186     return retcode == 1
187 
188 
189 def run(runner, args, environ=os.environ):
190     no_stash = args.no_stash or args.all_files or bool(args.files)
191 
192     # Check if we have unresolved merge conflict files and fail fast.
193     if _has_unmerged_paths(runner):
194         logger.error('Unmerged files.  Resolve before committing.')
195         return 1
196     if bool(args.source) != bool(args.origin):
197         logger.error('Specify both --origin and --source.')
198         return 1
199     if _has_unstaged_config(runner) and not no_stash:
200         if args.allow_unstaged_config:
201             logger.warn(
202                 'You have an unstaged config file and have specified the '
203                 '--allow-unstaged-config option.\n'
204                 'Note that your config will be stashed before the config is '
205                 'parsed unless --no-stash is specified.',
206             )
207         else:
208             logger.error(
209                 'Your .pre-commit-config.yaml is unstaged.\n'
210                 '`git add .pre-commit-config.yaml` to fix this.\n'
211                 'Run pre-commit with --allow-unstaged-config to silence this.'
212             )
213             return 1
214 
215     if no_stash:
216         ctx = noop_context()
217     else:
218         ctx = staged_files_only(runner.cmd_runner)
219 
220     with ctx:
221         repo_hooks = list(get_repo_hooks(runner))
222 
223         if args.hook:
224             repo_hooks = [
225                 (repo, hook) for repo, hook in repo_hooks
226                 if hook['id'] == args.hook
227             ]
228             if not repo_hooks:
229                 output.write_line('No hook with id `{}`'.format(args.hook))
230                 return 1
231 
232         # Filter hooks for stages
233         repo_hooks = [
234             (repo, hook) for repo, hook in repo_hooks
235             if not hook['stages'] or args.hook_stage in hook['stages']
236         ]
237 
238         return _run_hooks(repo_hooks, args, environ)
239
```
--- 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/commands/run.py b/pre_commit/commands/run.py
--- a/pre_commit/commands/run.py
+++ b/pre_commit/commands/run.py
@@ -57,7 +57,7 @@
 
 
 def _run_single_hook(hook, repo, args, skips, cols):
-    filenames = get_filenames(args, hook['files'], hook['exclude'])
+    filenames = get_filenames(args, hook.get('files', ''), hook['exclude'])
     if hook['id'] in skips:
         output.write(get_hook_message(
             _hook_msg_start(hook, args.verbose),

{"golden_diff": "diff --git a/pre_commit/commands/run.py b/pre_commit/commands/run.py\n--- a/pre_commit/commands/run.py\n+++ b/pre_commit/commands/run.py\n@@ -57,7 +57,7 @@\n \n \n def _run_single_hook(hook, repo, args, skips, cols):\n-    filenames = get_filenames(args, hook['files'], hook['exclude'])\n+    filenames = get_filenames(args, hook.get('files', ''), hook['exclude'])\n     if hook['id'] in skips:\n         output.write(get_hook_message(\n             _hook_msg_start(hook, args.verbose),\n", "issue": "always_run + no `files` still crashes on `KeyError: files`\nI was under the impression `files` was completely ignored for `always_run`, I guess not!\r\n\r\nHere's a small reproduction:\r\n\r\n```yaml\r\n-   repo: local\r\n    hooks:\r\n    -   id: foo\r\n        name: foo\r\n        always_run: true\r\n        entry: bash -c 'echo hello && exit 1'\r\n        language: system\r\n```\r\n\r\n```\r\n$ ./venv-pre_commit/bin/pre-commit run foo\r\nAn unexpected error has occurred: KeyError: u'files'\r\nCheck the log at ~/.pre-commit/pre-commit.log\r\n```\r\n\r\n```\r\n$ cat ~/.pre-commit/pre-commit.log \r\nAn unexpected error has occurred: KeyError: u'files'\r\nTraceback (most recent call last):\r\n  File \"/tmp/foo/pre-commit/pre_commit/error_handler.py\", line 48, in error_handler\r\n    yield\r\n  File \"/tmp/foo/pre-commit/pre_commit/main.py\", line 226, in main\r\n    return run(runner, args)\r\n  File \"/tmp/foo/pre-commit/pre_commit/commands/run.py\", line 235, in run\r\n    return _run_hooks(repo_hooks, args, environ)\r\n  File \"/tmp/foo/pre-commit/pre_commit/commands/run.py\", line 155, in _run_hooks\r\n    retval |= _run_single_hook(hook, repo, args, skips, cols)\r\n  File \"/tmp/foo/pre-commit/pre_commit/commands/run.py\", line 60, in _run_single_hook\r\n    filenames = get_filenames(args, hook['files'], hook['exclude'])\r\nKeyError: u'files'\r\n```\r\n\n", "before_files": [{"content": "from __future__ import print_function\nfrom __future__ import unicode_literals\n\nimport logging\nimport os\nimport subprocess\nimport sys\n\nfrom pre_commit import color\nfrom pre_commit import git\nfrom pre_commit import output\nfrom pre_commit.output import get_hook_message\nfrom pre_commit.staged_files_only import staged_files_only\nfrom pre_commit.util import cmd_output\nfrom pre_commit.util import noop_context\n\n\nlogger = logging.getLogger('pre_commit')\n\n\ndef _get_skips(environ):\n    skips = environ.get('SKIP', '')\n    return {skip.strip() for skip in skips.split(',') if skip.strip()}\n\n\ndef _hook_msg_start(hook, verbose):\n    return '{}{}'.format(\n        '[{}] '.format(hook['id']) if verbose else '',\n        hook['name'],\n    )\n\n\ndef get_changed_files(new, old):\n    return cmd_output(\n        'git', 'diff', '--name-only', '{}...{}'.format(old, new),\n    )[1].splitlines()\n\n\ndef get_filenames(args, include_expr, exclude_expr):\n    if args.origin and args.source:\n        getter = git.get_files_matching(\n            lambda: get_changed_files(args.origin, args.source),\n        )\n    elif args.files:\n        getter = git.get_files_matching(lambda: args.files)\n    elif args.all_files:\n        getter = git.get_all_files_matching\n    elif git.is_in_merge_conflict():\n        getter = git.get_conflicted_files_matching\n    else:\n        getter = git.get_staged_files_matching\n    return getter(include_expr, exclude_expr)\n\n\nSKIPPED = 'Skipped'\nNO_FILES = '(no files to check)'\n\n\ndef _run_single_hook(hook, repo, args, skips, cols):\n    filenames = get_filenames(args, hook['files'], hook['exclude'])\n    if hook['id'] in skips:\n        output.write(get_hook_message(\n            _hook_msg_start(hook, args.verbose),\n            end_msg=SKIPPED,\n            end_color=color.YELLOW,\n            use_color=args.color,\n            cols=cols,\n        ))\n        return 0\n    elif not filenames and not hook['always_run']:\n        output.write(get_hook_message(\n            _hook_msg_start(hook, args.verbose),\n            postfix=NO_FILES,\n            end_msg=SKIPPED,\n            end_color=color.TURQUOISE,\n            use_color=args.color,\n            cols=cols,\n        ))\n        return 0\n\n    # Print the hook and the dots first in case the hook takes hella long to\n    # run.\n    output.write(get_hook_message(\n        _hook_msg_start(hook, args.verbose), end_len=6, cols=cols,\n    ))\n    sys.stdout.flush()\n\n    diff_before = cmd_output('git', 'diff', retcode=None, encoding=None)\n    retcode, stdout, stderr = repo.run_hook(\n        hook,\n        tuple(filenames) if hook['pass_filenames'] else (),\n    )\n    diff_after = cmd_output('git', 'diff', retcode=None, encoding=None)\n\n    file_modifications = diff_before != diff_after\n\n    # If the hook makes changes, fail the commit\n    if file_modifications:\n        retcode = 1\n\n    if retcode:\n        retcode = 1\n        print_color = color.RED\n        pass_fail = 'Failed'\n    else:\n        retcode = 0\n        print_color = color.GREEN\n        pass_fail = 'Passed'\n\n    output.write_line(color.format_color(pass_fail, print_color, args.color))\n\n    if (stdout or stderr or file_modifications) and (retcode or args.verbose):\n        output.write_line('hookid: {}\\n'.format(hook['id']))\n\n        # Print a message if failing due to file modifications\n        if file_modifications:\n            output.write('Files were modified by this hook.')\n\n            if stdout or stderr:\n                output.write_line(' Additional output:')\n\n            output.write_line()\n\n        for out in (stdout, stderr):\n            assert type(out) is bytes, type(out)\n            if out.strip():\n                output.write_line(out.strip(), logfile_name=hook['log_file'])\n        output.write_line()\n\n    return retcode\n\n\ndef _compute_cols(hooks, verbose):\n    \"\"\"Compute the number of columns to display hook messages.  The widest\n    that will be displayed is in the no files skipped case:\n\n        Hook name...(no files to check) Skipped\n\n    or in the verbose case\n\n        Hook name [hookid]...(no files to check) Skipped\n    \"\"\"\n    if hooks:\n        name_len = max(len(_hook_msg_start(hook, verbose)) for hook in hooks)\n    else:\n        name_len = 0\n\n    cols = name_len + 3 + len(NO_FILES) + 1 + len(SKIPPED)\n    return max(cols, 80)\n\n\ndef _run_hooks(repo_hooks, args, environ):\n    \"\"\"Actually run the hooks.\"\"\"\n    skips = _get_skips(environ)\n    cols = _compute_cols([hook for _, hook in repo_hooks], args.verbose)\n    retval = 0\n    for repo, hook in repo_hooks:\n        retval |= _run_single_hook(hook, repo, args, skips, cols)\n    if (\n            retval and\n            args.show_diff_on_failure and\n            subprocess.call(('git', 'diff', '--quiet')) != 0\n    ):\n        print('All changes made by hooks:')\n        subprocess.call(('git', 'diff'))\n    return retval\n\n\ndef get_repo_hooks(runner):\n    for repo in runner.repositories:\n        for _, hook in repo.hooks:\n            yield (repo, hook)\n\n\ndef _has_unmerged_paths(runner):\n    _, stdout, _ = runner.cmd_runner.run(['git', 'ls-files', '--unmerged'])\n    return bool(stdout.strip())\n\n\ndef _has_unstaged_config(runner):\n    retcode, _, _ = runner.cmd_runner.run(\n        ('git', 'diff', '--exit-code', runner.config_file_path),\n        retcode=None,\n    )\n    # be explicit, other git errors don't mean it has an unstaged config.\n    return retcode == 1\n\n\ndef run(runner, args, environ=os.environ):\n    no_stash = args.no_stash or args.all_files or bool(args.files)\n\n    # Check if we have unresolved merge conflict files and fail fast.\n    if _has_unmerged_paths(runner):\n        logger.error('Unmerged files.  Resolve before committing.')\n        return 1\n    if bool(args.source) != bool(args.origin):\n        logger.error('Specify both --origin and --source.')\n        return 1\n    if _has_unstaged_config(runner) and not no_stash:\n        if args.allow_unstaged_config:\n            logger.warn(\n                'You have an unstaged config file and have specified the '\n                '--allow-unstaged-config option.\\n'\n                'Note that your config will be stashed before the config is '\n                'parsed unless --no-stash is specified.',\n            )\n        else:\n            logger.error(\n                'Your .pre-commit-config.yaml is unstaged.\\n'\n                '`git add .pre-commit-config.yaml` to fix this.\\n'\n                'Run pre-commit with --allow-unstaged-config to silence this.'\n            )\n            return 1\n\n    if no_stash:\n        ctx = noop_context()\n    else:\n        ctx = staged_files_only(runner.cmd_runner)\n\n    with ctx:\n        repo_hooks = list(get_repo_hooks(runner))\n\n        if args.hook:\n            repo_hooks = [\n                (repo, hook) for repo, hook in repo_hooks\n                if hook['id'] == args.hook\n            ]\n            if not repo_hooks:\n                output.write_line('No hook with id `{}`'.format(args.hook))\n                return 1\n\n        # Filter hooks for stages\n        repo_hooks = [\n            (repo, hook) for repo, hook in repo_hooks\n            if not hook['stages'] or args.hook_stage in hook['stages']\n        ]\n\n        return _run_hooks(repo_hooks, args, environ)\n", "path": "pre_commit/commands/run.py"}], "after_files": [{"content": "from __future__ import print_function\nfrom __future__ import unicode_literals\n\nimport logging\nimport os\nimport subprocess\nimport sys\n\nfrom pre_commit import color\nfrom pre_commit import git\nfrom pre_commit import output\nfrom pre_commit.output import get_hook_message\nfrom pre_commit.staged_files_only import staged_files_only\nfrom pre_commit.util import cmd_output\nfrom pre_commit.util import noop_context\n\n\nlogger = logging.getLogger('pre_commit')\n\n\ndef _get_skips(environ):\n    skips = environ.get('SKIP', '')\n    return {skip.strip() for skip in skips.split(',') if skip.strip()}\n\n\ndef _hook_msg_start(hook, verbose):\n    return '{}{}'.format(\n        '[{}] '.format(hook['id']) if verbose else '',\n        hook['name'],\n    )\n\n\ndef get_changed_files(new, old):\n    return cmd_output(\n        'git', 'diff', '--name-only', '{}...{}'.format(old, new),\n    )[1].splitlines()\n\n\ndef get_filenames(args, include_expr, exclude_expr):\n    if args.origin and args.source:\n        getter = git.get_files_matching(\n            lambda: get_changed_files(args.origin, args.source),\n        )\n    elif args.files:\n        getter = git.get_files_matching(lambda: args.files)\n    elif args.all_files:\n        getter = git.get_all_files_matching\n    elif git.is_in_merge_conflict():\n        getter = git.get_conflicted_files_matching\n    else:\n        getter = git.get_staged_files_matching\n    return getter(include_expr, exclude_expr)\n\n\nSKIPPED = 'Skipped'\nNO_FILES = '(no files to check)'\n\n\ndef _run_single_hook(hook, repo, args, skips, cols):\n    filenames = get_filenames(args, hook.get('files', ''), hook['exclude'])\n    if hook['id'] in skips:\n        output.write(get_hook_message(\n            _hook_msg_start(hook, args.verbose),\n            end_msg=SKIPPED,\n            end_color=color.YELLOW,\n            use_color=args.color,\n            cols=cols,\n        ))\n        return 0\n    elif not filenames and not hook['always_run']:\n        output.write(get_hook_message(\n            _hook_msg_start(hook, args.verbose),\n            postfix=NO_FILES,\n            end_msg=SKIPPED,\n            end_color=color.TURQUOISE,\n            use_color=args.color,\n            cols=cols,\n        ))\n        return 0\n\n    # Print the hook and the dots first in case the hook takes hella long to\n    # run.\n    output.write(get_hook_message(\n        _hook_msg_start(hook, args.verbose), end_len=6, cols=cols,\n    ))\n    sys.stdout.flush()\n\n    diff_before = cmd_output('git', 'diff', retcode=None, encoding=None)\n    retcode, stdout, stderr = repo.run_hook(\n        hook,\n        tuple(filenames) if hook['pass_filenames'] else (),\n    )\n    diff_after = cmd_output('git', 'diff', retcode=None, encoding=None)\n\n    file_modifications = diff_before != diff_after\n\n    # If the hook makes changes, fail the commit\n    if file_modifications:\n        retcode = 1\n\n    if retcode:\n        retcode = 1\n        print_color = color.RED\n        pass_fail = 'Failed'\n    else:\n        retcode = 0\n        print_color = color.GREEN\n        pass_fail = 'Passed'\n\n    output.write_line(color.format_color(pass_fail, print_color, args.color))\n\n    if (stdout or stderr or file_modifications) and (retcode or args.verbose):\n        output.write_line('hookid: {}\\n'.format(hook['id']))\n\n        # Print a message if failing due to file modifications\n        if file_modifications:\n            output.write('Files were modified by this hook.')\n\n            if stdout or stderr:\n                output.write_line(' Additional output:')\n\n            output.write_line()\n\n        for out in (stdout, stderr):\n            assert type(out) is bytes, type(out)\n            if out.strip():\n                output.write_line(out.strip(), logfile_name=hook['log_file'])\n        output.write_line()\n\n    return retcode\n\n\ndef _compute_cols(hooks, verbose):\n    \"\"\"Compute the number of columns to display hook messages.  The widest\n    that will be displayed is in the no files skipped case:\n\n        Hook name...(no files to check) Skipped\n\n    or in the verbose case\n\n        Hook name [hookid]...(no files to check) Skipped\n    \"\"\"\n    if hooks:\n        name_len = max(len(_hook_msg_start(hook, verbose)) for hook in hooks)\n    else:\n        name_len = 0\n\n    cols = name_len + 3 + len(NO_FILES) + 1 + len(SKIPPED)\n    return max(cols, 80)\n\n\ndef _run_hooks(repo_hooks, args, environ):\n    \"\"\"Actually run the hooks.\"\"\"\n    skips = _get_skips(environ)\n    cols = _compute_cols([hook for _, hook in repo_hooks], args.verbose)\n    retval = 0\n    for repo, hook in repo_hooks:\n        retval |= _run_single_hook(hook, repo, args, skips, cols)\n    if (\n            retval and\n            args.show_diff_on_failure and\n            subprocess.call(('git', 'diff', '--quiet')) != 0\n    ):\n        print('All changes made by hooks:')\n        subprocess.call(('git', 'diff'))\n    return retval\n\n\ndef get_repo_hooks(runner):\n    for repo in runner.repositories:\n        for _, hook in repo.hooks:\n            yield (repo, hook)\n\n\ndef _has_unmerged_paths(runner):\n    _, stdout, _ = runner.cmd_runner.run(['git', 'ls-files', '--unmerged'])\n    return bool(stdout.strip())\n\n\ndef _has_unstaged_config(runner):\n    retcode, _, _ = runner.cmd_runner.run(\n        ('git', 'diff', '--exit-code', runner.config_file_path),\n        retcode=None,\n    )\n    # be explicit, other git errors don't mean it has an unstaged config.\n    return retcode == 1\n\n\ndef run(runner, args, environ=os.environ):\n    no_stash = args.no_stash or args.all_files or bool(args.files)\n\n    # Check if we have unresolved merge conflict files and fail fast.\n    if _has_unmerged_paths(runner):\n        logger.error('Unmerged files.  Resolve before committing.')\n        return 1\n    if bool(args.source) != bool(args.origin):\n        logger.error('Specify both --origin and --source.')\n        return 1\n    if _has_unstaged_config(runner) and not no_stash:\n        if args.allow_unstaged_config:\n            logger.warn(\n                'You have an unstaged config file and have specified the '\n                '--allow-unstaged-config option.\\n'\n                'Note that your config will be stashed before the config is '\n                'parsed unless --no-stash is specified.',\n            )\n        else:\n            logger.error(\n                'Your .pre-commit-config.yaml is unstaged.\\n'\n                '`git add .pre-commit-config.yaml` to fix this.\\n'\n                'Run pre-commit with --allow-unstaged-config to silence this.'\n            )\n            return 1\n\n    if no_stash:\n        ctx = noop_context()\n    else:\n        ctx = staged_files_only(runner.cmd_runner)\n\n    with ctx:\n        repo_hooks = list(get_repo_hooks(runner))\n\n        if args.hook:\n            repo_hooks = [\n                (repo, hook) for repo, hook in repo_hooks\n                if hook['id'] == args.hook\n            ]\n            if not repo_hooks:\n                output.write_line('No hook with id `{}`'.format(args.hook))\n                return 1\n\n        # Filter hooks for stages\n        repo_hooks = [\n            (repo, hook) for repo, hook in repo_hooks\n            if not hook['stages'] or args.hook_stage in hook['stages']\n        ]\n\n        return _run_hooks(repo_hooks, args, environ)\n", "path": "pre_commit/commands/run.py"}]}