We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Add way to temporarily/permanently disable hooks.
[overcommit](https://github.com/causes/overcommit) uses environment variables to do temporary skipping...

For instance:

`SKIP=foo git commit` will skip the `foo` hook

Whereas I've used a more-permanent switching with `git config hooks.foo false` in the past.

Considering both approaches, I think overcommit does this quite elegantly while focusing on only _temporarily_ disabling hooks.


--- 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.py`
Content:
```
1 from __future__ import print_function
2 
3 import logging
4 import os
5 import pkg_resources
6 import shutil
7 import stat
8 import subprocess
9 import sys
10 from asottile.ordereddict import OrderedDict
11 from asottile.yaml import ordered_dump
12 from asottile.yaml import ordered_load
13 from plumbum import local
14 
15 import pre_commit.constants as C
16 from pre_commit import git
17 from pre_commit import color
18 from pre_commit.clientlib.validate_config import CONFIG_JSON_SCHEMA
19 from pre_commit.clientlib.validate_config import load_config
20 from pre_commit.jsonschema_extensions import remove_defaults
21 from pre_commit.logging_handler import LoggingHandler
22 from pre_commit.repository import Repository
23 from pre_commit.staged_files_only import staged_files_only
24 from pre_commit.util import noop_context
25 
26 
27 logger = logging.getLogger('pre_commit')
28 
29 COLS = int(subprocess.Popen(['tput', 'cols'], stdout=subprocess.PIPE).communicate()[0])
30 
31 PASS_FAIL_LENGTH = 6
32 
33 
34 def install(runner):
35     """Install the pre-commit hooks."""
36     pre_commit_file = pkg_resources.resource_filename('pre_commit', 'resources/pre-commit.sh')
37     with open(runner.pre_commit_path, 'w') as pre_commit_file_obj:
38         pre_commit_file_obj.write(open(pre_commit_file).read())
39 
40     original_mode = os.stat(runner.pre_commit_path).st_mode
41     os.chmod(
42         runner.pre_commit_path,
43         original_mode | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH,
44     )
45 
46     print('pre-commit installed at {0}'.format(runner.pre_commit_path))
47 
48     return 0
49 
50 
51 def uninstall(runner):
52     """Uninstall the pre-commit hooks."""
53     if os.path.exists(runner.pre_commit_path):
54         os.remove(runner.pre_commit_path)
55         print('pre-commit uninstalled')
56     return 0
57 
58 
59 class RepositoryCannotBeUpdatedError(RuntimeError):
60     pass
61 
62 
63 def _update_repository(repo_config):
64     """Updates a repository to the tip of `master`.  If the repository cannot
65     be updated because a hook that is configured does not exist in `master`,
66     this raises a RepositoryCannotBeUpdatedError
67 
68     Args:
69         repo_config - A config for a repository
70     """
71     repo = Repository(repo_config)
72 
73     with repo.in_checkout():
74         local['git']['fetch']()
75         head_sha = local['git']['rev-parse', 'origin/master']().strip()
76 
77     # Don't bother trying to update if our sha is the same
78     if head_sha == repo_config['sha']:
79         return repo_config
80 
81     # Construct a new config with the head sha
82     new_config = OrderedDict(repo_config)
83     new_config['sha'] = head_sha
84     new_repo = Repository(new_config)
85 
86     # See if any of our hooks were deleted with the new commits
87     hooks = set(repo.hooks.keys())
88     hooks_missing = hooks - (hooks & set(new_repo.manifest.keys()))
89     if hooks_missing:
90         raise RepositoryCannotBeUpdatedError(
91             'Cannot update because the tip of master is missing these hooks:\n'
92             '{0}'.format(', '.join(sorted(hooks_missing)))
93         )
94 
95     return remove_defaults([new_config], CONFIG_JSON_SCHEMA)[0]
96 
97 
98 def autoupdate(runner):
99     """Auto-update the pre-commit config to the latest versions of repos."""
100     retv = 0
101     output_configs = []
102     changed = False
103 
104     input_configs = load_config(
105         runner.config_file_path,
106         load_strategy=ordered_load,
107     )
108 
109     for repo_config in input_configs:
110         print('Updating {0}...'.format(repo_config['repo']), end='')
111         try:
112             new_repo_config = _update_repository(repo_config)
113         except RepositoryCannotBeUpdatedError as error:
114             print(error.args[0])
115             output_configs.append(repo_config)
116             retv = 1
117             continue
118 
119         if new_repo_config['sha'] != repo_config['sha']:
120             changed = True
121             print(
122                 'updating {0} -> {1}.'.format(
123                     repo_config['sha'], new_repo_config['sha'],
124                 )
125             )
126             output_configs.append(new_repo_config)
127         else:
128             print('already up to date.')
129             output_configs.append(repo_config)
130 
131     if changed:
132         with open(runner.config_file_path, 'w') as config_file:
133             config_file.write(
134                 ordered_dump(output_configs, **C.YAML_DUMP_KWARGS)
135             )
136 
137     return retv
138 
139 
140 def clean(runner):
141     if os.path.exists(runner.hooks_workspace_path):
142         shutil.rmtree(runner.hooks_workspace_path)
143         print('Cleaned {0}.'.format(runner.hooks_workspace_path))
144     return 0
145 
146 
147 def _run_single_hook(runner, repository, hook_id, args, write):
148     if args.all_files:
149         get_filenames = git.get_all_files_matching
150     elif git.is_in_merge_conflict():
151         get_filenames = git.get_conflicted_files_matching
152     else:
153         get_filenames = git.get_staged_files_matching
154 
155     hook = repository.hooks[hook_id]
156 
157     filenames = get_filenames(hook['files'], hook['exclude'])
158     if not filenames:
159         no_files_msg = '(no files to check) '
160         skipped_msg = 'Skipped'
161         write(
162             '{0}{1}{2}{3}\n'.format(
163                 hook['name'],
164                 '.' * (
165                     COLS -
166                     len(hook['name']) -
167                     len(no_files_msg) -
168                     len(skipped_msg) -
169                     6
170                 ),
171                 no_files_msg,
172                 color.format_color(skipped_msg, color.TURQUOISE, args.color),
173             )
174         )
175         return 0
176 
177     # Print the hook and the dots first in case the hook takes hella long to
178     # run.
179     write(
180         '{0}{1}'.format(
181             hook['name'],
182             '.' * (COLS - len(hook['name']) - PASS_FAIL_LENGTH - 6),
183         ),
184     )
185     sys.stdout.flush()
186 
187     retcode, stdout, stderr = repository.run_hook(
188         runner.cmd_runner,
189         hook_id,
190         filenames,
191     )
192 
193     if retcode != repository.hooks[hook_id]['expected_return_value']:
194         retcode = 1
195         print_color = color.RED
196         pass_fail = 'Failed'
197     else:
198         retcode = 0
199         print_color = color.GREEN
200         pass_fail = 'Passed'
201 
202     write(color.format_color(pass_fail, print_color, args.color) + '\n')
203 
204     if (stdout or stderr) and (retcode or args.verbose):
205         write('\n')
206         for output in (stdout, stderr):
207             if output.strip():
208                 write(output.strip() + '\n')
209         write('\n')
210 
211     return retcode
212 
213 
214 def _run_hooks(runner, args, write):
215     """Actually run the hooks."""
216     retval = 0
217 
218     for repo in runner.repositories:
219         for hook_id in repo.hooks:
220             retval |= _run_single_hook(runner, repo, hook_id, args, write=write)
221 
222     return retval
223 
224 
225 def _run_hook(runner, hook_id, args, write):
226     for repo in runner.repositories:
227         if hook_id in repo.hooks:
228             return _run_single_hook(runner, repo, hook_id, args, write=write)
229     else:
230         write('No hook with id `{0}`\n'.format(hook_id))
231         return 1
232 
233 
234 def _has_unmerged_paths(runner):
235     _, stdout, _ = runner.cmd_runner.run(['git', 'ls-files', '--unmerged'])
236     return bool(stdout.strip())
237 
238 
239 def run(runner, args, write=sys.stdout.write):
240     # Set up our logging handler
241     logger.addHandler(LoggingHandler(args.color, write=write))
242     logger.setLevel(logging.INFO)
243 
244     # Check if we have unresolved merge conflict files and fail fast.
245     if _has_unmerged_paths(runner):
246         logger.error('Unmerged files.  Resolve before committing.')
247         return 1
248 
249     if args.no_stash or args.all_files:
250         ctx = noop_context()
251     else:
252         ctx = staged_files_only(runner.cmd_runner)
253 
254     with ctx:
255         if args.hook:
256             return _run_hook(runner, args.hook, args, write=write)
257         else:
258             return _run_hooks(runner, args, write=write)
259
```
--- 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.py b/pre_commit/commands.py
--- a/pre_commit/commands.py
+++ b/pre_commit/commands.py
@@ -144,7 +144,42 @@
     return 0
 
 
-def _run_single_hook(runner, repository, hook_id, args, write):
+def _get_skips(environ):
+    skips = environ.get('SKIP', '')
+    return set(skip.strip() for skip in skips.split(',') if skip.strip())
+
+
+def _print_no_files_skipped(hook, write, args):
+    no_files_msg = '(no files to check) '
+    skipped_msg = 'Skipped'
+    write(
+        '{0}{1}{2}{3}\n'.format(
+            hook['name'],
+            '.' * (
+                COLS -
+                len(hook['name']) -
+                len(no_files_msg) -
+                len(skipped_msg) -
+                6
+            ),
+            no_files_msg,
+            color.format_color(skipped_msg, color.TURQUOISE, args.color),
+        )
+    )
+
+
+def _print_user_skipped(hook, write, args):
+    skipped_msg = 'Skipped'
+    write(
+        '{0}{1}{2}\n'.format(
+            hook['name'],
+            '.' * (COLS - len(hook['name']) - len(skipped_msg) - 6),
+            color.format_color(skipped_msg, color.YELLOW, args.color),
+        ),
+    )
+
+
+def _run_single_hook(runner, repository, hook_id, args, write, skips=set()):
     if args.all_files:
         get_filenames = git.get_all_files_matching
     elif git.is_in_merge_conflict():
@@ -155,23 +190,11 @@
     hook = repository.hooks[hook_id]
 
     filenames = get_filenames(hook['files'], hook['exclude'])
-    if not filenames:
-        no_files_msg = '(no files to check) '
-        skipped_msg = 'Skipped'
-        write(
-            '{0}{1}{2}{3}\n'.format(
-                hook['name'],
-                '.' * (
-                    COLS -
-                    len(hook['name']) -
-                    len(no_files_msg) -
-                    len(skipped_msg) -
-                    6
-                ),
-                no_files_msg,
-                color.format_color(skipped_msg, color.TURQUOISE, args.color),
-            )
-        )
+    if hook_id in skips:
+        _print_user_skipped(hook, write, args)
+        return 0
+    elif not filenames:
+        _print_no_files_skipped(hook, write, args)
         return 0
 
     # Print the hook and the dots first in case the hook takes hella long to
@@ -211,18 +234,23 @@
     return retcode
 
 
-def _run_hooks(runner, args, write):
+def _run_hooks(runner, args, write, environ):
     """Actually run the hooks."""
     retval = 0
 
+    skips = _get_skips(environ)
+
     for repo in runner.repositories:
         for hook_id in repo.hooks:
-            retval |= _run_single_hook(runner, repo, hook_id, args, write=write)
+            retval |= _run_single_hook(
+                runner, repo, hook_id, args, write, skips=skips,
+            )
 
     return retval
 
 
-def _run_hook(runner, hook_id, args, write):
+def _run_hook(runner, args, write):
+    hook_id = args.hook
     for repo in runner.repositories:
         if hook_id in repo.hooks:
             return _run_single_hook(runner, repo, hook_id, args, write=write)
@@ -236,7 +264,7 @@
     return bool(stdout.strip())
 
 
-def run(runner, args, write=sys.stdout.write):
+def run(runner, args, write=sys.stdout.write, environ=os.environ):
     # Set up our logging handler
     logger.addHandler(LoggingHandler(args.color, write=write))
     logger.setLevel(logging.INFO)
@@ -253,6 +281,6 @@
 
     with ctx:
         if args.hook:
-            return _run_hook(runner, args.hook, args, write=write)
+            return _run_hook(runner, args, write=write)
         else:
-            return _run_hooks(runner, args, write=write)
+            return _run_hooks(runner, args, write=write, environ=environ)

{"golden_diff": "diff --git a/pre_commit/commands.py b/pre_commit/commands.py\n--- a/pre_commit/commands.py\n+++ b/pre_commit/commands.py\n@@ -144,7 +144,42 @@\n     return 0\n \n \n-def _run_single_hook(runner, repository, hook_id, args, write):\n+def _get_skips(environ):\n+    skips = environ.get('SKIP', '')\n+    return set(skip.strip() for skip in skips.split(',') if skip.strip())\n+\n+\n+def _print_no_files_skipped(hook, write, args):\n+    no_files_msg = '(no files to check) '\n+    skipped_msg = 'Skipped'\n+    write(\n+        '{0}{1}{2}{3}\\n'.format(\n+            hook['name'],\n+            '.' * (\n+                COLS -\n+                len(hook['name']) -\n+                len(no_files_msg) -\n+                len(skipped_msg) -\n+                6\n+            ),\n+            no_files_msg,\n+            color.format_color(skipped_msg, color.TURQUOISE, args.color),\n+        )\n+    )\n+\n+\n+def _print_user_skipped(hook, write, args):\n+    skipped_msg = 'Skipped'\n+    write(\n+        '{0}{1}{2}\\n'.format(\n+            hook['name'],\n+            '.' * (COLS - len(hook['name']) - len(skipped_msg) - 6),\n+            color.format_color(skipped_msg, color.YELLOW, args.color),\n+        ),\n+    )\n+\n+\n+def _run_single_hook(runner, repository, hook_id, args, write, skips=set()):\n     if args.all_files:\n         get_filenames = git.get_all_files_matching\n     elif git.is_in_merge_conflict():\n@@ -155,23 +190,11 @@\n     hook = repository.hooks[hook_id]\n \n     filenames = get_filenames(hook['files'], hook['exclude'])\n-    if not filenames:\n-        no_files_msg = '(no files to check) '\n-        skipped_msg = 'Skipped'\n-        write(\n-            '{0}{1}{2}{3}\\n'.format(\n-                hook['name'],\n-                '.' * (\n-                    COLS -\n-                    len(hook['name']) -\n-                    len(no_files_msg) -\n-                    len(skipped_msg) -\n-                    6\n-                ),\n-                no_files_msg,\n-                color.format_color(skipped_msg, color.TURQUOISE, args.color),\n-            )\n-        )\n+    if hook_id in skips:\n+        _print_user_skipped(hook, write, args)\n+        return 0\n+    elif not filenames:\n+        _print_no_files_skipped(hook, write, args)\n         return 0\n \n     # Print the hook and the dots first in case the hook takes hella long to\n@@ -211,18 +234,23 @@\n     return retcode\n \n \n-def _run_hooks(runner, args, write):\n+def _run_hooks(runner, args, write, environ):\n     \"\"\"Actually run the hooks.\"\"\"\n     retval = 0\n \n+    skips = _get_skips(environ)\n+\n     for repo in runner.repositories:\n         for hook_id in repo.hooks:\n-            retval |= _run_single_hook(runner, repo, hook_id, args, write=write)\n+            retval |= _run_single_hook(\n+                runner, repo, hook_id, args, write, skips=skips,\n+            )\n \n     return retval\n \n \n-def _run_hook(runner, hook_id, args, write):\n+def _run_hook(runner, args, write):\n+    hook_id = args.hook\n     for repo in runner.repositories:\n         if hook_id in repo.hooks:\n             return _run_single_hook(runner, repo, hook_id, args, write=write)\n@@ -236,7 +264,7 @@\n     return bool(stdout.strip())\n \n \n-def run(runner, args, write=sys.stdout.write):\n+def run(runner, args, write=sys.stdout.write, environ=os.environ):\n     # Set up our logging handler\n     logger.addHandler(LoggingHandler(args.color, write=write))\n     logger.setLevel(logging.INFO)\n@@ -253,6 +281,6 @@\n \n     with ctx:\n         if args.hook:\n-            return _run_hook(runner, args.hook, args, write=write)\n+            return _run_hook(runner, args, write=write)\n         else:\n-            return _run_hooks(runner, args, write=write)\n+            return _run_hooks(runner, args, write=write, environ=environ)\n", "issue": "Add way to temporarily/permanently disable hooks.\n[overcommit](https://github.com/causes/overcommit) uses environment variables to do temporary skipping...\n\nFor instance:\n\n`SKIP=foo git commit` will skip the `foo` hook\n\nWhereas I've used a more-permanent switching with `git config hooks.foo false` in the past.\n\nConsidering both approaches, I think overcommit does this quite elegantly while focusing on only _temporarily_ disabling hooks.\n\n", "before_files": [{"content": "from __future__ import print_function\n\nimport logging\nimport os\nimport pkg_resources\nimport shutil\nimport stat\nimport subprocess\nimport sys\nfrom asottile.ordereddict import OrderedDict\nfrom asottile.yaml import ordered_dump\nfrom asottile.yaml import ordered_load\nfrom plumbum import local\n\nimport pre_commit.constants as C\nfrom pre_commit import git\nfrom pre_commit import color\nfrom pre_commit.clientlib.validate_config import CONFIG_JSON_SCHEMA\nfrom pre_commit.clientlib.validate_config import load_config\nfrom pre_commit.jsonschema_extensions import remove_defaults\nfrom pre_commit.logging_handler import LoggingHandler\nfrom pre_commit.repository import Repository\nfrom pre_commit.staged_files_only import staged_files_only\nfrom pre_commit.util import noop_context\n\n\nlogger = logging.getLogger('pre_commit')\n\nCOLS = int(subprocess.Popen(['tput', 'cols'], stdout=subprocess.PIPE).communicate()[0])\n\nPASS_FAIL_LENGTH = 6\n\n\ndef install(runner):\n    \"\"\"Install the pre-commit hooks.\"\"\"\n    pre_commit_file = pkg_resources.resource_filename('pre_commit', 'resources/pre-commit.sh')\n    with open(runner.pre_commit_path, 'w') as pre_commit_file_obj:\n        pre_commit_file_obj.write(open(pre_commit_file).read())\n\n    original_mode = os.stat(runner.pre_commit_path).st_mode\n    os.chmod(\n        runner.pre_commit_path,\n        original_mode | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH,\n    )\n\n    print('pre-commit installed at {0}'.format(runner.pre_commit_path))\n\n    return 0\n\n\ndef uninstall(runner):\n    \"\"\"Uninstall the pre-commit hooks.\"\"\"\n    if os.path.exists(runner.pre_commit_path):\n        os.remove(runner.pre_commit_path)\n        print('pre-commit uninstalled')\n    return 0\n\n\nclass RepositoryCannotBeUpdatedError(RuntimeError):\n    pass\n\n\ndef _update_repository(repo_config):\n    \"\"\"Updates a repository to the tip of `master`.  If the repository cannot\n    be updated because a hook that is configured does not exist in `master`,\n    this raises a RepositoryCannotBeUpdatedError\n\n    Args:\n        repo_config - A config for a repository\n    \"\"\"\n    repo = Repository(repo_config)\n\n    with repo.in_checkout():\n        local['git']['fetch']()\n        head_sha = local['git']['rev-parse', 'origin/master']().strip()\n\n    # Don't bother trying to update if our sha is the same\n    if head_sha == repo_config['sha']:\n        return repo_config\n\n    # Construct a new config with the head sha\n    new_config = OrderedDict(repo_config)\n    new_config['sha'] = head_sha\n    new_repo = Repository(new_config)\n\n    # See if any of our hooks were deleted with the new commits\n    hooks = set(repo.hooks.keys())\n    hooks_missing = hooks - (hooks & set(new_repo.manifest.keys()))\n    if hooks_missing:\n        raise RepositoryCannotBeUpdatedError(\n            'Cannot update because the tip of master is missing these hooks:\\n'\n            '{0}'.format(', '.join(sorted(hooks_missing)))\n        )\n\n    return remove_defaults([new_config], CONFIG_JSON_SCHEMA)[0]\n\n\ndef autoupdate(runner):\n    \"\"\"Auto-update the pre-commit config to the latest versions of repos.\"\"\"\n    retv = 0\n    output_configs = []\n    changed = False\n\n    input_configs = load_config(\n        runner.config_file_path,\n        load_strategy=ordered_load,\n    )\n\n    for repo_config in input_configs:\n        print('Updating {0}...'.format(repo_config['repo']), end='')\n        try:\n            new_repo_config = _update_repository(repo_config)\n        except RepositoryCannotBeUpdatedError as error:\n            print(error.args[0])\n            output_configs.append(repo_config)\n            retv = 1\n            continue\n\n        if new_repo_config['sha'] != repo_config['sha']:\n            changed = True\n            print(\n                'updating {0} -> {1}.'.format(\n                    repo_config['sha'], new_repo_config['sha'],\n                )\n            )\n            output_configs.append(new_repo_config)\n        else:\n            print('already up to date.')\n            output_configs.append(repo_config)\n\n    if changed:\n        with open(runner.config_file_path, 'w') as config_file:\n            config_file.write(\n                ordered_dump(output_configs, **C.YAML_DUMP_KWARGS)\n            )\n\n    return retv\n\n\ndef clean(runner):\n    if os.path.exists(runner.hooks_workspace_path):\n        shutil.rmtree(runner.hooks_workspace_path)\n        print('Cleaned {0}.'.format(runner.hooks_workspace_path))\n    return 0\n\n\ndef _run_single_hook(runner, repository, hook_id, args, write):\n    if args.all_files:\n        get_filenames = git.get_all_files_matching\n    elif git.is_in_merge_conflict():\n        get_filenames = git.get_conflicted_files_matching\n    else:\n        get_filenames = git.get_staged_files_matching\n\n    hook = repository.hooks[hook_id]\n\n    filenames = get_filenames(hook['files'], hook['exclude'])\n    if not filenames:\n        no_files_msg = '(no files to check) '\n        skipped_msg = 'Skipped'\n        write(\n            '{0}{1}{2}{3}\\n'.format(\n                hook['name'],\n                '.' * (\n                    COLS -\n                    len(hook['name']) -\n                    len(no_files_msg) -\n                    len(skipped_msg) -\n                    6\n                ),\n                no_files_msg,\n                color.format_color(skipped_msg, color.TURQUOISE, args.color),\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    write(\n        '{0}{1}'.format(\n            hook['name'],\n            '.' * (COLS - len(hook['name']) - PASS_FAIL_LENGTH - 6),\n        ),\n    )\n    sys.stdout.flush()\n\n    retcode, stdout, stderr = repository.run_hook(\n        runner.cmd_runner,\n        hook_id,\n        filenames,\n    )\n\n    if retcode != repository.hooks[hook_id]['expected_return_value']:\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    write(color.format_color(pass_fail, print_color, args.color) + '\\n')\n\n    if (stdout or stderr) and (retcode or args.verbose):\n        write('\\n')\n        for output in (stdout, stderr):\n            if output.strip():\n                write(output.strip() + '\\n')\n        write('\\n')\n\n    return retcode\n\n\ndef _run_hooks(runner, args, write):\n    \"\"\"Actually run the hooks.\"\"\"\n    retval = 0\n\n    for repo in runner.repositories:\n        for hook_id in repo.hooks:\n            retval |= _run_single_hook(runner, repo, hook_id, args, write=write)\n\n    return retval\n\n\ndef _run_hook(runner, hook_id, args, write):\n    for repo in runner.repositories:\n        if hook_id in repo.hooks:\n            return _run_single_hook(runner, repo, hook_id, args, write=write)\n    else:\n        write('No hook with id `{0}`\\n'.format(hook_id))\n        return 1\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 run(runner, args, write=sys.stdout.write):\n    # Set up our logging handler\n    logger.addHandler(LoggingHandler(args.color, write=write))\n    logger.setLevel(logging.INFO)\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\n    if args.no_stash or args.all_files:\n        ctx = noop_context()\n    else:\n        ctx = staged_files_only(runner.cmd_runner)\n\n    with ctx:\n        if args.hook:\n            return _run_hook(runner, args.hook, args, write=write)\n        else:\n            return _run_hooks(runner, args, write=write)\n", "path": "pre_commit/commands.py"}], "after_files": [{"content": "from __future__ import print_function\n\nimport logging\nimport os\nimport pkg_resources\nimport shutil\nimport stat\nimport subprocess\nimport sys\nfrom asottile.ordereddict import OrderedDict\nfrom asottile.yaml import ordered_dump\nfrom asottile.yaml import ordered_load\nfrom plumbum import local\n\nimport pre_commit.constants as C\nfrom pre_commit import git\nfrom pre_commit import color\nfrom pre_commit.clientlib.validate_config import CONFIG_JSON_SCHEMA\nfrom pre_commit.clientlib.validate_config import load_config\nfrom pre_commit.jsonschema_extensions import remove_defaults\nfrom pre_commit.logging_handler import LoggingHandler\nfrom pre_commit.repository import Repository\nfrom pre_commit.staged_files_only import staged_files_only\nfrom pre_commit.util import noop_context\n\n\nlogger = logging.getLogger('pre_commit')\n\nCOLS = int(subprocess.Popen(['tput', 'cols'], stdout=subprocess.PIPE).communicate()[0])\n\nPASS_FAIL_LENGTH = 6\n\n\ndef install(runner):\n    \"\"\"Install the pre-commit hooks.\"\"\"\n    pre_commit_file = pkg_resources.resource_filename('pre_commit', 'resources/pre-commit.sh')\n    with open(runner.pre_commit_path, 'w') as pre_commit_file_obj:\n        pre_commit_file_obj.write(open(pre_commit_file).read())\n\n    original_mode = os.stat(runner.pre_commit_path).st_mode\n    os.chmod(\n        runner.pre_commit_path,\n        original_mode | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH,\n    )\n\n    print('pre-commit installed at {0}'.format(runner.pre_commit_path))\n\n    return 0\n\n\ndef uninstall(runner):\n    \"\"\"Uninstall the pre-commit hooks.\"\"\"\n    if os.path.exists(runner.pre_commit_path):\n        os.remove(runner.pre_commit_path)\n        print('pre-commit uninstalled')\n    return 0\n\n\nclass RepositoryCannotBeUpdatedError(RuntimeError):\n    pass\n\n\ndef _update_repository(repo_config):\n    \"\"\"Updates a repository to the tip of `master`.  If the repository cannot\n    be updated because a hook that is configured does not exist in `master`,\n    this raises a RepositoryCannotBeUpdatedError\n\n    Args:\n        repo_config - A config for a repository\n    \"\"\"\n    repo = Repository(repo_config)\n\n    with repo.in_checkout():\n        local['git']['fetch']()\n        head_sha = local['git']['rev-parse', 'origin/master']().strip()\n\n    # Don't bother trying to update if our sha is the same\n    if head_sha == repo_config['sha']:\n        return repo_config\n\n    # Construct a new config with the head sha\n    new_config = OrderedDict(repo_config)\n    new_config['sha'] = head_sha\n    new_repo = Repository(new_config)\n\n    # See if any of our hooks were deleted with the new commits\n    hooks = set(repo.hooks.keys())\n    hooks_missing = hooks - (hooks & set(new_repo.manifest.keys()))\n    if hooks_missing:\n        raise RepositoryCannotBeUpdatedError(\n            'Cannot update because the tip of master is missing these hooks:\\n'\n            '{0}'.format(', '.join(sorted(hooks_missing)))\n        )\n\n    return remove_defaults([new_config], CONFIG_JSON_SCHEMA)[0]\n\n\ndef autoupdate(runner):\n    \"\"\"Auto-update the pre-commit config to the latest versions of repos.\"\"\"\n    retv = 0\n    output_configs = []\n    changed = False\n\n    input_configs = load_config(\n        runner.config_file_path,\n        load_strategy=ordered_load,\n    )\n\n    for repo_config in input_configs:\n        print('Updating {0}...'.format(repo_config['repo']), end='')\n        try:\n            new_repo_config = _update_repository(repo_config)\n        except RepositoryCannotBeUpdatedError as error:\n            print(error.args[0])\n            output_configs.append(repo_config)\n            retv = 1\n            continue\n\n        if new_repo_config['sha'] != repo_config['sha']:\n            changed = True\n            print(\n                'updating {0} -> {1}.'.format(\n                    repo_config['sha'], new_repo_config['sha'],\n                )\n            )\n            output_configs.append(new_repo_config)\n        else:\n            print('already up to date.')\n            output_configs.append(repo_config)\n\n    if changed:\n        with open(runner.config_file_path, 'w') as config_file:\n            config_file.write(\n                ordered_dump(output_configs, **C.YAML_DUMP_KWARGS)\n            )\n\n    return retv\n\n\ndef clean(runner):\n    if os.path.exists(runner.hooks_workspace_path):\n        shutil.rmtree(runner.hooks_workspace_path)\n        print('Cleaned {0}.'.format(runner.hooks_workspace_path))\n    return 0\n\n\ndef _get_skips(environ):\n    skips = environ.get('SKIP', '')\n    return set(skip.strip() for skip in skips.split(',') if skip.strip())\n\n\ndef _print_no_files_skipped(hook, write, args):\n    no_files_msg = '(no files to check) '\n    skipped_msg = 'Skipped'\n    write(\n        '{0}{1}{2}{3}\\n'.format(\n            hook['name'],\n            '.' * (\n                COLS -\n                len(hook['name']) -\n                len(no_files_msg) -\n                len(skipped_msg) -\n                6\n            ),\n            no_files_msg,\n            color.format_color(skipped_msg, color.TURQUOISE, args.color),\n        )\n    )\n\n\ndef _print_user_skipped(hook, write, args):\n    skipped_msg = 'Skipped'\n    write(\n        '{0}{1}{2}\\n'.format(\n            hook['name'],\n            '.' * (COLS - len(hook['name']) - len(skipped_msg) - 6),\n            color.format_color(skipped_msg, color.YELLOW, args.color),\n        ),\n    )\n\n\ndef _run_single_hook(runner, repository, hook_id, args, write, skips=set()):\n    if args.all_files:\n        get_filenames = git.get_all_files_matching\n    elif git.is_in_merge_conflict():\n        get_filenames = git.get_conflicted_files_matching\n    else:\n        get_filenames = git.get_staged_files_matching\n\n    hook = repository.hooks[hook_id]\n\n    filenames = get_filenames(hook['files'], hook['exclude'])\n    if hook_id in skips:\n        _print_user_skipped(hook, write, args)\n        return 0\n    elif not filenames:\n        _print_no_files_skipped(hook, write, args)\n        return 0\n\n    # Print the hook and the dots first in case the hook takes hella long to\n    # run.\n    write(\n        '{0}{1}'.format(\n            hook['name'],\n            '.' * (COLS - len(hook['name']) - PASS_FAIL_LENGTH - 6),\n        ),\n    )\n    sys.stdout.flush()\n\n    retcode, stdout, stderr = repository.run_hook(\n        runner.cmd_runner,\n        hook_id,\n        filenames,\n    )\n\n    if retcode != repository.hooks[hook_id]['expected_return_value']:\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    write(color.format_color(pass_fail, print_color, args.color) + '\\n')\n\n    if (stdout or stderr) and (retcode or args.verbose):\n        write('\\n')\n        for output in (stdout, stderr):\n            if output.strip():\n                write(output.strip() + '\\n')\n        write('\\n')\n\n    return retcode\n\n\ndef _run_hooks(runner, args, write, environ):\n    \"\"\"Actually run the hooks.\"\"\"\n    retval = 0\n\n    skips = _get_skips(environ)\n\n    for repo in runner.repositories:\n        for hook_id in repo.hooks:\n            retval |= _run_single_hook(\n                runner, repo, hook_id, args, write, skips=skips,\n            )\n\n    return retval\n\n\ndef _run_hook(runner, args, write):\n    hook_id = args.hook\n    for repo in runner.repositories:\n        if hook_id in repo.hooks:\n            return _run_single_hook(runner, repo, hook_id, args, write=write)\n    else:\n        write('No hook with id `{0}`\\n'.format(hook_id))\n        return 1\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 run(runner, args, write=sys.stdout.write, environ=os.environ):\n    # Set up our logging handler\n    logger.addHandler(LoggingHandler(args.color, write=write))\n    logger.setLevel(logging.INFO)\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\n    if args.no_stash or args.all_files:\n        ctx = noop_context()\n    else:\n        ctx = staged_files_only(runner.cmd_runner)\n\n    with ctx:\n        if args.hook:\n            return _run_hook(runner, args, write=write)\n        else:\n            return _run_hooks(runner, args, write=write, environ=environ)\n", "path": "pre_commit/commands.py"}]}