##// END OF EJS Templates
hg: move return statement after finally block...
simon@laptop-tosh -
r19313:3b96d6e4 default
parent child Browse files
Show More
@@ -1,633 +1,632
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, unionrepo, httppeer, sshpeer, statichttprepo
13 13 import bookmarks, lock, util, extensions, error, node, scmutil, phases, url
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.urllocalpath(path))
21 21 return (os.path.isfile(path) and bundlerepo or localrepo)
22 22
23 23 def addbranchrevs(lrepo, other, branches, revs):
24 24 peer = other.peer() # a courtesy to callers using a localrepo for other
25 25 hashbranch, branches = branches
26 26 if not hashbranch and not branches:
27 27 return revs or None, revs and revs[0] or None
28 28 revs = revs and list(revs) or []
29 29 if not peer.capable('branchmap'):
30 30 if branches:
31 31 raise util.Abort(_("remote branch lookup not supported"))
32 32 revs.append(hashbranch)
33 33 return revs, revs[0]
34 34 branchmap = peer.branchmap()
35 35
36 36 def primary(branch):
37 37 if branch == '.':
38 38 if not lrepo:
39 39 raise util.Abort(_("dirstate branch not accessible"))
40 40 branch = lrepo.dirstate.branch()
41 41 if branch in branchmap:
42 42 revs.extend(node.hex(r) for r in reversed(branchmap[branch]))
43 43 return True
44 44 else:
45 45 return False
46 46
47 47 for branch in branches:
48 48 if not primary(branch):
49 49 raise error.RepoLookupError(_("unknown branch '%s'") % branch)
50 50 if hashbranch:
51 51 if not primary(hashbranch):
52 52 revs.append(hashbranch)
53 53 return revs, revs[0]
54 54
55 55 def parseurl(path, branches=None):
56 56 '''parse url#branch, returning (url, (branch, branches))'''
57 57
58 58 u = util.url(path)
59 59 branch = None
60 60 if u.fragment:
61 61 branch = u.fragment
62 62 u.fragment = None
63 63 return str(u), (branch, branches or [])
64 64
65 65 schemes = {
66 66 'bundle': bundlerepo,
67 67 'union': unionrepo,
68 68 'file': _local,
69 69 'http': httppeer,
70 70 'https': httppeer,
71 71 'ssh': sshpeer,
72 72 'static-http': statichttprepo,
73 73 }
74 74
75 75 def _peerlookup(path):
76 76 u = util.url(path)
77 77 scheme = u.scheme or 'file'
78 78 thing = schemes.get(scheme) or schemes['file']
79 79 try:
80 80 return thing(path)
81 81 except TypeError:
82 82 return thing
83 83
84 84 def islocal(repo):
85 85 '''return true if repo or path is local'''
86 86 if isinstance(repo, str):
87 87 try:
88 88 return _peerlookup(repo).islocal(repo)
89 89 except AttributeError:
90 90 return False
91 91 return repo.local()
92 92
93 93 def openpath(ui, path):
94 94 '''open path with open if local, url.open if remote'''
95 95 if islocal(path):
96 96 return util.posixfile(util.urllocalpath(path), 'rb')
97 97 else:
98 98 return url.open(ui, path)
99 99
100 100 def _peerorrepo(ui, path, create=False):
101 101 """return a repository object for the specified path"""
102 102 obj = _peerlookup(path).instance(ui, path, create)
103 103 ui = getattr(obj, "ui", ui)
104 104 for name, module in extensions.extensions():
105 105 hook = getattr(module, 'reposetup', None)
106 106 if hook:
107 107 hook(ui, obj)
108 108 return obj
109 109
110 110 def repository(ui, path='', create=False):
111 111 """return a repository object for the specified path"""
112 112 peer = _peerorrepo(ui, path, create)
113 113 repo = peer.local()
114 114 if not repo:
115 115 raise util.Abort(_("repository '%s' is not local") %
116 116 (path or peer.url()))
117 117 return repo.filtered('visible')
118 118
119 119 def peer(uiorrepo, opts, path, create=False):
120 120 '''return a repository peer for the specified path'''
121 121 rui = remoteui(uiorrepo, opts)
122 122 return _peerorrepo(rui, path, create).peer()
123 123
124 124 def defaultdest(source):
125 125 '''return default destination of clone if none is given'''
126 126 return os.path.basename(os.path.normpath(util.url(source).path or ''))
127 127
128 128 def share(ui, source, dest=None, update=True):
129 129 '''create a shared repository'''
130 130
131 131 if not islocal(source):
132 132 raise util.Abort(_('can only share local repositories'))
133 133
134 134 if not dest:
135 135 dest = defaultdest(source)
136 136 else:
137 137 dest = ui.expandpath(dest)
138 138
139 139 if isinstance(source, str):
140 140 origsource = ui.expandpath(source)
141 141 source, branches = parseurl(origsource)
142 142 srcrepo = repository(ui, source)
143 143 rev, checkout = addbranchrevs(srcrepo, srcrepo, branches, None)
144 144 else:
145 145 srcrepo = source.local()
146 146 origsource = source = srcrepo.url()
147 147 checkout = None
148 148
149 149 sharedpath = srcrepo.sharedpath # if our source is already sharing
150 150
151 151 root = os.path.realpath(dest)
152 152 roothg = os.path.join(root, '.hg')
153 153
154 154 if os.path.exists(roothg):
155 155 raise util.Abort(_('destination already exists'))
156 156
157 157 if not os.path.isdir(root):
158 158 os.mkdir(root)
159 159 util.makedir(roothg, notindexed=True)
160 160
161 161 requirements = ''
162 162 try:
163 163 requirements = srcrepo.opener.read('requires')
164 164 except IOError, inst:
165 165 if inst.errno != errno.ENOENT:
166 166 raise
167 167
168 168 requirements += 'shared\n'
169 169 util.writefile(os.path.join(roothg, 'requires'), requirements)
170 170 util.writefile(os.path.join(roothg, 'sharedpath'), sharedpath)
171 171
172 172 r = repository(ui, root)
173 173
174 174 default = srcrepo.ui.config('paths', 'default')
175 175 if default:
176 176 fp = r.opener("hgrc", "w", text=True)
177 177 fp.write("[paths]\n")
178 178 fp.write("default = %s\n" % default)
179 179 fp.close()
180 180
181 181 if update:
182 182 r.ui.status(_("updating working directory\n"))
183 183 if update is not True:
184 184 checkout = update
185 185 for test in (checkout, 'default', 'tip'):
186 186 if test is None:
187 187 continue
188 188 try:
189 189 uprev = r.lookup(test)
190 190 break
191 191 except error.RepoLookupError:
192 192 continue
193 193 _update(r, uprev)
194 194
195 195 def copystore(ui, srcrepo, destpath):
196 196 '''copy files from store of srcrepo in destpath
197 197
198 198 returns destlock
199 199 '''
200 200 destlock = None
201 201 try:
202 202 hardlink = None
203 203 num = 0
204 204 srcpublishing = srcrepo.ui.configbool('phases', 'publish', True)
205 205 for f in srcrepo.store.copylist():
206 206 if srcpublishing and f.endswith('phaseroots'):
207 207 continue
208 208 src = os.path.join(srcrepo.sharedpath, f)
209 209 dst = os.path.join(destpath, f)
210 210 dstbase = os.path.dirname(dst)
211 211 if dstbase and not os.path.exists(dstbase):
212 212 os.mkdir(dstbase)
213 213 if os.path.exists(src):
214 214 if dst.endswith('data'):
215 215 # lock to avoid premature writing to the target
216 216 destlock = lock.lock(os.path.join(dstbase, "lock"))
217 217 hardlink, n = util.copyfiles(src, dst, hardlink)
218 218 num += n
219 219 if hardlink:
220 220 ui.debug("linked %d files\n" % num)
221 221 else:
222 222 ui.debug("copied %d files\n" % num)
223 223 return destlock
224 224 except: # re-raises
225 225 release(destlock)
226 226 raise
227 227
228 228 def clone(ui, peeropts, source, dest=None, pull=False, rev=None,
229 229 update=True, stream=False, branch=None):
230 230 """Make a copy of an existing repository.
231 231
232 232 Create a copy of an existing repository in a new directory. The
233 233 source and destination are URLs, as passed to the repository
234 234 function. Returns a pair of repository peers, the source and
235 235 newly created destination.
236 236
237 237 The location of the source is added to the new repository's
238 238 .hg/hgrc file, as the default to be used for future pulls and
239 239 pushes.
240 240
241 241 If an exception is raised, the partly cloned/updated destination
242 242 repository will be deleted.
243 243
244 244 Arguments:
245 245
246 246 source: repository object or URL
247 247
248 248 dest: URL of destination repository to create (defaults to base
249 249 name of source repository)
250 250
251 251 pull: always pull from source repository, even in local case
252 252
253 253 stream: stream raw data uncompressed from repository (fast over
254 254 LAN, slow over WAN)
255 255
256 256 rev: revision to clone up to (implies pull=True)
257 257
258 258 update: update working directory after clone completes, if
259 259 destination is local repository (True means update to default rev,
260 260 anything else is treated as a revision)
261 261
262 262 branch: branches to clone
263 263 """
264 264
265 265 if isinstance(source, str):
266 266 origsource = ui.expandpath(source)
267 267 source, branch = parseurl(origsource, branch)
268 268 srcpeer = peer(ui, peeropts, source)
269 269 else:
270 270 srcpeer = source.peer() # in case we were called with a localrepo
271 271 branch = (None, branch or [])
272 272 origsource = source = srcpeer.url()
273 273 rev, checkout = addbranchrevs(srcpeer, srcpeer, branch, rev)
274 274
275 275 if dest is None:
276 276 dest = defaultdest(source)
277 277 ui.status(_("destination directory: %s\n") % dest)
278 278 else:
279 279 dest = ui.expandpath(dest)
280 280
281 281 dest = util.urllocalpath(dest)
282 282 source = util.urllocalpath(source)
283 283
284 284 if not dest:
285 285 raise util.Abort(_("empty destination path is not valid"))
286 286 if os.path.exists(dest):
287 287 if not os.path.isdir(dest):
288 288 raise util.Abort(_("destination '%s' already exists") % dest)
289 289 elif os.listdir(dest):
290 290 raise util.Abort(_("destination '%s' is not empty") % dest)
291 291
292 292 srclock = destlock = cleandir = None
293 293 srcrepo = srcpeer.local()
294 294 try:
295 295 abspath = origsource
296 296 if islocal(origsource):
297 297 abspath = os.path.abspath(util.urllocalpath(origsource))
298 298
299 299 if islocal(dest):
300 300 cleandir = dest
301 301
302 302 copy = False
303 303 if (srcrepo and srcrepo.cancopy() and islocal(dest)
304 304 and not phases.hassecret(srcrepo)):
305 305 copy = not pull and not rev
306 306
307 307 if copy:
308 308 try:
309 309 # we use a lock here because if we race with commit, we
310 310 # can end up with extra data in the cloned revlogs that's
311 311 # not pointed to by changesets, thus causing verify to
312 312 # fail
313 313 srclock = srcrepo.lock(wait=False)
314 314 except error.LockError:
315 315 copy = False
316 316
317 317 if copy:
318 318 srcrepo.hook('preoutgoing', throw=True, source='clone')
319 319 hgdir = os.path.realpath(os.path.join(dest, ".hg"))
320 320 if not os.path.exists(dest):
321 321 os.mkdir(dest)
322 322 else:
323 323 # only clean up directories we create ourselves
324 324 cleandir = hgdir
325 325 try:
326 326 destpath = hgdir
327 327 util.makedir(destpath, notindexed=True)
328 328 except OSError, inst:
329 329 if inst.errno == errno.EEXIST:
330 330 cleandir = None
331 331 raise util.Abort(_("destination '%s' already exists")
332 332 % dest)
333 333 raise
334 334
335 335 destlock = copystore(ui, srcrepo, destpath)
336 336
337 337 # Recomputing branch cache might be slow on big repos,
338 338 # so just copy it
339 339 dstcachedir = os.path.join(destpath, 'cache')
340 340 srcbranchcache = srcrepo.sjoin('cache/branchheads')
341 341 dstbranchcache = os.path.join(dstcachedir, 'branchheads')
342 342 if os.path.exists(srcbranchcache):
343 343 if not os.path.exists(dstcachedir):
344 344 os.mkdir(dstcachedir)
345 345 util.copyfile(srcbranchcache, dstbranchcache)
346 346
347 347 # we need to re-init the repo after manually copying the data
348 348 # into it
349 349 destpeer = peer(srcrepo, peeropts, dest)
350 350 srcrepo.hook('outgoing', source='clone',
351 351 node=node.hex(node.nullid))
352 352 else:
353 353 try:
354 354 destpeer = peer(srcrepo or ui, peeropts, dest, create=True)
355 355 # only pass ui when no srcrepo
356 356 except OSError, inst:
357 357 if inst.errno == errno.EEXIST:
358 358 cleandir = None
359 359 raise util.Abort(_("destination '%s' already exists")
360 360 % dest)
361 361 raise
362 362
363 363 revs = None
364 364 if rev:
365 365 if not srcpeer.capable('lookup'):
366 366 raise util.Abort(_("src repository does not support "
367 367 "revision lookup and so doesn't "
368 368 "support clone by revision"))
369 369 revs = [srcpeer.lookup(r) for r in rev]
370 370 checkout = revs[0]
371 371 if destpeer.local():
372 372 destpeer.local().clone(srcpeer, heads=revs, stream=stream)
373 373 elif srcrepo:
374 374 srcrepo.push(destpeer, revs=revs)
375 375 else:
376 376 raise util.Abort(_("clone from remote to remote not supported"))
377 377
378 378 cleandir = None
379 379
380 380 # clone all bookmarks except divergent ones
381 381 destrepo = destpeer.local()
382 382 if destrepo and srcpeer.capable("pushkey"):
383 383 rb = srcpeer.listkeys('bookmarks')
384 384 marks = destrepo._bookmarks
385 385 for k, n in rb.iteritems():
386 386 try:
387 387 m = destrepo.lookup(n)
388 388 marks[k] = m
389 389 except error.RepoLookupError:
390 390 pass
391 391 if rb:
392 392 marks.write()
393 393 elif srcrepo and destpeer.capable("pushkey"):
394 394 for k, n in srcrepo._bookmarks.iteritems():
395 395 destpeer.pushkey('bookmarks', k, '', hex(n))
396 396
397 397 if destrepo:
398 398 fp = destrepo.opener("hgrc", "w", text=True)
399 399 fp.write("[paths]\n")
400 400 u = util.url(abspath)
401 401 u.passwd = None
402 402 defaulturl = str(u)
403 403 fp.write("default = %s\n" % defaulturl)
404 404 fp.close()
405 405
406 406 destrepo.ui.setconfig('paths', 'default', defaulturl)
407 407
408 408 if update:
409 409 if update is not True:
410 410 checkout = srcpeer.lookup(update)
411 411 uprev = None
412 412 status = None
413 413 if checkout is not None:
414 414 try:
415 415 uprev = destrepo.lookup(checkout)
416 416 except error.RepoLookupError:
417 417 pass
418 418 if uprev is None:
419 419 try:
420 420 uprev = destrepo._bookmarks['@']
421 421 update = '@'
422 422 bn = destrepo[uprev].branch()
423 423 if bn == 'default':
424 424 status = _("updating to bookmark @\n")
425 425 else:
426 426 status = _("updating to bookmark @ on branch %s\n"
427 427 % bn)
428 428 except KeyError:
429 429 try:
430 430 uprev = destrepo.branchtip('default')
431 431 except error.RepoLookupError:
432 432 uprev = destrepo.lookup('tip')
433 433 if not status:
434 434 bn = destrepo[uprev].branch()
435 435 status = _("updating to branch %s\n") % bn
436 436 destrepo.ui.status(status)
437 437 _update(destrepo, uprev)
438 438 if update in destrepo._bookmarks:
439 439 bookmarks.setcurrent(destrepo, update)
440
441 return srcpeer, destpeer
442 440 finally:
443 441 release(srclock, destlock)
444 442 if cleandir is not None:
445 443 shutil.rmtree(cleandir, True)
446 444 if srcpeer is not None:
447 445 srcpeer.close()
446 return srcpeer, destpeer
448 447
449 448 def _showstats(repo, stats):
450 449 repo.ui.status(_("%d files updated, %d files merged, "
451 450 "%d files removed, %d files unresolved\n") % stats)
452 451
453 452 def updaterepo(repo, node, overwrite):
454 453 """Update the working directory to node.
455 454
456 455 When overwrite is set, changes are clobbered, merged else
457 456
458 457 returns stats (see pydoc mercurial.merge.applyupdates)"""
459 458 return mergemod.update(repo, node, False, overwrite, None)
460 459
461 460 def update(repo, node):
462 461 """update the working directory to node, merging linear changes"""
463 462 stats = updaterepo(repo, node, False)
464 463 _showstats(repo, stats)
465 464 if stats[3]:
466 465 repo.ui.status(_("use 'hg resolve' to retry unresolved file merges\n"))
467 466 return stats[3] > 0
468 467
469 468 # naming conflict in clone()
470 469 _update = update
471 470
472 471 def clean(repo, node, show_stats=True):
473 472 """forcibly switch the working directory to node, clobbering changes"""
474 473 stats = updaterepo(repo, node, True)
475 474 if show_stats:
476 475 _showstats(repo, stats)
477 476 return stats[3] > 0
478 477
479 478 def merge(repo, node, force=None, remind=True):
480 479 """Branch merge with node, resolving changes. Return true if any
481 480 unresolved conflicts."""
482 481 stats = mergemod.update(repo, node, True, force, False)
483 482 _showstats(repo, stats)
484 483 if stats[3]:
485 484 repo.ui.status(_("use 'hg resolve' to retry unresolved file merges "
486 485 "or 'hg update -C .' to abandon\n"))
487 486 elif remind:
488 487 repo.ui.status(_("(branch merge, don't forget to commit)\n"))
489 488 return stats[3] > 0
490 489
491 490 def _incoming(displaychlist, subreporecurse, ui, repo, source,
492 491 opts, buffered=False):
493 492 """
494 493 Helper for incoming / gincoming.
495 494 displaychlist gets called with
496 495 (remoterepo, incomingchangesetlist, displayer) parameters,
497 496 and is supposed to contain only code that can't be unified.
498 497 """
499 498 source, branches = parseurl(ui.expandpath(source), opts.get('branch'))
500 499 other = peer(repo, opts, source)
501 500 ui.status(_('comparing with %s\n') % util.hidepassword(source))
502 501 revs, checkout = addbranchrevs(repo, other, branches, opts.get('rev'))
503 502
504 503 if revs:
505 504 revs = [other.lookup(rev) for rev in revs]
506 505 other, chlist, cleanupfn = bundlerepo.getremotechanges(ui, repo, other,
507 506 revs, opts["bundle"], opts["force"])
508 507 try:
509 508 if not chlist:
510 509 ui.status(_("no changes found\n"))
511 510 return subreporecurse()
512 511
513 512 displayer = cmdutil.show_changeset(ui, other, opts, buffered)
514 513
515 514 # XXX once graphlog extension makes it into core,
516 515 # should be replaced by a if graph/else
517 516 displaychlist(other, chlist, displayer)
518 517
519 518 displayer.close()
520 519 finally:
521 520 cleanupfn()
522 521 subreporecurse()
523 522 return 0 # exit code is zero since we found incoming changes
524 523
525 524 def incoming(ui, repo, source, opts):
526 525 def subreporecurse():
527 526 ret = 1
528 527 if opts.get('subrepos'):
529 528 ctx = repo[None]
530 529 for subpath in sorted(ctx.substate):
531 530 sub = ctx.sub(subpath)
532 531 ret = min(ret, sub.incoming(ui, source, opts))
533 532 return ret
534 533
535 534 def display(other, chlist, displayer):
536 535 limit = cmdutil.loglimit(opts)
537 536 if opts.get('newest_first'):
538 537 chlist.reverse()
539 538 count = 0
540 539 for n in chlist:
541 540 if limit is not None and count >= limit:
542 541 break
543 542 parents = [p for p in other.changelog.parents(n) if p != nullid]
544 543 if opts.get('no_merges') and len(parents) == 2:
545 544 continue
546 545 count += 1
547 546 displayer.show(other[n])
548 547 return _incoming(display, subreporecurse, ui, repo, source, opts)
549 548
550 549 def _outgoing(ui, repo, dest, opts):
551 550 dest = ui.expandpath(dest or 'default-push', dest or 'default')
552 551 dest, branches = parseurl(dest, opts.get('branch'))
553 552 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
554 553 revs, checkout = addbranchrevs(repo, repo, branches, opts.get('rev'))
555 554 if revs:
556 555 revs = [repo.lookup(rev) for rev in scmutil.revrange(repo, revs)]
557 556
558 557 other = peer(repo, opts, dest)
559 558 outgoing = discovery.findcommonoutgoing(repo.unfiltered(), other, revs,
560 559 force=opts.get('force'))
561 560 o = outgoing.missing
562 561 if not o:
563 562 scmutil.nochangesfound(repo.ui, repo, outgoing.excluded)
564 563 return None
565 564 return o
566 565
567 566 def outgoing(ui, repo, dest, opts):
568 567 def recurse():
569 568 ret = 1
570 569 if opts.get('subrepos'):
571 570 ctx = repo[None]
572 571 for subpath in sorted(ctx.substate):
573 572 sub = ctx.sub(subpath)
574 573 ret = min(ret, sub.outgoing(ui, dest, opts))
575 574 return ret
576 575
577 576 limit = cmdutil.loglimit(opts)
578 577 o = _outgoing(ui, repo, dest, opts)
579 578 if o is None:
580 579 return recurse()
581 580
582 581 if opts.get('newest_first'):
583 582 o.reverse()
584 583 displayer = cmdutil.show_changeset(ui, repo, opts)
585 584 count = 0
586 585 for n in o:
587 586 if limit is not None and count >= limit:
588 587 break
589 588 parents = [p for p in repo.changelog.parents(n) if p != nullid]
590 589 if opts.get('no_merges') and len(parents) == 2:
591 590 continue
592 591 count += 1
593 592 displayer.show(repo[n])
594 593 displayer.close()
595 594 recurse()
596 595 return 0 # exit code is zero since we found outgoing changes
597 596
598 597 def revert(repo, node, choose):
599 598 """revert changes to revision in node without updating dirstate"""
600 599 return mergemod.update(repo, node, False, True, choose)[3] > 0
601 600
602 601 def verify(repo):
603 602 """verify the consistency of a repository"""
604 603 return verifymod.verify(repo)
605 604
606 605 def remoteui(src, opts):
607 606 'build a remote ui from ui or repo and opts'
608 607 if util.safehasattr(src, 'baseui'): # looks like a repository
609 608 dst = src.baseui.copy() # drop repo-specific config
610 609 src = src.ui # copy target options from repo
611 610 else: # assume it's a global ui object
612 611 dst = src.copy() # keep all global options
613 612
614 613 # copy ssh-specific options
615 614 for o in 'ssh', 'remotecmd':
616 615 v = opts.get(o) or src.config('ui', o)
617 616 if v:
618 617 dst.setconfig("ui", o, v)
619 618
620 619 # copy bundle-specific options
621 620 r = src.config('bundle', 'mainreporoot')
622 621 if r:
623 622 dst.setconfig('bundle', 'mainreporoot', r)
624 623
625 624 # copy selected local settings to the remote ui
626 625 for sect in ('auth', 'hostfingerprints', 'http_proxy'):
627 626 for key, val in src.configitems(sect):
628 627 dst.setconfig(sect, key, val)
629 628 v = src.config('web', 'cacerts')
630 629 if v:
631 630 dst.setconfig('web', 'cacerts', util.expandpath(v))
632 631
633 632 return dst
General Comments 0
You need to be logged in to leave comments. Login now