We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
--show-diff-on-failure does not respect --color
I'm running `pre-commit` on gitlab, and I noticed that the diffs produced when `--show-diff-on-failure` is enabled do not respect `--color=always`.  

It looks like a pretty easy fix:   we just need to pass the `--color` setting to `git diff`.  It supports the same "auto", "always", "never" settings as `pre-commit`.  

`git diff --color` [documentation](https://git-scm.com/docs/git-diff#Documentation/git-diff.txt---colorltwhengt).

code in question: https://github.com/pre-commit/pre-commit/blob/master/pre_commit/commands/run.py#L227


--- 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 unicode_literals
2 
3 import logging
4 import os
5 import re
6 import subprocess
7 import sys
8 
9 from identify.identify import tags_from_path
10 
11 from pre_commit import color
12 from pre_commit import git
13 from pre_commit import output
14 from pre_commit.clientlib import load_config
15 from pre_commit.output import get_hook_message
16 from pre_commit.repository import all_hooks
17 from pre_commit.repository import install_hook_envs
18 from pre_commit.staged_files_only import staged_files_only
19 from pre_commit.util import cmd_output
20 from pre_commit.util import noop_context
21 
22 
23 logger = logging.getLogger('pre_commit')
24 
25 
26 def filter_by_include_exclude(names, include, exclude):
27     include_re, exclude_re = re.compile(include), re.compile(exclude)
28     return [
29         filename for filename in names
30         if include_re.search(filename)
31         if not exclude_re.search(filename)
32     ]
33 
34 
35 class Classifier(object):
36     def __init__(self, filenames):
37         self.filenames = [f for f in filenames if os.path.lexists(f)]
38         self._types_cache = {}
39 
40     def _types_for_file(self, filename):
41         try:
42             return self._types_cache[filename]
43         except KeyError:
44             ret = self._types_cache[filename] = tags_from_path(filename)
45             return ret
46 
47     def by_types(self, names, types, exclude_types):
48         types, exclude_types = frozenset(types), frozenset(exclude_types)
49         ret = []
50         for filename in names:
51             tags = self._types_for_file(filename)
52             if tags >= types and not tags & exclude_types:
53                 ret.append(filename)
54         return ret
55 
56     def filenames_for_hook(self, hook):
57         names = self.filenames
58         names = filter_by_include_exclude(names, hook.files, hook.exclude)
59         names = self.by_types(names, hook.types, hook.exclude_types)
60         return names
61 
62 
63 def _get_skips(environ):
64     skips = environ.get('SKIP', '')
65     return {skip.strip() for skip in skips.split(',') if skip.strip()}
66 
67 
68 def _hook_msg_start(hook, verbose):
69     return '{}{}'.format('[{}] '.format(hook.id) if verbose else '', hook.name)
70 
71 
72 SKIPPED = 'Skipped'
73 NO_FILES = '(no files to check)'
74 
75 
76 def _run_single_hook(classifier, hook, args, skips, cols):
77     filenames = classifier.filenames_for_hook(hook)
78 
79     if hook.language == 'pcre':
80         logger.warning(
81             '`{}` (from {}) uses the deprecated pcre language.\n'
82             'The pcre language is scheduled for removal in pre-commit 2.x.\n'
83             'The pygrep language is a more portable (and usually drop-in) '
84             'replacement.'.format(hook.id, hook.src),
85         )
86 
87     if hook.id in skips or hook.alias in skips:
88         output.write(
89             get_hook_message(
90                 _hook_msg_start(hook, args.verbose),
91                 end_msg=SKIPPED,
92                 end_color=color.YELLOW,
93                 use_color=args.color,
94                 cols=cols,
95             ),
96         )
97         return 0
98     elif not filenames and not hook.always_run:
99         output.write(
100             get_hook_message(
101                 _hook_msg_start(hook, args.verbose),
102                 postfix=NO_FILES,
103                 end_msg=SKIPPED,
104                 end_color=color.TURQUOISE,
105                 use_color=args.color,
106                 cols=cols,
107             ),
108         )
109         return 0
110 
111     # Print the hook and the dots first in case the hook takes hella long to
112     # run.
113     output.write(
114         get_hook_message(
115             _hook_msg_start(hook, args.verbose), end_len=6, cols=cols,
116         ),
117     )
118     sys.stdout.flush()
119 
120     diff_before = cmd_output(
121         'git', 'diff', '--no-ext-diff', retcode=None, encoding=None,
122     )
123     retcode, stdout, stderr = hook.run(
124         tuple(filenames) if hook.pass_filenames else (),
125     )
126     diff_after = cmd_output(
127         'git', 'diff', '--no-ext-diff', retcode=None, encoding=None,
128     )
129 
130     file_modifications = diff_before != diff_after
131 
132     # If the hook makes changes, fail the commit
133     if file_modifications:
134         retcode = 1
135 
136     if retcode:
137         retcode = 1
138         print_color = color.RED
139         pass_fail = 'Failed'
140     else:
141         retcode = 0
142         print_color = color.GREEN
143         pass_fail = 'Passed'
144 
145     output.write_line(color.format_color(pass_fail, print_color, args.color))
146 
147     if (
148             (stdout or stderr or file_modifications) and
149             (retcode or args.verbose or hook.verbose)
150     ):
151         output.write_line('hookid: {}\n'.format(hook.id))
152 
153         # Print a message if failing due to file modifications
154         if file_modifications:
155             output.write('Files were modified by this hook.')
156 
157             if stdout or stderr:
158                 output.write_line(' Additional output:')
159 
160             output.write_line()
161 
162         for out in (stdout, stderr):
163             assert type(out) is bytes, type(out)
164             if out.strip():
165                 output.write_line(out.strip(), logfile_name=hook.log_file)
166         output.write_line()
167 
168     return retcode
169 
170 
171 def _compute_cols(hooks, verbose):
172     """Compute the number of columns to display hook messages.  The widest
173     that will be displayed is in the no files skipped case:
174 
175         Hook name...(no files to check) Skipped
176 
177     or in the verbose case
178 
179         Hook name [hookid]...(no files to check) Skipped
180     """
181     if hooks:
182         name_len = max(len(_hook_msg_start(hook, verbose)) for hook in hooks)
183     else:
184         name_len = 0
185 
186     cols = name_len + 3 + len(NO_FILES) + 1 + len(SKIPPED)
187     return max(cols, 80)
188 
189 
190 def _all_filenames(args):
191     if args.origin and args.source:
192         return git.get_changed_files(args.origin, args.source)
193     elif args.hook_stage in {'prepare-commit-msg', 'commit-msg'}:
194         return (args.commit_msg_filename,)
195     elif args.files:
196         return args.files
197     elif args.all_files:
198         return git.get_all_files()
199     elif git.is_in_merge_conflict():
200         return git.get_conflicted_files()
201     else:
202         return git.get_staged_files()
203 
204 
205 def _run_hooks(config, hooks, args, environ):
206     """Actually run the hooks."""
207     skips = _get_skips(environ)
208     cols = _compute_cols(hooks, args.verbose)
209     filenames = _all_filenames(args)
210     filenames = filter_by_include_exclude(filenames, '', config['exclude'])
211     classifier = Classifier(filenames)
212     retval = 0
213     for hook in hooks:
214         retval |= _run_single_hook(classifier, hook, args, skips, cols)
215         if retval and config['fail_fast']:
216             break
217     if retval and args.show_diff_on_failure and git.has_diff():
218         if args.all_files:
219             output.write_line(
220                 'pre-commit hook(s) made changes.\n'
221                 'If you are seeing this message in CI, '
222                 'reproduce locally with: `pre-commit run --all-files`.\n'
223                 'To run `pre-commit` as part of git workflow, use '
224                 '`pre-commit install`.',
225             )
226         output.write_line('All changes made by hooks:')
227         subprocess.call(('git', '--no-pager', 'diff', '--no-ext-diff'))
228     return retval
229 
230 
231 def _has_unmerged_paths():
232     _, stdout, _ = cmd_output('git', 'ls-files', '--unmerged')
233     return bool(stdout.strip())
234 
235 
236 def _has_unstaged_config(config_file):
237     retcode, _, _ = cmd_output(
238         'git', 'diff', '--no-ext-diff', '--exit-code', config_file,
239         retcode=None,
240     )
241     # be explicit, other git errors don't mean it has an unstaged config.
242     return retcode == 1
243 
244 
245 def run(config_file, store, args, environ=os.environ):
246     no_stash = args.all_files or bool(args.files)
247 
248     # Check if we have unresolved merge conflict files and fail fast.
249     if _has_unmerged_paths():
250         logger.error('Unmerged files.  Resolve before committing.')
251         return 1
252     if bool(args.source) != bool(args.origin):
253         logger.error('Specify both --origin and --source.')
254         return 1
255     if _has_unstaged_config(config_file) and not no_stash:
256         logger.error(
257             'Your pre-commit configuration is unstaged.\n'
258             '`git add {}` to fix this.'.format(config_file),
259         )
260         return 1
261 
262     # Expose origin / source as environment variables for hooks to consume
263     if args.origin and args.source:
264         environ['PRE_COMMIT_ORIGIN'] = args.origin
265         environ['PRE_COMMIT_SOURCE'] = args.source
266 
267     if no_stash:
268         ctx = noop_context()
269     else:
270         ctx = staged_files_only(store.directory)
271 
272     with ctx:
273         config = load_config(config_file)
274         hooks = [
275             hook
276             for hook in all_hooks(config, store)
277             if not args.hook or hook.id == args.hook or hook.alias == args.hook
278             if args.hook_stage in hook.stages
279         ]
280 
281         if args.hook and not hooks:
282             output.write_line('No hook with id `{}`'.format(args.hook))
283             return 1
284 
285         install_hook_envs(hooks, store)
286 
287         return _run_hooks(config, hooks, args, environ)
288
```
--- 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
@@ -224,7 +224,13 @@
                 '`pre-commit install`.',
             )
         output.write_line('All changes made by hooks:')
-        subprocess.call(('git', '--no-pager', 'diff', '--no-ext-diff'))
+        # args.color is a boolean.
+        # See user_color function in color.py
+        subprocess.call((
+            'git', '--no-pager', 'diff', '--no-ext-diff',
+            '--color={}'.format({True: 'always', False: 'never'}[args.color]),
+        ))
+
     return retval

{"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@@ -224,7 +224,13 @@\n                 '`pre-commit install`.',\n             )\n         output.write_line('All changes made by hooks:')\n-        subprocess.call(('git', '--no-pager', 'diff', '--no-ext-diff'))\n+        # args.color is a boolean.\n+        # See user_color function in color.py\n+        subprocess.call((\n+            'git', '--no-pager', 'diff', '--no-ext-diff',\n+            '--color={}'.format({True: 'always', False: 'never'}[args.color]),\n+        ))\n+\n     return retval\n", "issue": "--show-diff-on-failure does not respect --color\nI'm running `pre-commit` on gitlab, and I noticed that the diffs produced when `--show-diff-on-failure` is enabled do not respect `--color=always`.  \r\n\r\nIt looks like a pretty easy fix:   we just need to pass the `--color` setting to `git diff`.  It supports the same \"auto\", \"always\", \"never\" settings as `pre-commit`.  \r\n\r\n`git diff --color` [documentation](https://git-scm.com/docs/git-diff#Documentation/git-diff.txt---colorltwhengt).\r\n\r\ncode in question: https://github.com/pre-commit/pre-commit/blob/master/pre_commit/commands/run.py#L227\r\n\n", "before_files": [{"content": "from __future__ import unicode_literals\n\nimport logging\nimport os\nimport re\nimport subprocess\nimport sys\n\nfrom identify.identify import tags_from_path\n\nfrom pre_commit import color\nfrom pre_commit import git\nfrom pre_commit import output\nfrom pre_commit.clientlib import load_config\nfrom pre_commit.output import get_hook_message\nfrom pre_commit.repository import all_hooks\nfrom pre_commit.repository import install_hook_envs\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 filter_by_include_exclude(names, include, exclude):\n    include_re, exclude_re = re.compile(include), re.compile(exclude)\n    return [\n        filename for filename in names\n        if include_re.search(filename)\n        if not exclude_re.search(filename)\n    ]\n\n\nclass Classifier(object):\n    def __init__(self, filenames):\n        self.filenames = [f for f in filenames if os.path.lexists(f)]\n        self._types_cache = {}\n\n    def _types_for_file(self, filename):\n        try:\n            return self._types_cache[filename]\n        except KeyError:\n            ret = self._types_cache[filename] = tags_from_path(filename)\n            return ret\n\n    def by_types(self, names, types, exclude_types):\n        types, exclude_types = frozenset(types), frozenset(exclude_types)\n        ret = []\n        for filename in names:\n            tags = self._types_for_file(filename)\n            if tags >= types and not tags & exclude_types:\n                ret.append(filename)\n        return ret\n\n    def filenames_for_hook(self, hook):\n        names = self.filenames\n        names = filter_by_include_exclude(names, hook.files, hook.exclude)\n        names = self.by_types(names, hook.types, hook.exclude_types)\n        return names\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('[{}] '.format(hook.id) if verbose else '', hook.name)\n\n\nSKIPPED = 'Skipped'\nNO_FILES = '(no files to check)'\n\n\ndef _run_single_hook(classifier, hook, args, skips, cols):\n    filenames = classifier.filenames_for_hook(hook)\n\n    if hook.language == 'pcre':\n        logger.warning(\n            '`{}` (from {}) uses the deprecated pcre language.\\n'\n            'The pcre language is scheduled for removal in pre-commit 2.x.\\n'\n            'The pygrep language is a more portable (and usually drop-in) '\n            'replacement.'.format(hook.id, hook.src),\n        )\n\n    if hook.id in skips or hook.alias in skips:\n        output.write(\n            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        )\n        return 0\n    elif not filenames and not hook.always_run:\n        output.write(\n            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        )\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(\n        get_hook_message(\n            _hook_msg_start(hook, args.verbose), end_len=6, cols=cols,\n        ),\n    )\n    sys.stdout.flush()\n\n    diff_before = cmd_output(\n        'git', 'diff', '--no-ext-diff', retcode=None, encoding=None,\n    )\n    retcode, stdout, stderr = hook.run(\n        tuple(filenames) if hook.pass_filenames else (),\n    )\n    diff_after = cmd_output(\n        'git', 'diff', '--no-ext-diff', retcode=None, encoding=None,\n    )\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 (\n            (stdout or stderr or file_modifications) and\n            (retcode or args.verbose or hook.verbose)\n    ):\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 _all_filenames(args):\n    if args.origin and args.source:\n        return git.get_changed_files(args.origin, args.source)\n    elif args.hook_stage in {'prepare-commit-msg', 'commit-msg'}:\n        return (args.commit_msg_filename,)\n    elif args.files:\n        return args.files\n    elif args.all_files:\n        return git.get_all_files()\n    elif git.is_in_merge_conflict():\n        return git.get_conflicted_files()\n    else:\n        return git.get_staged_files()\n\n\ndef _run_hooks(config, hooks, args, environ):\n    \"\"\"Actually run the hooks.\"\"\"\n    skips = _get_skips(environ)\n    cols = _compute_cols(hooks, args.verbose)\n    filenames = _all_filenames(args)\n    filenames = filter_by_include_exclude(filenames, '', config['exclude'])\n    classifier = Classifier(filenames)\n    retval = 0\n    for hook in hooks:\n        retval |= _run_single_hook(classifier, hook, args, skips, cols)\n        if retval and config['fail_fast']:\n            break\n    if retval and args.show_diff_on_failure and git.has_diff():\n        if args.all_files:\n            output.write_line(\n                'pre-commit hook(s) made changes.\\n'\n                'If you are seeing this message in CI, '\n                'reproduce locally with: `pre-commit run --all-files`.\\n'\n                'To run `pre-commit` as part of git workflow, use '\n                '`pre-commit install`.',\n            )\n        output.write_line('All changes made by hooks:')\n        subprocess.call(('git', '--no-pager', 'diff', '--no-ext-diff'))\n    return retval\n\n\ndef _has_unmerged_paths():\n    _, stdout, _ = cmd_output('git', 'ls-files', '--unmerged')\n    return bool(stdout.strip())\n\n\ndef _has_unstaged_config(config_file):\n    retcode, _, _ = cmd_output(\n        'git', 'diff', '--no-ext-diff', '--exit-code', config_file,\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(config_file, store, args, environ=os.environ):\n    no_stash = 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():\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(config_file) and not no_stash:\n        logger.error(\n            'Your pre-commit configuration is unstaged.\\n'\n            '`git add {}` to fix this.'.format(config_file),\n        )\n        return 1\n\n    # Expose origin / source as environment variables for hooks to consume\n    if args.origin and args.source:\n        environ['PRE_COMMIT_ORIGIN'] = args.origin\n        environ['PRE_COMMIT_SOURCE'] = args.source\n\n    if no_stash:\n        ctx = noop_context()\n    else:\n        ctx = staged_files_only(store.directory)\n\n    with ctx:\n        config = load_config(config_file)\n        hooks = [\n            hook\n            for hook in all_hooks(config, store)\n            if not args.hook or hook.id == args.hook or hook.alias == args.hook\n            if args.hook_stage in hook.stages\n        ]\n\n        if args.hook and not hooks:\n            output.write_line('No hook with id `{}`'.format(args.hook))\n            return 1\n\n        install_hook_envs(hooks, store)\n\n        return _run_hooks(config, hooks, args, environ)\n", "path": "pre_commit/commands/run.py"}], "after_files": [{"content": "from __future__ import unicode_literals\n\nimport logging\nimport os\nimport re\nimport subprocess\nimport sys\n\nfrom identify.identify import tags_from_path\n\nfrom pre_commit import color\nfrom pre_commit import git\nfrom pre_commit import output\nfrom pre_commit.clientlib import load_config\nfrom pre_commit.output import get_hook_message\nfrom pre_commit.repository import all_hooks\nfrom pre_commit.repository import install_hook_envs\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 filter_by_include_exclude(names, include, exclude):\n    include_re, exclude_re = re.compile(include), re.compile(exclude)\n    return [\n        filename for filename in names\n        if include_re.search(filename)\n        if not exclude_re.search(filename)\n    ]\n\n\nclass Classifier(object):\n    def __init__(self, filenames):\n        self.filenames = [f for f in filenames if os.path.lexists(f)]\n        self._types_cache = {}\n\n    def _types_for_file(self, filename):\n        try:\n            return self._types_cache[filename]\n        except KeyError:\n            ret = self._types_cache[filename] = tags_from_path(filename)\n            return ret\n\n    def by_types(self, names, types, exclude_types):\n        types, exclude_types = frozenset(types), frozenset(exclude_types)\n        ret = []\n        for filename in names:\n            tags = self._types_for_file(filename)\n            if tags >= types and not tags & exclude_types:\n                ret.append(filename)\n        return ret\n\n    def filenames_for_hook(self, hook):\n        names = self.filenames\n        names = filter_by_include_exclude(names, hook.files, hook.exclude)\n        names = self.by_types(names, hook.types, hook.exclude_types)\n        return names\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('[{}] '.format(hook.id) if verbose else '', hook.name)\n\n\nSKIPPED = 'Skipped'\nNO_FILES = '(no files to check)'\n\n\ndef _run_single_hook(classifier, hook, args, skips, cols):\n    filenames = classifier.filenames_for_hook(hook)\n\n    if hook.language == 'pcre':\n        logger.warning(\n            '`{}` (from {}) uses the deprecated pcre language.\\n'\n            'The pcre language is scheduled for removal in pre-commit 2.x.\\n'\n            'The pygrep language is a more portable (and usually drop-in) '\n            'replacement.'.format(hook.id, hook.src),\n        )\n\n    if hook.id in skips or hook.alias in skips:\n        output.write(\n            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        )\n        return 0\n    elif not filenames and not hook.always_run:\n        output.write(\n            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        )\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(\n        get_hook_message(\n            _hook_msg_start(hook, args.verbose), end_len=6, cols=cols,\n        ),\n    )\n    sys.stdout.flush()\n\n    diff_before = cmd_output(\n        'git', 'diff', '--no-ext-diff', retcode=None, encoding=None,\n    )\n    retcode, stdout, stderr = hook.run(\n        tuple(filenames) if hook.pass_filenames else (),\n    )\n    diff_after = cmd_output(\n        'git', 'diff', '--no-ext-diff', retcode=None, encoding=None,\n    )\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 (\n            (stdout or stderr or file_modifications) and\n            (retcode or args.verbose or hook.verbose)\n    ):\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 _all_filenames(args):\n    if args.origin and args.source:\n        return git.get_changed_files(args.origin, args.source)\n    elif args.hook_stage in {'prepare-commit-msg', 'commit-msg'}:\n        return (args.commit_msg_filename,)\n    elif args.files:\n        return args.files\n    elif args.all_files:\n        return git.get_all_files()\n    elif git.is_in_merge_conflict():\n        return git.get_conflicted_files()\n    else:\n        return git.get_staged_files()\n\n\ndef _run_hooks(config, hooks, args, environ):\n    \"\"\"Actually run the hooks.\"\"\"\n    skips = _get_skips(environ)\n    cols = _compute_cols(hooks, args.verbose)\n    filenames = _all_filenames(args)\n    filenames = filter_by_include_exclude(filenames, '', config['exclude'])\n    classifier = Classifier(filenames)\n    retval = 0\n    for hook in hooks:\n        retval |= _run_single_hook(classifier, hook, args, skips, cols)\n        if retval and config['fail_fast']:\n            break\n    if retval and args.show_diff_on_failure and git.has_diff():\n        if args.all_files:\n            output.write_line(\n                'pre-commit hook(s) made changes.\\n'\n                'If you are seeing this message in CI, '\n                'reproduce locally with: `pre-commit run --all-files`.\\n'\n                'To run `pre-commit` as part of git workflow, use '\n                '`pre-commit install`.',\n            )\n        output.write_line('All changes made by hooks:')\n        # args.color is a boolean.\n        # See user_color function in color.py\n        subprocess.call((\n            'git', '--no-pager', 'diff', '--no-ext-diff',\n            '--color={}'.format({True: 'always', False: 'never'}[args.color]),\n        ))\n\n    return retval\n\n\ndef _has_unmerged_paths():\n    _, stdout, _ = cmd_output('git', 'ls-files', '--unmerged')\n    return bool(stdout.strip())\n\n\ndef _has_unstaged_config(config_file):\n    retcode, _, _ = cmd_output(\n        'git', 'diff', '--no-ext-diff', '--exit-code', config_file,\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(config_file, store, args, environ=os.environ):\n    no_stash = 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():\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(config_file) and not no_stash:\n        logger.error(\n            'Your pre-commit configuration is unstaged.\\n'\n            '`git add {}` to fix this.'.format(config_file),\n        )\n        return 1\n\n    # Expose origin / source as environment variables for hooks to consume\n    if args.origin and args.source:\n        environ['PRE_COMMIT_ORIGIN'] = args.origin\n        environ['PRE_COMMIT_SOURCE'] = args.source\n\n    if no_stash:\n        ctx = noop_context()\n    else:\n        ctx = staged_files_only(store.directory)\n\n    with ctx:\n        config = load_config(config_file)\n        hooks = [\n            hook\n            for hook in all_hooks(config, store)\n            if not args.hook or hook.id == args.hook or hook.alias == args.hook\n            if args.hook_stage in hook.stages\n        ]\n\n        if args.hook and not hooks:\n            output.write_line('No hook with id `{}`'.format(args.hook))\n            return 1\n\n        install_hook_envs(hooks, store)\n\n        return _run_hooks(config, hooks, args, environ)\n", "path": "pre_commit/commands/run.py"}]}