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