##// END OF EJS Templates
merge with crew-stable
Thomas Arendsen Hein -
r17704:3d1a781b merge default
parent child Browse files
Show More
@@ -1,598 +1,600 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 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(source))
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 # we need to re-init the repo after manually copying the data
340 340 # into it
341 341 destpeer = peer(ui, peeropts, dest)
342 342 srcrepo.hook('outgoing', source='clone',
343 343 node=node.hex(node.nullid))
344 344 else:
345 345 try:
346 346 destpeer = peer(ui, peeropts, dest, create=True)
347 347 except OSError, inst:
348 348 if inst.errno == errno.EEXIST:
349 349 dircleanup.close()
350 350 raise util.Abort(_("destination '%s' already exists")
351 351 % dest)
352 352 raise
353 353
354 354 revs = None
355 355 if rev:
356 356 if not srcpeer.capable('lookup'):
357 357 raise util.Abort(_("src repository does not support "
358 358 "revision lookup and so doesn't "
359 359 "support clone by revision"))
360 360 revs = [srcpeer.lookup(r) for r in rev]
361 361 checkout = revs[0]
362 362 if destpeer.local():
363 363 destpeer.local().clone(srcpeer, heads=revs, stream=stream)
364 364 elif srcrepo:
365 365 srcrepo.push(destpeer, revs=revs)
366 366 else:
367 367 raise util.Abort(_("clone from remote to remote not supported"))
368 368
369 369 if dircleanup:
370 370 dircleanup.close()
371 371
372 372 # clone all bookmarks except divergent ones
373 373 destrepo = destpeer.local()
374 374 if destrepo and srcpeer.capable("pushkey"):
375 375 rb = srcpeer.listkeys('bookmarks')
376 376 for k, n in rb.iteritems():
377 377 try:
378 378 m = destrepo.lookup(n)
379 379 destrepo._bookmarks[k] = m
380 380 except error.RepoLookupError:
381 381 pass
382 382 if rb:
383 383 bookmarks.write(destrepo)
384 384 elif srcrepo and destpeer.capable("pushkey"):
385 385 for k, n in srcrepo._bookmarks.iteritems():
386 386 destpeer.pushkey('bookmarks', k, '', hex(n))
387 387
388 388 if destrepo:
389 389 fp = destrepo.opener("hgrc", "w", text=True)
390 390 fp.write("[paths]\n")
391 391 u = util.url(abspath)
392 392 u.passwd = None
393 393 defaulturl = str(u)
394 394 fp.write("default = %s\n" % defaulturl)
395 395 fp.close()
396 396
397 397 destrepo.ui.setconfig('paths', 'default', defaulturl)
398 398
399 399 if update:
400 400 if update is not True:
401 401 checkout = srcpeer.lookup(update)
402 402 for test in (checkout, 'default', 'tip'):
403 403 if test is None:
404 404 continue
405 405 try:
406 406 uprev = destrepo.lookup(test)
407 407 break
408 408 except error.RepoLookupError:
409 409 continue
410 410 bn = destrepo[uprev].branch()
411 411 destrepo.ui.status(_("updating to branch %s\n") % bn)
412 412 _update(destrepo, uprev)
413 if update in destrepo._bookmarks:
414 bookmarks.setcurrent(destrepo, update)
413 415
414 416 return srcpeer, destpeer
415 417 finally:
416 418 release(srclock, destlock)
417 419 if dircleanup is not None:
418 420 dircleanup.cleanup()
419 421 if srcpeer is not None:
420 422 srcpeer.close()
421 423
422 424 def _showstats(repo, stats):
423 425 repo.ui.status(_("%d files updated, %d files merged, "
424 426 "%d files removed, %d files unresolved\n") % stats)
425 427
426 428 def update(repo, node):
427 429 """update the working directory to node, merging linear changes"""
428 430 stats = mergemod.update(repo, node, False, False, None)
429 431 _showstats(repo, stats)
430 432 if stats[3]:
431 433 repo.ui.status(_("use 'hg resolve' to retry unresolved file merges\n"))
432 434 return stats[3] > 0
433 435
434 436 # naming conflict in clone()
435 437 _update = update
436 438
437 439 def clean(repo, node, show_stats=True):
438 440 """forcibly switch the working directory to node, clobbering changes"""
439 441 stats = mergemod.update(repo, node, False, True, None)
440 442 if show_stats:
441 443 _showstats(repo, stats)
442 444 return stats[3] > 0
443 445
444 446 def merge(repo, node, force=None, remind=True):
445 447 """Branch merge with node, resolving changes. Return true if any
446 448 unresolved conflicts."""
447 449 stats = mergemod.update(repo, node, True, force, False)
448 450 _showstats(repo, stats)
449 451 if stats[3]:
450 452 repo.ui.status(_("use 'hg resolve' to retry unresolved file merges "
451 453 "or 'hg update -C .' to abandon\n"))
452 454 elif remind:
453 455 repo.ui.status(_("(branch merge, don't forget to commit)\n"))
454 456 return stats[3] > 0
455 457
456 458 def _incoming(displaychlist, subreporecurse, ui, repo, source,
457 459 opts, buffered=False):
458 460 """
459 461 Helper for incoming / gincoming.
460 462 displaychlist gets called with
461 463 (remoterepo, incomingchangesetlist, displayer) parameters,
462 464 and is supposed to contain only code that can't be unified.
463 465 """
464 466 source, branches = parseurl(ui.expandpath(source), opts.get('branch'))
465 467 other = peer(repo, opts, source)
466 468 ui.status(_('comparing with %s\n') % util.hidepassword(source))
467 469 revs, checkout = addbranchrevs(repo, other, branches, opts.get('rev'))
468 470
469 471 if revs:
470 472 revs = [other.lookup(rev) for rev in revs]
471 473 other, chlist, cleanupfn = bundlerepo.getremotechanges(ui, repo, other,
472 474 revs, opts["bundle"], opts["force"])
473 475 try:
474 476 if not chlist:
475 477 ui.status(_("no changes found\n"))
476 478 return subreporecurse()
477 479
478 480 displayer = cmdutil.show_changeset(ui, other, opts, buffered)
479 481
480 482 # XXX once graphlog extension makes it into core,
481 483 # should be replaced by a if graph/else
482 484 displaychlist(other, chlist, displayer)
483 485
484 486 displayer.close()
485 487 finally:
486 488 cleanupfn()
487 489 subreporecurse()
488 490 return 0 # exit code is zero since we found incoming changes
489 491
490 492 def incoming(ui, repo, source, opts):
491 493 def subreporecurse():
492 494 ret = 1
493 495 if opts.get('subrepos'):
494 496 ctx = repo[None]
495 497 for subpath in sorted(ctx.substate):
496 498 sub = ctx.sub(subpath)
497 499 ret = min(ret, sub.incoming(ui, source, opts))
498 500 return ret
499 501
500 502 def display(other, chlist, displayer):
501 503 limit = cmdutil.loglimit(opts)
502 504 if opts.get('newest_first'):
503 505 chlist.reverse()
504 506 count = 0
505 507 for n in chlist:
506 508 if limit is not None and count >= limit:
507 509 break
508 510 parents = [p for p in other.changelog.parents(n) if p != nullid]
509 511 if opts.get('no_merges') and len(parents) == 2:
510 512 continue
511 513 count += 1
512 514 displayer.show(other[n])
513 515 return _incoming(display, subreporecurse, ui, repo, source, opts)
514 516
515 517 def _outgoing(ui, repo, dest, opts):
516 518 dest = ui.expandpath(dest or 'default-push', dest or 'default')
517 519 dest, branches = parseurl(dest, opts.get('branch'))
518 520 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
519 521 revs, checkout = addbranchrevs(repo, repo, branches, opts.get('rev'))
520 522 if revs:
521 523 revs = [repo.lookup(rev) for rev in scmutil.revrange(repo, revs)]
522 524
523 525 other = peer(repo, opts, dest)
524 526 outgoing = discovery.findcommonoutgoing(repo, other, revs,
525 527 force=opts.get('force'))
526 528 o = outgoing.missing
527 529 if not o:
528 530 scmutil.nochangesfound(repo.ui, repo, outgoing.excluded)
529 531 return None
530 532 return o
531 533
532 534 def outgoing(ui, repo, dest, opts):
533 535 def recurse():
534 536 ret = 1
535 537 if opts.get('subrepos'):
536 538 ctx = repo[None]
537 539 for subpath in sorted(ctx.substate):
538 540 sub = ctx.sub(subpath)
539 541 ret = min(ret, sub.outgoing(ui, dest, opts))
540 542 return ret
541 543
542 544 limit = cmdutil.loglimit(opts)
543 545 o = _outgoing(ui, repo, dest, opts)
544 546 if o is None:
545 547 return recurse()
546 548
547 549 if opts.get('newest_first'):
548 550 o.reverse()
549 551 displayer = cmdutil.show_changeset(ui, repo, opts)
550 552 count = 0
551 553 for n in o:
552 554 if limit is not None and count >= limit:
553 555 break
554 556 parents = [p for p in repo.changelog.parents(n) if p != nullid]
555 557 if opts.get('no_merges') and len(parents) == 2:
556 558 continue
557 559 count += 1
558 560 displayer.show(repo[n])
559 561 displayer.close()
560 562 recurse()
561 563 return 0 # exit code is zero since we found outgoing changes
562 564
563 565 def revert(repo, node, choose):
564 566 """revert changes to revision in node without updating dirstate"""
565 567 return mergemod.update(repo, node, False, True, choose)[3] > 0
566 568
567 569 def verify(repo):
568 570 """verify the consistency of a repository"""
569 571 return verifymod.verify(repo)
570 572
571 573 def remoteui(src, opts):
572 574 'build a remote ui from ui or repo and opts'
573 575 if util.safehasattr(src, 'baseui'): # looks like a repository
574 576 dst = src.baseui.copy() # drop repo-specific config
575 577 src = src.ui # copy target options from repo
576 578 else: # assume it's a global ui object
577 579 dst = src.copy() # keep all global options
578 580
579 581 # copy ssh-specific options
580 582 for o in 'ssh', 'remotecmd':
581 583 v = opts.get(o) or src.config('ui', o)
582 584 if v:
583 585 dst.setconfig("ui", o, v)
584 586
585 587 # copy bundle-specific options
586 588 r = src.config('bundle', 'mainreporoot')
587 589 if r:
588 590 dst.setconfig('bundle', 'mainreporoot', r)
589 591
590 592 # copy selected local settings to the remote ui
591 593 for sect in ('auth', 'hostfingerprints', 'http_proxy'):
592 594 for key, val in src.configitems(sect):
593 595 dst.setconfig(sect, key, val)
594 596 v = src.config('web', 'cacerts')
595 597 if v:
596 598 dst.setconfig('web', 'cacerts', util.expandpath(v))
597 599
598 600 return dst
@@ -1,472 +1,483 b''
1 1 $ hg init
2 2
3 3 no bookmarks
4 4
5 5 $ hg bookmarks
6 6 no bookmarks set
7 7
8 8 bookmark rev -1
9 9
10 10 $ hg bookmark X
11 11
12 12 list bookmarks
13 13
14 14 $ hg bookmarks
15 15 * X -1:000000000000
16 16
17 17 list bookmarks with color
18 18
19 19 $ hg --config extensions.color= --config color.mode=ansi \
20 20 > bookmarks --color=always
21 21 \x1b[0;32m * X -1:000000000000\x1b[0m (esc)
22 22
23 23 $ echo a > a
24 24 $ hg add a
25 25 $ hg commit -m 0
26 26
27 27 bookmark X moved to rev 0
28 28
29 29 $ hg bookmarks
30 30 * X 0:f7b1eb17ad24
31 31
32 32 look up bookmark
33 33
34 34 $ hg log -r X
35 35 changeset: 0:f7b1eb17ad24
36 36 bookmark: X
37 37 tag: tip
38 38 user: test
39 39 date: Thu Jan 01 00:00:00 1970 +0000
40 40 summary: 0
41 41
42 42
43 43 second bookmark for rev 0
44 44
45 45 $ hg bookmark X2
46 46
47 47 bookmark rev -1 again
48 48
49 49 $ hg bookmark -r null Y
50 50
51 51 list bookmarks
52 52
53 53 $ hg bookmarks
54 54 X 0:f7b1eb17ad24
55 55 * X2 0:f7b1eb17ad24
56 56 Y -1:000000000000
57 57
58 58 $ echo b > b
59 59 $ hg add b
60 60 $ hg commit -m 1
61 61
62 62 bookmarks revset
63 63
64 64 $ hg log -r 'bookmark()'
65 65 changeset: 0:f7b1eb17ad24
66 66 bookmark: X
67 67 user: test
68 68 date: Thu Jan 01 00:00:00 1970 +0000
69 69 summary: 0
70 70
71 71 changeset: 1:925d80f479bb
72 72 bookmark: X2
73 73 tag: tip
74 74 user: test
75 75 date: Thu Jan 01 00:00:00 1970 +0000
76 76 summary: 1
77 77
78 78 $ hg log -r 'bookmark(Y)'
79 79 $ hg log -r 'bookmark(X2)'
80 80 changeset: 1:925d80f479bb
81 81 bookmark: X2
82 82 tag: tip
83 83 user: test
84 84 date: Thu Jan 01 00:00:00 1970 +0000
85 85 summary: 1
86 86
87 87 $ hg log -r 'bookmark("re:X")'
88 88 changeset: 0:f7b1eb17ad24
89 89 bookmark: X
90 90 user: test
91 91 date: Thu Jan 01 00:00:00 1970 +0000
92 92 summary: 0
93 93
94 94 changeset: 1:925d80f479bb
95 95 bookmark: X2
96 96 tag: tip
97 97 user: test
98 98 date: Thu Jan 01 00:00:00 1970 +0000
99 99 summary: 1
100 100
101 101 $ hg log -r 'bookmark(unknown)'
102 102 abort: bookmark 'unknown' does not exist
103 103 [255]
104 104
105 105 $ hg help revsets | grep 'bookmark('
106 106 "bookmark([name])"
107 107
108 108 bookmarks X and X2 moved to rev 1, Y at rev -1
109 109
110 110 $ hg bookmarks
111 111 X 0:f7b1eb17ad24
112 112 * X2 1:925d80f479bb
113 113 Y -1:000000000000
114 114
115 115 bookmark rev 0 again
116 116
117 117 $ hg bookmark -r 0 Z
118 118
119 119 $ hg update X
120 120 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
121 121 $ echo c > c
122 122 $ hg add c
123 123 $ hg commit -m 2
124 124 created new head
125 125
126 126 bookmarks X moved to rev 2, Y at rev -1, Z at rev 0
127 127
128 128 $ hg bookmarks
129 129 * X 2:db815d6d32e6
130 130 X2 1:925d80f479bb
131 131 Y -1:000000000000
132 132 Z 0:f7b1eb17ad24
133 133
134 134 rename nonexistent bookmark
135 135
136 136 $ hg bookmark -m A B
137 137 abort: bookmark 'A' does not exist
138 138 [255]
139 139
140 140 rename to existent bookmark
141 141
142 142 $ hg bookmark -m X Y
143 143 abort: bookmark 'Y' already exists (use -f to force)
144 144 [255]
145 145
146 146 force rename to existent bookmark
147 147
148 148 $ hg bookmark -f -m X Y
149 149
150 150 list bookmarks
151 151
152 152 $ hg bookmark
153 153 X2 1:925d80f479bb
154 154 * Y 2:db815d6d32e6
155 155 Z 0:f7b1eb17ad24
156 156
157 157 bookmarks from a revset
158 158 $ hg bookmark -r '.^1' REVSET
159 159 $ hg bookmark -r ':tip' TIP
160 160 $ hg bookmarks
161 161 REVSET 0:f7b1eb17ad24
162 162 * TIP 2:db815d6d32e6
163 163 X2 1:925d80f479bb
164 164 Y 2:db815d6d32e6
165 165 Z 0:f7b1eb17ad24
166 166
167 167 $ hg bookmark -d REVSET
168 168 $ hg bookmark -d TIP
169 169
170 170 rename without new name
171 171
172 172 $ hg bookmark -m Y
173 173 abort: new bookmark name required
174 174 [255]
175 175
176 176 delete without name
177 177
178 178 $ hg bookmark -d
179 179 abort: bookmark name required
180 180 [255]
181 181
182 182 delete nonexistent bookmark
183 183
184 184 $ hg bookmark -d A
185 185 abort: bookmark 'A' does not exist
186 186 [255]
187 187
188 188 bookmark name with spaces should be stripped
189 189
190 190 $ hg bookmark ' x y '
191 191
192 192 list bookmarks
193 193
194 194 $ hg bookmarks
195 195 X2 1:925d80f479bb
196 196 Y 2:db815d6d32e6
197 197 Z 0:f7b1eb17ad24
198 198 * x y 2:db815d6d32e6
199 199
200 200 look up stripped bookmark name
201 201
202 202 $ hg log -r '"x y"'
203 203 changeset: 2:db815d6d32e6
204 204 bookmark: Y
205 205 bookmark: x y
206 206 tag: tip
207 207 parent: 0:f7b1eb17ad24
208 208 user: test
209 209 date: Thu Jan 01 00:00:00 1970 +0000
210 210 summary: 2
211 211
212 212
213 213 reject bookmark name with newline
214 214
215 215 $ hg bookmark '
216 216 > '
217 217 abort: bookmark name cannot contain newlines
218 218 [255]
219 219
220 220 bookmark with existing name
221 221
222 222 $ hg bookmark Z
223 223 abort: bookmark 'Z' already exists (use -f to force)
224 224 [255]
225 225
226 226 force bookmark with existing name
227 227
228 228 $ hg bookmark -f Z
229 229
230 230 list bookmarks
231 231
232 232 $ hg bookmark
233 233 X2 1:925d80f479bb
234 234 Y 2:db815d6d32e6
235 235 * Z 2:db815d6d32e6
236 236 x y 2:db815d6d32e6
237 237
238 238 revision but no bookmark name
239 239
240 240 $ hg bookmark -r .
241 241 abort: bookmark name required
242 242 [255]
243 243
244 244 bookmark name with whitespace only
245 245
246 246 $ hg bookmark ' '
247 247 abort: bookmark names cannot consist entirely of whitespace
248 248 [255]
249 249
250 250 invalid bookmark
251 251
252 252 $ hg bookmark 'foo:bar'
253 253 abort: bookmark 'foo:bar' contains illegal character
254 254 [255]
255 255
256 256 the bookmark extension should be ignored now that it is part of core
257 257
258 258 $ echo "[extensions]" >> $HGRCPATH
259 259 $ echo "bookmarks=" >> $HGRCPATH
260 260 $ hg bookmarks
261 261 X2 1:925d80f479bb
262 262 Y 2:db815d6d32e6
263 263 * Z 2:db815d6d32e6
264 264 x y 2:db815d6d32e6
265 265
266 266 test summary
267 267
268 268 $ hg summary
269 269 parent: 2:db815d6d32e6 tip
270 270 2
271 271 branch: default
272 272 bookmarks: *Z Y x y
273 273 commit: (clean)
274 274 update: 1 new changesets, 2 branch heads (merge)
275 275
276 276 test id
277 277
278 278 $ hg id
279 279 db815d6d32e6 tip Y/Z/x y
280 280
281 281 test rollback
282 282
283 283 $ echo foo > f1
284 284 $ hg ci -Amr
285 285 adding f1
286 286 $ hg bookmark -f Y -r 1
287 287 $ hg bookmark -f Z -r 1
288 288 $ hg rollback
289 289 repository tip rolled back to revision 2 (undo commit)
290 290 working directory now based on revision 2
291 291 $ hg bookmarks
292 292 X2 1:925d80f479bb
293 293 Y 2:db815d6d32e6
294 294 * Z 2:db815d6d32e6
295 295 x y 2:db815d6d32e6
296 296
297 297 test clone
298 298
299 299 $ hg bookmark -r 2 -i @
300 300 $ hg bookmark -r 2 -i a@
301 301 $ hg bookmarks
302 302 @ 2:db815d6d32e6
303 303 X2 1:925d80f479bb
304 304 Y 2:db815d6d32e6
305 305 * Z 2:db815d6d32e6
306 306 a@ 2:db815d6d32e6
307 307 x y 2:db815d6d32e6
308 308 $ hg clone . cloned-bookmarks
309 309 updating to branch default
310 310 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
311 311 $ hg -R cloned-bookmarks bookmarks
312 312 @ 2:db815d6d32e6
313 313 X2 1:925d80f479bb
314 314 Y 2:db815d6d32e6
315 315 Z 2:db815d6d32e6
316 316 a@ 2:db815d6d32e6
317 317 x y 2:db815d6d32e6
318 318
319 319 test clone with pull protocol
320 320
321 321 $ hg clone --pull . cloned-bookmarks-pull
322 322 requesting all changes
323 323 adding changesets
324 324 adding manifests
325 325 adding file changes
326 326 added 3 changesets with 3 changes to 3 files (+1 heads)
327 327 updating to branch default
328 328 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
329 329 $ hg -R cloned-bookmarks-pull bookmarks
330 330 @ 2:db815d6d32e6
331 331 X2 1:925d80f479bb
332 332 Y 2:db815d6d32e6
333 333 Z 2:db815d6d32e6
334 334 a@ 2:db815d6d32e6
335 335 x y 2:db815d6d32e6
336 336
337 337 $ hg bookmark -d @
338 338 $ hg bookmark -d a@
339 339
340 340 test clone with a specific revision
341 341
342 342 $ hg clone -r 925d80 . cloned-bookmarks-rev
343 343 adding changesets
344 344 adding manifests
345 345 adding file changes
346 346 added 2 changesets with 2 changes to 2 files
347 347 updating to branch default
348 348 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
349 349 $ hg -R cloned-bookmarks-rev bookmarks
350 350 X2 1:925d80f479bb
351 351
352 test clone with update to a bookmark
353
354 $ hg clone -u Z . cloned-bookmarks-update
355 updating to branch default
356 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
357 $ hg -R cloned-bookmarks-update bookmarks
358 X2 1:925d80f479bb
359 Y 2:db815d6d32e6
360 * Z 2:db815d6d32e6
361 x y 2:db815d6d32e6
362
352 363 create bundle with two heads
353 364
354 365 $ hg clone . tobundle
355 366 updating to branch default
356 367 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
357 368 $ echo x > tobundle/x
358 369 $ hg -R tobundle add tobundle/x
359 370 $ hg -R tobundle commit -m'x'
360 371 $ hg -R tobundle update -r -2
361 372 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
362 373 $ echo y > tobundle/y
363 374 $ hg -R tobundle branch test
364 375 marked working directory as branch test
365 376 (branches are permanent and global, did you want a bookmark?)
366 377 $ hg -R tobundle add tobundle/y
367 378 $ hg -R tobundle commit -m'y'
368 379 $ hg -R tobundle bundle tobundle.hg
369 380 searching for changes
370 381 2 changesets found
371 382 $ hg unbundle tobundle.hg
372 383 adding changesets
373 384 adding manifests
374 385 adding file changes
375 386 added 2 changesets with 2 changes to 2 files (+1 heads)
376 387 (run 'hg heads' to see heads, 'hg merge' to merge)
377 388 $ hg update
378 389 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
379 390 $ hg bookmarks
380 391 X2 1:925d80f479bb
381 392 Y 2:db815d6d32e6
382 393 * Z 3:125c9a1d6df6
383 394 x y 2:db815d6d32e6
384 395
385 396 test wrongly formated bookmark
386 397
387 398 $ echo '' >> .hg/bookmarks
388 399 $ hg bookmarks
389 400 X2 1:925d80f479bb
390 401 Y 2:db815d6d32e6
391 402 * Z 3:125c9a1d6df6
392 403 x y 2:db815d6d32e6
393 404 $ echo "Ican'thasformatedlines" >> .hg/bookmarks
394 405 $ hg bookmarks
395 406 malformed line in .hg/bookmarks: "Ican'thasformatedlines"
396 407 X2 1:925d80f479bb
397 408 Y 2:db815d6d32e6
398 409 * Z 3:125c9a1d6df6
399 410 x y 2:db815d6d32e6
400 411
401 412 test missing revisions
402 413
403 414 $ echo "925d80f479bc z" > .hg/bookmarks
404 415 $ hg book
405 416 no bookmarks set
406 417
407 418 test stripping a non-checked-out but bookmarked revision
408 419
409 420 $ hg --config extensions.graphlog= log --graph
410 421 o changeset: 4:9ba5f110a0b3
411 422 | branch: test
412 423 | tag: tip
413 424 | parent: 2:db815d6d32e6
414 425 | user: test
415 426 | date: Thu Jan 01 00:00:00 1970 +0000
416 427 | summary: y
417 428 |
418 429 | @ changeset: 3:125c9a1d6df6
419 430 |/ user: test
420 431 | date: Thu Jan 01 00:00:00 1970 +0000
421 432 | summary: x
422 433 |
423 434 o changeset: 2:db815d6d32e6
424 435 | parent: 0:f7b1eb17ad24
425 436 | user: test
426 437 | date: Thu Jan 01 00:00:00 1970 +0000
427 438 | summary: 2
428 439 |
429 440 | o changeset: 1:925d80f479bb
430 441 |/ user: test
431 442 | date: Thu Jan 01 00:00:00 1970 +0000
432 443 | summary: 1
433 444 |
434 445 o changeset: 0:f7b1eb17ad24
435 446 user: test
436 447 date: Thu Jan 01 00:00:00 1970 +0000
437 448 summary: 0
438 449
439 450 $ hg book should-end-on-two
440 451 $ hg co --clean 4
441 452 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
442 453 $ hg book four
443 454 $ hg --config extensions.mq= strip 3
444 455 saved backup bundle to * (glob)
445 456 should-end-on-two should end up pointing to revision 2, as that's the
446 457 tipmost surviving ancestor of the stripped revision.
447 458 $ hg --config extensions.graphlog= log --graph
448 459 @ changeset: 3:9ba5f110a0b3
449 460 | branch: test
450 461 | bookmark: four
451 462 | tag: tip
452 463 | user: test
453 464 | date: Thu Jan 01 00:00:00 1970 +0000
454 465 | summary: y
455 466 |
456 467 o changeset: 2:db815d6d32e6
457 468 | bookmark: should-end-on-two
458 469 | parent: 0:f7b1eb17ad24
459 470 | user: test
460 471 | date: Thu Jan 01 00:00:00 1970 +0000
461 472 | summary: 2
462 473 |
463 474 | o changeset: 1:925d80f479bb
464 475 |/ user: test
465 476 | date: Thu Jan 01 00:00:00 1970 +0000
466 477 | summary: 1
467 478 |
468 479 o changeset: 0:f7b1eb17ad24
469 480 user: test
470 481 date: Thu Jan 01 00:00:00 1970 +0000
471 482 summary: 0
472 483
General Comments 0
You need to be logged in to leave comments. Login now