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