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