##// END OF EJS Templates
hg: split peer and repo lookup tables
Matt Mackall -
r14568:5f002e33 default
parent child Browse files
Show More
@@ -1,98 +1,98 b''
1 1 # Copyright 2009, Alexander Solovyov <piranha@piranha.org.ua>
2 2 #
3 3 # This software may be used and distributed according to the terms of the
4 4 # GNU General Public License version 2 or any later version.
5 5
6 6 """extend schemes with shortcuts to repository swarms
7 7
8 8 This extension allows you to specify shortcuts for parent URLs with a
9 9 lot of repositories to act like a scheme, for example::
10 10
11 11 [schemes]
12 12 py = http://code.python.org/hg/
13 13
14 14 After that you can use it like::
15 15
16 16 hg clone py://trunk/
17 17
18 18 Additionally there is support for some more complex schemas, for
19 19 example used by Google Code::
20 20
21 21 [schemes]
22 22 gcode = http://{1}.googlecode.com/hg/
23 23
24 24 The syntax is taken from Mercurial templates, and you have unlimited
25 25 number of variables, starting with ``{1}`` and continuing with
26 26 ``{2}``, ``{3}`` and so on. This variables will receive parts of URL
27 27 supplied, split by ``/``. Anything not specified as ``{part}`` will be
28 28 just appended to an URL.
29 29
30 30 For convenience, the extension adds these schemes by default::
31 31
32 32 [schemes]
33 33 py = http://hg.python.org/
34 34 bb = https://bitbucket.org/
35 35 bb+ssh = ssh://hg@bitbucket.org/
36 36 gcode = https://{1}.googlecode.com/hg/
37 37 kiln = https://{1}.kilnhg.com/Repo/
38 38
39 39 You can override a predefined scheme by defining a new scheme with the
40 40 same name.
41 41 """
42 42
43 43 import os, re
44 44 from mercurial import extensions, hg, templater, util
45 45 from mercurial.i18n import _
46 46
47 47
48 48 class ShortRepository(object):
49 49 def __init__(self, url, scheme, templater):
50 50 self.scheme = scheme
51 51 self.templater = templater
52 52 self.url = url
53 53 try:
54 54 self.parts = max(map(int, re.findall(r'\{(\d+)\}', self.url)))
55 55 except ValueError:
56 56 self.parts = 0
57 57
58 58 def __repr__(self):
59 59 return '<ShortRepository: %s>' % self.scheme
60 60
61 61 def instance(self, ui, url, create):
62 62 # Should this use urlmod.url(), or is manual parsing better?
63 63 url = url.split('://', 1)[1]
64 64 parts = url.split('/', self.parts)
65 65 if len(parts) > self.parts:
66 66 tail = parts[-1]
67 67 parts = parts[:-1]
68 68 else:
69 69 tail = ''
70 70 context = dict((str(i + 1), v) for i, v in enumerate(parts))
71 71 url = ''.join(self.templater.process(self.url, context)) + tail
72 return hg._lookup(url).instance(ui, url, create)
72 return hg._peerlookup(url).instance(ui, url, create)
73 73
74 74 def hasdriveletter(orig, path):
75 75 for scheme in schemes:
76 76 if path.startswith(scheme + ':'):
77 77 return False
78 78 return orig(path)
79 79
80 80 schemes = {
81 81 'py': 'http://hg.python.org/',
82 82 'bb': 'https://bitbucket.org/',
83 83 'bb+ssh': 'ssh://hg@bitbucket.org/',
84 84 'gcode': 'https://{1}.googlecode.com/hg/',
85 85 'kiln': 'https://{1}.kilnhg.com/Repo/'
86 86 }
87 87
88 88 def extsetup(ui):
89 89 schemes.update(dict(ui.configitems('schemes')))
90 90 t = templater.engine(lambda x: x)
91 91 for scheme, url in schemes.items():
92 92 if (os.name == 'nt' and len(scheme) == 1 and scheme.isalpha()
93 93 and os.path.exists('%s:\\' % scheme)):
94 94 raise util.Abort(_('custom scheme %s:// conflicts with drive '
95 95 'letter %s:\\\n') % (scheme, scheme.upper()))
96 hg.schemes[scheme] = ShortRepository(url, scheme, t)
96 hg._peerschemes[scheme] = ShortRepository(url, scheme, t)
97 97
98 98 extensions.wrapfunction(util, 'hasdriveletter', hasdriveletter)
@@ -1,564 +1,582 b''
1 1 # hg.py - repository classes for mercurial
2 2 #
3 3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 4 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
5 5 #
6 6 # This software may be used and distributed according to the terms of the
7 7 # GNU General Public License version 2 or any later version.
8 8
9 9 from i18n import _
10 10 from lock import release
11 11 from node import hex, nullid
12 12 import localrepo, bundlerepo, httprepo, sshrepo, statichttprepo, bookmarks
13 13 import lock, util, extensions, error, node
14 14 import cmdutil, discovery
15 15 import merge as mergemod
16 16 import verify as verifymod
17 17 import errno, os, shutil
18 18
19 19 def _local(path):
20 20 path = util.expandpath(util.localpath(path))
21 21 return (os.path.isfile(path) and bundlerepo or localrepo)
22 22
23 23 def addbranchrevs(lrepo, repo, branches, revs):
24 24 hashbranch, branches = branches
25 25 if not hashbranch and not branches:
26 26 return revs or None, revs and revs[0] or None
27 27 revs = revs and list(revs) or []
28 28 if not repo.capable('branchmap'):
29 29 if branches:
30 30 raise util.Abort(_("remote branch lookup not supported"))
31 31 revs.append(hashbranch)
32 32 return revs, revs[0]
33 33 branchmap = repo.branchmap()
34 34
35 35 def primary(branch):
36 36 if branch == '.':
37 37 if not lrepo or not lrepo.local():
38 38 raise util.Abort(_("dirstate branch not accessible"))
39 39 branch = lrepo.dirstate.branch()
40 40 if branch in branchmap:
41 41 revs.extend(node.hex(r) for r in reversed(branchmap[branch]))
42 42 return True
43 43 else:
44 44 return False
45 45
46 46 for branch in branches:
47 47 if not primary(branch):
48 48 raise error.RepoLookupError(_("unknown branch '%s'") % branch)
49 49 if hashbranch:
50 50 if not primary(hashbranch):
51 51 revs.append(hashbranch)
52 52 return revs, revs[0]
53 53
54 54 def parseurl(path, branches=None):
55 55 '''parse url#branch, returning (url, (branch, branches))'''
56 56
57 57 u = util.url(path)
58 58 branch = None
59 59 if u.fragment:
60 60 branch = u.fragment
61 61 u.fragment = None
62 62 return str(u), (branch, branches or [])
63 63
64 schemes = {
64 _reposchemes = {
65 65 'bundle': bundlerepo,
66 66 'file': _local,
67 67 'http': httprepo,
68 68 'https': httprepo,
69 69 'ssh': sshrepo,
70 70 'static-http': statichttprepo,
71 71 }
72 72
73 def _lookup(path):
73 def _repolookup(path):
74 74 u = util.url(path)
75 75 scheme = u.scheme or 'file'
76 thing = schemes.get(scheme) or schemes['file']
76 thing = _reposchemes.get(scheme) or _reposchemes['file']
77 77 try:
78 78 return thing(path)
79 79 except TypeError:
80 80 return thing
81 81
82 82 def islocal(repo):
83 83 '''return true if repo or path is local'''
84 84 if isinstance(repo, str):
85 85 try:
86 return _lookup(repo).islocal(repo)
86 return _repolookup(repo).islocal(repo)
87 87 except AttributeError:
88 88 return False
89 89 return repo.local()
90 90
91 91 def repository(ui, path='', create=False):
92 92 """return a repository object for the specified path"""
93 repo = _lookup(path).instance(ui, path, create)
93 repo = _repolookup(path).instance(ui, path, create)
94 94 ui = getattr(repo, "ui", ui)
95 95 for name, module in extensions.extensions():
96 96 hook = getattr(module, 'reposetup', None)
97 97 if hook:
98 98 hook(ui, repo)
99 99 return repo
100 100
101 _peerschemes = {
102 'bundle': bundlerepo,
103 'file': _local,
104 'http': httprepo,
105 'https': httprepo,
106 'ssh': sshrepo,
107 'static-http': statichttprepo,
108 }
109
110 def _peerlookup(path):
111 u = util.url(path)
112 scheme = u.scheme or 'file'
113 thing = _peerschemes.get(scheme) or _peerschemes['file']
114 try:
115 return thing(path)
116 except TypeError:
117 return thing
118
101 119 def peer(ui, opts, path, create=False):
102 120 '''return a repository peer for the specified path'''
103 121 rui = remoteui(ui, opts)
104 return _lookup(path).instance(rui, path, create)
122 return _peerlookup(path).instance(rui, path, create)
105 123
106 124 def defaultdest(source):
107 125 '''return default destination of clone if none is given'''
108 126 return os.path.basename(os.path.normpath(source))
109 127
110 128 def share(ui, source, dest=None, update=True):
111 129 '''create a shared repository'''
112 130
113 131 if not islocal(source):
114 132 raise util.Abort(_('can only share local repositories'))
115 133
116 134 if not dest:
117 135 dest = defaultdest(source)
118 136 else:
119 137 dest = ui.expandpath(dest)
120 138
121 139 if isinstance(source, str):
122 140 origsource = ui.expandpath(source)
123 141 source, branches = parseurl(origsource)
124 142 srcrepo = repository(ui, source)
125 143 rev, checkout = addbranchrevs(srcrepo, srcrepo, branches, None)
126 144 else:
127 145 srcrepo = source
128 146 origsource = source = srcrepo.url()
129 147 checkout = None
130 148
131 149 sharedpath = srcrepo.sharedpath # if our source is already sharing
132 150
133 151 root = os.path.realpath(dest)
134 152 roothg = os.path.join(root, '.hg')
135 153
136 154 if os.path.exists(roothg):
137 155 raise util.Abort(_('destination already exists'))
138 156
139 157 if not os.path.isdir(root):
140 158 os.mkdir(root)
141 159 util.makedir(roothg, notindexed=True)
142 160
143 161 requirements = ''
144 162 try:
145 163 requirements = srcrepo.opener.read('requires')
146 164 except IOError, inst:
147 165 if inst.errno != errno.ENOENT:
148 166 raise
149 167
150 168 requirements += 'shared\n'
151 169 util.writefile(os.path.join(roothg, 'requires'), requirements)
152 170 util.writefile(os.path.join(roothg, 'sharedpath'), sharedpath)
153 171
154 172 r = repository(ui, root)
155 173
156 174 default = srcrepo.ui.config('paths', 'default')
157 175 if default:
158 176 fp = r.opener("hgrc", "w", text=True)
159 177 fp.write("[paths]\n")
160 178 fp.write("default = %s\n" % default)
161 179 fp.close()
162 180
163 181 if update:
164 182 r.ui.status(_("updating working directory\n"))
165 183 if update is not True:
166 184 checkout = update
167 185 for test in (checkout, 'default', 'tip'):
168 186 if test is None:
169 187 continue
170 188 try:
171 189 uprev = r.lookup(test)
172 190 break
173 191 except error.RepoLookupError:
174 192 continue
175 193 _update(r, uprev)
176 194
177 195 def clone(ui, opts, source, dest=None, pull=False, rev=None, update=True,
178 196 stream=False, branch=None):
179 197 """Make a copy of an existing repository.
180 198
181 199 Create a copy of an existing repository in a new directory. The
182 200 source and destination are URLs, as passed to the repository
183 201 function. Returns a pair of repository objects, the source and
184 202 newly created destination.
185 203
186 204 The location of the source is added to the new repository's
187 205 .hg/hgrc file, as the default to be used for future pulls and
188 206 pushes.
189 207
190 208 If an exception is raised, the partly cloned/updated destination
191 209 repository will be deleted.
192 210
193 211 Arguments:
194 212
195 213 source: repository object or URL
196 214
197 215 dest: URL of destination repository to create (defaults to base
198 216 name of source repository)
199 217
200 218 pull: always pull from source repository, even in local case
201 219
202 220 stream: stream raw data uncompressed from repository (fast over
203 221 LAN, slow over WAN)
204 222
205 223 rev: revision to clone up to (implies pull=True)
206 224
207 225 update: update working directory after clone completes, if
208 226 destination is local repository (True means update to default rev,
209 227 anything else is treated as a revision)
210 228
211 229 branch: branches to clone
212 230 """
213 231
214 232 if isinstance(source, str):
215 233 origsource = ui.expandpath(source)
216 234 source, branch = parseurl(origsource, branch)
217 235 srcrepo = repository(remoteui(ui, opts), source)
218 236 else:
219 237 srcrepo = source
220 238 branch = (None, branch or [])
221 239 origsource = source = srcrepo.url()
222 240 rev, checkout = addbranchrevs(srcrepo, srcrepo, branch, rev)
223 241
224 242 if dest is None:
225 243 dest = defaultdest(source)
226 244 ui.status(_("destination directory: %s\n") % dest)
227 245 else:
228 246 dest = ui.expandpath(dest)
229 247
230 248 dest = util.localpath(dest)
231 249 source = util.localpath(source)
232 250
233 251 if os.path.exists(dest):
234 252 if not os.path.isdir(dest):
235 253 raise util.Abort(_("destination '%s' already exists") % dest)
236 254 elif os.listdir(dest):
237 255 raise util.Abort(_("destination '%s' is not empty") % dest)
238 256
239 257 class DirCleanup(object):
240 258 def __init__(self, dir_):
241 259 self.rmtree = shutil.rmtree
242 260 self.dir_ = dir_
243 261 def close(self):
244 262 self.dir_ = None
245 263 def cleanup(self):
246 264 if self.dir_:
247 265 self.rmtree(self.dir_, True)
248 266
249 267 srclock = destlock = dircleanup = None
250 268 try:
251 269 abspath = origsource
252 270 if islocal(origsource):
253 271 abspath = os.path.abspath(util.localpath(origsource))
254 272
255 273 if islocal(dest):
256 274 dircleanup = DirCleanup(dest)
257 275
258 276 copy = False
259 277 if srcrepo.cancopy() and islocal(dest):
260 278 copy = not pull and not rev
261 279
262 280 if copy:
263 281 try:
264 282 # we use a lock here because if we race with commit, we
265 283 # can end up with extra data in the cloned revlogs that's
266 284 # not pointed to by changesets, thus causing verify to
267 285 # fail
268 286 srclock = srcrepo.lock(wait=False)
269 287 except error.LockError:
270 288 copy = False
271 289
272 290 if copy:
273 291 srcrepo.hook('preoutgoing', throw=True, source='clone')
274 292 hgdir = os.path.realpath(os.path.join(dest, ".hg"))
275 293 if not os.path.exists(dest):
276 294 os.mkdir(dest)
277 295 else:
278 296 # only clean up directories we create ourselves
279 297 dircleanup.dir_ = hgdir
280 298 try:
281 299 destpath = hgdir
282 300 util.makedir(destpath, notindexed=True)
283 301 except OSError, inst:
284 302 if inst.errno == errno.EEXIST:
285 303 dircleanup.close()
286 304 raise util.Abort(_("destination '%s' already exists")
287 305 % dest)
288 306 raise
289 307
290 308 hardlink = None
291 309 num = 0
292 310 for f in srcrepo.store.copylist():
293 311 src = os.path.join(srcrepo.sharedpath, f)
294 312 dst = os.path.join(destpath, f)
295 313 dstbase = os.path.dirname(dst)
296 314 if dstbase and not os.path.exists(dstbase):
297 315 os.mkdir(dstbase)
298 316 if os.path.exists(src):
299 317 if dst.endswith('data'):
300 318 # lock to avoid premature writing to the target
301 319 destlock = lock.lock(os.path.join(dstbase, "lock"))
302 320 hardlink, n = util.copyfiles(src, dst, hardlink)
303 321 num += n
304 322 if hardlink:
305 323 ui.debug("linked %d files\n" % num)
306 324 else:
307 325 ui.debug("copied %d files\n" % num)
308 326
309 327 # we need to re-init the repo after manually copying the data
310 328 # into it
311 329 destrepo = repository(remoteui(ui, opts), dest)
312 330 srcrepo.hook('outgoing', source='clone',
313 331 node=node.hex(node.nullid))
314 332 else:
315 333 try:
316 334 destrepo = repository(remoteui(ui, opts), dest, create=True)
317 335 except OSError, inst:
318 336 if inst.errno == errno.EEXIST:
319 337 dircleanup.close()
320 338 raise util.Abort(_("destination '%s' already exists")
321 339 % dest)
322 340 raise
323 341
324 342 revs = None
325 343 if rev:
326 344 if not srcrepo.capable('lookup'):
327 345 raise util.Abort(_("src repository does not support "
328 346 "revision lookup and so doesn't "
329 347 "support clone by revision"))
330 348 revs = [srcrepo.lookup(r) for r in rev]
331 349 checkout = revs[0]
332 350 if destrepo.local():
333 351 destrepo.clone(srcrepo, heads=revs, stream=stream)
334 352 elif srcrepo.local():
335 353 srcrepo.push(destrepo, revs=revs)
336 354 else:
337 355 raise util.Abort(_("clone from remote to remote not supported"))
338 356
339 357 if dircleanup:
340 358 dircleanup.close()
341 359
342 360 if destrepo.local():
343 361 fp = destrepo.opener("hgrc", "w", text=True)
344 362 fp.write("[paths]\n")
345 363 fp.write("default = %s\n" % abspath)
346 364 fp.close()
347 365
348 366 destrepo.ui.setconfig('paths', 'default', abspath)
349 367
350 368 if update:
351 369 if update is not True:
352 370 checkout = update
353 371 if srcrepo.local():
354 372 checkout = srcrepo.lookup(update)
355 373 for test in (checkout, 'default', 'tip'):
356 374 if test is None:
357 375 continue
358 376 try:
359 377 uprev = destrepo.lookup(test)
360 378 break
361 379 except error.RepoLookupError:
362 380 continue
363 381 bn = destrepo[uprev].branch()
364 382 destrepo.ui.status(_("updating to branch %s\n") % bn)
365 383 _update(destrepo, uprev)
366 384
367 385 # clone all bookmarks
368 386 if destrepo.local() and srcrepo.capable("pushkey"):
369 387 rb = srcrepo.listkeys('bookmarks')
370 388 for k, n in rb.iteritems():
371 389 try:
372 390 m = destrepo.lookup(n)
373 391 destrepo._bookmarks[k] = m
374 392 except error.RepoLookupError:
375 393 pass
376 394 if rb:
377 395 bookmarks.write(destrepo)
378 396 elif srcrepo.local() and destrepo.capable("pushkey"):
379 397 for k, n in srcrepo._bookmarks.iteritems():
380 398 destrepo.pushkey('bookmarks', k, '', hex(n))
381 399
382 400 return srcrepo, destrepo
383 401 finally:
384 402 release(srclock, destlock)
385 403 if dircleanup is not None:
386 404 dircleanup.cleanup()
387 405
388 406 def _showstats(repo, stats):
389 407 repo.ui.status(_("%d files updated, %d files merged, "
390 408 "%d files removed, %d files unresolved\n") % stats)
391 409
392 410 def update(repo, node):
393 411 """update the working directory to node, merging linear changes"""
394 412 stats = mergemod.update(repo, node, False, False, None)
395 413 _showstats(repo, stats)
396 414 if stats[3]:
397 415 repo.ui.status(_("use 'hg resolve' to retry unresolved file merges\n"))
398 416 return stats[3] > 0
399 417
400 418 # naming conflict in clone()
401 419 _update = update
402 420
403 421 def clean(repo, node, show_stats=True):
404 422 """forcibly switch the working directory to node, clobbering changes"""
405 423 stats = mergemod.update(repo, node, False, True, None)
406 424 if show_stats:
407 425 _showstats(repo, stats)
408 426 return stats[3] > 0
409 427
410 428 def merge(repo, node, force=None, remind=True):
411 429 """Branch merge with node, resolving changes. Return true if any
412 430 unresolved conflicts."""
413 431 stats = mergemod.update(repo, node, True, force, False)
414 432 _showstats(repo, stats)
415 433 if stats[3]:
416 434 repo.ui.status(_("use 'hg resolve' to retry unresolved file merges "
417 435 "or 'hg update -C .' to abandon\n"))
418 436 elif remind:
419 437 repo.ui.status(_("(branch merge, don't forget to commit)\n"))
420 438 return stats[3] > 0
421 439
422 440 def _incoming(displaychlist, subreporecurse, ui, repo, source,
423 441 opts, buffered=False):
424 442 """
425 443 Helper for incoming / gincoming.
426 444 displaychlist gets called with
427 445 (remoterepo, incomingchangesetlist, displayer) parameters,
428 446 and is supposed to contain only code that can't be unified.
429 447 """
430 448 source, branches = parseurl(ui.expandpath(source), opts.get('branch'))
431 449 other = peer(repo, opts, source)
432 450 ui.status(_('comparing with %s\n') % util.hidepassword(source))
433 451 revs, checkout = addbranchrevs(repo, other, branches, opts.get('rev'))
434 452
435 453 if revs:
436 454 revs = [other.lookup(rev) for rev in revs]
437 455 other, chlist, cleanupfn = bundlerepo.getremotechanges(ui, repo, other,
438 456 revs, opts["bundle"], opts["force"])
439 457 try:
440 458 if not chlist:
441 459 ui.status(_("no changes found\n"))
442 460 return subreporecurse()
443 461
444 462 displayer = cmdutil.show_changeset(ui, other, opts, buffered)
445 463
446 464 # XXX once graphlog extension makes it into core,
447 465 # should be replaced by a if graph/else
448 466 displaychlist(other, chlist, displayer)
449 467
450 468 displayer.close()
451 469 finally:
452 470 cleanupfn()
453 471 subreporecurse()
454 472 return 0 # exit code is zero since we found incoming changes
455 473
456 474 def incoming(ui, repo, source, opts):
457 475 def subreporecurse():
458 476 ret = 1
459 477 if opts.get('subrepos'):
460 478 ctx = repo[None]
461 479 for subpath in sorted(ctx.substate):
462 480 sub = ctx.sub(subpath)
463 481 ret = min(ret, sub.incoming(ui, source, opts))
464 482 return ret
465 483
466 484 def display(other, chlist, displayer):
467 485 limit = cmdutil.loglimit(opts)
468 486 if opts.get('newest_first'):
469 487 chlist.reverse()
470 488 count = 0
471 489 for n in chlist:
472 490 if limit is not None and count >= limit:
473 491 break
474 492 parents = [p for p in other.changelog.parents(n) if p != nullid]
475 493 if opts.get('no_merges') and len(parents) == 2:
476 494 continue
477 495 count += 1
478 496 displayer.show(other[n])
479 497 return _incoming(display, subreporecurse, ui, repo, source, opts)
480 498
481 499 def _outgoing(ui, repo, dest, opts):
482 500 dest = ui.expandpath(dest or 'default-push', dest or 'default')
483 501 dest, branches = parseurl(dest, opts.get('branch'))
484 502 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
485 503 revs, checkout = addbranchrevs(repo, repo, branches, opts.get('rev'))
486 504 if revs:
487 505 revs = [repo.lookup(rev) for rev in revs]
488 506
489 507 other = peer(repo, opts, dest)
490 508 common, outheads = discovery.findcommonoutgoing(repo, other, revs,
491 509 force=opts.get('force'))
492 510 o = repo.changelog.findmissing(common, outheads)
493 511 if not o:
494 512 ui.status(_("no changes found\n"))
495 513 return None
496 514 return o
497 515
498 516 def outgoing(ui, repo, dest, opts):
499 517 def recurse():
500 518 ret = 1
501 519 if opts.get('subrepos'):
502 520 ctx = repo[None]
503 521 for subpath in sorted(ctx.substate):
504 522 sub = ctx.sub(subpath)
505 523 ret = min(ret, sub.outgoing(ui, dest, opts))
506 524 return ret
507 525
508 526 limit = cmdutil.loglimit(opts)
509 527 o = _outgoing(ui, repo, dest, opts)
510 528 if o is None:
511 529 return recurse()
512 530
513 531 if opts.get('newest_first'):
514 532 o.reverse()
515 533 displayer = cmdutil.show_changeset(ui, repo, opts)
516 534 count = 0
517 535 for n in o:
518 536 if limit is not None and count >= limit:
519 537 break
520 538 parents = [p for p in repo.changelog.parents(n) if p != nullid]
521 539 if opts.get('no_merges') and len(parents) == 2:
522 540 continue
523 541 count += 1
524 542 displayer.show(repo[n])
525 543 displayer.close()
526 544 recurse()
527 545 return 0 # exit code is zero since we found outgoing changes
528 546
529 547 def revert(repo, node, choose):
530 548 """revert changes to revision in node without updating dirstate"""
531 549 return mergemod.update(repo, node, False, True, choose)[3] > 0
532 550
533 551 def verify(repo):
534 552 """verify the consistency of a repository"""
535 553 return verifymod.verify(repo)
536 554
537 555 def remoteui(src, opts):
538 556 'build a remote ui from ui or repo and opts'
539 557 if hasattr(src, 'baseui'): # looks like a repository
540 558 dst = src.baseui.copy() # drop repo-specific config
541 559 src = src.ui # copy target options from repo
542 560 else: # assume it's a global ui object
543 561 dst = src.copy() # keep all global options
544 562
545 563 # copy ssh-specific options
546 564 for o in 'ssh', 'remotecmd':
547 565 v = opts.get(o) or src.config('ui', o)
548 566 if v:
549 567 dst.setconfig("ui", o, v)
550 568
551 569 # copy bundle-specific options
552 570 r = src.config('bundle', 'mainreporoot')
553 571 if r:
554 572 dst.setconfig('bundle', 'mainreporoot', r)
555 573
556 574 # copy selected local settings to the remote ui
557 575 for sect in ('auth', 'hostfingerprints', 'http_proxy'):
558 576 for key, val in src.configitems(sect):
559 577 dst.setconfig(sect, key, val)
560 578 v = src.config('web', 'cacerts')
561 579 if v:
562 580 dst.setconfig('web', 'cacerts', util.expandpath(v))
563 581
564 582 return dst
General Comments 0
You need to be logged in to leave comments. Login now