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