We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Bug: base manifest value for 'exclude' is always ignored
I stumbled upon this bug while working on #226: the culprit is [`Repository.hooks`](https://github.com/pre-commit/pre-commit/blob/master/pre_commit/repository.py#L48).

A quick fix for this would be to simply remove the default value from `pre_commit/clientlib/validate_config.py`, but the root cause is that any default value defined for a field in this file will make the corresponding manifest field useless.

Basically here is what happens in `Repository.hooks`:
- all the hooks defined in the current repository are enumerated
- at this stage, a `hook` is a dict closely matching the Yaml the config file content, **plus** default values for fields not defined in the Yaml but having a JSON schema 'default'
- when doing the dict merge, **every** (key,value) pair in `hook` overrides the corresponding manifest entry. This includes default config value like `exclude: '$^'` overriding a base manifest value like `exclude: '.bak$'`

Hence I suggest either adding a test ensuring there will never be any 'default' defined in `CONFIG_JSON_SCHEMA`, or improving the merge logic.


--- 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/clientlib/validate_config.py`
Content:
```
1 from __future__ import unicode_literals
2 
3 from pre_commit.clientlib.validate_base import get_run_function
4 from pre_commit.clientlib.validate_base import get_validator
5 from pre_commit.clientlib.validate_base import is_regex_valid
6 from pre_commit.errors import FatalError
7 
8 
9 _LOCAL_HOOKS_MAGIC_REPO_STRING = 'local'
10 
11 
12 def is_local_hooks(repo_entry):
13     return repo_entry['repo'] == _LOCAL_HOOKS_MAGIC_REPO_STRING
14 
15 
16 class InvalidConfigError(FatalError):
17     pass
18 
19 
20 CONFIG_JSON_SCHEMA = {
21     'type': 'array',
22     'minItems': 1,
23     'items': {
24         'type': 'object',
25         'properties': {
26             'repo': {'type': 'string'},
27             'sha': {'type': 'string'},
28             'hooks': {
29                 'type': 'array',
30                 'minItems': 1,
31                 'items': {
32                     'type': 'object',
33                     'properties': {
34                         'id': {'type': 'string'},
35                         'files': {'type': 'string'},
36                         'exclude': {'type': 'string', 'default': '^$'},
37                         'language_version': {'type': 'string'},
38                         'args': {
39                             'type': 'array',
40                             'items': {'type': 'string'},
41                         },
42                     },
43                     'required': ['id'],
44                 }
45             }
46         },
47         'required': ['repo', 'hooks'],
48     }
49 }
50 
51 
52 def try_regex(repo, hook, value, field_name):
53     if not is_regex_valid(value):
54         raise InvalidConfigError(
55             'Invalid {0} regex at {1}, {2}: {3}'.format(
56                 field_name, repo, hook, value,
57             )
58         )
59 
60 
61 def validate_config_extra(config):
62     for repo in config:
63         if is_local_hooks(repo):
64             if 'sha' in repo:
65                 raise InvalidConfigError(
66                     '"sha" property provided for local hooks'
67                 )
68         elif 'sha' not in repo:
69             raise InvalidConfigError(
70                 'Missing "sha" field for repository {0}'.format(repo['repo'])
71             )
72         for hook in repo['hooks']:
73             try_regex(repo, hook['id'], hook.get('files', ''), 'files')
74             try_regex(repo, hook['id'], hook['exclude'], 'exclude')
75 
76 
77 load_config = get_validator(
78     CONFIG_JSON_SCHEMA,
79     InvalidConfigError,
80     additional_validation_strategy=validate_config_extra,
81 )
82 
83 
84 run = get_run_function('Config filenames.', load_config, InvalidConfigError)
85 
86 
87 if __name__ == '__main__':
88     exit(run())
89
```

Path: `pre_commit/clientlib/validate_manifest.py`
Content:
```
1 from __future__ import unicode_literals
2 
3 from pre_commit.clientlib.validate_base import get_run_function
4 from pre_commit.clientlib.validate_base import get_validator
5 from pre_commit.clientlib.validate_base import is_regex_valid
6 from pre_commit.languages.all import all_languages
7 
8 
9 class InvalidManifestError(ValueError):
10     pass
11 
12 
13 MANIFEST_JSON_SCHEMA = {
14     'type': 'array',
15     'minItems': 1,
16     'items': {
17         'type': 'object',
18         'properties': {
19             'id': {'type': 'string'},
20             'name': {'type': 'string'},
21             'description': {'type': 'string', 'default': ''},
22             'entry': {'type': 'string'},
23             'language': {'type': 'string'},
24             'language_version': {'type': 'string', 'default': 'default'},
25             'files': {'type': 'string'},
26             'expected_return_value': {'type': 'number', 'default': 0},
27             'args': {
28                 'type': 'array',
29                 'default': [],
30                 'items': {
31                     'type': 'string',
32                 },
33             },
34         },
35         'required': ['id', 'name', 'entry', 'language', 'files'],
36     },
37 }
38 
39 
40 def validate_languages(hook_config):
41     if hook_config['language'] not in all_languages:
42         raise InvalidManifestError(
43             'Expected language {0} for {1} to be one of {2!r}'.format(
44                 hook_config['id'],
45                 hook_config['language'],
46                 all_languages,
47             )
48         )
49 
50 
51 def validate_files(hook_config):
52     if not is_regex_valid(hook_config['files']):
53         raise InvalidManifestError(
54             'Invalid files regex at {0}: {1}'.format(
55                 hook_config['id'],
56                 hook_config['files'],
57             )
58         )
59 
60 
61 def additional_manifest_check(obj):
62     for hook_config in obj:
63         validate_languages(hook_config)
64         validate_files(hook_config)
65 
66 
67 load_manifest = get_validator(
68     MANIFEST_JSON_SCHEMA,
69     InvalidManifestError,
70     additional_manifest_check,
71 )
72 
73 
74 run = get_run_function(
75     'Manifest filenames.',
76     load_manifest,
77     InvalidManifestError,
78 )
79 
80 
81 if __name__ == '__main__':
82     exit(run())
83
```
--- 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/clientlib/validate_config.py b/pre_commit/clientlib/validate_config.py
--- a/pre_commit/clientlib/validate_config.py
+++ b/pre_commit/clientlib/validate_config.py
@@ -33,7 +33,7 @@
                     'properties': {
                         'id': {'type': 'string'},
                         'files': {'type': 'string'},
-                        'exclude': {'type': 'string', 'default': '^$'},
+                        'exclude': {'type': 'string'},
                         'language_version': {'type': 'string'},
                         'args': {
                             'type': 'array',
@@ -71,7 +71,7 @@
             )
         for hook in repo['hooks']:
             try_regex(repo, hook['id'], hook.get('files', ''), 'files')
-            try_regex(repo, hook['id'], hook['exclude'], 'exclude')
+            try_regex(repo, hook['id'], hook.get('exclude', ''), 'exclude')
 
 
 load_config = get_validator(
diff --git a/pre_commit/clientlib/validate_manifest.py b/pre_commit/clientlib/validate_manifest.py
--- a/pre_commit/clientlib/validate_manifest.py
+++ b/pre_commit/clientlib/validate_manifest.py
@@ -20,6 +20,7 @@
             'name': {'type': 'string'},
             'description': {'type': 'string', 'default': ''},
             'entry': {'type': 'string'},
+            'exclude': {'type': 'string', 'default': '^$'},
             'language': {'type': 'string'},
             'language_version': {'type': 'string', 'default': 'default'},
             'files': {'type': 'string'},
@@ -52,8 +53,14 @@
     if not is_regex_valid(hook_config['files']):
         raise InvalidManifestError(
             'Invalid files regex at {0}: {1}'.format(
-                hook_config['id'],
-                hook_config['files'],
+                hook_config['id'], hook_config['files'],
+            )
+        )
+
+    if not is_regex_valid(hook_config.get('exclude', '')):
+        raise InvalidManifestError(
+            'Invalid exclude regex at {0}: {1}'.format(
+                hook_config['id'], hook_config['exclude'],
             )
         )

{"golden_diff": "diff --git a/pre_commit/clientlib/validate_config.py b/pre_commit/clientlib/validate_config.py\n--- a/pre_commit/clientlib/validate_config.py\n+++ b/pre_commit/clientlib/validate_config.py\n@@ -33,7 +33,7 @@\n                     'properties': {\n                         'id': {'type': 'string'},\n                         'files': {'type': 'string'},\n-                        'exclude': {'type': 'string', 'default': '^$'},\n+                        'exclude': {'type': 'string'},\n                         'language_version': {'type': 'string'},\n                         'args': {\n                             'type': 'array',\n@@ -71,7 +71,7 @@\n             )\n         for hook in repo['hooks']:\n             try_regex(repo, hook['id'], hook.get('files', ''), 'files')\n-            try_regex(repo, hook['id'], hook['exclude'], 'exclude')\n+            try_regex(repo, hook['id'], hook.get('exclude', ''), 'exclude')\n \n \n load_config = get_validator(\ndiff --git a/pre_commit/clientlib/validate_manifest.py b/pre_commit/clientlib/validate_manifest.py\n--- a/pre_commit/clientlib/validate_manifest.py\n+++ b/pre_commit/clientlib/validate_manifest.py\n@@ -20,6 +20,7 @@\n             'name': {'type': 'string'},\n             'description': {'type': 'string', 'default': ''},\n             'entry': {'type': 'string'},\n+            'exclude': {'type': 'string', 'default': '^$'},\n             'language': {'type': 'string'},\n             'language_version': {'type': 'string', 'default': 'default'},\n             'files': {'type': 'string'},\n@@ -52,8 +53,14 @@\n     if not is_regex_valid(hook_config['files']):\n         raise InvalidManifestError(\n             'Invalid files regex at {0}: {1}'.format(\n-                hook_config['id'],\n-                hook_config['files'],\n+                hook_config['id'], hook_config['files'],\n+            )\n+        )\n+\n+    if not is_regex_valid(hook_config.get('exclude', '')):\n+        raise InvalidManifestError(\n+            'Invalid exclude regex at {0}: {1}'.format(\n+                hook_config['id'], hook_config['exclude'],\n             )\n         )\n", "issue": "Bug: base manifest value for 'exclude' is always ignored\nI stumbled upon this bug while working on #226: the culprit is [`Repository.hooks`](https://github.com/pre-commit/pre-commit/blob/master/pre_commit/repository.py#L48).\n\nA quick fix for this would be to simply remove the default value from `pre_commit/clientlib/validate_config.py`, but the root cause is that any default value defined for a field in this file will make the corresponding manifest field useless.\n\nBasically here is what happens in `Repository.hooks`:\n- all the hooks defined in the current repository are enumerated\n- at this stage, a `hook` is a dict closely matching the Yaml the config file content, **plus** default values for fields not defined in the Yaml but having a JSON schema 'default'\n- when doing the dict merge, **every** (key,value) pair in `hook` overrides the corresponding manifest entry. This includes default config value like `exclude: '$^'` overriding a base manifest value like `exclude: '.bak$'`\n\nHence I suggest either adding a test ensuring there will never be any 'default' defined in `CONFIG_JSON_SCHEMA`, or improving the merge logic.\n\n", "before_files": [{"content": "from __future__ import unicode_literals\n\nfrom pre_commit.clientlib.validate_base import get_run_function\nfrom pre_commit.clientlib.validate_base import get_validator\nfrom pre_commit.clientlib.validate_base import is_regex_valid\nfrom pre_commit.errors import FatalError\n\n\n_LOCAL_HOOKS_MAGIC_REPO_STRING = 'local'\n\n\ndef is_local_hooks(repo_entry):\n    return repo_entry['repo'] == _LOCAL_HOOKS_MAGIC_REPO_STRING\n\n\nclass InvalidConfigError(FatalError):\n    pass\n\n\nCONFIG_JSON_SCHEMA = {\n    'type': 'array',\n    'minItems': 1,\n    'items': {\n        'type': 'object',\n        'properties': {\n            'repo': {'type': 'string'},\n            'sha': {'type': 'string'},\n            'hooks': {\n                'type': 'array',\n                'minItems': 1,\n                'items': {\n                    'type': 'object',\n                    'properties': {\n                        'id': {'type': 'string'},\n                        'files': {'type': 'string'},\n                        'exclude': {'type': 'string', 'default': '^$'},\n                        'language_version': {'type': 'string'},\n                        'args': {\n                            'type': 'array',\n                            'items': {'type': 'string'},\n                        },\n                    },\n                    'required': ['id'],\n                }\n            }\n        },\n        'required': ['repo', 'hooks'],\n    }\n}\n\n\ndef try_regex(repo, hook, value, field_name):\n    if not is_regex_valid(value):\n        raise InvalidConfigError(\n            'Invalid {0} regex at {1}, {2}: {3}'.format(\n                field_name, repo, hook, value,\n            )\n        )\n\n\ndef validate_config_extra(config):\n    for repo in config:\n        if is_local_hooks(repo):\n            if 'sha' in repo:\n                raise InvalidConfigError(\n                    '\"sha\" property provided for local hooks'\n                )\n        elif 'sha' not in repo:\n            raise InvalidConfigError(\n                'Missing \"sha\" field for repository {0}'.format(repo['repo'])\n            )\n        for hook in repo['hooks']:\n            try_regex(repo, hook['id'], hook.get('files', ''), 'files')\n            try_regex(repo, hook['id'], hook['exclude'], 'exclude')\n\n\nload_config = get_validator(\n    CONFIG_JSON_SCHEMA,\n    InvalidConfigError,\n    additional_validation_strategy=validate_config_extra,\n)\n\n\nrun = get_run_function('Config filenames.', load_config, InvalidConfigError)\n\n\nif __name__ == '__main__':\n    exit(run())\n", "path": "pre_commit/clientlib/validate_config.py"}, {"content": "from __future__ import unicode_literals\n\nfrom pre_commit.clientlib.validate_base import get_run_function\nfrom pre_commit.clientlib.validate_base import get_validator\nfrom pre_commit.clientlib.validate_base import is_regex_valid\nfrom pre_commit.languages.all import all_languages\n\n\nclass InvalidManifestError(ValueError):\n    pass\n\n\nMANIFEST_JSON_SCHEMA = {\n    'type': 'array',\n    'minItems': 1,\n    'items': {\n        'type': 'object',\n        'properties': {\n            'id': {'type': 'string'},\n            'name': {'type': 'string'},\n            'description': {'type': 'string', 'default': ''},\n            'entry': {'type': 'string'},\n            'language': {'type': 'string'},\n            'language_version': {'type': 'string', 'default': 'default'},\n            'files': {'type': 'string'},\n            'expected_return_value': {'type': 'number', 'default': 0},\n            'args': {\n                'type': 'array',\n                'default': [],\n                'items': {\n                    'type': 'string',\n                },\n            },\n        },\n        'required': ['id', 'name', 'entry', 'language', 'files'],\n    },\n}\n\n\ndef validate_languages(hook_config):\n    if hook_config['language'] not in all_languages:\n        raise InvalidManifestError(\n            'Expected language {0} for {1} to be one of {2!r}'.format(\n                hook_config['id'],\n                hook_config['language'],\n                all_languages,\n            )\n        )\n\n\ndef validate_files(hook_config):\n    if not is_regex_valid(hook_config['files']):\n        raise InvalidManifestError(\n            'Invalid files regex at {0}: {1}'.format(\n                hook_config['id'],\n                hook_config['files'],\n            )\n        )\n\n\ndef additional_manifest_check(obj):\n    for hook_config in obj:\n        validate_languages(hook_config)\n        validate_files(hook_config)\n\n\nload_manifest = get_validator(\n    MANIFEST_JSON_SCHEMA,\n    InvalidManifestError,\n    additional_manifest_check,\n)\n\n\nrun = get_run_function(\n    'Manifest filenames.',\n    load_manifest,\n    InvalidManifestError,\n)\n\n\nif __name__ == '__main__':\n    exit(run())\n", "path": "pre_commit/clientlib/validate_manifest.py"}], "after_files": [{"content": "from __future__ import unicode_literals\n\nfrom pre_commit.clientlib.validate_base import get_run_function\nfrom pre_commit.clientlib.validate_base import get_validator\nfrom pre_commit.clientlib.validate_base import is_regex_valid\nfrom pre_commit.errors import FatalError\n\n\n_LOCAL_HOOKS_MAGIC_REPO_STRING = 'local'\n\n\ndef is_local_hooks(repo_entry):\n    return repo_entry['repo'] == _LOCAL_HOOKS_MAGIC_REPO_STRING\n\n\nclass InvalidConfigError(FatalError):\n    pass\n\n\nCONFIG_JSON_SCHEMA = {\n    'type': 'array',\n    'minItems': 1,\n    'items': {\n        'type': 'object',\n        'properties': {\n            'repo': {'type': 'string'},\n            'sha': {'type': 'string'},\n            'hooks': {\n                'type': 'array',\n                'minItems': 1,\n                'items': {\n                    'type': 'object',\n                    'properties': {\n                        'id': {'type': 'string'},\n                        'files': {'type': 'string'},\n                        'exclude': {'type': 'string'},\n                        'language_version': {'type': 'string'},\n                        'args': {\n                            'type': 'array',\n                            'items': {'type': 'string'},\n                        },\n                    },\n                    'required': ['id'],\n                }\n            }\n        },\n        'required': ['repo', 'hooks'],\n    }\n}\n\n\ndef try_regex(repo, hook, value, field_name):\n    if not is_regex_valid(value):\n        raise InvalidConfigError(\n            'Invalid {0} regex at {1}, {2}: {3}'.format(\n                field_name, repo, hook, value,\n            )\n        )\n\n\ndef validate_config_extra(config):\n    for repo in config:\n        if is_local_hooks(repo):\n            if 'sha' in repo:\n                raise InvalidConfigError(\n                    '\"sha\" property provided for local hooks'\n                )\n        elif 'sha' not in repo:\n            raise InvalidConfigError(\n                'Missing \"sha\" field for repository {0}'.format(repo['repo'])\n            )\n        for hook in repo['hooks']:\n            try_regex(repo, hook['id'], hook.get('files', ''), 'files')\n            try_regex(repo, hook['id'], hook.get('exclude', ''), 'exclude')\n\n\nload_config = get_validator(\n    CONFIG_JSON_SCHEMA,\n    InvalidConfigError,\n    additional_validation_strategy=validate_config_extra,\n)\n\n\nrun = get_run_function('Config filenames.', load_config, InvalidConfigError)\n\n\nif __name__ == '__main__':\n    exit(run())\n", "path": "pre_commit/clientlib/validate_config.py"}, {"content": "from __future__ import unicode_literals\n\nfrom pre_commit.clientlib.validate_base import get_run_function\nfrom pre_commit.clientlib.validate_base import get_validator\nfrom pre_commit.clientlib.validate_base import is_regex_valid\nfrom pre_commit.languages.all import all_languages\n\n\nclass InvalidManifestError(ValueError):\n    pass\n\n\nMANIFEST_JSON_SCHEMA = {\n    'type': 'array',\n    'minItems': 1,\n    'items': {\n        'type': 'object',\n        'properties': {\n            'id': {'type': 'string'},\n            'name': {'type': 'string'},\n            'description': {'type': 'string', 'default': ''},\n            'entry': {'type': 'string'},\n            'exclude': {'type': 'string', 'default': '^$'},\n            'language': {'type': 'string'},\n            'language_version': {'type': 'string', 'default': 'default'},\n            'files': {'type': 'string'},\n            'expected_return_value': {'type': 'number', 'default': 0},\n            'args': {\n                'type': 'array',\n                'default': [],\n                'items': {\n                    'type': 'string',\n                },\n            },\n        },\n        'required': ['id', 'name', 'entry', 'language', 'files'],\n    },\n}\n\n\ndef validate_languages(hook_config):\n    if hook_config['language'] not in all_languages:\n        raise InvalidManifestError(\n            'Expected language {0} for {1} to be one of {2!r}'.format(\n                hook_config['id'],\n                hook_config['language'],\n                all_languages,\n            )\n        )\n\n\ndef validate_files(hook_config):\n    if not is_regex_valid(hook_config['files']):\n        raise InvalidManifestError(\n            'Invalid files regex at {0}: {1}'.format(\n                hook_config['id'], hook_config['files'],\n            )\n        )\n\n    if not is_regex_valid(hook_config.get('exclude', '')):\n        raise InvalidManifestError(\n            'Invalid exclude regex at {0}: {1}'.format(\n                hook_config['id'], hook_config['exclude'],\n            )\n        )\n\n\ndef additional_manifest_check(obj):\n    for hook_config in obj:\n        validate_languages(hook_config)\n        validate_files(hook_config)\n\n\nload_manifest = get_validator(\n    MANIFEST_JSON_SCHEMA,\n    InvalidManifestError,\n    additional_manifest_check,\n)\n\n\nrun = get_run_function(\n    'Manifest filenames.',\n    load_manifest,\n    InvalidManifestError,\n)\n\n\nif __name__ == '__main__':\n    exit(run())\n", "path": "pre_commit/clientlib/validate_manifest.py"}]}