##// END OF EJS Templates
clone: get rid of ui.backupconfig
Jun Wu -
r31456:2545ee88 default
parent child Browse files
Show More
@@ -1,1056 +1,1052
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 __future__ import absolute_import
10 10
11 11 import errno
12 12 import hashlib
13 13 import os
14 14 import shutil
15 15
16 16 from .i18n import _
17 17 from .node import nullid
18 18
19 19 from . import (
20 20 bookmarks,
21 21 bundlerepo,
22 22 cmdutil,
23 23 destutil,
24 24 discovery,
25 25 error,
26 26 exchange,
27 27 extensions,
28 28 httppeer,
29 29 localrepo,
30 30 lock,
31 31 merge as mergemod,
32 32 node,
33 33 phases,
34 34 repoview,
35 35 scmutil,
36 36 sshpeer,
37 37 statichttprepo,
38 38 ui as uimod,
39 39 unionrepo,
40 40 url,
41 41 util,
42 42 verify as verifymod,
43 43 vfs as vfsmod,
44 44 )
45 45
46 46 release = lock.release
47 47
48 48 # shared features
49 49 sharedbookmarks = 'bookmarks'
50 50
51 51 def _local(path):
52 52 path = util.expandpath(util.urllocalpath(path))
53 53 return (os.path.isfile(path) and bundlerepo or localrepo)
54 54
55 55 def addbranchrevs(lrepo, other, branches, revs):
56 56 peer = other.peer() # a courtesy to callers using a localrepo for other
57 57 hashbranch, branches = branches
58 58 if not hashbranch and not branches:
59 59 x = revs or None
60 60 if util.safehasattr(revs, 'first'):
61 61 y = revs.first()
62 62 elif revs:
63 63 y = revs[0]
64 64 else:
65 65 y = None
66 66 return x, y
67 67 if revs:
68 68 revs = list(revs)
69 69 else:
70 70 revs = []
71 71
72 72 if not peer.capable('branchmap'):
73 73 if branches:
74 74 raise error.Abort(_("remote branch lookup not supported"))
75 75 revs.append(hashbranch)
76 76 return revs, revs[0]
77 77 branchmap = peer.branchmap()
78 78
79 79 def primary(branch):
80 80 if branch == '.':
81 81 if not lrepo:
82 82 raise error.Abort(_("dirstate branch not accessible"))
83 83 branch = lrepo.dirstate.branch()
84 84 if branch in branchmap:
85 85 revs.extend(node.hex(r) for r in reversed(branchmap[branch]))
86 86 return True
87 87 else:
88 88 return False
89 89
90 90 for branch in branches:
91 91 if not primary(branch):
92 92 raise error.RepoLookupError(_("unknown branch '%s'") % branch)
93 93 if hashbranch:
94 94 if not primary(hashbranch):
95 95 revs.append(hashbranch)
96 96 return revs, revs[0]
97 97
98 98 def parseurl(path, branches=None):
99 99 '''parse url#branch, returning (url, (branch, branches))'''
100 100
101 101 u = util.url(path)
102 102 branch = None
103 103 if u.fragment:
104 104 branch = u.fragment
105 105 u.fragment = None
106 106 return str(u), (branch, branches or [])
107 107
108 108 schemes = {
109 109 'bundle': bundlerepo,
110 110 'union': unionrepo,
111 111 'file': _local,
112 112 'http': httppeer,
113 113 'https': httppeer,
114 114 'ssh': sshpeer,
115 115 'static-http': statichttprepo,
116 116 }
117 117
118 118 def _peerlookup(path):
119 119 u = util.url(path)
120 120 scheme = u.scheme or 'file'
121 121 thing = schemes.get(scheme) or schemes['file']
122 122 try:
123 123 return thing(path)
124 124 except TypeError:
125 125 # we can't test callable(thing) because 'thing' can be an unloaded
126 126 # module that implements __call__
127 127 if not util.safehasattr(thing, 'instance'):
128 128 raise
129 129 return thing
130 130
131 131 def islocal(repo):
132 132 '''return true if repo (or path pointing to repo) is local'''
133 133 if isinstance(repo, str):
134 134 try:
135 135 return _peerlookup(repo).islocal(repo)
136 136 except AttributeError:
137 137 return False
138 138 return repo.local()
139 139
140 140 def openpath(ui, path):
141 141 '''open path with open if local, url.open if remote'''
142 142 pathurl = util.url(path, parsequery=False, parsefragment=False)
143 143 if pathurl.islocal():
144 144 return util.posixfile(pathurl.localpath(), 'rb')
145 145 else:
146 146 return url.open(ui, path)
147 147
148 148 # a list of (ui, repo) functions called for wire peer initialization
149 149 wirepeersetupfuncs = []
150 150
151 151 def _peerorrepo(ui, path, create=False):
152 152 """return a repository object for the specified path"""
153 153 obj = _peerlookup(path).instance(ui, path, create)
154 154 ui = getattr(obj, "ui", ui)
155 155 for name, module in extensions.extensions(ui):
156 156 hook = getattr(module, 'reposetup', None)
157 157 if hook:
158 158 hook(ui, obj)
159 159 if not obj.local():
160 160 for f in wirepeersetupfuncs:
161 161 f(ui, obj)
162 162 return obj
163 163
164 164 def repository(ui, path='', create=False):
165 165 """return a repository object for the specified path"""
166 166 peer = _peerorrepo(ui, path, create)
167 167 repo = peer.local()
168 168 if not repo:
169 169 raise error.Abort(_("repository '%s' is not local") %
170 170 (path or peer.url()))
171 171 return repo.filtered('visible')
172 172
173 173 def peer(uiorrepo, opts, path, create=False):
174 174 '''return a repository peer for the specified path'''
175 175 rui = remoteui(uiorrepo, opts)
176 176 return _peerorrepo(rui, path, create).peer()
177 177
178 178 def defaultdest(source):
179 179 '''return default destination of clone if none is given
180 180
181 181 >>> defaultdest('foo')
182 182 'foo'
183 183 >>> defaultdest('/foo/bar')
184 184 'bar'
185 185 >>> defaultdest('/')
186 186 ''
187 187 >>> defaultdest('')
188 188 ''
189 189 >>> defaultdest('http://example.org/')
190 190 ''
191 191 >>> defaultdest('http://example.org/foo/')
192 192 'foo'
193 193 '''
194 194 path = util.url(source).path
195 195 if not path:
196 196 return ''
197 197 return os.path.basename(os.path.normpath(path))
198 198
199 199 def share(ui, source, dest=None, update=True, bookmarks=True, defaultpath=None,
200 200 relative=False):
201 201 '''create a shared repository'''
202 202
203 203 if not islocal(source):
204 204 raise error.Abort(_('can only share local repositories'))
205 205
206 206 if not dest:
207 207 dest = defaultdest(source)
208 208 else:
209 209 dest = ui.expandpath(dest)
210 210
211 211 if isinstance(source, str):
212 212 origsource = ui.expandpath(source)
213 213 source, branches = parseurl(origsource)
214 214 srcrepo = repository(ui, source)
215 215 rev, checkout = addbranchrevs(srcrepo, srcrepo, branches, None)
216 216 else:
217 217 srcrepo = source.local()
218 218 origsource = source = srcrepo.url()
219 219 checkout = None
220 220
221 221 sharedpath = srcrepo.sharedpath # if our source is already sharing
222 222
223 223 destwvfs = vfsmod.vfs(dest, realpath=True)
224 224 destvfs = vfsmod.vfs(os.path.join(destwvfs.base, '.hg'), realpath=True)
225 225
226 226 if destvfs.lexists():
227 227 raise error.Abort(_('destination already exists'))
228 228
229 229 if not destwvfs.isdir():
230 230 destwvfs.mkdir()
231 231 destvfs.makedir()
232 232
233 233 requirements = ''
234 234 try:
235 235 requirements = srcrepo.vfs.read('requires')
236 236 except IOError as inst:
237 237 if inst.errno != errno.ENOENT:
238 238 raise
239 239
240 240 if relative:
241 241 try:
242 242 sharedpath = os.path.relpath(sharedpath, destvfs.base)
243 243 requirements += 'relshared\n'
244 244 except IOError as e:
245 245 raise error.Abort(_('cannot calculate relative path'),
246 246 hint=str(e))
247 247 else:
248 248 requirements += 'shared\n'
249 249
250 250 destvfs.write('requires', requirements)
251 251 destvfs.write('sharedpath', sharedpath)
252 252
253 253 r = repository(ui, destwvfs.base)
254 254 postshare(srcrepo, r, bookmarks=bookmarks, defaultpath=defaultpath)
255 255 _postshareupdate(r, update, checkout=checkout)
256 256
257 257 def postshare(sourcerepo, destrepo, bookmarks=True, defaultpath=None):
258 258 """Called after a new shared repo is created.
259 259
260 260 The new repo only has a requirements file and pointer to the source.
261 261 This function configures additional shared data.
262 262
263 263 Extensions can wrap this function and write additional entries to
264 264 destrepo/.hg/shared to indicate additional pieces of data to be shared.
265 265 """
266 266 default = defaultpath or sourcerepo.ui.config('paths', 'default')
267 267 if default:
268 268 fp = destrepo.vfs("hgrc", "w", text=True)
269 269 fp.write("[paths]\n")
270 270 fp.write("default = %s\n" % default)
271 271 fp.close()
272 272
273 273 with destrepo.wlock():
274 274 if bookmarks:
275 275 fp = destrepo.vfs('shared', 'w')
276 276 fp.write(sharedbookmarks + '\n')
277 277 fp.close()
278 278
279 279 def _postshareupdate(repo, update, checkout=None):
280 280 """Maybe perform a working directory update after a shared repo is created.
281 281
282 282 ``update`` can be a boolean or a revision to update to.
283 283 """
284 284 if not update:
285 285 return
286 286
287 287 repo.ui.status(_("updating working directory\n"))
288 288 if update is not True:
289 289 checkout = update
290 290 for test in (checkout, 'default', 'tip'):
291 291 if test is None:
292 292 continue
293 293 try:
294 294 uprev = repo.lookup(test)
295 295 break
296 296 except error.RepoLookupError:
297 297 continue
298 298 _update(repo, uprev)
299 299
300 300 def copystore(ui, srcrepo, destpath):
301 301 '''copy files from store of srcrepo in destpath
302 302
303 303 returns destlock
304 304 '''
305 305 destlock = None
306 306 try:
307 307 hardlink = None
308 308 num = 0
309 309 closetopic = [None]
310 310 def prog(topic, pos):
311 311 if pos is None:
312 312 closetopic[0] = topic
313 313 else:
314 314 ui.progress(topic, pos + num)
315 315 srcpublishing = srcrepo.publishing()
316 316 srcvfs = vfsmod.vfs(srcrepo.sharedpath)
317 317 dstvfs = vfsmod.vfs(destpath)
318 318 for f in srcrepo.store.copylist():
319 319 if srcpublishing and f.endswith('phaseroots'):
320 320 continue
321 321 dstbase = os.path.dirname(f)
322 322 if dstbase and not dstvfs.exists(dstbase):
323 323 dstvfs.mkdir(dstbase)
324 324 if srcvfs.exists(f):
325 325 if f.endswith('data'):
326 326 # 'dstbase' may be empty (e.g. revlog format 0)
327 327 lockfile = os.path.join(dstbase, "lock")
328 328 # lock to avoid premature writing to the target
329 329 destlock = lock.lock(dstvfs, lockfile)
330 330 hardlink, n = util.copyfiles(srcvfs.join(f), dstvfs.join(f),
331 331 hardlink, progress=prog)
332 332 num += n
333 333 if hardlink:
334 334 ui.debug("linked %d files\n" % num)
335 335 if closetopic[0]:
336 336 ui.progress(closetopic[0], None)
337 337 else:
338 338 ui.debug("copied %d files\n" % num)
339 339 if closetopic[0]:
340 340 ui.progress(closetopic[0], None)
341 341 return destlock
342 342 except: # re-raises
343 343 release(destlock)
344 344 raise
345 345
346 346 def clonewithshare(ui, peeropts, sharepath, source, srcpeer, dest, pull=False,
347 347 rev=None, update=True, stream=False):
348 348 """Perform a clone using a shared repo.
349 349
350 350 The store for the repository will be located at <sharepath>/.hg. The
351 351 specified revisions will be cloned or pulled from "source". A shared repo
352 352 will be created at "dest" and a working copy will be created if "update" is
353 353 True.
354 354 """
355 355 revs = None
356 356 if rev:
357 357 if not srcpeer.capable('lookup'):
358 358 raise error.Abort(_("src repository does not support "
359 359 "revision lookup and so doesn't "
360 360 "support clone by revision"))
361 361 revs = [srcpeer.lookup(r) for r in rev]
362 362
363 363 # Obtain a lock before checking for or cloning the pooled repo otherwise
364 364 # 2 clients may race creating or populating it.
365 365 pooldir = os.path.dirname(sharepath)
366 366 # lock class requires the directory to exist.
367 367 try:
368 368 util.makedir(pooldir, False)
369 369 except OSError as e:
370 370 if e.errno != errno.EEXIST:
371 371 raise
372 372
373 373 poolvfs = vfsmod.vfs(pooldir)
374 374 basename = os.path.basename(sharepath)
375 375
376 376 with lock.lock(poolvfs, '%s.lock' % basename):
377 377 if os.path.exists(sharepath):
378 378 ui.status(_('(sharing from existing pooled repository %s)\n') %
379 379 basename)
380 380 else:
381 381 ui.status(_('(sharing from new pooled repository %s)\n') % basename)
382 382 # Always use pull mode because hardlinks in share mode don't work
383 383 # well. Never update because working copies aren't necessary in
384 384 # share mode.
385 385 clone(ui, peeropts, source, dest=sharepath, pull=True,
386 386 rev=rev, update=False, stream=stream)
387 387
388 388 # Resolve the value to put in [paths] section for the source.
389 389 if islocal(source):
390 390 defaultpath = os.path.abspath(util.urllocalpath(source))
391 391 else:
392 392 defaultpath = source
393 393
394 394 sharerepo = repository(ui, path=sharepath)
395 395 share(ui, sharerepo, dest=dest, update=False, bookmarks=False,
396 396 defaultpath=defaultpath)
397 397
398 398 # We need to perform a pull against the dest repo to fetch bookmarks
399 399 # and other non-store data that isn't shared by default. In the case of
400 400 # non-existing shared repo, this means we pull from the remote twice. This
401 401 # is a bit weird. But at the time it was implemented, there wasn't an easy
402 402 # way to pull just non-changegroup data.
403 403 destrepo = repository(ui, path=dest)
404 404 exchange.pull(destrepo, srcpeer, heads=revs)
405 405
406 406 _postshareupdate(destrepo, update)
407 407
408 408 return srcpeer, peer(ui, peeropts, dest)
409 409
410 410 def clone(ui, peeropts, source, dest=None, pull=False, rev=None,
411 411 update=True, stream=False, branch=None, shareopts=None):
412 412 """Make a copy of an existing repository.
413 413
414 414 Create a copy of an existing repository in a new directory. The
415 415 source and destination are URLs, as passed to the repository
416 416 function. Returns a pair of repository peers, the source and
417 417 newly created destination.
418 418
419 419 The location of the source is added to the new repository's
420 420 .hg/hgrc file, as the default to be used for future pulls and
421 421 pushes.
422 422
423 423 If an exception is raised, the partly cloned/updated destination
424 424 repository will be deleted.
425 425
426 426 Arguments:
427 427
428 428 source: repository object or URL
429 429
430 430 dest: URL of destination repository to create (defaults to base
431 431 name of source repository)
432 432
433 433 pull: always pull from source repository, even in local case or if the
434 434 server prefers streaming
435 435
436 436 stream: stream raw data uncompressed from repository (fast over
437 437 LAN, slow over WAN)
438 438
439 439 rev: revision to clone up to (implies pull=True)
440 440
441 441 update: update working directory after clone completes, if
442 442 destination is local repository (True means update to default rev,
443 443 anything else is treated as a revision)
444 444
445 445 branch: branches to clone
446 446
447 447 shareopts: dict of options to control auto sharing behavior. The "pool" key
448 448 activates auto sharing mode and defines the directory for stores. The
449 449 "mode" key determines how to construct the directory name of the shared
450 450 repository. "identity" means the name is derived from the node of the first
451 451 changeset in the repository. "remote" means the name is derived from the
452 452 remote's path/URL. Defaults to "identity."
453 453 """
454 454
455 455 if isinstance(source, str):
456 456 origsource = ui.expandpath(source)
457 457 source, branch = parseurl(origsource, branch)
458 458 srcpeer = peer(ui, peeropts, source)
459 459 else:
460 460 srcpeer = source.peer() # in case we were called with a localrepo
461 461 branch = (None, branch or [])
462 462 origsource = source = srcpeer.url()
463 463 rev, checkout = addbranchrevs(srcpeer, srcpeer, branch, rev)
464 464
465 465 if dest is None:
466 466 dest = defaultdest(source)
467 467 if dest:
468 468 ui.status(_("destination directory: %s\n") % dest)
469 469 else:
470 470 dest = ui.expandpath(dest)
471 471
472 472 dest = util.urllocalpath(dest)
473 473 source = util.urllocalpath(source)
474 474
475 475 if not dest:
476 476 raise error.Abort(_("empty destination path is not valid"))
477 477
478 478 destvfs = vfsmod.vfs(dest, expandpath=True)
479 479 if destvfs.lexists():
480 480 if not destvfs.isdir():
481 481 raise error.Abort(_("destination '%s' already exists") % dest)
482 482 elif destvfs.listdir():
483 483 raise error.Abort(_("destination '%s' is not empty") % dest)
484 484
485 485 shareopts = shareopts or {}
486 486 sharepool = shareopts.get('pool')
487 487 sharenamemode = shareopts.get('mode')
488 488 if sharepool and islocal(dest):
489 489 sharepath = None
490 490 if sharenamemode == 'identity':
491 491 # Resolve the name from the initial changeset in the remote
492 492 # repository. This returns nullid when the remote is empty. It
493 493 # raises RepoLookupError if revision 0 is filtered or otherwise
494 494 # not available. If we fail to resolve, sharing is not enabled.
495 495 try:
496 496 rootnode = srcpeer.lookup('0')
497 497 if rootnode != node.nullid:
498 498 sharepath = os.path.join(sharepool, node.hex(rootnode))
499 499 else:
500 500 ui.status(_('(not using pooled storage: '
501 501 'remote appears to be empty)\n'))
502 502 except error.RepoLookupError:
503 503 ui.status(_('(not using pooled storage: '
504 504 'unable to resolve identity of remote)\n'))
505 505 elif sharenamemode == 'remote':
506 506 sharepath = os.path.join(
507 507 sharepool, hashlib.sha1(source).hexdigest())
508 508 else:
509 509 raise error.Abort(_('unknown share naming mode: %s') %
510 510 sharenamemode)
511 511
512 512 if sharepath:
513 513 return clonewithshare(ui, peeropts, sharepath, source, srcpeer,
514 514 dest, pull=pull, rev=rev, update=update,
515 515 stream=stream)
516 516
517 517 srclock = destlock = cleandir = None
518 518 srcrepo = srcpeer.local()
519 519 try:
520 520 abspath = origsource
521 521 if islocal(origsource):
522 522 abspath = os.path.abspath(util.urllocalpath(origsource))
523 523
524 524 if islocal(dest):
525 525 cleandir = dest
526 526
527 527 copy = False
528 528 if (srcrepo and srcrepo.cancopy() and islocal(dest)
529 529 and not phases.hassecret(srcrepo)):
530 530 copy = not pull and not rev
531 531
532 532 if copy:
533 533 try:
534 534 # we use a lock here because if we race with commit, we
535 535 # can end up with extra data in the cloned revlogs that's
536 536 # not pointed to by changesets, thus causing verify to
537 537 # fail
538 538 srclock = srcrepo.lock(wait=False)
539 539 except error.LockError:
540 540 copy = False
541 541
542 542 if copy:
543 543 srcrepo.hook('preoutgoing', throw=True, source='clone')
544 544 hgdir = os.path.realpath(os.path.join(dest, ".hg"))
545 545 if not os.path.exists(dest):
546 546 os.mkdir(dest)
547 547 else:
548 548 # only clean up directories we create ourselves
549 549 cleandir = hgdir
550 550 try:
551 551 destpath = hgdir
552 552 util.makedir(destpath, notindexed=True)
553 553 except OSError as inst:
554 554 if inst.errno == errno.EEXIST:
555 555 cleandir = None
556 556 raise error.Abort(_("destination '%s' already exists")
557 557 % dest)
558 558 raise
559 559
560 560 destlock = copystore(ui, srcrepo, destpath)
561 561 # copy bookmarks over
562 562 srcbookmarks = srcrepo.vfs.join('bookmarks')
563 563 dstbookmarks = os.path.join(destpath, 'bookmarks')
564 564 if os.path.exists(srcbookmarks):
565 565 util.copyfile(srcbookmarks, dstbookmarks)
566 566
567 567 # Recomputing branch cache might be slow on big repos,
568 568 # so just copy it
569 569 def copybranchcache(fname):
570 570 srcbranchcache = srcrepo.vfs.join('cache/%s' % fname)
571 571 dstbranchcache = os.path.join(dstcachedir, fname)
572 572 if os.path.exists(srcbranchcache):
573 573 if not os.path.exists(dstcachedir):
574 574 os.mkdir(dstcachedir)
575 575 util.copyfile(srcbranchcache, dstbranchcache)
576 576
577 577 dstcachedir = os.path.join(destpath, 'cache')
578 578 # In local clones we're copying all nodes, not just served
579 579 # ones. Therefore copy all branch caches over.
580 580 copybranchcache('branch2')
581 581 for cachename in repoview.filtertable:
582 582 copybranchcache('branch2-%s' % cachename)
583 583
584 584 # we need to re-init the repo after manually copying the data
585 585 # into it
586 586 destpeer = peer(srcrepo, peeropts, dest)
587 587 srcrepo.hook('outgoing', source='clone',
588 588 node=node.hex(node.nullid))
589 589 else:
590 590 try:
591 591 destpeer = peer(srcrepo or ui, peeropts, dest, create=True)
592 592 # only pass ui when no srcrepo
593 593 except OSError as inst:
594 594 if inst.errno == errno.EEXIST:
595 595 cleandir = None
596 596 raise error.Abort(_("destination '%s' already exists")
597 597 % dest)
598 598 raise
599 599
600 600 revs = None
601 601 if rev:
602 602 if not srcpeer.capable('lookup'):
603 603 raise error.Abort(_("src repository does not support "
604 604 "revision lookup and so doesn't "
605 605 "support clone by revision"))
606 606 revs = [srcpeer.lookup(r) for r in rev]
607 607 checkout = revs[0]
608 608 local = destpeer.local()
609 609 if local:
610 610 if not stream:
611 611 if pull:
612 612 stream = False
613 613 else:
614 614 stream = None
615 615 # internal config: ui.quietbookmarkmove
616 quiet = local.ui.backupconfig('ui', 'quietbookmarkmove')
617 try:
618 local.ui.setconfig(
619 'ui', 'quietbookmarkmove', True, 'clone')
616 overrides = {('ui', 'quietbookmarkmove'): True}
617 with local.ui.configoverride(overrides, 'clone'):
620 618 exchange.pull(local, srcpeer, revs,
621 619 streamclonerequested=stream)
622 finally:
623 local.ui.restoreconfig(quiet)
624 620 elif srcrepo:
625 621 exchange.push(srcrepo, destpeer, revs=revs,
626 622 bookmarks=srcrepo._bookmarks.keys())
627 623 else:
628 624 raise error.Abort(_("clone from remote to remote not supported")
629 625 )
630 626
631 627 cleandir = None
632 628
633 629 destrepo = destpeer.local()
634 630 if destrepo:
635 631 template = uimod.samplehgrcs['cloned']
636 632 fp = destrepo.vfs("hgrc", "w", text=True)
637 633 u = util.url(abspath)
638 634 u.passwd = None
639 635 defaulturl = str(u)
640 636 fp.write(template % defaulturl)
641 637 fp.close()
642 638
643 639 destrepo.ui.setconfig('paths', 'default', defaulturl, 'clone')
644 640
645 641 if update:
646 642 if update is not True:
647 643 checkout = srcpeer.lookup(update)
648 644 uprev = None
649 645 status = None
650 646 if checkout is not None:
651 647 try:
652 648 uprev = destrepo.lookup(checkout)
653 649 except error.RepoLookupError:
654 650 if update is not True:
655 651 try:
656 652 uprev = destrepo.lookup(update)
657 653 except error.RepoLookupError:
658 654 pass
659 655 if uprev is None:
660 656 try:
661 657 uprev = destrepo._bookmarks['@']
662 658 update = '@'
663 659 bn = destrepo[uprev].branch()
664 660 if bn == 'default':
665 661 status = _("updating to bookmark @\n")
666 662 else:
667 663 status = (_("updating to bookmark @ on branch %s\n")
668 664 % bn)
669 665 except KeyError:
670 666 try:
671 667 uprev = destrepo.branchtip('default')
672 668 except error.RepoLookupError:
673 669 uprev = destrepo.lookup('tip')
674 670 if not status:
675 671 bn = destrepo[uprev].branch()
676 672 status = _("updating to branch %s\n") % bn
677 673 destrepo.ui.status(status)
678 674 _update(destrepo, uprev)
679 675 if update in destrepo._bookmarks:
680 676 bookmarks.activate(destrepo, update)
681 677 finally:
682 678 release(srclock, destlock)
683 679 if cleandir is not None:
684 680 shutil.rmtree(cleandir, True)
685 681 if srcpeer is not None:
686 682 srcpeer.close()
687 683 return srcpeer, destpeer
688 684
689 685 def _showstats(repo, stats, quietempty=False):
690 686 if quietempty and not any(stats):
691 687 return
692 688 repo.ui.status(_("%d files updated, %d files merged, "
693 689 "%d files removed, %d files unresolved\n") % stats)
694 690
695 691 def updaterepo(repo, node, overwrite, updatecheck=None):
696 692 """Update the working directory to node.
697 693
698 694 When overwrite is set, changes are clobbered, merged else
699 695
700 696 returns stats (see pydoc mercurial.merge.applyupdates)"""
701 697 return mergemod.update(repo, node, False, overwrite,
702 698 labels=['working copy', 'destination'],
703 699 updatecheck=updatecheck)
704 700
705 701 def update(repo, node, quietempty=False, updatecheck=None):
706 702 """update the working directory to node"""
707 703 stats = updaterepo(repo, node, False, updatecheck=updatecheck)
708 704 _showstats(repo, stats, quietempty)
709 705 if stats[3]:
710 706 repo.ui.status(_("use 'hg resolve' to retry unresolved file merges\n"))
711 707 return stats[3] > 0
712 708
713 709 # naming conflict in clone()
714 710 _update = update
715 711
716 712 def clean(repo, node, show_stats=True, quietempty=False):
717 713 """forcibly switch the working directory to node, clobbering changes"""
718 714 stats = updaterepo(repo, node, True)
719 715 repo.vfs.unlinkpath('graftstate', ignoremissing=True)
720 716 if show_stats:
721 717 _showstats(repo, stats, quietempty)
722 718 return stats[3] > 0
723 719
724 720 # naming conflict in updatetotally()
725 721 _clean = clean
726 722
727 723 def updatetotally(ui, repo, checkout, brev, clean=False, updatecheck=None):
728 724 """Update the working directory with extra care for non-file components
729 725
730 726 This takes care of non-file components below:
731 727
732 728 :bookmark: might be advanced or (in)activated
733 729
734 730 This takes arguments below:
735 731
736 732 :checkout: to which revision the working directory is updated
737 733 :brev: a name, which might be a bookmark to be activated after updating
738 734 :clean: whether changes in the working directory can be discarded
739 735 :updatecheck: how to deal with a dirty working directory
740 736
741 737 Valid values for updatecheck are (None => linear):
742 738
743 739 * abort: abort if the working directory is dirty
744 740 * none: don't check (merge working directory changes into destination)
745 741 * linear: check that update is linear before merging working directory
746 742 changes into destination
747 743 * noconflict: check that the update does not result in file merges
748 744
749 745 This returns whether conflict is detected at updating or not.
750 746 """
751 747 if updatecheck is None:
752 748 updatecheck = ui.config('experimental', 'updatecheck')
753 749 if updatecheck not in ('abort', 'none', 'linear', 'noconflict'):
754 750 # If not configured, or invalid value configured
755 751 updatecheck = 'linear'
756 752 with repo.wlock():
757 753 movemarkfrom = None
758 754 warndest = False
759 755 if checkout is None:
760 756 updata = destutil.destupdate(repo, clean=clean)
761 757 checkout, movemarkfrom, brev = updata
762 758 warndest = True
763 759
764 760 if clean:
765 761 ret = _clean(repo, checkout)
766 762 else:
767 763 if updatecheck == 'abort':
768 764 cmdutil.bailifchanged(repo, merge=False)
769 765 updatecheck = 'none'
770 766 ret = _update(repo, checkout, updatecheck=updatecheck)
771 767
772 768 if not ret and movemarkfrom:
773 769 if movemarkfrom == repo['.'].node():
774 770 pass # no-op update
775 771 elif bookmarks.update(repo, [movemarkfrom], repo['.'].node()):
776 772 b = ui.label(repo._activebookmark, 'bookmarks.active')
777 773 ui.status(_("updating bookmark %s\n") % b)
778 774 else:
779 775 # this can happen with a non-linear update
780 776 b = ui.label(repo._activebookmark, 'bookmarks')
781 777 ui.status(_("(leaving bookmark %s)\n") % b)
782 778 bookmarks.deactivate(repo)
783 779 elif brev in repo._bookmarks:
784 780 if brev != repo._activebookmark:
785 781 b = ui.label(brev, 'bookmarks.active')
786 782 ui.status(_("(activating bookmark %s)\n") % b)
787 783 bookmarks.activate(repo, brev)
788 784 elif brev:
789 785 if repo._activebookmark:
790 786 b = ui.label(repo._activebookmark, 'bookmarks')
791 787 ui.status(_("(leaving bookmark %s)\n") % b)
792 788 bookmarks.deactivate(repo)
793 789
794 790 if warndest:
795 791 destutil.statusotherdests(ui, repo)
796 792
797 793 return ret
798 794
799 795 def merge(repo, node, force=None, remind=True, mergeforce=False, labels=None):
800 796 """Branch merge with node, resolving changes. Return true if any
801 797 unresolved conflicts."""
802 798 stats = mergemod.update(repo, node, True, force, mergeforce=mergeforce,
803 799 labels=labels)
804 800 _showstats(repo, stats)
805 801 if stats[3]:
806 802 repo.ui.status(_("use 'hg resolve' to retry unresolved file merges "
807 803 "or 'hg update -C .' to abandon\n"))
808 804 elif remind:
809 805 repo.ui.status(_("(branch merge, don't forget to commit)\n"))
810 806 return stats[3] > 0
811 807
812 808 def _incoming(displaychlist, subreporecurse, ui, repo, source,
813 809 opts, buffered=False):
814 810 """
815 811 Helper for incoming / gincoming.
816 812 displaychlist gets called with
817 813 (remoterepo, incomingchangesetlist, displayer) parameters,
818 814 and is supposed to contain only code that can't be unified.
819 815 """
820 816 source, branches = parseurl(ui.expandpath(source), opts.get('branch'))
821 817 other = peer(repo, opts, source)
822 818 ui.status(_('comparing with %s\n') % util.hidepassword(source))
823 819 revs, checkout = addbranchrevs(repo, other, branches, opts.get('rev'))
824 820
825 821 if revs:
826 822 revs = [other.lookup(rev) for rev in revs]
827 823 other, chlist, cleanupfn = bundlerepo.getremotechanges(ui, repo, other,
828 824 revs, opts["bundle"], opts["force"])
829 825 try:
830 826 if not chlist:
831 827 ui.status(_("no changes found\n"))
832 828 return subreporecurse()
833 829 ui.pager('incoming')
834 830 displayer = cmdutil.show_changeset(ui, other, opts, buffered)
835 831 displaychlist(other, chlist, displayer)
836 832 displayer.close()
837 833 finally:
838 834 cleanupfn()
839 835 subreporecurse()
840 836 return 0 # exit code is zero since we found incoming changes
841 837
842 838 def incoming(ui, repo, source, opts):
843 839 def subreporecurse():
844 840 ret = 1
845 841 if opts.get('subrepos'):
846 842 ctx = repo[None]
847 843 for subpath in sorted(ctx.substate):
848 844 sub = ctx.sub(subpath)
849 845 ret = min(ret, sub.incoming(ui, source, opts))
850 846 return ret
851 847
852 848 def display(other, chlist, displayer):
853 849 limit = cmdutil.loglimit(opts)
854 850 if opts.get('newest_first'):
855 851 chlist.reverse()
856 852 count = 0
857 853 for n in chlist:
858 854 if limit is not None and count >= limit:
859 855 break
860 856 parents = [p for p in other.changelog.parents(n) if p != nullid]
861 857 if opts.get('no_merges') and len(parents) == 2:
862 858 continue
863 859 count += 1
864 860 displayer.show(other[n])
865 861 return _incoming(display, subreporecurse, ui, repo, source, opts)
866 862
867 863 def _outgoing(ui, repo, dest, opts):
868 864 dest = ui.expandpath(dest or 'default-push', dest or 'default')
869 865 dest, branches = parseurl(dest, opts.get('branch'))
870 866 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
871 867 revs, checkout = addbranchrevs(repo, repo, branches, opts.get('rev'))
872 868 if revs:
873 869 revs = [repo.lookup(rev) for rev in scmutil.revrange(repo, revs)]
874 870
875 871 other = peer(repo, opts, dest)
876 872 outgoing = discovery.findcommonoutgoing(repo.unfiltered(), other, revs,
877 873 force=opts.get('force'))
878 874 o = outgoing.missing
879 875 if not o:
880 876 scmutil.nochangesfound(repo.ui, repo, outgoing.excluded)
881 877 return o, other
882 878
883 879 def outgoing(ui, repo, dest, opts):
884 880 def recurse():
885 881 ret = 1
886 882 if opts.get('subrepos'):
887 883 ctx = repo[None]
888 884 for subpath in sorted(ctx.substate):
889 885 sub = ctx.sub(subpath)
890 886 ret = min(ret, sub.outgoing(ui, dest, opts))
891 887 return ret
892 888
893 889 limit = cmdutil.loglimit(opts)
894 890 o, other = _outgoing(ui, repo, dest, opts)
895 891 if not o:
896 892 cmdutil.outgoinghooks(ui, repo, other, opts, o)
897 893 return recurse()
898 894
899 895 if opts.get('newest_first'):
900 896 o.reverse()
901 897 ui.pager('outgoing')
902 898 displayer = cmdutil.show_changeset(ui, repo, opts)
903 899 count = 0
904 900 for n in o:
905 901 if limit is not None and count >= limit:
906 902 break
907 903 parents = [p for p in repo.changelog.parents(n) if p != nullid]
908 904 if opts.get('no_merges') and len(parents) == 2:
909 905 continue
910 906 count += 1
911 907 displayer.show(repo[n])
912 908 displayer.close()
913 909 cmdutil.outgoinghooks(ui, repo, other, opts, o)
914 910 recurse()
915 911 return 0 # exit code is zero since we found outgoing changes
916 912
917 913 def verify(repo):
918 914 """verify the consistency of a repository"""
919 915 ret = verifymod.verify(repo)
920 916
921 917 # Broken subrepo references in hidden csets don't seem worth worrying about,
922 918 # since they can't be pushed/pulled, and --hidden can be used if they are a
923 919 # concern.
924 920
925 921 # pathto() is needed for -R case
926 922 revs = repo.revs("filelog(%s)",
927 923 util.pathto(repo.root, repo.getcwd(), '.hgsubstate'))
928 924
929 925 if revs:
930 926 repo.ui.status(_('checking subrepo links\n'))
931 927 for rev in revs:
932 928 ctx = repo[rev]
933 929 try:
934 930 for subpath in ctx.substate:
935 931 try:
936 932 ret = (ctx.sub(subpath, allowcreate=False).verify()
937 933 or ret)
938 934 except error.RepoError as e:
939 935 repo.ui.warn(('%s: %s\n') % (rev, e))
940 936 except Exception:
941 937 repo.ui.warn(_('.hgsubstate is corrupt in revision %s\n') %
942 938 node.short(ctx.node()))
943 939
944 940 return ret
945 941
946 942 def remoteui(src, opts):
947 943 'build a remote ui from ui or repo and opts'
948 944 if util.safehasattr(src, 'baseui'): # looks like a repository
949 945 dst = src.baseui.copy() # drop repo-specific config
950 946 src = src.ui # copy target options from repo
951 947 else: # assume it's a global ui object
952 948 dst = src.copy() # keep all global options
953 949
954 950 # copy ssh-specific options
955 951 for o in 'ssh', 'remotecmd':
956 952 v = opts.get(o) or src.config('ui', o)
957 953 if v:
958 954 dst.setconfig("ui", o, v, 'copied')
959 955
960 956 # copy bundle-specific options
961 957 r = src.config('bundle', 'mainreporoot')
962 958 if r:
963 959 dst.setconfig('bundle', 'mainreporoot', r, 'copied')
964 960
965 961 # copy selected local settings to the remote ui
966 962 for sect in ('auth', 'hostfingerprints', 'hostsecurity', 'http_proxy'):
967 963 for key, val in src.configitems(sect):
968 964 dst.setconfig(sect, key, val, 'copied')
969 965 v = src.config('web', 'cacerts')
970 966 if v:
971 967 dst.setconfig('web', 'cacerts', util.expandpath(v), 'copied')
972 968
973 969 return dst
974 970
975 971 # Files of interest
976 972 # Used to check if the repository has changed looking at mtime and size of
977 973 # these files.
978 974 foi = [('spath', '00changelog.i'),
979 975 ('spath', 'phaseroots'), # ! phase can change content at the same size
980 976 ('spath', 'obsstore'),
981 977 ('path', 'bookmarks'), # ! bookmark can change content at the same size
982 978 ]
983 979
984 980 class cachedlocalrepo(object):
985 981 """Holds a localrepository that can be cached and reused."""
986 982
987 983 def __init__(self, repo):
988 984 """Create a new cached repo from an existing repo.
989 985
990 986 We assume the passed in repo was recently created. If the
991 987 repo has changed between when it was created and when it was
992 988 turned into a cache, it may not refresh properly.
993 989 """
994 990 assert isinstance(repo, localrepo.localrepository)
995 991 self._repo = repo
996 992 self._state, self.mtime = self._repostate()
997 993 self._filtername = repo.filtername
998 994
999 995 def fetch(self):
1000 996 """Refresh (if necessary) and return a repository.
1001 997
1002 998 If the cached instance is out of date, it will be recreated
1003 999 automatically and returned.
1004 1000
1005 1001 Returns a tuple of the repo and a boolean indicating whether a new
1006 1002 repo instance was created.
1007 1003 """
1008 1004 # We compare the mtimes and sizes of some well-known files to
1009 1005 # determine if the repo changed. This is not precise, as mtimes
1010 1006 # are susceptible to clock skew and imprecise filesystems and
1011 1007 # file content can change while maintaining the same size.
1012 1008
1013 1009 state, mtime = self._repostate()
1014 1010 if state == self._state:
1015 1011 return self._repo, False
1016 1012
1017 1013 repo = repository(self._repo.baseui, self._repo.url())
1018 1014 if self._filtername:
1019 1015 self._repo = repo.filtered(self._filtername)
1020 1016 else:
1021 1017 self._repo = repo.unfiltered()
1022 1018 self._state = state
1023 1019 self.mtime = mtime
1024 1020
1025 1021 return self._repo, True
1026 1022
1027 1023 def _repostate(self):
1028 1024 state = []
1029 1025 maxmtime = -1
1030 1026 for attr, fname in foi:
1031 1027 prefix = getattr(self._repo, attr)
1032 1028 p = os.path.join(prefix, fname)
1033 1029 try:
1034 1030 st = os.stat(p)
1035 1031 except OSError:
1036 1032 st = os.stat(prefix)
1037 1033 state.append((st.st_mtime, st.st_size))
1038 1034 maxmtime = max(maxmtime, st.st_mtime)
1039 1035
1040 1036 return tuple(state), maxmtime
1041 1037
1042 1038 def copy(self):
1043 1039 """Obtain a copy of this class instance.
1044 1040
1045 1041 A new localrepository instance is obtained. The new instance should be
1046 1042 completely independent of the original.
1047 1043 """
1048 1044 repo = repository(self._repo.baseui, self._repo.origroot)
1049 1045 if self._filtername:
1050 1046 repo = repo.filtered(self._filtername)
1051 1047 else:
1052 1048 repo = repo.unfiltered()
1053 1049 c = cachedlocalrepo(repo)
1054 1050 c._state = self._state
1055 1051 c.mtime = self.mtime
1056 1052 return c
General Comments 0
You need to be logged in to leave comments. Login now