Skip to content

Commit f000241

Browse files
committed
Local repositories clone a blank repo
1 parent e704edb commit f000241

File tree

15 files changed

+156
-121
lines changed

15 files changed

+156
-121
lines changed

pre_commit/commands/autoupdate.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ def _update_repo(repo_config, runner, tags_only):
3232
"""
3333
repo = Repository.create(repo_config, runner.store)
3434

35-
with cwd(repo.repo_path_getter.repo_path):
35+
with cwd(repo._repo_path):
3636
cmd_output('git', 'fetch')
3737
tag_cmd = ('git', 'describe', 'origin/master', '--tags')
3838
if tags_only:

pre_commit/manifest.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,14 @@
1313

1414

1515
class Manifest(object):
16-
def __init__(self, repo_path_getter, repo_url):
17-
self.repo_path_getter = repo_path_getter
16+
def __init__(self, repo_path, repo_url):
17+
self.repo_path = repo_path
1818
self.repo_url = repo_url
1919

2020
@cached_property
2121
def manifest_contents(self):
22-
repo_path = self.repo_path_getter.repo_path
23-
default_path = os.path.join(repo_path, C.MANIFEST_FILE)
24-
legacy_path = os.path.join(repo_path, C.MANIFEST_FILE_LEGACY)
22+
default_path = os.path.join(self.repo_path, C.MANIFEST_FILE)
23+
legacy_path = os.path.join(self.repo_path, C.MANIFEST_FILE_LEGACY)
2524
if os.path.exists(legacy_path) and not os.path.exists(default_path):
2625
logger.warning(
2726
'{} uses legacy {} to provide hooks.\n'

pre_commit/repository.py

Lines changed: 56 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -96,40 +96,34 @@ def _install_all(venvs, repo_url):
9696

9797

9898
class Repository(object):
99-
def __init__(self, repo_config, repo_path_getter):
99+
def __init__(self, repo_config, store):
100100
self.repo_config = repo_config
101-
self.repo_path_getter = repo_path_getter
101+
self.store = store
102102
self.__installed = False
103103

104104
@classmethod
105105
def create(cls, config, store):
106106
if is_local_hooks(config):
107-
return LocalRepository(config)
107+
return LocalRepository(config, store)
108108
else:
109-
repo_path_getter = store.get_repo_path_getter(
110-
config['repo'], config['sha']
111-
)
112-
return cls(config, repo_path_getter)
109+
return cls(config, store)
113110

114111
@cached_property
115-
def _cmd_runner(self):
116-
return PrefixedCommandRunner(self.repo_path_getter.repo_path)
112+
def _repo_path(self):
113+
return self.store.clone(
114+
self.repo_config['repo'], self.repo_config['sha'],
115+
)
117116

118117
@cached_property
119-
def _venvs(self):
120-
deps_dict = defaultdict(_UniqueList)
121-
for _, hook in self.hooks:
122-
deps_dict[(hook['language'], hook['language_version'])].update(
123-
hook.get('additional_dependencies', []),
124-
)
125-
ret = []
126-
for (language, version), deps in deps_dict.items():
127-
ret.append((self._cmd_runner, language, version, deps))
128-
return tuple(ret)
118+
def _cmd_runner(self):
119+
return PrefixedCommandRunner(self._repo_path)
120+
121+
def _cmd_runner_from_deps(self, language_name, deps):
122+
return self._cmd_runner
129123

130124
@cached_property
131125
def manifest(self):
132-
return Manifest(self.repo_path_getter, self.repo_config['repo'])
126+
return Manifest(self._repo_path, self.repo_config['repo'])
133127

134128
@cached_property
135129
def hooks(self):
@@ -160,6 +154,18 @@ def hooks(self):
160154
for hook in self.repo_config['hooks']
161155
)
162156

157+
@cached_property
158+
def _venvs(self):
159+
deps_dict = defaultdict(_UniqueList)
160+
for _, hook in self.hooks:
161+
deps_dict[(hook['language'], hook['language_version'])].update(
162+
hook.get('additional_dependencies', []),
163+
)
164+
ret = []
165+
for (language, version), deps in deps_dict.items():
166+
ret.append((self._cmd_runner, language, version, deps))
167+
return tuple(ret)
168+
163169
def require_installed(self):
164170
if not self.__installed:
165171
_install_all(self._venvs, self.repo_config['repo'])
@@ -168,19 +174,30 @@ def require_installed(self):
168174
def run_hook(self, hook, file_args):
169175
"""Run a hook.
170176
171-
Args:
172-
hook - Hook dictionary
173-
file_args - List of files to run
177+
:param dict hook:
178+
:param tuple file_args: all the files to run the hook on
174179
"""
175180
self.require_installed()
176-
return languages[hook['language']].run_hook(
177-
self._cmd_runner, hook, file_args,
178-
)
181+
language_name = hook['language']
182+
deps = hook.get('additional_dependencies', [])
183+
cmd_runner = self._cmd_runner_from_deps(language_name, deps)
184+
return languages[language_name].run_hook(cmd_runner, hook, file_args)
179185

180186

181187
class LocalRepository(Repository):
182-
def __init__(self, repo_config):
183-
super(LocalRepository, self).__init__(repo_config, None)
188+
def _cmd_runner_from_deps(self, language_name, deps):
189+
"""local repositories have a cmd runner per hook"""
190+
language = languages[language_name]
191+
# pcre / script / system do not have environments so they work out
192+
# of the current directory
193+
if language.ENVIRONMENT_DIR is None:
194+
return PrefixedCommandRunner(git.get_root())
195+
else:
196+
return PrefixedCommandRunner(self.store.make_local(deps))
197+
198+
@cached_property
199+
def manifest(self):
200+
raise NotImplementedError
184201

185202
@cached_property
186203
def hooks(self):
@@ -190,12 +207,17 @@ def hooks(self):
190207
)
191208

192209
@cached_property
193-
def cmd_runner(self):
194-
return PrefixedCommandRunner(git.get_root())
195-
196-
@cached_property
197-
def manifest(self):
198-
raise NotImplementedError
210+
def _venvs(self):
211+
ret = []
212+
for _, hook in self.hooks:
213+
language = hook['language']
214+
version = hook['language_version']
215+
deps = hook.get('additional_dependencies', [])
216+
ret.append((
217+
self._cmd_runner_from_deps(language, deps),
218+
language, version, deps,
219+
))
220+
return tuple(ret)
199221

200222

201223
class _UniqueList(list):
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
*
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
package main
2+
3+
func main() {}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"name": "pre_commit_dummy_package",
3+
"version": "0.0.0"
4+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
Gem::Specification.new do |s|
2+
s.name = 'pre_commit_dummy_package'
3+
s.version = '0.0.0'
4+
s.authors = ['Anthony Sottile']
5+
end
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
from setuptools import setup
2+
3+
4+
setup(name='pre-commit-dummy-package', version='0.0.0')

pre_commit/store.py

Lines changed: 32 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,10 @@
1212
from pre_commit.prefixed_command_runner import PrefixedCommandRunner
1313
from pre_commit.util import clean_path_on_failure
1414
from pre_commit.util import cmd_output
15+
from pre_commit.util import copy_tree_to_path
1516
from pre_commit.util import cwd
1617
from pre_commit.util import no_git_env
18+
from pre_commit.util import resource_filename
1719

1820

1921
logger = logging.getLogger('pre_commit')
@@ -35,16 +37,6 @@ def _get_default_directory():
3537
class Store(object):
3638
get_default_directory = staticmethod(_get_default_directory)
3739

38-
class RepoPathGetter(object):
39-
def __init__(self, repo, ref, store):
40-
self._repo = repo
41-
self._ref = ref
42-
self._store = store
43-
44-
@cached_property
45-
def repo_path(self):
46-
return self._store.clone(self._repo, self._ref)
47-
4840
def __init__(self, directory=None):
4941
if directory is None:
5042
directory = self.get_default_directory()
@@ -91,45 +83,55 @@ def _create(self):
9183

9284
def require_created(self):
9385
"""Require the pre-commit file store to be created."""
94-
if self.__created:
95-
return
96-
97-
self._create()
98-
self.__created = True
86+
if not self.__created:
87+
self._create()
88+
self.__created = True
9989

100-
def clone(self, url, ref):
101-
"""Clone the given url and checkout the specific ref."""
90+
def _new_repo(self, repo, ref, make_strategy):
10291
self.require_created()
10392

10493
# Check if we already exist
10594
with sqlite3.connect(self.db_path) as db:
10695
result = db.execute(
10796
'SELECT path FROM repos WHERE repo = ? AND ref = ?',
108-
[url, ref],
97+
[repo, ref],
10998
).fetchone()
11099
if result:
111100
return result[0]
112101

113-
logger.info('Initializing environment for {}.'.format(url))
102+
logger.info('Initializing environment for {}.'.format(repo))
114103

115-
dir = tempfile.mkdtemp(prefix='repo', dir=self.directory)
116-
with clean_path_on_failure(dir):
117-
cmd_output(
118-
'git', 'clone', '--no-checkout', url, dir, env=no_git_env(),
119-
)
120-
with cwd(dir):
121-
cmd_output('git', 'reset', ref, '--hard', env=no_git_env())
104+
directory = tempfile.mkdtemp(prefix='repo', dir=self.directory)
105+
with clean_path_on_failure(directory):
106+
make_strategy(directory)
122107

123108
# Update our db with the created repo
124109
with sqlite3.connect(self.db_path) as db:
125110
db.execute(
126111
'INSERT INTO repos (repo, ref, path) VALUES (?, ?, ?)',
127-
[url, ref, dir],
112+
[repo, ref, directory],
128113
)
129-
return dir
114+
return directory
115+
116+
def clone(self, repo, ref):
117+
"""Clone the given url and checkout the specific ref."""
118+
def clone_strategy(directory):
119+
cmd_output(
120+
'git', 'clone', '--no-checkout', repo, directory,
121+
env=no_git_env(),
122+
)
123+
with cwd(directory):
124+
cmd_output('git', 'reset', ref, '--hard', env=no_git_env())
125+
126+
return self._new_repo(repo, ref, clone_strategy)
130127

131-
def get_repo_path_getter(self, repo, ref):
132-
return self.RepoPathGetter(repo, ref, self)
128+
def make_local(self, deps):
129+
def make_local_strategy(directory):
130+
copy_tree_to_path(resource_filename('empty_template'), directory)
131+
return self._new_repo(
132+
'local:{}'.format(','.join(sorted(deps))), 'N/A',
133+
make_local_strategy,
134+
)
133135

134136
@cached_property
135137
def cmd_runner(self):

pre_commit/util.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,3 +204,21 @@ def handle_remove_readonly(func, path, exc): # pragma: no cover (windows)
204204
else:
205205
raise
206206
shutil.rmtree(path, ignore_errors=False, onerror=handle_remove_readonly)
207+
208+
209+
def copy_tree_to_path(src_dir, dest_dir):
210+
"""Copies all of the things inside src_dir to an already existing dest_dir.
211+
212+
This looks eerily similar to shutil.copytree, but copytree has no option
213+
for not creating dest_dir.
214+
"""
215+
names = os.listdir(src_dir)
216+
217+
for name in names:
218+
srcname = os.path.join(src_dir, name)
219+
destname = os.path.join(dest_dir, name)
220+
221+
if os.path.isdir(srcname):
222+
shutil.copytree(srcname, destname)
223+
else:
224+
shutil.copy(srcname, destname)

0 commit comments

Comments
 (0)