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