##// END OF EJS Templates
git: don not verify dangling commit branch assignment for tags
marcink -
r747:9c2265ca default
parent child Browse files
Show More
@@ -1,1092 +1,1094 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-2019 RhodeCode GmbH
2 # Copyright (C) 2014-2019 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
17
18 import collections
18 import collections
19 import logging
19 import logging
20 import os
20 import os
21 import posixpath as vcspath
21 import posixpath as vcspath
22 import re
22 import re
23 import stat
23 import stat
24 import traceback
24 import traceback
25 import urllib
25 import urllib
26 import urllib2
26 import urllib2
27 from functools import wraps
27 from functools import wraps
28
28
29 import more_itertools
29 import more_itertools
30 import pygit2
30 import pygit2
31 from pygit2 import Repository as LibGit2Repo
31 from pygit2 import Repository as LibGit2Repo
32 from dulwich import index, objects
32 from dulwich import index, objects
33 from dulwich.client import HttpGitClient, LocalGitClient
33 from dulwich.client import HttpGitClient, LocalGitClient
34 from dulwich.errors import (
34 from dulwich.errors import (
35 NotGitRepository, ChecksumMismatch, WrongObjectException,
35 NotGitRepository, ChecksumMismatch, WrongObjectException,
36 MissingCommitError, ObjectMissing, HangupException,
36 MissingCommitError, ObjectMissing, HangupException,
37 UnexpectedCommandError)
37 UnexpectedCommandError)
38 from dulwich.repo import Repo as DulwichRepo
38 from dulwich.repo import Repo as DulwichRepo
39 from dulwich.server import update_server_info
39 from dulwich.server import update_server_info
40
40
41 from vcsserver import exceptions, settings, subprocessio
41 from vcsserver import exceptions, settings, subprocessio
42 from vcsserver.utils import safe_str, safe_int
42 from vcsserver.utils import safe_str, safe_int
43 from vcsserver.base import RepoFactory, obfuscate_qs
43 from vcsserver.base import RepoFactory, obfuscate_qs
44 from vcsserver.hgcompat import (
44 from vcsserver.hgcompat import (
45 hg_url as url_parser, httpbasicauthhandler, httpdigestauthhandler)
45 hg_url as url_parser, httpbasicauthhandler, httpdigestauthhandler)
46 from vcsserver.git_lfs.lib import LFSOidStore
46 from vcsserver.git_lfs.lib import LFSOidStore
47
47
48 DIR_STAT = stat.S_IFDIR
48 DIR_STAT = stat.S_IFDIR
49 FILE_MODE = stat.S_IFMT
49 FILE_MODE = stat.S_IFMT
50 GIT_LINK = objects.S_IFGITLINK
50 GIT_LINK = objects.S_IFGITLINK
51 PEELED_REF_MARKER = '^{}'
51 PEELED_REF_MARKER = '^{}'
52
52
53
53
54 log = logging.getLogger(__name__)
54 log = logging.getLogger(__name__)
55
55
56
56
57 def str_to_dulwich(value):
57 def str_to_dulwich(value):
58 """
58 """
59 Dulwich 0.10.1a requires `unicode` objects to be passed in.
59 Dulwich 0.10.1a requires `unicode` objects to be passed in.
60 """
60 """
61 return value.decode(settings.WIRE_ENCODING)
61 return value.decode(settings.WIRE_ENCODING)
62
62
63
63
64 def reraise_safe_exceptions(func):
64 def reraise_safe_exceptions(func):
65 """Converts Dulwich exceptions to something neutral."""
65 """Converts Dulwich exceptions to something neutral."""
66
66
67 @wraps(func)
67 @wraps(func)
68 def wrapper(*args, **kwargs):
68 def wrapper(*args, **kwargs):
69 try:
69 try:
70 return func(*args, **kwargs)
70 return func(*args, **kwargs)
71 except (ChecksumMismatch, WrongObjectException, MissingCommitError, ObjectMissing,) as e:
71 except (ChecksumMismatch, WrongObjectException, MissingCommitError, ObjectMissing,) as e:
72 exc = exceptions.LookupException(org_exc=e)
72 exc = exceptions.LookupException(org_exc=e)
73 raise exc(safe_str(e))
73 raise exc(safe_str(e))
74 except (HangupException, UnexpectedCommandError) as e:
74 except (HangupException, UnexpectedCommandError) as e:
75 exc = exceptions.VcsException(org_exc=e)
75 exc = exceptions.VcsException(org_exc=e)
76 raise exc(safe_str(e))
76 raise exc(safe_str(e))
77 except Exception as e:
77 except Exception as e:
78 # NOTE(marcink): becuase of how dulwich handles some exceptions
78 # NOTE(marcink): becuase of how dulwich handles some exceptions
79 # (KeyError on empty repos), we cannot track this and catch all
79 # (KeyError on empty repos), we cannot track this and catch all
80 # exceptions, it's an exceptions from other handlers
80 # exceptions, it's an exceptions from other handlers
81 #if not hasattr(e, '_vcs_kind'):
81 #if not hasattr(e, '_vcs_kind'):
82 #log.exception("Unhandled exception in git remote call")
82 #log.exception("Unhandled exception in git remote call")
83 #raise_from_original(exceptions.UnhandledException)
83 #raise_from_original(exceptions.UnhandledException)
84 raise
84 raise
85 return wrapper
85 return wrapper
86
86
87
87
88 class Repo(DulwichRepo):
88 class Repo(DulwichRepo):
89 """
89 """
90 A wrapper for dulwich Repo class.
90 A wrapper for dulwich Repo class.
91
91
92 Since dulwich is sometimes keeping .idx file descriptors open, it leads to
92 Since dulwich is sometimes keeping .idx file descriptors open, it leads to
93 "Too many open files" error. We need to close all opened file descriptors
93 "Too many open files" error. We need to close all opened file descriptors
94 once the repo object is destroyed.
94 once the repo object is destroyed.
95 """
95 """
96 def __del__(self):
96 def __del__(self):
97 if hasattr(self, 'object_store'):
97 if hasattr(self, 'object_store'):
98 self.close()
98 self.close()
99
99
100
100
101 class Repository(LibGit2Repo):
101 class Repository(LibGit2Repo):
102
102
103 def __enter__(self):
103 def __enter__(self):
104 return self
104 return self
105
105
106 def __exit__(self, exc_type, exc_val, exc_tb):
106 def __exit__(self, exc_type, exc_val, exc_tb):
107 self.free()
107 self.free()
108
108
109
109
110 class GitFactory(RepoFactory):
110 class GitFactory(RepoFactory):
111 repo_type = 'git'
111 repo_type = 'git'
112
112
113 def _create_repo(self, wire, create, use_libgit2=False):
113 def _create_repo(self, wire, create, use_libgit2=False):
114 if use_libgit2:
114 if use_libgit2:
115 return Repository(wire['path'])
115 return Repository(wire['path'])
116 else:
116 else:
117 repo_path = str_to_dulwich(wire['path'])
117 repo_path = str_to_dulwich(wire['path'])
118 return Repo(repo_path)
118 return Repo(repo_path)
119
119
120 def repo(self, wire, create=False, use_libgit2=False):
120 def repo(self, wire, create=False, use_libgit2=False):
121 """
121 """
122 Get a repository instance for the given path.
122 Get a repository instance for the given path.
123 """
123 """
124 return self._create_repo(wire, create, use_libgit2)
124 return self._create_repo(wire, create, use_libgit2)
125
125
126 def repo_libgit2(self, wire):
126 def repo_libgit2(self, wire):
127 return self.repo(wire, use_libgit2=True)
127 return self.repo(wire, use_libgit2=True)
128
128
129
129
130 class GitRemote(object):
130 class GitRemote(object):
131 EMPTY_COMMIT = '0' * 40
131 EMPTY_COMMIT = '0' * 40
132
132
133 def __init__(self, factory):
133 def __init__(self, factory):
134 self._factory = factory
134 self._factory = factory
135 self._bulk_methods = {
135 self._bulk_methods = {
136 "date": self.date,
136 "date": self.date,
137 "author": self.author,
137 "author": self.author,
138 "branch": self.branch,
138 "branch": self.branch,
139 "message": self.message,
139 "message": self.message,
140 "parents": self.parents,
140 "parents": self.parents,
141 "_commit": self.revision,
141 "_commit": self.revision,
142 }
142 }
143 self.region = self._factory._cache_region
143 self.region = self._factory._cache_region
144
144
145 def _wire_to_config(self, wire):
145 def _wire_to_config(self, wire):
146 if 'config' in wire:
146 if 'config' in wire:
147 return dict([(x[0] + '_' + x[1], x[2]) for x in wire['config']])
147 return dict([(x[0] + '_' + x[1], x[2]) for x in wire['config']])
148 return {}
148 return {}
149
149
150 def _remote_conf(self, config):
150 def _remote_conf(self, config):
151 params = [
151 params = [
152 '-c', 'core.askpass=""',
152 '-c', 'core.askpass=""',
153 ]
153 ]
154 ssl_cert_dir = config.get('vcs_ssl_dir')
154 ssl_cert_dir = config.get('vcs_ssl_dir')
155 if ssl_cert_dir:
155 if ssl_cert_dir:
156 params.extend(['-c', 'http.sslCAinfo={}'.format(ssl_cert_dir)])
156 params.extend(['-c', 'http.sslCAinfo={}'.format(ssl_cert_dir)])
157 return params
157 return params
158
158
159 def _cache_on(self, wire):
159 def _cache_on(self, wire):
160 context = wire.get('context', '')
160 context = wire.get('context', '')
161 context_uid = '{}'.format(context)
161 context_uid = '{}'.format(context)
162 repo_id = wire.get('repo_id', '')
162 repo_id = wire.get('repo_id', '')
163 cache = wire.get('cache', True)
163 cache = wire.get('cache', True)
164 cache_on = context and cache
164 cache_on = context and cache
165 return cache_on, context_uid, repo_id
165 return cache_on, context_uid, repo_id
166
166
167 @reraise_safe_exceptions
167 @reraise_safe_exceptions
168 def discover_git_version(self):
168 def discover_git_version(self):
169 stdout, _ = self.run_git_command(
169 stdout, _ = self.run_git_command(
170 {}, ['--version'], _bare=True, _safe=True)
170 {}, ['--version'], _bare=True, _safe=True)
171 prefix = 'git version'
171 prefix = 'git version'
172 if stdout.startswith(prefix):
172 if stdout.startswith(prefix):
173 stdout = stdout[len(prefix):]
173 stdout = stdout[len(prefix):]
174 return stdout.strip()
174 return stdout.strip()
175
175
176 @reraise_safe_exceptions
176 @reraise_safe_exceptions
177 def is_empty(self, wire):
177 def is_empty(self, wire):
178 repo_init = self._factory.repo_libgit2(wire)
178 repo_init = self._factory.repo_libgit2(wire)
179 with repo_init as repo:
179 with repo_init as repo:
180
180
181 try:
181 try:
182 has_head = repo.head.name
182 has_head = repo.head.name
183 if has_head:
183 if has_head:
184 return False
184 return False
185
185
186 # NOTE(marcink): check again using more expensive method
186 # NOTE(marcink): check again using more expensive method
187 return repo.is_empty
187 return repo.is_empty
188 except Exception:
188 except Exception:
189 pass
189 pass
190
190
191 return True
191 return True
192
192
193 @reraise_safe_exceptions
193 @reraise_safe_exceptions
194 def assert_correct_path(self, wire):
194 def assert_correct_path(self, wire):
195 cache_on, context_uid, repo_id = self._cache_on(wire)
195 cache_on, context_uid, repo_id = self._cache_on(wire)
196 @self.region.conditional_cache_on_arguments(condition=cache_on)
196 @self.region.conditional_cache_on_arguments(condition=cache_on)
197 def _assert_correct_path(_context_uid, _repo_id):
197 def _assert_correct_path(_context_uid, _repo_id):
198 try:
198 try:
199 repo_init = self._factory.repo_libgit2(wire)
199 repo_init = self._factory.repo_libgit2(wire)
200 with repo_init as repo:
200 with repo_init as repo:
201 pass
201 pass
202 except pygit2.GitError:
202 except pygit2.GitError:
203 path = wire.get('path')
203 path = wire.get('path')
204 tb = traceback.format_exc()
204 tb = traceback.format_exc()
205 log.debug("Invalid Git path `%s`, tb: %s", path, tb)
205 log.debug("Invalid Git path `%s`, tb: %s", path, tb)
206 return False
206 return False
207
207
208 return True
208 return True
209 return _assert_correct_path(context_uid, repo_id)
209 return _assert_correct_path(context_uid, repo_id)
210
210
211 @reraise_safe_exceptions
211 @reraise_safe_exceptions
212 def bare(self, wire):
212 def bare(self, wire):
213 repo_init = self._factory.repo_libgit2(wire)
213 repo_init = self._factory.repo_libgit2(wire)
214 with repo_init as repo:
214 with repo_init as repo:
215 return repo.is_bare
215 return repo.is_bare
216
216
217 @reraise_safe_exceptions
217 @reraise_safe_exceptions
218 def blob_as_pretty_string(self, wire, sha):
218 def blob_as_pretty_string(self, wire, sha):
219 repo_init = self._factory.repo_libgit2(wire)
219 repo_init = self._factory.repo_libgit2(wire)
220 with repo_init as repo:
220 with repo_init as repo:
221 blob_obj = repo[sha]
221 blob_obj = repo[sha]
222 blob = blob_obj.data
222 blob = blob_obj.data
223 return blob
223 return blob
224
224
225 @reraise_safe_exceptions
225 @reraise_safe_exceptions
226 def blob_raw_length(self, wire, sha):
226 def blob_raw_length(self, wire, sha):
227 cache_on, context_uid, repo_id = self._cache_on(wire)
227 cache_on, context_uid, repo_id = self._cache_on(wire)
228 @self.region.conditional_cache_on_arguments(condition=cache_on)
228 @self.region.conditional_cache_on_arguments(condition=cache_on)
229 def _blob_raw_length(_repo_id, _sha):
229 def _blob_raw_length(_repo_id, _sha):
230
230
231 repo_init = self._factory.repo_libgit2(wire)
231 repo_init = self._factory.repo_libgit2(wire)
232 with repo_init as repo:
232 with repo_init as repo:
233 blob = repo[sha]
233 blob = repo[sha]
234 return blob.size
234 return blob.size
235
235
236 return _blob_raw_length(repo_id, sha)
236 return _blob_raw_length(repo_id, sha)
237
237
238 def _parse_lfs_pointer(self, raw_content):
238 def _parse_lfs_pointer(self, raw_content):
239
239
240 spec_string = 'version https://git-lfs.github.com/spec'
240 spec_string = 'version https://git-lfs.github.com/spec'
241 if raw_content and raw_content.startswith(spec_string):
241 if raw_content and raw_content.startswith(spec_string):
242 pattern = re.compile(r"""
242 pattern = re.compile(r"""
243 (?:\n)?
243 (?:\n)?
244 ^version[ ]https://git-lfs\.github\.com/spec/(?P<spec_ver>v\d+)\n
244 ^version[ ]https://git-lfs\.github\.com/spec/(?P<spec_ver>v\d+)\n
245 ^oid[ ] sha256:(?P<oid_hash>[0-9a-f]{64})\n
245 ^oid[ ] sha256:(?P<oid_hash>[0-9a-f]{64})\n
246 ^size[ ](?P<oid_size>[0-9]+)\n
246 ^size[ ](?P<oid_size>[0-9]+)\n
247 (?:\n)?
247 (?:\n)?
248 """, re.VERBOSE | re.MULTILINE)
248 """, re.VERBOSE | re.MULTILINE)
249 match = pattern.match(raw_content)
249 match = pattern.match(raw_content)
250 if match:
250 if match:
251 return match.groupdict()
251 return match.groupdict()
252
252
253 return {}
253 return {}
254
254
255 @reraise_safe_exceptions
255 @reraise_safe_exceptions
256 def is_large_file(self, wire, commit_id):
256 def is_large_file(self, wire, commit_id):
257
257
258 cache_on, context_uid, repo_id = self._cache_on(wire)
258 cache_on, context_uid, repo_id = self._cache_on(wire)
259 @self.region.conditional_cache_on_arguments(condition=cache_on)
259 @self.region.conditional_cache_on_arguments(condition=cache_on)
260 def _is_large_file(_repo_id, _sha):
260 def _is_large_file(_repo_id, _sha):
261 repo_init = self._factory.repo_libgit2(wire)
261 repo_init = self._factory.repo_libgit2(wire)
262 with repo_init as repo:
262 with repo_init as repo:
263 blob = repo[commit_id]
263 blob = repo[commit_id]
264 if blob.is_binary:
264 if blob.is_binary:
265 return {}
265 return {}
266
266
267 return self._parse_lfs_pointer(blob.data)
267 return self._parse_lfs_pointer(blob.data)
268
268
269 return _is_large_file(repo_id, commit_id)
269 return _is_large_file(repo_id, commit_id)
270
270
271 @reraise_safe_exceptions
271 @reraise_safe_exceptions
272 def in_largefiles_store(self, wire, oid):
272 def in_largefiles_store(self, wire, oid):
273 conf = self._wire_to_config(wire)
273 conf = self._wire_to_config(wire)
274 repo_init = self._factory.repo_libgit2(wire)
274 repo_init = self._factory.repo_libgit2(wire)
275 with repo_init as repo:
275 with repo_init as repo:
276 repo_name = repo.path
276 repo_name = repo.path
277
277
278 store_location = conf.get('vcs_git_lfs_store_location')
278 store_location = conf.get('vcs_git_lfs_store_location')
279 if store_location:
279 if store_location:
280
280
281 store = LFSOidStore(
281 store = LFSOidStore(
282 oid=oid, repo=repo_name, store_location=store_location)
282 oid=oid, repo=repo_name, store_location=store_location)
283 return store.has_oid()
283 return store.has_oid()
284
284
285 return False
285 return False
286
286
287 @reraise_safe_exceptions
287 @reraise_safe_exceptions
288 def store_path(self, wire, oid):
288 def store_path(self, wire, oid):
289 conf = self._wire_to_config(wire)
289 conf = self._wire_to_config(wire)
290 repo_init = self._factory.repo_libgit2(wire)
290 repo_init = self._factory.repo_libgit2(wire)
291 with repo_init as repo:
291 with repo_init as repo:
292 repo_name = repo.path
292 repo_name = repo.path
293
293
294 store_location = conf.get('vcs_git_lfs_store_location')
294 store_location = conf.get('vcs_git_lfs_store_location')
295 if store_location:
295 if store_location:
296 store = LFSOidStore(
296 store = LFSOidStore(
297 oid=oid, repo=repo_name, store_location=store_location)
297 oid=oid, repo=repo_name, store_location=store_location)
298 return store.oid_path
298 return store.oid_path
299 raise ValueError('Unable to fetch oid with path {}'.format(oid))
299 raise ValueError('Unable to fetch oid with path {}'.format(oid))
300
300
301 @reraise_safe_exceptions
301 @reraise_safe_exceptions
302 def bulk_request(self, wire, rev, pre_load):
302 def bulk_request(self, wire, rev, pre_load):
303 cache_on, context_uid, repo_id = self._cache_on(wire)
303 cache_on, context_uid, repo_id = self._cache_on(wire)
304 @self.region.conditional_cache_on_arguments(condition=cache_on)
304 @self.region.conditional_cache_on_arguments(condition=cache_on)
305 def _bulk_request(_repo_id, _rev, _pre_load):
305 def _bulk_request(_repo_id, _rev, _pre_load):
306 result = {}
306 result = {}
307 for attr in pre_load:
307 for attr in pre_load:
308 try:
308 try:
309 method = self._bulk_methods[attr]
309 method = self._bulk_methods[attr]
310 args = [wire, rev]
310 args = [wire, rev]
311 result[attr] = method(*args)
311 result[attr] = method(*args)
312 except KeyError as e:
312 except KeyError as e:
313 raise exceptions.VcsException(e)(
313 raise exceptions.VcsException(e)(
314 "Unknown bulk attribute: %s" % attr)
314 "Unknown bulk attribute: %s" % attr)
315 return result
315 return result
316
316
317 return _bulk_request(repo_id, rev, sorted(pre_load))
317 return _bulk_request(repo_id, rev, sorted(pre_load))
318
318
319 def _build_opener(self, url):
319 def _build_opener(self, url):
320 handlers = []
320 handlers = []
321 url_obj = url_parser(url)
321 url_obj = url_parser(url)
322 _, authinfo = url_obj.authinfo()
322 _, authinfo = url_obj.authinfo()
323
323
324 if authinfo:
324 if authinfo:
325 # create a password manager
325 # create a password manager
326 passmgr = urllib2.HTTPPasswordMgrWithDefaultRealm()
326 passmgr = urllib2.HTTPPasswordMgrWithDefaultRealm()
327 passmgr.add_password(*authinfo)
327 passmgr.add_password(*authinfo)
328
328
329 handlers.extend((httpbasicauthhandler(passmgr),
329 handlers.extend((httpbasicauthhandler(passmgr),
330 httpdigestauthhandler(passmgr)))
330 httpdigestauthhandler(passmgr)))
331
331
332 return urllib2.build_opener(*handlers)
332 return urllib2.build_opener(*handlers)
333
333
334 def _type_id_to_name(self, type_id):
334 def _type_id_to_name(self, type_id):
335 return {
335 return {
336 1: b'commit',
336 1: b'commit',
337 2: b'tree',
337 2: b'tree',
338 3: b'blob',
338 3: b'blob',
339 4: b'tag'
339 4: b'tag'
340 }[type_id]
340 }[type_id]
341
341
342 @reraise_safe_exceptions
342 @reraise_safe_exceptions
343 def check_url(self, url, config):
343 def check_url(self, url, config):
344 url_obj = url_parser(url)
344 url_obj = url_parser(url)
345 test_uri, _ = url_obj.authinfo()
345 test_uri, _ = url_obj.authinfo()
346 url_obj.passwd = '*****' if url_obj.passwd else url_obj.passwd
346 url_obj.passwd = '*****' if url_obj.passwd else url_obj.passwd
347 url_obj.query = obfuscate_qs(url_obj.query)
347 url_obj.query = obfuscate_qs(url_obj.query)
348 cleaned_uri = str(url_obj)
348 cleaned_uri = str(url_obj)
349 log.info("Checking URL for remote cloning/import: %s", cleaned_uri)
349 log.info("Checking URL for remote cloning/import: %s", cleaned_uri)
350
350
351 if not test_uri.endswith('info/refs'):
351 if not test_uri.endswith('info/refs'):
352 test_uri = test_uri.rstrip('/') + '/info/refs'
352 test_uri = test_uri.rstrip('/') + '/info/refs'
353
353
354 o = self._build_opener(url)
354 o = self._build_opener(url)
355 o.addheaders = [('User-Agent', 'git/1.7.8.0')] # fake some git
355 o.addheaders = [('User-Agent', 'git/1.7.8.0')] # fake some git
356
356
357 q = {"service": 'git-upload-pack'}
357 q = {"service": 'git-upload-pack'}
358 qs = '?%s' % urllib.urlencode(q)
358 qs = '?%s' % urllib.urlencode(q)
359 cu = "%s%s" % (test_uri, qs)
359 cu = "%s%s" % (test_uri, qs)
360 req = urllib2.Request(cu, None, {})
360 req = urllib2.Request(cu, None, {})
361
361
362 try:
362 try:
363 log.debug("Trying to open URL %s", cleaned_uri)
363 log.debug("Trying to open URL %s", cleaned_uri)
364 resp = o.open(req)
364 resp = o.open(req)
365 if resp.code != 200:
365 if resp.code != 200:
366 raise exceptions.URLError()('Return Code is not 200')
366 raise exceptions.URLError()('Return Code is not 200')
367 except Exception as e:
367 except Exception as e:
368 log.warning("URL cannot be opened: %s", cleaned_uri, exc_info=True)
368 log.warning("URL cannot be opened: %s", cleaned_uri, exc_info=True)
369 # means it cannot be cloned
369 # means it cannot be cloned
370 raise exceptions.URLError(e)("[%s] org_exc: %s" % (cleaned_uri, e))
370 raise exceptions.URLError(e)("[%s] org_exc: %s" % (cleaned_uri, e))
371
371
372 # now detect if it's proper git repo
372 # now detect if it's proper git repo
373 gitdata = resp.read()
373 gitdata = resp.read()
374 if 'service=git-upload-pack' in gitdata:
374 if 'service=git-upload-pack' in gitdata:
375 pass
375 pass
376 elif re.findall(r'[0-9a-fA-F]{40}\s+refs', gitdata):
376 elif re.findall(r'[0-9a-fA-F]{40}\s+refs', gitdata):
377 # old style git can return some other format !
377 # old style git can return some other format !
378 pass
378 pass
379 else:
379 else:
380 raise exceptions.URLError()(
380 raise exceptions.URLError()(
381 "url [%s] does not look like an git" % (cleaned_uri,))
381 "url [%s] does not look like an git" % (cleaned_uri,))
382
382
383 return True
383 return True
384
384
385 @reraise_safe_exceptions
385 @reraise_safe_exceptions
386 def clone(self, wire, url, deferred, valid_refs, update_after_clone):
386 def clone(self, wire, url, deferred, valid_refs, update_after_clone):
387 # TODO(marcink): deprecate this method. Last i checked we don't use it anymore
387 # TODO(marcink): deprecate this method. Last i checked we don't use it anymore
388 remote_refs = self.pull(wire, url, apply_refs=False)
388 remote_refs = self.pull(wire, url, apply_refs=False)
389 repo = self._factory.repo(wire)
389 repo = self._factory.repo(wire)
390 if isinstance(valid_refs, list):
390 if isinstance(valid_refs, list):
391 valid_refs = tuple(valid_refs)
391 valid_refs = tuple(valid_refs)
392
392
393 for k in remote_refs:
393 for k in remote_refs:
394 # only parse heads/tags and skip so called deferred tags
394 # only parse heads/tags and skip so called deferred tags
395 if k.startswith(valid_refs) and not k.endswith(deferred):
395 if k.startswith(valid_refs) and not k.endswith(deferred):
396 repo[k] = remote_refs[k]
396 repo[k] = remote_refs[k]
397
397
398 if update_after_clone:
398 if update_after_clone:
399 # we want to checkout HEAD
399 # we want to checkout HEAD
400 repo["HEAD"] = remote_refs["HEAD"]
400 repo["HEAD"] = remote_refs["HEAD"]
401 index.build_index_from_tree(repo.path, repo.index_path(),
401 index.build_index_from_tree(repo.path, repo.index_path(),
402 repo.object_store, repo["HEAD"].tree)
402 repo.object_store, repo["HEAD"].tree)
403
403
404 @reraise_safe_exceptions
404 @reraise_safe_exceptions
405 def branch(self, wire, commit_id):
405 def branch(self, wire, commit_id):
406 cache_on, context_uid, repo_id = self._cache_on(wire)
406 cache_on, context_uid, repo_id = self._cache_on(wire)
407 cache_on = False
407 cache_on = False
408 @self.region.conditional_cache_on_arguments(condition=cache_on)
408 @self.region.conditional_cache_on_arguments(condition=cache_on)
409 def _branch(_context_uid, _repo_id, _commit_id):
409 def _branch(_context_uid, _repo_id, _commit_id):
410 regex = re.compile('^refs/heads')
410 regex = re.compile('^refs/heads')
411
411
412 def filter_with(ref):
412 def filter_with(ref):
413 return regex.match(ref[0]) and ref[1] == _commit_id
413 return regex.match(ref[0]) and ref[1] == _commit_id
414
414
415 branches = filter(filter_with, self.get_refs(wire).items())
415 branches = filter(filter_with, self.get_refs(wire).items())
416 return [x[0].split('refs/heads/')[-1] for x in branches]
416 return [x[0].split('refs/heads/')[-1] for x in branches]
417
417
418 return _branch(context_uid, repo_id, commit_id)
418 return _branch(context_uid, repo_id, commit_id)
419
419
420 @reraise_safe_exceptions
420 @reraise_safe_exceptions
421 def commit_branches(self, wire, commit_id):
421 def commit_branches(self, wire, commit_id):
422 cache_on, context_uid, repo_id = self._cache_on(wire)
422 cache_on, context_uid, repo_id = self._cache_on(wire)
423 @self.region.conditional_cache_on_arguments(condition=cache_on)
423 @self.region.conditional_cache_on_arguments(condition=cache_on)
424 def _commit_branches(_context_uid, _repo_id, _commit_id):
424 def _commit_branches(_context_uid, _repo_id, _commit_id):
425 repo_init = self._factory.repo_libgit2(wire)
425 repo_init = self._factory.repo_libgit2(wire)
426 with repo_init as repo:
426 with repo_init as repo:
427 branches = [x for x in repo.branches.with_commit(_commit_id)]
427 branches = [x for x in repo.branches.with_commit(_commit_id)]
428 return branches
428 return branches
429
429
430 return _commit_branches(context_uid, repo_id, commit_id)
430 return _commit_branches(context_uid, repo_id, commit_id)
431
431
432 @reraise_safe_exceptions
432 @reraise_safe_exceptions
433 def add_object(self, wire, content):
433 def add_object(self, wire, content):
434 repo_init = self._factory.repo_libgit2(wire)
434 repo_init = self._factory.repo_libgit2(wire)
435 with repo_init as repo:
435 with repo_init as repo:
436 blob = objects.Blob()
436 blob = objects.Blob()
437 blob.set_raw_string(content)
437 blob.set_raw_string(content)
438 repo.object_store.add_object(blob)
438 repo.object_store.add_object(blob)
439 return blob.id
439 return blob.id
440
440
441 # TODO: this is quite complex, check if that can be simplified
441 # TODO: this is quite complex, check if that can be simplified
442 @reraise_safe_exceptions
442 @reraise_safe_exceptions
443 def commit(self, wire, commit_data, branch, commit_tree, updated, removed):
443 def commit(self, wire, commit_data, branch, commit_tree, updated, removed):
444 repo = self._factory.repo(wire)
444 repo = self._factory.repo(wire)
445 object_store = repo.object_store
445 object_store = repo.object_store
446
446
447 # Create tree and populates it with blobs
447 # Create tree and populates it with blobs
448 commit_tree = commit_tree and repo[commit_tree] or objects.Tree()
448 commit_tree = commit_tree and repo[commit_tree] or objects.Tree()
449
449
450 for node in updated:
450 for node in updated:
451 # Compute subdirs if needed
451 # Compute subdirs if needed
452 dirpath, nodename = vcspath.split(node['path'])
452 dirpath, nodename = vcspath.split(node['path'])
453 dirnames = map(safe_str, dirpath and dirpath.split('/') or [])
453 dirnames = map(safe_str, dirpath and dirpath.split('/') or [])
454 parent = commit_tree
454 parent = commit_tree
455 ancestors = [('', parent)]
455 ancestors = [('', parent)]
456
456
457 # Tries to dig for the deepest existing tree
457 # Tries to dig for the deepest existing tree
458 while dirnames:
458 while dirnames:
459 curdir = dirnames.pop(0)
459 curdir = dirnames.pop(0)
460 try:
460 try:
461 dir_id = parent[curdir][1]
461 dir_id = parent[curdir][1]
462 except KeyError:
462 except KeyError:
463 # put curdir back into dirnames and stops
463 # put curdir back into dirnames and stops
464 dirnames.insert(0, curdir)
464 dirnames.insert(0, curdir)
465 break
465 break
466 else:
466 else:
467 # If found, updates parent
467 # If found, updates parent
468 parent = repo[dir_id]
468 parent = repo[dir_id]
469 ancestors.append((curdir, parent))
469 ancestors.append((curdir, parent))
470 # Now parent is deepest existing tree and we need to create
470 # Now parent is deepest existing tree and we need to create
471 # subtrees for dirnames (in reverse order)
471 # subtrees for dirnames (in reverse order)
472 # [this only applies for nodes from added]
472 # [this only applies for nodes from added]
473 new_trees = []
473 new_trees = []
474
474
475 blob = objects.Blob.from_string(node['content'])
475 blob = objects.Blob.from_string(node['content'])
476
476
477 if dirnames:
477 if dirnames:
478 # If there are trees which should be created we need to build
478 # If there are trees which should be created we need to build
479 # them now (in reverse order)
479 # them now (in reverse order)
480 reversed_dirnames = list(reversed(dirnames))
480 reversed_dirnames = list(reversed(dirnames))
481 curtree = objects.Tree()
481 curtree = objects.Tree()
482 curtree[node['node_path']] = node['mode'], blob.id
482 curtree[node['node_path']] = node['mode'], blob.id
483 new_trees.append(curtree)
483 new_trees.append(curtree)
484 for dirname in reversed_dirnames[:-1]:
484 for dirname in reversed_dirnames[:-1]:
485 newtree = objects.Tree()
485 newtree = objects.Tree()
486 newtree[dirname] = (DIR_STAT, curtree.id)
486 newtree[dirname] = (DIR_STAT, curtree.id)
487 new_trees.append(newtree)
487 new_trees.append(newtree)
488 curtree = newtree
488 curtree = newtree
489 parent[reversed_dirnames[-1]] = (DIR_STAT, curtree.id)
489 parent[reversed_dirnames[-1]] = (DIR_STAT, curtree.id)
490 else:
490 else:
491 parent.add(name=node['node_path'], mode=node['mode'], hexsha=blob.id)
491 parent.add(name=node['node_path'], mode=node['mode'], hexsha=blob.id)
492
492
493 new_trees.append(parent)
493 new_trees.append(parent)
494 # Update ancestors
494 # Update ancestors
495 reversed_ancestors = reversed(
495 reversed_ancestors = reversed(
496 [(a[1], b[1], b[0]) for a, b in zip(ancestors, ancestors[1:])])
496 [(a[1], b[1], b[0]) for a, b in zip(ancestors, ancestors[1:])])
497 for parent, tree, path in reversed_ancestors:
497 for parent, tree, path in reversed_ancestors:
498 parent[path] = (DIR_STAT, tree.id)
498 parent[path] = (DIR_STAT, tree.id)
499 object_store.add_object(tree)
499 object_store.add_object(tree)
500
500
501 object_store.add_object(blob)
501 object_store.add_object(blob)
502 for tree in new_trees:
502 for tree in new_trees:
503 object_store.add_object(tree)
503 object_store.add_object(tree)
504
504
505 for node_path in removed:
505 for node_path in removed:
506 paths = node_path.split('/')
506 paths = node_path.split('/')
507 tree = commit_tree
507 tree = commit_tree
508 trees = [tree]
508 trees = [tree]
509 # Traverse deep into the forest...
509 # Traverse deep into the forest...
510 for path in paths:
510 for path in paths:
511 try:
511 try:
512 obj = repo[tree[path][1]]
512 obj = repo[tree[path][1]]
513 if isinstance(obj, objects.Tree):
513 if isinstance(obj, objects.Tree):
514 trees.append(obj)
514 trees.append(obj)
515 tree = obj
515 tree = obj
516 except KeyError:
516 except KeyError:
517 break
517 break
518 # Cut down the blob and all rotten trees on the way back...
518 # Cut down the blob and all rotten trees on the way back...
519 for path, tree in reversed(zip(paths, trees)):
519 for path, tree in reversed(zip(paths, trees)):
520 del tree[path]
520 del tree[path]
521 if tree:
521 if tree:
522 # This tree still has elements - don't remove it or any
522 # This tree still has elements - don't remove it or any
523 # of it's parents
523 # of it's parents
524 break
524 break
525
525
526 object_store.add_object(commit_tree)
526 object_store.add_object(commit_tree)
527
527
528 # Create commit
528 # Create commit
529 commit = objects.Commit()
529 commit = objects.Commit()
530 commit.tree = commit_tree.id
530 commit.tree = commit_tree.id
531 for k, v in commit_data.iteritems():
531 for k, v in commit_data.iteritems():
532 setattr(commit, k, v)
532 setattr(commit, k, v)
533 object_store.add_object(commit)
533 object_store.add_object(commit)
534
534
535 self.create_branch(wire, branch, commit.id)
535 self.create_branch(wire, branch, commit.id)
536
536
537 # dulwich set-ref
537 # dulwich set-ref
538 ref = 'refs/heads/%s' % branch
538 ref = 'refs/heads/%s' % branch
539 repo.refs[ref] = commit.id
539 repo.refs[ref] = commit.id
540
540
541 return commit.id
541 return commit.id
542
542
543 @reraise_safe_exceptions
543 @reraise_safe_exceptions
544 def pull(self, wire, url, apply_refs=True, refs=None, update_after=False):
544 def pull(self, wire, url, apply_refs=True, refs=None, update_after=False):
545 if url != 'default' and '://' not in url:
545 if url != 'default' and '://' not in url:
546 client = LocalGitClient(url)
546 client = LocalGitClient(url)
547 else:
547 else:
548 url_obj = url_parser(url)
548 url_obj = url_parser(url)
549 o = self._build_opener(url)
549 o = self._build_opener(url)
550 url, _ = url_obj.authinfo()
550 url, _ = url_obj.authinfo()
551 client = HttpGitClient(base_url=url, opener=o)
551 client = HttpGitClient(base_url=url, opener=o)
552 repo = self._factory.repo(wire)
552 repo = self._factory.repo(wire)
553
553
554 determine_wants = repo.object_store.determine_wants_all
554 determine_wants = repo.object_store.determine_wants_all
555 if refs:
555 if refs:
556 def determine_wants_requested(references):
556 def determine_wants_requested(references):
557 return [references[r] for r in references if r in refs]
557 return [references[r] for r in references if r in refs]
558 determine_wants = determine_wants_requested
558 determine_wants = determine_wants_requested
559
559
560 try:
560 try:
561 remote_refs = client.fetch(
561 remote_refs = client.fetch(
562 path=url, target=repo, determine_wants=determine_wants)
562 path=url, target=repo, determine_wants=determine_wants)
563 except NotGitRepository as e:
563 except NotGitRepository as e:
564 log.warning(
564 log.warning(
565 'Trying to fetch from "%s" failed, not a Git repository.', url)
565 'Trying to fetch from "%s" failed, not a Git repository.', url)
566 # Exception can contain unicode which we convert
566 # Exception can contain unicode which we convert
567 raise exceptions.AbortException(e)(repr(e))
567 raise exceptions.AbortException(e)(repr(e))
568
568
569 # mikhail: client.fetch() returns all the remote refs, but fetches only
569 # mikhail: client.fetch() returns all the remote refs, but fetches only
570 # refs filtered by `determine_wants` function. We need to filter result
570 # refs filtered by `determine_wants` function. We need to filter result
571 # as well
571 # as well
572 if refs:
572 if refs:
573 remote_refs = {k: remote_refs[k] for k in remote_refs if k in refs}
573 remote_refs = {k: remote_refs[k] for k in remote_refs if k in refs}
574
574
575 if apply_refs:
575 if apply_refs:
576 # TODO: johbo: Needs proper test coverage with a git repository
576 # TODO: johbo: Needs proper test coverage with a git repository
577 # that contains a tag object, so that we would end up with
577 # that contains a tag object, so that we would end up with
578 # a peeled ref at this point.
578 # a peeled ref at this point.
579 for k in remote_refs:
579 for k in remote_refs:
580 if k.endswith(PEELED_REF_MARKER):
580 if k.endswith(PEELED_REF_MARKER):
581 log.debug("Skipping peeled reference %s", k)
581 log.debug("Skipping peeled reference %s", k)
582 continue
582 continue
583 repo[k] = remote_refs[k]
583 repo[k] = remote_refs[k]
584
584
585 if refs and not update_after:
585 if refs and not update_after:
586 # mikhail: explicitly set the head to the last ref.
586 # mikhail: explicitly set the head to the last ref.
587 repo['HEAD'] = remote_refs[refs[-1]]
587 repo['HEAD'] = remote_refs[refs[-1]]
588
588
589 if update_after:
589 if update_after:
590 # we want to checkout HEAD
590 # we want to checkout HEAD
591 repo["HEAD"] = remote_refs["HEAD"]
591 repo["HEAD"] = remote_refs["HEAD"]
592 index.build_index_from_tree(repo.path, repo.index_path(),
592 index.build_index_from_tree(repo.path, repo.index_path(),
593 repo.object_store, repo["HEAD"].tree)
593 repo.object_store, repo["HEAD"].tree)
594 return remote_refs
594 return remote_refs
595
595
596 @reraise_safe_exceptions
596 @reraise_safe_exceptions
597 def sync_fetch(self, wire, url, refs=None):
597 def sync_fetch(self, wire, url, refs=None):
598 repo = self._factory.repo(wire)
598 repo = self._factory.repo(wire)
599 if refs and not isinstance(refs, (list, tuple)):
599 if refs and not isinstance(refs, (list, tuple)):
600 refs = [refs]
600 refs = [refs]
601 config = self._wire_to_config(wire)
601 config = self._wire_to_config(wire)
602 # get all remote refs we'll use to fetch later
602 # get all remote refs we'll use to fetch later
603 output, __ = self.run_git_command(
603 output, __ = self.run_git_command(
604 wire, ['ls-remote', url], fail_on_stderr=False,
604 wire, ['ls-remote', url], fail_on_stderr=False,
605 _copts=self._remote_conf(config),
605 _copts=self._remote_conf(config),
606 extra_env={'GIT_TERMINAL_PROMPT': '0'})
606 extra_env={'GIT_TERMINAL_PROMPT': '0'})
607
607
608 remote_refs = collections.OrderedDict()
608 remote_refs = collections.OrderedDict()
609 fetch_refs = []
609 fetch_refs = []
610
610
611 for ref_line in output.splitlines():
611 for ref_line in output.splitlines():
612 sha, ref = ref_line.split('\t')
612 sha, ref = ref_line.split('\t')
613 sha = sha.strip()
613 sha = sha.strip()
614 if ref in remote_refs:
614 if ref in remote_refs:
615 # duplicate, skip
615 # duplicate, skip
616 continue
616 continue
617 if ref.endswith(PEELED_REF_MARKER):
617 if ref.endswith(PEELED_REF_MARKER):
618 log.debug("Skipping peeled reference %s", ref)
618 log.debug("Skipping peeled reference %s", ref)
619 continue
619 continue
620 # don't sync HEAD
620 # don't sync HEAD
621 if ref in ['HEAD']:
621 if ref in ['HEAD']:
622 continue
622 continue
623
623
624 remote_refs[ref] = sha
624 remote_refs[ref] = sha
625
625
626 if refs and sha in refs:
626 if refs and sha in refs:
627 # we filter fetch using our specified refs
627 # we filter fetch using our specified refs
628 fetch_refs.append('{}:{}'.format(ref, ref))
628 fetch_refs.append('{}:{}'.format(ref, ref))
629 elif not refs:
629 elif not refs:
630 fetch_refs.append('{}:{}'.format(ref, ref))
630 fetch_refs.append('{}:{}'.format(ref, ref))
631 log.debug('Finished obtaining fetch refs, total: %s', len(fetch_refs))
631 log.debug('Finished obtaining fetch refs, total: %s', len(fetch_refs))
632 if fetch_refs:
632 if fetch_refs:
633 for chunk in more_itertools.chunked(fetch_refs, 1024 * 4):
633 for chunk in more_itertools.chunked(fetch_refs, 1024 * 4):
634 fetch_refs_chunks = list(chunk)
634 fetch_refs_chunks = list(chunk)
635 log.debug('Fetching %s refs from import url', len(fetch_refs_chunks))
635 log.debug('Fetching %s refs from import url', len(fetch_refs_chunks))
636 _out, _err = self.run_git_command(
636 _out, _err = self.run_git_command(
637 wire, ['fetch', url, '--force', '--prune', '--'] + fetch_refs_chunks,
637 wire, ['fetch', url, '--force', '--prune', '--'] + fetch_refs_chunks,
638 fail_on_stderr=False,
638 fail_on_stderr=False,
639 _copts=self._remote_conf(config),
639 _copts=self._remote_conf(config),
640 extra_env={'GIT_TERMINAL_PROMPT': '0'})
640 extra_env={'GIT_TERMINAL_PROMPT': '0'})
641
641
642 return remote_refs
642 return remote_refs
643
643
644 @reraise_safe_exceptions
644 @reraise_safe_exceptions
645 def sync_push(self, wire, url, refs=None):
645 def sync_push(self, wire, url, refs=None):
646 if not self.check_url(url, wire):
646 if not self.check_url(url, wire):
647 return
647 return
648 config = self._wire_to_config(wire)
648 config = self._wire_to_config(wire)
649 self._factory.repo(wire)
649 self._factory.repo(wire)
650 self.run_git_command(
650 self.run_git_command(
651 wire, ['push', url, '--mirror'], fail_on_stderr=False,
651 wire, ['push', url, '--mirror'], fail_on_stderr=False,
652 _copts=self._remote_conf(config),
652 _copts=self._remote_conf(config),
653 extra_env={'GIT_TERMINAL_PROMPT': '0'})
653 extra_env={'GIT_TERMINAL_PROMPT': '0'})
654
654
655 @reraise_safe_exceptions
655 @reraise_safe_exceptions
656 def get_remote_refs(self, wire, url):
656 def get_remote_refs(self, wire, url):
657 repo = Repo(url)
657 repo = Repo(url)
658 return repo.get_refs()
658 return repo.get_refs()
659
659
660 @reraise_safe_exceptions
660 @reraise_safe_exceptions
661 def get_description(self, wire):
661 def get_description(self, wire):
662 repo = self._factory.repo(wire)
662 repo = self._factory.repo(wire)
663 return repo.get_description()
663 return repo.get_description()
664
664
665 @reraise_safe_exceptions
665 @reraise_safe_exceptions
666 def get_missing_revs(self, wire, rev1, rev2, path2):
666 def get_missing_revs(self, wire, rev1, rev2, path2):
667 repo = self._factory.repo(wire)
667 repo = self._factory.repo(wire)
668 LocalGitClient(thin_packs=False).fetch(path2, repo)
668 LocalGitClient(thin_packs=False).fetch(path2, repo)
669
669
670 wire_remote = wire.copy()
670 wire_remote = wire.copy()
671 wire_remote['path'] = path2
671 wire_remote['path'] = path2
672 repo_remote = self._factory.repo(wire_remote)
672 repo_remote = self._factory.repo(wire_remote)
673 LocalGitClient(thin_packs=False).fetch(wire["path"], repo_remote)
673 LocalGitClient(thin_packs=False).fetch(wire["path"], repo_remote)
674
674
675 revs = [
675 revs = [
676 x.commit.id
676 x.commit.id
677 for x in repo_remote.get_walker(include=[rev2], exclude=[rev1])]
677 for x in repo_remote.get_walker(include=[rev2], exclude=[rev1])]
678 return revs
678 return revs
679
679
680 @reraise_safe_exceptions
680 @reraise_safe_exceptions
681 def get_object(self, wire, sha):
681 def get_object(self, wire, sha):
682
683 cache_on, context_uid, repo_id = self._cache_on(wire)
682 cache_on, context_uid, repo_id = self._cache_on(wire)
684 @self.region.conditional_cache_on_arguments(condition=cache_on)
683 @self.region.conditional_cache_on_arguments(condition=cache_on)
685 def _get_object(_context_uid, _repo_id, _sha):
684 def _get_object(_context_uid, _repo_id, _sha):
686 repo_init = self._factory.repo_libgit2(wire)
685 repo_init = self._factory.repo_libgit2(wire)
687 with repo_init as repo:
686 with repo_init as repo:
688
687
689 missing_commit_err = 'Commit {} does not exist for `{}`'.format(sha, wire['path'])
688 missing_commit_err = 'Commit {} does not exist for `{}`'.format(sha, wire['path'])
690 try:
689 try:
691 commit = repo.revparse_single(sha)
690 commit = repo.revparse_single(sha)
692 except (KeyError, ValueError) as e:
691 except (KeyError, ValueError) as e:
693 raise exceptions.LookupException(e)(missing_commit_err)
692 raise exceptions.LookupException(e)(missing_commit_err)
694
693
694 is_tag = False
695 if isinstance(commit, pygit2.Tag):
695 if isinstance(commit, pygit2.Tag):
696 commit = repo.get(commit.target)
696 commit = repo.get(commit.target)
697 is_tag = True
697
698
698 # check for dangling commit
699 if not is_tag:
699 branches = [x for x in repo.branches.with_commit(commit.hex)]
700 # check for dangling commit
700 if not branches:
701 branches = [x for x in repo.branches.with_commit(commit.hex)]
701 raise exceptions.LookupException(None)(missing_commit_err)
702 if not branches:
703 raise exceptions.LookupException(None)(missing_commit_err)
702
704
703 commit_id = commit.hex
705 commit_id = commit.hex
704 type_id = commit.type
706 type_id = commit.type
705
707
706 return {
708 return {
707 'id': commit_id,
709 'id': commit_id,
708 'type': self._type_id_to_name(type_id),
710 'type': self._type_id_to_name(type_id),
709 'commit_id': commit_id,
711 'commit_id': commit_id,
710 'idx': 0
712 'idx': 0
711 }
713 }
712
714
713 return _get_object(context_uid, repo_id, sha)
715 return _get_object(context_uid, repo_id, sha)
714
716
715 @reraise_safe_exceptions
717 @reraise_safe_exceptions
716 def get_refs(self, wire):
718 def get_refs(self, wire):
717 cache_on, context_uid, repo_id = self._cache_on(wire)
719 cache_on, context_uid, repo_id = self._cache_on(wire)
718 @self.region.conditional_cache_on_arguments(condition=cache_on)
720 @self.region.conditional_cache_on_arguments(condition=cache_on)
719 def _get_refs(_context_uid, _repo_id):
721 def _get_refs(_context_uid, _repo_id):
720
722
721 repo_init = self._factory.repo_libgit2(wire)
723 repo_init = self._factory.repo_libgit2(wire)
722 with repo_init as repo:
724 with repo_init as repo:
723 regex = re.compile('^refs/(heads|tags)/')
725 regex = re.compile('^refs/(heads|tags)/')
724 return {x.name: x.target.hex for x in
726 return {x.name: x.target.hex for x in
725 filter(lambda ref: regex.match(ref.name) ,repo.listall_reference_objects())}
727 filter(lambda ref: regex.match(ref.name) ,repo.listall_reference_objects())}
726
728
727 return _get_refs(context_uid, repo_id)
729 return _get_refs(context_uid, repo_id)
728
730
729 @reraise_safe_exceptions
731 @reraise_safe_exceptions
730 def get_branch_pointers(self, wire):
732 def get_branch_pointers(self, wire):
731 cache_on, context_uid, repo_id = self._cache_on(wire)
733 cache_on, context_uid, repo_id = self._cache_on(wire)
732 @self.region.conditional_cache_on_arguments(condition=cache_on)
734 @self.region.conditional_cache_on_arguments(condition=cache_on)
733 def _get_branch_pointers(_context_uid, _repo_id):
735 def _get_branch_pointers(_context_uid, _repo_id):
734
736
735 repo_init = self._factory.repo_libgit2(wire)
737 repo_init = self._factory.repo_libgit2(wire)
736 regex = re.compile('^refs/heads')
738 regex = re.compile('^refs/heads')
737 with repo_init as repo:
739 with repo_init as repo:
738 branches = filter(lambda ref: regex.match(ref.name), repo.listall_reference_objects())
740 branches = filter(lambda ref: regex.match(ref.name), repo.listall_reference_objects())
739 return {x.target.hex: x.shorthand for x in branches}
741 return {x.target.hex: x.shorthand for x in branches}
740
742
741 return _get_branch_pointers(context_uid, repo_id)
743 return _get_branch_pointers(context_uid, repo_id)
742
744
743 @reraise_safe_exceptions
745 @reraise_safe_exceptions
744 def head(self, wire, show_exc=True):
746 def head(self, wire, show_exc=True):
745 cache_on, context_uid, repo_id = self._cache_on(wire)
747 cache_on, context_uid, repo_id = self._cache_on(wire)
746 @self.region.conditional_cache_on_arguments(condition=cache_on)
748 @self.region.conditional_cache_on_arguments(condition=cache_on)
747 def _head(_context_uid, _repo_id, _show_exc):
749 def _head(_context_uid, _repo_id, _show_exc):
748 repo_init = self._factory.repo_libgit2(wire)
750 repo_init = self._factory.repo_libgit2(wire)
749 with repo_init as repo:
751 with repo_init as repo:
750 try:
752 try:
751 return repo.head.peel().hex
753 return repo.head.peel().hex
752 except Exception:
754 except Exception:
753 if show_exc:
755 if show_exc:
754 raise
756 raise
755 return _head(context_uid, repo_id, show_exc)
757 return _head(context_uid, repo_id, show_exc)
756
758
757 @reraise_safe_exceptions
759 @reraise_safe_exceptions
758 def init(self, wire):
760 def init(self, wire):
759 repo_path = str_to_dulwich(wire['path'])
761 repo_path = str_to_dulwich(wire['path'])
760 self.repo = Repo.init(repo_path)
762 self.repo = Repo.init(repo_path)
761
763
762 @reraise_safe_exceptions
764 @reraise_safe_exceptions
763 def init_bare(self, wire):
765 def init_bare(self, wire):
764 repo_path = str_to_dulwich(wire['path'])
766 repo_path = str_to_dulwich(wire['path'])
765 self.repo = Repo.init_bare(repo_path)
767 self.repo = Repo.init_bare(repo_path)
766
768
767 @reraise_safe_exceptions
769 @reraise_safe_exceptions
768 def revision(self, wire, rev):
770 def revision(self, wire, rev):
769
771
770 cache_on, context_uid, repo_id = self._cache_on(wire)
772 cache_on, context_uid, repo_id = self._cache_on(wire)
771 @self.region.conditional_cache_on_arguments(condition=cache_on)
773 @self.region.conditional_cache_on_arguments(condition=cache_on)
772 def _revision(_context_uid, _repo_id, _rev):
774 def _revision(_context_uid, _repo_id, _rev):
773 repo_init = self._factory.repo_libgit2(wire)
775 repo_init = self._factory.repo_libgit2(wire)
774 with repo_init as repo:
776 with repo_init as repo:
775 commit = repo[rev]
777 commit = repo[rev]
776 obj_data = {
778 obj_data = {
777 'id': commit.id.hex,
779 'id': commit.id.hex,
778 }
780 }
779 # tree objects itself don't have tree_id attribute
781 # tree objects itself don't have tree_id attribute
780 if hasattr(commit, 'tree_id'):
782 if hasattr(commit, 'tree_id'):
781 obj_data['tree'] = commit.tree_id.hex
783 obj_data['tree'] = commit.tree_id.hex
782
784
783 return obj_data
785 return obj_data
784 return _revision(context_uid, repo_id, rev)
786 return _revision(context_uid, repo_id, rev)
785
787
786 @reraise_safe_exceptions
788 @reraise_safe_exceptions
787 def date(self, wire, commit_id):
789 def date(self, wire, commit_id):
788 cache_on, context_uid, repo_id = self._cache_on(wire)
790 cache_on, context_uid, repo_id = self._cache_on(wire)
789 @self.region.conditional_cache_on_arguments(condition=cache_on)
791 @self.region.conditional_cache_on_arguments(condition=cache_on)
790 def _date(_repo_id, _commit_id):
792 def _date(_repo_id, _commit_id):
791 repo_init = self._factory.repo_libgit2(wire)
793 repo_init = self._factory.repo_libgit2(wire)
792 with repo_init as repo:
794 with repo_init as repo:
793 commit = repo[commit_id]
795 commit = repo[commit_id]
794 # TODO(marcink): check dulwich difference of offset vs timezone
796 # TODO(marcink): check dulwich difference of offset vs timezone
795 return [commit.commit_time, commit.commit_time_offset]
797 return [commit.commit_time, commit.commit_time_offset]
796 return _date(repo_id, commit_id)
798 return _date(repo_id, commit_id)
797
799
798 @reraise_safe_exceptions
800 @reraise_safe_exceptions
799 def author(self, wire, commit_id):
801 def author(self, wire, commit_id):
800 cache_on, context_uid, repo_id = self._cache_on(wire)
802 cache_on, context_uid, repo_id = self._cache_on(wire)
801 @self.region.conditional_cache_on_arguments(condition=cache_on)
803 @self.region.conditional_cache_on_arguments(condition=cache_on)
802 def _author(_repo_id, _commit_id):
804 def _author(_repo_id, _commit_id):
803 repo_init = self._factory.repo_libgit2(wire)
805 repo_init = self._factory.repo_libgit2(wire)
804 with repo_init as repo:
806 with repo_init as repo:
805 commit = repo[commit_id]
807 commit = repo[commit_id]
806 if commit.author.email:
808 if commit.author.email:
807 return u"{} <{}>".format(commit.author.name, commit.author.email)
809 return u"{} <{}>".format(commit.author.name, commit.author.email)
808
810
809 return u"{}".format(commit.author.raw_name)
811 return u"{}".format(commit.author.raw_name)
810 return _author(repo_id, commit_id)
812 return _author(repo_id, commit_id)
811
813
812 @reraise_safe_exceptions
814 @reraise_safe_exceptions
813 def message(self, wire, commit_id):
815 def message(self, wire, commit_id):
814 cache_on, context_uid, repo_id = self._cache_on(wire)
816 cache_on, context_uid, repo_id = self._cache_on(wire)
815 @self.region.conditional_cache_on_arguments(condition=cache_on)
817 @self.region.conditional_cache_on_arguments(condition=cache_on)
816 def _message(_repo_id, _commit_id):
818 def _message(_repo_id, _commit_id):
817 repo_init = self._factory.repo_libgit2(wire)
819 repo_init = self._factory.repo_libgit2(wire)
818 with repo_init as repo:
820 with repo_init as repo:
819 commit = repo[commit_id]
821 commit = repo[commit_id]
820 return commit.message
822 return commit.message
821 return _message(repo_id, commit_id)
823 return _message(repo_id, commit_id)
822
824
823 @reraise_safe_exceptions
825 @reraise_safe_exceptions
824 def parents(self, wire, commit_id):
826 def parents(self, wire, commit_id):
825 cache_on, context_uid, repo_id = self._cache_on(wire)
827 cache_on, context_uid, repo_id = self._cache_on(wire)
826 @self.region.conditional_cache_on_arguments(condition=cache_on)
828 @self.region.conditional_cache_on_arguments(condition=cache_on)
827 def _parents(_repo_id, _commit_id):
829 def _parents(_repo_id, _commit_id):
828 repo_init = self._factory.repo_libgit2(wire)
830 repo_init = self._factory.repo_libgit2(wire)
829 with repo_init as repo:
831 with repo_init as repo:
830 commit = repo[commit_id]
832 commit = repo[commit_id]
831 return [x.hex for x in commit.parent_ids]
833 return [x.hex for x in commit.parent_ids]
832 return _parents(repo_id, commit_id)
834 return _parents(repo_id, commit_id)
833
835
834 @reraise_safe_exceptions
836 @reraise_safe_exceptions
835 def children(self, wire, commit_id):
837 def children(self, wire, commit_id):
836 cache_on, context_uid, repo_id = self._cache_on(wire)
838 cache_on, context_uid, repo_id = self._cache_on(wire)
837 @self.region.conditional_cache_on_arguments(condition=cache_on)
839 @self.region.conditional_cache_on_arguments(condition=cache_on)
838 def _children(_repo_id, _commit_id):
840 def _children(_repo_id, _commit_id):
839 output, __ = self.run_git_command(
841 output, __ = self.run_git_command(
840 wire, ['rev-list', '--all', '--children'])
842 wire, ['rev-list', '--all', '--children'])
841
843
842 child_ids = []
844 child_ids = []
843 pat = re.compile(r'^%s' % commit_id)
845 pat = re.compile(r'^%s' % commit_id)
844 for l in output.splitlines():
846 for l in output.splitlines():
845 if pat.match(l):
847 if pat.match(l):
846 found_ids = l.split(' ')[1:]
848 found_ids = l.split(' ')[1:]
847 child_ids.extend(found_ids)
849 child_ids.extend(found_ids)
848
850
849 return child_ids
851 return child_ids
850 return _children(repo_id, commit_id)
852 return _children(repo_id, commit_id)
851
853
852 @reraise_safe_exceptions
854 @reraise_safe_exceptions
853 def set_refs(self, wire, key, value):
855 def set_refs(self, wire, key, value):
854 repo_init = self._factory.repo_libgit2(wire)
856 repo_init = self._factory.repo_libgit2(wire)
855 with repo_init as repo:
857 with repo_init as repo:
856 repo.references.create(key, value, force=True)
858 repo.references.create(key, value, force=True)
857
859
858 @reraise_safe_exceptions
860 @reraise_safe_exceptions
859 def create_branch(self, wire, branch_name, commit_id, force=False):
861 def create_branch(self, wire, branch_name, commit_id, force=False):
860 repo_init = self._factory.repo_libgit2(wire)
862 repo_init = self._factory.repo_libgit2(wire)
861 with repo_init as repo:
863 with repo_init as repo:
862 commit = repo[commit_id]
864 commit = repo[commit_id]
863
865
864 if force:
866 if force:
865 repo.branches.local.create(branch_name, commit, force=force)
867 repo.branches.local.create(branch_name, commit, force=force)
866 elif not repo.branches.get(branch_name):
868 elif not repo.branches.get(branch_name):
867 # create only if that branch isn't existing
869 # create only if that branch isn't existing
868 repo.branches.local.create(branch_name, commit, force=force)
870 repo.branches.local.create(branch_name, commit, force=force)
869
871
870 @reraise_safe_exceptions
872 @reraise_safe_exceptions
871 def remove_ref(self, wire, key):
873 def remove_ref(self, wire, key):
872 repo_init = self._factory.repo_libgit2(wire)
874 repo_init = self._factory.repo_libgit2(wire)
873 with repo_init as repo:
875 with repo_init as repo:
874 repo.references.delete(key)
876 repo.references.delete(key)
875
877
876 @reraise_safe_exceptions
878 @reraise_safe_exceptions
877 def tag_remove(self, wire, tag_name):
879 def tag_remove(self, wire, tag_name):
878 repo_init = self._factory.repo_libgit2(wire)
880 repo_init = self._factory.repo_libgit2(wire)
879 with repo_init as repo:
881 with repo_init as repo:
880 key = 'refs/tags/{}'.format(tag_name)
882 key = 'refs/tags/{}'.format(tag_name)
881 repo.references.delete(key)
883 repo.references.delete(key)
882
884
883 @reraise_safe_exceptions
885 @reraise_safe_exceptions
884 def tree_changes(self, wire, source_id, target_id):
886 def tree_changes(self, wire, source_id, target_id):
885 # TODO(marcink): remove this seems it's only used by tests
887 # TODO(marcink): remove this seems it's only used by tests
886 repo = self._factory.repo(wire)
888 repo = self._factory.repo(wire)
887 source = repo[source_id].tree if source_id else None
889 source = repo[source_id].tree if source_id else None
888 target = repo[target_id].tree
890 target = repo[target_id].tree
889 result = repo.object_store.tree_changes(source, target)
891 result = repo.object_store.tree_changes(source, target)
890 return list(result)
892 return list(result)
891
893
892 @reraise_safe_exceptions
894 @reraise_safe_exceptions
893 def tree_and_type_for_path(self, wire, commit_id, path):
895 def tree_and_type_for_path(self, wire, commit_id, path):
894
896
895 cache_on, context_uid, repo_id = self._cache_on(wire)
897 cache_on, context_uid, repo_id = self._cache_on(wire)
896 @self.region.conditional_cache_on_arguments(condition=cache_on)
898 @self.region.conditional_cache_on_arguments(condition=cache_on)
897 def _tree_and_type_for_path(_context_uid, _repo_id, _commit_id, _path):
899 def _tree_and_type_for_path(_context_uid, _repo_id, _commit_id, _path):
898 repo_init = self._factory.repo_libgit2(wire)
900 repo_init = self._factory.repo_libgit2(wire)
899
901
900 with repo_init as repo:
902 with repo_init as repo:
901 commit = repo[commit_id]
903 commit = repo[commit_id]
902 try:
904 try:
903 tree = commit.tree[path]
905 tree = commit.tree[path]
904 except KeyError:
906 except KeyError:
905 return None, None, None
907 return None, None, None
906
908
907 return tree.id.hex, tree.type, tree.filemode
909 return tree.id.hex, tree.type, tree.filemode
908 return _tree_and_type_for_path(context_uid, repo_id, commit_id, path)
910 return _tree_and_type_for_path(context_uid, repo_id, commit_id, path)
909
911
910 @reraise_safe_exceptions
912 @reraise_safe_exceptions
911 def tree_items(self, wire, tree_id):
913 def tree_items(self, wire, tree_id):
912 cache_on, context_uid, repo_id = self._cache_on(wire)
914 cache_on, context_uid, repo_id = self._cache_on(wire)
913 @self.region.conditional_cache_on_arguments(condition=cache_on)
915 @self.region.conditional_cache_on_arguments(condition=cache_on)
914 def _tree_items(_repo_id, _tree_id):
916 def _tree_items(_repo_id, _tree_id):
915
917
916 repo_init = self._factory.repo_libgit2(wire)
918 repo_init = self._factory.repo_libgit2(wire)
917 with repo_init as repo:
919 with repo_init as repo:
918 try:
920 try:
919 tree = repo[tree_id]
921 tree = repo[tree_id]
920 except KeyError:
922 except KeyError:
921 raise ObjectMissing('No tree with id: {}'.format(tree_id))
923 raise ObjectMissing('No tree with id: {}'.format(tree_id))
922
924
923 result = []
925 result = []
924 for item in tree:
926 for item in tree:
925 item_sha = item.hex
927 item_sha = item.hex
926 item_mode = item.filemode
928 item_mode = item.filemode
927 item_type = item.type
929 item_type = item.type
928
930
929 if item_type == 'commit':
931 if item_type == 'commit':
930 # NOTE(marcink): submodules we translate to 'link' for backward compat
932 # NOTE(marcink): submodules we translate to 'link' for backward compat
931 item_type = 'link'
933 item_type = 'link'
932
934
933 result.append((item.name, item_mode, item_sha, item_type))
935 result.append((item.name, item_mode, item_sha, item_type))
934 return result
936 return result
935 return _tree_items(repo_id, tree_id)
937 return _tree_items(repo_id, tree_id)
936
938
937 @reraise_safe_exceptions
939 @reraise_safe_exceptions
938 def diff(self, wire, commit_id_1, commit_id_2, file_filter, opt_ignorews, context):
940 def diff(self, wire, commit_id_1, commit_id_2, file_filter, opt_ignorews, context):
939
941
940 flags = [
942 flags = [
941 '-U%s' % context, '--full-index', '--binary', '-p',
943 '-U%s' % context, '--full-index', '--binary', '-p',
942 '-M', '--abbrev=40']
944 '-M', '--abbrev=40']
943
945
944 if opt_ignorews:
946 if opt_ignorews:
945 flags.append('-w')
947 flags.append('-w')
946
948
947 if commit_id_1 == self.EMPTY_COMMIT:
949 if commit_id_1 == self.EMPTY_COMMIT:
948 cmd = ['show'] + flags + [commit_id_2]
950 cmd = ['show'] + flags + [commit_id_2]
949 else:
951 else:
950 cmd = ['diff'] + flags + [commit_id_1, commit_id_2]
952 cmd = ['diff'] + flags + [commit_id_1, commit_id_2]
951
953
952 if file_filter:
954 if file_filter:
953 cmd.extend(['--', file_filter])
955 cmd.extend(['--', file_filter])
954
956
955 diff, __ = self.run_git_command(wire, cmd)
957 diff, __ = self.run_git_command(wire, cmd)
956 # If we used 'show' command, strip first few lines (until actual diff
958 # If we used 'show' command, strip first few lines (until actual diff
957 # starts)
959 # starts)
958 if commit_id_1 == self.EMPTY_COMMIT:
960 if commit_id_1 == self.EMPTY_COMMIT:
959 lines = diff.splitlines()
961 lines = diff.splitlines()
960 x = 0
962 x = 0
961 for line in lines:
963 for line in lines:
962 if line.startswith('diff'):
964 if line.startswith('diff'):
963 break
965 break
964 x += 1
966 x += 1
965 # Append new line just like 'diff' command do
967 # Append new line just like 'diff' command do
966 diff = '\n'.join(lines[x:]) + '\n'
968 diff = '\n'.join(lines[x:]) + '\n'
967 return diff
969 return diff
968
970
969 @reraise_safe_exceptions
971 @reraise_safe_exceptions
970 def node_history(self, wire, commit_id, path, limit):
972 def node_history(self, wire, commit_id, path, limit):
971 cache_on, context_uid, repo_id = self._cache_on(wire)
973 cache_on, context_uid, repo_id = self._cache_on(wire)
972 @self.region.conditional_cache_on_arguments(condition=cache_on)
974 @self.region.conditional_cache_on_arguments(condition=cache_on)
973 def _node_history(_context_uid, _repo_id, _commit_id, _path, _limit):
975 def _node_history(_context_uid, _repo_id, _commit_id, _path, _limit):
974 # optimize for n==1, rev-list is much faster for that use-case
976 # optimize for n==1, rev-list is much faster for that use-case
975 if limit == 1:
977 if limit == 1:
976 cmd = ['rev-list', '-1', commit_id, '--', path]
978 cmd = ['rev-list', '-1', commit_id, '--', path]
977 else:
979 else:
978 cmd = ['log']
980 cmd = ['log']
979 if limit:
981 if limit:
980 cmd.extend(['-n', str(safe_int(limit, 0))])
982 cmd.extend(['-n', str(safe_int(limit, 0))])
981 cmd.extend(['--pretty=format: %H', '-s', commit_id, '--', path])
983 cmd.extend(['--pretty=format: %H', '-s', commit_id, '--', path])
982
984
983 output, __ = self.run_git_command(wire, cmd)
985 output, __ = self.run_git_command(wire, cmd)
984 commit_ids = re.findall(r'[0-9a-fA-F]{40}', output)
986 commit_ids = re.findall(r'[0-9a-fA-F]{40}', output)
985
987
986 return [x for x in commit_ids]
988 return [x for x in commit_ids]
987 return _node_history(context_uid, repo_id, commit_id, path, limit)
989 return _node_history(context_uid, repo_id, commit_id, path, limit)
988
990
989 @reraise_safe_exceptions
991 @reraise_safe_exceptions
990 def node_annotate(self, wire, commit_id, path):
992 def node_annotate(self, wire, commit_id, path):
991
993
992 cmd = ['blame', '-l', '--root', '-r', commit_id, '--', path]
994 cmd = ['blame', '-l', '--root', '-r', commit_id, '--', path]
993 # -l ==> outputs long shas (and we need all 40 characters)
995 # -l ==> outputs long shas (and we need all 40 characters)
994 # --root ==> doesn't put '^' character for boundaries
996 # --root ==> doesn't put '^' character for boundaries
995 # -r commit_id ==> blames for the given commit
997 # -r commit_id ==> blames for the given commit
996 output, __ = self.run_git_command(wire, cmd)
998 output, __ = self.run_git_command(wire, cmd)
997
999
998 result = []
1000 result = []
999 for i, blame_line in enumerate(output.split('\n')[:-1]):
1001 for i, blame_line in enumerate(output.split('\n')[:-1]):
1000 line_no = i + 1
1002 line_no = i + 1
1001 commit_id, line = re.split(r' ', blame_line, 1)
1003 commit_id, line = re.split(r' ', blame_line, 1)
1002 result.append((line_no, commit_id, line))
1004 result.append((line_no, commit_id, line))
1003 return result
1005 return result
1004
1006
1005 @reraise_safe_exceptions
1007 @reraise_safe_exceptions
1006 def update_server_info(self, wire):
1008 def update_server_info(self, wire):
1007 repo = self._factory.repo(wire)
1009 repo = self._factory.repo(wire)
1008 update_server_info(repo)
1010 update_server_info(repo)
1009
1011
1010 @reraise_safe_exceptions
1012 @reraise_safe_exceptions
1011 def get_all_commit_ids(self, wire):
1013 def get_all_commit_ids(self, wire):
1012
1014
1013 cache_on, context_uid, repo_id = self._cache_on(wire)
1015 cache_on, context_uid, repo_id = self._cache_on(wire)
1014 @self.region.conditional_cache_on_arguments(condition=cache_on)
1016 @self.region.conditional_cache_on_arguments(condition=cache_on)
1015 def _get_all_commit_ids(_context_uid, _repo_id):
1017 def _get_all_commit_ids(_context_uid, _repo_id):
1016
1018
1017 cmd = ['rev-list', '--reverse', '--date-order', '--branches', '--tags']
1019 cmd = ['rev-list', '--reverse', '--date-order', '--branches', '--tags']
1018 try:
1020 try:
1019 output, __ = self.run_git_command(wire, cmd)
1021 output, __ = self.run_git_command(wire, cmd)
1020 return output.splitlines()
1022 return output.splitlines()
1021 except Exception:
1023 except Exception:
1022 # Can be raised for empty repositories
1024 # Can be raised for empty repositories
1023 return []
1025 return []
1024 return _get_all_commit_ids(context_uid, repo_id)
1026 return _get_all_commit_ids(context_uid, repo_id)
1025
1027
1026 @reraise_safe_exceptions
1028 @reraise_safe_exceptions
1027 def run_git_command(self, wire, cmd, **opts):
1029 def run_git_command(self, wire, cmd, **opts):
1028 path = wire.get('path', None)
1030 path = wire.get('path', None)
1029
1031
1030 if path and os.path.isdir(path):
1032 if path and os.path.isdir(path):
1031 opts['cwd'] = path
1033 opts['cwd'] = path
1032
1034
1033 if '_bare' in opts:
1035 if '_bare' in opts:
1034 _copts = []
1036 _copts = []
1035 del opts['_bare']
1037 del opts['_bare']
1036 else:
1038 else:
1037 _copts = ['-c', 'core.quotepath=false', ]
1039 _copts = ['-c', 'core.quotepath=false', ]
1038 safe_call = False
1040 safe_call = False
1039 if '_safe' in opts:
1041 if '_safe' in opts:
1040 # no exc on failure
1042 # no exc on failure
1041 del opts['_safe']
1043 del opts['_safe']
1042 safe_call = True
1044 safe_call = True
1043
1045
1044 if '_copts' in opts:
1046 if '_copts' in opts:
1045 _copts.extend(opts['_copts'] or [])
1047 _copts.extend(opts['_copts'] or [])
1046 del opts['_copts']
1048 del opts['_copts']
1047
1049
1048 gitenv = os.environ.copy()
1050 gitenv = os.environ.copy()
1049 gitenv.update(opts.pop('extra_env', {}))
1051 gitenv.update(opts.pop('extra_env', {}))
1050 # need to clean fix GIT_DIR !
1052 # need to clean fix GIT_DIR !
1051 if 'GIT_DIR' in gitenv:
1053 if 'GIT_DIR' in gitenv:
1052 del gitenv['GIT_DIR']
1054 del gitenv['GIT_DIR']
1053 gitenv['GIT_CONFIG_NOGLOBAL'] = '1'
1055 gitenv['GIT_CONFIG_NOGLOBAL'] = '1'
1054 gitenv['GIT_DISCOVERY_ACROSS_FILESYSTEM'] = '1'
1056 gitenv['GIT_DISCOVERY_ACROSS_FILESYSTEM'] = '1'
1055
1057
1056 cmd = [settings.GIT_EXECUTABLE] + _copts + cmd
1058 cmd = [settings.GIT_EXECUTABLE] + _copts + cmd
1057 _opts = {'env': gitenv, 'shell': False}
1059 _opts = {'env': gitenv, 'shell': False}
1058
1060
1059 try:
1061 try:
1060 _opts.update(opts)
1062 _opts.update(opts)
1061 p = subprocessio.SubprocessIOChunker(cmd, **_opts)
1063 p = subprocessio.SubprocessIOChunker(cmd, **_opts)
1062
1064
1063 return ''.join(p), ''.join(p.error)
1065 return ''.join(p), ''.join(p.error)
1064 except (EnvironmentError, OSError) as err:
1066 except (EnvironmentError, OSError) as err:
1065 cmd = ' '.join(cmd) # human friendly CMD
1067 cmd = ' '.join(cmd) # human friendly CMD
1066 tb_err = ("Couldn't run git command (%s).\n"
1068 tb_err = ("Couldn't run git command (%s).\n"
1067 "Original error was:%s\n"
1069 "Original error was:%s\n"
1068 "Call options:%s\n"
1070 "Call options:%s\n"
1069 % (cmd, err, _opts))
1071 % (cmd, err, _opts))
1070 log.exception(tb_err)
1072 log.exception(tb_err)
1071 if safe_call:
1073 if safe_call:
1072 return '', err
1074 return '', err
1073 else:
1075 else:
1074 raise exceptions.VcsException()(tb_err)
1076 raise exceptions.VcsException()(tb_err)
1075
1077
1076 @reraise_safe_exceptions
1078 @reraise_safe_exceptions
1077 def install_hooks(self, wire, force=False):
1079 def install_hooks(self, wire, force=False):
1078 from vcsserver.hook_utils import install_git_hooks
1080 from vcsserver.hook_utils import install_git_hooks
1079 bare = self.bare(wire)
1081 bare = self.bare(wire)
1080 path = wire['path']
1082 path = wire['path']
1081 return install_git_hooks(path, bare, force_create=force)
1083 return install_git_hooks(path, bare, force_create=force)
1082
1084
1083 @reraise_safe_exceptions
1085 @reraise_safe_exceptions
1084 def get_hooks_info(self, wire):
1086 def get_hooks_info(self, wire):
1085 from vcsserver.hook_utils import (
1087 from vcsserver.hook_utils import (
1086 get_git_pre_hook_version, get_git_post_hook_version)
1088 get_git_pre_hook_version, get_git_post_hook_version)
1087 bare = self.bare(wire)
1089 bare = self.bare(wire)
1088 path = wire['path']
1090 path = wire['path']
1089 return {
1091 return {
1090 'pre_version': get_git_pre_hook_version(path, bare),
1092 'pre_version': get_git_pre_hook_version(path, bare),
1091 'post_version': get_git_post_hook_version(path, bare),
1093 'post_version': get_git_post_hook_version(path, bare),
1092 }
1094 }
General Comments 0
You need to be logged in to leave comments. Login now