comparison hgext/evolve.py @ 589:8945a62f9096

merge with stable
author Pierre-Yves David <pierre-yves.david@logilab.fr>
date Tue, 23 Oct 2012 16:53:11 +0200
parents f6063ef211fd 89c8550019d0
children 02cadd3dc9f4
comparison
equal deleted inserted replaced
583:95089805c3fc 589:8945a62f9096
17 - alters core commands and extensions that rewrite history to use 17 - alters core commands and extensions that rewrite history to use
18 this feature, 18 this feature,
19 - improves some aspect of the early implementation in 2.3 19 - improves some aspect of the early implementation in 2.3
20 ''' 20 '''
21 21
22 testedwith = '2.3 2.3.1 2.3.2'
23 buglink = 'https://bitbucket.org/marmoute/mutable-history/issues'
24
25
22 import random 26 import random
23 27
24 from mercurial import util 28 from mercurial import util
25 29
26 try: 30 try:
27 from mercurial import obsolete 31 from mercurial import obsolete
28 if not obsolete._enabled: 32 if not obsolete._enabled:
29 obsolete._enabled = True 33 obsolete._enabled = True
30 except ImportError: 34 except ImportError:
31 raise util.Abort('Obsolete extension requires Mercurial 2.3 (or later)') 35 raise util.Abort('Evolve extension requires Mercurial 2.3 (or later)')
36
37 try:
38 getattr(obsolete, 'getrevs') # 2.4 specific
39 raise util.Abort('Your version of Mercurial is too recent for this '
40 'version of evolve',
41 hint="upgrade your evolve")
42 except AttributeError:
43 pass
44
32 45
33 from mercurial import bookmarks 46 from mercurial import bookmarks
34 from mercurial import cmdutil 47 from mercurial import cmdutil
35 from mercurial import commands 48 from mercurial import commands
36 from mercurial import context 49 from mercurial import context
322 ### Complete troubles computation logic ### 335 ### Complete troubles computation logic ###
323 ##################################################################### 336 #####################################################################
324 337
325 # there is two kind of trouble not handled by core right now: 338 # there is two kind of trouble not handled by core right now:
326 # - latecomer: (successors for public changeset) 339 # - latecomer: (successors for public changeset)
327 # - conflicting: (two changeset try to succeed to the same precursors) 340 # - divergent: (two changeset try to succeed to the same precursors)
328 # 341 #
329 # This section add support for those two addition trouble 342 # This section add support for those two addition trouble
330 # 343 #
331 # - Cache computation 344 # - Cache computation
332 # - revset and ctx method 345 # - revset and ctx method
341 candidates = _allsuccessors(repo, repo.revs('public()'), 354 candidates = _allsuccessors(repo, repo.revs('public()'),
342 haltonflags=latediff) 355 haltonflags=latediff)
343 query = '%ld - obsolete() - public()' 356 query = '%ld - obsolete() - public()'
344 return set(repo.revs(query, candidates)) 357 return set(repo.revs(query, candidates))
345 358
346 @cachefor('conflicting') 359 @cachefor('divergent')
347 def _computeconflictingset(repo): 360 def _computedivergentset(repo):
348 """the set of rev trying to obsolete public revision""" 361 """the set of rev trying to obsolete public revision"""
349 conflicting = set() 362 divergent = set()
350 obsstore = repo.obsstore 363 obsstore = repo.obsstore
351 newermap = {} 364 newermap = {}
352 for ctx in repo.set('(not public()) - obsolete()'): 365 for ctx in repo.set('(not public()) - obsolete()'):
353 prec = obsstore.successors.get(ctx.node(), ()) 366 mark = obsstore.successors.get(ctx.node(), ())
354 toprocess = set(prec) 367 toprocess = set(mark)
355 while toprocess: 368 while toprocess:
356 prec = toprocess.pop()[0] 369 prec = toprocess.pop()[0]
357 if prec not in newermap: 370 if prec not in newermap:
358 newermap[prec] = newerversion(repo, prec) 371 successorssets(repo, prec, newermap)
359 newer = [n for n in newermap[prec] if n] # filter kill 372 newer = [n for n in newermap[prec] if n]
360 if len(newer) > 1: 373 if len(newer) > 1:
361 conflicting.add(ctx.rev()) 374 divergent.add(ctx.rev())
362 break 375 break
363 toprocess.update(obsstore.successors.get(prec, ())) 376 toprocess.update(obsstore.successors.get(prec, ()))
364 return conflicting 377 return divergent
365 378
366 ### changectx method 379 ### changectx method
367 380
368 @eh.addattr(context.changectx, 'latecomer') 381 @eh.addattr(context.changectx, 'latecomer')
369 def latecomer(ctx): 382 def latecomer(ctx):
371 if ctx.node() is None: 384 if ctx.node() is None:
372 return False 385 return False
373 return ctx.rev() in getobscache(ctx._repo, 'latecomer') 386 return ctx.rev() in getobscache(ctx._repo, 'latecomer')
374 387
375 @eh.addattr(context.changectx, 'conflicting') 388 @eh.addattr(context.changectx, 'conflicting')
376 def conflicting(ctx): 389 @eh.addattr(context.changectx, 'divergent')
377 """is the changeset conflicting (Try to succeed to public change)""" 390 def divergent(ctx):
391 """is the changeset divergent (Try to succeed to public change)"""
378 if ctx.node() is None: 392 if ctx.node() is None:
379 return False 393 return False
380 return ctx.rev() in getobscache(ctx._repo, 'conflicting') 394 return ctx.rev() in getobscache(ctx._repo, 'divergent')
381 395
382 ### revset symbol 396 ### revset symbol
383 397
384 @eh.revset('latecomer') 398 @eh.revset('latecomer')
385 def revsetlatecomer(repo, subset, x): 399 def revsetlatecomer(repo, subset, x):
389 args = revset.getargs(x, 0, 0, 'latecomer takes no arguments') 403 args = revset.getargs(x, 0, 0, 'latecomer takes no arguments')
390 lates = getobscache(repo, 'latecomer') 404 lates = getobscache(repo, 'latecomer')
391 return [r for r in subset if r in lates] 405 return [r for r in subset if r in lates]
392 406
393 @eh.revset('conflicting') 407 @eh.revset('conflicting')
394 def revsetconflicting(repo, subset, x):
395 """``conflicting()``
396 Changesets marked as successors of a same changeset.
397 """
398 args = revset.getargs(x, 0, 0, 'conflicting takes no arguments')
399 conf = getobscache(repo, 'conflicting')
400 return [r for r in subset if r in conf]
401
402 @eh.revset('divergent') 408 @eh.revset('divergent')
403 def revsetdivergent(repo, subset, x): 409 def revsetdivergent(repo, subset, x):
404 """``divergent()`` 410 """``divergent()``
405 Changesets marked as successors of a same changeset. 411 Changesets marked as successors of a same changeset.
406 """ 412 """
407 args = revset.getargs(x, 0, 0, 'divergent takes no arguments') 413 args = revset.getargs(x, 0, 0, 'divergent takes no arguments')
408 conf = getobscache(repo, 'conflicting') 414 conf = getobscache(repo, 'divergent')
409 return [r for r in subset if r in conf] 415 return [r for r in subset if r in conf]
416
410 417
411 418
412 ### Discovery wrapping 419 ### Discovery wrapping
413 420
414 @eh.wrapfunction(discovery, 'checkheads') 421 @eh.wrapfunction(discovery, 'checkheads')
423 # obsolete or unstable. 430 # obsolete or unstable.
424 ctx = repo[h] 431 ctx = repo[h]
425 if ctx.latecomer(): 432 if ctx.latecomer():
426 raise util.Abort(_("push includes a latecomer changeset: %s!") 433 raise util.Abort(_("push includes a latecomer changeset: %s!")
427 % ctx) 434 % ctx)
428 if ctx.conflicting(): 435 if ctx.divergent():
429 raise util.Abort(_("push includes a conflicting changeset: %s!") 436 raise util.Abort(_("push includes a divergent changeset: %s!")
430 % ctx) 437 % ctx)
431 return orig(repo, remote, outgoing, *args, **kwargs) 438 return orig(repo, remote, outgoing, *args, **kwargs)
432 439
433 ##################################################################### 440 #####################################################################
434 ### Filter extinct changesets from common operations ### 441 ### Filter extinct changesets from common operations ###
496 503
497 @eh.addattr(context.changectx, 'troubles') 504 @eh.addattr(context.changectx, 'troubles')
498 def troubles(ctx): 505 def troubles(ctx):
499 """Return a tuple listing all the troubles that affect a changeset 506 """Return a tuple listing all the troubles that affect a changeset
500 507
501 Troubles may be "unstable", "latecomer" or "conflicting". 508 Troubles may be "unstable", "latecomer" or "divergent".
502 """ 509 """
503 troubles = [] 510 troubles = []
504 if ctx.unstable(): 511 if ctx.unstable():
505 troubles.append('unstable') 512 troubles.append('unstable')
506 if ctx.latecomer(): 513 if ctx.latecomer():
507 troubles.append('latecomer') 514 troubles.append('latecomer')
508 if ctx.conflicting(): 515 if ctx.divergent():
509 troubles.append('conflicting') 516 troubles.append('divergent')
510 return tuple(troubles) 517 return tuple(troubles)
511 518
512 ### Troubled revset symbol 519 ### Troubled revset symbol
513 520
514 @eh.revset('troubled') 521 @eh.revset('troubled')
515 def revsetlatecomer(repo, subset, x): 522 def revsetlatecomer(repo, subset, x):
516 """``troubled()`` 523 """``troubled()``
517 Changesets with troubles. 524 Changesets with troubles.
518 """ 525 """
519 _ = revset.getargs(x, 0, 0, 'troubled takes no arguments') 526 _ = revset.getargs(x, 0, 0, 'troubled takes no arguments')
520 return repo.revs('%ld and (unstable() + latecomer() + conflicting())', 527 return repo.revs('%ld and (unstable() + latecomer() + divergent())',
521 subset) 528 subset)
522 529
523 530
524 ### Obsolescence graph 531 ### Obsolescence graph
525 532
595 sr = nm.get(s) 602 sr = nm.get(s)
596 if sr is not None: 603 if sr is not None:
597 cs.add(sr) 604 cs.add(sr)
598 return cs 605 return cs
599 606
600 607 nodemod = node
601 608 def successorssets(repo, initialnode, cache=None):
602 def newerversion(repo, obs):
603 """Return the newer version of an obsolete changeset""" 609 """Return the newer version of an obsolete changeset"""
604 toproceed = set([(obs,)]) 610
605 # XXX known optimization available 611 # prec -> markers mapping
606 newer = set() 612 markersfor = repo.obsstore.precursors
607 objectrels = repo.obsstore.precursors 613
614 # Stack of node need to know the last successors set
615 toproceed = [initialnode]
616 # set version of toproceed for fast loop detection
617 stackedset = set(toproceed)
618 if cache is None:
619 cache = {}
608 while toproceed: 620 while toproceed:
609 current = toproceed.pop() 621 # work on the last node of the stack
610 assert len(current) <= 1, 'splitting not handled yet. %r' % current 622 node = toproceed[-1]
611 current = [n for n in current if n != nullid] 623 if node in cache:
612 if current: 624 # We already have a value for it.
613 n, = current 625 # Keep working on something else.
614 if n in objectrels: 626 stackedset.remove(toproceed.pop())
615 markers = objectrels[n] 627 elif node not in markersfor:
616 for mark in markers: 628 # The node is not obsolete.
617 toproceed.add(tuple(mark[1])) 629 # This mean it is its own last successors.
630 if node in repo:
631 # We have a valid last successors.
632 cache[node] = [(node,)]
618 else: 633 else:
619 newer.add(tuple(current)) 634 # final obsolete version is unknown locally.
635 # Do not count that as a valid successors
636 cache[node] = []
620 else: 637 else:
621 newer.add(()) 638 # <lss> stand for Last Successors Sets
622 return sorted(newer) 639 # it contains the list of all last successors for the current node.
640 lss = []
641 for mark in markersfor[node]:
642 # <mlss> stand for Marker Last Successors Sets
643 # it contains the list of last successors set introduced by
644 # this marker.
645 mlss = [[]]
646 # iterate over possible multiple successors
647 for suc in mark[1]:
648 if suc not in cache:
649 # We do not know the last successors of that yet.
650 if suc in stackedset:
651 # Loop detected!
652 #
653 # we won't be able to ever compute a proper last
654 # successors the naive and simple approve is to
655 # consider it killed
656 cache[suc] = []
657 else:
658 # Add the successor to the stack and break the next
659 # iteration will work on this successors and the
660 # algorithm will eventually process the current
661 # node again.
662 toproceed.append(suc)
663 stackedset.add(suc)
664 break
665 # if we did not break, we can extend the possible set of
666 # last successors.
667 #
668 # I say "extends" because if the marker have multiple
669 # successors we have to generate
670 #
671 # if successors have multiple successors set (when ther are
672 # divergent themself), we do a cartesian product of
673 # possible successors set of already processed successors
674 # and newly obtains successors set.
675 newmlss = []
676 for prefix in mlss:
677 for suffix in cache[suc]:
678 newss = list(prefix)
679 for part in suffix:
680 # do not duplicated entry in successors set.
681 # first entry win.
682 if part not in newss:
683 newss.append(part)
684 newmlss.append(newss)
685 mlss = newmlss
686 else:
687 # note: mlss is still empty if the marker was a bare killing
688 # of this changeset
689 #
690 # We extends the list of all possible successors sets with
691 # successors set continuted by this marker
692 lss.extend(mlss)
693 # we use continue here to skip the break right bellow
694 continue
695 # propagate "nested for" break.
696 # if the nested for exited on break, it did not ran the else
697 # clause and didn't "continue
698 break
699 else:
700 # computation was succesful for *all* marker.
701 # Add computed successors set to the cache
702 # (will be poped from to proceeed) on the new iteration
703 #
704 # We remove successors set that are subset of another one
705 # this fil
706 candsucset = sorted(((len(ss), set(ss), ss) for ss in lss),
707 reverse=True)
708 finalsucset = []
709 for cl, cs, css in candsucset:
710 if not css:
711 # remove empty successors set
712 continue
713 for fs, fss in finalsucset:
714 if cs.issubset(fs):
715 break
716 else:
717 finalsucset.append((cs, css))
718 finalsucset = [s[1] for s in finalsucset]
719 finalsucset.reverse()
720 cache[node] = finalsucset
721 return cache[initialnode]
722
723
623 724
624 725
625 ##################################################################### 726 #####################################################################
626 ### Extending revset and template ### 727 ### Extending revset and template ###
627 ##################################################################### 728 #####################################################################
722 @eh.wrapcommand("unbundle") 823 @eh.wrapcommand("unbundle")
723 def warnobserrors(orig, ui, repo, *args, **kwargs): 824 def warnobserrors(orig, ui, repo, *args, **kwargs):
724 """display warning is the command resulted in more instable changeset""" 825 """display warning is the command resulted in more instable changeset"""
725 priorunstables = len(repo.revs('unstable()')) 826 priorunstables = len(repo.revs('unstable()'))
726 priorlatecomers = len(repo.revs('latecomer()')) 827 priorlatecomers = len(repo.revs('latecomer()'))
727 priorconflictings = len(repo.revs('conflicting()')) 828 priordivergents = len(repo.revs('divergent()'))
728 ret = orig(ui, repo, *args, **kwargs) 829 ret = orig(ui, repo, *args, **kwargs)
729 # workaround phase stupidity 830 # workaround phase stupidity
730 phases._filterunknown(ui, repo.changelog, repo._phasecache.phaseroots) 831 phases._filterunknown(ui, repo.changelog, repo._phasecache.phaseroots)
731 newunstables = len(repo.revs('unstable()')) - priorunstables 832 newunstables = len(repo.revs('unstable()')) - priorunstables
732 newlatecomers = len(repo.revs('latecomer()')) - priorlatecomers 833 newlatecomers = len(repo.revs('latecomer()')) - priorlatecomers
733 newconflictings = len(repo.revs('conflicting()')) - priorconflictings 834 newdivergents = len(repo.revs('divergent()')) - priordivergents
734 if newunstables > 0: 835 if newunstables > 0:
735 ui.warn(_('%i new unstable changesets\n') % newunstables) 836 ui.warn(_('%i new unstable changesets\n') % newunstables)
736 if newlatecomers > 0: 837 if newlatecomers > 0:
737 ui.warn(_('%i new latecomer changesets\n') % newlatecomers) 838 ui.warn(_('%i new latecomer changesets\n') % newlatecomers)
738 if newconflictings > 0: 839 if newdivergents > 0:
739 ui.warn(_('%i new conflicting changesets\n') % newconflictings) 840 ui.warn(_('%i new divergent changesets\n') % newdivergents)
740 return ret 841 return ret
741 842
742 @eh.reposetup 843 @eh.reposetup
743 def _repostabilizesetup(ui, repo): 844 def _repostabilizesetup(ui, repo):
744 """Add a hint for "hg evolve" when troubles make push fails 845 """Add a hint for "hg evolve" when troubles make push fails
774 ui.note(s) 875 ui.note(s)
775 876
776 ret = orig(ui, repo, *args, **kwargs) 877 ret = orig(ui, repo, *args, **kwargs)
777 nbunstable = len(getobscache(repo, 'unstable')) 878 nbunstable = len(getobscache(repo, 'unstable'))
778 nblatecomer = len(getobscache(repo, 'latecomer')) 879 nblatecomer = len(getobscache(repo, 'latecomer'))
779 nbconflicting = len(getobscache(repo, 'unstable')) 880 nbdivergent = len(getobscache(repo, 'unstable'))
780 write('unstable: %i changesets\n', nbunstable) 881 write('unstable: %i changesets\n', nbunstable)
781 write('latecomer: %i changesets\n', nblatecomer) 882 write('latecomer: %i changesets\n', nblatecomer)
782 write('conflicting: %i changesets\n', nbconflicting) 883 write('divergent: %i changesets\n', nbdivergent)
783 return ret 884 return ret
784 885
785 886
786 ##################################################################### 887 #####################################################################
787 ### Core Other extension compat ### 888 ### Core Other extension compat ###
996 def evolve(ui, repo, **opts): 1097 def evolve(ui, repo, **opts):
997 """Solve trouble in your repository 1098 """Solve trouble in your repository
998 1099
999 - rebase unstable changeset to make it stable again, 1100 - rebase unstable changeset to make it stable again,
1000 - create proper diff from latecomer changeset, 1101 - create proper diff from latecomer changeset,
1001 - merge conflicting changeset. 1102 - merge divergent changeset.
1002 1103
1003 By default, take the first troubles changeset that looks relevant. 1104 By default, take the first troubles changeset that looks relevant.
1004 1105
1005 (The logic is still a bit fuzzy) 1106 (The logic is still a bit fuzzy)
1006 1107
1007 - For unstable, that mean the first which could be rebased as child of the 1108 - For unstable, that mean the first which could be rebased as child of the
1008 working directory parent revision or one of its descendants and rebase 1109 working directory parent revision or one of its descendants and rebase
1009 it. 1110 it.
1010 1111
1011 - For conflicting this mean "." if applicable. 1112 - For divergent this mean "." if applicable.
1012 1113
1013 With --any, evolve pick any troubled changeset to solve 1114 With --any, evolve pick any troubled changeset to solve
1014 1115
1015 The working directory is updated to the newly created revision. 1116 The working directory is updated to the newly created revision.
1016 """ 1117 """
1039 troubles = tr.troubles() 1140 troubles = tr.troubles()
1040 if 'unstable' in troubles: 1141 if 'unstable' in troubles:
1041 return _solveunstable(ui, repo, tr, opts['dry_run']) 1142 return _solveunstable(ui, repo, tr, opts['dry_run'])
1042 elif 'latecomer' in troubles: 1143 elif 'latecomer' in troubles:
1043 return _solvelatecomer(ui, repo, tr, opts['dry_run']) 1144 return _solvelatecomer(ui, repo, tr, opts['dry_run'])
1044 elif 'conflicting' in troubles: 1145 elif 'divergent' in troubles:
1045 return _solveconflicting(ui, repo, tr, opts['dry_run']) 1146 return _solvedivergent(ui, repo, tr, opts['dry_run'])
1046 else: 1147 else:
1047 assert False # WHAT? unknown troubles 1148 assert False # WHAT? unknown troubles
1048 1149
1049 def _picknexttroubled(ui, repo, pickany=False): 1150 def _picknexttroubled(ui, repo, pickany=False):
1050 """Pick a the next trouble changeset to solve""" 1151 """Pick a the next trouble changeset to solve"""
1051 tr = _stabilizableunstable(repo, repo['.']) 1152 tr = _stabilizableunstable(repo, repo['.'])
1052 if tr is None: 1153 if tr is None:
1053 wdp = repo['.'] 1154 wdp = repo['.']
1054 if 'conflicting' in wdp.troubles(): 1155 if 'divergent' in wdp.troubles():
1055 tr = wdp 1156 tr = wdp
1056 if tr is None and pickany: 1157 if tr is None and pickany:
1057 troubled = list(repo.set('unstable()')) 1158 troubled = list(repo.set('unstable()'))
1058 if not troubled: 1159 if not troubled:
1059 troubled = list(repo.set('latecomer()')) 1160 troubled = list(repo.set('latecomer()'))
1060 if not troubled: 1161 if not troubled:
1061 troubled = list(repo.set('conflicting()')) 1162 troubled = list(repo.set('divergent()'))
1062 if troubled: 1163 if troubled:
1063 tr = troubled[0] 1164 tr = troubled[0]
1064 1165
1065 return tr 1166 return tr
1066 1167
1087 """Stabilize a unstable changeset""" 1188 """Stabilize a unstable changeset"""
1088 obs = orig.parents()[0] 1189 obs = orig.parents()[0]
1089 if not obs.obsolete(): 1190 if not obs.obsolete():
1090 obs = orig.parents()[1] 1191 obs = orig.parents()[1]
1091 assert obs.obsolete() 1192 assert obs.obsolete()
1092 newer = newerversion(repo, obs.node()) 1193 newer = successorssets(repo, obs.node())
1093 # search of a parent which is not killed 1194 # search of a parent which is not killed
1094 while newer == [()]: 1195 while not newer or newer == [()]:
1095 ui.debug("stabilize target %s is plain dead," 1196 ui.debug("stabilize target %s is plain dead,"
1096 " trying to stabilize on its parent") 1197 " trying to stabilize on its parent")
1097 obs = obs.parents()[0] 1198 obs = obs.parents()[0]
1098 newer = newerversion(repo, obs.node()) 1199 newer = successorssets(repo, obs.node())
1099 if len(newer) > 1: 1200 if len(newer) > 1:
1100 ui.write_err(_("conflict rewriting. can't choose destination\n")) 1201 ui.write_err(_("conflict rewriting. can't choose destination\n"))
1101 return 2 1202 return 2
1102 targets = newer[0] 1203 targets = newer[0]
1103 assert targets 1204 assert targets
1229 # reroute the working copy parent to the new changeset 1330 # reroute the working copy parent to the new changeset
1230 repo.dirstate.setparents(newid, node.nullid) 1331 repo.dirstate.setparents(newid, node.nullid)
1231 finally: 1332 finally:
1232 wlock.release() 1333 wlock.release()
1233 1334
1234 def _solveconflicting(ui, repo, conflicting, dryrun=False): 1335 def _solvedivergent(ui, repo, divergent, dryrun=False):
1235 base, others = conflictingdata(conflicting) 1336 base, others = divergentdata(divergent)
1236 if len(others) > 1: 1337 if len(others) > 1:
1237 raise util.Abort("We do not handle split yet") 1338 raise util.Abort("We do not handle split yet")
1238 other = others[0] 1339 other = others[0]
1239 if conflicting.phase() <= phases.public: 1340 if divergent.phase() <= phases.public:
1240 raise util.Abort("We can't resolve this conflict from the public side") 1341 raise util.Abort("We can't resolve this conflict from the public side")
1241 if len(other.parents()) > 1: 1342 if len(other.parents()) > 1:
1242 raise util.Abort("conflicting changeset can't be a merge (yet)") 1343 raise util.Abort("divergent changeset can't be a merge (yet)")
1243 if other.p1() not in conflicting.parents(): 1344 if other.p1() not in divergent.parents():
1244 raise util.Abort("parents are not common (not handled yet)") 1345 raise util.Abort("parents are not common (not handled yet)")
1245 1346
1246 displayer = cmdutil.show_changeset(ui, repo, {'template': shorttemplate}) 1347 displayer = cmdutil.show_changeset(ui, repo, {'template': shorttemplate})
1247 ui.status(_('merge:')) 1348 ui.status(_('merge:'))
1248 if not ui.quiet: 1349 if not ui.quiet:
1249 displayer.show(conflicting) 1350 displayer.show(divergent)
1250 ui.status(_('with: ')) 1351 ui.status(_('with: '))
1251 if not ui.quiet: 1352 if not ui.quiet:
1252 displayer.show(other) 1353 displayer.show(other)
1253 ui.status(_('base: ')) 1354 ui.status(_('base: '))
1254 if not ui.quiet: 1355 if not ui.quiet:
1255 displayer.show(base) 1356 displayer.show(base)
1256 if dryrun: 1357 if dryrun:
1257 ui.write('hg update -c %s &&\n' % conflicting) 1358 ui.write('hg update -c %s &&\n' % divergent)
1258 ui.write('hg merge %s &&\n' % other) 1359 ui.write('hg merge %s &&\n' % other)
1259 ui.write('hg commit -m "auto merge resolving conflict between ' 1360 ui.write('hg commit -m "auto merge resolving conflict between '
1260 '%s and %s"&&\n' % (conflicting, other)) 1361 '%s and %s"&&\n' % (divergent, other))
1261 ui.write('hg up -C %s &&\n' % base) 1362 ui.write('hg up -C %s &&\n' % base)
1262 ui.write('hg revert --all --rev tip &&\n') 1363 ui.write('hg revert --all --rev tip &&\n')
1263 ui.write('hg commit -m "`hg log -r %s --template={desc}`";\n' 1364 ui.write('hg commit -m "`hg log -r %s --template={desc}`";\n'
1264 % conflicting) 1365 % divergent)
1265 return 1366 return
1266 wlock = lock = None 1367 wlock = lock = None
1267 try: 1368 try:
1268 wlock = repo.wlock() 1369 wlock = repo.wlock()
1269 lock = repo.lock() 1370 lock = repo.lock()
1270 if conflicting not in repo[None].parents(): 1371 if divergent not in repo[None].parents():
1271 repo.ui.status(_('updating to "local" conflict\n')) 1372 repo.ui.status(_('updating to "local" conflict\n'))
1272 hg.update(repo, conflicting.rev()) 1373 hg.update(repo, divergent.rev())
1273 repo.ui.note(_('merging conflicting changeset\n')) 1374 repo.ui.note(_('merging divergent changeset\n'))
1274 stats = merge.update(repo, 1375 stats = merge.update(repo,
1275 other.node(), 1376 other.node(),
1276 branchmerge=True, 1377 branchmerge=True,
1277 force=False, 1378 force=False,
1278 partial=None, 1379 partial=None,
1289 /!\ * hg up to the parent of the amended changeset (which are named W and Z) 1390 /!\ * hg up to the parent of the amended changeset (which are named W and Z)
1290 /!\ * hg revert --all -r X 1391 /!\ * hg revert --all -r X
1291 /!\ * hg ci -m "same message as the amended changeset" => new cset Y 1392 /!\ * hg ci -m "same message as the amended changeset" => new cset Y
1292 /!\ * hg kill -n Y W Z 1393 /!\ * hg kill -n Y W Z
1293 """) 1394 """)
1294 tr = repo.transaction('stabilize-conflicting') 1395 tr = repo.transaction('stabilize-divergent')
1295 try: 1396 try:
1296 repo.dirstate.setparents(conflicting.node(), node.nullid) 1397 repo.dirstate.setparents(divergent.node(), node.nullid)
1297 oldlen = len(repo) 1398 oldlen = len(repo)
1298 amend(ui, repo) 1399 amend(ui, repo)
1299 if oldlen == len(repo): 1400 if oldlen == len(repo):
1300 new = conflicting 1401 new = divergent
1301 # no changes 1402 # no changes
1302 else: 1403 else:
1303 new = repo['.'] 1404 new = repo['.']
1304 createmarkers(repo, [(other, (new,))]) 1405 createmarkers(repo, [(other, (new,))])
1305 phases.retractboundary(repo, other.phase(), [new.node()]) 1406 phases.retractboundary(repo, other.phase(), [new.node()])
1308 tr.release() 1409 tr.release()
1309 finally: 1410 finally:
1310 lockmod.release(lock, wlock) 1411 lockmod.release(lock, wlock)
1311 1412
1312 1413
1313 def conflictingdata(ctx): 1414 def divergentdata(ctx):
1314 """return base, other part of a conflict 1415 """return base, other part of a conflict
1315 1416
1316 This only return the first one. 1417 This only return the first one.
1317 1418
1318 XXX this woobly function won't survive XXX 1419 XXX this woobly function won't survive XXX
1319 """ 1420 """
1320 for base in ctx._repo.set('reverse(precursors(%d))', ctx): 1421 for base in ctx._repo.set('reverse(precursors(%d))', ctx):
1321 newer = newerversion(ctx._repo, base.node()) 1422 newer = successorssets(ctx._repo, base.node())
1322 # drop filter and solution including the original ctx 1423 # drop filter and solution including the original ctx
1323 newer = [n for n in newer if n and ctx.node() not in n] 1424 newer = [n for n in newer if n and ctx.node() not in n]
1324 if newer: 1425 if newer:
1325 return base, tuple(ctx._repo[o] for o in newer[0]) 1426 return base, tuple(ctx._repo[o] for o in newer[0])
1326 raise KeyError('Base seem unknown. This case is not handled yet.') 1427 raise KeyError('Base seem unknown. This case is not handled yet.')
1776 if repo.revs('. and %ld', revs): 1877 if repo.revs('. and %ld', revs):
1777 hg.update(repo, newid) 1878 hg.update(repo, newid)
1778 finally: 1879 finally:
1779 lockmod.release(lock, wlock) 1880 lockmod.release(lock, wlock)
1780 1881
1882 if 'debugsuccessorssets' not in commands.table:
1883
1884 @command('debugsuccessorssets',
1885 [],
1886 _('[REV]'))
1887 def debugsuccessorssets(ui, repo, *revs):
1888 """show set of successors for revision
1889
1890 Successors set of changeset A are a consistent group of revision that
1891 succeed to A. Successors set contains non-obsolete changeset only.
1892
1893 In most case a changeset A have zero (changeset pruned) or a single
1894 successors set that contains a single successors (changeset A replacement
1895 by A')
1896
1897 But splitted changeset will result with successors set containing more than
1898 a single element. Divergent rewritting will result in multiple successor
1899 set.
1900
1901 result is displayed as follows::
1902
1903 <rev1>
1904 <successors-1A>
1905 <rev2>
1906 <successors-2A>
1907 <successors-2B1> <successors-2B1> <successors-2B1>
1908
1909 here rev2 have two possible successors sets. One hold three elements.
1910
1911 add --debug if you want full size node id.
1912 """
1913 cache = {}
1914 s = str
1915 if ui.debug:
1916 def s(ctx):
1917 return ctx.hex()
1918 for rev in scmutil.revrange(repo, revs):
1919 ctx = repo[rev]
1920 if ui.debug():
1921 ui.write('%s\n'% ctx.hex())
1922 s = node.hex
1923 else:
1924 ui.write('%s\n'% ctx)
1925 s = node.short
1926 for ss in successorssets(repo, ctx.node(), cache):
1927 if ss:
1928 ui.write(' ')
1929 ui.write(s(ss[0]))
1930 for n in ss[1:]:
1931 ui.write(' ')
1932 ui.write(s(n))
1933 ui.write('\n')
1934 pass
1935
1781 1936
1782 @eh.wrapcommand('graft') 1937 @eh.wrapcommand('graft')
1783 def graftwrapper(orig, ui, repo, *revs, **kwargs): 1938 def graftwrapper(orig, ui, repo, *revs, **kwargs):
1784 kwargs = dict(kwargs) 1939 kwargs = dict(kwargs)
1785 revs = list(revs) + kwargs.get('rev', []) 1940 revs = list(revs) + kwargs.get('rev', [])