##// END OF EJS Templates
clone: make sure to use "@" as bookmark and "default" as branch (issue3677)...
Thomas Arendsen Hein -
r17867:c9339efe stable
parent child Browse files
Show More
@@ -1,610 +1,616
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 i18n import _
10 10 from lock import release
11 11 from node import hex, nullid
12 12 import localrepo, bundlerepo, httppeer, sshpeer, statichttprepo, bookmarks
13 13 import lock, util, extensions, error, node, scmutil, phases
14 14 import cmdutil, discovery
15 15 import merge as mergemod
16 16 import verify as verifymod
17 17 import errno, os, shutil
18 18
19 19 def _local(path):
20 20 path = util.expandpath(util.urllocalpath(path))
21 21 return (os.path.isfile(path) and bundlerepo or localrepo)
22 22
23 23 def addbranchrevs(lrepo, other, branches, revs):
24 24 peer = other.peer() # a courtesy to callers using a localrepo for other
25 25 hashbranch, branches = branches
26 26 if not hashbranch and not branches:
27 27 return revs or None, revs and revs[0] or None
28 28 revs = revs and list(revs) or []
29 29 if not peer.capable('branchmap'):
30 30 if branches:
31 31 raise util.Abort(_("remote branch lookup not supported"))
32 32 revs.append(hashbranch)
33 33 return revs, revs[0]
34 34 branchmap = peer.branchmap()
35 35
36 36 def primary(branch):
37 37 if branch == '.':
38 38 if not lrepo:
39 39 raise util.Abort(_("dirstate branch not accessible"))
40 40 branch = lrepo.dirstate.branch()
41 41 if branch in branchmap:
42 42 revs.extend(node.hex(r) for r in reversed(branchmap[branch]))
43 43 return True
44 44 else:
45 45 return False
46 46
47 47 for branch in branches:
48 48 if not primary(branch):
49 49 raise error.RepoLookupError(_("unknown branch '%s'") % branch)
50 50 if hashbranch:
51 51 if not primary(hashbranch):
52 52 revs.append(hashbranch)
53 53 return revs, revs[0]
54 54
55 55 def parseurl(path, branches=None):
56 56 '''parse url#branch, returning (url, (branch, branches))'''
57 57
58 58 u = util.url(path)
59 59 branch = None
60 60 if u.fragment:
61 61 branch = u.fragment
62 62 u.fragment = None
63 63 return str(u), (branch, branches or [])
64 64
65 65 schemes = {
66 66 'bundle': bundlerepo,
67 67 'file': _local,
68 68 'http': httppeer,
69 69 'https': httppeer,
70 70 'ssh': sshpeer,
71 71 'static-http': statichttprepo,
72 72 }
73 73
74 74 def _peerlookup(path):
75 75 u = util.url(path)
76 76 scheme = u.scheme or 'file'
77 77 thing = schemes.get(scheme) or schemes['file']
78 78 try:
79 79 return thing(path)
80 80 except TypeError:
81 81 return thing
82 82
83 83 def islocal(repo):
84 84 '''return true if repo or path is local'''
85 85 if isinstance(repo, str):
86 86 try:
87 87 return _peerlookup(repo).islocal(repo)
88 88 except AttributeError:
89 89 return False
90 90 return repo.local()
91 91
92 92 def _peerorrepo(ui, path, create=False):
93 93 """return a repository object for the specified path"""
94 94 obj = _peerlookup(path).instance(ui, path, create)
95 95 ui = getattr(obj, "ui", ui)
96 96 for name, module in extensions.extensions():
97 97 hook = getattr(module, 'reposetup', None)
98 98 if hook:
99 99 hook(ui, obj)
100 100 return obj
101 101
102 102 def repository(ui, path='', create=False):
103 103 """return a repository object for the specified path"""
104 104 peer = _peerorrepo(ui, path, create)
105 105 repo = peer.local()
106 106 if not repo:
107 107 raise util.Abort(_("repository '%s' is not local") %
108 108 (path or peer.url()))
109 109 return repo
110 110
111 111 def peer(uiorrepo, opts, path, create=False):
112 112 '''return a repository peer for the specified path'''
113 113 rui = remoteui(uiorrepo, opts)
114 114 return _peerorrepo(rui, path, create).peer()
115 115
116 116 def defaultdest(source):
117 117 '''return default destination of clone if none is given'''
118 118 return os.path.basename(os.path.normpath(util.url(source).path))
119 119
120 120 def share(ui, source, dest=None, update=True):
121 121 '''create a shared repository'''
122 122
123 123 if not islocal(source):
124 124 raise util.Abort(_('can only share local repositories'))
125 125
126 126 if not dest:
127 127 dest = defaultdest(source)
128 128 else:
129 129 dest = ui.expandpath(dest)
130 130
131 131 if isinstance(source, str):
132 132 origsource = ui.expandpath(source)
133 133 source, branches = parseurl(origsource)
134 134 srcrepo = repository(ui, source)
135 135 rev, checkout = addbranchrevs(srcrepo, srcrepo, branches, None)
136 136 else:
137 137 srcrepo = source.local()
138 138 origsource = source = srcrepo.url()
139 139 checkout = None
140 140
141 141 sharedpath = srcrepo.sharedpath # if our source is already sharing
142 142
143 143 root = os.path.realpath(dest)
144 144 roothg = os.path.join(root, '.hg')
145 145
146 146 if os.path.exists(roothg):
147 147 raise util.Abort(_('destination already exists'))
148 148
149 149 if not os.path.isdir(root):
150 150 os.mkdir(root)
151 151 util.makedir(roothg, notindexed=True)
152 152
153 153 requirements = ''
154 154 try:
155 155 requirements = srcrepo.opener.read('requires')
156 156 except IOError, inst:
157 157 if inst.errno != errno.ENOENT:
158 158 raise
159 159
160 160 requirements += 'shared\n'
161 161 util.writefile(os.path.join(roothg, 'requires'), requirements)
162 162 util.writefile(os.path.join(roothg, 'sharedpath'), sharedpath)
163 163
164 164 r = repository(ui, root)
165 165
166 166 default = srcrepo.ui.config('paths', 'default')
167 167 if default:
168 168 fp = r.opener("hgrc", "w", text=True)
169 169 fp.write("[paths]\n")
170 170 fp.write("default = %s\n" % default)
171 171 fp.close()
172 172
173 173 if update:
174 174 r.ui.status(_("updating working directory\n"))
175 175 if update is not True:
176 176 checkout = update
177 177 for test in (checkout, 'default', 'tip'):
178 178 if test is None:
179 179 continue
180 180 try:
181 181 uprev = r.lookup(test)
182 182 break
183 183 except error.RepoLookupError:
184 184 continue
185 185 _update(r, uprev)
186 186
187 187 def copystore(ui, srcrepo, destpath):
188 188 '''copy files from store of srcrepo in destpath
189 189
190 190 returns destlock
191 191 '''
192 192 destlock = None
193 193 try:
194 194 hardlink = None
195 195 num = 0
196 196 srcpublishing = srcrepo.ui.configbool('phases', 'publish', True)
197 197 for f in srcrepo.store.copylist():
198 198 if srcpublishing and f.endswith('phaseroots'):
199 199 continue
200 200 src = os.path.join(srcrepo.sharedpath, f)
201 201 dst = os.path.join(destpath, f)
202 202 dstbase = os.path.dirname(dst)
203 203 if dstbase and not os.path.exists(dstbase):
204 204 os.mkdir(dstbase)
205 205 if os.path.exists(src):
206 206 if dst.endswith('data'):
207 207 # lock to avoid premature writing to the target
208 208 destlock = lock.lock(os.path.join(dstbase, "lock"))
209 209 hardlink, n = util.copyfiles(src, dst, hardlink)
210 210 num += n
211 211 if hardlink:
212 212 ui.debug("linked %d files\n" % num)
213 213 else:
214 214 ui.debug("copied %d files\n" % num)
215 215 return destlock
216 216 except: # re-raises
217 217 release(destlock)
218 218 raise
219 219
220 220 def clone(ui, peeropts, source, dest=None, pull=False, rev=None,
221 221 update=True, stream=False, branch=None):
222 222 """Make a copy of an existing repository.
223 223
224 224 Create a copy of an existing repository in a new directory. The
225 225 source and destination are URLs, as passed to the repository
226 226 function. Returns a pair of repository peers, the source and
227 227 newly created destination.
228 228
229 229 The location of the source is added to the new repository's
230 230 .hg/hgrc file, as the default to be used for future pulls and
231 231 pushes.
232 232
233 233 If an exception is raised, the partly cloned/updated destination
234 234 repository will be deleted.
235 235
236 236 Arguments:
237 237
238 238 source: repository object or URL
239 239
240 240 dest: URL of destination repository to create (defaults to base
241 241 name of source repository)
242 242
243 243 pull: always pull from source repository, even in local case
244 244
245 245 stream: stream raw data uncompressed from repository (fast over
246 246 LAN, slow over WAN)
247 247
248 248 rev: revision to clone up to (implies pull=True)
249 249
250 250 update: update working directory after clone completes, if
251 251 destination is local repository (True means update to default rev,
252 252 anything else is treated as a revision)
253 253
254 254 branch: branches to clone
255 255 """
256 256
257 257 if isinstance(source, str):
258 258 origsource = ui.expandpath(source)
259 259 source, branch = parseurl(origsource, branch)
260 260 srcpeer = peer(ui, peeropts, source)
261 261 else:
262 262 srcpeer = source.peer() # in case we were called with a localrepo
263 263 branch = (None, branch or [])
264 264 origsource = source = srcpeer.url()
265 265 rev, checkout = addbranchrevs(srcpeer, srcpeer, branch, rev)
266 266
267 267 if dest is None:
268 268 dest = defaultdest(source)
269 269 ui.status(_("destination directory: %s\n") % dest)
270 270 else:
271 271 dest = ui.expandpath(dest)
272 272
273 273 dest = util.urllocalpath(dest)
274 274 source = util.urllocalpath(source)
275 275
276 276 if not dest:
277 277 raise util.Abort(_("empty destination path is not valid"))
278 278 if os.path.exists(dest):
279 279 if not os.path.isdir(dest):
280 280 raise util.Abort(_("destination '%s' already exists") % dest)
281 281 elif os.listdir(dest):
282 282 raise util.Abort(_("destination '%s' is not empty") % dest)
283 283
284 284 class DirCleanup(object):
285 285 def __init__(self, dir_):
286 286 self.rmtree = shutil.rmtree
287 287 self.dir_ = dir_
288 288 def close(self):
289 289 self.dir_ = None
290 290 def cleanup(self):
291 291 if self.dir_:
292 292 self.rmtree(self.dir_, True)
293 293
294 294 srclock = destlock = dircleanup = None
295 295 srcrepo = srcpeer.local()
296 296 try:
297 297 abspath = origsource
298 298 if islocal(origsource):
299 299 abspath = os.path.abspath(util.urllocalpath(origsource))
300 300
301 301 if islocal(dest):
302 302 dircleanup = DirCleanup(dest)
303 303
304 304 copy = False
305 305 if (srcrepo and srcrepo.cancopy() and islocal(dest)
306 306 and not phases.hassecret(srcrepo)):
307 307 copy = not pull and not rev
308 308
309 309 if copy:
310 310 try:
311 311 # we use a lock here because if we race with commit, we
312 312 # can end up with extra data in the cloned revlogs that's
313 313 # not pointed to by changesets, thus causing verify to
314 314 # fail
315 315 srclock = srcrepo.lock(wait=False)
316 316 except error.LockError:
317 317 copy = False
318 318
319 319 if copy:
320 320 srcrepo.hook('preoutgoing', throw=True, source='clone')
321 321 hgdir = os.path.realpath(os.path.join(dest, ".hg"))
322 322 if not os.path.exists(dest):
323 323 os.mkdir(dest)
324 324 else:
325 325 # only clean up directories we create ourselves
326 326 dircleanup.dir_ = hgdir
327 327 try:
328 328 destpath = hgdir
329 329 util.makedir(destpath, notindexed=True)
330 330 except OSError, inst:
331 331 if inst.errno == errno.EEXIST:
332 332 dircleanup.close()
333 333 raise util.Abort(_("destination '%s' already exists")
334 334 % dest)
335 335 raise
336 336
337 337 destlock = copystore(ui, srcrepo, destpath)
338 338
339 339 # Recomputing branch cache might be slow on big repos,
340 340 # so just copy it
341 341 dstcachedir = os.path.join(destpath, 'cache')
342 342 srcbranchcache = srcrepo.sjoin('cache/branchheads')
343 343 dstbranchcache = os.path.join(dstcachedir, 'branchheads')
344 344 if os.path.exists(srcbranchcache):
345 345 if not os.path.exists(dstcachedir):
346 346 os.mkdir(dstcachedir)
347 347 util.copyfile(srcbranchcache, dstbranchcache)
348 348
349 349 # we need to re-init the repo after manually copying the data
350 350 # into it
351 351 destpeer = peer(ui, peeropts, dest)
352 352 srcrepo.hook('outgoing', source='clone',
353 353 node=node.hex(node.nullid))
354 354 else:
355 355 try:
356 356 destpeer = peer(ui, peeropts, dest, create=True)
357 357 except OSError, inst:
358 358 if inst.errno == errno.EEXIST:
359 359 dircleanup.close()
360 360 raise util.Abort(_("destination '%s' already exists")
361 361 % dest)
362 362 raise
363 363
364 364 revs = None
365 365 if rev:
366 366 if not srcpeer.capable('lookup'):
367 367 raise util.Abort(_("src repository does not support "
368 368 "revision lookup and so doesn't "
369 369 "support clone by revision"))
370 370 revs = [srcpeer.lookup(r) for r in rev]
371 371 checkout = revs[0]
372 372 if destpeer.local():
373 373 destpeer.local().clone(srcpeer, heads=revs, stream=stream)
374 374 elif srcrepo:
375 375 srcrepo.push(destpeer, revs=revs)
376 376 else:
377 377 raise util.Abort(_("clone from remote to remote not supported"))
378 378
379 379 if dircleanup:
380 380 dircleanup.close()
381 381
382 382 # clone all bookmarks except divergent ones
383 383 destrepo = destpeer.local()
384 384 if destrepo and srcpeer.capable("pushkey"):
385 385 rb = srcpeer.listkeys('bookmarks')
386 386 for k, n in rb.iteritems():
387 387 try:
388 388 m = destrepo.lookup(n)
389 389 destrepo._bookmarks[k] = m
390 390 except error.RepoLookupError:
391 391 pass
392 392 if rb:
393 393 bookmarks.write(destrepo)
394 394 elif srcrepo and destpeer.capable("pushkey"):
395 395 for k, n in srcrepo._bookmarks.iteritems():
396 396 destpeer.pushkey('bookmarks', k, '', hex(n))
397 397
398 398 if destrepo:
399 399 fp = destrepo.opener("hgrc", "w", text=True)
400 400 fp.write("[paths]\n")
401 401 u = util.url(abspath)
402 402 u.passwd = None
403 403 defaulturl = str(u)
404 404 fp.write("default = %s\n" % defaulturl)
405 405 fp.close()
406 406
407 407 destrepo.ui.setconfig('paths', 'default', defaulturl)
408 408
409 409 if update:
410 410 if update is not True:
411 411 checkout = srcpeer.lookup(update)
412 for test in (checkout, '@', 'default', 'tip'):
413 if test is None:
414 continue
412 uprev = None
413 if checkout is not None:
415 414 try:
416 uprev = destrepo.lookup(test)
417 break
415 uprev = destrepo.lookup(checkout)
418 416 except error.RepoLookupError:
419 continue
417 pass
418 if uprev is None:
419 try:
420 uprev = destrepo._bookmarks['@']
421 except KeyError:
422 try:
423 uprev = destrepo.branchtip('default')
424 except error.RepoLookupError:
425 uprev = destrepo.lookup('tip')
420 426 bn = destrepo[uprev].branch()
421 427 destrepo.ui.status(_("updating to branch %s\n") % bn)
422 428 _update(destrepo, uprev)
423 429 if update in destrepo._bookmarks:
424 430 bookmarks.setcurrent(destrepo, update)
425 431
426 432 return srcpeer, destpeer
427 433 finally:
428 434 release(srclock, destlock)
429 435 if dircleanup is not None:
430 436 dircleanup.cleanup()
431 437 if srcpeer is not None:
432 438 srcpeer.close()
433 439
434 440 def _showstats(repo, stats):
435 441 repo.ui.status(_("%d files updated, %d files merged, "
436 442 "%d files removed, %d files unresolved\n") % stats)
437 443
438 444 def update(repo, node):
439 445 """update the working directory to node, merging linear changes"""
440 446 stats = mergemod.update(repo, node, False, False, None)
441 447 _showstats(repo, stats)
442 448 if stats[3]:
443 449 repo.ui.status(_("use 'hg resolve' to retry unresolved file merges\n"))
444 450 return stats[3] > 0
445 451
446 452 # naming conflict in clone()
447 453 _update = update
448 454
449 455 def clean(repo, node, show_stats=True):
450 456 """forcibly switch the working directory to node, clobbering changes"""
451 457 stats = mergemod.update(repo, node, False, True, None)
452 458 if show_stats:
453 459 _showstats(repo, stats)
454 460 return stats[3] > 0
455 461
456 462 def merge(repo, node, force=None, remind=True):
457 463 """Branch merge with node, resolving changes. Return true if any
458 464 unresolved conflicts."""
459 465 stats = mergemod.update(repo, node, True, force, False)
460 466 _showstats(repo, stats)
461 467 if stats[3]:
462 468 repo.ui.status(_("use 'hg resolve' to retry unresolved file merges "
463 469 "or 'hg update -C .' to abandon\n"))
464 470 elif remind:
465 471 repo.ui.status(_("(branch merge, don't forget to commit)\n"))
466 472 return stats[3] > 0
467 473
468 474 def _incoming(displaychlist, subreporecurse, ui, repo, source,
469 475 opts, buffered=False):
470 476 """
471 477 Helper for incoming / gincoming.
472 478 displaychlist gets called with
473 479 (remoterepo, incomingchangesetlist, displayer) parameters,
474 480 and is supposed to contain only code that can't be unified.
475 481 """
476 482 source, branches = parseurl(ui.expandpath(source), opts.get('branch'))
477 483 other = peer(repo, opts, source)
478 484 ui.status(_('comparing with %s\n') % util.hidepassword(source))
479 485 revs, checkout = addbranchrevs(repo, other, branches, opts.get('rev'))
480 486
481 487 if revs:
482 488 revs = [other.lookup(rev) for rev in revs]
483 489 other, chlist, cleanupfn = bundlerepo.getremotechanges(ui, repo, other,
484 490 revs, opts["bundle"], opts["force"])
485 491 try:
486 492 if not chlist:
487 493 ui.status(_("no changes found\n"))
488 494 return subreporecurse()
489 495
490 496 displayer = cmdutil.show_changeset(ui, other, opts, buffered)
491 497
492 498 # XXX once graphlog extension makes it into core,
493 499 # should be replaced by a if graph/else
494 500 displaychlist(other, chlist, displayer)
495 501
496 502 displayer.close()
497 503 finally:
498 504 cleanupfn()
499 505 subreporecurse()
500 506 return 0 # exit code is zero since we found incoming changes
501 507
502 508 def incoming(ui, repo, source, opts):
503 509 def subreporecurse():
504 510 ret = 1
505 511 if opts.get('subrepos'):
506 512 ctx = repo[None]
507 513 for subpath in sorted(ctx.substate):
508 514 sub = ctx.sub(subpath)
509 515 ret = min(ret, sub.incoming(ui, source, opts))
510 516 return ret
511 517
512 518 def display(other, chlist, displayer):
513 519 limit = cmdutil.loglimit(opts)
514 520 if opts.get('newest_first'):
515 521 chlist.reverse()
516 522 count = 0
517 523 for n in chlist:
518 524 if limit is not None and count >= limit:
519 525 break
520 526 parents = [p for p in other.changelog.parents(n) if p != nullid]
521 527 if opts.get('no_merges') and len(parents) == 2:
522 528 continue
523 529 count += 1
524 530 displayer.show(other[n])
525 531 return _incoming(display, subreporecurse, ui, repo, source, opts)
526 532
527 533 def _outgoing(ui, repo, dest, opts):
528 534 dest = ui.expandpath(dest or 'default-push', dest or 'default')
529 535 dest, branches = parseurl(dest, opts.get('branch'))
530 536 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
531 537 revs, checkout = addbranchrevs(repo, repo, branches, opts.get('rev'))
532 538 if revs:
533 539 revs = [repo.lookup(rev) for rev in scmutil.revrange(repo, revs)]
534 540
535 541 other = peer(repo, opts, dest)
536 542 outgoing = discovery.findcommonoutgoing(repo, other, revs,
537 543 force=opts.get('force'))
538 544 o = outgoing.missing
539 545 if not o:
540 546 scmutil.nochangesfound(repo.ui, repo, outgoing.excluded)
541 547 return None
542 548 return o
543 549
544 550 def outgoing(ui, repo, dest, opts):
545 551 def recurse():
546 552 ret = 1
547 553 if opts.get('subrepos'):
548 554 ctx = repo[None]
549 555 for subpath in sorted(ctx.substate):
550 556 sub = ctx.sub(subpath)
551 557 ret = min(ret, sub.outgoing(ui, dest, opts))
552 558 return ret
553 559
554 560 limit = cmdutil.loglimit(opts)
555 561 o = _outgoing(ui, repo, dest, opts)
556 562 if o is None:
557 563 return recurse()
558 564
559 565 if opts.get('newest_first'):
560 566 o.reverse()
561 567 displayer = cmdutil.show_changeset(ui, repo, opts)
562 568 count = 0
563 569 for n in o:
564 570 if limit is not None and count >= limit:
565 571 break
566 572 parents = [p for p in repo.changelog.parents(n) if p != nullid]
567 573 if opts.get('no_merges') and len(parents) == 2:
568 574 continue
569 575 count += 1
570 576 displayer.show(repo[n])
571 577 displayer.close()
572 578 recurse()
573 579 return 0 # exit code is zero since we found outgoing changes
574 580
575 581 def revert(repo, node, choose):
576 582 """revert changes to revision in node without updating dirstate"""
577 583 return mergemod.update(repo, node, False, True, choose)[3] > 0
578 584
579 585 def verify(repo):
580 586 """verify the consistency of a repository"""
581 587 return verifymod.verify(repo)
582 588
583 589 def remoteui(src, opts):
584 590 'build a remote ui from ui or repo and opts'
585 591 if util.safehasattr(src, 'baseui'): # looks like a repository
586 592 dst = src.baseui.copy() # drop repo-specific config
587 593 src = src.ui # copy target options from repo
588 594 else: # assume it's a global ui object
589 595 dst = src.copy() # keep all global options
590 596
591 597 # copy ssh-specific options
592 598 for o in 'ssh', 'remotecmd':
593 599 v = opts.get(o) or src.config('ui', o)
594 600 if v:
595 601 dst.setconfig("ui", o, v)
596 602
597 603 # copy bundle-specific options
598 604 r = src.config('bundle', 'mainreporoot')
599 605 if r:
600 606 dst.setconfig('bundle', 'mainreporoot', r)
601 607
602 608 # copy selected local settings to the remote ui
603 609 for sect in ('auth', 'hostfingerprints', 'http_proxy'):
604 610 for key, val in src.configitems(sect):
605 611 dst.setconfig(sect, key, val)
606 612 v = src.config('web', 'cacerts')
607 613 if v:
608 614 dst.setconfig('web', 'cacerts', util.expandpath(v))
609 615
610 616 return dst
@@ -1,352 +1,350
1 1 $ "$TESTDIR/hghave" hardlink || exit 80
2 2
3 3 $ cat > nlinks.py <<EOF
4 4 > import sys
5 5 > from mercurial import util
6 6 > for f in sorted(sys.stdin.readlines()):
7 7 > f = f[:-1]
8 8 > print util.nlinks(f), f
9 9 > EOF
10 10
11 11 $ nlinksdir()
12 12 > {
13 13 > find $1 -type f | python $TESTTMP/nlinks.py
14 14 > }
15 15
16 16 Some implementations of cp can't create hardlinks (replaces 'cp -al' on Linux):
17 17
18 18 $ cat > linkcp.py <<EOF
19 19 > from mercurial import util
20 20 > import sys
21 21 > util.copyfiles(sys.argv[1], sys.argv[2], hardlink=True)
22 22 > EOF
23 23
24 24 $ linkcp()
25 25 > {
26 26 > python $TESTTMP/linkcp.py $1 $2
27 27 > }
28 28
29 29 Prepare repo r1:
30 30
31 31 $ hg init r1
32 32 $ cd r1
33 33
34 34 $ echo c1 > f1
35 35 $ hg add f1
36 36 $ hg ci -m0
37 37
38 38 $ mkdir d1
39 39 $ cd d1
40 40 $ echo c2 > f2
41 41 $ hg add f2
42 42 $ hg ci -m1
43 43 $ cd ../..
44 44
45 45 $ nlinksdir r1/.hg/store
46 46 1 r1/.hg/store/00changelog.i
47 47 1 r1/.hg/store/00manifest.i
48 48 1 r1/.hg/store/data/d1/f2.i
49 49 1 r1/.hg/store/data/f1.i
50 50 1 r1/.hg/store/fncache
51 51 1 r1/.hg/store/phaseroots
52 52 1 r1/.hg/store/undo
53 53 1 r1/.hg/store/undo.phaseroots
54 54
55 55
56 56 Create hardlinked clone r2:
57 57
58 58 $ hg clone -U --debug r1 r2
59 59 linked 7 files
60 60 listing keys for "bookmarks"
61 61
62 62 Create non-hardlinked clone r3:
63 63
64 64 $ hg clone --pull r1 r3
65 65 requesting all changes
66 66 adding changesets
67 67 adding manifests
68 68 adding file changes
69 69 added 2 changesets with 2 changes to 2 files
70 70 updating to branch default
71 71 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
72 72
73 73
74 74 Repos r1 and r2 should now contain hardlinked files:
75 75
76 76 $ nlinksdir r1/.hg/store
77 77 2 r1/.hg/store/00changelog.i
78 78 2 r1/.hg/store/00manifest.i
79 79 2 r1/.hg/store/data/d1/f2.i
80 80 2 r1/.hg/store/data/f1.i
81 81 2 r1/.hg/store/fncache
82 82 1 r1/.hg/store/phaseroots
83 83 1 r1/.hg/store/undo
84 84 1 r1/.hg/store/undo.phaseroots
85 85
86 86 $ nlinksdir r2/.hg/store
87 87 2 r2/.hg/store/00changelog.i
88 88 2 r2/.hg/store/00manifest.i
89 89 2 r2/.hg/store/data/d1/f2.i
90 90 2 r2/.hg/store/data/f1.i
91 91 2 r2/.hg/store/fncache
92 92
93 93 Repo r3 should not be hardlinked:
94 94
95 95 $ nlinksdir r3/.hg/store
96 96 1 r3/.hg/store/00changelog.i
97 97 1 r3/.hg/store/00manifest.i
98 98 1 r3/.hg/store/data/d1/f2.i
99 99 1 r3/.hg/store/data/f1.i
100 100 1 r3/.hg/store/fncache
101 101 1 r3/.hg/store/phaseroots
102 102 1 r3/.hg/store/undo
103 103 1 r3/.hg/store/undo.phaseroots
104 104
105 105
106 106 Create a non-inlined filelog in r3:
107 107
108 108 $ cd r3/d1
109 109 >>> f = open('data1', 'wb')
110 110 >>> for x in range(10000):
111 111 ... f.write("%s\n" % str(x))
112 112 >>> f.close()
113 113 $ for j in 0 1 2 3 4 5 6 7 8 9; do
114 114 > cat data1 >> f2
115 115 > hg commit -m$j
116 116 > done
117 117 $ cd ../..
118 118
119 119 $ nlinksdir r3/.hg/store
120 120 1 r3/.hg/store/00changelog.i
121 121 1 r3/.hg/store/00manifest.i
122 122 1 r3/.hg/store/data/d1/f2.d
123 123 1 r3/.hg/store/data/d1/f2.i
124 124 1 r3/.hg/store/data/f1.i
125 125 1 r3/.hg/store/fncache
126 126 1 r3/.hg/store/phaseroots
127 127 1 r3/.hg/store/undo
128 128 1 r3/.hg/store/undo.phaseroots
129 129
130 130 Push to repo r1 should break up most hardlinks in r2:
131 131
132 132 $ hg -R r2 verify
133 133 checking changesets
134 134 checking manifests
135 135 crosschecking files in changesets and manifests
136 136 checking files
137 137 2 files, 2 changesets, 2 total revisions
138 138
139 139 $ cd r3
140 140 $ hg push
141 141 pushing to $TESTTMP/r1 (glob)
142 142 searching for changes
143 143 adding changesets
144 144 adding manifests
145 145 adding file changes
146 146 added 10 changesets with 10 changes to 1 files
147 147
148 148 $ cd ..
149 149
150 150 $ nlinksdir r2/.hg/store
151 151 1 r2/.hg/store/00changelog.i
152 152 1 r2/.hg/store/00manifest.i
153 153 1 r2/.hg/store/data/d1/f2.i
154 154 2 r2/.hg/store/data/f1.i
155 155 1 r2/.hg/store/fncache
156 156
157 157 $ hg -R r2 verify
158 158 checking changesets
159 159 checking manifests
160 160 crosschecking files in changesets and manifests
161 161 checking files
162 162 2 files, 2 changesets, 2 total revisions
163 163
164 164
165 165 $ cd r1
166 166 $ hg up
167 167 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
168 168
169 169 Committing a change to f1 in r1 must break up hardlink f1.i in r2:
170 170
171 171 $ echo c1c1 >> f1
172 172 $ hg ci -m00
173 173 $ cd ..
174 174
175 175 $ nlinksdir r2/.hg/store
176 176 1 r2/.hg/store/00changelog.i
177 177 1 r2/.hg/store/00manifest.i
178 178 1 r2/.hg/store/data/d1/f2.i
179 179 1 r2/.hg/store/data/f1.i
180 180 1 r2/.hg/store/fncache
181 181
182 182
183 183 $ cd r3
184 184 $ hg tip --template '{rev}:{node|short}\n'
185 185 11:a6451b6bc41f
186 186 $ echo bla > f1
187 187 $ hg ci -m1
188 188 $ cd ..
189 189
190 190 Create hardlinked copy r4 of r3 (on Linux, we would call 'cp -al'):
191 191
192 192 $ linkcp r3 r4
193 193
194 194 r4 has hardlinks in the working dir (not just inside .hg):
195 195
196 196 $ nlinksdir r4
197 197 2 r4/.hg/00changelog.i
198 198 2 r4/.hg/branch
199 199 2 r4/.hg/cache/branchheads
200 2 r4/.hg/cache/tags
201 200 2 r4/.hg/dirstate
202 201 2 r4/.hg/hgrc
203 202 2 r4/.hg/last-message.txt
204 203 2 r4/.hg/requires
205 204 2 r4/.hg/store/00changelog.i
206 205 2 r4/.hg/store/00manifest.i
207 206 2 r4/.hg/store/data/d1/f2.d
208 207 2 r4/.hg/store/data/d1/f2.i
209 208 2 r4/.hg/store/data/f1.i
210 209 2 r4/.hg/store/fncache
211 210 2 r4/.hg/store/phaseroots
212 211 2 r4/.hg/store/undo
213 212 2 r4/.hg/store/undo.phaseroots
214 213 2 r4/.hg/undo.bookmarks
215 214 2 r4/.hg/undo.branch
216 215 2 r4/.hg/undo.desc
217 216 2 r4/.hg/undo.dirstate
218 217 2 r4/d1/data1
219 218 2 r4/d1/f2
220 219 2 r4/f1
221 220
222 221 Update back to revision 11 in r4 should break hardlink of file f1:
223 222
224 223 $ hg -R r4 up 11
225 224 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
226 225
227 226 $ nlinksdir r4
228 227 2 r4/.hg/00changelog.i
229 228 1 r4/.hg/branch
230 229 2 r4/.hg/cache/branchheads
231 2 r4/.hg/cache/tags
232 230 1 r4/.hg/dirstate
233 231 2 r4/.hg/hgrc
234 232 2 r4/.hg/last-message.txt
235 233 2 r4/.hg/requires
236 234 2 r4/.hg/store/00changelog.i
237 235 2 r4/.hg/store/00manifest.i
238 236 2 r4/.hg/store/data/d1/f2.d
239 237 2 r4/.hg/store/data/d1/f2.i
240 238 2 r4/.hg/store/data/f1.i
241 239 2 r4/.hg/store/fncache
242 240 2 r4/.hg/store/phaseroots
243 241 2 r4/.hg/store/undo
244 242 2 r4/.hg/store/undo.phaseroots
245 243 2 r4/.hg/undo.bookmarks
246 244 2 r4/.hg/undo.branch
247 245 2 r4/.hg/undo.desc
248 246 2 r4/.hg/undo.dirstate
249 247 2 r4/d1/data1
250 248 2 r4/d1/f2
251 249 1 r4/f1
252 250
253 251
254 252 Test hardlinking outside hg:
255 253
256 254 $ mkdir x
257 255 $ echo foo > x/a
258 256
259 257 $ linkcp x y
260 258 $ echo bar >> y/a
261 259
262 260 No diff if hardlink:
263 261
264 262 $ diff x/a y/a
265 263
266 264 Test mq hardlinking:
267 265
268 266 $ echo "[extensions]" >> $HGRCPATH
269 267 $ echo "mq=" >> $HGRCPATH
270 268
271 269 $ hg init a
272 270 $ cd a
273 271
274 272 $ hg qimport -n foo - << EOF
275 273 > # HG changeset patch
276 274 > # Date 1 0
277 275 > diff -r 2588a8b53d66 a
278 276 > --- /dev/null Thu Jan 01 00:00:00 1970 +0000
279 277 > +++ b/a Wed Jul 23 15:54:29 2008 +0200
280 278 > @@ -0,0 +1,1 @@
281 279 > +a
282 280 > EOF
283 281 adding foo to series file
284 282
285 283 $ hg qpush
286 284 applying foo
287 285 now at: foo
288 286
289 287 $ cd ..
290 288 $ linkcp a b
291 289 $ cd b
292 290
293 291 $ hg qimport -n bar - << EOF
294 292 > # HG changeset patch
295 293 > # Date 2 0
296 294 > diff -r 2588a8b53d66 a
297 295 > --- /dev/null Thu Jan 01 00:00:00 1970 +0000
298 296 > +++ b/b Wed Jul 23 15:54:29 2008 +0200
299 297 > @@ -0,0 +1,1 @@
300 298 > +b
301 299 > EOF
302 300 adding bar to series file
303 301
304 302 $ hg qpush
305 303 applying bar
306 304 now at: bar
307 305
308 306 $ cat .hg/patches/status
309 307 430ed4828a74fa4047bc816a25500f7472ab4bfe:foo
310 308 4e7abb4840c46a910f6d7b4d3c3fc7e5209e684c:bar
311 309
312 310 $ cat .hg/patches/series
313 311 foo
314 312 bar
315 313
316 314 $ cat ../a/.hg/patches/status
317 315 430ed4828a74fa4047bc816a25500f7472ab4bfe:foo
318 316
319 317 $ cat ../a/.hg/patches/series
320 318 foo
321 319
322 320 Test tags hardlinking:
323 321
324 322 $ hg qdel -r qbase:qtip
325 323 patch foo finalized without changeset message
326 324 patch bar finalized without changeset message
327 325
328 326 $ hg tag -l lfoo
329 327 $ hg tag foo
330 328
331 329 $ cd ..
332 330 $ linkcp b c
333 331 $ cd c
334 332
335 333 $ hg tag -l -r 0 lbar
336 334 $ hg tag -r 0 bar
337 335
338 336 $ cat .hgtags
339 337 4e7abb4840c46a910f6d7b4d3c3fc7e5209e684c foo
340 338 430ed4828a74fa4047bc816a25500f7472ab4bfe bar
341 339
342 340 $ cat .hg/localtags
343 341 4e7abb4840c46a910f6d7b4d3c3fc7e5209e684c lfoo
344 342 430ed4828a74fa4047bc816a25500f7472ab4bfe lbar
345 343
346 344 $ cat ../b/.hgtags
347 345 4e7abb4840c46a910f6d7b4d3c3fc7e5209e684c foo
348 346
349 347 $ cat ../b/.hg/localtags
350 348 4e7abb4840c46a910f6d7b4d3c3fc7e5209e684c lfoo
351 349
352 350 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now