##// END OF EJS Templates
exceptions: use python3 compatible exception handling
marcink -
r566:0cd102ec default
parent child Browse files
Show More
@@ -1,726 +1,728 b''
1 # RhodeCode VCSServer provides access to different vcs backends via network.
1 # RhodeCode VCSServer provides access to different vcs backends via network.
2 # Copyright (C) 2014-2018 RhodeCode GmbH
2 # Copyright (C) 2014-2018 RhodeCode GmbH
3 #
3 #
4 # This program is free software; you can redistribute it and/or modify
4 # This program is free software; you can redistribute it and/or modify
5 # it under the terms of the GNU General Public License as published by
5 # it under the terms of the GNU General Public License as published by
6 # the Free Software Foundation; either version 3 of the License, or
6 # the Free Software Foundation; either version 3 of the License, or
7 # (at your option) any later version.
7 # (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software Foundation,
15 # along with this program; if not, write to the Free Software Foundation,
16 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17 import collections
17 import collections
18 import logging
18 import logging
19 import os
19 import os
20 import posixpath as vcspath
20 import posixpath as vcspath
21 import re
21 import re
22 import stat
22 import stat
23 import traceback
23 import traceback
24 import urllib
24 import urllib
25 import urllib2
25 import urllib2
26 from functools import wraps
26 from functools import wraps
27
27
28 from dulwich import index, objects
28 from dulwich import index, objects
29 from dulwich.client import HttpGitClient, LocalGitClient
29 from dulwich.client import HttpGitClient, LocalGitClient
30 from dulwich.errors import (
30 from dulwich.errors import (
31 NotGitRepository, ChecksumMismatch, WrongObjectException,
31 NotGitRepository, ChecksumMismatch, WrongObjectException,
32 MissingCommitError, ObjectMissing, HangupException,
32 MissingCommitError, ObjectMissing, HangupException,
33 UnexpectedCommandError)
33 UnexpectedCommandError)
34 from dulwich.repo import Repo as DulwichRepo, Tag
34 from dulwich.repo import Repo as DulwichRepo, Tag
35 from dulwich.server import update_server_info
35 from dulwich.server import update_server_info
36
36
37 from vcsserver import exceptions, settings, subprocessio
37 from vcsserver import exceptions, settings, subprocessio
38 from vcsserver.utils import safe_str
38 from vcsserver.utils import safe_str
39 from vcsserver.base import RepoFactory, obfuscate_qs, raise_from_original
39 from vcsserver.base import RepoFactory, obfuscate_qs, raise_from_original
40 from vcsserver.hgcompat import (
40 from vcsserver.hgcompat import (
41 hg_url as url_parser, httpbasicauthhandler, httpdigestauthhandler)
41 hg_url as url_parser, httpbasicauthhandler, httpdigestauthhandler)
42 from vcsserver.git_lfs.lib import LFSOidStore
42 from vcsserver.git_lfs.lib import LFSOidStore
43
43
44 DIR_STAT = stat.S_IFDIR
44 DIR_STAT = stat.S_IFDIR
45 FILE_MODE = stat.S_IFMT
45 FILE_MODE = stat.S_IFMT
46 GIT_LINK = objects.S_IFGITLINK
46 GIT_LINK = objects.S_IFGITLINK
47
47
48 log = logging.getLogger(__name__)
48 log = logging.getLogger(__name__)
49
49
50
50
51 def reraise_safe_exceptions(func):
51 def reraise_safe_exceptions(func):
52 """Converts Dulwich exceptions to something neutral."""
52 """Converts Dulwich exceptions to something neutral."""
53 @wraps(func)
53 @wraps(func)
54 def wrapper(*args, **kwargs):
54 def wrapper(*args, **kwargs):
55 try:
55 try:
56 return func(*args, **kwargs)
56 return func(*args, **kwargs)
57 except (ChecksumMismatch, WrongObjectException, MissingCommitError,
57 except (ChecksumMismatch, WrongObjectException, MissingCommitError,
58 ObjectMissing) as e:
58 ObjectMissing) as e:
59 raise exceptions.LookupException(e)(e.message)
59 exc = exceptions.LookupException(e)
60 raise exc(e)
60 except (HangupException, UnexpectedCommandError) as e:
61 except (HangupException, UnexpectedCommandError) as e:
61 raise exceptions.VcsException(e)(e.message)
62 exc = exceptions.VcsException(e)
63 raise exc(e)
62 except Exception as e:
64 except Exception as e:
63 # NOTE(marcink): becuase of how dulwich handles some exceptions
65 # NOTE(marcink): becuase of how dulwich handles some exceptions
64 # (KeyError on empty repos), we cannot track this and catch all
66 # (KeyError on empty repos), we cannot track this and catch all
65 # exceptions, it's an exceptions from other handlers
67 # exceptions, it's an exceptions from other handlers
66 #if not hasattr(e, '_vcs_kind'):
68 #if not hasattr(e, '_vcs_kind'):
67 #log.exception("Unhandled exception in git remote call")
69 #log.exception("Unhandled exception in git remote call")
68 #raise_from_original(exceptions.UnhandledException)
70 #raise_from_original(exceptions.UnhandledException)
69 raise
71 raise
70 return wrapper
72 return wrapper
71
73
72
74
73 class Repo(DulwichRepo):
75 class Repo(DulwichRepo):
74 """
76 """
75 A wrapper for dulwich Repo class.
77 A wrapper for dulwich Repo class.
76
78
77 Since dulwich is sometimes keeping .idx file descriptors open, it leads to
79 Since dulwich is sometimes keeping .idx file descriptors open, it leads to
78 "Too many open files" error. We need to close all opened file descriptors
80 "Too many open files" error. We need to close all opened file descriptors
79 once the repo object is destroyed.
81 once the repo object is destroyed.
80
82
81 TODO: mikhail: please check if we need this wrapper after updating dulwich
83 TODO: mikhail: please check if we need this wrapper after updating dulwich
82 to 0.12.0 +
84 to 0.12.0 +
83 """
85 """
84 def __del__(self):
86 def __del__(self):
85 if hasattr(self, 'object_store'):
87 if hasattr(self, 'object_store'):
86 self.close()
88 self.close()
87
89
88
90
89 class GitFactory(RepoFactory):
91 class GitFactory(RepoFactory):
90 repo_type = 'git'
92 repo_type = 'git'
91
93
92 def _create_repo(self, wire, create):
94 def _create_repo(self, wire, create):
93 repo_path = str_to_dulwich(wire['path'])
95 repo_path = str_to_dulwich(wire['path'])
94 return Repo(repo_path)
96 return Repo(repo_path)
95
97
96
98
97 class GitRemote(object):
99 class GitRemote(object):
98
100
99 def __init__(self, factory):
101 def __init__(self, factory):
100 self._factory = factory
102 self._factory = factory
101 self.peeled_ref_marker = '^{}'
103 self.peeled_ref_marker = '^{}'
102 self._bulk_methods = {
104 self._bulk_methods = {
103 "author": self.commit_attribute,
105 "author": self.commit_attribute,
104 "date": self.get_object_attrs,
106 "date": self.get_object_attrs,
105 "message": self.commit_attribute,
107 "message": self.commit_attribute,
106 "parents": self.commit_attribute,
108 "parents": self.commit_attribute,
107 "_commit": self.revision,
109 "_commit": self.revision,
108 }
110 }
109
111
110 def _wire_to_config(self, wire):
112 def _wire_to_config(self, wire):
111 if 'config' in wire:
113 if 'config' in wire:
112 return dict([(x[0] + '_' + x[1], x[2]) for x in wire['config']])
114 return dict([(x[0] + '_' + x[1], x[2]) for x in wire['config']])
113 return {}
115 return {}
114
116
115 def _assign_ref(self, wire, ref, commit_id):
117 def _assign_ref(self, wire, ref, commit_id):
116 repo = self._factory.repo(wire)
118 repo = self._factory.repo(wire)
117 repo[ref] = commit_id
119 repo[ref] = commit_id
118
120
119 @reraise_safe_exceptions
121 @reraise_safe_exceptions
120 def add_object(self, wire, content):
122 def add_object(self, wire, content):
121 repo = self._factory.repo(wire)
123 repo = self._factory.repo(wire)
122 blob = objects.Blob()
124 blob = objects.Blob()
123 blob.set_raw_string(content)
125 blob.set_raw_string(content)
124 repo.object_store.add_object(blob)
126 repo.object_store.add_object(blob)
125 return blob.id
127 return blob.id
126
128
127 @reraise_safe_exceptions
129 @reraise_safe_exceptions
128 def assert_correct_path(self, wire):
130 def assert_correct_path(self, wire):
129 path = wire.get('path')
131 path = wire.get('path')
130 try:
132 try:
131 self._factory.repo(wire)
133 self._factory.repo(wire)
132 except NotGitRepository as e:
134 except NotGitRepository as e:
133 tb = traceback.format_exc()
135 tb = traceback.format_exc()
134 log.debug("Invalid Git path `%s`, tb: %s", path, tb)
136 log.debug("Invalid Git path `%s`, tb: %s", path, tb)
135 return False
137 return False
136
138
137 return True
139 return True
138
140
139 @reraise_safe_exceptions
141 @reraise_safe_exceptions
140 def bare(self, wire):
142 def bare(self, wire):
141 repo = self._factory.repo(wire)
143 repo = self._factory.repo(wire)
142 return repo.bare
144 return repo.bare
143
145
144 @reraise_safe_exceptions
146 @reraise_safe_exceptions
145 def blob_as_pretty_string(self, wire, sha):
147 def blob_as_pretty_string(self, wire, sha):
146 repo = self._factory.repo(wire)
148 repo = self._factory.repo(wire)
147 return repo[sha].as_pretty_string()
149 return repo[sha].as_pretty_string()
148
150
149 @reraise_safe_exceptions
151 @reraise_safe_exceptions
150 def blob_raw_length(self, wire, sha):
152 def blob_raw_length(self, wire, sha):
151 repo = self._factory.repo(wire)
153 repo = self._factory.repo(wire)
152 blob = repo[sha]
154 blob = repo[sha]
153 return blob.raw_length()
155 return blob.raw_length()
154
156
155 def _parse_lfs_pointer(self, raw_content):
157 def _parse_lfs_pointer(self, raw_content):
156
158
157 spec_string = 'version https://git-lfs.github.com/spec'
159 spec_string = 'version https://git-lfs.github.com/spec'
158 if raw_content and raw_content.startswith(spec_string):
160 if raw_content and raw_content.startswith(spec_string):
159 pattern = re.compile(r"""
161 pattern = re.compile(r"""
160 (?:\n)?
162 (?:\n)?
161 ^version[ ]https://git-lfs\.github\.com/spec/(?P<spec_ver>v\d+)\n
163 ^version[ ]https://git-lfs\.github\.com/spec/(?P<spec_ver>v\d+)\n
162 ^oid[ ] sha256:(?P<oid_hash>[0-9a-f]{64})\n
164 ^oid[ ] sha256:(?P<oid_hash>[0-9a-f]{64})\n
163 ^size[ ](?P<oid_size>[0-9]+)\n
165 ^size[ ](?P<oid_size>[0-9]+)\n
164 (?:\n)?
166 (?:\n)?
165 """, re.VERBOSE | re.MULTILINE)
167 """, re.VERBOSE | re.MULTILINE)
166 match = pattern.match(raw_content)
168 match = pattern.match(raw_content)
167 if match:
169 if match:
168 return match.groupdict()
170 return match.groupdict()
169
171
170 return {}
172 return {}
171
173
172 @reraise_safe_exceptions
174 @reraise_safe_exceptions
173 def is_large_file(self, wire, sha):
175 def is_large_file(self, wire, sha):
174 repo = self._factory.repo(wire)
176 repo = self._factory.repo(wire)
175 blob = repo[sha]
177 blob = repo[sha]
176 return self._parse_lfs_pointer(blob.as_raw_string())
178 return self._parse_lfs_pointer(blob.as_raw_string())
177
179
178 @reraise_safe_exceptions
180 @reraise_safe_exceptions
179 def in_largefiles_store(self, wire, oid):
181 def in_largefiles_store(self, wire, oid):
180 repo = self._factory.repo(wire)
182 repo = self._factory.repo(wire)
181 conf = self._wire_to_config(wire)
183 conf = self._wire_to_config(wire)
182
184
183 store_location = conf.get('vcs_git_lfs_store_location')
185 store_location = conf.get('vcs_git_lfs_store_location')
184 if store_location:
186 if store_location:
185 repo_name = repo.path
187 repo_name = repo.path
186 store = LFSOidStore(
188 store = LFSOidStore(
187 oid=oid, repo=repo_name, store_location=store_location)
189 oid=oid, repo=repo_name, store_location=store_location)
188 return store.has_oid()
190 return store.has_oid()
189
191
190 return False
192 return False
191
193
192 @reraise_safe_exceptions
194 @reraise_safe_exceptions
193 def store_path(self, wire, oid):
195 def store_path(self, wire, oid):
194 repo = self._factory.repo(wire)
196 repo = self._factory.repo(wire)
195 conf = self._wire_to_config(wire)
197 conf = self._wire_to_config(wire)
196
198
197 store_location = conf.get('vcs_git_lfs_store_location')
199 store_location = conf.get('vcs_git_lfs_store_location')
198 if store_location:
200 if store_location:
199 repo_name = repo.path
201 repo_name = repo.path
200 store = LFSOidStore(
202 store = LFSOidStore(
201 oid=oid, repo=repo_name, store_location=store_location)
203 oid=oid, repo=repo_name, store_location=store_location)
202 return store.oid_path
204 return store.oid_path
203 raise ValueError('Unable to fetch oid with path {}'.format(oid))
205 raise ValueError('Unable to fetch oid with path {}'.format(oid))
204
206
205 @reraise_safe_exceptions
207 @reraise_safe_exceptions
206 def bulk_request(self, wire, rev, pre_load):
208 def bulk_request(self, wire, rev, pre_load):
207 result = {}
209 result = {}
208 for attr in pre_load:
210 for attr in pre_load:
209 try:
211 try:
210 method = self._bulk_methods[attr]
212 method = self._bulk_methods[attr]
211 args = [wire, rev]
213 args = [wire, rev]
212 if attr == "date":
214 if attr == "date":
213 args.extend(["commit_time", "commit_timezone"])
215 args.extend(["commit_time", "commit_timezone"])
214 elif attr in ["author", "message", "parents"]:
216 elif attr in ["author", "message", "parents"]:
215 args.append(attr)
217 args.append(attr)
216 result[attr] = method(*args)
218 result[attr] = method(*args)
217 except KeyError as e:
219 except KeyError as e:
218 raise exceptions.VcsException(e)(
220 raise exceptions.VcsException(e)(
219 "Unknown bulk attribute: %s" % attr)
221 "Unknown bulk attribute: %s" % attr)
220 return result
222 return result
221
223
222 def _build_opener(self, url):
224 def _build_opener(self, url):
223 handlers = []
225 handlers = []
224 url_obj = url_parser(url)
226 url_obj = url_parser(url)
225 _, authinfo = url_obj.authinfo()
227 _, authinfo = url_obj.authinfo()
226
228
227 if authinfo:
229 if authinfo:
228 # create a password manager
230 # create a password manager
229 passmgr = urllib2.HTTPPasswordMgrWithDefaultRealm()
231 passmgr = urllib2.HTTPPasswordMgrWithDefaultRealm()
230 passmgr.add_password(*authinfo)
232 passmgr.add_password(*authinfo)
231
233
232 handlers.extend((httpbasicauthhandler(passmgr),
234 handlers.extend((httpbasicauthhandler(passmgr),
233 httpdigestauthhandler(passmgr)))
235 httpdigestauthhandler(passmgr)))
234
236
235 return urllib2.build_opener(*handlers)
237 return urllib2.build_opener(*handlers)
236
238
237 @reraise_safe_exceptions
239 @reraise_safe_exceptions
238 def check_url(self, url, config):
240 def check_url(self, url, config):
239 url_obj = url_parser(url)
241 url_obj = url_parser(url)
240 test_uri, _ = url_obj.authinfo()
242 test_uri, _ = url_obj.authinfo()
241 url_obj.passwd = '*****' if url_obj.passwd else url_obj.passwd
243 url_obj.passwd = '*****' if url_obj.passwd else url_obj.passwd
242 url_obj.query = obfuscate_qs(url_obj.query)
244 url_obj.query = obfuscate_qs(url_obj.query)
243 cleaned_uri = str(url_obj)
245 cleaned_uri = str(url_obj)
244 log.info("Checking URL for remote cloning/import: %s", cleaned_uri)
246 log.info("Checking URL for remote cloning/import: %s", cleaned_uri)
245
247
246 if not test_uri.endswith('info/refs'):
248 if not test_uri.endswith('info/refs'):
247 test_uri = test_uri.rstrip('/') + '/info/refs'
249 test_uri = test_uri.rstrip('/') + '/info/refs'
248
250
249 o = self._build_opener(url)
251 o = self._build_opener(url)
250 o.addheaders = [('User-Agent', 'git/1.7.8.0')] # fake some git
252 o.addheaders = [('User-Agent', 'git/1.7.8.0')] # fake some git
251
253
252 q = {"service": 'git-upload-pack'}
254 q = {"service": 'git-upload-pack'}
253 qs = '?%s' % urllib.urlencode(q)
255 qs = '?%s' % urllib.urlencode(q)
254 cu = "%s%s" % (test_uri, qs)
256 cu = "%s%s" % (test_uri, qs)
255 req = urllib2.Request(cu, None, {})
257 req = urllib2.Request(cu, None, {})
256
258
257 try:
259 try:
258 log.debug("Trying to open URL %s", cleaned_uri)
260 log.debug("Trying to open URL %s", cleaned_uri)
259 resp = o.open(req)
261 resp = o.open(req)
260 if resp.code != 200:
262 if resp.code != 200:
261 raise exceptions.URLError()('Return Code is not 200')
263 raise exceptions.URLError()('Return Code is not 200')
262 except Exception as e:
264 except Exception as e:
263 log.warning("URL cannot be opened: %s", cleaned_uri, exc_info=True)
265 log.warning("URL cannot be opened: %s", cleaned_uri, exc_info=True)
264 # means it cannot be cloned
266 # means it cannot be cloned
265 raise exceptions.URLError(e)("[%s] org_exc: %s" % (cleaned_uri, e))
267 raise exceptions.URLError(e)("[%s] org_exc: %s" % (cleaned_uri, e))
266
268
267 # now detect if it's proper git repo
269 # now detect if it's proper git repo
268 gitdata = resp.read()
270 gitdata = resp.read()
269 if 'service=git-upload-pack' in gitdata:
271 if 'service=git-upload-pack' in gitdata:
270 pass
272 pass
271 elif re.findall(r'[0-9a-fA-F]{40}\s+refs', gitdata):
273 elif re.findall(r'[0-9a-fA-F]{40}\s+refs', gitdata):
272 # old style git can return some other format !
274 # old style git can return some other format !
273 pass
275 pass
274 else:
276 else:
275 raise exceptions.URLError()(
277 raise exceptions.URLError()(
276 "url [%s] does not look like an git" % (cleaned_uri,))
278 "url [%s] does not look like an git" % (cleaned_uri,))
277
279
278 return True
280 return True
279
281
280 @reraise_safe_exceptions
282 @reraise_safe_exceptions
281 def clone(self, wire, url, deferred, valid_refs, update_after_clone):
283 def clone(self, wire, url, deferred, valid_refs, update_after_clone):
282 # TODO(marcink): deprecate this method. Last i checked we don't use it anymore
284 # TODO(marcink): deprecate this method. Last i checked we don't use it anymore
283 remote_refs = self.pull(wire, url, apply_refs=False)
285 remote_refs = self.pull(wire, url, apply_refs=False)
284 repo = self._factory.repo(wire)
286 repo = self._factory.repo(wire)
285 if isinstance(valid_refs, list):
287 if isinstance(valid_refs, list):
286 valid_refs = tuple(valid_refs)
288 valid_refs = tuple(valid_refs)
287
289
288 for k in remote_refs:
290 for k in remote_refs:
289 # only parse heads/tags and skip so called deferred tags
291 # only parse heads/tags and skip so called deferred tags
290 if k.startswith(valid_refs) and not k.endswith(deferred):
292 if k.startswith(valid_refs) and not k.endswith(deferred):
291 repo[k] = remote_refs[k]
293 repo[k] = remote_refs[k]
292
294
293 if update_after_clone:
295 if update_after_clone:
294 # we want to checkout HEAD
296 # we want to checkout HEAD
295 repo["HEAD"] = remote_refs["HEAD"]
297 repo["HEAD"] = remote_refs["HEAD"]
296 index.build_index_from_tree(repo.path, repo.index_path(),
298 index.build_index_from_tree(repo.path, repo.index_path(),
297 repo.object_store, repo["HEAD"].tree)
299 repo.object_store, repo["HEAD"].tree)
298
300
299 # TODO: this is quite complex, check if that can be simplified
301 # TODO: this is quite complex, check if that can be simplified
300 @reraise_safe_exceptions
302 @reraise_safe_exceptions
301 def commit(self, wire, commit_data, branch, commit_tree, updated, removed):
303 def commit(self, wire, commit_data, branch, commit_tree, updated, removed):
302 repo = self._factory.repo(wire)
304 repo = self._factory.repo(wire)
303 object_store = repo.object_store
305 object_store = repo.object_store
304
306
305 # Create tree and populates it with blobs
307 # Create tree and populates it with blobs
306 commit_tree = commit_tree and repo[commit_tree] or objects.Tree()
308 commit_tree = commit_tree and repo[commit_tree] or objects.Tree()
307
309
308 for node in updated:
310 for node in updated:
309 # Compute subdirs if needed
311 # Compute subdirs if needed
310 dirpath, nodename = vcspath.split(node['path'])
312 dirpath, nodename = vcspath.split(node['path'])
311 dirnames = map(safe_str, dirpath and dirpath.split('/') or [])
313 dirnames = map(safe_str, dirpath and dirpath.split('/') or [])
312 parent = commit_tree
314 parent = commit_tree
313 ancestors = [('', parent)]
315 ancestors = [('', parent)]
314
316
315 # Tries to dig for the deepest existing tree
317 # Tries to dig for the deepest existing tree
316 while dirnames:
318 while dirnames:
317 curdir = dirnames.pop(0)
319 curdir = dirnames.pop(0)
318 try:
320 try:
319 dir_id = parent[curdir][1]
321 dir_id = parent[curdir][1]
320 except KeyError:
322 except KeyError:
321 # put curdir back into dirnames and stops
323 # put curdir back into dirnames and stops
322 dirnames.insert(0, curdir)
324 dirnames.insert(0, curdir)
323 break
325 break
324 else:
326 else:
325 # If found, updates parent
327 # If found, updates parent
326 parent = repo[dir_id]
328 parent = repo[dir_id]
327 ancestors.append((curdir, parent))
329 ancestors.append((curdir, parent))
328 # Now parent is deepest existing tree and we need to create
330 # Now parent is deepest existing tree and we need to create
329 # subtrees for dirnames (in reverse order)
331 # subtrees for dirnames (in reverse order)
330 # [this only applies for nodes from added]
332 # [this only applies for nodes from added]
331 new_trees = []
333 new_trees = []
332
334
333 blob = objects.Blob.from_string(node['content'])
335 blob = objects.Blob.from_string(node['content'])
334
336
335 if dirnames:
337 if dirnames:
336 # If there are trees which should be created we need to build
338 # If there are trees which should be created we need to build
337 # them now (in reverse order)
339 # them now (in reverse order)
338 reversed_dirnames = list(reversed(dirnames))
340 reversed_dirnames = list(reversed(dirnames))
339 curtree = objects.Tree()
341 curtree = objects.Tree()
340 curtree[node['node_path']] = node['mode'], blob.id
342 curtree[node['node_path']] = node['mode'], blob.id
341 new_trees.append(curtree)
343 new_trees.append(curtree)
342 for dirname in reversed_dirnames[:-1]:
344 for dirname in reversed_dirnames[:-1]:
343 newtree = objects.Tree()
345 newtree = objects.Tree()
344 newtree[dirname] = (DIR_STAT, curtree.id)
346 newtree[dirname] = (DIR_STAT, curtree.id)
345 new_trees.append(newtree)
347 new_trees.append(newtree)
346 curtree = newtree
348 curtree = newtree
347 parent[reversed_dirnames[-1]] = (DIR_STAT, curtree.id)
349 parent[reversed_dirnames[-1]] = (DIR_STAT, curtree.id)
348 else:
350 else:
349 parent.add(
351 parent.add(
350 name=node['node_path'], mode=node['mode'], hexsha=blob.id)
352 name=node['node_path'], mode=node['mode'], hexsha=blob.id)
351
353
352 new_trees.append(parent)
354 new_trees.append(parent)
353 # Update ancestors
355 # Update ancestors
354 reversed_ancestors = reversed(
356 reversed_ancestors = reversed(
355 [(a[1], b[1], b[0]) for a, b in zip(ancestors, ancestors[1:])])
357 [(a[1], b[1], b[0]) for a, b in zip(ancestors, ancestors[1:])])
356 for parent, tree, path in reversed_ancestors:
358 for parent, tree, path in reversed_ancestors:
357 parent[path] = (DIR_STAT, tree.id)
359 parent[path] = (DIR_STAT, tree.id)
358 object_store.add_object(tree)
360 object_store.add_object(tree)
359
361
360 object_store.add_object(blob)
362 object_store.add_object(blob)
361 for tree in new_trees:
363 for tree in new_trees:
362 object_store.add_object(tree)
364 object_store.add_object(tree)
363
365
364 for node_path in removed:
366 for node_path in removed:
365 paths = node_path.split('/')
367 paths = node_path.split('/')
366 tree = commit_tree
368 tree = commit_tree
367 trees = [tree]
369 trees = [tree]
368 # Traverse deep into the forest...
370 # Traverse deep into the forest...
369 for path in paths:
371 for path in paths:
370 try:
372 try:
371 obj = repo[tree[path][1]]
373 obj = repo[tree[path][1]]
372 if isinstance(obj, objects.Tree):
374 if isinstance(obj, objects.Tree):
373 trees.append(obj)
375 trees.append(obj)
374 tree = obj
376 tree = obj
375 except KeyError:
377 except KeyError:
376 break
378 break
377 # Cut down the blob and all rotten trees on the way back...
379 # Cut down the blob and all rotten trees on the way back...
378 for path, tree in reversed(zip(paths, trees)):
380 for path, tree in reversed(zip(paths, trees)):
379 del tree[path]
381 del tree[path]
380 if tree:
382 if tree:
381 # This tree still has elements - don't remove it or any
383 # This tree still has elements - don't remove it or any
382 # of it's parents
384 # of it's parents
383 break
385 break
384
386
385 object_store.add_object(commit_tree)
387 object_store.add_object(commit_tree)
386
388
387 # Create commit
389 # Create commit
388 commit = objects.Commit()
390 commit = objects.Commit()
389 commit.tree = commit_tree.id
391 commit.tree = commit_tree.id
390 for k, v in commit_data.iteritems():
392 for k, v in commit_data.iteritems():
391 setattr(commit, k, v)
393 setattr(commit, k, v)
392 object_store.add_object(commit)
394 object_store.add_object(commit)
393
395
394 ref = 'refs/heads/%s' % branch
396 ref = 'refs/heads/%s' % branch
395 repo.refs[ref] = commit.id
397 repo.refs[ref] = commit.id
396
398
397 return commit.id
399 return commit.id
398
400
399 @reraise_safe_exceptions
401 @reraise_safe_exceptions
400 def pull(self, wire, url, apply_refs=True, refs=None, update_after=False):
402 def pull(self, wire, url, apply_refs=True, refs=None, update_after=False):
401 if url != 'default' and '://' not in url:
403 if url != 'default' and '://' not in url:
402 client = LocalGitClient(url)
404 client = LocalGitClient(url)
403 else:
405 else:
404 url_obj = url_parser(url)
406 url_obj = url_parser(url)
405 o = self._build_opener(url)
407 o = self._build_opener(url)
406 url, _ = url_obj.authinfo()
408 url, _ = url_obj.authinfo()
407 client = HttpGitClient(base_url=url, opener=o)
409 client = HttpGitClient(base_url=url, opener=o)
408 repo = self._factory.repo(wire)
410 repo = self._factory.repo(wire)
409
411
410 determine_wants = repo.object_store.determine_wants_all
412 determine_wants = repo.object_store.determine_wants_all
411 if refs:
413 if refs:
412 def determine_wants_requested(references):
414 def determine_wants_requested(references):
413 return [references[r] for r in references if r in refs]
415 return [references[r] for r in references if r in refs]
414 determine_wants = determine_wants_requested
416 determine_wants = determine_wants_requested
415
417
416 try:
418 try:
417 remote_refs = client.fetch(
419 remote_refs = client.fetch(
418 path=url, target=repo, determine_wants=determine_wants)
420 path=url, target=repo, determine_wants=determine_wants)
419 except NotGitRepository as e:
421 except NotGitRepository as e:
420 log.warning(
422 log.warning(
421 'Trying to fetch from "%s" failed, not a Git repository.', url)
423 'Trying to fetch from "%s" failed, not a Git repository.', url)
422 # Exception can contain unicode which we convert
424 # Exception can contain unicode which we convert
423 raise exceptions.AbortException(e)(repr(e))
425 raise exceptions.AbortException(e)(repr(e))
424
426
425 # mikhail: client.fetch() returns all the remote refs, but fetches only
427 # mikhail: client.fetch() returns all the remote refs, but fetches only
426 # refs filtered by `determine_wants` function. We need to filter result
428 # refs filtered by `determine_wants` function. We need to filter result
427 # as well
429 # as well
428 if refs:
430 if refs:
429 remote_refs = {k: remote_refs[k] for k in remote_refs if k in refs}
431 remote_refs = {k: remote_refs[k] for k in remote_refs if k in refs}
430
432
431 if apply_refs:
433 if apply_refs:
432 # TODO: johbo: Needs proper test coverage with a git repository
434 # TODO: johbo: Needs proper test coverage with a git repository
433 # that contains a tag object, so that we would end up with
435 # that contains a tag object, so that we would end up with
434 # a peeled ref at this point.
436 # a peeled ref at this point.
435 for k in remote_refs:
437 for k in remote_refs:
436 if k.endswith(self.peeled_ref_marker):
438 if k.endswith(self.peeled_ref_marker):
437 log.debug("Skipping peeled reference %s", k)
439 log.debug("Skipping peeled reference %s", k)
438 continue
440 continue
439 repo[k] = remote_refs[k]
441 repo[k] = remote_refs[k]
440
442
441 if refs and not update_after:
443 if refs and not update_after:
442 # mikhail: explicitly set the head to the last ref.
444 # mikhail: explicitly set the head to the last ref.
443 repo['HEAD'] = remote_refs[refs[-1]]
445 repo['HEAD'] = remote_refs[refs[-1]]
444
446
445 if update_after:
447 if update_after:
446 # we want to checkout HEAD
448 # we want to checkout HEAD
447 repo["HEAD"] = remote_refs["HEAD"]
449 repo["HEAD"] = remote_refs["HEAD"]
448 index.build_index_from_tree(repo.path, repo.index_path(),
450 index.build_index_from_tree(repo.path, repo.index_path(),
449 repo.object_store, repo["HEAD"].tree)
451 repo.object_store, repo["HEAD"].tree)
450 return remote_refs
452 return remote_refs
451
453
452 @reraise_safe_exceptions
454 @reraise_safe_exceptions
453 def sync_fetch(self, wire, url, refs=None):
455 def sync_fetch(self, wire, url, refs=None):
454 repo = self._factory.repo(wire)
456 repo = self._factory.repo(wire)
455 if refs and not isinstance(refs, (list, tuple)):
457 if refs and not isinstance(refs, (list, tuple)):
456 refs = [refs]
458 refs = [refs]
457
459
458 # get all remote refs we'll use to fetch later
460 # get all remote refs we'll use to fetch later
459 output, __ = self.run_git_command(
461 output, __ = self.run_git_command(
460 wire, ['ls-remote', url], fail_on_stderr=False,
462 wire, ['ls-remote', url], fail_on_stderr=False,
461 _copts=['-c', 'core.askpass=""'],
463 _copts=['-c', 'core.askpass=""'],
462 extra_env={'GIT_TERMINAL_PROMPT': '0'})
464 extra_env={'GIT_TERMINAL_PROMPT': '0'})
463
465
464 remote_refs = collections.OrderedDict()
466 remote_refs = collections.OrderedDict()
465 fetch_refs = []
467 fetch_refs = []
466
468
467 for ref_line in output.splitlines():
469 for ref_line in output.splitlines():
468 sha, ref = ref_line.split('\t')
470 sha, ref = ref_line.split('\t')
469 sha = sha.strip()
471 sha = sha.strip()
470 if ref in remote_refs:
472 if ref in remote_refs:
471 # duplicate, skip
473 # duplicate, skip
472 continue
474 continue
473 if ref.endswith(self.peeled_ref_marker):
475 if ref.endswith(self.peeled_ref_marker):
474 log.debug("Skipping peeled reference %s", ref)
476 log.debug("Skipping peeled reference %s", ref)
475 continue
477 continue
476 # don't sync HEAD
478 # don't sync HEAD
477 if ref in ['HEAD']:
479 if ref in ['HEAD']:
478 continue
480 continue
479
481
480 remote_refs[ref] = sha
482 remote_refs[ref] = sha
481
483
482 if refs and sha in refs:
484 if refs and sha in refs:
483 # we filter fetch using our specified refs
485 # we filter fetch using our specified refs
484 fetch_refs.append('{}:{}'.format(ref, ref))
486 fetch_refs.append('{}:{}'.format(ref, ref))
485 elif not refs:
487 elif not refs:
486 fetch_refs.append('{}:{}'.format(ref, ref))
488 fetch_refs.append('{}:{}'.format(ref, ref))
487
489
488 if fetch_refs:
490 if fetch_refs:
489 _out, _err = self.run_git_command(
491 _out, _err = self.run_git_command(
490 wire, ['fetch', url, '--force', '--prune', '--'] + fetch_refs,
492 wire, ['fetch', url, '--force', '--prune', '--'] + fetch_refs,
491 fail_on_stderr=False,
493 fail_on_stderr=False,
492 _copts=['-c', 'core.askpass=""'],
494 _copts=['-c', 'core.askpass=""'],
493 extra_env={'GIT_TERMINAL_PROMPT': '0'})
495 extra_env={'GIT_TERMINAL_PROMPT': '0'})
494
496
495 return remote_refs
497 return remote_refs
496
498
497 @reraise_safe_exceptions
499 @reraise_safe_exceptions
498 def sync_push(self, wire, url, refs=None):
500 def sync_push(self, wire, url, refs=None):
499 if not self.check_url(url, wire):
501 if not self.check_url(url, wire):
500 return
502 return
501
503
502 repo = self._factory.repo(wire)
504 repo = self._factory.repo(wire)
503 self.run_git_command(
505 self.run_git_command(
504 wire, ['push', url, '--mirror'], fail_on_stderr=False,
506 wire, ['push', url, '--mirror'], fail_on_stderr=False,
505 _copts=['-c', 'core.askpass=""'],
507 _copts=['-c', 'core.askpass=""'],
506 extra_env={'GIT_TERMINAL_PROMPT': '0'})
508 extra_env={'GIT_TERMINAL_PROMPT': '0'})
507
509
508 @reraise_safe_exceptions
510 @reraise_safe_exceptions
509 def get_remote_refs(self, wire, url):
511 def get_remote_refs(self, wire, url):
510 repo = Repo(url)
512 repo = Repo(url)
511 return repo.get_refs()
513 return repo.get_refs()
512
514
513 @reraise_safe_exceptions
515 @reraise_safe_exceptions
514 def get_description(self, wire):
516 def get_description(self, wire):
515 repo = self._factory.repo(wire)
517 repo = self._factory.repo(wire)
516 return repo.get_description()
518 return repo.get_description()
517
519
518 @reraise_safe_exceptions
520 @reraise_safe_exceptions
519 def get_file_history(self, wire, file_path, commit_id, limit):
521 def get_file_history(self, wire, file_path, commit_id, limit):
520 repo = self._factory.repo(wire)
522 repo = self._factory.repo(wire)
521 include = [commit_id]
523 include = [commit_id]
522 paths = [file_path]
524 paths = [file_path]
523
525
524 walker = repo.get_walker(include, paths=paths, max_entries=limit)
526 walker = repo.get_walker(include, paths=paths, max_entries=limit)
525 return [x.commit.id for x in walker]
527 return [x.commit.id for x in walker]
526
528
527 @reraise_safe_exceptions
529 @reraise_safe_exceptions
528 def get_missing_revs(self, wire, rev1, rev2, path2):
530 def get_missing_revs(self, wire, rev1, rev2, path2):
529 repo = self._factory.repo(wire)
531 repo = self._factory.repo(wire)
530 LocalGitClient(thin_packs=False).fetch(path2, repo)
532 LocalGitClient(thin_packs=False).fetch(path2, repo)
531
533
532 wire_remote = wire.copy()
534 wire_remote = wire.copy()
533 wire_remote['path'] = path2
535 wire_remote['path'] = path2
534 repo_remote = self._factory.repo(wire_remote)
536 repo_remote = self._factory.repo(wire_remote)
535 LocalGitClient(thin_packs=False).fetch(wire["path"], repo_remote)
537 LocalGitClient(thin_packs=False).fetch(wire["path"], repo_remote)
536
538
537 revs = [
539 revs = [
538 x.commit.id
540 x.commit.id
539 for x in repo_remote.get_walker(include=[rev2], exclude=[rev1])]
541 for x in repo_remote.get_walker(include=[rev2], exclude=[rev1])]
540 return revs
542 return revs
541
543
542 @reraise_safe_exceptions
544 @reraise_safe_exceptions
543 def get_object(self, wire, sha):
545 def get_object(self, wire, sha):
544 repo = self._factory.repo(wire)
546 repo = self._factory.repo(wire)
545 obj = repo.get_object(sha)
547 obj = repo.get_object(sha)
546 commit_id = obj.id
548 commit_id = obj.id
547
549
548 if isinstance(obj, Tag):
550 if isinstance(obj, Tag):
549 commit_id = obj.object[1]
551 commit_id = obj.object[1]
550
552
551 return {
553 return {
552 'id': obj.id,
554 'id': obj.id,
553 'type': obj.type_name,
555 'type': obj.type_name,
554 'commit_id': commit_id
556 'commit_id': commit_id
555 }
557 }
556
558
557 @reraise_safe_exceptions
559 @reraise_safe_exceptions
558 def get_object_attrs(self, wire, sha, *attrs):
560 def get_object_attrs(self, wire, sha, *attrs):
559 repo = self._factory.repo(wire)
561 repo = self._factory.repo(wire)
560 obj = repo.get_object(sha)
562 obj = repo.get_object(sha)
561 return list(getattr(obj, a) for a in attrs)
563 return list(getattr(obj, a) for a in attrs)
562
564
563 @reraise_safe_exceptions
565 @reraise_safe_exceptions
564 def get_refs(self, wire):
566 def get_refs(self, wire):
565 repo = self._factory.repo(wire)
567 repo = self._factory.repo(wire)
566 result = {}
568 result = {}
567 for ref, sha in repo.refs.as_dict().items():
569 for ref, sha in repo.refs.as_dict().items():
568 peeled_sha = repo.get_peeled(ref)
570 peeled_sha = repo.get_peeled(ref)
569 result[ref] = peeled_sha
571 result[ref] = peeled_sha
570 return result
572 return result
571
573
572 @reraise_safe_exceptions
574 @reraise_safe_exceptions
573 def get_refs_path(self, wire):
575 def get_refs_path(self, wire):
574 repo = self._factory.repo(wire)
576 repo = self._factory.repo(wire)
575 return repo.refs.path
577 return repo.refs.path
576
578
577 @reraise_safe_exceptions
579 @reraise_safe_exceptions
578 def head(self, wire, show_exc=True):
580 def head(self, wire, show_exc=True):
579 repo = self._factory.repo(wire)
581 repo = self._factory.repo(wire)
580 try:
582 try:
581 return repo.head()
583 return repo.head()
582 except Exception:
584 except Exception:
583 if show_exc:
585 if show_exc:
584 raise
586 raise
585
587
586 @reraise_safe_exceptions
588 @reraise_safe_exceptions
587 def init(self, wire):
589 def init(self, wire):
588 repo_path = str_to_dulwich(wire['path'])
590 repo_path = str_to_dulwich(wire['path'])
589 self.repo = Repo.init(repo_path)
591 self.repo = Repo.init(repo_path)
590
592
591 @reraise_safe_exceptions
593 @reraise_safe_exceptions
592 def init_bare(self, wire):
594 def init_bare(self, wire):
593 repo_path = str_to_dulwich(wire['path'])
595 repo_path = str_to_dulwich(wire['path'])
594 self.repo = Repo.init_bare(repo_path)
596 self.repo = Repo.init_bare(repo_path)
595
597
596 @reraise_safe_exceptions
598 @reraise_safe_exceptions
597 def revision(self, wire, rev):
599 def revision(self, wire, rev):
598 repo = self._factory.repo(wire)
600 repo = self._factory.repo(wire)
599 obj = repo[rev]
601 obj = repo[rev]
600 obj_data = {
602 obj_data = {
601 'id': obj.id,
603 'id': obj.id,
602 }
604 }
603 try:
605 try:
604 obj_data['tree'] = obj.tree
606 obj_data['tree'] = obj.tree
605 except AttributeError:
607 except AttributeError:
606 pass
608 pass
607 return obj_data
609 return obj_data
608
610
609 @reraise_safe_exceptions
611 @reraise_safe_exceptions
610 def commit_attribute(self, wire, rev, attr):
612 def commit_attribute(self, wire, rev, attr):
611 repo = self._factory.repo(wire)
613 repo = self._factory.repo(wire)
612 obj = repo[rev]
614 obj = repo[rev]
613 return getattr(obj, attr)
615 return getattr(obj, attr)
614
616
615 @reraise_safe_exceptions
617 @reraise_safe_exceptions
616 def set_refs(self, wire, key, value):
618 def set_refs(self, wire, key, value):
617 repo = self._factory.repo(wire)
619 repo = self._factory.repo(wire)
618 repo.refs[key] = value
620 repo.refs[key] = value
619
621
620 @reraise_safe_exceptions
622 @reraise_safe_exceptions
621 def remove_ref(self, wire, key):
623 def remove_ref(self, wire, key):
622 repo = self._factory.repo(wire)
624 repo = self._factory.repo(wire)
623 del repo.refs[key]
625 del repo.refs[key]
624
626
625 @reraise_safe_exceptions
627 @reraise_safe_exceptions
626 def tree_changes(self, wire, source_id, target_id):
628 def tree_changes(self, wire, source_id, target_id):
627 repo = self._factory.repo(wire)
629 repo = self._factory.repo(wire)
628 source = repo[source_id].tree if source_id else None
630 source = repo[source_id].tree if source_id else None
629 target = repo[target_id].tree
631 target = repo[target_id].tree
630 result = repo.object_store.tree_changes(source, target)
632 result = repo.object_store.tree_changes(source, target)
631 return list(result)
633 return list(result)
632
634
633 @reraise_safe_exceptions
635 @reraise_safe_exceptions
634 def tree_items(self, wire, tree_id):
636 def tree_items(self, wire, tree_id):
635 repo = self._factory.repo(wire)
637 repo = self._factory.repo(wire)
636 tree = repo[tree_id]
638 tree = repo[tree_id]
637
639
638 result = []
640 result = []
639 for item in tree.iteritems():
641 for item in tree.iteritems():
640 item_sha = item.sha
642 item_sha = item.sha
641 item_mode = item.mode
643 item_mode = item.mode
642
644
643 if FILE_MODE(item_mode) == GIT_LINK:
645 if FILE_MODE(item_mode) == GIT_LINK:
644 item_type = "link"
646 item_type = "link"
645 else:
647 else:
646 item_type = repo[item_sha].type_name
648 item_type = repo[item_sha].type_name
647
649
648 result.append((item.path, item_mode, item_sha, item_type))
650 result.append((item.path, item_mode, item_sha, item_type))
649 return result
651 return result
650
652
651 @reraise_safe_exceptions
653 @reraise_safe_exceptions
652 def update_server_info(self, wire):
654 def update_server_info(self, wire):
653 repo = self._factory.repo(wire)
655 repo = self._factory.repo(wire)
654 update_server_info(repo)
656 update_server_info(repo)
655
657
656 @reraise_safe_exceptions
658 @reraise_safe_exceptions
657 def discover_git_version(self):
659 def discover_git_version(self):
658 stdout, _ = self.run_git_command(
660 stdout, _ = self.run_git_command(
659 {}, ['--version'], _bare=True, _safe=True)
661 {}, ['--version'], _bare=True, _safe=True)
660 prefix = 'git version'
662 prefix = 'git version'
661 if stdout.startswith(prefix):
663 if stdout.startswith(prefix):
662 stdout = stdout[len(prefix):]
664 stdout = stdout[len(prefix):]
663 return stdout.strip()
665 return stdout.strip()
664
666
665 @reraise_safe_exceptions
667 @reraise_safe_exceptions
666 def run_git_command(self, wire, cmd, **opts):
668 def run_git_command(self, wire, cmd, **opts):
667 path = wire.get('path', None)
669 path = wire.get('path', None)
668
670
669 if path and os.path.isdir(path):
671 if path and os.path.isdir(path):
670 opts['cwd'] = path
672 opts['cwd'] = path
671
673
672 if '_bare' in opts:
674 if '_bare' in opts:
673 _copts = []
675 _copts = []
674 del opts['_bare']
676 del opts['_bare']
675 else:
677 else:
676 _copts = ['-c', 'core.quotepath=false', ]
678 _copts = ['-c', 'core.quotepath=false', ]
677 safe_call = False
679 safe_call = False
678 if '_safe' in opts:
680 if '_safe' in opts:
679 # no exc on failure
681 # no exc on failure
680 del opts['_safe']
682 del opts['_safe']
681 safe_call = True
683 safe_call = True
682
684
683 if '_copts' in opts:
685 if '_copts' in opts:
684 _copts.extend(opts['_copts'] or [])
686 _copts.extend(opts['_copts'] or [])
685 del opts['_copts']
687 del opts['_copts']
686
688
687 gitenv = os.environ.copy()
689 gitenv = os.environ.copy()
688 gitenv.update(opts.pop('extra_env', {}))
690 gitenv.update(opts.pop('extra_env', {}))
689 # need to clean fix GIT_DIR !
691 # need to clean fix GIT_DIR !
690 if 'GIT_DIR' in gitenv:
692 if 'GIT_DIR' in gitenv:
691 del gitenv['GIT_DIR']
693 del gitenv['GIT_DIR']
692 gitenv['GIT_CONFIG_NOGLOBAL'] = '1'
694 gitenv['GIT_CONFIG_NOGLOBAL'] = '1'
693 gitenv['GIT_DISCOVERY_ACROSS_FILESYSTEM'] = '1'
695 gitenv['GIT_DISCOVERY_ACROSS_FILESYSTEM'] = '1'
694
696
695 cmd = [settings.GIT_EXECUTABLE] + _copts + cmd
697 cmd = [settings.GIT_EXECUTABLE] + _copts + cmd
696 _opts = {'env': gitenv, 'shell': False}
698 _opts = {'env': gitenv, 'shell': False}
697
699
698 try:
700 try:
699 _opts.update(opts)
701 _opts.update(opts)
700 p = subprocessio.SubprocessIOChunker(cmd, **_opts)
702 p = subprocessio.SubprocessIOChunker(cmd, **_opts)
701
703
702 return ''.join(p), ''.join(p.error)
704 return ''.join(p), ''.join(p.error)
703 except (EnvironmentError, OSError) as err:
705 except (EnvironmentError, OSError) as err:
704 cmd = ' '.join(cmd) # human friendly CMD
706 cmd = ' '.join(cmd) # human friendly CMD
705 tb_err = ("Couldn't run git command (%s).\n"
707 tb_err = ("Couldn't run git command (%s).\n"
706 "Original error was:%s\n"
708 "Original error was:%s\n"
707 "Call options:%s\n"
709 "Call options:%s\n"
708 % (cmd, err, _opts))
710 % (cmd, err, _opts))
709 log.exception(tb_err)
711 log.exception(tb_err)
710 if safe_call:
712 if safe_call:
711 return '', err
713 return '', err
712 else:
714 else:
713 raise exceptions.VcsException()(tb_err)
715 raise exceptions.VcsException()(tb_err)
714
716
715 @reraise_safe_exceptions
717 @reraise_safe_exceptions
716 def install_hooks(self, wire, force=False):
718 def install_hooks(self, wire, force=False):
717 from vcsserver.hook_utils import install_git_hooks
719 from vcsserver.hook_utils import install_git_hooks
718 repo = self._factory.repo(wire)
720 repo = self._factory.repo(wire)
719 return install_git_hooks(repo.path, repo.bare, force_create=force)
721 return install_git_hooks(repo.path, repo.bare, force_create=force)
720
722
721
723
722 def str_to_dulwich(value):
724 def str_to_dulwich(value):
723 """
725 """
724 Dulwich 0.10.1a requires `unicode` objects to be passed in.
726 Dulwich 0.10.1a requires `unicode` objects to be passed in.
725 """
727 """
726 return value.decode(settings.WIRE_ENCODING)
728 return value.decode(settings.WIRE_ENCODING)
General Comments 0
You need to be logged in to leave comments. Login now