We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Does not work within submodules
I'm getting: 

```
An unexpected error has occurred: NotADirectoryError: [Errno 20] Not a directory: '/home/quentin/chef-repo/cookbooks/ssmtp-cookbook/.git/hooks/pre-commit'
```

chef-repo is my primary repository and ssmtp-cookbook a git submodule of that. 

**ssmtp-cookbook/.git file contents:**

```
gitdir: ../../.git/modules/cookbooks/ssmtp-cookbook
```


--- 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/git.py`
Content:
```
1 from __future__ import unicode_literals
2 
3 import functools
4 import logging
5 import os
6 import os.path
7 import re
8 
9 from pre_commit.errors import FatalError
10 from pre_commit.util import CalledProcessError
11 from pre_commit.util import cmd_output
12 from pre_commit.util import memoize_by_cwd
13 
14 
15 logger = logging.getLogger('pre_commit')
16 
17 
18 def get_root():
19     try:
20         return cmd_output('git', 'rev-parse', '--show-toplevel')[1].strip()
21     except CalledProcessError:
22         raise FatalError(
23             'Called from outside of the gits.  Please cd to a git repository.'
24         )
25 
26 
27 def is_in_merge_conflict():
28     return (
29         os.path.exists(os.path.join('.git', 'MERGE_MSG')) and
30         os.path.exists(os.path.join('.git', 'MERGE_HEAD'))
31     )
32 
33 
34 def parse_merge_msg_for_conflicts(merge_msg):
35     # Conflicted files start with tabs
36     return [
37         line.lstrip('#').strip()
38         for line in merge_msg.splitlines()
39         # '#\t' for git 2.4.1
40         if line.startswith(('\t', '#\t'))
41     ]
42 
43 
44 @memoize_by_cwd
45 def get_conflicted_files():
46     logger.info('Checking merge-conflict files only.')
47     # Need to get the conflicted files from the MERGE_MSG because they could
48     # have resolved the conflict by choosing one side or the other
49     merge_msg = open(os.path.join('.git', 'MERGE_MSG')).read()
50     merge_conflict_filenames = parse_merge_msg_for_conflicts(merge_msg)
51 
52     # This will get the rest of the changes made after the merge.
53     # If they resolved the merge conflict by choosing a mesh of both sides
54     # this will also include the conflicted files
55     tree_hash = cmd_output('git', 'write-tree')[1].strip()
56     merge_diff_filenames = cmd_output(
57         'git', 'diff', '-m', tree_hash, 'HEAD', 'MERGE_HEAD', '--name-only',
58     )[1].splitlines()
59     return set(merge_conflict_filenames) | set(merge_diff_filenames)
60 
61 
62 @memoize_by_cwd
63 def get_staged_files():
64     return cmd_output('git', 'diff', '--staged', '--name-only')[1].splitlines()
65 
66 
67 @memoize_by_cwd
68 def get_all_files():
69     return cmd_output('git', 'ls-files')[1].splitlines()
70 
71 
72 def get_files_matching(all_file_list_strategy):
73     @functools.wraps(all_file_list_strategy)
74     @memoize_by_cwd
75     def wrapper(include_expr, exclude_expr):
76         include_regex = re.compile(include_expr)
77         exclude_regex = re.compile(exclude_expr)
78         return set(
79             filename
80             for filename in all_file_list_strategy()
81             if (
82                 include_regex.search(filename) and
83                 not exclude_regex.search(filename) and
84                 os.path.exists(filename)
85             )
86         )
87     return wrapper
88 
89 
90 get_staged_files_matching = get_files_matching(get_staged_files)
91 get_all_files_matching = get_files_matching(get_all_files)
92 get_conflicted_files_matching = get_files_matching(get_conflicted_files)
93
```

Path: `pre_commit/runner.py`
Content:
```
1 from __future__ import unicode_literals
2 
3 import os
4 import os.path
5 
6 from cached_property import cached_property
7 
8 import pre_commit.constants as C
9 from pre_commit import git
10 from pre_commit.clientlib.validate_config import load_config
11 from pre_commit.repository import Repository
12 from pre_commit.store import Store
13 
14 
15 class Runner(object):
16     """A `Runner` represents the execution context of the hooks.  Notably the
17     repository under test.
18     """
19 
20     def __init__(self, git_root):
21         self.git_root = git_root
22 
23     @classmethod
24     def create(cls):
25         """Creates a PreCommitRunner by doing the following:
26             - Finds the root of the current git repository
27             - chdirs to that directory
28         """
29         root = git.get_root()
30         os.chdir(root)
31         return cls(root)
32 
33     @cached_property
34     def config_file_path(self):
35         return os.path.join(self.git_root, C.CONFIG_FILE)
36 
37     @cached_property
38     def repositories(self):
39         """Returns a tuple of the configured repositories."""
40         config = load_config(self.config_file_path)
41         repositories = tuple(Repository.create(x, self.store) for x in config)
42         for repository in repositories:
43             repository.require_installed()
44         return repositories
45 
46     def get_hook_path(self, hook_type):
47         return os.path.join(self.git_root, '.git', 'hooks', hook_type)
48 
49     @cached_property
50     def pre_commit_path(self):
51         return self.get_hook_path('pre-commit')
52 
53     @cached_property
54     def pre_push_path(self):
55         return self.get_hook_path('pre-push')
56 
57     @cached_property
58     def cmd_runner(self):
59         # TODO: remove this and inline runner.store.cmd_runner
60         return self.store.cmd_runner
61 
62     @cached_property
63     def store(self):
64         return Store()
65
```

Path: `pre_commit/main.py`
Content:
```
1 from __future__ import unicode_literals
2 
3 import argparse
4 import os
5 import sys
6 
7 import pkg_resources
8 
9 from pre_commit import color
10 from pre_commit import five
11 from pre_commit.commands.autoupdate import autoupdate
12 from pre_commit.commands.clean import clean
13 from pre_commit.commands.install_uninstall import install
14 from pre_commit.commands.install_uninstall import uninstall
15 from pre_commit.commands.run import run
16 from pre_commit.error_handler import error_handler
17 from pre_commit.runner import Runner
18 
19 
20 # https://github.com/pre-commit/pre-commit/issues/217
21 # On OSX, making a virtualenv using pyvenv at . causes `virtualenv` and `pip`
22 # to install packages to the wrong place.  We don't want anything to deal with
23 # pyvenv
24 os.environ.pop('__PYVENV_LAUNCHER__', None)
25 # https://github.com/pre-commit/pre-commit/issues/300
26 # In git 2.6.3 (maybe others), git exports this while running pre-commit hooks
27 os.environ.pop('GIT_WORK_TREE', None)
28 
29 
30 def main(argv=None):
31     argv = argv if argv is not None else sys.argv[1:]
32     argv = [five.to_text(arg) for arg in argv]
33     parser = argparse.ArgumentParser()
34 
35     # http://stackoverflow.com/a/8521644/812183
36     parser.add_argument(
37         '-V', '--version',
38         action='version',
39         version='%(prog)s {0}'.format(
40             pkg_resources.get_distribution('pre-commit').version
41         )
42     )
43 
44     subparsers = parser.add_subparsers(dest='command')
45 
46     install_parser = subparsers.add_parser(
47         'install', help='Install the pre-commit script.',
48     )
49     install_parser.add_argument(
50         '-f', '--overwrite', action='store_true',
51         help='Overwrite existing hooks / remove migration mode.',
52     )
53     install_parser.add_argument(
54         '--install-hooks', action='store_true',
55         help=(
56             'Whether to install hook environments for all environments '
57             'in the config file.'
58         ),
59     )
60     install_parser.add_argument(
61         '-t', '--hook-type', choices=('pre-commit', 'pre-push'),
62         default='pre-commit',
63     )
64 
65     uninstall_parser = subparsers.add_parser(
66         'uninstall', help='Uninstall the pre-commit script.',
67     )
68     uninstall_parser.add_argument(
69         '-t', '--hook-type', choices=('pre-commit', 'pre-push'),
70         default='pre-commit',
71     )
72 
73     subparsers.add_parser('clean', help='Clean out pre-commit files.')
74 
75     subparsers.add_parser(
76         'autoupdate',
77         help="Auto-update pre-commit config to the latest repos' versions.",
78     )
79 
80     run_parser = subparsers.add_parser('run', help='Run hooks.')
81     run_parser.add_argument('hook', nargs='?', help='A single hook-id to run')
82     run_parser.add_argument(
83         '--color', default='auto', type=color.use_color,
84         help='Whether to use color in output.  Defaults to `auto`',
85     )
86     run_parser.add_argument(
87         '--no-stash', default=False, action='store_true',
88         help='Use this option to prevent auto stashing of unstaged files.',
89     )
90     run_parser.add_argument(
91         '--verbose', '-v', action='store_true', default=False,
92     )
93     run_parser.add_argument(
94         '--origin', '-o',
95         help='The origin branch\'s commit_id when using `git push`',
96     )
97     run_parser.add_argument(
98         '--source', '-s',
99         help='The remote branch\'s commit_id when using `git push`',
100     )
101     run_parser.add_argument(
102         '--allow-unstaged-config', default=False, action='store_true',
103         help='Allow an unstaged config to be present.  Note that this will'
104         'be stashed before parsing unless --no-stash is specified'
105     )
106     run_parser.add_argument(
107         '--hook-stage', choices=('commit', 'push'), default='commit',
108         help='The stage during which the hook is fired e.g. commit or push',
109     )
110     run_mutex_group = run_parser.add_mutually_exclusive_group(required=False)
111     run_mutex_group.add_argument(
112         '--all-files', '-a', action='store_true', default=False,
113         help='Run on all the files in the repo.  Implies --no-stash.',
114     )
115     run_mutex_group.add_argument(
116         '--files', nargs='*', help='Specific filenames to run hooks on.',
117     )
118 
119     help = subparsers.add_parser(
120         'help', help='Show help for a specific command.'
121     )
122     help.add_argument('help_cmd', nargs='?', help='Command to show help for.')
123 
124     # Argparse doesn't really provide a way to use a `default` subparser
125     if len(argv) == 0:
126         argv = ['run']
127     args = parser.parse_args(argv)
128 
129     if args.command == 'help':
130         if args.help_cmd:
131             parser.parse_args([args.help_cmd, '--help'])
132         else:
133             parser.parse_args(['--help'])
134 
135     with error_handler():
136         runner = Runner.create()
137 
138         if args.command == 'install':
139             return install(
140                 runner, overwrite=args.overwrite, hooks=args.install_hooks,
141                 hook_type=args.hook_type,
142             )
143         elif args.command == 'uninstall':
144             return uninstall(runner, hook_type=args.hook_type)
145         elif args.command == 'clean':
146             return clean(runner)
147         elif args.command == 'autoupdate':
148             return autoupdate(runner)
149         elif args.command == 'run':
150             return run(runner, args)
151         else:
152             raise NotImplementedError(
153                 'Command {0} not implemented.'.format(args.command)
154             )
155 
156         raise AssertionError(
157             'Command {0} failed to exit with a returncode'.format(args.command)
158         )
159 
160 
161 if __name__ == '__main__':
162     exit(main())
163
```
--- 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/git.py b/pre_commit/git.py
--- a/pre_commit/git.py
+++ b/pre_commit/git.py
@@ -24,10 +24,18 @@
         )
 
 
+def get_git_dir(git_root):
+    return os.path.normpath(os.path.join(
+        git_root,
+        cmd_output('git', 'rev-parse', '--git-dir', cwd=git_root)[1].strip(),
+    ))
+
+
 def is_in_merge_conflict():
+    git_dir = get_git_dir('.')
     return (
-        os.path.exists(os.path.join('.git', 'MERGE_MSG')) and
-        os.path.exists(os.path.join('.git', 'MERGE_HEAD'))
+        os.path.exists(os.path.join(git_dir, 'MERGE_MSG')) and
+        os.path.exists(os.path.join(git_dir, 'MERGE_HEAD'))
     )
 
 
@@ -46,7 +54,7 @@
     logger.info('Checking merge-conflict files only.')
     # Need to get the conflicted files from the MERGE_MSG because they could
     # have resolved the conflict by choosing one side or the other
-    merge_msg = open(os.path.join('.git', 'MERGE_MSG')).read()
+    merge_msg = open(os.path.join(get_git_dir('.'), 'MERGE_MSG')).read()
     merge_conflict_filenames = parse_merge_msg_for_conflicts(merge_msg)
 
     # This will get the rest of the changes made after the merge.
diff --git a/pre_commit/main.py b/pre_commit/main.py
--- a/pre_commit/main.py
+++ b/pre_commit/main.py
@@ -25,6 +25,13 @@
 # https://github.com/pre-commit/pre-commit/issues/300
 # In git 2.6.3 (maybe others), git exports this while running pre-commit hooks
 os.environ.pop('GIT_WORK_TREE', None)
+# In git 1.9.1 (maybe others), git exports these while running pre-commit hooks
+# in submodules.  In the general case this causes problems.
+# These are covered by test_install_in_submodule_and_run
+# Causes git clone to clone wrong thing
+os.environ.pop('GIT_DIR', None)
+# Causes 'error invalid object ...' during commit
+os.environ.pop('GIT_INDEX_FILE', None)
 
 
 def main(argv=None):
diff --git a/pre_commit/runner.py b/pre_commit/runner.py
--- a/pre_commit/runner.py
+++ b/pre_commit/runner.py
@@ -30,6 +30,10 @@
         os.chdir(root)
         return cls(root)
 
+    @cached_property
+    def git_dir(self):
+        return git.get_git_dir(self.git_root)
+
     @cached_property
     def config_file_path(self):
         return os.path.join(self.git_root, C.CONFIG_FILE)
@@ -44,7 +48,7 @@
         return repositories
 
     def get_hook_path(self, hook_type):
-        return os.path.join(self.git_root, '.git', 'hooks', hook_type)
+        return os.path.join(self.git_dir, 'hooks', hook_type)
 
     @cached_property
     def pre_commit_path(self):

{"golden_diff": "diff --git a/pre_commit/git.py b/pre_commit/git.py\n--- a/pre_commit/git.py\n+++ b/pre_commit/git.py\n@@ -24,10 +24,18 @@\n         )\n \n \n+def get_git_dir(git_root):\n+    return os.path.normpath(os.path.join(\n+        git_root,\n+        cmd_output('git', 'rev-parse', '--git-dir', cwd=git_root)[1].strip(),\n+    ))\n+\n+\n def is_in_merge_conflict():\n+    git_dir = get_git_dir('.')\n     return (\n-        os.path.exists(os.path.join('.git', 'MERGE_MSG')) and\n-        os.path.exists(os.path.join('.git', 'MERGE_HEAD'))\n+        os.path.exists(os.path.join(git_dir, 'MERGE_MSG')) and\n+        os.path.exists(os.path.join(git_dir, 'MERGE_HEAD'))\n     )\n \n \n@@ -46,7 +54,7 @@\n     logger.info('Checking merge-conflict files only.')\n     # Need to get the conflicted files from the MERGE_MSG because they could\n     # have resolved the conflict by choosing one side or the other\n-    merge_msg = open(os.path.join('.git', 'MERGE_MSG')).read()\n+    merge_msg = open(os.path.join(get_git_dir('.'), 'MERGE_MSG')).read()\n     merge_conflict_filenames = parse_merge_msg_for_conflicts(merge_msg)\n \n     # This will get the rest of the changes made after the merge.\ndiff --git a/pre_commit/main.py b/pre_commit/main.py\n--- a/pre_commit/main.py\n+++ b/pre_commit/main.py\n@@ -25,6 +25,13 @@\n # https://github.com/pre-commit/pre-commit/issues/300\n # In git 2.6.3 (maybe others), git exports this while running pre-commit hooks\n os.environ.pop('GIT_WORK_TREE', None)\n+# In git 1.9.1 (maybe others), git exports these while running pre-commit hooks\n+# in submodules.  In the general case this causes problems.\n+# These are covered by test_install_in_submodule_and_run\n+# Causes git clone to clone wrong thing\n+os.environ.pop('GIT_DIR', None)\n+# Causes 'error invalid object ...' during commit\n+os.environ.pop('GIT_INDEX_FILE', None)\n \n \n def main(argv=None):\ndiff --git a/pre_commit/runner.py b/pre_commit/runner.py\n--- a/pre_commit/runner.py\n+++ b/pre_commit/runner.py\n@@ -30,6 +30,10 @@\n         os.chdir(root)\n         return cls(root)\n \n+    @cached_property\n+    def git_dir(self):\n+        return git.get_git_dir(self.git_root)\n+\n     @cached_property\n     def config_file_path(self):\n         return os.path.join(self.git_root, C.CONFIG_FILE)\n@@ -44,7 +48,7 @@\n         return repositories\n \n     def get_hook_path(self, hook_type):\n-        return os.path.join(self.git_root, '.git', 'hooks', hook_type)\n+        return os.path.join(self.git_dir, 'hooks', hook_type)\n \n     @cached_property\n     def pre_commit_path(self):\n", "issue": "Does not work within submodules\nI'm getting: \n\n```\nAn unexpected error has occurred: NotADirectoryError: [Errno 20] Not a directory: '/home/quentin/chef-repo/cookbooks/ssmtp-cookbook/.git/hooks/pre-commit'\n```\n\nchef-repo is my primary repository and ssmtp-cookbook a git submodule of that. \n\n**ssmtp-cookbook/.git file contents:**\n\n```\ngitdir: ../../.git/modules/cookbooks/ssmtp-cookbook\n```\n\n", "before_files": [{"content": "from __future__ import unicode_literals\n\nimport functools\nimport logging\nimport os\nimport os.path\nimport re\n\nfrom pre_commit.errors import FatalError\nfrom pre_commit.util import CalledProcessError\nfrom pre_commit.util import cmd_output\nfrom pre_commit.util import memoize_by_cwd\n\n\nlogger = logging.getLogger('pre_commit')\n\n\ndef get_root():\n    try:\n        return cmd_output('git', 'rev-parse', '--show-toplevel')[1].strip()\n    except CalledProcessError:\n        raise FatalError(\n            'Called from outside of the gits.  Please cd to a git repository.'\n        )\n\n\ndef is_in_merge_conflict():\n    return (\n        os.path.exists(os.path.join('.git', 'MERGE_MSG')) and\n        os.path.exists(os.path.join('.git', 'MERGE_HEAD'))\n    )\n\n\ndef parse_merge_msg_for_conflicts(merge_msg):\n    # Conflicted files start with tabs\n    return [\n        line.lstrip('#').strip()\n        for line in merge_msg.splitlines()\n        # '#\\t' for git 2.4.1\n        if line.startswith(('\\t', '#\\t'))\n    ]\n\n\n@memoize_by_cwd\ndef get_conflicted_files():\n    logger.info('Checking merge-conflict files only.')\n    # Need to get the conflicted files from the MERGE_MSG because they could\n    # have resolved the conflict by choosing one side or the other\n    merge_msg = open(os.path.join('.git', 'MERGE_MSG')).read()\n    merge_conflict_filenames = parse_merge_msg_for_conflicts(merge_msg)\n\n    # This will get the rest of the changes made after the merge.\n    # If they resolved the merge conflict by choosing a mesh of both sides\n    # this will also include the conflicted files\n    tree_hash = cmd_output('git', 'write-tree')[1].strip()\n    merge_diff_filenames = cmd_output(\n        'git', 'diff', '-m', tree_hash, 'HEAD', 'MERGE_HEAD', '--name-only',\n    )[1].splitlines()\n    return set(merge_conflict_filenames) | set(merge_diff_filenames)\n\n\n@memoize_by_cwd\ndef get_staged_files():\n    return cmd_output('git', 'diff', '--staged', '--name-only')[1].splitlines()\n\n\n@memoize_by_cwd\ndef get_all_files():\n    return cmd_output('git', 'ls-files')[1].splitlines()\n\n\ndef get_files_matching(all_file_list_strategy):\n    @functools.wraps(all_file_list_strategy)\n    @memoize_by_cwd\n    def wrapper(include_expr, exclude_expr):\n        include_regex = re.compile(include_expr)\n        exclude_regex = re.compile(exclude_expr)\n        return set(\n            filename\n            for filename in all_file_list_strategy()\n            if (\n                include_regex.search(filename) and\n                not exclude_regex.search(filename) and\n                os.path.exists(filename)\n            )\n        )\n    return wrapper\n\n\nget_staged_files_matching = get_files_matching(get_staged_files)\nget_all_files_matching = get_files_matching(get_all_files)\nget_conflicted_files_matching = get_files_matching(get_conflicted_files)\n", "path": "pre_commit/git.py"}, {"content": "from __future__ import unicode_literals\n\nimport os\nimport os.path\n\nfrom cached_property import cached_property\n\nimport pre_commit.constants as C\nfrom pre_commit import git\nfrom pre_commit.clientlib.validate_config import load_config\nfrom pre_commit.repository import Repository\nfrom pre_commit.store import Store\n\n\nclass Runner(object):\n    \"\"\"A `Runner` represents the execution context of the hooks.  Notably the\n    repository under test.\n    \"\"\"\n\n    def __init__(self, git_root):\n        self.git_root = git_root\n\n    @classmethod\n    def create(cls):\n        \"\"\"Creates a PreCommitRunner by doing the following:\n            - Finds the root of the current git repository\n            - chdirs to that directory\n        \"\"\"\n        root = git.get_root()\n        os.chdir(root)\n        return cls(root)\n\n    @cached_property\n    def config_file_path(self):\n        return os.path.join(self.git_root, C.CONFIG_FILE)\n\n    @cached_property\n    def repositories(self):\n        \"\"\"Returns a tuple of the configured repositories.\"\"\"\n        config = load_config(self.config_file_path)\n        repositories = tuple(Repository.create(x, self.store) for x in config)\n        for repository in repositories:\n            repository.require_installed()\n        return repositories\n\n    def get_hook_path(self, hook_type):\n        return os.path.join(self.git_root, '.git', 'hooks', hook_type)\n\n    @cached_property\n    def pre_commit_path(self):\n        return self.get_hook_path('pre-commit')\n\n    @cached_property\n    def pre_push_path(self):\n        return self.get_hook_path('pre-push')\n\n    @cached_property\n    def cmd_runner(self):\n        # TODO: remove this and inline runner.store.cmd_runner\n        return self.store.cmd_runner\n\n    @cached_property\n    def store(self):\n        return Store()\n", "path": "pre_commit/runner.py"}, {"content": "from __future__ import unicode_literals\n\nimport argparse\nimport os\nimport sys\n\nimport pkg_resources\n\nfrom pre_commit import color\nfrom pre_commit import five\nfrom pre_commit.commands.autoupdate import autoupdate\nfrom pre_commit.commands.clean import clean\nfrom pre_commit.commands.install_uninstall import install\nfrom pre_commit.commands.install_uninstall import uninstall\nfrom pre_commit.commands.run import run\nfrom pre_commit.error_handler import error_handler\nfrom pre_commit.runner import Runner\n\n\n# https://github.com/pre-commit/pre-commit/issues/217\n# On OSX, making a virtualenv using pyvenv at . causes `virtualenv` and `pip`\n# to install packages to the wrong place.  We don't want anything to deal with\n# pyvenv\nos.environ.pop('__PYVENV_LAUNCHER__', None)\n# https://github.com/pre-commit/pre-commit/issues/300\n# In git 2.6.3 (maybe others), git exports this while running pre-commit hooks\nos.environ.pop('GIT_WORK_TREE', None)\n\n\ndef main(argv=None):\n    argv = argv if argv is not None else sys.argv[1:]\n    argv = [five.to_text(arg) for arg in argv]\n    parser = argparse.ArgumentParser()\n\n    # http://stackoverflow.com/a/8521644/812183\n    parser.add_argument(\n        '-V', '--version',\n        action='version',\n        version='%(prog)s {0}'.format(\n            pkg_resources.get_distribution('pre-commit').version\n        )\n    )\n\n    subparsers = parser.add_subparsers(dest='command')\n\n    install_parser = subparsers.add_parser(\n        'install', help='Install the pre-commit script.',\n    )\n    install_parser.add_argument(\n        '-f', '--overwrite', action='store_true',\n        help='Overwrite existing hooks / remove migration mode.',\n    )\n    install_parser.add_argument(\n        '--install-hooks', action='store_true',\n        help=(\n            'Whether to install hook environments for all environments '\n            'in the config file.'\n        ),\n    )\n    install_parser.add_argument(\n        '-t', '--hook-type', choices=('pre-commit', 'pre-push'),\n        default='pre-commit',\n    )\n\n    uninstall_parser = subparsers.add_parser(\n        'uninstall', help='Uninstall the pre-commit script.',\n    )\n    uninstall_parser.add_argument(\n        '-t', '--hook-type', choices=('pre-commit', 'pre-push'),\n        default='pre-commit',\n    )\n\n    subparsers.add_parser('clean', help='Clean out pre-commit files.')\n\n    subparsers.add_parser(\n        'autoupdate',\n        help=\"Auto-update pre-commit config to the latest repos' versions.\",\n    )\n\n    run_parser = subparsers.add_parser('run', help='Run hooks.')\n    run_parser.add_argument('hook', nargs='?', help='A single hook-id to run')\n    run_parser.add_argument(\n        '--color', default='auto', type=color.use_color,\n        help='Whether to use color in output.  Defaults to `auto`',\n    )\n    run_parser.add_argument(\n        '--no-stash', default=False, action='store_true',\n        help='Use this option to prevent auto stashing of unstaged files.',\n    )\n    run_parser.add_argument(\n        '--verbose', '-v', action='store_true', default=False,\n    )\n    run_parser.add_argument(\n        '--origin', '-o',\n        help='The origin branch\\'s commit_id when using `git push`',\n    )\n    run_parser.add_argument(\n        '--source', '-s',\n        help='The remote branch\\'s commit_id when using `git push`',\n    )\n    run_parser.add_argument(\n        '--allow-unstaged-config', default=False, action='store_true',\n        help='Allow an unstaged config to be present.  Note that this will'\n        'be stashed before parsing unless --no-stash is specified'\n    )\n    run_parser.add_argument(\n        '--hook-stage', choices=('commit', 'push'), default='commit',\n        help='The stage during which the hook is fired e.g. commit or push',\n    )\n    run_mutex_group = run_parser.add_mutually_exclusive_group(required=False)\n    run_mutex_group.add_argument(\n        '--all-files', '-a', action='store_true', default=False,\n        help='Run on all the files in the repo.  Implies --no-stash.',\n    )\n    run_mutex_group.add_argument(\n        '--files', nargs='*', help='Specific filenames to run hooks on.',\n    )\n\n    help = subparsers.add_parser(\n        'help', help='Show help for a specific command.'\n    )\n    help.add_argument('help_cmd', nargs='?', help='Command to show help for.')\n\n    # Argparse doesn't really provide a way to use a `default` subparser\n    if len(argv) == 0:\n        argv = ['run']\n    args = parser.parse_args(argv)\n\n    if args.command == 'help':\n        if args.help_cmd:\n            parser.parse_args([args.help_cmd, '--help'])\n        else:\n            parser.parse_args(['--help'])\n\n    with error_handler():\n        runner = Runner.create()\n\n        if args.command == 'install':\n            return install(\n                runner, overwrite=args.overwrite, hooks=args.install_hooks,\n                hook_type=args.hook_type,\n            )\n        elif args.command == 'uninstall':\n            return uninstall(runner, hook_type=args.hook_type)\n        elif args.command == 'clean':\n            return clean(runner)\n        elif args.command == 'autoupdate':\n            return autoupdate(runner)\n        elif args.command == 'run':\n            return run(runner, args)\n        else:\n            raise NotImplementedError(\n                'Command {0} not implemented.'.format(args.command)\n            )\n\n        raise AssertionError(\n            'Command {0} failed to exit with a returncode'.format(args.command)\n        )\n\n\nif __name__ == '__main__':\n    exit(main())\n", "path": "pre_commit/main.py"}], "after_files": [{"content": "from __future__ import unicode_literals\n\nimport functools\nimport logging\nimport os\nimport os.path\nimport re\n\nfrom pre_commit.errors import FatalError\nfrom pre_commit.util import CalledProcessError\nfrom pre_commit.util import cmd_output\nfrom pre_commit.util import memoize_by_cwd\n\n\nlogger = logging.getLogger('pre_commit')\n\n\ndef get_root():\n    try:\n        return cmd_output('git', 'rev-parse', '--show-toplevel')[1].strip()\n    except CalledProcessError:\n        raise FatalError(\n            'Called from outside of the gits.  Please cd to a git repository.'\n        )\n\n\ndef get_git_dir(git_root):\n    return os.path.normpath(os.path.join(\n        git_root,\n        cmd_output('git', 'rev-parse', '--git-dir', cwd=git_root)[1].strip(),\n    ))\n\n\ndef is_in_merge_conflict():\n    git_dir = get_git_dir('.')\n    return (\n        os.path.exists(os.path.join(git_dir, 'MERGE_MSG')) and\n        os.path.exists(os.path.join(git_dir, 'MERGE_HEAD'))\n    )\n\n\ndef parse_merge_msg_for_conflicts(merge_msg):\n    # Conflicted files start with tabs\n    return [\n        line.lstrip('#').strip()\n        for line in merge_msg.splitlines()\n        # '#\\t' for git 2.4.1\n        if line.startswith(('\\t', '#\\t'))\n    ]\n\n\n@memoize_by_cwd\ndef get_conflicted_files():\n    logger.info('Checking merge-conflict files only.')\n    # Need to get the conflicted files from the MERGE_MSG because they could\n    # have resolved the conflict by choosing one side or the other\n    merge_msg = open(os.path.join(get_git_dir('.'), 'MERGE_MSG')).read()\n    merge_conflict_filenames = parse_merge_msg_for_conflicts(merge_msg)\n\n    # This will get the rest of the changes made after the merge.\n    # If they resolved the merge conflict by choosing a mesh of both sides\n    # this will also include the conflicted files\n    tree_hash = cmd_output('git', 'write-tree')[1].strip()\n    merge_diff_filenames = cmd_output(\n        'git', 'diff', '-m', tree_hash, 'HEAD', 'MERGE_HEAD', '--name-only',\n    )[1].splitlines()\n    return set(merge_conflict_filenames) | set(merge_diff_filenames)\n\n\n@memoize_by_cwd\ndef get_staged_files():\n    return cmd_output('git', 'diff', '--staged', '--name-only')[1].splitlines()\n\n\n@memoize_by_cwd\ndef get_all_files():\n    return cmd_output('git', 'ls-files')[1].splitlines()\n\n\ndef get_files_matching(all_file_list_strategy):\n    @functools.wraps(all_file_list_strategy)\n    @memoize_by_cwd\n    def wrapper(include_expr, exclude_expr):\n        include_regex = re.compile(include_expr)\n        exclude_regex = re.compile(exclude_expr)\n        return set(\n            filename\n            for filename in all_file_list_strategy()\n            if (\n                include_regex.search(filename) and\n                not exclude_regex.search(filename) and\n                os.path.exists(filename)\n            )\n        )\n    return wrapper\n\n\nget_staged_files_matching = get_files_matching(get_staged_files)\nget_all_files_matching = get_files_matching(get_all_files)\nget_conflicted_files_matching = get_files_matching(get_conflicted_files)\n", "path": "pre_commit/git.py"}, {"content": "from __future__ import unicode_literals\n\nimport os\nimport os.path\n\nfrom cached_property import cached_property\n\nimport pre_commit.constants as C\nfrom pre_commit import git\nfrom pre_commit.clientlib.validate_config import load_config\nfrom pre_commit.repository import Repository\nfrom pre_commit.store import Store\n\n\nclass Runner(object):\n    \"\"\"A `Runner` represents the execution context of the hooks.  Notably the\n    repository under test.\n    \"\"\"\n\n    def __init__(self, git_root):\n        self.git_root = git_root\n\n    @classmethod\n    def create(cls):\n        \"\"\"Creates a PreCommitRunner by doing the following:\n            - Finds the root of the current git repository\n            - chdirs to that directory\n        \"\"\"\n        root = git.get_root()\n        os.chdir(root)\n        return cls(root)\n\n    @cached_property\n    def git_dir(self):\n        return git.get_git_dir(self.git_root)\n\n    @cached_property\n    def config_file_path(self):\n        return os.path.join(self.git_root, C.CONFIG_FILE)\n\n    @cached_property\n    def repositories(self):\n        \"\"\"Returns a tuple of the configured repositories.\"\"\"\n        config = load_config(self.config_file_path)\n        repositories = tuple(Repository.create(x, self.store) for x in config)\n        for repository in repositories:\n            repository.require_installed()\n        return repositories\n\n    def get_hook_path(self, hook_type):\n        return os.path.join(self.git_dir, 'hooks', hook_type)\n\n    @cached_property\n    def pre_commit_path(self):\n        return self.get_hook_path('pre-commit')\n\n    @cached_property\n    def pre_push_path(self):\n        return self.get_hook_path('pre-push')\n\n    @cached_property\n    def cmd_runner(self):\n        # TODO: remove this and inline runner.store.cmd_runner\n        return self.store.cmd_runner\n\n    @cached_property\n    def store(self):\n        return Store()\n", "path": "pre_commit/runner.py"}, {"content": "from __future__ import unicode_literals\n\nimport argparse\nimport os\nimport sys\n\nimport pkg_resources\n\nfrom pre_commit import color\nfrom pre_commit import five\nfrom pre_commit.commands.autoupdate import autoupdate\nfrom pre_commit.commands.clean import clean\nfrom pre_commit.commands.install_uninstall import install\nfrom pre_commit.commands.install_uninstall import uninstall\nfrom pre_commit.commands.run import run\nfrom pre_commit.error_handler import error_handler\nfrom pre_commit.runner import Runner\n\n\n# https://github.com/pre-commit/pre-commit/issues/217\n# On OSX, making a virtualenv using pyvenv at . causes `virtualenv` and `pip`\n# to install packages to the wrong place.  We don't want anything to deal with\n# pyvenv\nos.environ.pop('__PYVENV_LAUNCHER__', None)\n# https://github.com/pre-commit/pre-commit/issues/300\n# In git 2.6.3 (maybe others), git exports this while running pre-commit hooks\nos.environ.pop('GIT_WORK_TREE', None)\n# In git 1.9.1 (maybe others), git exports these while running pre-commit hooks\n# in submodules.  In the general case this causes problems.\n# These are covered by test_install_in_submodule_and_run\n# Causes git clone to clone wrong thing\nos.environ.pop('GIT_DIR', None)\n# Causes 'error invalid object ...' during commit\nos.environ.pop('GIT_INDEX_FILE', None)\n\n\ndef main(argv=None):\n    argv = argv if argv is not None else sys.argv[1:]\n    argv = [five.to_text(arg) for arg in argv]\n    parser = argparse.ArgumentParser()\n\n    # http://stackoverflow.com/a/8521644/812183\n    parser.add_argument(\n        '-V', '--version',\n        action='version',\n        version='%(prog)s {0}'.format(\n            pkg_resources.get_distribution('pre-commit').version\n        )\n    )\n\n    subparsers = parser.add_subparsers(dest='command')\n\n    install_parser = subparsers.add_parser(\n        'install', help='Install the pre-commit script.',\n    )\n    install_parser.add_argument(\n        '-f', '--overwrite', action='store_true',\n        help='Overwrite existing hooks / remove migration mode.',\n    )\n    install_parser.add_argument(\n        '--install-hooks', action='store_true',\n        help=(\n            'Whether to install hook environments for all environments '\n            'in the config file.'\n        ),\n    )\n    install_parser.add_argument(\n        '-t', '--hook-type', choices=('pre-commit', 'pre-push'),\n        default='pre-commit',\n    )\n\n    uninstall_parser = subparsers.add_parser(\n        'uninstall', help='Uninstall the pre-commit script.',\n    )\n    uninstall_parser.add_argument(\n        '-t', '--hook-type', choices=('pre-commit', 'pre-push'),\n        default='pre-commit',\n    )\n\n    subparsers.add_parser('clean', help='Clean out pre-commit files.')\n\n    subparsers.add_parser(\n        'autoupdate',\n        help=\"Auto-update pre-commit config to the latest repos' versions.\",\n    )\n\n    run_parser = subparsers.add_parser('run', help='Run hooks.')\n    run_parser.add_argument('hook', nargs='?', help='A single hook-id to run')\n    run_parser.add_argument(\n        '--color', default='auto', type=color.use_color,\n        help='Whether to use color in output.  Defaults to `auto`',\n    )\n    run_parser.add_argument(\n        '--no-stash', default=False, action='store_true',\n        help='Use this option to prevent auto stashing of unstaged files.',\n    )\n    run_parser.add_argument(\n        '--verbose', '-v', action='store_true', default=False,\n    )\n    run_parser.add_argument(\n        '--origin', '-o',\n        help='The origin branch\\'s commit_id when using `git push`',\n    )\n    run_parser.add_argument(\n        '--source', '-s',\n        help='The remote branch\\'s commit_id when using `git push`',\n    )\n    run_parser.add_argument(\n        '--allow-unstaged-config', default=False, action='store_true',\n        help='Allow an unstaged config to be present.  Note that this will'\n        'be stashed before parsing unless --no-stash is specified'\n    )\n    run_parser.add_argument(\n        '--hook-stage', choices=('commit', 'push'), default='commit',\n        help='The stage during which the hook is fired e.g. commit or push',\n    )\n    run_mutex_group = run_parser.add_mutually_exclusive_group(required=False)\n    run_mutex_group.add_argument(\n        '--all-files', '-a', action='store_true', default=False,\n        help='Run on all the files in the repo.  Implies --no-stash.',\n    )\n    run_mutex_group.add_argument(\n        '--files', nargs='*', help='Specific filenames to run hooks on.',\n    )\n\n    help = subparsers.add_parser(\n        'help', help='Show help for a specific command.'\n    )\n    help.add_argument('help_cmd', nargs='?', help='Command to show help for.')\n\n    # Argparse doesn't really provide a way to use a `default` subparser\n    if len(argv) == 0:\n        argv = ['run']\n    args = parser.parse_args(argv)\n\n    if args.command == 'help':\n        if args.help_cmd:\n            parser.parse_args([args.help_cmd, '--help'])\n        else:\n            parser.parse_args(['--help'])\n\n    with error_handler():\n        runner = Runner.create()\n\n        if args.command == 'install':\n            return install(\n                runner, overwrite=args.overwrite, hooks=args.install_hooks,\n                hook_type=args.hook_type,\n            )\n        elif args.command == 'uninstall':\n            return uninstall(runner, hook_type=args.hook_type)\n        elif args.command == 'clean':\n            return clean(runner)\n        elif args.command == 'autoupdate':\n            return autoupdate(runner)\n        elif args.command == 'run':\n            return run(runner, args)\n        else:\n            raise NotImplementedError(\n                'Command {0} not implemented.'.format(args.command)\n            )\n\n        raise AssertionError(\n            'Command {0} failed to exit with a returncode'.format(args.command)\n        )\n\n\nif __name__ == '__main__':\n    exit(main())\n", "path": "pre_commit/main.py"}]}