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

Looks good

You need to be logged in to leave comments. Login now