##// END OF EJS Templates
obfuscation: don't always set passwd to obfuscated text. In case there's...
marcink -
r114:46f02be5 default
parent child Browse files
Show More
@@ -1,580 +1,580 b''
1 1 # RhodeCode VCSServer provides access to different vcs backends via network.
2 2 # Copyright (C) 2014-2016 RodeCode 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 urllib
24 24 import urllib2
25 25 from functools import wraps
26 26
27 27 from dulwich import index, objects
28 28 from dulwich.client import HttpGitClient, LocalGitClient
29 29 from dulwich.errors import (
30 30 NotGitRepository, ChecksumMismatch, WrongObjectException,
31 31 MissingCommitError, ObjectMissing, HangupException,
32 32 UnexpectedCommandError)
33 33 from dulwich.repo import Repo as DulwichRepo, Tag
34 34 from dulwich.server import update_server_info
35 35
36 36 from vcsserver import exceptions, settings, subprocessio
37 37 from vcsserver.utils import safe_str
38 38 from vcsserver.base import RepoFactory, obfuscate_qs
39 39 from vcsserver.hgcompat import (
40 40 hg_url as url_parser, httpbasicauthhandler, httpdigestauthhandler)
41 41
42 42
43 43 DIR_STAT = stat.S_IFDIR
44 44 FILE_MODE = stat.S_IFMT
45 45 GIT_LINK = objects.S_IFGITLINK
46 46
47 47 log = logging.getLogger(__name__)
48 48
49 49
50 50 def reraise_safe_exceptions(func):
51 51 """Converts Dulwich exceptions to something neutral."""
52 52 @wraps(func)
53 53 def wrapper(*args, **kwargs):
54 54 try:
55 55 return func(*args, **kwargs)
56 56 except (ChecksumMismatch, WrongObjectException, MissingCommitError,
57 57 ObjectMissing) as e:
58 58 raise exceptions.LookupException(e.message)
59 59 except (HangupException, UnexpectedCommandError) as e:
60 60 raise exceptions.VcsException(e.message)
61 61 return wrapper
62 62
63 63
64 64 class Repo(DulwichRepo):
65 65 """
66 66 A wrapper for dulwich Repo class.
67 67
68 68 Since dulwich is sometimes keeping .idx file descriptors open, it leads to
69 69 "Too many open files" error. We need to close all opened file descriptors
70 70 once the repo object is destroyed.
71 71
72 72 TODO: mikhail: please check if we need this wrapper after updating dulwich
73 73 to 0.12.0 +
74 74 """
75 75 def __del__(self):
76 76 if hasattr(self, 'object_store'):
77 77 self.close()
78 78
79 79
80 80 class GitFactory(RepoFactory):
81 81
82 82 def _create_repo(self, wire, create):
83 83 repo_path = str_to_dulwich(wire['path'])
84 84 return Repo(repo_path)
85 85
86 86
87 87 class GitRemote(object):
88 88
89 89 def __init__(self, factory):
90 90 self._factory = factory
91 91
92 92 self._bulk_methods = {
93 93 "author": self.commit_attribute,
94 94 "date": self.get_object_attrs,
95 95 "message": self.commit_attribute,
96 96 "parents": self.commit_attribute,
97 97 "_commit": self.revision,
98 98 }
99 99
100 100 def _assign_ref(self, wire, ref, commit_id):
101 101 repo = self._factory.repo(wire)
102 102 repo[ref] = commit_id
103 103
104 104 @reraise_safe_exceptions
105 105 def add_object(self, wire, content):
106 106 repo = self._factory.repo(wire)
107 107 blob = objects.Blob()
108 108 blob.set_raw_string(content)
109 109 repo.object_store.add_object(blob)
110 110 return blob.id
111 111
112 112 @reraise_safe_exceptions
113 113 def assert_correct_path(self, wire):
114 114 try:
115 115 self._factory.repo(wire)
116 116 except NotGitRepository as e:
117 117 # Exception can contain unicode which we convert
118 118 raise exceptions.AbortException(repr(e))
119 119
120 120 @reraise_safe_exceptions
121 121 def bare(self, wire):
122 122 repo = self._factory.repo(wire)
123 123 return repo.bare
124 124
125 125 @reraise_safe_exceptions
126 126 def blob_as_pretty_string(self, wire, sha):
127 127 repo = self._factory.repo(wire)
128 128 return repo[sha].as_pretty_string()
129 129
130 130 @reraise_safe_exceptions
131 131 def blob_raw_length(self, wire, sha):
132 132 repo = self._factory.repo(wire)
133 133 blob = repo[sha]
134 134 return blob.raw_length()
135 135
136 136 @reraise_safe_exceptions
137 137 def bulk_request(self, wire, rev, pre_load):
138 138 result = {}
139 139 for attr in pre_load:
140 140 try:
141 141 method = self._bulk_methods[attr]
142 142 args = [wire, rev]
143 143 if attr == "date":
144 144 args.extend(["commit_time", "commit_timezone"])
145 145 elif attr in ["author", "message", "parents"]:
146 146 args.append(attr)
147 147 result[attr] = method(*args)
148 148 except KeyError:
149 149 raise exceptions.VcsException(
150 150 "Unknown bulk attribute: %s" % attr)
151 151 return result
152 152
153 153 def _build_opener(self, url):
154 154 handlers = []
155 155 url_obj = url_parser(url)
156 156 _, authinfo = url_obj.authinfo()
157 157
158 158 if authinfo:
159 159 # create a password manager
160 160 passmgr = urllib2.HTTPPasswordMgrWithDefaultRealm()
161 161 passmgr.add_password(*authinfo)
162 162
163 163 handlers.extend((httpbasicauthhandler(passmgr),
164 164 httpdigestauthhandler(passmgr)))
165 165
166 166 return urllib2.build_opener(*handlers)
167 167
168 168 @reraise_safe_exceptions
169 169 def check_url(self, url, config):
170 170 url_obj = url_parser(url)
171 171 test_uri, _ = url_obj.authinfo()
172 url_obj.passwd = '*****'
172 url_obj.passwd = '*****' if url_obj.passwd else url_obj.passwd
173 173 url_obj.query = obfuscate_qs(url_obj.query)
174 174 cleaned_uri = str(url_obj)
175 175 log.info("Checking URL for remote cloning/import: %s", cleaned_uri)
176 176
177 177 if not test_uri.endswith('info/refs'):
178 178 test_uri = test_uri.rstrip('/') + '/info/refs'
179 179
180 180 o = self._build_opener(url)
181 181 o.addheaders = [('User-Agent', 'git/1.7.8.0')] # fake some git
182 182
183 183 q = {"service": 'git-upload-pack'}
184 184 qs = '?%s' % urllib.urlencode(q)
185 185 cu = "%s%s" % (test_uri, qs)
186 186 req = urllib2.Request(cu, None, {})
187 187
188 188 try:
189 189 log.debug("Trying to open URL %s", cleaned_uri)
190 190 resp = o.open(req)
191 191 if resp.code != 200:
192 192 raise exceptions.URLError('Return Code is not 200')
193 193 except Exception as e:
194 194 log.warning("URL cannot be opened: %s", cleaned_uri, exc_info=True)
195 195 # means it cannot be cloned
196 196 raise exceptions.URLError("[%s] org_exc: %s" % (cleaned_uri, e))
197 197
198 198 # now detect if it's proper git repo
199 199 gitdata = resp.read()
200 200 if 'service=git-upload-pack' in gitdata:
201 201 pass
202 202 elif re.findall(r'[0-9a-fA-F]{40}\s+refs', gitdata):
203 203 # old style git can return some other format !
204 204 pass
205 205 else:
206 206 raise exceptions.URLError(
207 207 "url [%s] does not look like an git" % (cleaned_uri,))
208 208
209 209 return True
210 210
211 211 @reraise_safe_exceptions
212 212 def clone(self, wire, url, deferred, valid_refs, update_after_clone):
213 213 remote_refs = self.fetch(wire, url, apply_refs=False)
214 214 repo = self._factory.repo(wire)
215 215 if isinstance(valid_refs, list):
216 216 valid_refs = tuple(valid_refs)
217 217
218 218 for k in remote_refs:
219 219 # only parse heads/tags and skip so called deferred tags
220 220 if k.startswith(valid_refs) and not k.endswith(deferred):
221 221 repo[k] = remote_refs[k]
222 222
223 223 if update_after_clone:
224 224 # we want to checkout HEAD
225 225 repo["HEAD"] = remote_refs["HEAD"]
226 226 index.build_index_from_tree(repo.path, repo.index_path(),
227 227 repo.object_store, repo["HEAD"].tree)
228 228
229 229 # TODO: this is quite complex, check if that can be simplified
230 230 @reraise_safe_exceptions
231 231 def commit(self, wire, commit_data, branch, commit_tree, updated, removed):
232 232 repo = self._factory.repo(wire)
233 233 object_store = repo.object_store
234 234
235 235 # Create tree and populates it with blobs
236 236 commit_tree = commit_tree and repo[commit_tree] or objects.Tree()
237 237
238 238 for node in updated:
239 239 # Compute subdirs if needed
240 240 dirpath, nodename = vcspath.split(node['path'])
241 241 dirnames = map(safe_str, dirpath and dirpath.split('/') or [])
242 242 parent = commit_tree
243 243 ancestors = [('', parent)]
244 244
245 245 # Tries to dig for the deepest existing tree
246 246 while dirnames:
247 247 curdir = dirnames.pop(0)
248 248 try:
249 249 dir_id = parent[curdir][1]
250 250 except KeyError:
251 251 # put curdir back into dirnames and stops
252 252 dirnames.insert(0, curdir)
253 253 break
254 254 else:
255 255 # If found, updates parent
256 256 parent = repo[dir_id]
257 257 ancestors.append((curdir, parent))
258 258 # Now parent is deepest existing tree and we need to create
259 259 # subtrees for dirnames (in reverse order)
260 260 # [this only applies for nodes from added]
261 261 new_trees = []
262 262
263 263 blob = objects.Blob.from_string(node['content'])
264 264
265 265 if dirnames:
266 266 # If there are trees which should be created we need to build
267 267 # them now (in reverse order)
268 268 reversed_dirnames = list(reversed(dirnames))
269 269 curtree = objects.Tree()
270 270 curtree[node['node_path']] = node['mode'], blob.id
271 271 new_trees.append(curtree)
272 272 for dirname in reversed_dirnames[:-1]:
273 273 newtree = objects.Tree()
274 274 newtree[dirname] = (DIR_STAT, curtree.id)
275 275 new_trees.append(newtree)
276 276 curtree = newtree
277 277 parent[reversed_dirnames[-1]] = (DIR_STAT, curtree.id)
278 278 else:
279 279 parent.add(
280 280 name=node['node_path'], mode=node['mode'], hexsha=blob.id)
281 281
282 282 new_trees.append(parent)
283 283 # Update ancestors
284 284 reversed_ancestors = reversed(
285 285 [(a[1], b[1], b[0]) for a, b in zip(ancestors, ancestors[1:])])
286 286 for parent, tree, path in reversed_ancestors:
287 287 parent[path] = (DIR_STAT, tree.id)
288 288 object_store.add_object(tree)
289 289
290 290 object_store.add_object(blob)
291 291 for tree in new_trees:
292 292 object_store.add_object(tree)
293 293
294 294 for node_path in removed:
295 295 paths = node_path.split('/')
296 296 tree = commit_tree
297 297 trees = [tree]
298 298 # Traverse deep into the forest...
299 299 for path in paths:
300 300 try:
301 301 obj = repo[tree[path][1]]
302 302 if isinstance(obj, objects.Tree):
303 303 trees.append(obj)
304 304 tree = obj
305 305 except KeyError:
306 306 break
307 307 # Cut down the blob and all rotten trees on the way back...
308 308 for path, tree in reversed(zip(paths, trees)):
309 309 del tree[path]
310 310 if tree:
311 311 # This tree still has elements - don't remove it or any
312 312 # of it's parents
313 313 break
314 314
315 315 object_store.add_object(commit_tree)
316 316
317 317 # Create commit
318 318 commit = objects.Commit()
319 319 commit.tree = commit_tree.id
320 320 for k, v in commit_data.iteritems():
321 321 setattr(commit, k, v)
322 322 object_store.add_object(commit)
323 323
324 324 ref = 'refs/heads/%s' % branch
325 325 repo.refs[ref] = commit.id
326 326
327 327 return commit.id
328 328
329 329 @reraise_safe_exceptions
330 330 def fetch(self, wire, url, apply_refs=True, refs=None):
331 331 if url != 'default' and '://' not in url:
332 332 client = LocalGitClient(url)
333 333 else:
334 334 url_obj = url_parser(url)
335 335 o = self._build_opener(url)
336 336 url, _ = url_obj.authinfo()
337 337 client = HttpGitClient(base_url=url, opener=o)
338 338 repo = self._factory.repo(wire)
339 339
340 340 determine_wants = repo.object_store.determine_wants_all
341 341 if refs:
342 342 def determine_wants_requested(references):
343 343 return [references[r] for r in references if r in refs]
344 344 determine_wants = determine_wants_requested
345 345
346 346 try:
347 347 remote_refs = client.fetch(
348 348 path=url, target=repo, determine_wants=determine_wants)
349 349 except NotGitRepository:
350 350 log.warning(
351 351 'Trying to fetch from "%s" failed, not a Git repository.', url)
352 352 raise exceptions.AbortException()
353 353
354 354 # mikhail: client.fetch() returns all the remote refs, but fetches only
355 355 # refs filtered by `determine_wants` function. We need to filter result
356 356 # as well
357 357 if refs:
358 358 remote_refs = {k: remote_refs[k] for k in remote_refs if k in refs}
359 359
360 360 if apply_refs:
361 361 # TODO: johbo: Needs proper test coverage with a git repository
362 362 # that contains a tag object, so that we would end up with
363 363 # a peeled ref at this point.
364 364 PEELED_REF_MARKER = '^{}'
365 365 for k in remote_refs:
366 366 if k.endswith(PEELED_REF_MARKER):
367 367 log.info("Skipping peeled reference %s", k)
368 368 continue
369 369 repo[k] = remote_refs[k]
370 370
371 371 if refs:
372 372 # mikhail: explicitly set the head to the last ref.
373 373 repo['HEAD'] = remote_refs[refs[-1]]
374 374
375 375 # TODO: mikhail: should we return remote_refs here to be
376 376 # consistent?
377 377 else:
378 378 return remote_refs
379 379
380 380 @reraise_safe_exceptions
381 381 def get_remote_refs(self, wire, url):
382 382 repo = Repo(url)
383 383 return repo.get_refs()
384 384
385 385 @reraise_safe_exceptions
386 386 def get_description(self, wire):
387 387 repo = self._factory.repo(wire)
388 388 return repo.get_description()
389 389
390 390 @reraise_safe_exceptions
391 391 def get_file_history(self, wire, file_path, commit_id, limit):
392 392 repo = self._factory.repo(wire)
393 393 include = [commit_id]
394 394 paths = [file_path]
395 395
396 396 walker = repo.get_walker(include, paths=paths, max_entries=limit)
397 397 return [x.commit.id for x in walker]
398 398
399 399 @reraise_safe_exceptions
400 400 def get_missing_revs(self, wire, rev1, rev2, path2):
401 401 repo = self._factory.repo(wire)
402 402 LocalGitClient(thin_packs=False).fetch(path2, repo)
403 403
404 404 wire_remote = wire.copy()
405 405 wire_remote['path'] = path2
406 406 repo_remote = self._factory.repo(wire_remote)
407 407 LocalGitClient(thin_packs=False).fetch(wire["path"], repo_remote)
408 408
409 409 revs = [
410 410 x.commit.id
411 411 for x in repo_remote.get_walker(include=[rev2], exclude=[rev1])]
412 412 return revs
413 413
414 414 @reraise_safe_exceptions
415 415 def get_object(self, wire, sha):
416 416 repo = self._factory.repo(wire)
417 417 obj = repo.get_object(sha)
418 418 commit_id = obj.id
419 419
420 420 if isinstance(obj, Tag):
421 421 commit_id = obj.object[1]
422 422
423 423 return {
424 424 'id': obj.id,
425 425 'type': obj.type_name,
426 426 'commit_id': commit_id
427 427 }
428 428
429 429 @reraise_safe_exceptions
430 430 def get_object_attrs(self, wire, sha, *attrs):
431 431 repo = self._factory.repo(wire)
432 432 obj = repo.get_object(sha)
433 433 return list(getattr(obj, a) for a in attrs)
434 434
435 435 @reraise_safe_exceptions
436 436 def get_refs(self, wire):
437 437 repo = self._factory.repo(wire)
438 438 result = {}
439 439 for ref, sha in repo.refs.as_dict().items():
440 440 peeled_sha = repo.get_peeled(ref)
441 441 result[ref] = peeled_sha
442 442 return result
443 443
444 444 @reraise_safe_exceptions
445 445 def get_refs_path(self, wire):
446 446 repo = self._factory.repo(wire)
447 447 return repo.refs.path
448 448
449 449 @reraise_safe_exceptions
450 450 def head(self, wire):
451 451 repo = self._factory.repo(wire)
452 452 return repo.head()
453 453
454 454 @reraise_safe_exceptions
455 455 def init(self, wire):
456 456 repo_path = str_to_dulwich(wire['path'])
457 457 self.repo = Repo.init(repo_path)
458 458
459 459 @reraise_safe_exceptions
460 460 def init_bare(self, wire):
461 461 repo_path = str_to_dulwich(wire['path'])
462 462 self.repo = Repo.init_bare(repo_path)
463 463
464 464 @reraise_safe_exceptions
465 465 def revision(self, wire, rev):
466 466 repo = self._factory.repo(wire)
467 467 obj = repo[rev]
468 468 obj_data = {
469 469 'id': obj.id,
470 470 }
471 471 try:
472 472 obj_data['tree'] = obj.tree
473 473 except AttributeError:
474 474 pass
475 475 return obj_data
476 476
477 477 @reraise_safe_exceptions
478 478 def commit_attribute(self, wire, rev, attr):
479 479 repo = self._factory.repo(wire)
480 480 obj = repo[rev]
481 481 return getattr(obj, attr)
482 482
483 483 @reraise_safe_exceptions
484 484 def set_refs(self, wire, key, value):
485 485 repo = self._factory.repo(wire)
486 486 repo.refs[key] = value
487 487
488 488 @reraise_safe_exceptions
489 489 def remove_ref(self, wire, key):
490 490 repo = self._factory.repo(wire)
491 491 del repo.refs[key]
492 492
493 493 @reraise_safe_exceptions
494 494 def tree_changes(self, wire, source_id, target_id):
495 495 repo = self._factory.repo(wire)
496 496 source = repo[source_id].tree if source_id else None
497 497 target = repo[target_id].tree
498 498 result = repo.object_store.tree_changes(source, target)
499 499 return list(result)
500 500
501 501 @reraise_safe_exceptions
502 502 def tree_items(self, wire, tree_id):
503 503 repo = self._factory.repo(wire)
504 504 tree = repo[tree_id]
505 505
506 506 result = []
507 507 for item in tree.iteritems():
508 508 item_sha = item.sha
509 509 item_mode = item.mode
510 510
511 511 if FILE_MODE(item_mode) == GIT_LINK:
512 512 item_type = "link"
513 513 else:
514 514 item_type = repo[item_sha].type_name
515 515
516 516 result.append((item.path, item_mode, item_sha, item_type))
517 517 return result
518 518
519 519 @reraise_safe_exceptions
520 520 def update_server_info(self, wire):
521 521 repo = self._factory.repo(wire)
522 522 update_server_info(repo)
523 523
524 524 @reraise_safe_exceptions
525 525 def discover_git_version(self):
526 526 stdout, _ = self.run_git_command(
527 527 {}, ['--version'], _bare=True, _safe=True)
528 528 prefix = 'git version'
529 529 if stdout.startswith(prefix):
530 530 stdout = stdout[len(prefix):]
531 531 return stdout.strip()
532 532
533 533 @reraise_safe_exceptions
534 534 def run_git_command(self, wire, cmd, **opts):
535 535 path = wire.get('path', None)
536 536
537 537 if path and os.path.isdir(path):
538 538 opts['cwd'] = path
539 539
540 540 if '_bare' in opts:
541 541 _copts = []
542 542 del opts['_bare']
543 543 else:
544 544 _copts = ['-c', 'core.quotepath=false', ]
545 545 safe_call = False
546 546 if '_safe' in opts:
547 547 # no exc on failure
548 548 del opts['_safe']
549 549 safe_call = True
550 550
551 551 gitenv = os.environ.copy()
552 552 gitenv.update(opts.pop('extra_env', {}))
553 553 # need to clean fix GIT_DIR !
554 554 if 'GIT_DIR' in gitenv:
555 555 del gitenv['GIT_DIR']
556 556 gitenv['GIT_CONFIG_NOGLOBAL'] = '1'
557 557
558 558 cmd = [settings.GIT_EXECUTABLE] + _copts + cmd
559 559
560 560 try:
561 561 _opts = {'env': gitenv, 'shell': False}
562 562 _opts.update(opts)
563 563 p = subprocessio.SubprocessIOChunker(cmd, **_opts)
564 564
565 565 return ''.join(p), ''.join(p.error)
566 566 except (EnvironmentError, OSError) as err:
567 567 tb_err = ("Couldn't run git command (%s).\n"
568 568 "Original error was:%s\n" % (cmd, err))
569 569 log.exception(tb_err)
570 570 if safe_call:
571 571 return '', err
572 572 else:
573 573 raise exceptions.VcsException(tb_err)
574 574
575 575
576 576 def str_to_dulwich(value):
577 577 """
578 578 Dulwich 0.10.1a requires `unicode` objects to be passed in.
579 579 """
580 580 return value.decode(settings.WIRE_ENCODING)
@@ -1,723 +1,724 b''
1 1 # RhodeCode VCSServer provides access to different vcs backends via network.
2 2 # Copyright (C) 2014-2016 RodeCode 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 io
19 19 import logging
20 20 import stat
21 21 import sys
22 22 import urllib
23 23 import urllib2
24 24
25 25 from hgext import largefiles, rebase
26 26 from hgext.strip import strip as hgext_strip
27 27 from mercurial import commands
28 28 from mercurial import unionrepo
29 29
30 30 from vcsserver import exceptions
31 31 from vcsserver.base import RepoFactory, obfuscate_qs
32 32 from vcsserver.hgcompat import (
33 33 archival, bin, clone, config as hgconfig, diffopts, hex,
34 34 hg_url as url_parser, httpbasicauthhandler, httpdigestauthhandler,
35 35 httppeer, localrepository, match, memctx, exchange, memfilectx, nullrev,
36 36 patch, peer, revrange, ui, Abort, LookupError, RepoError, RepoLookupError,
37 37 InterventionRequired, RequirementError)
38 38
39 39 log = logging.getLogger(__name__)
40 40
41 41
42 42 def make_ui_from_config(repo_config):
43 43 baseui = ui.ui()
44 44
45 45 # clean the baseui object
46 46 baseui._ocfg = hgconfig.config()
47 47 baseui._ucfg = hgconfig.config()
48 48 baseui._tcfg = hgconfig.config()
49 49
50 50 for section, option, value in repo_config:
51 51 baseui.setconfig(section, option, value)
52 52
53 53 # make our hgweb quiet so it doesn't print output
54 54 baseui.setconfig('ui', 'quiet', 'true')
55 55
56 56 # force mercurial to only use 1 thread, otherwise it may try to set a
57 57 # signal in a non-main thread, thus generating a ValueError.
58 58 baseui.setconfig('worker', 'numcpus', 1)
59 59
60 60 # If there is no config for the largefiles extension, we explicitly disable
61 61 # it here. This overrides settings from repositories hgrc file. Recent
62 62 # mercurial versions enable largefiles in hgrc on clone from largefile
63 63 # repo.
64 64 if not baseui.hasconfig('extensions', 'largefiles'):
65 65 log.debug('Explicitly disable largefiles extension for repo.')
66 66 baseui.setconfig('extensions', 'largefiles', '!')
67 67
68 68 return baseui
69 69
70 70
71 71 def reraise_safe_exceptions(func):
72 72 """Decorator for converting mercurial exceptions to something neutral."""
73 73 def wrapper(*args, **kwargs):
74 74 try:
75 75 return func(*args, **kwargs)
76 76 except (Abort, InterventionRequired):
77 77 raise_from_original(exceptions.AbortException)
78 78 except RepoLookupError:
79 79 raise_from_original(exceptions.LookupException)
80 80 except RequirementError:
81 81 raise_from_original(exceptions.RequirementException)
82 82 except RepoError:
83 83 raise_from_original(exceptions.VcsException)
84 84 except LookupError:
85 85 raise_from_original(exceptions.LookupException)
86 86 except Exception as e:
87 87 if not hasattr(e, '_vcs_kind'):
88 88 log.exception("Unhandled exception in hg remote call")
89 89 raise_from_original(exceptions.UnhandledException)
90 90 raise
91 91 return wrapper
92 92
93 93
94 94 def raise_from_original(new_type):
95 95 """
96 96 Raise a new exception type with original args and traceback.
97 97 """
98 98 _, original, traceback = sys.exc_info()
99 99 try:
100 100 raise new_type(*original.args), None, traceback
101 101 finally:
102 102 del traceback
103 103
104 104
105 105 class MercurialFactory(RepoFactory):
106 106
107 107 def _create_config(self, config, hooks=True):
108 108 if not hooks:
109 109 hooks_to_clean = frozenset((
110 110 'changegroup.repo_size', 'preoutgoing.pre_pull',
111 111 'outgoing.pull_logger', 'prechangegroup.pre_push'))
112 112 new_config = []
113 113 for section, option, value in config:
114 114 if section == 'hooks' and option in hooks_to_clean:
115 115 continue
116 116 new_config.append((section, option, value))
117 117 config = new_config
118 118
119 119 baseui = make_ui_from_config(config)
120 120 return baseui
121 121
122 122 def _create_repo(self, wire, create):
123 123 baseui = self._create_config(wire["config"])
124 124 return localrepository(baseui, wire["path"], create)
125 125
126 126
127 127 class HgRemote(object):
128 128
129 129 def __init__(self, factory):
130 130 self._factory = factory
131 131
132 132 self._bulk_methods = {
133 133 "affected_files": self.ctx_files,
134 134 "author": self.ctx_user,
135 135 "branch": self.ctx_branch,
136 136 "children": self.ctx_children,
137 137 "date": self.ctx_date,
138 138 "message": self.ctx_description,
139 139 "parents": self.ctx_parents,
140 140 "status": self.ctx_status,
141 141 "_file_paths": self.ctx_list,
142 142 }
143 143
144 144 @reraise_safe_exceptions
145 145 def discover_hg_version(self):
146 146 from mercurial import util
147 147 return util.version()
148 148
149 149 @reraise_safe_exceptions
150 150 def archive_repo(self, archive_path, mtime, file_info, kind):
151 151 if kind == "tgz":
152 152 archiver = archival.tarit(archive_path, mtime, "gz")
153 153 elif kind == "tbz2":
154 154 archiver = archival.tarit(archive_path, mtime, "bz2")
155 155 elif kind == 'zip':
156 156 archiver = archival.zipit(archive_path, mtime)
157 157 else:
158 158 raise exceptions.ArchiveException(
159 159 'Remote does not support: "%s".' % kind)
160 160
161 161 for f_path, f_mode, f_is_link, f_content in file_info:
162 162 archiver.addfile(f_path, f_mode, f_is_link, f_content)
163 163 archiver.done()
164 164
165 165 @reraise_safe_exceptions
166 166 def bookmarks(self, wire):
167 167 repo = self._factory.repo(wire)
168 168 return dict(repo._bookmarks)
169 169
170 170 @reraise_safe_exceptions
171 171 def branches(self, wire, normal, closed):
172 172 repo = self._factory.repo(wire)
173 173 iter_branches = repo.branchmap().iterbranches()
174 174 bt = {}
175 175 for branch_name, _heads, tip, is_closed in iter_branches:
176 176 if normal and not is_closed:
177 177 bt[branch_name] = tip
178 178 if closed and is_closed:
179 179 bt[branch_name] = tip
180 180
181 181 return bt
182 182
183 183 @reraise_safe_exceptions
184 184 def bulk_request(self, wire, rev, pre_load):
185 185 result = {}
186 186 for attr in pre_load:
187 187 try:
188 188 method = self._bulk_methods[attr]
189 189 result[attr] = method(wire, rev)
190 190 except KeyError:
191 191 raise exceptions.VcsException(
192 192 'Unknown bulk attribute: "%s"' % attr)
193 193 return result
194 194
195 195 @reraise_safe_exceptions
196 196 def clone(self, wire, source, dest, update_after_clone=False, hooks=True):
197 197 baseui = self._factory._create_config(wire["config"], hooks=hooks)
198 198 clone(baseui, source, dest, noupdate=not update_after_clone)
199 199
200 200 @reraise_safe_exceptions
201 201 def commitctx(
202 202 self, wire, message, parents, commit_time, commit_timezone,
203 203 user, files, extra, removed, updated):
204 204
205 205 def _filectxfn(_repo, memctx, path):
206 206 """
207 207 Marks given path as added/changed/removed in a given _repo. This is
208 208 for internal mercurial commit function.
209 209 """
210 210
211 211 # check if this path is removed
212 212 if path in removed:
213 213 # returning None is a way to mark node for removal
214 214 return None
215 215
216 216 # check if this path is added
217 217 for node in updated:
218 218 if node['path'] == path:
219 219 return memfilectx(
220 220 _repo,
221 221 path=node['path'],
222 222 data=node['content'],
223 223 islink=False,
224 224 isexec=bool(node['mode'] & stat.S_IXUSR),
225 225 copied=False,
226 226 memctx=memctx)
227 227
228 228 raise exceptions.AbortException(
229 229 "Given path haven't been marked as added, "
230 230 "changed or removed (%s)" % path)
231 231
232 232 repo = self._factory.repo(wire)
233 233
234 234 commit_ctx = memctx(
235 235 repo=repo,
236 236 parents=parents,
237 237 text=message,
238 238 files=files,
239 239 filectxfn=_filectxfn,
240 240 user=user,
241 241 date=(commit_time, commit_timezone),
242 242 extra=extra)
243 243
244 244 n = repo.commitctx(commit_ctx)
245 245 new_id = hex(n)
246 246
247 247 return new_id
248 248
249 249 @reraise_safe_exceptions
250 250 def ctx_branch(self, wire, revision):
251 251 repo = self._factory.repo(wire)
252 252 ctx = repo[revision]
253 253 return ctx.branch()
254 254
255 255 @reraise_safe_exceptions
256 256 def ctx_children(self, wire, revision):
257 257 repo = self._factory.repo(wire)
258 258 ctx = repo[revision]
259 259 return [child.rev() for child in ctx.children()]
260 260
261 261 @reraise_safe_exceptions
262 262 def ctx_date(self, wire, revision):
263 263 repo = self._factory.repo(wire)
264 264 ctx = repo[revision]
265 265 return ctx.date()
266 266
267 267 @reraise_safe_exceptions
268 268 def ctx_description(self, wire, revision):
269 269 repo = self._factory.repo(wire)
270 270 ctx = repo[revision]
271 271 return ctx.description()
272 272
273 273 @reraise_safe_exceptions
274 274 def ctx_diff(
275 275 self, wire, revision, git=True, ignore_whitespace=True, context=3):
276 276 repo = self._factory.repo(wire)
277 277 ctx = repo[revision]
278 278 result = ctx.diff(
279 279 git=git, ignore_whitespace=ignore_whitespace, context=context)
280 280 return list(result)
281 281
282 282 @reraise_safe_exceptions
283 283 def ctx_files(self, wire, revision):
284 284 repo = self._factory.repo(wire)
285 285 ctx = repo[revision]
286 286 return ctx.files()
287 287
288 288 @reraise_safe_exceptions
289 289 def ctx_list(self, path, revision):
290 290 repo = self._factory.repo(path)
291 291 ctx = repo[revision]
292 292 return list(ctx)
293 293
294 294 @reraise_safe_exceptions
295 295 def ctx_parents(self, wire, revision):
296 296 repo = self._factory.repo(wire)
297 297 ctx = repo[revision]
298 298 return [parent.rev() for parent in ctx.parents()]
299 299
300 300 @reraise_safe_exceptions
301 301 def ctx_substate(self, wire, revision):
302 302 repo = self._factory.repo(wire)
303 303 ctx = repo[revision]
304 304 return ctx.substate
305 305
306 306 @reraise_safe_exceptions
307 307 def ctx_status(self, wire, revision):
308 308 repo = self._factory.repo(wire)
309 309 ctx = repo[revision]
310 310 status = repo[ctx.p1().node()].status(other=ctx.node())
311 311 # object of status (odd, custom named tuple in mercurial) is not
312 312 # correctly serializable via Pyro, we make it a list, as the underling
313 313 # API expects this to be a list
314 314 return list(status)
315 315
316 316 @reraise_safe_exceptions
317 317 def ctx_user(self, wire, revision):
318 318 repo = self._factory.repo(wire)
319 319 ctx = repo[revision]
320 320 return ctx.user()
321 321
322 322 @reraise_safe_exceptions
323 323 def check_url(self, url, config):
324 324 _proto = None
325 325 if '+' in url[:url.find('://')]:
326 326 _proto = url[0:url.find('+')]
327 327 url = url[url.find('+') + 1:]
328 328 handlers = []
329 329 url_obj = url_parser(url)
330 330 test_uri, authinfo = url_obj.authinfo()
331 url_obj.passwd = '*****'
331 url_obj.passwd = '*****' if url_obj.passwd else url_obj.passwd
332 332 url_obj.query = obfuscate_qs(url_obj.query)
333 333
334 334 cleaned_uri = str(url_obj)
335 335 log.info("Checking URL for remote cloning/import: %s", cleaned_uri)
336 336
337 337 if authinfo:
338 338 # create a password manager
339 339 passmgr = urllib2.HTTPPasswordMgrWithDefaultRealm()
340 340 passmgr.add_password(*authinfo)
341 341
342 342 handlers.extend((httpbasicauthhandler(passmgr),
343 343 httpdigestauthhandler(passmgr)))
344 344
345 345 o = urllib2.build_opener(*handlers)
346 346 o.addheaders = [('Content-Type', 'application/mercurial-0.1'),
347 347 ('Accept', 'application/mercurial-0.1')]
348 348
349 349 q = {"cmd": 'between'}
350 350 q.update({'pairs': "%s-%s" % ('0' * 40, '0' * 40)})
351 351 qs = '?%s' % urllib.urlencode(q)
352 352 cu = "%s%s" % (test_uri, qs)
353 353 req = urllib2.Request(cu, None, {})
354 354
355 355 try:
356 356 log.debug("Trying to open URL %s", cleaned_uri)
357 357 resp = o.open(req)
358 358 if resp.code != 200:
359 359 raise exceptions.URLError('Return Code is not 200')
360 360 except Exception as e:
361 361 log.warning("URL cannot be opened: %s", cleaned_uri, exc_info=True)
362 362 # means it cannot be cloned
363 363 raise exceptions.URLError("[%s] org_exc: %s" % (cleaned_uri, e))
364 364
365 365 # now check if it's a proper hg repo, but don't do it for svn
366 366 try:
367 367 if _proto == 'svn':
368 368 pass
369 369 else:
370 370 # check for pure hg repos
371 371 log.debug(
372 372 "Verifying if URL is a Mercurial repository: %s",
373 373 cleaned_uri)
374 374 httppeer(make_ui_from_config(config), url).lookup('tip')
375 375 except Exception as e:
376 376 log.warning("URL is not a valid Mercurial repository: %s",
377 377 cleaned_uri)
378 378 raise exceptions.URLError(
379 379 "url [%s] does not look like an hg repo org_exc: %s"
380 380 % (cleaned_uri, e))
381 381
382 382 log.info("URL is a valid Mercurial repository: %s", cleaned_uri)
383 383 return True
384 384
385 385 @reraise_safe_exceptions
386 386 def diff(
387 387 self, wire, rev1, rev2, file_filter, opt_git, opt_ignorews,
388 388 context):
389 389 repo = self._factory.repo(wire)
390 390
391 391 if file_filter:
392 392 filter = match(file_filter[0], '', [file_filter[1]])
393 393 else:
394 394 filter = file_filter
395 395 opts = diffopts(git=opt_git, ignorews=opt_ignorews, context=context)
396 396
397 397 try:
398 398 return "".join(patch.diff(
399 399 repo, node1=rev1, node2=rev2, match=filter, opts=opts))
400 400 except RepoLookupError:
401 401 raise exceptions.LookupException()
402 402
403 403 @reraise_safe_exceptions
404 404 def file_history(self, wire, revision, path, limit):
405 405 repo = self._factory.repo(wire)
406 406
407 407 ctx = repo[revision]
408 408 fctx = ctx.filectx(path)
409 409
410 410 def history_iter():
411 411 limit_rev = fctx.rev()
412 412 for obj in reversed(list(fctx.filelog())):
413 413 obj = fctx.filectx(obj)
414 414 if limit_rev >= obj.rev():
415 415 yield obj
416 416
417 417 history = []
418 418 for cnt, obj in enumerate(history_iter()):
419 419 if limit and cnt >= limit:
420 420 break
421 421 history.append(hex(obj.node()))
422 422
423 423 return [x for x in history]
424 424
425 425 @reraise_safe_exceptions
426 426 def file_history_untill(self, wire, revision, path, limit):
427 427 repo = self._factory.repo(wire)
428 428 ctx = repo[revision]
429 429 fctx = ctx.filectx(path)
430 430
431 431 file_log = list(fctx.filelog())
432 432 if limit:
433 433 # Limit to the last n items
434 434 file_log = file_log[-limit:]
435 435
436 436 return [hex(fctx.filectx(cs).node()) for cs in reversed(file_log)]
437 437
438 438 @reraise_safe_exceptions
439 439 def fctx_annotate(self, wire, revision, path):
440 440 repo = self._factory.repo(wire)
441 441 ctx = repo[revision]
442 442 fctx = ctx.filectx(path)
443 443
444 444 result = []
445 445 for i, annotate_data in enumerate(fctx.annotate()):
446 446 ln_no = i + 1
447 sha = hex(annotate_data[0].node())
448 result.append((ln_no, sha, annotate_data[1]))
447 node_info, content = annotate_data
448 sha = hex(node_info[0].node())
449 result.append((ln_no, sha, content))
449 450 return result
450 451
451 452 @reraise_safe_exceptions
452 453 def fctx_data(self, wire, revision, path):
453 454 repo = self._factory.repo(wire)
454 455 ctx = repo[revision]
455 456 fctx = ctx.filectx(path)
456 457 return fctx.data()
457 458
458 459 @reraise_safe_exceptions
459 460 def fctx_flags(self, wire, revision, path):
460 461 repo = self._factory.repo(wire)
461 462 ctx = repo[revision]
462 463 fctx = ctx.filectx(path)
463 464 return fctx.flags()
464 465
465 466 @reraise_safe_exceptions
466 467 def fctx_size(self, wire, revision, path):
467 468 repo = self._factory.repo(wire)
468 469 ctx = repo[revision]
469 470 fctx = ctx.filectx(path)
470 471 return fctx.size()
471 472
472 473 @reraise_safe_exceptions
473 474 def get_all_commit_ids(self, wire, name):
474 475 repo = self._factory.repo(wire)
475 476 revs = repo.filtered(name).changelog.index
476 477 return map(lambda x: hex(x[7]), revs)[:-1]
477 478
478 479 @reraise_safe_exceptions
479 480 def get_config_value(self, wire, section, name, untrusted=False):
480 481 repo = self._factory.repo(wire)
481 482 return repo.ui.config(section, name, untrusted=untrusted)
482 483
483 484 @reraise_safe_exceptions
484 485 def get_config_bool(self, wire, section, name, untrusted=False):
485 486 repo = self._factory.repo(wire)
486 487 return repo.ui.configbool(section, name, untrusted=untrusted)
487 488
488 489 @reraise_safe_exceptions
489 490 def get_config_list(self, wire, section, name, untrusted=False):
490 491 repo = self._factory.repo(wire)
491 492 return repo.ui.configlist(section, name, untrusted=untrusted)
492 493
493 494 @reraise_safe_exceptions
494 495 def is_large_file(self, wire, path):
495 496 return largefiles.lfutil.isstandin(path)
496 497
497 498 @reraise_safe_exceptions
498 499 def in_store(self, wire, sha):
499 500 repo = self._factory.repo(wire)
500 501 return largefiles.lfutil.instore(repo, sha)
501 502
502 503 @reraise_safe_exceptions
503 504 def in_user_cache(self, wire, sha):
504 505 repo = self._factory.repo(wire)
505 506 return largefiles.lfutil.inusercache(repo.ui, sha)
506 507
507 508 @reraise_safe_exceptions
508 509 def store_path(self, wire, sha):
509 510 repo = self._factory.repo(wire)
510 511 return largefiles.lfutil.storepath(repo, sha)
511 512
512 513 @reraise_safe_exceptions
513 514 def link(self, wire, sha, path):
514 515 repo = self._factory.repo(wire)
515 516 largefiles.lfutil.link(
516 517 largefiles.lfutil.usercachepath(repo.ui, sha), path)
517 518
518 519 @reraise_safe_exceptions
519 520 def localrepository(self, wire, create=False):
520 521 self._factory.repo(wire, create=create)
521 522
522 523 @reraise_safe_exceptions
523 524 def lookup(self, wire, revision, both):
524 525 # TODO Paris: Ugly hack to "deserialize" long for msgpack
525 526 if isinstance(revision, float):
526 527 revision = long(revision)
527 528 repo = self._factory.repo(wire)
528 529 try:
529 530 ctx = repo[revision]
530 531 except RepoLookupError:
531 532 raise exceptions.LookupException(revision)
532 533 except LookupError as e:
533 534 raise exceptions.LookupException(e.name)
534 535
535 536 if not both:
536 537 return ctx.hex()
537 538
538 539 ctx = repo[ctx.hex()]
539 540 return ctx.hex(), ctx.rev()
540 541
541 542 @reraise_safe_exceptions
542 543 def pull(self, wire, url, commit_ids=None):
543 544 repo = self._factory.repo(wire)
544 545 remote = peer(repo, {}, url)
545 546 if commit_ids:
546 547 commit_ids = [bin(commit_id) for commit_id in commit_ids]
547 548
548 549 return exchange.pull(
549 550 repo, remote, heads=commit_ids, force=None).cgresult
550 551
551 552 @reraise_safe_exceptions
552 553 def revision(self, wire, rev):
553 554 repo = self._factory.repo(wire)
554 555 ctx = repo[rev]
555 556 return ctx.rev()
556 557
557 558 @reraise_safe_exceptions
558 559 def rev_range(self, wire, filter):
559 560 repo = self._factory.repo(wire)
560 561 revisions = [rev for rev in revrange(repo, filter)]
561 562 return revisions
562 563
563 564 @reraise_safe_exceptions
564 565 def rev_range_hash(self, wire, node):
565 566 repo = self._factory.repo(wire)
566 567
567 568 def get_revs(repo, rev_opt):
568 569 if rev_opt:
569 570 revs = revrange(repo, rev_opt)
570 571 if len(revs) == 0:
571 572 return (nullrev, nullrev)
572 573 return max(revs), min(revs)
573 574 else:
574 575 return len(repo) - 1, 0
575 576
576 577 stop, start = get_revs(repo, [node + ':'])
577 578 revs = [hex(repo[r].node()) for r in xrange(start, stop + 1)]
578 579 return revs
579 580
580 581 @reraise_safe_exceptions
581 582 def revs_from_revspec(self, wire, rev_spec, *args, **kwargs):
582 583 other_path = kwargs.pop('other_path', None)
583 584
584 585 # case when we want to compare two independent repositories
585 586 if other_path and other_path != wire["path"]:
586 587 baseui = self._factory._create_config(wire["config"])
587 588 repo = unionrepo.unionrepository(baseui, other_path, wire["path"])
588 589 else:
589 590 repo = self._factory.repo(wire)
590 591 return list(repo.revs(rev_spec, *args))
591 592
592 593 @reraise_safe_exceptions
593 594 def strip(self, wire, revision, update, backup):
594 595 repo = self._factory.repo(wire)
595 596 ctx = repo[revision]
596 597 hgext_strip(
597 598 repo.baseui, repo, ctx.node(), update=update, backup=backup)
598 599
599 600 @reraise_safe_exceptions
600 601 def tag(self, wire, name, revision, message, local, user,
601 602 tag_time, tag_timezone):
602 603 repo = self._factory.repo(wire)
603 604 ctx = repo[revision]
604 605 node = ctx.node()
605 606
606 607 date = (tag_time, tag_timezone)
607 608 try:
608 609 repo.tag(name, node, message, local, user, date)
609 610 except Abort:
610 611 log.exception("Tag operation aborted")
611 612 raise exceptions.AbortException()
612 613
613 614 @reraise_safe_exceptions
614 615 def tags(self, wire):
615 616 repo = self._factory.repo(wire)
616 617 return repo.tags()
617 618
618 619 @reraise_safe_exceptions
619 620 def update(self, wire, node=None, clean=False):
620 621 repo = self._factory.repo(wire)
621 622 baseui = self._factory._create_config(wire['config'])
622 623 commands.update(baseui, repo, node=node, clean=clean)
623 624
624 625 @reraise_safe_exceptions
625 626 def identify(self, wire):
626 627 repo = self._factory.repo(wire)
627 628 baseui = self._factory._create_config(wire['config'])
628 629 output = io.BytesIO()
629 630 baseui.write = output.write
630 631 # This is required to get a full node id
631 632 baseui.debugflag = True
632 633 commands.identify(baseui, repo, id=True)
633 634
634 635 return output.getvalue()
635 636
636 637 @reraise_safe_exceptions
637 638 def pull_cmd(self, wire, source, bookmark=None, branch=None, revision=None,
638 639 hooks=True):
639 640 repo = self._factory.repo(wire)
640 641 baseui = self._factory._create_config(wire['config'], hooks=hooks)
641 642
642 643 # Mercurial internally has a lot of logic that checks ONLY if
643 644 # option is defined, we just pass those if they are defined then
644 645 opts = {}
645 646 if bookmark:
646 647 opts['bookmark'] = bookmark
647 648 if branch:
648 649 opts['branch'] = branch
649 650 if revision:
650 651 opts['rev'] = revision
651 652
652 653 commands.pull(baseui, repo, source, **opts)
653 654
654 655 @reraise_safe_exceptions
655 656 def heads(self, wire, branch=None):
656 657 repo = self._factory.repo(wire)
657 658 baseui = self._factory._create_config(wire['config'])
658 659 output = io.BytesIO()
659 660
660 661 def write(data, **unused_kwargs):
661 662 output.write(data)
662 663
663 664 baseui.write = write
664 665 if branch:
665 666 args = [branch]
666 667 else:
667 668 args = []
668 669 commands.heads(baseui, repo, template='{node} ', *args)
669 670
670 671 return output.getvalue()
671 672
672 673 @reraise_safe_exceptions
673 674 def ancestor(self, wire, revision1, revision2):
674 675 repo = self._factory.repo(wire)
675 676 baseui = self._factory._create_config(wire['config'])
676 677 output = io.BytesIO()
677 678 baseui.write = output.write
678 679 commands.debugancestor(baseui, repo, revision1, revision2)
679 680
680 681 return output.getvalue()
681 682
682 683 @reraise_safe_exceptions
683 684 def push(self, wire, revisions, dest_path, hooks=True,
684 685 push_branches=False):
685 686 repo = self._factory.repo(wire)
686 687 baseui = self._factory._create_config(wire['config'], hooks=hooks)
687 688 commands.push(baseui, repo, dest=dest_path, rev=revisions,
688 689 new_branch=push_branches)
689 690
690 691 @reraise_safe_exceptions
691 692 def merge(self, wire, revision):
692 693 repo = self._factory.repo(wire)
693 694 baseui = self._factory._create_config(wire['config'])
694 695 repo.ui.setconfig('ui', 'merge', 'internal:dump')
695 696
696 697 # In case of sub repositories are used mercurial prompts the user in
697 698 # case of merge conflicts or different sub repository sources. By
698 699 # setting the interactive flag to `False` mercurial doesn't prompt the
699 700 # used but instead uses a default value.
700 701 repo.ui.setconfig('ui', 'interactive', False)
701 702
702 703 commands.merge(baseui, repo, rev=revision)
703 704
704 705 @reraise_safe_exceptions
705 706 def commit(self, wire, message, username):
706 707 repo = self._factory.repo(wire)
707 708 baseui = self._factory._create_config(wire['config'])
708 709 repo.ui.setconfig('ui', 'username', username)
709 710 commands.commit(baseui, repo, message=message)
710 711
711 712 @reraise_safe_exceptions
712 713 def rebase(self, wire, source=None, dest=None, abort=False):
713 714 repo = self._factory.repo(wire)
714 715 baseui = self._factory._create_config(wire['config'])
715 716 repo.ui.setconfig('ui', 'merge', 'internal:dump')
716 717 rebase.rebase(
717 718 baseui, repo, base=source, dest=dest, abort=abort, keep=not abort)
718 719
719 720 @reraise_safe_exceptions
720 721 def bookmark(self, wire, bookmark, revision=None):
721 722 repo = self._factory.repo(wire)
722 723 baseui = self._factory._create_config(wire['config'])
723 724 commands.bookmark(baseui, repo, bookmark, rev=revision, force=True)
General Comments 0
You need to be logged in to leave comments. Login now