##// END OF EJS Templates
localrepo: use single quotes in use warning
timeless -
r29975:c15f0610 default
parent child Browse files
Show More
@@ -1,1996 +1,1996 b''
1 1 # localrepo.py - read/write repository class for mercurial
2 2 #
3 3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 from __future__ import absolute_import
9 9
10 10 import errno
11 11 import hashlib
12 12 import inspect
13 13 import os
14 14 import random
15 15 import time
16 16 import weakref
17 17
18 18 from .i18n import _
19 19 from .node import (
20 20 hex,
21 21 nullid,
22 22 short,
23 23 wdirrev,
24 24 )
25 25 from . import (
26 26 bookmarks,
27 27 branchmap,
28 28 bundle2,
29 29 changegroup,
30 30 changelog,
31 31 cmdutil,
32 32 context,
33 33 dirstate,
34 34 encoding,
35 35 error,
36 36 exchange,
37 37 extensions,
38 38 filelog,
39 39 hook,
40 40 lock as lockmod,
41 41 manifest,
42 42 match as matchmod,
43 43 merge as mergemod,
44 44 namespaces,
45 45 obsolete,
46 46 pathutil,
47 47 peer,
48 48 phases,
49 49 pushkey,
50 50 repoview,
51 51 revset,
52 52 scmutil,
53 53 store,
54 54 subrepo,
55 55 tags as tagsmod,
56 56 transaction,
57 57 util,
58 58 )
59 59
60 60 release = lockmod.release
61 61 urlerr = util.urlerr
62 62 urlreq = util.urlreq
63 63
64 64 class repofilecache(scmutil.filecache):
65 65 """All filecache usage on repo are done for logic that should be unfiltered
66 66 """
67 67
68 68 def __get__(self, repo, type=None):
69 69 if repo is None:
70 70 return self
71 71 return super(repofilecache, self).__get__(repo.unfiltered(), type)
72 72 def __set__(self, repo, value):
73 73 return super(repofilecache, self).__set__(repo.unfiltered(), value)
74 74 def __delete__(self, repo):
75 75 return super(repofilecache, self).__delete__(repo.unfiltered())
76 76
77 77 class storecache(repofilecache):
78 78 """filecache for files in the store"""
79 79 def join(self, obj, fname):
80 80 return obj.sjoin(fname)
81 81
82 82 class unfilteredpropertycache(util.propertycache):
83 83 """propertycache that apply to unfiltered repo only"""
84 84
85 85 def __get__(self, repo, type=None):
86 86 unfi = repo.unfiltered()
87 87 if unfi is repo:
88 88 return super(unfilteredpropertycache, self).__get__(unfi)
89 89 return getattr(unfi, self.name)
90 90
91 91 class filteredpropertycache(util.propertycache):
92 92 """propertycache that must take filtering in account"""
93 93
94 94 def cachevalue(self, obj, value):
95 95 object.__setattr__(obj, self.name, value)
96 96
97 97
98 98 def hasunfilteredcache(repo, name):
99 99 """check if a repo has an unfilteredpropertycache value for <name>"""
100 100 return name in vars(repo.unfiltered())
101 101
102 102 def unfilteredmethod(orig):
103 103 """decorate method that always need to be run on unfiltered version"""
104 104 def wrapper(repo, *args, **kwargs):
105 105 return orig(repo.unfiltered(), *args, **kwargs)
106 106 return wrapper
107 107
108 108 moderncaps = set(('lookup', 'branchmap', 'pushkey', 'known', 'getbundle',
109 109 'unbundle'))
110 110 legacycaps = moderncaps.union(set(['changegroupsubset']))
111 111
112 112 class localpeer(peer.peerrepository):
113 113 '''peer for a local repo; reflects only the most recent API'''
114 114
115 115 def __init__(self, repo, caps=moderncaps):
116 116 peer.peerrepository.__init__(self)
117 117 self._repo = repo.filtered('served')
118 118 self.ui = repo.ui
119 119 self._caps = repo._restrictcapabilities(caps)
120 120 self.requirements = repo.requirements
121 121 self.supportedformats = repo.supportedformats
122 122
123 123 def close(self):
124 124 self._repo.close()
125 125
126 126 def _capabilities(self):
127 127 return self._caps
128 128
129 129 def local(self):
130 130 return self._repo
131 131
132 132 def canpush(self):
133 133 return True
134 134
135 135 def url(self):
136 136 return self._repo.url()
137 137
138 138 def lookup(self, key):
139 139 return self._repo.lookup(key)
140 140
141 141 def branchmap(self):
142 142 return self._repo.branchmap()
143 143
144 144 def heads(self):
145 145 return self._repo.heads()
146 146
147 147 def known(self, nodes):
148 148 return self._repo.known(nodes)
149 149
150 150 def getbundle(self, source, heads=None, common=None, bundlecaps=None,
151 151 **kwargs):
152 152 cg = exchange.getbundle(self._repo, source, heads=heads,
153 153 common=common, bundlecaps=bundlecaps, **kwargs)
154 154 if bundlecaps is not None and 'HG20' in bundlecaps:
155 155 # When requesting a bundle2, getbundle returns a stream to make the
156 156 # wire level function happier. We need to build a proper object
157 157 # from it in local peer.
158 158 cg = bundle2.getunbundler(self.ui, cg)
159 159 return cg
160 160
161 161 # TODO We might want to move the next two calls into legacypeer and add
162 162 # unbundle instead.
163 163
164 164 def unbundle(self, cg, heads, url):
165 165 """apply a bundle on a repo
166 166
167 167 This function handles the repo locking itself."""
168 168 try:
169 169 try:
170 170 cg = exchange.readbundle(self.ui, cg, None)
171 171 ret = exchange.unbundle(self._repo, cg, heads, 'push', url)
172 172 if util.safehasattr(ret, 'getchunks'):
173 173 # This is a bundle20 object, turn it into an unbundler.
174 174 # This little dance should be dropped eventually when the
175 175 # API is finally improved.
176 176 stream = util.chunkbuffer(ret.getchunks())
177 177 ret = bundle2.getunbundler(self.ui, stream)
178 178 return ret
179 179 except Exception as exc:
180 180 # If the exception contains output salvaged from a bundle2
181 181 # reply, we need to make sure it is printed before continuing
182 182 # to fail. So we build a bundle2 with such output and consume
183 183 # it directly.
184 184 #
185 185 # This is not very elegant but allows a "simple" solution for
186 186 # issue4594
187 187 output = getattr(exc, '_bundle2salvagedoutput', ())
188 188 if output:
189 189 bundler = bundle2.bundle20(self._repo.ui)
190 190 for out in output:
191 191 bundler.addpart(out)
192 192 stream = util.chunkbuffer(bundler.getchunks())
193 193 b = bundle2.getunbundler(self.ui, stream)
194 194 bundle2.processbundle(self._repo, b)
195 195 raise
196 196 except error.PushRaced as exc:
197 197 raise error.ResponseError(_('push failed:'), str(exc))
198 198
199 199 def lock(self):
200 200 return self._repo.lock()
201 201
202 202 def addchangegroup(self, cg, source, url):
203 203 return cg.apply(self._repo, source, url)
204 204
205 205 def pushkey(self, namespace, key, old, new):
206 206 return self._repo.pushkey(namespace, key, old, new)
207 207
208 208 def listkeys(self, namespace):
209 209 return self._repo.listkeys(namespace)
210 210
211 211 def debugwireargs(self, one, two, three=None, four=None, five=None):
212 212 '''used to test argument passing over the wire'''
213 213 return "%s %s %s %s %s" % (one, two, three, four, five)
214 214
215 215 class locallegacypeer(localpeer):
216 216 '''peer extension which implements legacy methods too; used for tests with
217 217 restricted capabilities'''
218 218
219 219 def __init__(self, repo):
220 220 localpeer.__init__(self, repo, caps=legacycaps)
221 221
222 222 def branches(self, nodes):
223 223 return self._repo.branches(nodes)
224 224
225 225 def between(self, pairs):
226 226 return self._repo.between(pairs)
227 227
228 228 def changegroup(self, basenodes, source):
229 229 return changegroup.changegroup(self._repo, basenodes, source)
230 230
231 231 def changegroupsubset(self, bases, heads, source):
232 232 return changegroup.changegroupsubset(self._repo, bases, heads, source)
233 233
234 234 class localrepository(object):
235 235
236 236 supportedformats = set(('revlogv1', 'generaldelta', 'treemanifest',
237 237 'manifestv2'))
238 238 _basesupported = supportedformats | set(('store', 'fncache', 'shared',
239 239 'dotencode'))
240 240 openerreqs = set(('revlogv1', 'generaldelta', 'treemanifest', 'manifestv2'))
241 241 filtername = None
242 242
243 243 # a list of (ui, featureset) functions.
244 244 # only functions defined in module of enabled extensions are invoked
245 245 featuresetupfuncs = set()
246 246
247 247 def __init__(self, baseui, path=None, create=False):
248 248 self.requirements = set()
249 249 self.wvfs = scmutil.vfs(path, expandpath=True, realpath=True)
250 250 self.wopener = self.wvfs
251 251 self.root = self.wvfs.base
252 252 self.path = self.wvfs.join(".hg")
253 253 self.origroot = path
254 254 self.auditor = pathutil.pathauditor(self.root, self._checknested)
255 255 self.nofsauditor = pathutil.pathauditor(self.root, self._checknested,
256 256 realfs=False)
257 257 self.vfs = scmutil.vfs(self.path)
258 258 self.opener = self.vfs
259 259 self.baseui = baseui
260 260 self.ui = baseui.copy()
261 261 self.ui.copy = baseui.copy # prevent copying repo configuration
262 262 # A list of callback to shape the phase if no data were found.
263 263 # Callback are in the form: func(repo, roots) --> processed root.
264 264 # This list it to be filled by extension during repo setup
265 265 self._phasedefaults = []
266 266 try:
267 267 self.ui.readconfig(self.join("hgrc"), self.root)
268 268 extensions.loadall(self.ui)
269 269 except IOError:
270 270 pass
271 271
272 272 if self.featuresetupfuncs:
273 273 self.supported = set(self._basesupported) # use private copy
274 274 extmods = set(m.__name__ for n, m
275 275 in extensions.extensions(self.ui))
276 276 for setupfunc in self.featuresetupfuncs:
277 277 if setupfunc.__module__ in extmods:
278 278 setupfunc(self.ui, self.supported)
279 279 else:
280 280 self.supported = self._basesupported
281 281
282 282 if not self.vfs.isdir():
283 283 if create:
284 284 self.requirements = newreporequirements(self)
285 285
286 286 if not self.wvfs.exists():
287 287 self.wvfs.makedirs()
288 288 self.vfs.makedir(notindexed=True)
289 289
290 290 if 'store' in self.requirements:
291 291 self.vfs.mkdir("store")
292 292
293 293 # create an invalid changelog
294 294 self.vfs.append(
295 295 "00changelog.i",
296 296 '\0\0\0\2' # represents revlogv2
297 297 ' dummy changelog to prevent using the old repo layout'
298 298 )
299 299 else:
300 300 raise error.RepoError(_("repository %s not found") % path)
301 301 elif create:
302 302 raise error.RepoError(_("repository %s already exists") % path)
303 303 else:
304 304 try:
305 305 self.requirements = scmutil.readrequires(
306 306 self.vfs, self.supported)
307 307 except IOError as inst:
308 308 if inst.errno != errno.ENOENT:
309 309 raise
310 310
311 311 self.sharedpath = self.path
312 312 try:
313 313 vfs = scmutil.vfs(self.vfs.read("sharedpath").rstrip('\n'),
314 314 realpath=True)
315 315 s = vfs.base
316 316 if not vfs.exists():
317 317 raise error.RepoError(
318 318 _('.hg/sharedpath points to nonexistent directory %s') % s)
319 319 self.sharedpath = s
320 320 except IOError as inst:
321 321 if inst.errno != errno.ENOENT:
322 322 raise
323 323
324 324 self.store = store.store(
325 325 self.requirements, self.sharedpath, scmutil.vfs)
326 326 self.spath = self.store.path
327 327 self.svfs = self.store.vfs
328 328 self.sjoin = self.store.join
329 329 self.vfs.createmode = self.store.createmode
330 330 self._applyopenerreqs()
331 331 if create:
332 332 self._writerequirements()
333 333
334 334 self._dirstatevalidatewarned = False
335 335
336 336 self._branchcaches = {}
337 337 self._revbranchcache = None
338 338 self.filterpats = {}
339 339 self._datafilters = {}
340 340 self._transref = self._lockref = self._wlockref = None
341 341
342 342 # A cache for various files under .hg/ that tracks file changes,
343 343 # (used by the filecache decorator)
344 344 #
345 345 # Maps a property name to its util.filecacheentry
346 346 self._filecache = {}
347 347
348 348 # hold sets of revision to be filtered
349 349 # should be cleared when something might have changed the filter value:
350 350 # - new changesets,
351 351 # - phase change,
352 352 # - new obsolescence marker,
353 353 # - working directory parent change,
354 354 # - bookmark changes
355 355 self.filteredrevcache = {}
356 356
357 357 # generic mapping between names and nodes
358 358 self.names = namespaces.namespaces()
359 359
360 360 def close(self):
361 361 self._writecaches()
362 362
363 363 def _writecaches(self):
364 364 if self._revbranchcache:
365 365 self._revbranchcache.write()
366 366
367 367 def _restrictcapabilities(self, caps):
368 368 if self.ui.configbool('experimental', 'bundle2-advertise', True):
369 369 caps = set(caps)
370 370 capsblob = bundle2.encodecaps(bundle2.getrepocaps(self))
371 371 caps.add('bundle2=' + urlreq.quote(capsblob))
372 372 return caps
373 373
374 374 def _applyopenerreqs(self):
375 375 self.svfs.options = dict((r, 1) for r in self.requirements
376 376 if r in self.openerreqs)
377 377 # experimental config: format.chunkcachesize
378 378 chunkcachesize = self.ui.configint('format', 'chunkcachesize')
379 379 if chunkcachesize is not None:
380 380 self.svfs.options['chunkcachesize'] = chunkcachesize
381 381 # experimental config: format.maxchainlen
382 382 maxchainlen = self.ui.configint('format', 'maxchainlen')
383 383 if maxchainlen is not None:
384 384 self.svfs.options['maxchainlen'] = maxchainlen
385 385 # experimental config: format.manifestcachesize
386 386 manifestcachesize = self.ui.configint('format', 'manifestcachesize')
387 387 if manifestcachesize is not None:
388 388 self.svfs.options['manifestcachesize'] = manifestcachesize
389 389 # experimental config: format.aggressivemergedeltas
390 390 aggressivemergedeltas = self.ui.configbool('format',
391 391 'aggressivemergedeltas', False)
392 392 self.svfs.options['aggressivemergedeltas'] = aggressivemergedeltas
393 393 self.svfs.options['lazydeltabase'] = not scmutil.gddeltaconfig(self.ui)
394 394
395 395 def _writerequirements(self):
396 396 scmutil.writerequires(self.vfs, self.requirements)
397 397
398 398 def _checknested(self, path):
399 399 """Determine if path is a legal nested repository."""
400 400 if not path.startswith(self.root):
401 401 return False
402 402 subpath = path[len(self.root) + 1:]
403 403 normsubpath = util.pconvert(subpath)
404 404
405 405 # XXX: Checking against the current working copy is wrong in
406 406 # the sense that it can reject things like
407 407 #
408 408 # $ hg cat -r 10 sub/x.txt
409 409 #
410 410 # if sub/ is no longer a subrepository in the working copy
411 411 # parent revision.
412 412 #
413 413 # However, it can of course also allow things that would have
414 414 # been rejected before, such as the above cat command if sub/
415 415 # is a subrepository now, but was a normal directory before.
416 416 # The old path auditor would have rejected by mistake since it
417 417 # panics when it sees sub/.hg/.
418 418 #
419 419 # All in all, checking against the working copy seems sensible
420 420 # since we want to prevent access to nested repositories on
421 421 # the filesystem *now*.
422 422 ctx = self[None]
423 423 parts = util.splitpath(subpath)
424 424 while parts:
425 425 prefix = '/'.join(parts)
426 426 if prefix in ctx.substate:
427 427 if prefix == normsubpath:
428 428 return True
429 429 else:
430 430 sub = ctx.sub(prefix)
431 431 return sub.checknested(subpath[len(prefix) + 1:])
432 432 else:
433 433 parts.pop()
434 434 return False
435 435
436 436 def peer(self):
437 437 return localpeer(self) # not cached to avoid reference cycle
438 438
439 439 def unfiltered(self):
440 440 """Return unfiltered version of the repository
441 441
442 442 Intended to be overwritten by filtered repo."""
443 443 return self
444 444
445 445 def filtered(self, name):
446 446 """Return a filtered version of a repository"""
447 447 # build a new class with the mixin and the current class
448 448 # (possibly subclass of the repo)
449 449 class proxycls(repoview.repoview, self.unfiltered().__class__):
450 450 pass
451 451 return proxycls(self, name)
452 452
453 453 @repofilecache('bookmarks', 'bookmarks.current')
454 454 def _bookmarks(self):
455 455 return bookmarks.bmstore(self)
456 456
457 457 @property
458 458 def _activebookmark(self):
459 459 return self._bookmarks.active
460 460
461 461 def bookmarkheads(self, bookmark):
462 462 name = bookmark.split('@', 1)[0]
463 463 heads = []
464 464 for mark, n in self._bookmarks.iteritems():
465 465 if mark.split('@', 1)[0] == name:
466 466 heads.append(n)
467 467 return heads
468 468
469 469 # _phaserevs and _phasesets depend on changelog. what we need is to
470 470 # call _phasecache.invalidate() if '00changelog.i' was changed, but it
471 471 # can't be easily expressed in filecache mechanism.
472 472 @storecache('phaseroots', '00changelog.i')
473 473 def _phasecache(self):
474 474 return phases.phasecache(self, self._phasedefaults)
475 475
476 476 @storecache('obsstore')
477 477 def obsstore(self):
478 478 # read default format for new obsstore.
479 479 # developer config: format.obsstore-version
480 480 defaultformat = self.ui.configint('format', 'obsstore-version', None)
481 481 # rely on obsstore class default when possible.
482 482 kwargs = {}
483 483 if defaultformat is not None:
484 484 kwargs['defaultformat'] = defaultformat
485 485 readonly = not obsolete.isenabled(self, obsolete.createmarkersopt)
486 486 store = obsolete.obsstore(self.svfs, readonly=readonly,
487 487 **kwargs)
488 488 if store and readonly:
489 489 self.ui.warn(
490 490 _('obsolete feature not enabled but %i markers found!\n')
491 491 % len(list(store)))
492 492 return store
493 493
494 494 @storecache('00changelog.i')
495 495 def changelog(self):
496 496 c = changelog.changelog(self.svfs)
497 497 if 'HG_PENDING' in os.environ:
498 498 p = os.environ['HG_PENDING']
499 499 if p.startswith(self.root):
500 500 c.readpending('00changelog.i.a')
501 501 return c
502 502
503 503 @storecache('00manifest.i')
504 504 def manifest(self):
505 505 return manifest.manifest(self.svfs)
506 506
507 507 @property
508 508 def manifestlog(self):
509 509 return manifest.manifestlog(self.svfs, self)
510 510
511 511 @repofilecache('dirstate')
512 512 def dirstate(self):
513 513 return dirstate.dirstate(self.vfs, self.ui, self.root,
514 514 self._dirstatevalidate)
515 515
516 516 def _dirstatevalidate(self, node):
517 517 try:
518 518 self.changelog.rev(node)
519 519 return node
520 520 except error.LookupError:
521 521 if not self._dirstatevalidatewarned:
522 522 self._dirstatevalidatewarned = True
523 523 self.ui.warn(_("warning: ignoring unknown"
524 524 " working parent %s!\n") % short(node))
525 525 return nullid
526 526
527 527 def __getitem__(self, changeid):
528 528 if changeid is None or changeid == wdirrev:
529 529 return context.workingctx(self)
530 530 if isinstance(changeid, slice):
531 531 return [context.changectx(self, i)
532 532 for i in xrange(*changeid.indices(len(self)))
533 533 if i not in self.changelog.filteredrevs]
534 534 return context.changectx(self, changeid)
535 535
536 536 def __contains__(self, changeid):
537 537 try:
538 538 self[changeid]
539 539 return True
540 540 except error.RepoLookupError:
541 541 return False
542 542
543 543 def __nonzero__(self):
544 544 return True
545 545
546 546 def __len__(self):
547 547 return len(self.changelog)
548 548
549 549 def __iter__(self):
550 550 return iter(self.changelog)
551 551
552 552 def revs(self, expr, *args):
553 553 '''Find revisions matching a revset.
554 554
555 555 The revset is specified as a string ``expr`` that may contain
556 556 %-formatting to escape certain types. See ``revset.formatspec``.
557 557
558 558 Revset aliases from the configuration are not expanded. To expand
559 559 user aliases, consider calling ``scmutil.revrange()``.
560 560
561 561 Returns a revset.abstractsmartset, which is a list-like interface
562 562 that contains integer revisions.
563 563 '''
564 564 expr = revset.formatspec(expr, *args)
565 565 m = revset.match(None, expr)
566 566 return m(self)
567 567
568 568 def set(self, expr, *args):
569 569 '''Find revisions matching a revset and emit changectx instances.
570 570
571 571 This is a convenience wrapper around ``revs()`` that iterates the
572 572 result and is a generator of changectx instances.
573 573
574 574 Revset aliases from the configuration are not expanded. To expand
575 575 user aliases, consider calling ``scmutil.revrange()``.
576 576 '''
577 577 for r in self.revs(expr, *args):
578 578 yield self[r]
579 579
580 580 def url(self):
581 581 return 'file:' + self.root
582 582
583 583 def hook(self, name, throw=False, **args):
584 584 """Call a hook, passing this repo instance.
585 585
586 586 This a convenience method to aid invoking hooks. Extensions likely
587 587 won't call this unless they have registered a custom hook or are
588 588 replacing code that is expected to call a hook.
589 589 """
590 590 return hook.hook(self.ui, self, name, throw, **args)
591 591
592 592 @unfilteredmethod
593 593 def _tag(self, names, node, message, local, user, date, extra=None,
594 594 editor=False):
595 595 if isinstance(names, str):
596 596 names = (names,)
597 597
598 598 branches = self.branchmap()
599 599 for name in names:
600 600 self.hook('pretag', throw=True, node=hex(node), tag=name,
601 601 local=local)
602 602 if name in branches:
603 603 self.ui.warn(_("warning: tag %s conflicts with existing"
604 604 " branch name\n") % name)
605 605
606 606 def writetags(fp, names, munge, prevtags):
607 607 fp.seek(0, 2)
608 608 if prevtags and prevtags[-1] != '\n':
609 609 fp.write('\n')
610 610 for name in names:
611 611 if munge:
612 612 m = munge(name)
613 613 else:
614 614 m = name
615 615
616 616 if (self._tagscache.tagtypes and
617 617 name in self._tagscache.tagtypes):
618 618 old = self.tags().get(name, nullid)
619 619 fp.write('%s %s\n' % (hex(old), m))
620 620 fp.write('%s %s\n' % (hex(node), m))
621 621 fp.close()
622 622
623 623 prevtags = ''
624 624 if local:
625 625 try:
626 626 fp = self.vfs('localtags', 'r+')
627 627 except IOError:
628 628 fp = self.vfs('localtags', 'a')
629 629 else:
630 630 prevtags = fp.read()
631 631
632 632 # local tags are stored in the current charset
633 633 writetags(fp, names, None, prevtags)
634 634 for name in names:
635 635 self.hook('tag', node=hex(node), tag=name, local=local)
636 636 return
637 637
638 638 try:
639 639 fp = self.wfile('.hgtags', 'rb+')
640 640 except IOError as e:
641 641 if e.errno != errno.ENOENT:
642 642 raise
643 643 fp = self.wfile('.hgtags', 'ab')
644 644 else:
645 645 prevtags = fp.read()
646 646
647 647 # committed tags are stored in UTF-8
648 648 writetags(fp, names, encoding.fromlocal, prevtags)
649 649
650 650 fp.close()
651 651
652 652 self.invalidatecaches()
653 653
654 654 if '.hgtags' not in self.dirstate:
655 655 self[None].add(['.hgtags'])
656 656
657 657 m = matchmod.exact(self.root, '', ['.hgtags'])
658 658 tagnode = self.commit(message, user, date, extra=extra, match=m,
659 659 editor=editor)
660 660
661 661 for name in names:
662 662 self.hook('tag', node=hex(node), tag=name, local=local)
663 663
664 664 return tagnode
665 665
666 666 def tag(self, names, node, message, local, user, date, editor=False):
667 667 '''tag a revision with one or more symbolic names.
668 668
669 669 names is a list of strings or, when adding a single tag, names may be a
670 670 string.
671 671
672 672 if local is True, the tags are stored in a per-repository file.
673 673 otherwise, they are stored in the .hgtags file, and a new
674 674 changeset is committed with the change.
675 675
676 676 keyword arguments:
677 677
678 678 local: whether to store tags in non-version-controlled file
679 679 (default False)
680 680
681 681 message: commit message to use if committing
682 682
683 683 user: name of user to use if committing
684 684
685 685 date: date tuple to use if committing'''
686 686
687 687 if not local:
688 688 m = matchmod.exact(self.root, '', ['.hgtags'])
689 689 if any(self.status(match=m, unknown=True, ignored=True)):
690 690 raise error.Abort(_('working copy of .hgtags is changed'),
691 691 hint=_('please commit .hgtags manually'))
692 692
693 693 self.tags() # instantiate the cache
694 694 self._tag(names, node, message, local, user, date, editor=editor)
695 695
696 696 @filteredpropertycache
697 697 def _tagscache(self):
698 698 '''Returns a tagscache object that contains various tags related
699 699 caches.'''
700 700
701 701 # This simplifies its cache management by having one decorated
702 702 # function (this one) and the rest simply fetch things from it.
703 703 class tagscache(object):
704 704 def __init__(self):
705 705 # These two define the set of tags for this repository. tags
706 706 # maps tag name to node; tagtypes maps tag name to 'global' or
707 707 # 'local'. (Global tags are defined by .hgtags across all
708 708 # heads, and local tags are defined in .hg/localtags.)
709 709 # They constitute the in-memory cache of tags.
710 710 self.tags = self.tagtypes = None
711 711
712 712 self.nodetagscache = self.tagslist = None
713 713
714 714 cache = tagscache()
715 715 cache.tags, cache.tagtypes = self._findtags()
716 716
717 717 return cache
718 718
719 719 def tags(self):
720 720 '''return a mapping of tag to node'''
721 721 t = {}
722 722 if self.changelog.filteredrevs:
723 723 tags, tt = self._findtags()
724 724 else:
725 725 tags = self._tagscache.tags
726 726 for k, v in tags.iteritems():
727 727 try:
728 728 # ignore tags to unknown nodes
729 729 self.changelog.rev(v)
730 730 t[k] = v
731 731 except (error.LookupError, ValueError):
732 732 pass
733 733 return t
734 734
735 735 def _findtags(self):
736 736 '''Do the hard work of finding tags. Return a pair of dicts
737 737 (tags, tagtypes) where tags maps tag name to node, and tagtypes
738 738 maps tag name to a string like \'global\' or \'local\'.
739 739 Subclasses or extensions are free to add their own tags, but
740 740 should be aware that the returned dicts will be retained for the
741 741 duration of the localrepo object.'''
742 742
743 743 # XXX what tagtype should subclasses/extensions use? Currently
744 744 # mq and bookmarks add tags, but do not set the tagtype at all.
745 745 # Should each extension invent its own tag type? Should there
746 746 # be one tagtype for all such "virtual" tags? Or is the status
747 747 # quo fine?
748 748
749 749 alltags = {} # map tag name to (node, hist)
750 750 tagtypes = {}
751 751
752 752 tagsmod.findglobaltags(self.ui, self, alltags, tagtypes)
753 753 tagsmod.readlocaltags(self.ui, self, alltags, tagtypes)
754 754
755 755 # Build the return dicts. Have to re-encode tag names because
756 756 # the tags module always uses UTF-8 (in order not to lose info
757 757 # writing to the cache), but the rest of Mercurial wants them in
758 758 # local encoding.
759 759 tags = {}
760 760 for (name, (node, hist)) in alltags.iteritems():
761 761 if node != nullid:
762 762 tags[encoding.tolocal(name)] = node
763 763 tags['tip'] = self.changelog.tip()
764 764 tagtypes = dict([(encoding.tolocal(name), value)
765 765 for (name, value) in tagtypes.iteritems()])
766 766 return (tags, tagtypes)
767 767
768 768 def tagtype(self, tagname):
769 769 '''
770 770 return the type of the given tag. result can be:
771 771
772 772 'local' : a local tag
773 773 'global' : a global tag
774 774 None : tag does not exist
775 775 '''
776 776
777 777 return self._tagscache.tagtypes.get(tagname)
778 778
779 779 def tagslist(self):
780 780 '''return a list of tags ordered by revision'''
781 781 if not self._tagscache.tagslist:
782 782 l = []
783 783 for t, n in self.tags().iteritems():
784 784 l.append((self.changelog.rev(n), t, n))
785 785 self._tagscache.tagslist = [(t, n) for r, t, n in sorted(l)]
786 786
787 787 return self._tagscache.tagslist
788 788
789 789 def nodetags(self, node):
790 790 '''return the tags associated with a node'''
791 791 if not self._tagscache.nodetagscache:
792 792 nodetagscache = {}
793 793 for t, n in self._tagscache.tags.iteritems():
794 794 nodetagscache.setdefault(n, []).append(t)
795 795 for tags in nodetagscache.itervalues():
796 796 tags.sort()
797 797 self._tagscache.nodetagscache = nodetagscache
798 798 return self._tagscache.nodetagscache.get(node, [])
799 799
800 800 def nodebookmarks(self, node):
801 801 """return the list of bookmarks pointing to the specified node"""
802 802 marks = []
803 803 for bookmark, n in self._bookmarks.iteritems():
804 804 if n == node:
805 805 marks.append(bookmark)
806 806 return sorted(marks)
807 807
808 808 def branchmap(self):
809 809 '''returns a dictionary {branch: [branchheads]} with branchheads
810 810 ordered by increasing revision number'''
811 811 branchmap.updatecache(self)
812 812 return self._branchcaches[self.filtername]
813 813
814 814 @unfilteredmethod
815 815 def revbranchcache(self):
816 816 if not self._revbranchcache:
817 817 self._revbranchcache = branchmap.revbranchcache(self.unfiltered())
818 818 return self._revbranchcache
819 819
820 820 def branchtip(self, branch, ignoremissing=False):
821 821 '''return the tip node for a given branch
822 822
823 823 If ignoremissing is True, then this method will not raise an error.
824 824 This is helpful for callers that only expect None for a missing branch
825 825 (e.g. namespace).
826 826
827 827 '''
828 828 try:
829 829 return self.branchmap().branchtip(branch)
830 830 except KeyError:
831 831 if not ignoremissing:
832 832 raise error.RepoLookupError(_("unknown branch '%s'") % branch)
833 833 else:
834 834 pass
835 835
836 836 def lookup(self, key):
837 837 return self[key].node()
838 838
839 839 def lookupbranch(self, key, remote=None):
840 840 repo = remote or self
841 841 if key in repo.branchmap():
842 842 return key
843 843
844 844 repo = (remote and remote.local()) and remote or self
845 845 return repo[key].branch()
846 846
847 847 def known(self, nodes):
848 848 cl = self.changelog
849 849 nm = cl.nodemap
850 850 filtered = cl.filteredrevs
851 851 result = []
852 852 for n in nodes:
853 853 r = nm.get(n)
854 854 resp = not (r is None or r in filtered)
855 855 result.append(resp)
856 856 return result
857 857
858 858 def local(self):
859 859 return self
860 860
861 861 def publishing(self):
862 862 # it's safe (and desirable) to trust the publish flag unconditionally
863 863 # so that we don't finalize changes shared between users via ssh or nfs
864 864 return self.ui.configbool('phases', 'publish', True, untrusted=True)
865 865
866 866 def cancopy(self):
867 867 # so statichttprepo's override of local() works
868 868 if not self.local():
869 869 return False
870 870 if not self.publishing():
871 871 return True
872 872 # if publishing we can't copy if there is filtered content
873 873 return not self.filtered('visible').changelog.filteredrevs
874 874
875 875 def shared(self):
876 876 '''the type of shared repository (None if not shared)'''
877 877 if self.sharedpath != self.path:
878 878 return 'store'
879 879 return None
880 880
881 881 def join(self, f, *insidef):
882 882 return self.vfs.join(os.path.join(f, *insidef))
883 883
884 884 def wjoin(self, f, *insidef):
885 885 return self.vfs.reljoin(self.root, f, *insidef)
886 886
887 887 def file(self, f):
888 888 if f[0] == '/':
889 889 f = f[1:]
890 890 return filelog.filelog(self.svfs, f)
891 891
892 892 def changectx(self, changeid):
893 893 return self[changeid]
894 894
895 895 def setparents(self, p1, p2=nullid):
896 896 self.dirstate.beginparentchange()
897 897 copies = self.dirstate.setparents(p1, p2)
898 898 pctx = self[p1]
899 899 if copies:
900 900 # Adjust copy records, the dirstate cannot do it, it
901 901 # requires access to parents manifests. Preserve them
902 902 # only for entries added to first parent.
903 903 for f in copies:
904 904 if f not in pctx and copies[f] in pctx:
905 905 self.dirstate.copy(copies[f], f)
906 906 if p2 == nullid:
907 907 for f, s in sorted(self.dirstate.copies().items()):
908 908 if f not in pctx and s not in pctx:
909 909 self.dirstate.copy(None, f)
910 910 self.dirstate.endparentchange()
911 911
912 912 def filectx(self, path, changeid=None, fileid=None):
913 913 """changeid can be a changeset revision, node, or tag.
914 914 fileid can be a file revision or node."""
915 915 return context.filectx(self, path, changeid, fileid)
916 916
917 917 def getcwd(self):
918 918 return self.dirstate.getcwd()
919 919
920 920 def pathto(self, f, cwd=None):
921 921 return self.dirstate.pathto(f, cwd)
922 922
923 923 def wfile(self, f, mode='r'):
924 924 return self.wvfs(f, mode)
925 925
926 926 def _link(self, f):
927 927 return self.wvfs.islink(f)
928 928
929 929 def _loadfilter(self, filter):
930 930 if filter not in self.filterpats:
931 931 l = []
932 932 for pat, cmd in self.ui.configitems(filter):
933 933 if cmd == '!':
934 934 continue
935 935 mf = matchmod.match(self.root, '', [pat])
936 936 fn = None
937 937 params = cmd
938 938 for name, filterfn in self._datafilters.iteritems():
939 939 if cmd.startswith(name):
940 940 fn = filterfn
941 941 params = cmd[len(name):].lstrip()
942 942 break
943 943 if not fn:
944 944 fn = lambda s, c, **kwargs: util.filter(s, c)
945 945 # Wrap old filters not supporting keyword arguments
946 946 if not inspect.getargspec(fn)[2]:
947 947 oldfn = fn
948 948 fn = lambda s, c, **kwargs: oldfn(s, c)
949 949 l.append((mf, fn, params))
950 950 self.filterpats[filter] = l
951 951 return self.filterpats[filter]
952 952
953 953 def _filter(self, filterpats, filename, data):
954 954 for mf, fn, cmd in filterpats:
955 955 if mf(filename):
956 956 self.ui.debug("filtering %s through %s\n" % (filename, cmd))
957 957 data = fn(data, cmd, ui=self.ui, repo=self, filename=filename)
958 958 break
959 959
960 960 return data
961 961
962 962 @unfilteredpropertycache
963 963 def _encodefilterpats(self):
964 964 return self._loadfilter('encode')
965 965
966 966 @unfilteredpropertycache
967 967 def _decodefilterpats(self):
968 968 return self._loadfilter('decode')
969 969
970 970 def adddatafilter(self, name, filter):
971 971 self._datafilters[name] = filter
972 972
973 973 def wread(self, filename):
974 974 if self._link(filename):
975 975 data = self.wvfs.readlink(filename)
976 976 else:
977 977 data = self.wvfs.read(filename)
978 978 return self._filter(self._encodefilterpats, filename, data)
979 979
980 980 def wwrite(self, filename, data, flags, backgroundclose=False):
981 981 """write ``data`` into ``filename`` in the working directory
982 982
983 983 This returns length of written (maybe decoded) data.
984 984 """
985 985 data = self._filter(self._decodefilterpats, filename, data)
986 986 if 'l' in flags:
987 987 self.wvfs.symlink(data, filename)
988 988 else:
989 989 self.wvfs.write(filename, data, backgroundclose=backgroundclose)
990 990 if 'x' in flags:
991 991 self.wvfs.setflags(filename, False, True)
992 992 return len(data)
993 993
994 994 def wwritedata(self, filename, data):
995 995 return self._filter(self._decodefilterpats, filename, data)
996 996
997 997 def currenttransaction(self):
998 998 """return the current transaction or None if non exists"""
999 999 if self._transref:
1000 1000 tr = self._transref()
1001 1001 else:
1002 1002 tr = None
1003 1003
1004 1004 if tr and tr.running():
1005 1005 return tr
1006 1006 return None
1007 1007
1008 1008 def transaction(self, desc, report=None):
1009 1009 if (self.ui.configbool('devel', 'all-warnings')
1010 1010 or self.ui.configbool('devel', 'check-locks')):
1011 1011 if self._currentlock(self._lockref) is None:
1012 1012 raise RuntimeError('programming error: transaction requires '
1013 1013 'locking')
1014 1014 tr = self.currenttransaction()
1015 1015 if tr is not None:
1016 1016 return tr.nest()
1017 1017
1018 1018 # abort here if the journal already exists
1019 1019 if self.svfs.exists("journal"):
1020 1020 raise error.RepoError(
1021 1021 _("abandoned transaction found"),
1022 1022 hint=_("run 'hg recover' to clean up transaction"))
1023 1023
1024 1024 idbase = "%.40f#%f" % (random.random(), time.time())
1025 1025 txnid = 'TXN:' + hashlib.sha1(idbase).hexdigest()
1026 1026 self.hook('pretxnopen', throw=True, txnname=desc, txnid=txnid)
1027 1027
1028 1028 self._writejournal(desc)
1029 1029 renames = [(vfs, x, undoname(x)) for vfs, x in self._journalfiles()]
1030 1030 if report:
1031 1031 rp = report
1032 1032 else:
1033 1033 rp = self.ui.warn
1034 1034 vfsmap = {'plain': self.vfs} # root of .hg/
1035 1035 # we must avoid cyclic reference between repo and transaction.
1036 1036 reporef = weakref.ref(self)
1037 1037 def validate(tr):
1038 1038 """will run pre-closing hooks"""
1039 1039 reporef().hook('pretxnclose', throw=True,
1040 1040 txnname=desc, **tr.hookargs)
1041 1041 def releasefn(tr, success):
1042 1042 repo = reporef()
1043 1043 if success:
1044 1044 # this should be explicitly invoked here, because
1045 1045 # in-memory changes aren't written out at closing
1046 1046 # transaction, if tr.addfilegenerator (via
1047 1047 # dirstate.write or so) isn't invoked while
1048 1048 # transaction running
1049 1049 repo.dirstate.write(None)
1050 1050 else:
1051 1051 # discard all changes (including ones already written
1052 1052 # out) in this transaction
1053 1053 repo.dirstate.restorebackup(None, prefix='journal.')
1054 1054
1055 1055 repo.invalidate(clearfilecache=True)
1056 1056
1057 1057 tr = transaction.transaction(rp, self.svfs, vfsmap,
1058 1058 "journal",
1059 1059 "undo",
1060 1060 aftertrans(renames),
1061 1061 self.store.createmode,
1062 1062 validator=validate,
1063 1063 releasefn=releasefn)
1064 1064
1065 1065 tr.hookargs['txnid'] = txnid
1066 1066 # note: writing the fncache only during finalize mean that the file is
1067 1067 # outdated when running hooks. As fncache is used for streaming clone,
1068 1068 # this is not expected to break anything that happen during the hooks.
1069 1069 tr.addfinalize('flush-fncache', self.store.write)
1070 1070 def txnclosehook(tr2):
1071 1071 """To be run if transaction is successful, will schedule a hook run
1072 1072 """
1073 1073 # Don't reference tr2 in hook() so we don't hold a reference.
1074 1074 # This reduces memory consumption when there are multiple
1075 1075 # transactions per lock. This can likely go away if issue5045
1076 1076 # fixes the function accumulation.
1077 1077 hookargs = tr2.hookargs
1078 1078
1079 1079 def hook():
1080 1080 reporef().hook('txnclose', throw=False, txnname=desc,
1081 1081 **hookargs)
1082 1082 reporef()._afterlock(hook)
1083 1083 tr.addfinalize('txnclose-hook', txnclosehook)
1084 1084 def txnaborthook(tr2):
1085 1085 """To be run if transaction is aborted
1086 1086 """
1087 1087 reporef().hook('txnabort', throw=False, txnname=desc,
1088 1088 **tr2.hookargs)
1089 1089 tr.addabort('txnabort-hook', txnaborthook)
1090 1090 # avoid eager cache invalidation. in-memory data should be identical
1091 1091 # to stored data if transaction has no error.
1092 1092 tr.addpostclose('refresh-filecachestats', self._refreshfilecachestats)
1093 1093 self._transref = weakref.ref(tr)
1094 1094 return tr
1095 1095
1096 1096 def _journalfiles(self):
1097 1097 return ((self.svfs, 'journal'),
1098 1098 (self.vfs, 'journal.dirstate'),
1099 1099 (self.vfs, 'journal.branch'),
1100 1100 (self.vfs, 'journal.desc'),
1101 1101 (self.vfs, 'journal.bookmarks'),
1102 1102 (self.svfs, 'journal.phaseroots'))
1103 1103
1104 1104 def undofiles(self):
1105 1105 return [(vfs, undoname(x)) for vfs, x in self._journalfiles()]
1106 1106
1107 1107 def _writejournal(self, desc):
1108 1108 self.dirstate.savebackup(None, prefix='journal.')
1109 1109 self.vfs.write("journal.branch",
1110 1110 encoding.fromlocal(self.dirstate.branch()))
1111 1111 self.vfs.write("journal.desc",
1112 1112 "%d\n%s\n" % (len(self), desc))
1113 1113 self.vfs.write("journal.bookmarks",
1114 1114 self.vfs.tryread("bookmarks"))
1115 1115 self.svfs.write("journal.phaseroots",
1116 1116 self.svfs.tryread("phaseroots"))
1117 1117
1118 1118 def recover(self):
1119 1119 with self.lock():
1120 1120 if self.svfs.exists("journal"):
1121 1121 self.ui.status(_("rolling back interrupted transaction\n"))
1122 1122 vfsmap = {'': self.svfs,
1123 1123 'plain': self.vfs,}
1124 1124 transaction.rollback(self.svfs, vfsmap, "journal",
1125 1125 self.ui.warn)
1126 1126 self.invalidate()
1127 1127 return True
1128 1128 else:
1129 1129 self.ui.warn(_("no interrupted transaction available\n"))
1130 1130 return False
1131 1131
1132 1132 def rollback(self, dryrun=False, force=False):
1133 1133 wlock = lock = dsguard = None
1134 1134 try:
1135 1135 wlock = self.wlock()
1136 1136 lock = self.lock()
1137 1137 if self.svfs.exists("undo"):
1138 1138 dsguard = cmdutil.dirstateguard(self, 'rollback')
1139 1139
1140 1140 return self._rollback(dryrun, force, dsguard)
1141 1141 else:
1142 1142 self.ui.warn(_("no rollback information available\n"))
1143 1143 return 1
1144 1144 finally:
1145 1145 release(dsguard, lock, wlock)
1146 1146
1147 1147 @unfilteredmethod # Until we get smarter cache management
1148 1148 def _rollback(self, dryrun, force, dsguard):
1149 1149 ui = self.ui
1150 1150 try:
1151 1151 args = self.vfs.read('undo.desc').splitlines()
1152 1152 (oldlen, desc, detail) = (int(args[0]), args[1], None)
1153 1153 if len(args) >= 3:
1154 1154 detail = args[2]
1155 1155 oldtip = oldlen - 1
1156 1156
1157 1157 if detail and ui.verbose:
1158 1158 msg = (_('repository tip rolled back to revision %s'
1159 1159 ' (undo %s: %s)\n')
1160 1160 % (oldtip, desc, detail))
1161 1161 else:
1162 1162 msg = (_('repository tip rolled back to revision %s'
1163 1163 ' (undo %s)\n')
1164 1164 % (oldtip, desc))
1165 1165 except IOError:
1166 1166 msg = _('rolling back unknown transaction\n')
1167 1167 desc = None
1168 1168
1169 1169 if not force and self['.'] != self['tip'] and desc == 'commit':
1170 1170 raise error.Abort(
1171 1171 _('rollback of last commit while not checked out '
1172 1172 'may lose data'), hint=_('use -f to force'))
1173 1173
1174 1174 ui.status(msg)
1175 1175 if dryrun:
1176 1176 return 0
1177 1177
1178 1178 parents = self.dirstate.parents()
1179 1179 self.destroying()
1180 1180 vfsmap = {'plain': self.vfs, '': self.svfs}
1181 1181 transaction.rollback(self.svfs, vfsmap, 'undo', ui.warn)
1182 1182 if self.vfs.exists('undo.bookmarks'):
1183 1183 self.vfs.rename('undo.bookmarks', 'bookmarks', checkambig=True)
1184 1184 if self.svfs.exists('undo.phaseroots'):
1185 1185 self.svfs.rename('undo.phaseroots', 'phaseroots', checkambig=True)
1186 1186 self.invalidate()
1187 1187
1188 1188 parentgone = (parents[0] not in self.changelog.nodemap or
1189 1189 parents[1] not in self.changelog.nodemap)
1190 1190 if parentgone:
1191 1191 # prevent dirstateguard from overwriting already restored one
1192 1192 dsguard.close()
1193 1193
1194 1194 self.dirstate.restorebackup(None, prefix='undo.')
1195 1195 try:
1196 1196 branch = self.vfs.read('undo.branch')
1197 1197 self.dirstate.setbranch(encoding.tolocal(branch))
1198 1198 except IOError:
1199 1199 ui.warn(_('named branch could not be reset: '
1200 1200 'current branch is still \'%s\'\n')
1201 1201 % self.dirstate.branch())
1202 1202
1203 1203 parents = tuple([p.rev() for p in self[None].parents()])
1204 1204 if len(parents) > 1:
1205 1205 ui.status(_('working directory now based on '
1206 1206 'revisions %d and %d\n') % parents)
1207 1207 else:
1208 1208 ui.status(_('working directory now based on '
1209 1209 'revision %d\n') % parents)
1210 1210 mergemod.mergestate.clean(self, self['.'].node())
1211 1211
1212 1212 # TODO: if we know which new heads may result from this rollback, pass
1213 1213 # them to destroy(), which will prevent the branchhead cache from being
1214 1214 # invalidated.
1215 1215 self.destroyed()
1216 1216 return 0
1217 1217
1218 1218 def invalidatecaches(self):
1219 1219
1220 1220 if '_tagscache' in vars(self):
1221 1221 # can't use delattr on proxy
1222 1222 del self.__dict__['_tagscache']
1223 1223
1224 1224 self.unfiltered()._branchcaches.clear()
1225 1225 self.invalidatevolatilesets()
1226 1226
1227 1227 def invalidatevolatilesets(self):
1228 1228 self.filteredrevcache.clear()
1229 1229 obsolete.clearobscaches(self)
1230 1230
1231 1231 def invalidatedirstate(self):
1232 1232 '''Invalidates the dirstate, causing the next call to dirstate
1233 1233 to check if it was modified since the last time it was read,
1234 1234 rereading it if it has.
1235 1235
1236 1236 This is different to dirstate.invalidate() that it doesn't always
1237 1237 rereads the dirstate. Use dirstate.invalidate() if you want to
1238 1238 explicitly read the dirstate again (i.e. restoring it to a previous
1239 1239 known good state).'''
1240 1240 if hasunfilteredcache(self, 'dirstate'):
1241 1241 for k in self.dirstate._filecache:
1242 1242 try:
1243 1243 delattr(self.dirstate, k)
1244 1244 except AttributeError:
1245 1245 pass
1246 1246 delattr(self.unfiltered(), 'dirstate')
1247 1247
1248 1248 def invalidate(self, clearfilecache=False):
1249 1249 '''Invalidates both store and non-store parts other than dirstate
1250 1250
1251 1251 If a transaction is running, invalidation of store is omitted,
1252 1252 because discarding in-memory changes might cause inconsistency
1253 1253 (e.g. incomplete fncache causes unintentional failure, but
1254 1254 redundant one doesn't).
1255 1255 '''
1256 1256 unfiltered = self.unfiltered() # all file caches are stored unfiltered
1257 1257 for k in self._filecache.keys():
1258 1258 # dirstate is invalidated separately in invalidatedirstate()
1259 1259 if k == 'dirstate':
1260 1260 continue
1261 1261
1262 1262 if clearfilecache:
1263 1263 del self._filecache[k]
1264 1264 try:
1265 1265 delattr(unfiltered, k)
1266 1266 except AttributeError:
1267 1267 pass
1268 1268 self.invalidatecaches()
1269 1269 if not self.currenttransaction():
1270 1270 # TODO: Changing contents of store outside transaction
1271 1271 # causes inconsistency. We should make in-memory store
1272 1272 # changes detectable, and abort if changed.
1273 1273 self.store.invalidatecaches()
1274 1274
1275 1275 def invalidateall(self):
1276 1276 '''Fully invalidates both store and non-store parts, causing the
1277 1277 subsequent operation to reread any outside changes.'''
1278 1278 # extension should hook this to invalidate its caches
1279 1279 self.invalidate()
1280 1280 self.invalidatedirstate()
1281 1281
1282 1282 @unfilteredmethod
1283 1283 def _refreshfilecachestats(self, tr):
1284 1284 """Reload stats of cached files so that they are flagged as valid"""
1285 1285 for k, ce in self._filecache.items():
1286 1286 if k == 'dirstate' or k not in self.__dict__:
1287 1287 continue
1288 1288 ce.refresh()
1289 1289
1290 1290 def _lock(self, vfs, lockname, wait, releasefn, acquirefn, desc,
1291 1291 inheritchecker=None, parentenvvar=None):
1292 1292 parentlock = None
1293 1293 # the contents of parentenvvar are used by the underlying lock to
1294 1294 # determine whether it can be inherited
1295 1295 if parentenvvar is not None:
1296 1296 parentlock = os.environ.get(parentenvvar)
1297 1297 try:
1298 1298 l = lockmod.lock(vfs, lockname, 0, releasefn=releasefn,
1299 1299 acquirefn=acquirefn, desc=desc,
1300 1300 inheritchecker=inheritchecker,
1301 1301 parentlock=parentlock)
1302 1302 except error.LockHeld as inst:
1303 1303 if not wait:
1304 1304 raise
1305 1305 # show more details for new-style locks
1306 1306 if ':' in inst.locker:
1307 1307 host, pid = inst.locker.split(":", 1)
1308 1308 self.ui.warn(
1309 1309 _("waiting for lock on %s held by process %r "
1310 1310 "on host %r\n") % (desc, pid, host))
1311 1311 else:
1312 1312 self.ui.warn(_("waiting for lock on %s held by %r\n") %
1313 1313 (desc, inst.locker))
1314 1314 # default to 600 seconds timeout
1315 1315 l = lockmod.lock(vfs, lockname,
1316 1316 int(self.ui.config("ui", "timeout", "600")),
1317 1317 releasefn=releasefn, acquirefn=acquirefn,
1318 1318 desc=desc)
1319 1319 self.ui.warn(_("got lock after %s seconds\n") % l.delay)
1320 1320 return l
1321 1321
1322 1322 def _afterlock(self, callback):
1323 1323 """add a callback to be run when the repository is fully unlocked
1324 1324
1325 1325 The callback will be executed when the outermost lock is released
1326 1326 (with wlock being higher level than 'lock')."""
1327 1327 for ref in (self._wlockref, self._lockref):
1328 1328 l = ref and ref()
1329 1329 if l and l.held:
1330 1330 l.postrelease.append(callback)
1331 1331 break
1332 1332 else: # no lock have been found.
1333 1333 callback()
1334 1334
1335 1335 def lock(self, wait=True):
1336 1336 '''Lock the repository store (.hg/store) and return a weak reference
1337 1337 to the lock. Use this before modifying the store (e.g. committing or
1338 1338 stripping). If you are opening a transaction, get a lock as well.)
1339 1339
1340 1340 If both 'lock' and 'wlock' must be acquired, ensure you always acquires
1341 1341 'wlock' first to avoid a dead-lock hazard.'''
1342 1342 l = self._currentlock(self._lockref)
1343 1343 if l is not None:
1344 1344 l.lock()
1345 1345 return l
1346 1346
1347 1347 l = self._lock(self.svfs, "lock", wait, None,
1348 1348 self.invalidate, _('repository %s') % self.origroot)
1349 1349 self._lockref = weakref.ref(l)
1350 1350 return l
1351 1351
1352 1352 def _wlockchecktransaction(self):
1353 1353 if self.currenttransaction() is not None:
1354 1354 raise error.LockInheritanceContractViolation(
1355 1355 'wlock cannot be inherited in the middle of a transaction')
1356 1356
1357 1357 def wlock(self, wait=True):
1358 1358 '''Lock the non-store parts of the repository (everything under
1359 1359 .hg except .hg/store) and return a weak reference to the lock.
1360 1360
1361 1361 Use this before modifying files in .hg.
1362 1362
1363 1363 If both 'lock' and 'wlock' must be acquired, ensure you always acquires
1364 1364 'wlock' first to avoid a dead-lock hazard.'''
1365 1365 l = self._wlockref and self._wlockref()
1366 1366 if l is not None and l.held:
1367 1367 l.lock()
1368 1368 return l
1369 1369
1370 1370 # We do not need to check for non-waiting lock acquisition. Such
1371 1371 # acquisition would not cause dead-lock as they would just fail.
1372 1372 if wait and (self.ui.configbool('devel', 'all-warnings')
1373 1373 or self.ui.configbool('devel', 'check-locks')):
1374 1374 if self._currentlock(self._lockref) is not None:
1375 1375 self.ui.develwarn('"wlock" acquired after "lock"')
1376 1376
1377 1377 def unlock():
1378 1378 if self.dirstate.pendingparentchange():
1379 1379 self.dirstate.invalidate()
1380 1380 else:
1381 1381 self.dirstate.write(None)
1382 1382
1383 1383 self._filecache['dirstate'].refresh()
1384 1384
1385 1385 l = self._lock(self.vfs, "wlock", wait, unlock,
1386 1386 self.invalidatedirstate, _('working directory of %s') %
1387 1387 self.origroot,
1388 1388 inheritchecker=self._wlockchecktransaction,
1389 1389 parentenvvar='HG_WLOCK_LOCKER')
1390 1390 self._wlockref = weakref.ref(l)
1391 1391 return l
1392 1392
1393 1393 def _currentlock(self, lockref):
1394 1394 """Returns the lock if it's held, or None if it's not."""
1395 1395 if lockref is None:
1396 1396 return None
1397 1397 l = lockref()
1398 1398 if l is None or not l.held:
1399 1399 return None
1400 1400 return l
1401 1401
1402 1402 def currentwlock(self):
1403 1403 """Returns the wlock if it's held, or None if it's not."""
1404 1404 return self._currentlock(self._wlockref)
1405 1405
1406 1406 def _filecommit(self, fctx, manifest1, manifest2, linkrev, tr, changelist):
1407 1407 """
1408 1408 commit an individual file as part of a larger transaction
1409 1409 """
1410 1410
1411 1411 fname = fctx.path()
1412 1412 fparent1 = manifest1.get(fname, nullid)
1413 1413 fparent2 = manifest2.get(fname, nullid)
1414 1414 if isinstance(fctx, context.filectx):
1415 1415 node = fctx.filenode()
1416 1416 if node in [fparent1, fparent2]:
1417 1417 self.ui.debug('reusing %s filelog entry\n' % fname)
1418 1418 if manifest1.flags(fname) != fctx.flags():
1419 1419 changelist.append(fname)
1420 1420 return node
1421 1421
1422 1422 flog = self.file(fname)
1423 1423 meta = {}
1424 1424 copy = fctx.renamed()
1425 1425 if copy and copy[0] != fname:
1426 1426 # Mark the new revision of this file as a copy of another
1427 1427 # file. This copy data will effectively act as a parent
1428 1428 # of this new revision. If this is a merge, the first
1429 1429 # parent will be the nullid (meaning "look up the copy data")
1430 1430 # and the second one will be the other parent. For example:
1431 1431 #
1432 1432 # 0 --- 1 --- 3 rev1 changes file foo
1433 1433 # \ / rev2 renames foo to bar and changes it
1434 1434 # \- 2 -/ rev3 should have bar with all changes and
1435 1435 # should record that bar descends from
1436 1436 # bar in rev2 and foo in rev1
1437 1437 #
1438 1438 # this allows this merge to succeed:
1439 1439 #
1440 1440 # 0 --- 1 --- 3 rev4 reverts the content change from rev2
1441 1441 # \ / merging rev3 and rev4 should use bar@rev2
1442 1442 # \- 2 --- 4 as the merge base
1443 1443 #
1444 1444
1445 1445 cfname = copy[0]
1446 1446 crev = manifest1.get(cfname)
1447 1447 newfparent = fparent2
1448 1448
1449 1449 if manifest2: # branch merge
1450 1450 if fparent2 == nullid or crev is None: # copied on remote side
1451 1451 if cfname in manifest2:
1452 1452 crev = manifest2[cfname]
1453 1453 newfparent = fparent1
1454 1454
1455 1455 # Here, we used to search backwards through history to try to find
1456 1456 # where the file copy came from if the source of a copy was not in
1457 1457 # the parent directory. However, this doesn't actually make sense to
1458 1458 # do (what does a copy from something not in your working copy even
1459 1459 # mean?) and it causes bugs (eg, issue4476). Instead, we will warn
1460 1460 # the user that copy information was dropped, so if they didn't
1461 1461 # expect this outcome it can be fixed, but this is the correct
1462 1462 # behavior in this circumstance.
1463 1463
1464 1464 if crev:
1465 1465 self.ui.debug(" %s: copy %s:%s\n" % (fname, cfname, hex(crev)))
1466 1466 meta["copy"] = cfname
1467 1467 meta["copyrev"] = hex(crev)
1468 1468 fparent1, fparent2 = nullid, newfparent
1469 1469 else:
1470 1470 self.ui.warn(_("warning: can't find ancestor for '%s' "
1471 1471 "copied from '%s'!\n") % (fname, cfname))
1472 1472
1473 1473 elif fparent1 == nullid:
1474 1474 fparent1, fparent2 = fparent2, nullid
1475 1475 elif fparent2 != nullid:
1476 1476 # is one parent an ancestor of the other?
1477 1477 fparentancestors = flog.commonancestorsheads(fparent1, fparent2)
1478 1478 if fparent1 in fparentancestors:
1479 1479 fparent1, fparent2 = fparent2, nullid
1480 1480 elif fparent2 in fparentancestors:
1481 1481 fparent2 = nullid
1482 1482
1483 1483 # is the file changed?
1484 1484 text = fctx.data()
1485 1485 if fparent2 != nullid or flog.cmp(fparent1, text) or meta:
1486 1486 changelist.append(fname)
1487 1487 return flog.add(text, meta, tr, linkrev, fparent1, fparent2)
1488 1488 # are just the flags changed during merge?
1489 1489 elif fname in manifest1 and manifest1.flags(fname) != fctx.flags():
1490 1490 changelist.append(fname)
1491 1491
1492 1492 return fparent1
1493 1493
1494 1494 def checkcommitpatterns(self, wctx, vdirs, match, status, fail):
1495 1495 """check for commit arguments that aren't commitable"""
1496 1496 if match.isexact() or match.prefix():
1497 1497 matched = set(status.modified + status.added + status.removed)
1498 1498
1499 1499 for f in match.files():
1500 1500 f = self.dirstate.normalize(f)
1501 1501 if f == '.' or f in matched or f in wctx.substate:
1502 1502 continue
1503 1503 if f in status.deleted:
1504 1504 fail(f, _('file not found!'))
1505 1505 if f in vdirs: # visited directory
1506 1506 d = f + '/'
1507 1507 for mf in matched:
1508 1508 if mf.startswith(d):
1509 1509 break
1510 1510 else:
1511 1511 fail(f, _("no match under directory!"))
1512 1512 elif f not in self.dirstate:
1513 1513 fail(f, _("file not tracked!"))
1514 1514
1515 1515 @unfilteredmethod
1516 1516 def commit(self, text="", user=None, date=None, match=None, force=False,
1517 1517 editor=False, extra=None):
1518 1518 """Add a new revision to current repository.
1519 1519
1520 1520 Revision information is gathered from the working directory,
1521 1521 match can be used to filter the committed files. If editor is
1522 1522 supplied, it is called to get a commit message.
1523 1523 """
1524 1524 if extra is None:
1525 1525 extra = {}
1526 1526
1527 1527 def fail(f, msg):
1528 1528 raise error.Abort('%s: %s' % (f, msg))
1529 1529
1530 1530 if not match:
1531 1531 match = matchmod.always(self.root, '')
1532 1532
1533 1533 if not force:
1534 1534 vdirs = []
1535 1535 match.explicitdir = vdirs.append
1536 1536 match.bad = fail
1537 1537
1538 1538 wlock = lock = tr = None
1539 1539 try:
1540 1540 wlock = self.wlock()
1541 1541 lock = self.lock() # for recent changelog (see issue4368)
1542 1542
1543 1543 wctx = self[None]
1544 1544 merge = len(wctx.parents()) > 1
1545 1545
1546 1546 if not force and merge and match.ispartial():
1547 1547 raise error.Abort(_('cannot partially commit a merge '
1548 1548 '(do not specify files or patterns)'))
1549 1549
1550 1550 status = self.status(match=match, clean=force)
1551 1551 if force:
1552 1552 status.modified.extend(status.clean) # mq may commit clean files
1553 1553
1554 1554 # check subrepos
1555 1555 subs = []
1556 1556 commitsubs = set()
1557 1557 newstate = wctx.substate.copy()
1558 1558 # only manage subrepos and .hgsubstate if .hgsub is present
1559 1559 if '.hgsub' in wctx:
1560 1560 # we'll decide whether to track this ourselves, thanks
1561 1561 for c in status.modified, status.added, status.removed:
1562 1562 if '.hgsubstate' in c:
1563 1563 c.remove('.hgsubstate')
1564 1564
1565 1565 # compare current state to last committed state
1566 1566 # build new substate based on last committed state
1567 1567 oldstate = wctx.p1().substate
1568 1568 for s in sorted(newstate.keys()):
1569 1569 if not match(s):
1570 1570 # ignore working copy, use old state if present
1571 1571 if s in oldstate:
1572 1572 newstate[s] = oldstate[s]
1573 1573 continue
1574 1574 if not force:
1575 1575 raise error.Abort(
1576 1576 _("commit with new subrepo %s excluded") % s)
1577 1577 dirtyreason = wctx.sub(s).dirtyreason(True)
1578 1578 if dirtyreason:
1579 1579 if not self.ui.configbool('ui', 'commitsubrepos'):
1580 1580 raise error.Abort(dirtyreason,
1581 1581 hint=_("use --subrepos for recursive commit"))
1582 1582 subs.append(s)
1583 1583 commitsubs.add(s)
1584 1584 else:
1585 1585 bs = wctx.sub(s).basestate()
1586 1586 newstate[s] = (newstate[s][0], bs, newstate[s][2])
1587 1587 if oldstate.get(s, (None, None, None))[1] != bs:
1588 1588 subs.append(s)
1589 1589
1590 1590 # check for removed subrepos
1591 1591 for p in wctx.parents():
1592 1592 r = [s for s in p.substate if s not in newstate]
1593 1593 subs += [s for s in r if match(s)]
1594 1594 if subs:
1595 1595 if (not match('.hgsub') and
1596 1596 '.hgsub' in (wctx.modified() + wctx.added())):
1597 1597 raise error.Abort(
1598 1598 _("can't commit subrepos without .hgsub"))
1599 1599 status.modified.insert(0, '.hgsubstate')
1600 1600
1601 1601 elif '.hgsub' in status.removed:
1602 1602 # clean up .hgsubstate when .hgsub is removed
1603 1603 if ('.hgsubstate' in wctx and
1604 1604 '.hgsubstate' not in (status.modified + status.added +
1605 1605 status.removed)):
1606 1606 status.removed.insert(0, '.hgsubstate')
1607 1607
1608 1608 # make sure all explicit patterns are matched
1609 1609 if not force:
1610 1610 self.checkcommitpatterns(wctx, vdirs, match, status, fail)
1611 1611
1612 1612 cctx = context.workingcommitctx(self, status,
1613 1613 text, user, date, extra)
1614 1614
1615 1615 # internal config: ui.allowemptycommit
1616 1616 allowemptycommit = (wctx.branch() != wctx.p1().branch()
1617 1617 or extra.get('close') or merge or cctx.files()
1618 1618 or self.ui.configbool('ui', 'allowemptycommit'))
1619 1619 if not allowemptycommit:
1620 1620 return None
1621 1621
1622 1622 if merge and cctx.deleted():
1623 1623 raise error.Abort(_("cannot commit merge with missing files"))
1624 1624
1625 1625 ms = mergemod.mergestate.read(self)
1626 1626
1627 1627 if list(ms.unresolved()):
1628 raise error.Abort(_('unresolved merge conflicts '
1629 '(see "hg help resolve")'))
1628 raise error.Abort(_("unresolved merge conflicts "
1629 "(see 'hg help resolve')"))
1630 1630 if ms.mdstate() != 's' or list(ms.driverresolved()):
1631 1631 raise error.Abort(_('driver-resolved merge conflicts'),
1632 1632 hint=_('run "hg resolve --all" to resolve'))
1633 1633
1634 1634 if editor:
1635 1635 cctx._text = editor(self, cctx, subs)
1636 1636 edited = (text != cctx._text)
1637 1637
1638 1638 # Save commit message in case this transaction gets rolled back
1639 1639 # (e.g. by a pretxncommit hook). Leave the content alone on
1640 1640 # the assumption that the user will use the same editor again.
1641 1641 msgfn = self.savecommitmessage(cctx._text)
1642 1642
1643 1643 # commit subs and write new state
1644 1644 if subs:
1645 1645 for s in sorted(commitsubs):
1646 1646 sub = wctx.sub(s)
1647 1647 self.ui.status(_('committing subrepository %s\n') %
1648 1648 subrepo.subrelpath(sub))
1649 1649 sr = sub.commit(cctx._text, user, date)
1650 1650 newstate[s] = (newstate[s][0], sr)
1651 1651 subrepo.writestate(self, newstate)
1652 1652
1653 1653 p1, p2 = self.dirstate.parents()
1654 1654 hookp1, hookp2 = hex(p1), (p2 != nullid and hex(p2) or '')
1655 1655 try:
1656 1656 self.hook("precommit", throw=True, parent1=hookp1,
1657 1657 parent2=hookp2)
1658 1658 tr = self.transaction('commit')
1659 1659 ret = self.commitctx(cctx, True)
1660 1660 except: # re-raises
1661 1661 if edited:
1662 1662 self.ui.write(
1663 1663 _('note: commit message saved in %s\n') % msgfn)
1664 1664 raise
1665 1665 # update bookmarks, dirstate and mergestate
1666 1666 bookmarks.update(self, [p1, p2], ret)
1667 1667 cctx.markcommitted(ret)
1668 1668 ms.reset()
1669 1669 tr.close()
1670 1670
1671 1671 finally:
1672 1672 lockmod.release(tr, lock, wlock)
1673 1673
1674 1674 def commithook(node=hex(ret), parent1=hookp1, parent2=hookp2):
1675 1675 # hack for command that use a temporary commit (eg: histedit)
1676 1676 # temporary commit got stripped before hook release
1677 1677 if self.changelog.hasnode(ret):
1678 1678 self.hook("commit", node=node, parent1=parent1,
1679 1679 parent2=parent2)
1680 1680 self._afterlock(commithook)
1681 1681 return ret
1682 1682
1683 1683 @unfilteredmethod
1684 1684 def commitctx(self, ctx, error=False):
1685 1685 """Add a new revision to current repository.
1686 1686 Revision information is passed via the context argument.
1687 1687 """
1688 1688
1689 1689 tr = None
1690 1690 p1, p2 = ctx.p1(), ctx.p2()
1691 1691 user = ctx.user()
1692 1692
1693 1693 lock = self.lock()
1694 1694 try:
1695 1695 tr = self.transaction("commit")
1696 1696 trp = weakref.proxy(tr)
1697 1697
1698 1698 if ctx.files():
1699 1699 m1 = p1.manifest()
1700 1700 m2 = p2.manifest()
1701 1701 m = m1.copy()
1702 1702
1703 1703 # check in files
1704 1704 added = []
1705 1705 changed = []
1706 1706 removed = list(ctx.removed())
1707 1707 linkrev = len(self)
1708 1708 self.ui.note(_("committing files:\n"))
1709 1709 for f in sorted(ctx.modified() + ctx.added()):
1710 1710 self.ui.note(f + "\n")
1711 1711 try:
1712 1712 fctx = ctx[f]
1713 1713 if fctx is None:
1714 1714 removed.append(f)
1715 1715 else:
1716 1716 added.append(f)
1717 1717 m[f] = self._filecommit(fctx, m1, m2, linkrev,
1718 1718 trp, changed)
1719 1719 m.setflag(f, fctx.flags())
1720 1720 except OSError as inst:
1721 1721 self.ui.warn(_("trouble committing %s!\n") % f)
1722 1722 raise
1723 1723 except IOError as inst:
1724 1724 errcode = getattr(inst, 'errno', errno.ENOENT)
1725 1725 if error or errcode and errcode != errno.ENOENT:
1726 1726 self.ui.warn(_("trouble committing %s!\n") % f)
1727 1727 raise
1728 1728
1729 1729 # update manifest
1730 1730 self.ui.note(_("committing manifest\n"))
1731 1731 removed = [f for f in sorted(removed) if f in m1 or f in m2]
1732 1732 drop = [f for f in removed if f in m]
1733 1733 for f in drop:
1734 1734 del m[f]
1735 1735 mn = self.manifestlog.add(m, trp, linkrev,
1736 1736 p1.manifestnode(), p2.manifestnode(),
1737 1737 added, drop)
1738 1738 files = changed + removed
1739 1739 else:
1740 1740 mn = p1.manifestnode()
1741 1741 files = []
1742 1742
1743 1743 # update changelog
1744 1744 self.ui.note(_("committing changelog\n"))
1745 1745 self.changelog.delayupdate(tr)
1746 1746 n = self.changelog.add(mn, files, ctx.description(),
1747 1747 trp, p1.node(), p2.node(),
1748 1748 user, ctx.date(), ctx.extra().copy())
1749 1749 xp1, xp2 = p1.hex(), p2 and p2.hex() or ''
1750 1750 self.hook('pretxncommit', throw=True, node=hex(n), parent1=xp1,
1751 1751 parent2=xp2)
1752 1752 # set the new commit is proper phase
1753 1753 targetphase = subrepo.newcommitphase(self.ui, ctx)
1754 1754 if targetphase:
1755 1755 # retract boundary do not alter parent changeset.
1756 1756 # if a parent have higher the resulting phase will
1757 1757 # be compliant anyway
1758 1758 #
1759 1759 # if minimal phase was 0 we don't need to retract anything
1760 1760 phases.retractboundary(self, tr, targetphase, [n])
1761 1761 tr.close()
1762 1762 branchmap.updatecache(self.filtered('served'))
1763 1763 return n
1764 1764 finally:
1765 1765 if tr:
1766 1766 tr.release()
1767 1767 lock.release()
1768 1768
1769 1769 @unfilteredmethod
1770 1770 def destroying(self):
1771 1771 '''Inform the repository that nodes are about to be destroyed.
1772 1772 Intended for use by strip and rollback, so there's a common
1773 1773 place for anything that has to be done before destroying history.
1774 1774
1775 1775 This is mostly useful for saving state that is in memory and waiting
1776 1776 to be flushed when the current lock is released. Because a call to
1777 1777 destroyed is imminent, the repo will be invalidated causing those
1778 1778 changes to stay in memory (waiting for the next unlock), or vanish
1779 1779 completely.
1780 1780 '''
1781 1781 # When using the same lock to commit and strip, the phasecache is left
1782 1782 # dirty after committing. Then when we strip, the repo is invalidated,
1783 1783 # causing those changes to disappear.
1784 1784 if '_phasecache' in vars(self):
1785 1785 self._phasecache.write()
1786 1786
1787 1787 @unfilteredmethod
1788 1788 def destroyed(self):
1789 1789 '''Inform the repository that nodes have been destroyed.
1790 1790 Intended for use by strip and rollback, so there's a common
1791 1791 place for anything that has to be done after destroying history.
1792 1792 '''
1793 1793 # When one tries to:
1794 1794 # 1) destroy nodes thus calling this method (e.g. strip)
1795 1795 # 2) use phasecache somewhere (e.g. commit)
1796 1796 #
1797 1797 # then 2) will fail because the phasecache contains nodes that were
1798 1798 # removed. We can either remove phasecache from the filecache,
1799 1799 # causing it to reload next time it is accessed, or simply filter
1800 1800 # the removed nodes now and write the updated cache.
1801 1801 self._phasecache.filterunknown(self)
1802 1802 self._phasecache.write()
1803 1803
1804 1804 # update the 'served' branch cache to help read only server process
1805 1805 # Thanks to branchcache collaboration this is done from the nearest
1806 1806 # filtered subset and it is expected to be fast.
1807 1807 branchmap.updatecache(self.filtered('served'))
1808 1808
1809 1809 # Ensure the persistent tag cache is updated. Doing it now
1810 1810 # means that the tag cache only has to worry about destroyed
1811 1811 # heads immediately after a strip/rollback. That in turn
1812 1812 # guarantees that "cachetip == currenttip" (comparing both rev
1813 1813 # and node) always means no nodes have been added or destroyed.
1814 1814
1815 1815 # XXX this is suboptimal when qrefresh'ing: we strip the current
1816 1816 # head, refresh the tag cache, then immediately add a new head.
1817 1817 # But I think doing it this way is necessary for the "instant
1818 1818 # tag cache retrieval" case to work.
1819 1819 self.invalidate()
1820 1820
1821 1821 def walk(self, match, node=None):
1822 1822 '''
1823 1823 walk recursively through the directory tree or a given
1824 1824 changeset, finding all files matched by the match
1825 1825 function
1826 1826 '''
1827 1827 return self[node].walk(match)
1828 1828
1829 1829 def status(self, node1='.', node2=None, match=None,
1830 1830 ignored=False, clean=False, unknown=False,
1831 1831 listsubrepos=False):
1832 1832 '''a convenience method that calls node1.status(node2)'''
1833 1833 return self[node1].status(node2, match, ignored, clean, unknown,
1834 1834 listsubrepos)
1835 1835
1836 1836 def heads(self, start=None):
1837 1837 heads = self.changelog.heads(start)
1838 1838 # sort the output in rev descending order
1839 1839 return sorted(heads, key=self.changelog.rev, reverse=True)
1840 1840
1841 1841 def branchheads(self, branch=None, start=None, closed=False):
1842 1842 '''return a (possibly filtered) list of heads for the given branch
1843 1843
1844 1844 Heads are returned in topological order, from newest to oldest.
1845 1845 If branch is None, use the dirstate branch.
1846 1846 If start is not None, return only heads reachable from start.
1847 1847 If closed is True, return heads that are marked as closed as well.
1848 1848 '''
1849 1849 if branch is None:
1850 1850 branch = self[None].branch()
1851 1851 branches = self.branchmap()
1852 1852 if branch not in branches:
1853 1853 return []
1854 1854 # the cache returns heads ordered lowest to highest
1855 1855 bheads = list(reversed(branches.branchheads(branch, closed=closed)))
1856 1856 if start is not None:
1857 1857 # filter out the heads that cannot be reached from startrev
1858 1858 fbheads = set(self.changelog.nodesbetween([start], bheads)[2])
1859 1859 bheads = [h for h in bheads if h in fbheads]
1860 1860 return bheads
1861 1861
1862 1862 def branches(self, nodes):
1863 1863 if not nodes:
1864 1864 nodes = [self.changelog.tip()]
1865 1865 b = []
1866 1866 for n in nodes:
1867 1867 t = n
1868 1868 while True:
1869 1869 p = self.changelog.parents(n)
1870 1870 if p[1] != nullid or p[0] == nullid:
1871 1871 b.append((t, n, p[0], p[1]))
1872 1872 break
1873 1873 n = p[0]
1874 1874 return b
1875 1875
1876 1876 def between(self, pairs):
1877 1877 r = []
1878 1878
1879 1879 for top, bottom in pairs:
1880 1880 n, l, i = top, [], 0
1881 1881 f = 1
1882 1882
1883 1883 while n != bottom and n != nullid:
1884 1884 p = self.changelog.parents(n)[0]
1885 1885 if i == f:
1886 1886 l.append(n)
1887 1887 f = f * 2
1888 1888 n = p
1889 1889 i += 1
1890 1890
1891 1891 r.append(l)
1892 1892
1893 1893 return r
1894 1894
1895 1895 def checkpush(self, pushop):
1896 1896 """Extensions can override this function if additional checks have
1897 1897 to be performed before pushing, or call it if they override push
1898 1898 command.
1899 1899 """
1900 1900 pass
1901 1901
1902 1902 @unfilteredpropertycache
1903 1903 def prepushoutgoinghooks(self):
1904 1904 """Return util.hooks consists of a pushop with repo, remote, outgoing
1905 1905 methods, which are called before pushing changesets.
1906 1906 """
1907 1907 return util.hooks()
1908 1908
1909 1909 def pushkey(self, namespace, key, old, new):
1910 1910 try:
1911 1911 tr = self.currenttransaction()
1912 1912 hookargs = {}
1913 1913 if tr is not None:
1914 1914 hookargs.update(tr.hookargs)
1915 1915 hookargs['namespace'] = namespace
1916 1916 hookargs['key'] = key
1917 1917 hookargs['old'] = old
1918 1918 hookargs['new'] = new
1919 1919 self.hook('prepushkey', throw=True, **hookargs)
1920 1920 except error.HookAbort as exc:
1921 1921 self.ui.write_err(_("pushkey-abort: %s\n") % exc)
1922 1922 if exc.hint:
1923 1923 self.ui.write_err(_("(%s)\n") % exc.hint)
1924 1924 return False
1925 1925 self.ui.debug('pushing key for "%s:%s"\n' % (namespace, key))
1926 1926 ret = pushkey.push(self, namespace, key, old, new)
1927 1927 def runhook():
1928 1928 self.hook('pushkey', namespace=namespace, key=key, old=old, new=new,
1929 1929 ret=ret)
1930 1930 self._afterlock(runhook)
1931 1931 return ret
1932 1932
1933 1933 def listkeys(self, namespace):
1934 1934 self.hook('prelistkeys', throw=True, namespace=namespace)
1935 1935 self.ui.debug('listing keys for "%s"\n' % namespace)
1936 1936 values = pushkey.list(self, namespace)
1937 1937 self.hook('listkeys', namespace=namespace, values=values)
1938 1938 return values
1939 1939
1940 1940 def debugwireargs(self, one, two, three=None, four=None, five=None):
1941 1941 '''used to test argument passing over the wire'''
1942 1942 return "%s %s %s %s %s" % (one, two, three, four, five)
1943 1943
1944 1944 def savecommitmessage(self, text):
1945 1945 fp = self.vfs('last-message.txt', 'wb')
1946 1946 try:
1947 1947 fp.write(text)
1948 1948 finally:
1949 1949 fp.close()
1950 1950 return self.pathto(fp.name[len(self.root) + 1:])
1951 1951
1952 1952 # used to avoid circular references so destructors work
1953 1953 def aftertrans(files):
1954 1954 renamefiles = [tuple(t) for t in files]
1955 1955 def a():
1956 1956 for vfs, src, dest in renamefiles:
1957 1957 try:
1958 1958 vfs.rename(src, dest)
1959 1959 except OSError: # journal file does not yet exist
1960 1960 pass
1961 1961 return a
1962 1962
1963 1963 def undoname(fn):
1964 1964 base, name = os.path.split(fn)
1965 1965 assert name.startswith('journal')
1966 1966 return os.path.join(base, name.replace('journal', 'undo', 1))
1967 1967
1968 1968 def instance(ui, path, create):
1969 1969 return localrepository(ui, util.urllocalpath(path), create)
1970 1970
1971 1971 def islocal(path):
1972 1972 return True
1973 1973
1974 1974 def newreporequirements(repo):
1975 1975 """Determine the set of requirements for a new local repository.
1976 1976
1977 1977 Extensions can wrap this function to specify custom requirements for
1978 1978 new repositories.
1979 1979 """
1980 1980 ui = repo.ui
1981 1981 requirements = set(['revlogv1'])
1982 1982 if ui.configbool('format', 'usestore', True):
1983 1983 requirements.add('store')
1984 1984 if ui.configbool('format', 'usefncache', True):
1985 1985 requirements.add('fncache')
1986 1986 if ui.configbool('format', 'dotencode', True):
1987 1987 requirements.add('dotencode')
1988 1988
1989 1989 if scmutil.gdinitconfig(ui):
1990 1990 requirements.add('generaldelta')
1991 1991 if ui.configbool('experimental', 'treemanifest', False):
1992 1992 requirements.add('treemanifest')
1993 1993 if ui.configbool('experimental', 'manifestv2', False):
1994 1994 requirements.add('manifestv2')
1995 1995
1996 1996 return requirements
@@ -1,67 +1,67 b''
1 1 $ addcommit () {
2 2 > echo $1 > $1
3 3 > hg add $1
4 4 > hg commit -d "${2} 0" -m $1
5 5 > }
6 6
7 7 $ commit () {
8 8 > hg commit -d "${2} 0" -m $1
9 9 > }
10 10
11 11 $ hg init a
12 12 $ cd a
13 13 $ addcommit "A" 0
14 14 $ addcommit "B" 1
15 15 $ echo "C" >> A
16 16 $ commit "C" 2
17 17
18 18 $ hg update -C 0
19 19 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
20 20 $ echo "D" >> A
21 21 $ commit "D" 3
22 22 created new head
23 23
24 24 Merging a conflict araises
25 25
26 26 $ hg merge
27 27 merging A
28 28 warning: conflicts while merging A! (edit, then use 'hg resolve --mark')
29 29 1 files updated, 0 files merged, 0 files removed, 1 files unresolved
30 30 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
31 31 [1]
32 32
33 33 Correct the conflict without marking the file as resolved
34 34
35 35 $ echo "ABCD" > A
36 36 $ hg commit -m "Merged"
37 abort: unresolved merge conflicts (see "hg help resolve")
37 abort: unresolved merge conflicts (see 'hg help resolve')
38 38 [255]
39 39
40 40 Mark the conflict as resolved and commit
41 41
42 42 $ hg resolve -m A
43 43 (no more unresolved files)
44 44 $ hg commit -m "Merged"
45 45
46 46 Test that if a file is removed but not marked resolved, the commit still fails
47 47 (issue4972)
48 48
49 49 $ hg up ".^"
50 50 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
51 51 $ hg merge 2
52 52 merging A
53 53 warning: conflicts while merging A! (edit, then use 'hg resolve --mark')
54 54 1 files updated, 0 files merged, 0 files removed, 1 files unresolved
55 55 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
56 56 [1]
57 57 $ hg rm --force A
58 58 $ hg commit -m merged
59 abort: unresolved merge conflicts (see "hg help resolve")
59 abort: unresolved merge conflicts (see 'hg help resolve')
60 60 [255]
61 61
62 62 $ hg resolve -ma
63 63 (no more unresolved files)
64 64 $ hg commit -m merged
65 65 created new head
66 66
67 67 $ cd ..
@@ -1,844 +1,844 b''
1 1 $ cat >> $HGRCPATH <<EOF
2 2 > [extdiff]
3 3 > # for portability:
4 4 > pdiff = sh "$RUNTESTDIR/pdiff"
5 5 > EOF
6 6
7 7 Create a repo with some stuff in it:
8 8
9 9 $ hg init a
10 10 $ cd a
11 11 $ echo a > a
12 12 $ echo a > d
13 13 $ echo a > e
14 14 $ hg ci -qAm0
15 15 $ echo b > a
16 16 $ hg ci -m1 -u bar
17 17 $ hg mv a b
18 18 $ hg ci -m2
19 19 $ hg cp b c
20 20 $ hg ci -m3 -u baz
21 21 $ echo b > d
22 22 $ echo f > e
23 23 $ hg ci -m4
24 24 $ hg up -q 3
25 25 $ echo b > e
26 26 $ hg branch -q stable
27 27 $ hg ci -m5
28 28 $ hg merge -q default --tool internal:local
29 29 $ hg branch -q default
30 30 $ hg ci -m6
31 31 $ hg phase --public 3
32 32 $ hg phase --force --secret 6
33 33
34 34 $ hg log -G --template '{author}@{rev}.{phase}: {desc}\n'
35 35 @ test@6.secret: 6
36 36 |\
37 37 | o test@5.draft: 5
38 38 | |
39 39 o | test@4.draft: 4
40 40 |/
41 41 o baz@3.public: 3
42 42 |
43 43 o test@2.public: 2
44 44 |
45 45 o bar@1.public: 1
46 46 |
47 47 o test@0.public: 0
48 48
49 49 Can't continue without starting:
50 50
51 51 $ hg rm -q e
52 52 $ hg graft --continue
53 53 abort: no graft in progress
54 54 [255]
55 55 $ hg revert -r . -q e
56 56
57 57 Need to specify a rev:
58 58
59 59 $ hg graft
60 60 abort: no revisions specified
61 61 [255]
62 62
63 63 Can't graft ancestor:
64 64
65 65 $ hg graft 1 2
66 66 skipping ancestor revision 1:5d205f8b35b6
67 67 skipping ancestor revision 2:5c095ad7e90f
68 68 [255]
69 69
70 70 Specify revisions with -r:
71 71
72 72 $ hg graft -r 1 -r 2
73 73 skipping ancestor revision 1:5d205f8b35b6
74 74 skipping ancestor revision 2:5c095ad7e90f
75 75 [255]
76 76
77 77 $ hg graft -r 1 2
78 78 warning: inconsistent use of --rev might give unexpected revision ordering!
79 79 skipping ancestor revision 2:5c095ad7e90f
80 80 skipping ancestor revision 1:5d205f8b35b6
81 81 [255]
82 82
83 83 Can't graft with dirty wd:
84 84
85 85 $ hg up -q 0
86 86 $ echo foo > a
87 87 $ hg graft 1
88 88 abort: uncommitted changes
89 89 [255]
90 90 $ hg revert a
91 91
92 92 Graft a rename:
93 93 (this also tests that editor is invoked if '--edit' is specified)
94 94
95 95 $ hg status --rev "2^1" --rev 2
96 96 A b
97 97 R a
98 98 $ HGEDITOR=cat hg graft 2 -u foo --edit
99 99 grafting 2:5c095ad7e90f "2"
100 100 merging a and b to b
101 101 2
102 102
103 103
104 104 HG: Enter commit message. Lines beginning with 'HG:' are removed.
105 105 HG: Leave message empty to abort commit.
106 106 HG: --
107 107 HG: user: foo
108 108 HG: branch 'default'
109 109 HG: added b
110 110 HG: removed a
111 111 $ hg export tip --git
112 112 # HG changeset patch
113 113 # User foo
114 114 # Date 0 0
115 115 # Thu Jan 01 00:00:00 1970 +0000
116 116 # Node ID ef0ef43d49e79e81ddafdc7997401ba0041efc82
117 117 # Parent 68795b066622ca79a25816a662041d8f78f3cd9e
118 118 2
119 119
120 120 diff --git a/a b/b
121 121 rename from a
122 122 rename to b
123 123
124 124 Look for extra:source
125 125
126 126 $ hg log --debug -r tip
127 127 changeset: 7:ef0ef43d49e79e81ddafdc7997401ba0041efc82
128 128 tag: tip
129 129 phase: draft
130 130 parent: 0:68795b066622ca79a25816a662041d8f78f3cd9e
131 131 parent: -1:0000000000000000000000000000000000000000
132 132 manifest: 7:e59b6b228f9cbf9903d5e9abf996e083a1f533eb
133 133 user: foo
134 134 date: Thu Jan 01 00:00:00 1970 +0000
135 135 files+: b
136 136 files-: a
137 137 extra: branch=default
138 138 extra: source=5c095ad7e90f871700f02dd1fa5012cb4498a2d4
139 139 description:
140 140 2
141 141
142 142
143 143
144 144 Graft out of order, skipping a merge and a duplicate
145 145 (this also tests that editor is not invoked if '--edit' is not specified)
146 146
147 147 $ hg graft 1 5 4 3 'merge()' 2 -n
148 148 skipping ungraftable merge revision 6
149 149 skipping revision 2:5c095ad7e90f (already grafted to 7:ef0ef43d49e7)
150 150 grafting 1:5d205f8b35b6 "1"
151 151 grafting 5:97f8bfe72746 "5"
152 152 grafting 4:9c233e8e184d "4"
153 153 grafting 3:4c60f11aa304 "3"
154 154
155 155 $ HGEDITOR=cat hg graft 1 5 'merge()' 2 --debug
156 156 skipping ungraftable merge revision 6
157 157 scanning for duplicate grafts
158 158 skipping revision 2:5c095ad7e90f (already grafted to 7:ef0ef43d49e7)
159 159 grafting 1:5d205f8b35b6 "1"
160 160 searching for copies back to rev 1
161 161 unmatched files in local:
162 162 b
163 163 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
164 164 src: 'a' -> dst: 'b' *
165 165 checking for directory renames
166 166 resolving manifests
167 167 branchmerge: True, force: True, partial: False
168 168 ancestor: 68795b066622, local: ef0ef43d49e7+, remote: 5d205f8b35b6
169 169 preserving b for resolve of b
170 170 starting 4 threads for background file closing (?)
171 171 b: local copied/moved from a -> m (premerge)
172 172 picked tool ':merge' for b (binary False symlink False changedelete False)
173 173 merging b and a to b
174 174 my b@ef0ef43d49e7+ other a@5d205f8b35b6 ancestor a@68795b066622
175 175 premerge successful
176 176 committing files:
177 177 b
178 178 committing manifest
179 179 committing changelog
180 180 grafting 5:97f8bfe72746 "5"
181 181 searching for copies back to rev 1
182 182 resolving manifests
183 183 branchmerge: True, force: True, partial: False
184 184 ancestor: 4c60f11aa304, local: 6b9e5368ca4e+, remote: 97f8bfe72746
185 185 e: remote is newer -> g
186 186 getting e
187 187 b: remote unchanged -> k
188 188 committing files:
189 189 e
190 190 committing manifest
191 191 committing changelog
192 192 $ HGEDITOR=cat hg graft 4 3 --log --debug
193 193 scanning for duplicate grafts
194 194 grafting 4:9c233e8e184d "4"
195 195 searching for copies back to rev 1
196 196 resolving manifests
197 197 branchmerge: True, force: True, partial: False
198 198 ancestor: 4c60f11aa304, local: 1905859650ec+, remote: 9c233e8e184d
199 199 preserving e for resolve of e
200 200 d: remote is newer -> g
201 201 getting d
202 202 b: remote unchanged -> k
203 203 e: versions differ -> m (premerge)
204 204 picked tool ':merge' for e (binary False symlink False changedelete False)
205 205 merging e
206 206 my e@1905859650ec+ other e@9c233e8e184d ancestor e@4c60f11aa304
207 207 e: versions differ -> m (merge)
208 208 picked tool ':merge' for e (binary False symlink False changedelete False)
209 209 my e@1905859650ec+ other e@9c233e8e184d ancestor e@4c60f11aa304
210 210 warning: conflicts while merging e! (edit, then use 'hg resolve --mark')
211 211 abort: unresolved conflicts, can't continue
212 212 (use 'hg resolve' and 'hg graft --continue --log')
213 213 [255]
214 214
215 215 Summary should mention graft:
216 216
217 217 $ hg summary |grep graft
218 218 commit: 2 modified, 2 unknown, 1 unresolved (graft in progress)
219 219
220 220 Commit while interrupted should fail:
221 221
222 222 $ hg ci -m 'commit interrupted graft'
223 223 abort: graft in progress
224 224 (use 'hg graft --continue' or 'hg update' to abort)
225 225 [255]
226 226
227 227 Abort the graft and try committing:
228 228
229 229 $ hg up -C .
230 230 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
231 231 $ echo c >> e
232 232 $ hg ci -mtest
233 233
234 234 $ hg strip . --config extensions.strip=
235 235 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
236 236 saved backup bundle to $TESTTMP/a/.hg/strip-backup/*-backup.hg (glob)
237 237
238 238 Graft again:
239 239
240 240 $ hg graft 1 5 4 3 'merge()' 2
241 241 skipping ungraftable merge revision 6
242 242 skipping revision 2:5c095ad7e90f (already grafted to 7:ef0ef43d49e7)
243 243 skipping revision 1:5d205f8b35b6 (already grafted to 8:6b9e5368ca4e)
244 244 skipping revision 5:97f8bfe72746 (already grafted to 9:1905859650ec)
245 245 grafting 4:9c233e8e184d "4"
246 246 merging e
247 247 warning: conflicts while merging e! (edit, then use 'hg resolve --mark')
248 248 abort: unresolved conflicts, can't continue
249 249 (use 'hg resolve' and 'hg graft --continue')
250 250 [255]
251 251
252 252 Continue without resolve should fail:
253 253
254 254 $ hg graft -c
255 255 grafting 4:9c233e8e184d "4"
256 abort: unresolved merge conflicts (see "hg help resolve")
256 abort: unresolved merge conflicts (see 'hg help resolve')
257 257 [255]
258 258
259 259 Fix up:
260 260
261 261 $ echo b > e
262 262 $ hg resolve -m e
263 263 (no more unresolved files)
264 264 continue: hg graft --continue
265 265
266 266 Continue with a revision should fail:
267 267
268 268 $ hg graft -c 6
269 269 abort: can't specify --continue and revisions
270 270 [255]
271 271
272 272 $ hg graft -c -r 6
273 273 abort: can't specify --continue and revisions
274 274 [255]
275 275
276 276 Continue for real, clobber usernames
277 277
278 278 $ hg graft -c -U
279 279 grafting 4:9c233e8e184d "4"
280 280 grafting 3:4c60f11aa304 "3"
281 281
282 282 Compare with original:
283 283
284 284 $ hg diff -r 6
285 285 $ hg status --rev 0:. -C
286 286 M d
287 287 M e
288 288 A b
289 289 a
290 290 A c
291 291 a
292 292 R a
293 293
294 294 View graph:
295 295
296 296 $ hg log -G --template '{author}@{rev}.{phase}: {desc}\n'
297 297 @ test@11.draft: 3
298 298 |
299 299 o test@10.draft: 4
300 300 |
301 301 o test@9.draft: 5
302 302 |
303 303 o bar@8.draft: 1
304 304 |
305 305 o foo@7.draft: 2
306 306 |
307 307 | o test@6.secret: 6
308 308 | |\
309 309 | | o test@5.draft: 5
310 310 | | |
311 311 | o | test@4.draft: 4
312 312 | |/
313 313 | o baz@3.public: 3
314 314 | |
315 315 | o test@2.public: 2
316 316 | |
317 317 | o bar@1.public: 1
318 318 |/
319 319 o test@0.public: 0
320 320
321 321 Graft again onto another branch should preserve the original source
322 322 $ hg up -q 0
323 323 $ echo 'g'>g
324 324 $ hg add g
325 325 $ hg ci -m 7
326 326 created new head
327 327 $ hg graft 7
328 328 grafting 7:ef0ef43d49e7 "2"
329 329
330 330 $ hg log -r 7 --template '{rev}:{node}\n'
331 331 7:ef0ef43d49e79e81ddafdc7997401ba0041efc82
332 332 $ hg log -r 2 --template '{rev}:{node}\n'
333 333 2:5c095ad7e90f871700f02dd1fa5012cb4498a2d4
334 334
335 335 $ hg log --debug -r tip
336 336 changeset: 13:7a4785234d87ec1aa420ed6b11afe40fa73e12a9
337 337 tag: tip
338 338 phase: draft
339 339 parent: 12:b592ea63bb0c19a6c5c44685ee29a2284f9f1b8f
340 340 parent: -1:0000000000000000000000000000000000000000
341 341 manifest: 13:dc313617b8c32457c0d589e0dbbedfe71f3cd637
342 342 user: foo
343 343 date: Thu Jan 01 00:00:00 1970 +0000
344 344 files+: b
345 345 files-: a
346 346 extra: branch=default
347 347 extra: intermediate-source=ef0ef43d49e79e81ddafdc7997401ba0041efc82
348 348 extra: source=5c095ad7e90f871700f02dd1fa5012cb4498a2d4
349 349 description:
350 350 2
351 351
352 352
353 353 Disallow grafting an already grafted cset onto its original branch
354 354 $ hg up -q 6
355 355 $ hg graft 7
356 356 skipping already grafted revision 7:ef0ef43d49e7 (was grafted from 2:5c095ad7e90f)
357 357 [255]
358 358
359 359 $ hg pdiff --config extensions.extdiff= --patch -r 2 -r 13
360 360 --- */hg-5c095ad7e90f.patch * (glob)
361 361 +++ */hg-7a4785234d87.patch * (glob)
362 362 @@ -1,18 +1,18 @@
363 363 # HG changeset patch
364 364 -# User test
365 365 +# User foo
366 366 # Date 0 0
367 367 # Thu Jan 01 00:00:00 1970 +0000
368 368 -# Node ID 5c095ad7e90f871700f02dd1fa5012cb4498a2d4
369 369 -# Parent 5d205f8b35b66bc36375c9534ffd3237730e8f04
370 370 +# Node ID 7a4785234d87ec1aa420ed6b11afe40fa73e12a9
371 371 +# Parent b592ea63bb0c19a6c5c44685ee29a2284f9f1b8f
372 372 2
373 373
374 374 -diff -r 5d205f8b35b6 -r 5c095ad7e90f a
375 375 +diff -r b592ea63bb0c -r 7a4785234d87 a
376 376 --- a/a Thu Jan 01 00:00:00 1970 +0000
377 377 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
378 378 @@ -1,1 +0,0 @@
379 379 --b
380 380 -diff -r 5d205f8b35b6 -r 5c095ad7e90f b
381 381 +-a
382 382 +diff -r b592ea63bb0c -r 7a4785234d87 b
383 383 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
384 384 +++ b/b Thu Jan 01 00:00:00 1970 +0000
385 385 @@ -0,0 +1,1 @@
386 386 -+b
387 387 ++a
388 388 [1]
389 389
390 390 $ hg pdiff --config extensions.extdiff= --patch -r 2 -r 13 -X .
391 391 --- */hg-5c095ad7e90f.patch * (glob)
392 392 +++ */hg-7a4785234d87.patch * (glob)
393 393 @@ -1,8 +1,8 @@
394 394 # HG changeset patch
395 395 -# User test
396 396 +# User foo
397 397 # Date 0 0
398 398 # Thu Jan 01 00:00:00 1970 +0000
399 399 -# Node ID 5c095ad7e90f871700f02dd1fa5012cb4498a2d4
400 400 -# Parent 5d205f8b35b66bc36375c9534ffd3237730e8f04
401 401 +# Node ID 7a4785234d87ec1aa420ed6b11afe40fa73e12a9
402 402 +# Parent b592ea63bb0c19a6c5c44685ee29a2284f9f1b8f
403 403 2
404 404
405 405 [1]
406 406
407 407 Disallow grafting already grafted csets with the same origin onto each other
408 408 $ hg up -q 13
409 409 $ hg graft 2
410 410 skipping revision 2:5c095ad7e90f (already grafted to 13:7a4785234d87)
411 411 [255]
412 412 $ hg graft 7
413 413 skipping already grafted revision 7:ef0ef43d49e7 (13:7a4785234d87 also has origin 2:5c095ad7e90f)
414 414 [255]
415 415
416 416 $ hg up -q 7
417 417 $ hg graft 2
418 418 skipping revision 2:5c095ad7e90f (already grafted to 7:ef0ef43d49e7)
419 419 [255]
420 420 $ hg graft tip
421 421 skipping already grafted revision 13:7a4785234d87 (7:ef0ef43d49e7 also has origin 2:5c095ad7e90f)
422 422 [255]
423 423
424 424 Graft with --log
425 425
426 426 $ hg up -Cq 1
427 427 $ hg graft 3 --log -u foo
428 428 grafting 3:4c60f11aa304 "3"
429 429 warning: can't find ancestor for 'c' copied from 'b'!
430 430 $ hg log --template '{rev} {parents} {desc}\n' -r tip
431 431 14 1:5d205f8b35b6 3
432 432 (grafted from 4c60f11aa304a54ae1c199feb94e7fc771e51ed8)
433 433
434 434 Resolve conflicted graft
435 435 $ hg up -q 0
436 436 $ echo b > a
437 437 $ hg ci -m 8
438 438 created new head
439 439 $ echo c > a
440 440 $ hg ci -m 9
441 441 $ hg graft 1 --tool internal:fail
442 442 grafting 1:5d205f8b35b6 "1"
443 443 abort: unresolved conflicts, can't continue
444 444 (use 'hg resolve' and 'hg graft --continue')
445 445 [255]
446 446 $ hg resolve --all
447 447 merging a
448 448 warning: conflicts while merging a! (edit, then use 'hg resolve --mark')
449 449 [1]
450 450 $ cat a
451 451 <<<<<<< local: aaa4406d4f0a - test: 9
452 452 c
453 453 =======
454 454 b
455 455 >>>>>>> graft: 5d205f8b35b6 - bar: 1
456 456 $ echo b > a
457 457 $ hg resolve -m a
458 458 (no more unresolved files)
459 459 continue: hg graft --continue
460 460 $ hg graft -c
461 461 grafting 1:5d205f8b35b6 "1"
462 462 $ hg export tip --git
463 463 # HG changeset patch
464 464 # User bar
465 465 # Date 0 0
466 466 # Thu Jan 01 00:00:00 1970 +0000
467 467 # Node ID f67661df0c4804d301f064f332b57e7d5ddaf2be
468 468 # Parent aaa4406d4f0ae9befd6e58c82ec63706460cbca6
469 469 1
470 470
471 471 diff --git a/a b/a
472 472 --- a/a
473 473 +++ b/a
474 474 @@ -1,1 +1,1 @@
475 475 -c
476 476 +b
477 477
478 478 Resolve conflicted graft with rename
479 479 $ echo c > a
480 480 $ hg ci -m 10
481 481 $ hg graft 2 --tool internal:fail
482 482 grafting 2:5c095ad7e90f "2"
483 483 abort: unresolved conflicts, can't continue
484 484 (use 'hg resolve' and 'hg graft --continue')
485 485 [255]
486 486 $ hg resolve --all
487 487 merging a and b to b
488 488 (no more unresolved files)
489 489 continue: hg graft --continue
490 490 $ hg graft -c
491 491 grafting 2:5c095ad7e90f "2"
492 492 $ hg export tip --git
493 493 # HG changeset patch
494 494 # User test
495 495 # Date 0 0
496 496 # Thu Jan 01 00:00:00 1970 +0000
497 497 # Node ID 9627f653b421c61fc1ea4c4e366745070fa3d2bc
498 498 # Parent ee295f490a40b97f3d18dd4c4f1c8936c233b612
499 499 2
500 500
501 501 diff --git a/a b/b
502 502 rename from a
503 503 rename to b
504 504
505 505 Test simple origin(), with and without args
506 506 $ hg log -r 'origin()'
507 507 changeset: 1:5d205f8b35b6
508 508 user: bar
509 509 date: Thu Jan 01 00:00:00 1970 +0000
510 510 summary: 1
511 511
512 512 changeset: 2:5c095ad7e90f
513 513 user: test
514 514 date: Thu Jan 01 00:00:00 1970 +0000
515 515 summary: 2
516 516
517 517 changeset: 3:4c60f11aa304
518 518 user: baz
519 519 date: Thu Jan 01 00:00:00 1970 +0000
520 520 summary: 3
521 521
522 522 changeset: 4:9c233e8e184d
523 523 user: test
524 524 date: Thu Jan 01 00:00:00 1970 +0000
525 525 summary: 4
526 526
527 527 changeset: 5:97f8bfe72746
528 528 branch: stable
529 529 parent: 3:4c60f11aa304
530 530 user: test
531 531 date: Thu Jan 01 00:00:00 1970 +0000
532 532 summary: 5
533 533
534 534 $ hg log -r 'origin(7)'
535 535 changeset: 2:5c095ad7e90f
536 536 user: test
537 537 date: Thu Jan 01 00:00:00 1970 +0000
538 538 summary: 2
539 539
540 540 Now transplant a graft to test following through copies
541 541 $ hg up -q 0
542 542 $ hg branch -q dev
543 543 $ hg ci -qm "dev branch"
544 544 $ hg --config extensions.transplant= transplant -q 7
545 545 $ hg log -r 'origin(.)'
546 546 changeset: 2:5c095ad7e90f
547 547 user: test
548 548 date: Thu Jan 01 00:00:00 1970 +0000
549 549 summary: 2
550 550
551 551 Test that the graft and transplant markers in extra are converted, allowing
552 552 origin() to still work. Note that these recheck the immediately preceeding two
553 553 tests.
554 554 $ hg --quiet --config extensions.convert= --config convert.hg.saverev=True convert . ../converted
555 555
556 556 The graft case
557 557 $ hg -R ../converted log -r 7 --template "{rev}: {node}\n{join(extras, '\n')}\n"
558 558 7: 7ae846e9111fc8f57745634250c7b9ac0a60689b
559 559 branch=default
560 560 convert_revision=ef0ef43d49e79e81ddafdc7997401ba0041efc82
561 561 source=e0213322b2c1a5d5d236c74e79666441bee67a7d
562 562 $ hg -R ../converted log -r 'origin(7)'
563 563 changeset: 2:e0213322b2c1
564 564 user: test
565 565 date: Thu Jan 01 00:00:00 1970 +0000
566 566 summary: 2
567 567
568 568 Test that template correctly expands more than one 'extra' (issue4362), and that
569 569 'intermediate-source' is converted.
570 570 $ hg -R ../converted log -r 13 --template "{extras % ' Extra: {extra}\n'}"
571 571 Extra: branch=default
572 572 Extra: convert_revision=7a4785234d87ec1aa420ed6b11afe40fa73e12a9
573 573 Extra: intermediate-source=7ae846e9111fc8f57745634250c7b9ac0a60689b
574 574 Extra: source=e0213322b2c1a5d5d236c74e79666441bee67a7d
575 575
576 576 The transplant case
577 577 $ hg -R ../converted log -r tip --template "{rev}: {node}\n{join(extras, '\n')}\n"
578 578 21: fbb6c5cc81002f2b4b49c9d731404688bcae5ade
579 579 branch=dev
580 580 convert_revision=7e61b508e709a11d28194a5359bc3532d910af21
581 581 transplant_source=z\xe8F\xe9\x11\x1f\xc8\xf5wEcBP\xc7\xb9\xac (esc)
582 582 `h\x9b (esc)
583 583 $ hg -R ../converted log -r 'origin(tip)'
584 584 changeset: 2:e0213322b2c1
585 585 user: test
586 586 date: Thu Jan 01 00:00:00 1970 +0000
587 587 summary: 2
588 588
589 589
590 590 Test simple destination
591 591 $ hg log -r 'destination()'
592 592 changeset: 7:ef0ef43d49e7
593 593 parent: 0:68795b066622
594 594 user: foo
595 595 date: Thu Jan 01 00:00:00 1970 +0000
596 596 summary: 2
597 597
598 598 changeset: 8:6b9e5368ca4e
599 599 user: bar
600 600 date: Thu Jan 01 00:00:00 1970 +0000
601 601 summary: 1
602 602
603 603 changeset: 9:1905859650ec
604 604 user: test
605 605 date: Thu Jan 01 00:00:00 1970 +0000
606 606 summary: 5
607 607
608 608 changeset: 10:52dc0b4c6907
609 609 user: test
610 610 date: Thu Jan 01 00:00:00 1970 +0000
611 611 summary: 4
612 612
613 613 changeset: 11:882b35362a6b
614 614 user: test
615 615 date: Thu Jan 01 00:00:00 1970 +0000
616 616 summary: 3
617 617
618 618 changeset: 13:7a4785234d87
619 619 user: foo
620 620 date: Thu Jan 01 00:00:00 1970 +0000
621 621 summary: 2
622 622
623 623 changeset: 14:f64defefacee
624 624 parent: 1:5d205f8b35b6
625 625 user: foo
626 626 date: Thu Jan 01 00:00:00 1970 +0000
627 627 summary: 3
628 628
629 629 changeset: 17:f67661df0c48
630 630 user: bar
631 631 date: Thu Jan 01 00:00:00 1970 +0000
632 632 summary: 1
633 633
634 634 changeset: 19:9627f653b421
635 635 user: test
636 636 date: Thu Jan 01 00:00:00 1970 +0000
637 637 summary: 2
638 638
639 639 changeset: 21:7e61b508e709
640 640 branch: dev
641 641 tag: tip
642 642 user: foo
643 643 date: Thu Jan 01 00:00:00 1970 +0000
644 644 summary: 2
645 645
646 646 $ hg log -r 'destination(2)'
647 647 changeset: 7:ef0ef43d49e7
648 648 parent: 0:68795b066622
649 649 user: foo
650 650 date: Thu Jan 01 00:00:00 1970 +0000
651 651 summary: 2
652 652
653 653 changeset: 13:7a4785234d87
654 654 user: foo
655 655 date: Thu Jan 01 00:00:00 1970 +0000
656 656 summary: 2
657 657
658 658 changeset: 19:9627f653b421
659 659 user: test
660 660 date: Thu Jan 01 00:00:00 1970 +0000
661 661 summary: 2
662 662
663 663 changeset: 21:7e61b508e709
664 664 branch: dev
665 665 tag: tip
666 666 user: foo
667 667 date: Thu Jan 01 00:00:00 1970 +0000
668 668 summary: 2
669 669
670 670 Transplants of grafts can find a destination...
671 671 $ hg log -r 'destination(7)'
672 672 changeset: 21:7e61b508e709
673 673 branch: dev
674 674 tag: tip
675 675 user: foo
676 676 date: Thu Jan 01 00:00:00 1970 +0000
677 677 summary: 2
678 678
679 679 ... grafts of grafts unfortunately can't
680 680 $ hg graft -q 13
681 681 warning: can't find ancestor for 'b' copied from 'a'!
682 682 $ hg log -r 'destination(13)'
683 683 All copies of a cset
684 684 $ hg log -r 'origin(13) or destination(origin(13))'
685 685 changeset: 2:5c095ad7e90f
686 686 user: test
687 687 date: Thu Jan 01 00:00:00 1970 +0000
688 688 summary: 2
689 689
690 690 changeset: 7:ef0ef43d49e7
691 691 parent: 0:68795b066622
692 692 user: foo
693 693 date: Thu Jan 01 00:00:00 1970 +0000
694 694 summary: 2
695 695
696 696 changeset: 13:7a4785234d87
697 697 user: foo
698 698 date: Thu Jan 01 00:00:00 1970 +0000
699 699 summary: 2
700 700
701 701 changeset: 19:9627f653b421
702 702 user: test
703 703 date: Thu Jan 01 00:00:00 1970 +0000
704 704 summary: 2
705 705
706 706 changeset: 21:7e61b508e709
707 707 branch: dev
708 708 user: foo
709 709 date: Thu Jan 01 00:00:00 1970 +0000
710 710 summary: 2
711 711
712 712 changeset: 22:d1cb6591fa4b
713 713 branch: dev
714 714 tag: tip
715 715 user: foo
716 716 date: Thu Jan 01 00:00:00 1970 +0000
717 717 summary: 2
718 718
719 719
720 720 graft works on complex revset
721 721
722 722 $ hg graft 'origin(13) or destination(origin(13))'
723 723 skipping ancestor revision 21:7e61b508e709
724 724 skipping ancestor revision 22:d1cb6591fa4b
725 725 skipping revision 2:5c095ad7e90f (already grafted to 22:d1cb6591fa4b)
726 726 grafting 7:ef0ef43d49e7 "2"
727 727 warning: can't find ancestor for 'b' copied from 'a'!
728 728 grafting 13:7a4785234d87 "2"
729 729 warning: can't find ancestor for 'b' copied from 'a'!
730 730 grafting 19:9627f653b421 "2"
731 731 merging b
732 732 warning: can't find ancestor for 'b' copied from 'a'!
733 733
734 734 graft with --force (still doesn't graft merges)
735 735
736 736 $ hg graft 19 0 6
737 737 skipping ungraftable merge revision 6
738 738 skipping ancestor revision 0:68795b066622
739 739 skipping already grafted revision 19:9627f653b421 (22:d1cb6591fa4b also has origin 2:5c095ad7e90f)
740 740 [255]
741 741 $ hg graft 19 0 6 --force
742 742 skipping ungraftable merge revision 6
743 743 grafting 19:9627f653b421 "2"
744 744 merging b
745 745 warning: can't find ancestor for 'b' copied from 'a'!
746 746 grafting 0:68795b066622 "0"
747 747
748 748 graft --force after backout
749 749
750 750 $ echo abc > a
751 751 $ hg ci -m 28
752 752 $ hg backout 28
753 753 reverting a
754 754 changeset 29:53177ba928f6 backs out changeset 28:50a516bb8b57
755 755 $ hg graft 28
756 756 skipping ancestor revision 28:50a516bb8b57
757 757 [255]
758 758 $ hg graft 28 --force
759 759 grafting 28:50a516bb8b57 "28"
760 760 merging a
761 761 $ cat a
762 762 abc
763 763
764 764 graft --continue after --force
765 765
766 766 $ echo def > a
767 767 $ hg ci -m 31
768 768 $ hg graft 28 --force --tool internal:fail
769 769 grafting 28:50a516bb8b57 "28"
770 770 abort: unresolved conflicts, can't continue
771 771 (use 'hg resolve' and 'hg graft --continue')
772 772 [255]
773 773 $ hg resolve --all
774 774 merging a
775 775 warning: conflicts while merging a! (edit, then use 'hg resolve --mark')
776 776 [1]
777 777 $ echo abc > a
778 778 $ hg resolve -m a
779 779 (no more unresolved files)
780 780 continue: hg graft --continue
781 781 $ hg graft -c
782 782 grafting 28:50a516bb8b57 "28"
783 783 $ cat a
784 784 abc
785 785
786 786 Continue testing same origin policy, using revision numbers from test above
787 787 but do some destructive editing of the repo:
788 788
789 789 $ hg up -qC 7
790 790 $ hg tag -l -r 13 tmp
791 791 $ hg --config extensions.strip= strip 2
792 792 saved backup bundle to $TESTTMP/a/.hg/strip-backup/5c095ad7e90f-d323a1e4-backup.hg (glob)
793 793 $ hg graft tmp
794 794 skipping already grafted revision 8:7a4785234d87 (2:ef0ef43d49e7 also has unknown origin 5c095ad7e90f)
795 795 [255]
796 796
797 797 Empty graft
798 798
799 799 $ hg up -qr 26
800 800 $ hg tag -f something
801 801 $ hg graft -qr 27
802 802 $ hg graft -f 27
803 803 grafting 27:ed6c7e54e319 "28"
804 804 note: graft of 27:ed6c7e54e319 created no changes to commit
805 805
806 806 $ cd ..
807 807
808 808 Graft to duplicate a commit
809 809
810 810 $ hg init graftsibling
811 811 $ cd graftsibling
812 812 $ touch a
813 813 $ hg commit -qAm a
814 814 $ touch b
815 815 $ hg commit -qAm b
816 816 $ hg log -G -T '{rev}\n'
817 817 @ 1
818 818 |
819 819 o 0
820 820
821 821 $ hg up -q 0
822 822 $ hg graft -r 1
823 823 grafting 1:0e067c57feba "b" (tip)
824 824 $ hg log -G -T '{rev}\n'
825 825 @ 2
826 826 |
827 827 | o 1
828 828 |/
829 829 o 0
830 830
831 831 Graft to duplicate a commit twice
832 832
833 833 $ hg up -q 0
834 834 $ hg graft -r 2
835 835 grafting 2:044ec77f6389 "b" (tip)
836 836 $ hg log -G -T '{rev}\n'
837 837 @ 3
838 838 |
839 839 | o 2
840 840 |/
841 841 | o 1
842 842 |/
843 843 o 0
844 844
@@ -1,359 +1,359 b''
1 1 $ cat >> $HGRCPATH <<EOF
2 2 > [format]
3 3 > usegeneraldelta=yes
4 4 > [extensions]
5 5 > rebase=
6 6 >
7 7 > [phases]
8 8 > publish=False
9 9 >
10 10 > [alias]
11 11 > tglog = log -G --template "{rev}:{phase} '{desc}' {branches} {bookmarks}\n"
12 12 > EOF
13 13
14 14 $ hg init a
15 15 $ cd a
16 16 $ echo c1 >common
17 17 $ hg add common
18 18 $ hg ci -m C1
19 19
20 20 $ echo c2 >>common
21 21 $ hg ci -m C2
22 22
23 23 $ echo c3 >>common
24 24 $ hg ci -m C3
25 25
26 26 $ hg up -q -C 1
27 27
28 28 $ echo l1 >>extra
29 29 $ hg add extra
30 30 $ hg ci -m L1
31 31 created new head
32 32
33 33 $ sed -e 's/c2/l2/' common > common.new
34 34 $ mv common.new common
35 35 $ hg ci -m L2
36 36
37 37 $ echo l3 >> extra2
38 38 $ hg add extra2
39 39 $ hg ci -m L3
40 40 $ hg bookmark mybook
41 41
42 42 $ hg phase --force --secret 4
43 43
44 44 $ hg tglog
45 45 @ 5:secret 'L3' mybook
46 46 |
47 47 o 4:secret 'L2'
48 48 |
49 49 o 3:draft 'L1'
50 50 |
51 51 | o 2:draft 'C3'
52 52 |/
53 53 o 1:draft 'C2'
54 54 |
55 55 o 0:draft 'C1'
56 56
57 57 Try to call --continue:
58 58
59 59 $ hg rebase --continue
60 60 abort: no rebase in progress
61 61 [255]
62 62
63 63 Conflicting rebase:
64 64
65 65 $ hg rebase -s 3 -d 2
66 66 rebasing 3:3163e20567cc "L1"
67 67 rebasing 4:46f0b057b5c0 "L2"
68 68 merging common
69 69 warning: conflicts while merging common! (edit, then use 'hg resolve --mark')
70 70 unresolved conflicts (see hg resolve, then hg rebase --continue)
71 71 [1]
72 72
73 73 Try to continue without solving the conflict:
74 74
75 75 $ hg rebase --continue
76 76 already rebased 3:3163e20567cc "L1" as 3e046f2ecedb
77 77 rebasing 4:46f0b057b5c0 "L2"
78 abort: unresolved merge conflicts (see "hg help resolve")
78 abort: unresolved merge conflicts (see 'hg help resolve')
79 79 [255]
80 80
81 81 Conclude rebase:
82 82
83 83 $ echo 'resolved merge' >common
84 84 $ hg resolve -m common
85 85 (no more unresolved files)
86 86 continue: hg rebase --continue
87 87 $ hg rebase --continue
88 88 already rebased 3:3163e20567cc "L1" as 3e046f2ecedb
89 89 rebasing 4:46f0b057b5c0 "L2"
90 90 rebasing 5:8029388f38dc "L3" (mybook)
91 91 saved backup bundle to $TESTTMP/a/.hg/strip-backup/3163e20567cc-5ca4656e-backup.hg (glob)
92 92
93 93 $ hg tglog
94 94 @ 5:secret 'L3' mybook
95 95 |
96 96 o 4:secret 'L2'
97 97 |
98 98 o 3:draft 'L1'
99 99 |
100 100 o 2:draft 'C3'
101 101 |
102 102 o 1:draft 'C2'
103 103 |
104 104 o 0:draft 'C1'
105 105
106 106 Check correctness:
107 107
108 108 $ hg cat -r 0 common
109 109 c1
110 110
111 111 $ hg cat -r 1 common
112 112 c1
113 113 c2
114 114
115 115 $ hg cat -r 2 common
116 116 c1
117 117 c2
118 118 c3
119 119
120 120 $ hg cat -r 3 common
121 121 c1
122 122 c2
123 123 c3
124 124
125 125 $ hg cat -r 4 common
126 126 resolved merge
127 127
128 128 $ hg cat -r 5 common
129 129 resolved merge
130 130
131 131 Bookmark stays active after --continue
132 132 $ hg bookmarks
133 133 * mybook 5:d67b21408fc0
134 134
135 135 $ cd ..
136 136
137 137 Check that the right ancestors is used while rebasing a merge (issue4041)
138 138
139 139 $ hg clone "$TESTDIR/bundles/issue4041.hg" issue4041
140 140 requesting all changes
141 141 adding changesets
142 142 adding manifests
143 143 adding file changes
144 144 added 11 changesets with 8 changes to 3 files (+1 heads)
145 145 updating to branch default
146 146 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
147 147 $ cd issue4041
148 148 $ hg log -G
149 149 o changeset: 10:2f2496ddf49d
150 150 |\ branch: f1
151 151 | | tag: tip
152 152 | | parent: 7:4c9fbe56a16f
153 153 | | parent: 9:e31216eec445
154 154 | | user: szhang
155 155 | | date: Thu Sep 05 12:59:39 2013 -0400
156 156 | | summary: merge
157 157 | |
158 158 | o changeset: 9:e31216eec445
159 159 | | branch: f1
160 160 | | user: szhang
161 161 | | date: Thu Sep 05 12:59:10 2013 -0400
162 162 | | summary: more changes to f1
163 163 | |
164 164 | o changeset: 8:8e4e2c1a07ae
165 165 | |\ branch: f1
166 166 | | | parent: 2:4bc80088dc6b
167 167 | | | parent: 6:400110238667
168 168 | | | user: szhang
169 169 | | | date: Thu Sep 05 12:57:59 2013 -0400
170 170 | | | summary: bad merge
171 171 | | |
172 172 o | | changeset: 7:4c9fbe56a16f
173 173 |/ / branch: f1
174 174 | | parent: 2:4bc80088dc6b
175 175 | | user: szhang
176 176 | | date: Thu Sep 05 12:54:00 2013 -0400
177 177 | | summary: changed f1
178 178 | |
179 179 | o changeset: 6:400110238667
180 180 | | branch: f2
181 181 | | parent: 4:12e8ec6bb010
182 182 | | user: szhang
183 183 | | date: Tue Sep 03 13:58:02 2013 -0400
184 184 | | summary: changed f2 on f2
185 185 | |
186 186 | | @ changeset: 5:d79e2059b5c0
187 187 | | | parent: 3:8a951942e016
188 188 | | | user: szhang
189 189 | | | date: Tue Sep 03 13:57:39 2013 -0400
190 190 | | | summary: changed f2 on default
191 191 | | |
192 192 | o | changeset: 4:12e8ec6bb010
193 193 | |/ branch: f2
194 194 | | user: szhang
195 195 | | date: Tue Sep 03 13:57:18 2013 -0400
196 196 | | summary: created f2 branch
197 197 | |
198 198 | o changeset: 3:8a951942e016
199 199 | | parent: 0:24797d4f68de
200 200 | | user: szhang
201 201 | | date: Tue Sep 03 13:57:11 2013 -0400
202 202 | | summary: added f2.txt
203 203 | |
204 204 o | changeset: 2:4bc80088dc6b
205 205 | | branch: f1
206 206 | | user: szhang
207 207 | | date: Tue Sep 03 13:56:20 2013 -0400
208 208 | | summary: added f1.txt
209 209 | |
210 210 o | changeset: 1:ef53c9e6b608
211 211 |/ branch: f1
212 212 | user: szhang
213 213 | date: Tue Sep 03 13:55:26 2013 -0400
214 214 | summary: created f1 branch
215 215 |
216 216 o changeset: 0:24797d4f68de
217 217 user: szhang
218 218 date: Tue Sep 03 13:55:08 2013 -0400
219 219 summary: added default.txt
220 220
221 221 $ hg rebase -s9 -d2 --debug # use debug to really check merge base used
222 222 rebase onto 4bc80088dc6b starting from e31216eec445
223 223 ignoring null merge rebase of 3
224 224 ignoring null merge rebase of 4
225 225 ignoring null merge rebase of 6
226 226 ignoring null merge rebase of 8
227 227 rebasing 9:e31216eec445 "more changes to f1"
228 228 future parents are 2 and -1
229 229 rebase status stored
230 230 update to 2:4bc80088dc6b
231 231 resolving manifests
232 232 branchmerge: False, force: True, partial: False
233 233 ancestor: d79e2059b5c0+, local: d79e2059b5c0+, remote: 4bc80088dc6b
234 234 f2.txt: other deleted -> r
235 235 removing f2.txt
236 236 f1.txt: remote created -> g
237 237 getting f1.txt
238 238 merge against 9:e31216eec445
239 239 detach base 8:8e4e2c1a07ae
240 240 searching for copies back to rev 3
241 241 resolving manifests
242 242 branchmerge: True, force: True, partial: False
243 243 ancestor: 8e4e2c1a07ae, local: 4bc80088dc6b+, remote: e31216eec445
244 244 f1.txt: remote is newer -> g
245 245 getting f1.txt
246 246 committing files:
247 247 f1.txt
248 248 committing manifest
249 249 committing changelog
250 250 rebased as 19c888675e13
251 251 rebasing 10:2f2496ddf49d "merge" (tip)
252 252 future parents are 11 and 7
253 253 rebase status stored
254 254 already in target
255 255 merge against 10:2f2496ddf49d
256 256 detach base 9:e31216eec445
257 257 searching for copies back to rev 3
258 258 resolving manifests
259 259 branchmerge: True, force: True, partial: False
260 260 ancestor: e31216eec445, local: 19c888675e13+, remote: 2f2496ddf49d
261 261 f1.txt: remote is newer -> g
262 262 getting f1.txt
263 263 committing files:
264 264 f1.txt
265 265 committing manifest
266 266 committing changelog
267 267 rebased as 2a7f09cac94c
268 268 rebase merging completed
269 269 update back to initial working directory parent
270 270 resolving manifests
271 271 branchmerge: False, force: False, partial: False
272 272 ancestor: 2a7f09cac94c, local: 2a7f09cac94c+, remote: d79e2059b5c0
273 273 f1.txt: other deleted -> r
274 274 removing f1.txt
275 275 f2.txt: remote created -> g
276 276 getting f2.txt
277 277 2 changesets found
278 278 list of changesets:
279 279 e31216eec445e44352c5f01588856059466a24c9
280 280 2f2496ddf49d69b5ef23ad8cf9fb2e0e4faf0ac2
281 281 bundle2-output-bundle: "HG20", (1 params) 1 parts total
282 282 bundle2-output-part: "changegroup" (params: 1 mandatory 1 advisory) streamed payload
283 283 saved backup bundle to $TESTTMP/issue4041/.hg/strip-backup/e31216eec445-15f7a814-backup.hg (glob)
284 284 3 changesets found
285 285 list of changesets:
286 286 4c9fbe56a16f30c0d5dcc40ec1a97bbe3325209c
287 287 19c888675e133ab5dff84516926a65672eaf04d9
288 288 2a7f09cac94c7f4b73ebd5cd1a62d3b2e8e336bf
289 289 bundle2-output-bundle: "HG20", 1 parts total
290 290 bundle2-output-part: "changegroup" (params: 1 mandatory 1 advisory) streamed payload
291 291 adding branch
292 292 bundle2-input-bundle: with-transaction
293 293 bundle2-input-part: "changegroup" (params: 1 mandatory 1 advisory) supported
294 294 adding changesets
295 295 add changeset 4c9fbe56a16f
296 296 add changeset 19c888675e13
297 297 add changeset 2a7f09cac94c
298 298 adding manifests
299 299 adding file changes
300 300 adding f1.txt revisions
301 301 added 2 changesets with 2 changes to 1 files
302 302 bundle2-input-part: total payload size 1713
303 303 bundle2-input-bundle: 0 parts total
304 304 invalid branchheads cache (served): tip differs
305 305 history modification detected - truncating revision branch cache to revision 9
306 306 rebase completed
307 307 truncating cache/rbc-revs-v1 to 72
308 308
309 309 Test minimization of merge conflicts
310 310 $ hg up -q null
311 311 $ echo a > a
312 312 $ hg add a
313 313 $ hg commit -q -m 'a'
314 314 $ echo b >> a
315 315 $ hg commit -q -m 'ab'
316 316 $ hg bookmark ab
317 317 $ hg up -q '.^'
318 318 $ echo b >> a
319 319 $ echo c >> a
320 320 $ hg commit -q -m 'abc'
321 321 $ hg rebase -s 7bc217434fc1 -d ab --keep
322 322 rebasing 13:7bc217434fc1 "abc" (tip)
323 323 merging a
324 324 warning: conflicts while merging a! (edit, then use 'hg resolve --mark')
325 325 unresolved conflicts (see hg resolve, then hg rebase --continue)
326 326 [1]
327 327 $ hg diff
328 328 diff -r 328e4ab1f7cc a
329 329 --- a/a Thu Jan 01 00:00:00 1970 +0000
330 330 +++ b/a * (glob)
331 331 @@ -1,2 +1,6 @@
332 332 a
333 333 b
334 334 +<<<<<<< dest: 328e4ab1f7cc ab - test: ab
335 335 +=======
336 336 +c
337 337 +>>>>>>> source: 7bc217434fc1 - test: abc
338 338 $ hg rebase --abort
339 339 rebase aborted
340 340 $ hg up -q -C 7bc217434fc1
341 341 $ hg rebase -s . -d ab --keep -t internal:merge3
342 342 rebasing 13:7bc217434fc1 "abc" (tip)
343 343 merging a
344 344 warning: conflicts while merging a! (edit, then use 'hg resolve --mark')
345 345 unresolved conflicts (see hg resolve, then hg rebase --continue)
346 346 [1]
347 347 $ hg diff
348 348 diff -r 328e4ab1f7cc a
349 349 --- a/a Thu Jan 01 00:00:00 1970 +0000
350 350 +++ b/a * (glob)
351 351 @@ -1,2 +1,8 @@
352 352 a
353 353 +<<<<<<< dest: 328e4ab1f7cc ab - test: ab
354 354 b
355 355 +||||||| base
356 356 +=======
357 357 +b
358 358 +c
359 359 +>>>>>>> source: 7bc217434fc1 - test: abc
@@ -1,1071 +1,1071 b''
1 1
2 2 $ mkdir -p t
3 3 $ cd t
4 4 $ cat <<EOF > merge
5 5 > import sys, os
6 6 > f = open(sys.argv[1], "wb")
7 7 > f.write("merge %s %s %s" % (sys.argv[1], sys.argv[2], sys.argv[3]))
8 8 > f.close()
9 9 > EOF
10 10
11 11 perform a test merge with possible renaming
12 12 args:
13 13 $1 = action in local branch
14 14 $2 = action in remote branch
15 15 $3 = action in working dir
16 16 $4 = expected result
17 17
18 18 $ tm()
19 19 > {
20 20 > hg init t
21 21 > cd t
22 22 > echo "[merge]" >> .hg/hgrc
23 23 > echo "followcopies = 1" >> .hg/hgrc
24 24 >
25 25 > # base
26 26 > echo base > a
27 27 > echo base > rev # used to force commits
28 28 > hg add a rev
29 29 > hg ci -m "base"
30 30 >
31 31 > # remote
32 32 > echo remote > rev
33 33 > if [ "$2" != "" ] ; then $2 ; fi
34 34 > hg ci -m "remote"
35 35 >
36 36 > # local
37 37 > hg co -q 0
38 38 > echo local > rev
39 39 > if [ "$1" != "" ] ; then $1 ; fi
40 40 > hg ci -m "local"
41 41 >
42 42 > # working dir
43 43 > echo local > rev
44 44 > if [ "$3" != "" ] ; then $3 ; fi
45 45 >
46 46 > # merge
47 47 > echo "--------------"
48 48 > echo "test L:$1 R:$2 W:$3 - $4"
49 49 > echo "--------------"
50 50 > hg merge -y --debug --traceback --tool="python ../merge"
51 51 >
52 52 > echo "--------------"
53 53 > hg status -camC -X rev
54 54 >
55 55 > hg ci -m "merge"
56 56 >
57 57 > echo "--------------"
58 58 > echo
59 59 >
60 60 > cd ..
61 61 > rm -r t
62 62 > }
63 63 $ up() {
64 64 > cp rev $1
65 65 > hg add $1 2> /dev/null
66 66 > if [ "$2" != "" ] ; then
67 67 > cp rev $2
68 68 > hg add $2 2> /dev/null
69 69 > fi
70 70 > }
71 71 $ uc() { up $1; hg cp $1 $2; } # update + copy
72 72 $ um() { up $1; hg mv $1 $2; }
73 73 $ nc() { hg cp $1 $2; } # just copy
74 74 $ nm() { hg mv $1 $2; } # just move
75 75 $ tm "up a " "nc a b" " " "1 get local a to b"
76 76 created new head
77 77 --------------
78 78 test L:up a R:nc a b W: - 1 get local a to b
79 79 --------------
80 80 searching for copies back to rev 1
81 81 unmatched files in other:
82 82 b
83 83 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
84 84 src: 'a' -> dst: 'b' *
85 85 checking for directory renames
86 86 resolving manifests
87 87 branchmerge: True, force: False, partial: False
88 88 ancestor: 924404dff337, local: e300d1c794ec+, remote: 4ce40f5aca24
89 89 preserving a for resolve of b
90 90 preserving rev for resolve of rev
91 91 starting 4 threads for background file closing (?)
92 92 a: remote unchanged -> k
93 93 b: remote copied from a -> m (premerge)
94 94 picked tool 'python ../merge' for b (binary False symlink False changedelete False)
95 95 merging a and b to b
96 96 my b@e300d1c794ec+ other b@4ce40f5aca24 ancestor a@924404dff337
97 97 premerge successful
98 98 rev: versions differ -> m (premerge)
99 99 picked tool 'python ../merge' for rev (binary False symlink False changedelete False)
100 100 merging rev
101 101 my rev@e300d1c794ec+ other rev@4ce40f5aca24 ancestor rev@924404dff337
102 102 rev: versions differ -> m (merge)
103 103 picked tool 'python ../merge' for rev (binary False symlink False changedelete False)
104 104 my rev@e300d1c794ec+ other rev@4ce40f5aca24 ancestor rev@924404dff337
105 105 launching merge tool: python ../merge *$TESTTMP/t/t/rev* * * (glob)
106 106 merge tool returned: 0
107 107 0 files updated, 2 files merged, 0 files removed, 0 files unresolved
108 108 (branch merge, don't forget to commit)
109 109 --------------
110 110 M b
111 111 a
112 112 C a
113 113 --------------
114 114
115 115 $ tm "nc a b" "up a " " " "2 get rem change to a and b"
116 116 created new head
117 117 --------------
118 118 test L:nc a b R:up a W: - 2 get rem change to a and b
119 119 --------------
120 120 searching for copies back to rev 1
121 121 unmatched files in local:
122 122 b
123 123 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
124 124 src: 'a' -> dst: 'b' *
125 125 checking for directory renames
126 126 resolving manifests
127 127 branchmerge: True, force: False, partial: False
128 128 ancestor: 924404dff337, local: 86a2aa42fc76+, remote: f4db7e329e71
129 129 preserving b for resolve of b
130 130 preserving rev for resolve of rev
131 131 a: remote is newer -> g
132 132 getting a
133 133 b: local copied/moved from a -> m (premerge)
134 134 picked tool 'python ../merge' for b (binary False symlink False changedelete False)
135 135 merging b and a to b
136 136 my b@86a2aa42fc76+ other a@f4db7e329e71 ancestor a@924404dff337
137 137 premerge successful
138 138 rev: versions differ -> m (premerge)
139 139 picked tool 'python ../merge' for rev (binary False symlink False changedelete False)
140 140 merging rev
141 141 my rev@86a2aa42fc76+ other rev@f4db7e329e71 ancestor rev@924404dff337
142 142 rev: versions differ -> m (merge)
143 143 picked tool 'python ../merge' for rev (binary False symlink False changedelete False)
144 144 my rev@86a2aa42fc76+ other rev@f4db7e329e71 ancestor rev@924404dff337
145 145 launching merge tool: python ../merge *$TESTTMP/t/t/rev* * * (glob)
146 146 merge tool returned: 0
147 147 1 files updated, 2 files merged, 0 files removed, 0 files unresolved
148 148 (branch merge, don't forget to commit)
149 149 --------------
150 150 M a
151 151 M b
152 152 a
153 153 --------------
154 154
155 155 $ tm "up a " "nm a b" " " "3 get local a change to b, remove a"
156 156 created new head
157 157 --------------
158 158 test L:up a R:nm a b W: - 3 get local a change to b, remove a
159 159 --------------
160 160 searching for copies back to rev 1
161 161 unmatched files in other:
162 162 b
163 163 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
164 164 src: 'a' -> dst: 'b' *
165 165 checking for directory renames
166 166 resolving manifests
167 167 branchmerge: True, force: False, partial: False
168 168 ancestor: 924404dff337, local: e300d1c794ec+, remote: bdb19105162a
169 169 preserving a for resolve of b
170 170 preserving rev for resolve of rev
171 171 removing a
172 172 starting 4 threads for background file closing (?)
173 173 b: remote moved from a -> m (premerge)
174 174 picked tool 'python ../merge' for b (binary False symlink False changedelete False)
175 175 merging a and b to b
176 176 my b@e300d1c794ec+ other b@bdb19105162a ancestor a@924404dff337
177 177 premerge successful
178 178 rev: versions differ -> m (premerge)
179 179 picked tool 'python ../merge' for rev (binary False symlink False changedelete False)
180 180 merging rev
181 181 my rev@e300d1c794ec+ other rev@bdb19105162a ancestor rev@924404dff337
182 182 rev: versions differ -> m (merge)
183 183 picked tool 'python ../merge' for rev (binary False symlink False changedelete False)
184 184 my rev@e300d1c794ec+ other rev@bdb19105162a ancestor rev@924404dff337
185 185 launching merge tool: python ../merge *$TESTTMP/t/t/rev* * * (glob)
186 186 merge tool returned: 0
187 187 0 files updated, 2 files merged, 0 files removed, 0 files unresolved
188 188 (branch merge, don't forget to commit)
189 189 --------------
190 190 M b
191 191 a
192 192 --------------
193 193
194 194 $ tm "nm a b" "up a " " " "4 get remote change to b"
195 195 created new head
196 196 --------------
197 197 test L:nm a b R:up a W: - 4 get remote change to b
198 198 --------------
199 199 searching for copies back to rev 1
200 200 unmatched files in local:
201 201 b
202 202 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
203 203 src: 'a' -> dst: 'b' *
204 204 checking for directory renames
205 205 resolving manifests
206 206 branchmerge: True, force: False, partial: False
207 207 ancestor: 924404dff337, local: 02963e448370+, remote: f4db7e329e71
208 208 preserving b for resolve of b
209 209 preserving rev for resolve of rev
210 210 starting 4 threads for background file closing (?)
211 211 b: local copied/moved from a -> m (premerge)
212 212 picked tool 'python ../merge' for b (binary False symlink False changedelete False)
213 213 merging b and a to b
214 214 my b@02963e448370+ other a@f4db7e329e71 ancestor a@924404dff337
215 215 premerge successful
216 216 rev: versions differ -> m (premerge)
217 217 picked tool 'python ../merge' for rev (binary False symlink False changedelete False)
218 218 merging rev
219 219 my rev@02963e448370+ other rev@f4db7e329e71 ancestor rev@924404dff337
220 220 rev: versions differ -> m (merge)
221 221 picked tool 'python ../merge' for rev (binary False symlink False changedelete False)
222 222 my rev@02963e448370+ other rev@f4db7e329e71 ancestor rev@924404dff337
223 223 launching merge tool: python ../merge *$TESTTMP/t/t/rev* * * (glob)
224 224 merge tool returned: 0
225 225 0 files updated, 2 files merged, 0 files removed, 0 files unresolved
226 226 (branch merge, don't forget to commit)
227 227 --------------
228 228 M b
229 229 a
230 230 --------------
231 231
232 232 $ tm " " "nc a b" " " "5 get b"
233 233 created new head
234 234 --------------
235 235 test L: R:nc a b W: - 5 get b
236 236 --------------
237 237 searching for copies back to rev 1
238 238 unmatched files in other:
239 239 b
240 240 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
241 241 src: 'a' -> dst: 'b'
242 242 checking for directory renames
243 243 resolving manifests
244 244 branchmerge: True, force: False, partial: False
245 245 ancestor: 924404dff337, local: 94b33a1b7f2d+, remote: 4ce40f5aca24
246 246 preserving rev for resolve of rev
247 247 b: remote created -> g
248 248 getting b
249 249 rev: versions differ -> m (premerge)
250 250 picked tool 'python ../merge' for rev (binary False symlink False changedelete False)
251 251 merging rev
252 252 my rev@94b33a1b7f2d+ other rev@4ce40f5aca24 ancestor rev@924404dff337
253 253 rev: versions differ -> m (merge)
254 254 picked tool 'python ../merge' for rev (binary False symlink False changedelete False)
255 255 my rev@94b33a1b7f2d+ other rev@4ce40f5aca24 ancestor rev@924404dff337
256 256 launching merge tool: python ../merge *$TESTTMP/t/t/rev* * * (glob)
257 257 merge tool returned: 0
258 258 1 files updated, 1 files merged, 0 files removed, 0 files unresolved
259 259 (branch merge, don't forget to commit)
260 260 --------------
261 261 M b
262 262 C a
263 263 --------------
264 264
265 265 $ tm "nc a b" " " " " "6 nothing"
266 266 created new head
267 267 --------------
268 268 test L:nc a b R: W: - 6 nothing
269 269 --------------
270 270 searching for copies back to rev 1
271 271 unmatched files in local:
272 272 b
273 273 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
274 274 src: 'a' -> dst: 'b'
275 275 checking for directory renames
276 276 resolving manifests
277 277 branchmerge: True, force: False, partial: False
278 278 ancestor: 924404dff337, local: 86a2aa42fc76+, remote: 97c705ade336
279 279 preserving rev for resolve of rev
280 280 starting 4 threads for background file closing (?)
281 281 rev: versions differ -> m (premerge)
282 282 picked tool 'python ../merge' for rev (binary False symlink False changedelete False)
283 283 merging rev
284 284 my rev@86a2aa42fc76+ other rev@97c705ade336 ancestor rev@924404dff337
285 285 rev: versions differ -> m (merge)
286 286 picked tool 'python ../merge' for rev (binary False symlink False changedelete False)
287 287 my rev@86a2aa42fc76+ other rev@97c705ade336 ancestor rev@924404dff337
288 288 launching merge tool: python ../merge *$TESTTMP/t/t/rev* * * (glob)
289 289 merge tool returned: 0
290 290 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
291 291 (branch merge, don't forget to commit)
292 292 --------------
293 293 C a
294 294 C b
295 295 --------------
296 296
297 297 $ tm " " "nm a b" " " "7 get b"
298 298 created new head
299 299 --------------
300 300 test L: R:nm a b W: - 7 get b
301 301 --------------
302 302 searching for copies back to rev 1
303 303 unmatched files in other:
304 304 b
305 305 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
306 306 src: 'a' -> dst: 'b'
307 307 checking for directory renames
308 308 resolving manifests
309 309 branchmerge: True, force: False, partial: False
310 310 ancestor: 924404dff337, local: 94b33a1b7f2d+, remote: bdb19105162a
311 311 preserving rev for resolve of rev
312 312 a: other deleted -> r
313 313 removing a
314 314 b: remote created -> g
315 315 getting b
316 316 rev: versions differ -> m (premerge)
317 317 picked tool 'python ../merge' for rev (binary False symlink False changedelete False)
318 318 merging rev
319 319 my rev@94b33a1b7f2d+ other rev@bdb19105162a ancestor rev@924404dff337
320 320 rev: versions differ -> m (merge)
321 321 picked tool 'python ../merge' for rev (binary False symlink False changedelete False)
322 322 my rev@94b33a1b7f2d+ other rev@bdb19105162a ancestor rev@924404dff337
323 323 launching merge tool: python ../merge *$TESTTMP/t/t/rev* * * (glob)
324 324 merge tool returned: 0
325 325 1 files updated, 1 files merged, 1 files removed, 0 files unresolved
326 326 (branch merge, don't forget to commit)
327 327 --------------
328 328 M b
329 329 --------------
330 330
331 331 $ tm "nm a b" " " " " "8 nothing"
332 332 created new head
333 333 --------------
334 334 test L:nm a b R: W: - 8 nothing
335 335 --------------
336 336 searching for copies back to rev 1
337 337 unmatched files in local:
338 338 b
339 339 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
340 340 src: 'a' -> dst: 'b'
341 341 checking for directory renames
342 342 resolving manifests
343 343 branchmerge: True, force: False, partial: False
344 344 ancestor: 924404dff337, local: 02963e448370+, remote: 97c705ade336
345 345 preserving rev for resolve of rev
346 346 starting 4 threads for background file closing (?)
347 347 rev: versions differ -> m (premerge)
348 348 picked tool 'python ../merge' for rev (binary False symlink False changedelete False)
349 349 merging rev
350 350 my rev@02963e448370+ other rev@97c705ade336 ancestor rev@924404dff337
351 351 rev: versions differ -> m (merge)
352 352 picked tool 'python ../merge' for rev (binary False symlink False changedelete False)
353 353 my rev@02963e448370+ other rev@97c705ade336 ancestor rev@924404dff337
354 354 launching merge tool: python ../merge *$TESTTMP/t/t/rev* * * (glob)
355 355 merge tool returned: 0
356 356 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
357 357 (branch merge, don't forget to commit)
358 358 --------------
359 359 C b
360 360 --------------
361 361
362 362 $ tm "um a b" "um a b" " " "9 do merge with ancestor in a"
363 363 created new head
364 364 --------------
365 365 test L:um a b R:um a b W: - 9 do merge with ancestor in a
366 366 --------------
367 367 searching for copies back to rev 1
368 368 unmatched files new in both:
369 369 b
370 370 resolving manifests
371 371 branchmerge: True, force: False, partial: False
372 372 ancestor: 924404dff337, local: 62e7bf090eba+, remote: 49b6d8032493
373 373 preserving b for resolve of b
374 374 preserving rev for resolve of rev
375 375 starting 4 threads for background file closing (?)
376 376 b: both renamed from a -> m (premerge)
377 377 picked tool 'python ../merge' for b (binary False symlink False changedelete False)
378 378 merging b
379 379 my b@62e7bf090eba+ other b@49b6d8032493 ancestor a@924404dff337
380 380 rev: versions differ -> m (premerge)
381 381 picked tool 'python ../merge' for rev (binary False symlink False changedelete False)
382 382 merging rev
383 383 my rev@62e7bf090eba+ other rev@49b6d8032493 ancestor rev@924404dff337
384 384 b: both renamed from a -> m (merge)
385 385 picked tool 'python ../merge' for b (binary False symlink False changedelete False)
386 386 my b@62e7bf090eba+ other b@49b6d8032493 ancestor a@924404dff337
387 387 launching merge tool: python ../merge *$TESTTMP/t/t/b* * * (glob)
388 388 merge tool returned: 0
389 389 rev: versions differ -> m (merge)
390 390 picked tool 'python ../merge' for rev (binary False symlink False changedelete False)
391 391 my rev@62e7bf090eba+ other rev@49b6d8032493 ancestor rev@924404dff337
392 392 launching merge tool: python ../merge *$TESTTMP/t/t/rev* * * (glob)
393 393 merge tool returned: 0
394 394 0 files updated, 2 files merged, 0 files removed, 0 files unresolved
395 395 (branch merge, don't forget to commit)
396 396 --------------
397 397 M b
398 398 --------------
399 399
400 400
401 401 m "um a c" "um x c" " " "10 do merge with no ancestor"
402 402
403 403 $ tm "nm a b" "nm a c" " " "11 get c, keep b"
404 404 created new head
405 405 --------------
406 406 test L:nm a b R:nm a c W: - 11 get c, keep b
407 407 --------------
408 408 searching for copies back to rev 1
409 409 unmatched files in local:
410 410 b
411 411 unmatched files in other:
412 412 c
413 413 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
414 414 src: 'a' -> dst: 'b' !
415 415 src: 'a' -> dst: 'c' !
416 416 checking for directory renames
417 417 resolving manifests
418 418 branchmerge: True, force: False, partial: False
419 419 ancestor: 924404dff337, local: 02963e448370+, remote: fe905ef2c33e
420 420 note: possible conflict - a was renamed multiple times to:
421 421 b
422 422 c
423 423 preserving rev for resolve of rev
424 424 c: remote created -> g
425 425 getting c
426 426 rev: versions differ -> m (premerge)
427 427 picked tool 'python ../merge' for rev (binary False symlink False changedelete False)
428 428 merging rev
429 429 my rev@02963e448370+ other rev@fe905ef2c33e ancestor rev@924404dff337
430 430 rev: versions differ -> m (merge)
431 431 picked tool 'python ../merge' for rev (binary False symlink False changedelete False)
432 432 my rev@02963e448370+ other rev@fe905ef2c33e ancestor rev@924404dff337
433 433 launching merge tool: python ../merge *$TESTTMP/t/t/rev* * * (glob)
434 434 merge tool returned: 0
435 435 1 files updated, 1 files merged, 0 files removed, 0 files unresolved
436 436 (branch merge, don't forget to commit)
437 437 --------------
438 438 M c
439 439 C b
440 440 --------------
441 441
442 442 $ tm "nc a b" "up b " " " "12 merge b no ancestor"
443 443 created new head
444 444 --------------
445 445 test L:nc a b R:up b W: - 12 merge b no ancestor
446 446 --------------
447 447 searching for copies back to rev 1
448 448 unmatched files new in both:
449 449 b
450 450 resolving manifests
451 451 branchmerge: True, force: False, partial: False
452 452 ancestor: 924404dff337, local: 86a2aa42fc76+, remote: af30c7647fc7
453 453 preserving b for resolve of b
454 454 preserving rev for resolve of rev
455 455 starting 4 threads for background file closing (?)
456 456 b: both created -> m (premerge)
457 457 picked tool 'python ../merge' for b (binary False symlink False changedelete False)
458 458 merging b
459 459 my b@86a2aa42fc76+ other b@af30c7647fc7 ancestor b@000000000000
460 460 rev: versions differ -> m (premerge)
461 461 picked tool 'python ../merge' for rev (binary False symlink False changedelete False)
462 462 merging rev
463 463 my rev@86a2aa42fc76+ other rev@af30c7647fc7 ancestor rev@924404dff337
464 464 b: both created -> m (merge)
465 465 picked tool 'python ../merge' for b (binary False symlink False changedelete False)
466 466 my b@86a2aa42fc76+ other b@af30c7647fc7 ancestor b@000000000000
467 467 launching merge tool: python ../merge *$TESTTMP/t/t/b* * * (glob)
468 468 merge tool returned: 0
469 469 rev: versions differ -> m (merge)
470 470 picked tool 'python ../merge' for rev (binary False symlink False changedelete False)
471 471 my rev@86a2aa42fc76+ other rev@af30c7647fc7 ancestor rev@924404dff337
472 472 launching merge tool: python ../merge *$TESTTMP/t/t/rev* * * (glob)
473 473 merge tool returned: 0
474 474 0 files updated, 2 files merged, 0 files removed, 0 files unresolved
475 475 (branch merge, don't forget to commit)
476 476 --------------
477 477 M b
478 478 C a
479 479 --------------
480 480
481 481 $ tm "up b " "nm a b" " " "13 merge b no ancestor"
482 482 created new head
483 483 --------------
484 484 test L:up b R:nm a b W: - 13 merge b no ancestor
485 485 --------------
486 486 searching for copies back to rev 1
487 487 unmatched files new in both:
488 488 b
489 489 resolving manifests
490 490 branchmerge: True, force: False, partial: False
491 491 ancestor: 924404dff337, local: 59318016310c+, remote: bdb19105162a
492 492 preserving b for resolve of b
493 493 preserving rev for resolve of rev
494 494 a: other deleted -> r
495 495 removing a
496 496 starting 4 threads for background file closing (?)
497 497 b: both created -> m (premerge)
498 498 picked tool 'python ../merge' for b (binary False symlink False changedelete False)
499 499 merging b
500 500 my b@59318016310c+ other b@bdb19105162a ancestor b@000000000000
501 501 rev: versions differ -> m (premerge)
502 502 picked tool 'python ../merge' for rev (binary False symlink False changedelete False)
503 503 merging rev
504 504 my rev@59318016310c+ other rev@bdb19105162a ancestor rev@924404dff337
505 505 b: both created -> m (merge)
506 506 picked tool 'python ../merge' for b (binary False symlink False changedelete False)
507 507 my b@59318016310c+ other b@bdb19105162a ancestor b@000000000000
508 508 launching merge tool: python ../merge *$TESTTMP/t/t/b* * * (glob)
509 509 merge tool returned: 0
510 510 rev: versions differ -> m (merge)
511 511 picked tool 'python ../merge' for rev (binary False symlink False changedelete False)
512 512 my rev@59318016310c+ other rev@bdb19105162a ancestor rev@924404dff337
513 513 launching merge tool: python ../merge *$TESTTMP/t/t/rev* * * (glob)
514 514 merge tool returned: 0
515 515 0 files updated, 2 files merged, 1 files removed, 0 files unresolved
516 516 (branch merge, don't forget to commit)
517 517 --------------
518 518 M b
519 519 --------------
520 520
521 521 $ tm "nc a b" "up a b" " " "14 merge b no ancestor"
522 522 created new head
523 523 --------------
524 524 test L:nc a b R:up a b W: - 14 merge b no ancestor
525 525 --------------
526 526 searching for copies back to rev 1
527 527 unmatched files new in both:
528 528 b
529 529 resolving manifests
530 530 branchmerge: True, force: False, partial: False
531 531 ancestor: 924404dff337, local: 86a2aa42fc76+, remote: 8dbce441892a
532 532 preserving b for resolve of b
533 533 preserving rev for resolve of rev
534 534 a: remote is newer -> g
535 535 getting a
536 536 b: both created -> m (premerge)
537 537 picked tool 'python ../merge' for b (binary False symlink False changedelete False)
538 538 merging b
539 539 my b@86a2aa42fc76+ other b@8dbce441892a ancestor b@000000000000
540 540 rev: versions differ -> m (premerge)
541 541 picked tool 'python ../merge' for rev (binary False symlink False changedelete False)
542 542 merging rev
543 543 my rev@86a2aa42fc76+ other rev@8dbce441892a ancestor rev@924404dff337
544 544 b: both created -> m (merge)
545 545 picked tool 'python ../merge' for b (binary False symlink False changedelete False)
546 546 my b@86a2aa42fc76+ other b@8dbce441892a ancestor b@000000000000
547 547 launching merge tool: python ../merge *$TESTTMP/t/t/b* * * (glob)
548 548 merge tool returned: 0
549 549 rev: versions differ -> m (merge)
550 550 picked tool 'python ../merge' for rev (binary False symlink False changedelete False)
551 551 my rev@86a2aa42fc76+ other rev@8dbce441892a ancestor rev@924404dff337
552 552 launching merge tool: python ../merge *$TESTTMP/t/t/rev* * * (glob)
553 553 merge tool returned: 0
554 554 1 files updated, 2 files merged, 0 files removed, 0 files unresolved
555 555 (branch merge, don't forget to commit)
556 556 --------------
557 557 M a
558 558 M b
559 559 --------------
560 560
561 561 $ tm "up b " "nm a b" " " "15 merge b no ancestor, remove a"
562 562 created new head
563 563 --------------
564 564 test L:up b R:nm a b W: - 15 merge b no ancestor, remove a
565 565 --------------
566 566 searching for copies back to rev 1
567 567 unmatched files new in both:
568 568 b
569 569 resolving manifests
570 570 branchmerge: True, force: False, partial: False
571 571 ancestor: 924404dff337, local: 59318016310c+, remote: bdb19105162a
572 572 preserving b for resolve of b
573 573 preserving rev for resolve of rev
574 574 a: other deleted -> r
575 575 removing a
576 576 starting 4 threads for background file closing (?)
577 577 b: both created -> m (premerge)
578 578 picked tool 'python ../merge' for b (binary False symlink False changedelete False)
579 579 merging b
580 580 my b@59318016310c+ other b@bdb19105162a ancestor b@000000000000
581 581 rev: versions differ -> m (premerge)
582 582 picked tool 'python ../merge' for rev (binary False symlink False changedelete False)
583 583 merging rev
584 584 my rev@59318016310c+ other rev@bdb19105162a ancestor rev@924404dff337
585 585 b: both created -> m (merge)
586 586 picked tool 'python ../merge' for b (binary False symlink False changedelete False)
587 587 my b@59318016310c+ other b@bdb19105162a ancestor b@000000000000
588 588 launching merge tool: python ../merge *$TESTTMP/t/t/b* * * (glob)
589 589 merge tool returned: 0
590 590 rev: versions differ -> m (merge)
591 591 picked tool 'python ../merge' for rev (binary False symlink False changedelete False)
592 592 my rev@59318016310c+ other rev@bdb19105162a ancestor rev@924404dff337
593 593 launching merge tool: python ../merge *$TESTTMP/t/t/rev* * * (glob)
594 594 merge tool returned: 0
595 595 0 files updated, 2 files merged, 1 files removed, 0 files unresolved
596 596 (branch merge, don't forget to commit)
597 597 --------------
598 598 M b
599 599 --------------
600 600
601 601 $ tm "nc a b" "up a b" " " "16 get a, merge b no ancestor"
602 602 created new head
603 603 --------------
604 604 test L:nc a b R:up a b W: - 16 get a, merge b no ancestor
605 605 --------------
606 606 searching for copies back to rev 1
607 607 unmatched files new in both:
608 608 b
609 609 resolving manifests
610 610 branchmerge: True, force: False, partial: False
611 611 ancestor: 924404dff337, local: 86a2aa42fc76+, remote: 8dbce441892a
612 612 preserving b for resolve of b
613 613 preserving rev for resolve of rev
614 614 a: remote is newer -> g
615 615 getting a
616 616 b: both created -> m (premerge)
617 617 picked tool 'python ../merge' for b (binary False symlink False changedelete False)
618 618 merging b
619 619 my b@86a2aa42fc76+ other b@8dbce441892a ancestor b@000000000000
620 620 rev: versions differ -> m (premerge)
621 621 picked tool 'python ../merge' for rev (binary False symlink False changedelete False)
622 622 merging rev
623 623 my rev@86a2aa42fc76+ other rev@8dbce441892a ancestor rev@924404dff337
624 624 b: both created -> m (merge)
625 625 picked tool 'python ../merge' for b (binary False symlink False changedelete False)
626 626 my b@86a2aa42fc76+ other b@8dbce441892a ancestor b@000000000000
627 627 launching merge tool: python ../merge *$TESTTMP/t/t/b* * * (glob)
628 628 merge tool returned: 0
629 629 rev: versions differ -> m (merge)
630 630 picked tool 'python ../merge' for rev (binary False symlink False changedelete False)
631 631 my rev@86a2aa42fc76+ other rev@8dbce441892a ancestor rev@924404dff337
632 632 launching merge tool: python ../merge *$TESTTMP/t/t/rev* * * (glob)
633 633 merge tool returned: 0
634 634 1 files updated, 2 files merged, 0 files removed, 0 files unresolved
635 635 (branch merge, don't forget to commit)
636 636 --------------
637 637 M a
638 638 M b
639 639 --------------
640 640
641 641 $ tm "up a b" "nc a b" " " "17 keep a, merge b no ancestor"
642 642 created new head
643 643 --------------
644 644 test L:up a b R:nc a b W: - 17 keep a, merge b no ancestor
645 645 --------------
646 646 searching for copies back to rev 1
647 647 unmatched files new in both:
648 648 b
649 649 resolving manifests
650 650 branchmerge: True, force: False, partial: False
651 651 ancestor: 924404dff337, local: 0b76e65c8289+, remote: 4ce40f5aca24
652 652 preserving b for resolve of b
653 653 preserving rev for resolve of rev
654 654 starting 4 threads for background file closing (?)
655 655 a: remote unchanged -> k
656 656 b: both created -> m (premerge)
657 657 picked tool 'python ../merge' for b (binary False symlink False changedelete False)
658 658 merging b
659 659 my b@0b76e65c8289+ other b@4ce40f5aca24 ancestor b@000000000000
660 660 rev: versions differ -> m (premerge)
661 661 picked tool 'python ../merge' for rev (binary False symlink False changedelete False)
662 662 merging rev
663 663 my rev@0b76e65c8289+ other rev@4ce40f5aca24 ancestor rev@924404dff337
664 664 b: both created -> m (merge)
665 665 picked tool 'python ../merge' for b (binary False symlink False changedelete False)
666 666 my b@0b76e65c8289+ other b@4ce40f5aca24 ancestor b@000000000000
667 667 launching merge tool: python ../merge *$TESTTMP/t/t/b* * * (glob)
668 668 merge tool returned: 0
669 669 rev: versions differ -> m (merge)
670 670 picked tool 'python ../merge' for rev (binary False symlink False changedelete False)
671 671 my rev@0b76e65c8289+ other rev@4ce40f5aca24 ancestor rev@924404dff337
672 672 launching merge tool: python ../merge *$TESTTMP/t/t/rev* * * (glob)
673 673 merge tool returned: 0
674 674 0 files updated, 2 files merged, 0 files removed, 0 files unresolved
675 675 (branch merge, don't forget to commit)
676 676 --------------
677 677 M b
678 678 C a
679 679 --------------
680 680
681 681 $ tm "nm a b" "up a b" " " "18 merge b no ancestor"
682 682 created new head
683 683 --------------
684 684 test L:nm a b R:up a b W: - 18 merge b no ancestor
685 685 --------------
686 686 searching for copies back to rev 1
687 687 unmatched files new in both:
688 688 b
689 689 resolving manifests
690 690 branchmerge: True, force: False, partial: False
691 691 ancestor: 924404dff337, local: 02963e448370+, remote: 8dbce441892a
692 692 preserving b for resolve of b
693 693 preserving rev for resolve of rev
694 694 starting 4 threads for background file closing (?)
695 695 a: prompt deleted/changed -> m (premerge)
696 696 picked tool ':prompt' for a (binary False symlink False changedelete True)
697 697 other changed a which local deleted
698 698 use (c)hanged version, leave (d)eleted, or leave (u)nresolved? u
699 699 b: both created -> m (premerge)
700 700 picked tool 'python ../merge' for b (binary False symlink False changedelete False)
701 701 merging b
702 702 my b@02963e448370+ other b@8dbce441892a ancestor b@000000000000
703 703 rev: versions differ -> m (premerge)
704 704 picked tool 'python ../merge' for rev (binary False symlink False changedelete False)
705 705 merging rev
706 706 my rev@02963e448370+ other rev@8dbce441892a ancestor rev@924404dff337
707 707 b: both created -> m (merge)
708 708 picked tool 'python ../merge' for b (binary False symlink False changedelete False)
709 709 my b@02963e448370+ other b@8dbce441892a ancestor b@000000000000
710 710 launching merge tool: python ../merge *$TESTTMP/t/t/b* * * (glob)
711 711 merge tool returned: 0
712 712 rev: versions differ -> m (merge)
713 713 picked tool 'python ../merge' for rev (binary False symlink False changedelete False)
714 714 my rev@02963e448370+ other rev@8dbce441892a ancestor rev@924404dff337
715 715 launching merge tool: python ../merge *$TESTTMP/t/t/rev* * * (glob)
716 716 merge tool returned: 0
717 717 0 files updated, 2 files merged, 0 files removed, 1 files unresolved
718 718 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
719 719 --------------
720 720 M a
721 721 M b
722 abort: unresolved merge conflicts (see "hg help resolve")
722 abort: unresolved merge conflicts (see 'hg help resolve')
723 723 --------------
724 724
725 725 $ tm "up a b" "nm a b" " " "19 merge b no ancestor, prompt remove a"
726 726 created new head
727 727 --------------
728 728 test L:up a b R:nm a b W: - 19 merge b no ancestor, prompt remove a
729 729 --------------
730 730 searching for copies back to rev 1
731 731 unmatched files new in both:
732 732 b
733 733 resolving manifests
734 734 branchmerge: True, force: False, partial: False
735 735 ancestor: 924404dff337, local: 0b76e65c8289+, remote: bdb19105162a
736 736 preserving a for resolve of a
737 737 preserving b for resolve of b
738 738 preserving rev for resolve of rev
739 739 starting 4 threads for background file closing (?)
740 740 a: prompt changed/deleted -> m (premerge)
741 741 picked tool ':prompt' for a (binary False symlink False changedelete True)
742 742 local changed a which other deleted
743 743 use (c)hanged version, (d)elete, or leave (u)nresolved? u
744 744 b: both created -> m (premerge)
745 745 picked tool 'python ../merge' for b (binary False symlink False changedelete False)
746 746 merging b
747 747 my b@0b76e65c8289+ other b@bdb19105162a ancestor b@000000000000
748 748 rev: versions differ -> m (premerge)
749 749 picked tool 'python ../merge' for rev (binary False symlink False changedelete False)
750 750 merging rev
751 751 my rev@0b76e65c8289+ other rev@bdb19105162a ancestor rev@924404dff337
752 752 b: both created -> m (merge)
753 753 picked tool 'python ../merge' for b (binary False symlink False changedelete False)
754 754 my b@0b76e65c8289+ other b@bdb19105162a ancestor b@000000000000
755 755 launching merge tool: python ../merge *$TESTTMP/t/t/b* * * (glob)
756 756 merge tool returned: 0
757 757 rev: versions differ -> m (merge)
758 758 picked tool 'python ../merge' for rev (binary False symlink False changedelete False)
759 759 my rev@0b76e65c8289+ other rev@bdb19105162a ancestor rev@924404dff337
760 760 launching merge tool: python ../merge *$TESTTMP/t/t/rev* * * (glob)
761 761 merge tool returned: 0
762 762 0 files updated, 2 files merged, 0 files removed, 1 files unresolved
763 763 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
764 764 --------------
765 765 M b
766 766 C a
767 abort: unresolved merge conflicts (see "hg help resolve")
767 abort: unresolved merge conflicts (see 'hg help resolve')
768 768 --------------
769 769
770 770 $ tm "up a " "um a b" " " "20 merge a and b to b, remove a"
771 771 created new head
772 772 --------------
773 773 test L:up a R:um a b W: - 20 merge a and b to b, remove a
774 774 --------------
775 775 searching for copies back to rev 1
776 776 unmatched files in other:
777 777 b
778 778 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
779 779 src: 'a' -> dst: 'b' *
780 780 checking for directory renames
781 781 resolving manifests
782 782 branchmerge: True, force: False, partial: False
783 783 ancestor: 924404dff337, local: e300d1c794ec+, remote: 49b6d8032493
784 784 preserving a for resolve of b
785 785 preserving rev for resolve of rev
786 786 removing a
787 787 starting 4 threads for background file closing (?)
788 788 b: remote moved from a -> m (premerge)
789 789 picked tool 'python ../merge' for b (binary False symlink False changedelete False)
790 790 merging a and b to b
791 791 my b@e300d1c794ec+ other b@49b6d8032493 ancestor a@924404dff337
792 792 rev: versions differ -> m (premerge)
793 793 picked tool 'python ../merge' for rev (binary False symlink False changedelete False)
794 794 merging rev
795 795 my rev@e300d1c794ec+ other rev@49b6d8032493 ancestor rev@924404dff337
796 796 b: remote moved from a -> m (merge)
797 797 picked tool 'python ../merge' for b (binary False symlink False changedelete False)
798 798 my b@e300d1c794ec+ other b@49b6d8032493 ancestor a@924404dff337
799 799 launching merge tool: python ../merge *$TESTTMP/t/t/b* * * (glob)
800 800 merge tool returned: 0
801 801 rev: versions differ -> m (merge)
802 802 picked tool 'python ../merge' for rev (binary False symlink False changedelete False)
803 803 my rev@e300d1c794ec+ other rev@49b6d8032493 ancestor rev@924404dff337
804 804 launching merge tool: python ../merge *$TESTTMP/t/t/rev* * * (glob)
805 805 merge tool returned: 0
806 806 0 files updated, 2 files merged, 0 files removed, 0 files unresolved
807 807 (branch merge, don't forget to commit)
808 808 --------------
809 809 M b
810 810 a
811 811 --------------
812 812
813 813 $ tm "um a b" "up a " " " "21 merge a and b to b"
814 814 created new head
815 815 --------------
816 816 test L:um a b R:up a W: - 21 merge a and b to b
817 817 --------------
818 818 searching for copies back to rev 1
819 819 unmatched files in local:
820 820 b
821 821 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
822 822 src: 'a' -> dst: 'b' *
823 823 checking for directory renames
824 824 resolving manifests
825 825 branchmerge: True, force: False, partial: False
826 826 ancestor: 924404dff337, local: 62e7bf090eba+, remote: f4db7e329e71
827 827 preserving b for resolve of b
828 828 preserving rev for resolve of rev
829 829 starting 4 threads for background file closing (?)
830 830 b: local copied/moved from a -> m (premerge)
831 831 picked tool 'python ../merge' for b (binary False symlink False changedelete False)
832 832 merging b and a to b
833 833 my b@62e7bf090eba+ other a@f4db7e329e71 ancestor a@924404dff337
834 834 rev: versions differ -> m (premerge)
835 835 picked tool 'python ../merge' for rev (binary False symlink False changedelete False)
836 836 merging rev
837 837 my rev@62e7bf090eba+ other rev@f4db7e329e71 ancestor rev@924404dff337
838 838 b: local copied/moved from a -> m (merge)
839 839 picked tool 'python ../merge' for b (binary False symlink False changedelete False)
840 840 my b@62e7bf090eba+ other a@f4db7e329e71 ancestor a@924404dff337
841 841 launching merge tool: python ../merge *$TESTTMP/t/t/b* * * (glob)
842 842 merge tool returned: 0
843 843 rev: versions differ -> m (merge)
844 844 picked tool 'python ../merge' for rev (binary False symlink False changedelete False)
845 845 my rev@62e7bf090eba+ other rev@f4db7e329e71 ancestor rev@924404dff337
846 846 launching merge tool: python ../merge *$TESTTMP/t/t/rev* * * (glob)
847 847 merge tool returned: 0
848 848 0 files updated, 2 files merged, 0 files removed, 0 files unresolved
849 849 (branch merge, don't forget to commit)
850 850 --------------
851 851 M b
852 852 a
853 853 --------------
854 854
855 855
856 856 m "nm a b" "um x a" " " "22 get a, keep b"
857 857
858 858 $ tm "nm a b" "up a c" " " "23 get c, keep b"
859 859 created new head
860 860 --------------
861 861 test L:nm a b R:up a c W: - 23 get c, keep b
862 862 --------------
863 863 searching for copies back to rev 1
864 864 unmatched files in local:
865 865 b
866 866 unmatched files in other:
867 867 c
868 868 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
869 869 src: 'a' -> dst: 'b' *
870 870 checking for directory renames
871 871 resolving manifests
872 872 branchmerge: True, force: False, partial: False
873 873 ancestor: 924404dff337, local: 02963e448370+, remote: 2b958612230f
874 874 preserving b for resolve of b
875 875 preserving rev for resolve of rev
876 876 c: remote created -> g
877 877 getting c
878 878 b: local copied/moved from a -> m (premerge)
879 879 picked tool 'python ../merge' for b (binary False symlink False changedelete False)
880 880 merging b and a to b
881 881 my b@02963e448370+ other a@2b958612230f ancestor a@924404dff337
882 882 premerge successful
883 883 rev: versions differ -> m (premerge)
884 884 picked tool 'python ../merge' for rev (binary False symlink False changedelete False)
885 885 merging rev
886 886 my rev@02963e448370+ other rev@2b958612230f ancestor rev@924404dff337
887 887 rev: versions differ -> m (merge)
888 888 picked tool 'python ../merge' for rev (binary False symlink False changedelete False)
889 889 my rev@02963e448370+ other rev@2b958612230f ancestor rev@924404dff337
890 890 launching merge tool: python ../merge *$TESTTMP/t/t/rev* * * (glob)
891 891 merge tool returned: 0
892 892 1 files updated, 2 files merged, 0 files removed, 0 files unresolved
893 893 (branch merge, don't forget to commit)
894 894 --------------
895 895 M b
896 896 a
897 897 M c
898 898 --------------
899 899
900 900
901 901 $ cd ..
902 902
903 903
904 904 Systematic and terse testing of merge merges and ancestor calculation:
905 905
906 906 Expected result:
907 907
908 908 \ a m1 m2 dst
909 909 0 - f f f "versions differ"
910 910 1 f g g g "versions differ"
911 911 2 f f f f "versions differ"
912 912 3 f f g f+g "remote copied to " + f
913 913 4 f f g g "remote moved to " + f
914 914 5 f g f f+g "local copied to " + f2
915 915 6 f g f g "local moved to " + f2
916 916 7 - (f) f f "remote differs from untracked local"
917 917 8 f (f) f f "remote differs from untracked local"
918 918
919 919 $ hg init ancestortest
920 920 $ cd ancestortest
921 921 $ for x in 1 2 3 4 5 6 8; do mkdir $x; echo a > $x/f; done
922 922 $ hg ci -Aqm "a"
923 923 $ mkdir 0
924 924 $ touch 0/f
925 925 $ hg mv 1/f 1/g
926 926 $ hg cp 5/f 5/g
927 927 $ hg mv 6/f 6/g
928 928 $ hg rm 8/f
929 929 $ for x in */*; do echo m1 > $x; done
930 930 $ hg ci -Aqm "m1"
931 931 $ hg up -qr0
932 932 $ mkdir 0 7
933 933 $ touch 0/f 7/f
934 934 $ hg mv 1/f 1/g
935 935 $ hg cp 3/f 3/g
936 936 $ hg mv 4/f 4/g
937 937 $ for x in */*; do echo m2 > $x; done
938 938 $ hg ci -Aqm "m2"
939 939 $ hg up -qr1
940 940 $ mkdir 7 8
941 941 $ echo m > 7/f
942 942 $ echo m > 8/f
943 943 $ hg merge -f --tool internal:dump -v --debug -r2 | sed '/^resolving manifests/,$d' 2> /dev/null
944 944 searching for copies back to rev 1
945 945 unmatched files in local:
946 946 5/g
947 947 6/g
948 948 unmatched files in other:
949 949 3/g
950 950 4/g
951 951 7/f
952 952 unmatched files new in both:
953 953 0/f
954 954 1/g
955 955 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
956 956 src: '3/f' -> dst: '3/g' *
957 957 src: '4/f' -> dst: '4/g' *
958 958 src: '5/f' -> dst: '5/g' *
959 959 src: '6/f' -> dst: '6/g' *
960 960 checking for directory renames
961 961 $ hg mani
962 962 0/f
963 963 1/g
964 964 2/f
965 965 3/f
966 966 4/f
967 967 5/f
968 968 5/g
969 969 6/g
970 970 $ for f in */*; do echo $f:; cat $f; done
971 971 0/f:
972 972 m1
973 973 0/f.base:
974 974 0/f.local:
975 975 m1
976 976 0/f.orig:
977 977 m1
978 978 0/f.other:
979 979 m2
980 980 1/g:
981 981 m1
982 982 1/g.base:
983 983 a
984 984 1/g.local:
985 985 m1
986 986 1/g.orig:
987 987 m1
988 988 1/g.other:
989 989 m2
990 990 2/f:
991 991 m1
992 992 2/f.base:
993 993 a
994 994 2/f.local:
995 995 m1
996 996 2/f.orig:
997 997 m1
998 998 2/f.other:
999 999 m2
1000 1000 3/f:
1001 1001 m1
1002 1002 3/f.base:
1003 1003 a
1004 1004 3/f.local:
1005 1005 m1
1006 1006 3/f.orig:
1007 1007 m1
1008 1008 3/f.other:
1009 1009 m2
1010 1010 3/g:
1011 1011 m1
1012 1012 3/g.base:
1013 1013 a
1014 1014 3/g.local:
1015 1015 m1
1016 1016 3/g.orig:
1017 1017 m1
1018 1018 3/g.other:
1019 1019 m2
1020 1020 4/g:
1021 1021 m1
1022 1022 4/g.base:
1023 1023 a
1024 1024 4/g.local:
1025 1025 m1
1026 1026 4/g.orig:
1027 1027 m1
1028 1028 4/g.other:
1029 1029 m2
1030 1030 5/f:
1031 1031 m1
1032 1032 5/f.base:
1033 1033 a
1034 1034 5/f.local:
1035 1035 m1
1036 1036 5/f.orig:
1037 1037 m1
1038 1038 5/f.other:
1039 1039 m2
1040 1040 5/g:
1041 1041 m1
1042 1042 5/g.base:
1043 1043 a
1044 1044 5/g.local:
1045 1045 m1
1046 1046 5/g.orig:
1047 1047 m1
1048 1048 5/g.other:
1049 1049 m2
1050 1050 6/g:
1051 1051 m1
1052 1052 6/g.base:
1053 1053 a
1054 1054 6/g.local:
1055 1055 m1
1056 1056 6/g.orig:
1057 1057 m1
1058 1058 6/g.other:
1059 1059 m2
1060 1060 7/f:
1061 1061 m
1062 1062 7/f.base:
1063 1063 7/f.local:
1064 1064 m
1065 1065 7/f.orig:
1066 1066 m
1067 1067 7/f.other:
1068 1068 m2
1069 1069 8/f:
1070 1070 m2
1071 1071 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now