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