We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
pre-commit autoupdate fails on `local` hooks repos
```
$ pre-commit autoupdate
Updating git@github.com:pre-commit/pre-commit-hooks...updating 9ce45609a92f648c87b42207410386fd69a5d1e5 -> cf550fcab3f12015f8676b8278b30e1a5bc10e70.
Updating git@github.com:pre-commit/pre-commit...updating 4352d45451296934bc17494073b82bcacca3205c -> 1c46446427ab0dfa6293221426b855420533ef8d.
Updating git@github.com:asottile/reorder_python_imports...updating aeda21eb7df6af8c9f6cd990abb086375c71c953 -> 3d86483455ab5bd06cc1069fdd5ac57be5463f10.
Updating local...An unexpected error has occurred: AttributeError: 'NoneType' object has no attribute 'repo_path'
Check the log at ~/.pre-commit/pre-commit.log
(venv-pre_commit)asottile@work:~/workspace/pre-commit$ cat ~/.pre-commit/pre-commit.log 
An unexpected error has occurred: AttributeError: 'NoneType' object has no attribute 'repo_path'
Traceback (most recent call last):
  File "/home/asottile/workspace/pre-commit/pre_commit/error_handler.py", line 34, in error_handler
    yield
  File "/home/asottile/workspace/pre-commit/pre_commit/main.py", line 142, in main
    return autoupdate(runner)
  File "/home/asottile/workspace/pre-commit/pre_commit/commands/autoupdate.py", line 73, in autoupdate
    new_repo_config = _update_repository(repo_config, runner)
  File "/home/asottile/workspace/pre-commit/pre_commit/commands/autoupdate.py", line 33, in _update_repository
    with cwd(repo.repo_path_getter.repo_path):
AttributeError: 'NoneType' object has no attribute 'repo_path'

(venv-pre_commit)asottile@work:~/workspace/pre-commit$ git diff
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 397ee72..20393a7 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -20,3 +20,10 @@
     sha: aeda21eb7df6af8c9f6cd990abb086375c71c953
     hooks:
     -   id: reorder-python-imports
+-   repo: local
+    hooks:
+    -   id: herp
+        name: Herp
+        entry: echo derp
+        language: system
+        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/repository.py`
Content:
```
1 from __future__ import unicode_literals
2 
3 import logging
4 import shutil
5 
6 from cached_property import cached_property
7 
8 from pre_commit import git
9 from pre_commit.clientlib.validate_config import is_local_hooks
10 from pre_commit.clientlib.validate_manifest import MANIFEST_JSON_SCHEMA
11 from pre_commit.jsonschema_extensions import apply_defaults
12 from pre_commit.languages.all import languages
13 from pre_commit.languages.helpers import environment_dir
14 from pre_commit.manifest import Manifest
15 from pre_commit.prefixed_command_runner import PrefixedCommandRunner
16 
17 
18 logger = logging.getLogger('pre_commit')
19 
20 
21 class Repository(object):
22     def __init__(self, repo_config, repo_path_getter):
23         self.repo_config = repo_config
24         self.repo_path_getter = repo_path_getter
25         self.__installed = False
26 
27     @classmethod
28     def create(cls, config, store):
29         if is_local_hooks(config):
30             return LocalRepository(config)
31         else:
32             repo_path_getter = store.get_repo_path_getter(
33                 config['repo'], config['sha']
34             )
35             return cls(config, repo_path_getter)
36 
37     @cached_property
38     def repo_url(self):
39         return self.repo_config['repo']
40 
41     @cached_property
42     def sha(self):
43         return self.repo_config['sha']
44 
45     @cached_property
46     def languages(self):
47         return set(
48             (hook['language'], hook['language_version'])
49             for _, hook in self.hooks
50         )
51 
52     @cached_property
53     def hooks(self):
54         # TODO: merging in manifest dicts is a smell imo
55         return tuple(
56             (hook['id'], dict(self.manifest.hooks[hook['id']], **hook))
57             for hook in self.repo_config['hooks']
58         )
59 
60     @cached_property
61     def manifest(self):
62         return Manifest(self.repo_path_getter)
63 
64     @cached_property
65     def cmd_runner(self):
66         return PrefixedCommandRunner(self.repo_path_getter.repo_path)
67 
68     def require_installed(self):
69         if self.__installed:
70             return
71 
72         self.install()
73         self.__installed = True
74 
75     def install(self):
76         """Install the hook repository."""
77         def language_is_installed(language_name, language_version):
78             language = languages[language_name]
79             directory = environment_dir(
80                 language.ENVIRONMENT_DIR, language_version,
81             )
82             return (
83                 directory is None or
84                 self.cmd_runner.exists(directory, '.installed')
85             )
86 
87         if not all(
88             language_is_installed(language_name, language_version)
89             for language_name, language_version in self.languages
90         ):
91             logger.info(
92                 'Installing environment for {0}.'.format(self.repo_url)
93             )
94             logger.info('Once installed this environment will be reused.')
95             logger.info('This may take a few minutes...')
96 
97         for language_name, language_version in self.languages:
98             language = languages[language_name]
99             if language_is_installed(language_name, language_version):
100                 continue
101 
102             directory = environment_dir(
103                 language.ENVIRONMENT_DIR, language_version,
104             )
105             # There's potentially incomplete cleanup from previous runs
106             # Clean it up!
107             if self.cmd_runner.exists(directory):
108                 shutil.rmtree(self.cmd_runner.path(directory))
109 
110             language.install_environment(self.cmd_runner, language_version)
111             # Touch the .installed file (atomic) to indicate we've installed
112             open(self.cmd_runner.path(directory, '.installed'), 'w').close()
113 
114     def run_hook(self, hook, file_args):
115         """Run a hook.
116 
117         Args:
118             hook - Hook dictionary
119             file_args - List of files to run
120         """
121         self.require_installed()
122         return languages[hook['language']].run_hook(
123             self.cmd_runner, hook, file_args,
124         )
125 
126 
127 class LocalRepository(Repository):
128     def __init__(self, repo_config, repo_path_getter=None):
129         repo_path_getter = None
130         super(LocalRepository, self).__init__(repo_config, repo_path_getter)
131 
132     @cached_property
133     def hooks(self):
134         return tuple(
135             (hook['id'], apply_defaults(hook, MANIFEST_JSON_SCHEMA['items']))
136             for hook in self.repo_config['hooks']
137         )
138 
139     @cached_property
140     def cmd_runner(self):
141         return PrefixedCommandRunner(git.get_root())
142 
143     @cached_property
144     def sha(self):
145         raise NotImplementedError
146 
147     @cached_property
148     def manifest(self):
149         raise NotImplementedError
150
```

Path: `pre_commit/commands/autoupdate.py`
Content:
```
1 from __future__ import print_function
2 from __future__ import unicode_literals
3 
4 import sys
5 
6 from aspy.yaml import ordered_dump
7 from aspy.yaml import ordered_load
8 
9 import pre_commit.constants as C
10 from pre_commit.clientlib.validate_config import CONFIG_JSON_SCHEMA
11 from pre_commit.clientlib.validate_config import load_config
12 from pre_commit.jsonschema_extensions import remove_defaults
13 from pre_commit.ordereddict import OrderedDict
14 from pre_commit.repository import Repository
15 from pre_commit.util import cmd_output
16 from pre_commit.util import cwd
17 
18 
19 class RepositoryCannotBeUpdatedError(RuntimeError):
20     pass
21 
22 
23 def _update_repository(repo_config, runner):
24     """Updates a repository to the tip of `master`.  If the repository cannot
25     be updated because a hook that is configured does not exist in `master`,
26     this raises a RepositoryCannotBeUpdatedError
27 
28     Args:
29         repo_config - A config for a repository
30     """
31     repo = Repository.create(repo_config, runner.store)
32 
33     with cwd(repo.repo_path_getter.repo_path):
34         cmd_output('git', 'fetch')
35         head_sha = cmd_output('git', 'rev-parse', 'origin/master')[1].strip()
36 
37     # Don't bother trying to update if our sha is the same
38     if head_sha == repo_config['sha']:
39         return repo_config
40 
41     # Construct a new config with the head sha
42     new_config = OrderedDict(repo_config)
43     new_config['sha'] = head_sha
44     new_repo = Repository.create(new_config, runner.store)
45 
46     # See if any of our hooks were deleted with the new commits
47     hooks = set(hook_id for hook_id, _ in repo.hooks)
48     hooks_missing = hooks - (hooks & set(new_repo.manifest.hooks.keys()))
49     if hooks_missing:
50         raise RepositoryCannotBeUpdatedError(
51             'Cannot update because the tip of master is missing these hooks:\n'
52             '{0}'.format(', '.join(sorted(hooks_missing)))
53         )
54 
55     return new_config
56 
57 
58 def autoupdate(runner):
59     """Auto-update the pre-commit config to the latest versions of repos."""
60     retv = 0
61     output_configs = []
62     changed = False
63 
64     input_configs = load_config(
65         runner.config_file_path,
66         load_strategy=ordered_load,
67     )
68 
69     for repo_config in input_configs:
70         sys.stdout.write('Updating {0}...'.format(repo_config['repo']))
71         sys.stdout.flush()
72         try:
73             new_repo_config = _update_repository(repo_config, runner)
74         except RepositoryCannotBeUpdatedError as error:
75             print(error.args[0])
76             output_configs.append(repo_config)
77             retv = 1
78             continue
79 
80         if new_repo_config['sha'] != repo_config['sha']:
81             changed = True
82             print(
83                 'updating {0} -> {1}.'.format(
84                     repo_config['sha'], new_repo_config['sha'],
85                 )
86             )
87             output_configs.append(new_repo_config)
88         else:
89             print('already up to date.')
90             output_configs.append(repo_config)
91 
92     if changed:
93         with open(runner.config_file_path, 'w') as config_file:
94             config_file.write(
95                 ordered_dump(
96                     remove_defaults(output_configs, CONFIG_JSON_SCHEMA),
97                     **C.YAML_DUMP_KWARGS
98                 )
99             )
100 
101     return retv
102
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/pre_commit/commands/autoupdate.py b/pre_commit/commands/autoupdate.py
--- a/pre_commit/commands/autoupdate.py
+++ b/pre_commit/commands/autoupdate.py
@@ -8,6 +8,7 @@
 
 import pre_commit.constants as C
 from pre_commit.clientlib.validate_config import CONFIG_JSON_SCHEMA
+from pre_commit.clientlib.validate_config import is_local_hooks
 from pre_commit.clientlib.validate_config import load_config
 from pre_commit.jsonschema_extensions import remove_defaults
 from pre_commit.ordereddict import OrderedDict
@@ -67,6 +68,8 @@
     )
 
     for repo_config in input_configs:
+        if is_local_hooks(repo_config):
+            continue
         sys.stdout.write('Updating {0}...'.format(repo_config['repo']))
         sys.stdout.flush()
         try:
diff --git a/pre_commit/repository.py b/pre_commit/repository.py
--- a/pre_commit/repository.py
+++ b/pre_commit/repository.py
@@ -125,9 +125,8 @@
 
 
 class LocalRepository(Repository):
-    def __init__(self, repo_config, repo_path_getter=None):
-        repo_path_getter = None
-        super(LocalRepository, self).__init__(repo_config, repo_path_getter)
+    def __init__(self, repo_config):
+        super(LocalRepository, self).__init__(repo_config, None)
 
     @cached_property
     def hooks(self):

{"golden_diff": "diff --git a/pre_commit/commands/autoupdate.py b/pre_commit/commands/autoupdate.py\n--- a/pre_commit/commands/autoupdate.py\n+++ b/pre_commit/commands/autoupdate.py\n@@ -8,6 +8,7 @@\n \n import pre_commit.constants as C\n from pre_commit.clientlib.validate_config import CONFIG_JSON_SCHEMA\n+from pre_commit.clientlib.validate_config import is_local_hooks\n from pre_commit.clientlib.validate_config import load_config\n from pre_commit.jsonschema_extensions import remove_defaults\n from pre_commit.ordereddict import OrderedDict\n@@ -67,6 +68,8 @@\n     )\n \n     for repo_config in input_configs:\n+        if is_local_hooks(repo_config):\n+            continue\n         sys.stdout.write('Updating {0}...'.format(repo_config['repo']))\n         sys.stdout.flush()\n         try:\ndiff --git a/pre_commit/repository.py b/pre_commit/repository.py\n--- a/pre_commit/repository.py\n+++ b/pre_commit/repository.py\n@@ -125,9 +125,8 @@\n \n \n class LocalRepository(Repository):\n-    def __init__(self, repo_config, repo_path_getter=None):\n-        repo_path_getter = None\n-        super(LocalRepository, self).__init__(repo_config, repo_path_getter)\n+    def __init__(self, repo_config):\n+        super(LocalRepository, self).__init__(repo_config, None)\n \n     @cached_property\n     def hooks(self):\n", "issue": "pre-commit autoupdate fails on `local` hooks repos\n```\n$ pre-commit autoupdate\nUpdating git@github.com:pre-commit/pre-commit-hooks...updating 9ce45609a92f648c87b42207410386fd69a5d1e5 -> cf550fcab3f12015f8676b8278b30e1a5bc10e70.\nUpdating git@github.com:pre-commit/pre-commit...updating 4352d45451296934bc17494073b82bcacca3205c -> 1c46446427ab0dfa6293221426b855420533ef8d.\nUpdating git@github.com:asottile/reorder_python_imports...updating aeda21eb7df6af8c9f6cd990abb086375c71c953 -> 3d86483455ab5bd06cc1069fdd5ac57be5463f10.\nUpdating local...An unexpected error has occurred: AttributeError: 'NoneType' object has no attribute 'repo_path'\nCheck the log at ~/.pre-commit/pre-commit.log\n(venv-pre_commit)asottile@work:~/workspace/pre-commit$ cat ~/.pre-commit/pre-commit.log \nAn unexpected error has occurred: AttributeError: 'NoneType' object has no attribute 'repo_path'\nTraceback (most recent call last):\n  File \"/home/asottile/workspace/pre-commit/pre_commit/error_handler.py\", line 34, in error_handler\n    yield\n  File \"/home/asottile/workspace/pre-commit/pre_commit/main.py\", line 142, in main\n    return autoupdate(runner)\n  File \"/home/asottile/workspace/pre-commit/pre_commit/commands/autoupdate.py\", line 73, in autoupdate\n    new_repo_config = _update_repository(repo_config, runner)\n  File \"/home/asottile/workspace/pre-commit/pre_commit/commands/autoupdate.py\", line 33, in _update_repository\n    with cwd(repo.repo_path_getter.repo_path):\nAttributeError: 'NoneType' object has no attribute 'repo_path'\n\n(venv-pre_commit)asottile@work:~/workspace/pre-commit$ git diff\ndiff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml\nindex 397ee72..20393a7 100644\n--- a/.pre-commit-config.yaml\n+++ b/.pre-commit-config.yaml\n@@ -20,3 +20,10 @@\n     sha: aeda21eb7df6af8c9f6cd990abb086375c71c953\n     hooks:\n     -   id: reorder-python-imports\n+-   repo: local\n+    hooks:\n+    -   id: herp\n+        name: Herp\n+        entry: echo derp\n+        language: system\n+        files: ^$\n```\n\n", "before_files": [{"content": "from __future__ import unicode_literals\n\nimport logging\nimport shutil\n\nfrom cached_property import cached_property\n\nfrom pre_commit import git\nfrom pre_commit.clientlib.validate_config import is_local_hooks\nfrom pre_commit.clientlib.validate_manifest import MANIFEST_JSON_SCHEMA\nfrom pre_commit.jsonschema_extensions import apply_defaults\nfrom pre_commit.languages.all import languages\nfrom pre_commit.languages.helpers import environment_dir\nfrom pre_commit.manifest import Manifest\nfrom pre_commit.prefixed_command_runner import PrefixedCommandRunner\n\n\nlogger = logging.getLogger('pre_commit')\n\n\nclass Repository(object):\n    def __init__(self, repo_config, repo_path_getter):\n        self.repo_config = repo_config\n        self.repo_path_getter = repo_path_getter\n        self.__installed = False\n\n    @classmethod\n    def create(cls, config, store):\n        if is_local_hooks(config):\n            return LocalRepository(config)\n        else:\n            repo_path_getter = store.get_repo_path_getter(\n                config['repo'], config['sha']\n            )\n            return cls(config, repo_path_getter)\n\n    @cached_property\n    def repo_url(self):\n        return self.repo_config['repo']\n\n    @cached_property\n    def sha(self):\n        return self.repo_config['sha']\n\n    @cached_property\n    def languages(self):\n        return set(\n            (hook['language'], hook['language_version'])\n            for _, hook in self.hooks\n        )\n\n    @cached_property\n    def hooks(self):\n        # TODO: merging in manifest dicts is a smell imo\n        return tuple(\n            (hook['id'], dict(self.manifest.hooks[hook['id']], **hook))\n            for hook in self.repo_config['hooks']\n        )\n\n    @cached_property\n    def manifest(self):\n        return Manifest(self.repo_path_getter)\n\n    @cached_property\n    def cmd_runner(self):\n        return PrefixedCommandRunner(self.repo_path_getter.repo_path)\n\n    def require_installed(self):\n        if self.__installed:\n            return\n\n        self.install()\n        self.__installed = True\n\n    def install(self):\n        \"\"\"Install the hook repository.\"\"\"\n        def language_is_installed(language_name, language_version):\n            language = languages[language_name]\n            directory = environment_dir(\n                language.ENVIRONMENT_DIR, language_version,\n            )\n            return (\n                directory is None or\n                self.cmd_runner.exists(directory, '.installed')\n            )\n\n        if not all(\n            language_is_installed(language_name, language_version)\n            for language_name, language_version in self.languages\n        ):\n            logger.info(\n                'Installing environment for {0}.'.format(self.repo_url)\n            )\n            logger.info('Once installed this environment will be reused.')\n            logger.info('This may take a few minutes...')\n\n        for language_name, language_version in self.languages:\n            language = languages[language_name]\n            if language_is_installed(language_name, language_version):\n                continue\n\n            directory = environment_dir(\n                language.ENVIRONMENT_DIR, language_version,\n            )\n            # There's potentially incomplete cleanup from previous runs\n            # Clean it up!\n            if self.cmd_runner.exists(directory):\n                shutil.rmtree(self.cmd_runner.path(directory))\n\n            language.install_environment(self.cmd_runner, language_version)\n            # Touch the .installed file (atomic) to indicate we've installed\n            open(self.cmd_runner.path(directory, '.installed'), 'w').close()\n\n    def run_hook(self, hook, file_args):\n        \"\"\"Run a hook.\n\n        Args:\n            hook - Hook dictionary\n            file_args - List of files to run\n        \"\"\"\n        self.require_installed()\n        return languages[hook['language']].run_hook(\n            self.cmd_runner, hook, file_args,\n        )\n\n\nclass LocalRepository(Repository):\n    def __init__(self, repo_config, repo_path_getter=None):\n        repo_path_getter = None\n        super(LocalRepository, self).__init__(repo_config, repo_path_getter)\n\n    @cached_property\n    def hooks(self):\n        return tuple(\n            (hook['id'], apply_defaults(hook, MANIFEST_JSON_SCHEMA['items']))\n            for hook in self.repo_config['hooks']\n        )\n\n    @cached_property\n    def cmd_runner(self):\n        return PrefixedCommandRunner(git.get_root())\n\n    @cached_property\n    def sha(self):\n        raise NotImplementedError\n\n    @cached_property\n    def manifest(self):\n        raise NotImplementedError\n", "path": "pre_commit/repository.py"}, {"content": "from __future__ import print_function\nfrom __future__ import unicode_literals\n\nimport sys\n\nfrom aspy.yaml import ordered_dump\nfrom aspy.yaml import ordered_load\n\nimport pre_commit.constants as C\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.ordereddict import OrderedDict\nfrom pre_commit.repository import Repository\nfrom pre_commit.util import cmd_output\nfrom pre_commit.util import cwd\n\n\nclass RepositoryCannotBeUpdatedError(RuntimeError):\n    pass\n\n\ndef _update_repository(repo_config, runner):\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.create(repo_config, runner.store)\n\n    with cwd(repo.repo_path_getter.repo_path):\n        cmd_output('git', 'fetch')\n        head_sha = cmd_output('git', 'rev-parse', 'origin/master')[1].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.create(new_config, runner.store)\n\n    # See if any of our hooks were deleted with the new commits\n    hooks = set(hook_id for hook_id, _ in repo.hooks)\n    hooks_missing = hooks - (hooks & set(new_repo.manifest.hooks.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 new_config\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        sys.stdout.write('Updating {0}...'.format(repo_config['repo']))\n        sys.stdout.flush()\n        try:\n            new_repo_config = _update_repository(repo_config, runner)\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(\n                    remove_defaults(output_configs, CONFIG_JSON_SCHEMA),\n                    **C.YAML_DUMP_KWARGS\n                )\n            )\n\n    return retv\n", "path": "pre_commit/commands/autoupdate.py"}], "after_files": [{"content": "from __future__ import unicode_literals\n\nimport logging\nimport shutil\n\nfrom cached_property import cached_property\n\nfrom pre_commit import git\nfrom pre_commit.clientlib.validate_config import is_local_hooks\nfrom pre_commit.clientlib.validate_manifest import MANIFEST_JSON_SCHEMA\nfrom pre_commit.jsonschema_extensions import apply_defaults\nfrom pre_commit.languages.all import languages\nfrom pre_commit.languages.helpers import environment_dir\nfrom pre_commit.manifest import Manifest\nfrom pre_commit.prefixed_command_runner import PrefixedCommandRunner\n\n\nlogger = logging.getLogger('pre_commit')\n\n\nclass Repository(object):\n    def __init__(self, repo_config, repo_path_getter):\n        self.repo_config = repo_config\n        self.repo_path_getter = repo_path_getter\n        self.__installed = False\n\n    @classmethod\n    def create(cls, config, store):\n        if is_local_hooks(config):\n            return LocalRepository(config)\n        else:\n            repo_path_getter = store.get_repo_path_getter(\n                config['repo'], config['sha']\n            )\n            return cls(config, repo_path_getter)\n\n    @cached_property\n    def repo_url(self):\n        return self.repo_config['repo']\n\n    @cached_property\n    def sha(self):\n        return self.repo_config['sha']\n\n    @cached_property\n    def languages(self):\n        return set(\n            (hook['language'], hook['language_version'])\n            for _, hook in self.hooks\n        )\n\n    @cached_property\n    def hooks(self):\n        # TODO: merging in manifest dicts is a smell imo\n        return tuple(\n            (hook['id'], dict(self.manifest.hooks[hook['id']], **hook))\n            for hook in self.repo_config['hooks']\n        )\n\n    @cached_property\n    def manifest(self):\n        return Manifest(self.repo_path_getter)\n\n    @cached_property\n    def cmd_runner(self):\n        return PrefixedCommandRunner(self.repo_path_getter.repo_path)\n\n    def require_installed(self):\n        if self.__installed:\n            return\n\n        self.install()\n        self.__installed = True\n\n    def install(self):\n        \"\"\"Install the hook repository.\"\"\"\n        def language_is_installed(language_name, language_version):\n            language = languages[language_name]\n            directory = environment_dir(\n                language.ENVIRONMENT_DIR, language_version,\n            )\n            return (\n                directory is None or\n                self.cmd_runner.exists(directory, '.installed')\n            )\n\n        if not all(\n            language_is_installed(language_name, language_version)\n            for language_name, language_version in self.languages\n        ):\n            logger.info(\n                'Installing environment for {0}.'.format(self.repo_url)\n            )\n            logger.info('Once installed this environment will be reused.')\n            logger.info('This may take a few minutes...')\n\n        for language_name, language_version in self.languages:\n            language = languages[language_name]\n            if language_is_installed(language_name, language_version):\n                continue\n\n            directory = environment_dir(\n                language.ENVIRONMENT_DIR, language_version,\n            )\n            # There's potentially incomplete cleanup from previous runs\n            # Clean it up!\n            if self.cmd_runner.exists(directory):\n                shutil.rmtree(self.cmd_runner.path(directory))\n\n            language.install_environment(self.cmd_runner, language_version)\n            # Touch the .installed file (atomic) to indicate we've installed\n            open(self.cmd_runner.path(directory, '.installed'), 'w').close()\n\n    def run_hook(self, hook, file_args):\n        \"\"\"Run a hook.\n\n        Args:\n            hook - Hook dictionary\n            file_args - List of files to run\n        \"\"\"\n        self.require_installed()\n        return languages[hook['language']].run_hook(\n            self.cmd_runner, hook, file_args,\n        )\n\n\nclass LocalRepository(Repository):\n    def __init__(self, repo_config):\n        super(LocalRepository, self).__init__(repo_config, None)\n\n    @cached_property\n    def hooks(self):\n        return tuple(\n            (hook['id'], apply_defaults(hook, MANIFEST_JSON_SCHEMA['items']))\n            for hook in self.repo_config['hooks']\n        )\n\n    @cached_property\n    def cmd_runner(self):\n        return PrefixedCommandRunner(git.get_root())\n\n    @cached_property\n    def sha(self):\n        raise NotImplementedError\n\n    @cached_property\n    def manifest(self):\n        raise NotImplementedError\n", "path": "pre_commit/repository.py"}, {"content": "from __future__ import print_function\nfrom __future__ import unicode_literals\n\nimport sys\n\nfrom aspy.yaml import ordered_dump\nfrom aspy.yaml import ordered_load\n\nimport pre_commit.constants as C\nfrom pre_commit.clientlib.validate_config import CONFIG_JSON_SCHEMA\nfrom pre_commit.clientlib.validate_config import is_local_hooks\nfrom pre_commit.clientlib.validate_config import load_config\nfrom pre_commit.jsonschema_extensions import remove_defaults\nfrom pre_commit.ordereddict import OrderedDict\nfrom pre_commit.repository import Repository\nfrom pre_commit.util import cmd_output\nfrom pre_commit.util import cwd\n\n\nclass RepositoryCannotBeUpdatedError(RuntimeError):\n    pass\n\n\ndef _update_repository(repo_config, runner):\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.create(repo_config, runner.store)\n\n    with cwd(repo.repo_path_getter.repo_path):\n        cmd_output('git', 'fetch')\n        head_sha = cmd_output('git', 'rev-parse', 'origin/master')[1].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.create(new_config, runner.store)\n\n    # See if any of our hooks were deleted with the new commits\n    hooks = set(hook_id for hook_id, _ in repo.hooks)\n    hooks_missing = hooks - (hooks & set(new_repo.manifest.hooks.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 new_config\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        if is_local_hooks(repo_config):\n            continue\n        sys.stdout.write('Updating {0}...'.format(repo_config['repo']))\n        sys.stdout.flush()\n        try:\n            new_repo_config = _update_repository(repo_config, runner)\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(\n                    remove_defaults(output_configs, CONFIG_JSON_SCHEMA),\n                    **C.YAML_DUMP_KWARGS\n                )\n            )\n\n    return retv\n", "path": "pre_commit/commands/autoupdate.py"}]}