We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Investigate / implement shallow cloning
Now that github supports it, might be worth the speed improvements

It would have to be smart enough to fall back to slow clone when the option isn't available (either the client is too old or the server is too old)

--- 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/languages/node.py`
Content:
```
1 from __future__ import unicode_literals
2 
3 import contextlib
4 import os
5 import sys
6 
7 import pre_commit.constants as C
8 from pre_commit.envcontext import envcontext
9 from pre_commit.envcontext import Var
10 from pre_commit.languages import helpers
11 from pre_commit.languages.python import bin_dir
12 from pre_commit.util import clean_path_on_failure
13 from pre_commit.util import cmd_output
14 
15 
16 ENVIRONMENT_DIR = 'node_env'
17 get_default_version = helpers.basic_get_default_version
18 healthy = helpers.basic_healthy
19 
20 
21 def _envdir(prefix, version):
22     directory = helpers.environment_dir(ENVIRONMENT_DIR, version)
23     return prefix.path(directory)
24 
25 
26 def get_env_patch(venv):
27     if sys.platform == 'cygwin':  # pragma: no cover
28         _, win_venv, _ = cmd_output('cygpath', '-w', venv)
29         install_prefix = r'{}\bin'.format(win_venv.strip())
30     elif sys.platform == 'win32':  # pragma: no cover
31         install_prefix = bin_dir(venv)
32     else:  # pragma: windows no cover
33         install_prefix = venv
34     return (
35         ('NODE_VIRTUAL_ENV', venv),
36         ('NPM_CONFIG_PREFIX', install_prefix),
37         ('npm_config_prefix', install_prefix),
38         ('NODE_PATH', os.path.join(venv, 'lib', 'node_modules')),
39         ('PATH', (bin_dir(venv), os.pathsep, Var('PATH'))),
40     )
41 
42 
43 @contextlib.contextmanager
44 def in_env(prefix, language_version):
45     with envcontext(get_env_patch(_envdir(prefix, language_version))):
46         yield
47 
48 
49 def install_environment(prefix, version, additional_dependencies):
50     additional_dependencies = tuple(additional_dependencies)
51     assert prefix.exists('package.json')
52     envdir = _envdir(prefix, version)
53 
54     # https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx?f=255&MSPPError=-2147217396#maxpath
55     if sys.platform == 'win32':  # pragma: no cover
56         envdir = '\\\\?\\' + os.path.normpath(envdir)
57     with clean_path_on_failure(envdir):
58         cmd = [
59             sys.executable, '-mnodeenv', '--prebuilt', '--clean-src', envdir,
60         ]
61         if version != C.DEFAULT:
62             cmd.extend(['-n', version])
63         cmd_output(*cmd)
64 
65         dep = 'git+file:///{}'.format(prefix.prefix_dir)
66         with in_env(prefix, version):
67             helpers.run_setup_cmd(
68                 prefix,
69                 ('npm', 'install', '-g', dep) + additional_dependencies,
70             )
71 
72 
73 def run_hook(hook, file_args):
74     with in_env(hook.prefix, hook.language_version):
75         return helpers.run_xargs(hook, helpers.to_cmd(hook), file_args)
76
```

Path: `pre_commit/store.py`
Content:
```
1 from __future__ import unicode_literals
2 
3 import contextlib
4 import io
5 import logging
6 import os.path
7 import sqlite3
8 import tempfile
9 
10 import pre_commit.constants as C
11 from pre_commit import file_lock
12 from pre_commit import git
13 from pre_commit.util import clean_path_on_failure
14 from pre_commit.util import cmd_output
15 from pre_commit.util import resource_text
16 from pre_commit.util import rmtree
17 
18 
19 logger = logging.getLogger('pre_commit')
20 
21 
22 def _get_default_directory():
23     """Returns the default directory for the Store.  This is intentionally
24     underscored to indicate that `Store.get_default_directory` is the intended
25     way to get this information.  This is also done so
26     `Store.get_default_directory` can be mocked in tests and
27     `_get_default_directory` can be tested.
28     """
29     return os.environ.get('PRE_COMMIT_HOME') or os.path.join(
30         os.environ.get('XDG_CACHE_HOME') or os.path.expanduser('~/.cache'),
31         'pre-commit',
32     )
33 
34 
35 class Store(object):
36     get_default_directory = staticmethod(_get_default_directory)
37 
38     def __init__(self, directory=None):
39         self.directory = directory or Store.get_default_directory()
40         self.db_path = os.path.join(self.directory, 'db.db')
41 
42         if not os.path.exists(self.directory):
43             os.makedirs(self.directory)
44             with io.open(os.path.join(self.directory, 'README'), 'w') as f:
45                 f.write(
46                     'This directory is maintained by the pre-commit project.\n'
47                     'Learn more: https://github.com/pre-commit/pre-commit\n',
48                 )
49 
50         if os.path.exists(self.db_path):
51             return
52         with self.exclusive_lock():
53             # Another process may have already completed this work
54             if os.path.exists(self.db_path):  # pragma: no cover (race)
55                 return
56             # To avoid a race where someone ^Cs between db creation and
57             # execution of the CREATE TABLE statement
58             fd, tmpfile = tempfile.mkstemp(dir=self.directory)
59             # We'll be managing this file ourselves
60             os.close(fd)
61             with self.connect(db_path=tmpfile) as db:
62                 db.executescript(
63                     'CREATE TABLE repos ('
64                     '    repo TEXT NOT NULL,'
65                     '    ref TEXT NOT NULL,'
66                     '    path TEXT NOT NULL,'
67                     '    PRIMARY KEY (repo, ref)'
68                     ');',
69                 )
70                 self._create_config_table_if_not_exists(db)
71 
72             # Atomic file move
73             os.rename(tmpfile, self.db_path)
74 
75     @contextlib.contextmanager
76     def exclusive_lock(self):
77         def blocked_cb():  # pragma: no cover (tests are single-process)
78             logger.info('Locking pre-commit directory')
79 
80         with file_lock.lock(os.path.join(self.directory, '.lock'), blocked_cb):
81             yield
82 
83     @contextlib.contextmanager
84     def connect(self, db_path=None):
85         db_path = db_path or self.db_path
86         # sqlite doesn't close its fd with its contextmanager >.<
87         # contextlib.closing fixes this.
88         # See: https://stackoverflow.com/a/28032829/812183
89         with contextlib.closing(sqlite3.connect(db_path)) as db:
90             # this creates a transaction
91             with db:
92                 yield db
93 
94     @classmethod
95     def db_repo_name(cls, repo, deps):
96         if deps:
97             return '{}:{}'.format(repo, ','.join(sorted(deps)))
98         else:
99             return repo
100 
101     def _new_repo(self, repo, ref, deps, make_strategy):
102         repo = self.db_repo_name(repo, deps)
103 
104         def _get_result():
105             # Check if we already exist
106             with self.connect() as db:
107                 result = db.execute(
108                     'SELECT path FROM repos WHERE repo = ? AND ref = ?',
109                     (repo, ref),
110                 ).fetchone()
111                 if result:
112                     return result[0]
113 
114         result = _get_result()
115         if result:
116             return result
117         with self.exclusive_lock():
118             # Another process may have already completed this work
119             result = _get_result()
120             if result:  # pragma: no cover (race)
121                 return result
122 
123             logger.info('Initializing environment for {}.'.format(repo))
124 
125             directory = tempfile.mkdtemp(prefix='repo', dir=self.directory)
126             with clean_path_on_failure(directory):
127                 make_strategy(directory)
128 
129             # Update our db with the created repo
130             with self.connect() as db:
131                 db.execute(
132                     'INSERT INTO repos (repo, ref, path) VALUES (?, ?, ?)',
133                     [repo, ref, directory],
134                 )
135         return directory
136 
137     def clone(self, repo, ref, deps=()):
138         """Clone the given url and checkout the specific ref."""
139         def clone_strategy(directory):
140             env = git.no_git_env()
141 
142             cmd = ('git', 'clone', '--no-checkout', repo, directory)
143             cmd_output(*cmd, env=env)
144 
145             def _git_cmd(*args):
146                 return cmd_output('git', *args, cwd=directory, env=env)
147 
148             _git_cmd('reset', ref, '--hard')
149             _git_cmd('submodule', 'update', '--init', '--recursive')
150 
151         return self._new_repo(repo, ref, deps, clone_strategy)
152 
153     LOCAL_RESOURCES = (
154         'Cargo.toml', 'main.go', 'main.rs', '.npmignore', 'package.json',
155         'pre_commit_dummy_package.gemspec', 'setup.py',
156     )
157 
158     def make_local(self, deps):
159         def make_local_strategy(directory):
160             for resource in self.LOCAL_RESOURCES:
161                 contents = resource_text('empty_template_{}'.format(resource))
162                 with io.open(os.path.join(directory, resource), 'w') as f:
163                     f.write(contents)
164 
165             env = git.no_git_env()
166 
167             # initialize the git repository so it looks more like cloned repos
168             def _git_cmd(*args):
169                 cmd_output('git', *args, cwd=directory, env=env)
170 
171             _git_cmd('init', '.')
172             _git_cmd('config', 'remote.origin.url', '<<unknown>>')
173             _git_cmd('add', '.')
174             git.commit(repo=directory)
175 
176         return self._new_repo(
177             'local', C.LOCAL_REPO_VERSION, deps, make_local_strategy,
178         )
179 
180     def _create_config_table_if_not_exists(self, db):
181         db.executescript(
182             'CREATE TABLE IF NOT EXISTS configs ('
183             '   path TEXT NOT NULL,'
184             '   PRIMARY KEY (path)'
185             ');',
186         )
187 
188     def mark_config_used(self, path):
189         path = os.path.realpath(path)
190         # don't insert config files that do not exist
191         if not os.path.exists(path):
192             return
193         with self.connect() as db:
194             # TODO: eventually remove this and only create in _create
195             self._create_config_table_if_not_exists(db)
196             db.execute('INSERT OR IGNORE INTO configs VALUES (?)', (path,))
197 
198     def select_all_configs(self):
199         with self.connect() as db:
200             self._create_config_table_if_not_exists(db)
201             rows = db.execute('SELECT path FROM configs').fetchall()
202             return [path for path, in rows]
203 
204     def delete_configs(self, configs):
205         with self.connect() as db:
206             rows = [(path,) for path in configs]
207             db.executemany('DELETE FROM configs WHERE path = ?', rows)
208 
209     def select_all_repos(self):
210         with self.connect() as db:
211             return db.execute('SELECT repo, ref, path from repos').fetchall()
212 
213     def delete_repo(self, db_repo_name, ref, path):
214         with self.connect() as db:
215             db.execute(
216                 'DELETE FROM repos WHERE repo = ? and ref = ?',
217                 (db_repo_name, ref),
218             )
219         rmtree(path)
220
```
--- 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/languages/node.py b/pre_commit/languages/node.py
--- a/pre_commit/languages/node.py
+++ b/pre_commit/languages/node.py
@@ -62,11 +62,13 @@
             cmd.extend(['-n', version])
         cmd_output(*cmd)
 
-        dep = 'git+file:///{}'.format(prefix.prefix_dir)
         with in_env(prefix, version):
+            # https://npm.community/t/npm-install-g-git-vs-git-clone-cd-npm-install-g/5449
+            # install as if we installed from git
+            helpers.run_setup_cmd(prefix, ('npm', 'install'))
             helpers.run_setup_cmd(
                 prefix,
-                ('npm', 'install', '-g', dep) + additional_dependencies,
+                ('npm', 'install', '-g', '.') + additional_dependencies,
             )
 
 
diff --git a/pre_commit/store.py b/pre_commit/store.py
--- a/pre_commit/store.py
+++ b/pre_commit/store.py
@@ -10,6 +10,7 @@
 import pre_commit.constants as C
 from pre_commit import file_lock
 from pre_commit import git
+from pre_commit.util import CalledProcessError
 from pre_commit.util import clean_path_on_failure
 from pre_commit.util import cmd_output
 from pre_commit.util import resource_text
@@ -134,19 +135,43 @@
                 )
         return directory
 
+    def _complete_clone(self, ref, git_cmd):
+        """Perform a complete clone of a repository and its submodules """
+
+        git_cmd('fetch', 'origin')
+        git_cmd('checkout', ref)
+        git_cmd('submodule', 'update', '--init', '--recursive')
+
+    def _shallow_clone(self, ref, git_cmd):  # pragma: windows no cover
+        """Perform a shallow clone of a repository and its submodules """
+
+        git_config = 'protocol.version=2'
+        git_cmd('-c', git_config, 'fetch', 'origin', ref, '--depth=1')
+        git_cmd('checkout', ref)
+        git_cmd(
+            '-c', git_config, 'submodule', 'update', '--init',
+            '--recursive', '--depth=1',
+        )
+
     def clone(self, repo, ref, deps=()):
         """Clone the given url and checkout the specific ref."""
+
+        if os.path.isdir(repo):
+            repo = os.path.abspath(repo)
+
         def clone_strategy(directory):
             env = git.no_git_env()
 
-            cmd = ('git', 'clone', '--no-checkout', repo, directory)
-            cmd_output(*cmd, env=env)
-
             def _git_cmd(*args):
-                return cmd_output('git', *args, cwd=directory, env=env)
+                cmd_output('git', *args, cwd=directory, env=env)
+
+            _git_cmd('init', '.')
+            _git_cmd('remote', 'add', 'origin', repo)
 
-            _git_cmd('reset', ref, '--hard')
-            _git_cmd('submodule', 'update', '--init', '--recursive')
+            try:
+                self._shallow_clone(ref, _git_cmd)
+            except CalledProcessError:
+                self._complete_clone(ref, _git_cmd)
 
         return self._new_repo(repo, ref, deps, clone_strategy)

{"golden_diff": "diff --git a/pre_commit/languages/node.py b/pre_commit/languages/node.py\n--- a/pre_commit/languages/node.py\n+++ b/pre_commit/languages/node.py\n@@ -62,11 +62,13 @@\n             cmd.extend(['-n', version])\n         cmd_output(*cmd)\n \n-        dep = 'git+file:///{}'.format(prefix.prefix_dir)\n         with in_env(prefix, version):\n+            # https://npm.community/t/npm-install-g-git-vs-git-clone-cd-npm-install-g/5449\n+            # install as if we installed from git\n+            helpers.run_setup_cmd(prefix, ('npm', 'install'))\n             helpers.run_setup_cmd(\n                 prefix,\n-                ('npm', 'install', '-g', dep) + additional_dependencies,\n+                ('npm', 'install', '-g', '.') + additional_dependencies,\n             )\n \n \ndiff --git a/pre_commit/store.py b/pre_commit/store.py\n--- a/pre_commit/store.py\n+++ b/pre_commit/store.py\n@@ -10,6 +10,7 @@\n import pre_commit.constants as C\n from pre_commit import file_lock\n from pre_commit import git\n+from pre_commit.util import CalledProcessError\n from pre_commit.util import clean_path_on_failure\n from pre_commit.util import cmd_output\n from pre_commit.util import resource_text\n@@ -134,19 +135,43 @@\n                 )\n         return directory\n \n+    def _complete_clone(self, ref, git_cmd):\n+        \"\"\"Perform a complete clone of a repository and its submodules \"\"\"\n+\n+        git_cmd('fetch', 'origin')\n+        git_cmd('checkout', ref)\n+        git_cmd('submodule', 'update', '--init', '--recursive')\n+\n+    def _shallow_clone(self, ref, git_cmd):  # pragma: windows no cover\n+        \"\"\"Perform a shallow clone of a repository and its submodules \"\"\"\n+\n+        git_config = 'protocol.version=2'\n+        git_cmd('-c', git_config, 'fetch', 'origin', ref, '--depth=1')\n+        git_cmd('checkout', ref)\n+        git_cmd(\n+            '-c', git_config, 'submodule', 'update', '--init',\n+            '--recursive', '--depth=1',\n+        )\n+\n     def clone(self, repo, ref, deps=()):\n         \"\"\"Clone the given url and checkout the specific ref.\"\"\"\n+\n+        if os.path.isdir(repo):\n+            repo = os.path.abspath(repo)\n+\n         def clone_strategy(directory):\n             env = git.no_git_env()\n \n-            cmd = ('git', 'clone', '--no-checkout', repo, directory)\n-            cmd_output(*cmd, env=env)\n-\n             def _git_cmd(*args):\n-                return cmd_output('git', *args, cwd=directory, env=env)\n+                cmd_output('git', *args, cwd=directory, env=env)\n+\n+            _git_cmd('init', '.')\n+            _git_cmd('remote', 'add', 'origin', repo)\n \n-            _git_cmd('reset', ref, '--hard')\n-            _git_cmd('submodule', 'update', '--init', '--recursive')\n+            try:\n+                self._shallow_clone(ref, _git_cmd)\n+            except CalledProcessError:\n+                self._complete_clone(ref, _git_cmd)\n \n         return self._new_repo(repo, ref, deps, clone_strategy)\n", "issue": "Investigate / implement shallow cloning\nNow that github supports it, might be worth the speed improvements\r\n\r\nIt would have to be smart enough to fall back to slow clone when the option isn't available (either the client is too old or the server is too old)\n", "before_files": [{"content": "from __future__ import unicode_literals\n\nimport contextlib\nimport os\nimport sys\n\nimport pre_commit.constants as C\nfrom pre_commit.envcontext import envcontext\nfrom pre_commit.envcontext import Var\nfrom pre_commit.languages import helpers\nfrom pre_commit.languages.python import bin_dir\nfrom pre_commit.util import clean_path_on_failure\nfrom pre_commit.util import cmd_output\n\n\nENVIRONMENT_DIR = 'node_env'\nget_default_version = helpers.basic_get_default_version\nhealthy = helpers.basic_healthy\n\n\ndef _envdir(prefix, version):\n    directory = helpers.environment_dir(ENVIRONMENT_DIR, version)\n    return prefix.path(directory)\n\n\ndef get_env_patch(venv):\n    if sys.platform == 'cygwin':  # pragma: no cover\n        _, win_venv, _ = cmd_output('cygpath', '-w', venv)\n        install_prefix = r'{}\\bin'.format(win_venv.strip())\n    elif sys.platform == 'win32':  # pragma: no cover\n        install_prefix = bin_dir(venv)\n    else:  # pragma: windows no cover\n        install_prefix = venv\n    return (\n        ('NODE_VIRTUAL_ENV', venv),\n        ('NPM_CONFIG_PREFIX', install_prefix),\n        ('npm_config_prefix', install_prefix),\n        ('NODE_PATH', os.path.join(venv, 'lib', 'node_modules')),\n        ('PATH', (bin_dir(venv), os.pathsep, Var('PATH'))),\n    )\n\n\n@contextlib.contextmanager\ndef in_env(prefix, language_version):\n    with envcontext(get_env_patch(_envdir(prefix, language_version))):\n        yield\n\n\ndef install_environment(prefix, version, additional_dependencies):\n    additional_dependencies = tuple(additional_dependencies)\n    assert prefix.exists('package.json')\n    envdir = _envdir(prefix, version)\n\n    # https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx?f=255&MSPPError=-2147217396#maxpath\n    if sys.platform == 'win32':  # pragma: no cover\n        envdir = '\\\\\\\\?\\\\' + os.path.normpath(envdir)\n    with clean_path_on_failure(envdir):\n        cmd = [\n            sys.executable, '-mnodeenv', '--prebuilt', '--clean-src', envdir,\n        ]\n        if version != C.DEFAULT:\n            cmd.extend(['-n', version])\n        cmd_output(*cmd)\n\n        dep = 'git+file:///{}'.format(prefix.prefix_dir)\n        with in_env(prefix, version):\n            helpers.run_setup_cmd(\n                prefix,\n                ('npm', 'install', '-g', dep) + additional_dependencies,\n            )\n\n\ndef run_hook(hook, file_args):\n    with in_env(hook.prefix, hook.language_version):\n        return helpers.run_xargs(hook, helpers.to_cmd(hook), file_args)\n", "path": "pre_commit/languages/node.py"}, {"content": "from __future__ import unicode_literals\n\nimport contextlib\nimport io\nimport logging\nimport os.path\nimport sqlite3\nimport tempfile\n\nimport pre_commit.constants as C\nfrom pre_commit import file_lock\nfrom pre_commit import git\nfrom pre_commit.util import clean_path_on_failure\nfrom pre_commit.util import cmd_output\nfrom pre_commit.util import resource_text\nfrom pre_commit.util import rmtree\n\n\nlogger = logging.getLogger('pre_commit')\n\n\ndef _get_default_directory():\n    \"\"\"Returns the default directory for the Store.  This is intentionally\n    underscored to indicate that `Store.get_default_directory` is the intended\n    way to get this information.  This is also done so\n    `Store.get_default_directory` can be mocked in tests and\n    `_get_default_directory` can be tested.\n    \"\"\"\n    return os.environ.get('PRE_COMMIT_HOME') or os.path.join(\n        os.environ.get('XDG_CACHE_HOME') or os.path.expanduser('~/.cache'),\n        'pre-commit',\n    )\n\n\nclass Store(object):\n    get_default_directory = staticmethod(_get_default_directory)\n\n    def __init__(self, directory=None):\n        self.directory = directory or Store.get_default_directory()\n        self.db_path = os.path.join(self.directory, 'db.db')\n\n        if not os.path.exists(self.directory):\n            os.makedirs(self.directory)\n            with io.open(os.path.join(self.directory, 'README'), 'w') as f:\n                f.write(\n                    'This directory is maintained by the pre-commit project.\\n'\n                    'Learn more: https://github.com/pre-commit/pre-commit\\n',\n                )\n\n        if os.path.exists(self.db_path):\n            return\n        with self.exclusive_lock():\n            # Another process may have already completed this work\n            if os.path.exists(self.db_path):  # pragma: no cover (race)\n                return\n            # To avoid a race where someone ^Cs between db creation and\n            # execution of the CREATE TABLE statement\n            fd, tmpfile = tempfile.mkstemp(dir=self.directory)\n            # We'll be managing this file ourselves\n            os.close(fd)\n            with self.connect(db_path=tmpfile) as db:\n                db.executescript(\n                    'CREATE TABLE repos ('\n                    '    repo TEXT NOT NULL,'\n                    '    ref TEXT NOT NULL,'\n                    '    path TEXT NOT NULL,'\n                    '    PRIMARY KEY (repo, ref)'\n                    ');',\n                )\n                self._create_config_table_if_not_exists(db)\n\n            # Atomic file move\n            os.rename(tmpfile, self.db_path)\n\n    @contextlib.contextmanager\n    def exclusive_lock(self):\n        def blocked_cb():  # pragma: no cover (tests are single-process)\n            logger.info('Locking pre-commit directory')\n\n        with file_lock.lock(os.path.join(self.directory, '.lock'), blocked_cb):\n            yield\n\n    @contextlib.contextmanager\n    def connect(self, db_path=None):\n        db_path = db_path or self.db_path\n        # sqlite doesn't close its fd with its contextmanager >.<\n        # contextlib.closing fixes this.\n        # See: https://stackoverflow.com/a/28032829/812183\n        with contextlib.closing(sqlite3.connect(db_path)) as db:\n            # this creates a transaction\n            with db:\n                yield db\n\n    @classmethod\n    def db_repo_name(cls, repo, deps):\n        if deps:\n            return '{}:{}'.format(repo, ','.join(sorted(deps)))\n        else:\n            return repo\n\n    def _new_repo(self, repo, ref, deps, make_strategy):\n        repo = self.db_repo_name(repo, deps)\n\n        def _get_result():\n            # Check if we already exist\n            with self.connect() as db:\n                result = db.execute(\n                    'SELECT path FROM repos WHERE repo = ? AND ref = ?',\n                    (repo, ref),\n                ).fetchone()\n                if result:\n                    return result[0]\n\n        result = _get_result()\n        if result:\n            return result\n        with self.exclusive_lock():\n            # Another process may have already completed this work\n            result = _get_result()\n            if result:  # pragma: no cover (race)\n                return result\n\n            logger.info('Initializing environment for {}.'.format(repo))\n\n            directory = tempfile.mkdtemp(prefix='repo', dir=self.directory)\n            with clean_path_on_failure(directory):\n                make_strategy(directory)\n\n            # Update our db with the created repo\n            with self.connect() as db:\n                db.execute(\n                    'INSERT INTO repos (repo, ref, path) VALUES (?, ?, ?)',\n                    [repo, ref, directory],\n                )\n        return directory\n\n    def clone(self, repo, ref, deps=()):\n        \"\"\"Clone the given url and checkout the specific ref.\"\"\"\n        def clone_strategy(directory):\n            env = git.no_git_env()\n\n            cmd = ('git', 'clone', '--no-checkout', repo, directory)\n            cmd_output(*cmd, env=env)\n\n            def _git_cmd(*args):\n                return cmd_output('git', *args, cwd=directory, env=env)\n\n            _git_cmd('reset', ref, '--hard')\n            _git_cmd('submodule', 'update', '--init', '--recursive')\n\n        return self._new_repo(repo, ref, deps, clone_strategy)\n\n    LOCAL_RESOURCES = (\n        'Cargo.toml', 'main.go', 'main.rs', '.npmignore', 'package.json',\n        'pre_commit_dummy_package.gemspec', 'setup.py',\n    )\n\n    def make_local(self, deps):\n        def make_local_strategy(directory):\n            for resource in self.LOCAL_RESOURCES:\n                contents = resource_text('empty_template_{}'.format(resource))\n                with io.open(os.path.join(directory, resource), 'w') as f:\n                    f.write(contents)\n\n            env = git.no_git_env()\n\n            # initialize the git repository so it looks more like cloned repos\n            def _git_cmd(*args):\n                cmd_output('git', *args, cwd=directory, env=env)\n\n            _git_cmd('init', '.')\n            _git_cmd('config', 'remote.origin.url', '<<unknown>>')\n            _git_cmd('add', '.')\n            git.commit(repo=directory)\n\n        return self._new_repo(\n            'local', C.LOCAL_REPO_VERSION, deps, make_local_strategy,\n        )\n\n    def _create_config_table_if_not_exists(self, db):\n        db.executescript(\n            'CREATE TABLE IF NOT EXISTS configs ('\n            '   path TEXT NOT NULL,'\n            '   PRIMARY KEY (path)'\n            ');',\n        )\n\n    def mark_config_used(self, path):\n        path = os.path.realpath(path)\n        # don't insert config files that do not exist\n        if not os.path.exists(path):\n            return\n        with self.connect() as db:\n            # TODO: eventually remove this and only create in _create\n            self._create_config_table_if_not_exists(db)\n            db.execute('INSERT OR IGNORE INTO configs VALUES (?)', (path,))\n\n    def select_all_configs(self):\n        with self.connect() as db:\n            self._create_config_table_if_not_exists(db)\n            rows = db.execute('SELECT path FROM configs').fetchall()\n            return [path for path, in rows]\n\n    def delete_configs(self, configs):\n        with self.connect() as db:\n            rows = [(path,) for path in configs]\n            db.executemany('DELETE FROM configs WHERE path = ?', rows)\n\n    def select_all_repos(self):\n        with self.connect() as db:\n            return db.execute('SELECT repo, ref, path from repos').fetchall()\n\n    def delete_repo(self, db_repo_name, ref, path):\n        with self.connect() as db:\n            db.execute(\n                'DELETE FROM repos WHERE repo = ? and ref = ?',\n                (db_repo_name, ref),\n            )\n        rmtree(path)\n", "path": "pre_commit/store.py"}], "after_files": [{"content": "from __future__ import unicode_literals\n\nimport contextlib\nimport os\nimport sys\n\nimport pre_commit.constants as C\nfrom pre_commit.envcontext import envcontext\nfrom pre_commit.envcontext import Var\nfrom pre_commit.languages import helpers\nfrom pre_commit.languages.python import bin_dir\nfrom pre_commit.util import clean_path_on_failure\nfrom pre_commit.util import cmd_output\n\n\nENVIRONMENT_DIR = 'node_env'\nget_default_version = helpers.basic_get_default_version\nhealthy = helpers.basic_healthy\n\n\ndef _envdir(prefix, version):\n    directory = helpers.environment_dir(ENVIRONMENT_DIR, version)\n    return prefix.path(directory)\n\n\ndef get_env_patch(venv):\n    if sys.platform == 'cygwin':  # pragma: no cover\n        _, win_venv, _ = cmd_output('cygpath', '-w', venv)\n        install_prefix = r'{}\\bin'.format(win_venv.strip())\n    elif sys.platform == 'win32':  # pragma: no cover\n        install_prefix = bin_dir(venv)\n    else:  # pragma: windows no cover\n        install_prefix = venv\n    return (\n        ('NODE_VIRTUAL_ENV', venv),\n        ('NPM_CONFIG_PREFIX', install_prefix),\n        ('npm_config_prefix', install_prefix),\n        ('NODE_PATH', os.path.join(venv, 'lib', 'node_modules')),\n        ('PATH', (bin_dir(venv), os.pathsep, Var('PATH'))),\n    )\n\n\n@contextlib.contextmanager\ndef in_env(prefix, language_version):\n    with envcontext(get_env_patch(_envdir(prefix, language_version))):\n        yield\n\n\ndef install_environment(prefix, version, additional_dependencies):\n    additional_dependencies = tuple(additional_dependencies)\n    assert prefix.exists('package.json')\n    envdir = _envdir(prefix, version)\n\n    # https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx?f=255&MSPPError=-2147217396#maxpath\n    if sys.platform == 'win32':  # pragma: no cover\n        envdir = '\\\\\\\\?\\\\' + os.path.normpath(envdir)\n    with clean_path_on_failure(envdir):\n        cmd = [\n            sys.executable, '-mnodeenv', '--prebuilt', '--clean-src', envdir,\n        ]\n        if version != C.DEFAULT:\n            cmd.extend(['-n', version])\n        cmd_output(*cmd)\n\n        with in_env(prefix, version):\n            # https://npm.community/t/npm-install-g-git-vs-git-clone-cd-npm-install-g/5449\n            # install as if we installed from git\n            helpers.run_setup_cmd(prefix, ('npm', 'install'))\n            helpers.run_setup_cmd(\n                prefix,\n                ('npm', 'install', '-g', '.') + additional_dependencies,\n            )\n\n\ndef run_hook(hook, file_args):\n    with in_env(hook.prefix, hook.language_version):\n        return helpers.run_xargs(hook, helpers.to_cmd(hook), file_args)\n", "path": "pre_commit/languages/node.py"}, {"content": "from __future__ import unicode_literals\n\nimport contextlib\nimport io\nimport logging\nimport os.path\nimport sqlite3\nimport tempfile\n\nimport pre_commit.constants as C\nfrom pre_commit import file_lock\nfrom pre_commit import git\nfrom pre_commit.util import CalledProcessError\nfrom pre_commit.util import clean_path_on_failure\nfrom pre_commit.util import cmd_output\nfrom pre_commit.util import resource_text\nfrom pre_commit.util import rmtree\n\n\nlogger = logging.getLogger('pre_commit')\n\n\ndef _get_default_directory():\n    \"\"\"Returns the default directory for the Store.  This is intentionally\n    underscored to indicate that `Store.get_default_directory` is the intended\n    way to get this information.  This is also done so\n    `Store.get_default_directory` can be mocked in tests and\n    `_get_default_directory` can be tested.\n    \"\"\"\n    return os.environ.get('PRE_COMMIT_HOME') or os.path.join(\n        os.environ.get('XDG_CACHE_HOME') or os.path.expanduser('~/.cache'),\n        'pre-commit',\n    )\n\n\nclass Store(object):\n    get_default_directory = staticmethod(_get_default_directory)\n\n    def __init__(self, directory=None):\n        self.directory = directory or Store.get_default_directory()\n        self.db_path = os.path.join(self.directory, 'db.db')\n\n        if not os.path.exists(self.directory):\n            os.makedirs(self.directory)\n            with io.open(os.path.join(self.directory, 'README'), 'w') as f:\n                f.write(\n                    'This directory is maintained by the pre-commit project.\\n'\n                    'Learn more: https://github.com/pre-commit/pre-commit\\n',\n                )\n\n        if os.path.exists(self.db_path):\n            return\n        with self.exclusive_lock():\n            # Another process may have already completed this work\n            if os.path.exists(self.db_path):  # pragma: no cover (race)\n                return\n            # To avoid a race where someone ^Cs between db creation and\n            # execution of the CREATE TABLE statement\n            fd, tmpfile = tempfile.mkstemp(dir=self.directory)\n            # We'll be managing this file ourselves\n            os.close(fd)\n            with self.connect(db_path=tmpfile) as db:\n                db.executescript(\n                    'CREATE TABLE repos ('\n                    '    repo TEXT NOT NULL,'\n                    '    ref TEXT NOT NULL,'\n                    '    path TEXT NOT NULL,'\n                    '    PRIMARY KEY (repo, ref)'\n                    ');',\n                )\n                self._create_config_table_if_not_exists(db)\n\n            # Atomic file move\n            os.rename(tmpfile, self.db_path)\n\n    @contextlib.contextmanager\n    def exclusive_lock(self):\n        def blocked_cb():  # pragma: no cover (tests are single-process)\n            logger.info('Locking pre-commit directory')\n\n        with file_lock.lock(os.path.join(self.directory, '.lock'), blocked_cb):\n            yield\n\n    @contextlib.contextmanager\n    def connect(self, db_path=None):\n        db_path = db_path or self.db_path\n        # sqlite doesn't close its fd with its contextmanager >.<\n        # contextlib.closing fixes this.\n        # See: https://stackoverflow.com/a/28032829/812183\n        with contextlib.closing(sqlite3.connect(db_path)) as db:\n            # this creates a transaction\n            with db:\n                yield db\n\n    @classmethod\n    def db_repo_name(cls, repo, deps):\n        if deps:\n            return '{}:{}'.format(repo, ','.join(sorted(deps)))\n        else:\n            return repo\n\n    def _new_repo(self, repo, ref, deps, make_strategy):\n        repo = self.db_repo_name(repo, deps)\n\n        def _get_result():\n            # Check if we already exist\n            with self.connect() as db:\n                result = db.execute(\n                    'SELECT path FROM repos WHERE repo = ? AND ref = ?',\n                    (repo, ref),\n                ).fetchone()\n                if result:\n                    return result[0]\n\n        result = _get_result()\n        if result:\n            return result\n        with self.exclusive_lock():\n            # Another process may have already completed this work\n            result = _get_result()\n            if result:  # pragma: no cover (race)\n                return result\n\n            logger.info('Initializing environment for {}.'.format(repo))\n\n            directory = tempfile.mkdtemp(prefix='repo', dir=self.directory)\n            with clean_path_on_failure(directory):\n                make_strategy(directory)\n\n            # Update our db with the created repo\n            with self.connect() as db:\n                db.execute(\n                    'INSERT INTO repos (repo, ref, path) VALUES (?, ?, ?)',\n                    [repo, ref, directory],\n                )\n        return directory\n\n    def _complete_clone(self, ref, git_cmd):\n        \"\"\"Perform a complete clone of a repository and its submodules \"\"\"\n\n        git_cmd('fetch', 'origin')\n        git_cmd('checkout', ref)\n        git_cmd('submodule', 'update', '--init', '--recursive')\n\n    def _shallow_clone(self, ref, git_cmd):  # pragma: windows no cover\n        \"\"\"Perform a shallow clone of a repository and its submodules \"\"\"\n\n        git_config = 'protocol.version=2'\n        git_cmd('-c', git_config, 'fetch', 'origin', ref, '--depth=1')\n        git_cmd('checkout', ref)\n        git_cmd(\n            '-c', git_config, 'submodule', 'update', '--init',\n            '--recursive', '--depth=1',\n        )\n\n    def clone(self, repo, ref, deps=()):\n        \"\"\"Clone the given url and checkout the specific ref.\"\"\"\n\n        if os.path.isdir(repo):\n            repo = os.path.abspath(repo)\n\n        def clone_strategy(directory):\n            env = git.no_git_env()\n\n            def _git_cmd(*args):\n                cmd_output('git', *args, cwd=directory, env=env)\n\n            _git_cmd('init', '.')\n            _git_cmd('remote', 'add', 'origin', repo)\n\n            try:\n                self._shallow_clone(ref, _git_cmd)\n            except CalledProcessError:\n                self._complete_clone(ref, _git_cmd)\n\n        return self._new_repo(repo, ref, deps, clone_strategy)\n\n    LOCAL_RESOURCES = (\n        'Cargo.toml', 'main.go', 'main.rs', '.npmignore', 'package.json',\n        'pre_commit_dummy_package.gemspec', 'setup.py',\n    )\n\n    def make_local(self, deps):\n        def make_local_strategy(directory):\n            for resource in self.LOCAL_RESOURCES:\n                contents = resource_text('empty_template_{}'.format(resource))\n                with io.open(os.path.join(directory, resource), 'w') as f:\n                    f.write(contents)\n\n            env = git.no_git_env()\n\n            # initialize the git repository so it looks more like cloned repos\n            def _git_cmd(*args):\n                cmd_output('git', *args, cwd=directory, env=env)\n\n            _git_cmd('init', '.')\n            _git_cmd('config', 'remote.origin.url', '<<unknown>>')\n            _git_cmd('add', '.')\n            git.commit(repo=directory)\n\n        return self._new_repo(\n            'local', C.LOCAL_REPO_VERSION, deps, make_local_strategy,\n        )\n\n    def _create_config_table_if_not_exists(self, db):\n        db.executescript(\n            'CREATE TABLE IF NOT EXISTS configs ('\n            '   path TEXT NOT NULL,'\n            '   PRIMARY KEY (path)'\n            ');',\n        )\n\n    def mark_config_used(self, path):\n        path = os.path.realpath(path)\n        # don't insert config files that do not exist\n        if not os.path.exists(path):\n            return\n        with self.connect() as db:\n            # TODO: eventually remove this and only create in _create\n            self._create_config_table_if_not_exists(db)\n            db.execute('INSERT OR IGNORE INTO configs VALUES (?)', (path,))\n\n    def select_all_configs(self):\n        with self.connect() as db:\n            self._create_config_table_if_not_exists(db)\n            rows = db.execute('SELECT path FROM configs').fetchall()\n            return [path for path, in rows]\n\n    def delete_configs(self, configs):\n        with self.connect() as db:\n            rows = [(path,) for path in configs]\n            db.executemany('DELETE FROM configs WHERE path = ?', rows)\n\n    def select_all_repos(self):\n        with self.connect() as db:\n            return db.execute('SELECT repo, ref, path from repos').fetchall()\n\n    def delete_repo(self, db_repo_name, ref, path):\n        with self.connect() as db:\n            db.execute(\n                'DELETE FROM repos WHERE repo = ? and ref = ?',\n                (db_repo_name, ref),\n            )\n        rmtree(path)\n", "path": "pre_commit/store.py"}]}