Mercurial > evolve
changeset 1426:6db55f28c965
merge with stable
author | Pierre-Yves David <pierre-yves.david@fb.com> |
---|---|
date | Tue, 23 Jun 2015 15:32:47 -0700 |
parents | ecd669c36c12 (diff) e8f875227901 (current diff) |
children | fcc467ca740e |
files | README hgext/evolve.py tests/test-evolve.t |
diffstat | 29 files changed, 2970 insertions(+), 572 deletions(-) [+] |
line wrap: on
line diff
--- a/README Tue Jun 23 15:32:15 2015 -0700 +++ b/README Tue Jun 23 15:32:47 2015 -0700 @@ -51,6 +51,20 @@ Changelog ========= +5.2.0 -- + +- evolve: gain a --rev option to control what revisions to evolve (issue4391) +- evolve: revision are processed in the order they stack on destination +- evolve: properly skip unstable revision with non-evolved unstable parent +- evolve: gain --unstable --divergent --bumped flag to select the trouble +- evolve: issue more useful error message and hint when evolve has nothing to + do as invocated. +- evolve: bare `hg evolve` commands now abort when multiple changesets could be + a target. +- evolve: `hg evolve --all` only evolve changeset that will end up as + descendant of the current working copy. The old behavior of `--all` + in now in `--all --any`. + 5.1.5 -- 2015-06-23 - minor documentation cleanup
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hgext/directaccess.py Tue Jun 23 15:32:47 2015 -0700 @@ -0,0 +1,175 @@ +""" This extension provides direct access +It is the ability to refer and access hidden sha in commands provided that you +know their value. +For example hg log -r xxx where xxx is a commit has should work whether xxx is +hidden or not as we assume that the user knows what he is doing when referring +to xxx. +""" +from mercurial import extensions +from mercurial import cmdutil +from mercurial import repoview +from mercurial import branchmap +from mercurial import revset +from mercurial import error +from mercurial import commands +from mercurial import hg +from mercurial.i18n import _ + +cmdtable = {} +command = cmdutil.command(cmdtable) + +# By default, all the commands have directaccess with warnings +# List of commands that have no directaccess and directaccess with no warning +directaccesslevel = [ + # Format: + # ('nowarning', 'evolve', 'prune'), + # means: no directaccess warning, for the command in evolve named prune + # + # ('error', None, 'serve'), + # means: no directaccess for the command in core named serve + # + # The list is ordered alphabetically by command names, starting with all + # the commands in core then all the commands in the extensions + # + # The general guideline is: + # - remove directaccess warnings for read only commands + # - no direct access for commands with consequences outside of the repo + # - leave directaccess warnings for all the other commands + # + ('nowarning', None, 'annotate'), + ('nowarning', None, 'archive'), + ('nowarning', None, 'bisect'), + ('nowarning', None, 'bookmarks'), + ('nowarning', None, 'bundle'), + ('nowarning', None, 'cat'), + ('nowarning', None, 'diff'), + ('nowarning', None, 'export'), + ('nowarning', None, 'identify'), + ('nowarning', None, 'incoming'), + ('nowarning', None, 'log'), + ('nowarning', None, 'manifest'), + ('error', None, 'outgoing'), # confusing if push errors and not outgoing + ('error', None, 'push'), # destructive + ('nowarning', None, 'revert'), + ('error', None, 'serve'), + ('nowarning', None, 'tags'), + ('nowarning', None, 'unbundle'), + ('nowarning', None, 'update'), +] + +def reposetup(ui, repo): + repo._explicitaccess = set() + +def _computehidden(repo): + hidden = repoview.filterrevs(repo, 'visible') + cl = repo.changelog + dynamic = hidden & repo._explicitaccess + if dynamic: + blocked = cl.ancestors(dynamic, inclusive=True) + hidden = frozenset(r for r in hidden if r not in blocked) + return hidden + +def setupdirectaccess(): + """ Add two new filtername that behave like visible to provide direct access + and direct access with warning. Wraps the commands to setup direct access """ + repoview.filtertable.update({'visible-directaccess-nowarn': _computehidden}) + repoview.filtertable.update({'visible-directaccess-warn': _computehidden}) + branchmap.subsettable['visible-directaccess-nowarn'] = 'visible' + branchmap.subsettable['visible-directaccess-warn'] = 'visible' + + for warn, ext, cmd in directaccesslevel: + try: + cmdtable = extensions.find(ext).cmdtable if ext else commands.table + wrapper = wrapwitherror if warn == 'error' else wrapwithoutwarning + extensions.wrapcommand(cmdtable, cmd, wrapper) + except (error.UnknownCommand, KeyError): + pass + +def wrapwitherror(orig, ui, repo, *args, **kwargs): + if repo and repo.filtername == 'visible-directaccess-warn': + repo = repo.filtered('visible') + return orig(ui, repo, *args, **kwargs) + +def wrapwithoutwarning(orig, ui, repo, *args, **kwargs): + if repo and repo.filtername == 'visible-directaccess-warn': + repo = repo.filtered("visible-directaccess-nowarn") + return orig(ui, repo, *args, **kwargs) + +def uisetup(ui): + """ Change ordering of extensions to ensure that directaccess extsetup comes + after the one of the extensions in the loadsafter list """ + loadsafter = ui.configlist('directaccess','loadsafter') + order = list(extensions._order) + directaccesidx = order.index('directaccess') + + # The min idx for directaccess to load after all the extensions in loadafter + minidxdirectaccess = directaccesidx + + for ext in loadsafter: + try: + minidxdirectaccess = max(minidxdirectaccess, order.index(ext)) + except ValueError: + pass # extension not loaded + + if minidxdirectaccess > directaccesidx: + order.insert(minidxdirectaccess + 1, 'directaccess') + order.remove('directaccess') + extensions._order = order + +def _repository(orig, *args, **kwargs): + """Make visible-directaccess-warn the default filter for new repos""" + repo = orig(*args, **kwargs) + return repo.filtered("visible-directaccess-warn") + +def extsetup(ui): + extensions.wrapfunction(revset, 'posttreebuilthook', _posttreebuilthook) + extensions.wrapfunction(hg, 'repository', _repository) + setupdirectaccess() + +def gethashsymbols(tree): + # Returns the list of symbols of the tree that look like hashes + # for example for the revset 3::abe3ff it will return ('abe3ff') + if not tree: + return [] + + if len(tree) == 2 and tree[0] == "symbol": + try: + int(tree[1]) + return [] + except ValueError as e: + return [tree[1]] + elif len(tree) == 3: + return gethashsymbols(tree[1]) + gethashsymbols(tree[2]) + else: + return [] + +def _posttreebuilthook(orig, tree, repo): + # This is use to enabled direct hash access + # We extract the symbols that look like hashes and add them to the + # explicitaccess set + orig(tree, repo) + filternm = "" + if repo is not None: + filternm = repo.filtername + if filternm is not None and filternm.startswith('visible-directaccess'): + prelength = len(repo._explicitaccess) + accessbefore = set(repo._explicitaccess) + repo.symbols = gethashsymbols(tree) + cl = repo.unfiltered().changelog + for node in repo.symbols: + try: + node = cl._partialmatch(node) + except error.LookupError: + node = None + if node is not None: + rev = cl.rev(node) + if rev not in repo.changelog: + repo._explicitaccess.add(rev) + if prelength != len(repo._explicitaccess): + if repo.filtername != 'visible-directaccess-nowarn': + unhiddencommits = repo._explicitaccess - accessbefore + repo.ui.warn( _("Warning: accessing hidden changesets %s " + "for write operation\n") % + (",".join([str(repo.unfiltered()[l]) + for l in unhiddencommits]))) + repo.invalidatevolatilesets()
--- a/hgext/evolve.py Tue Jun 23 15:32:15 2015 -0700 +++ b/hgext/evolve.py Tue Jun 23 15:32:47 2015 -0700 @@ -23,11 +23,48 @@ testedwith = '3.3.3 3.4.1' buglink = 'http://bz.selenic.com/' + +evolutionhelptext = """ +Obsolescence markers make it possible to mark changesets that have been +deleted or superset in a new version of the changeset. + +Unlike the previous way of handling such changes, by stripping the old +changesets from the repository, obsolescence markers can be propagated +between repositories. This allows for a safe and simple way of exchanging +mutable history and altering it after the fact. Changeset phases are +respected, such that only draft and secret changesets can be altered (see +:hg:`hg phases` for details). + +Obsolescence is tracked using "obsolete markers", a piece of metadata +tracking which changesets have been made obsolete, potential successors for +a given changeset, the moment the changeset was marked as obsolete, and the +user who performed the rewriting operation. The markers are stored +separately from standard changeset data can be exchanged without any of the +precursor changesets, preventing unnecessary exchange of obsolescence data. + +The complete set of obsolescence markers describes a history of changeset +modifications that is orthogonal to the repository history of file +modifications. This changeset history allows for detection and automatic +resolution of edge cases arising from multiple users rewriting the same part +of history concurrently. + +Current feature status +====================== + +This feature is still in development. If you see this help, you have enable an +extension that turned this feature on. + +Obsolescence markers will be exchanged between repositories that explicitly +assert support for the obsolescence feature (this can currently only be done +via an extension).""".strip() + + import sys, os import random from StringIO import StringIO import struct import re +import collections import socket import errno sha1re = re.compile(r'\b[0-9a-f]{6,40}\b') @@ -45,6 +82,8 @@ except (ImportError, AttributeError): gboptslist = gboptsmap = None +# Flags for enabling optional parts of evolve +commandopt = 'allnewcommands' from mercurial import bookmarks from mercurial import cmdutil @@ -54,6 +93,7 @@ from mercurial import error from mercurial import exchange from mercurial import extensions +from mercurial import help from mercurial import httppeer from mercurial import hg from mercurial import lock as lockmod @@ -144,8 +184,11 @@ """ for cont, funcname, func in self._duckpunchers: setattr(cont, funcname, func) - for command, wrapper in self._commandwrappers: - extensions.wrapcommand(commands.table, command, wrapper) + for command, wrapper, opts in self._commandwrappers: + entry = extensions.wrapcommand(commands.table, command, wrapper) + if opts: + for short, long, val, msg in opts: + entry[1].append((short, long, val, msg)) for cont, funcname, wrapper in self._functionwrappers: extensions.wrapfunction(cont, funcname, wrapper) for c in self._uicallables: @@ -166,13 +209,20 @@ revset.symbols[name] = symbol for name, kw in self._templatekws: templatekw.keywords[name] = kw - for ext, command, wrapper in self._extcommandwrappers: + for ext, command, wrapper, opts in self._extcommandwrappers: if ext not in knownexts: - e = extensions.find(ext) - if e is None: - raise util.Abort('extension %s not found' % ext) + try: + e = extensions.find(ext) + except KeyError: + # Extension isn't enabled, so don't bother trying to wrap + # it. + continue knownexts[ext] = e.cmdtable - extensions.wrapcommand(knownexts[ext], commands, wrapper) + entry = extensions.wrapcommand(knownexts[ext], command, wrapper) + if opts: + for short, long, val, msg in opts: + entry[1].append((short, long, val, msg)) + for c in self._extcallables: c(ui) @@ -260,7 +310,7 @@ return keyword return dec - def wrapcommand(self, command, extension=None): + def wrapcommand(self, command, extension=None, opts=[]): """Decorated function is a command wrapper The name of the command must be given as the decorator argument. @@ -279,12 +329,16 @@ ui.note('Barry!') return orig(ui, repo, *args, **kwargs) + The `opts` argument allows specifying additional arguments for the + command. + """ def dec(wrapper): if extension is None: - self._commandwrappers.append((command, wrapper)) + self._commandwrappers.append((command, wrapper, opts)) else: - self._extcommandwrappers.append((extension, command, wrapper)) + self._extcommandwrappers.append((extension, command, wrapper, + opts)) return wrapper return dec @@ -330,6 +384,31 @@ reposetup = eh.final_reposetup ##################################################################### +### Option configuration ### +##################################################################### + +@eh.reposetup # must be the first of its kin. +def _configureoptions(ui, repo): + # If no capabilities are specified, enable everything. + # This is so existing evolve users don't need to change their config. + evolveopts = ui.configlist('experimental', 'evolution') + if not evolveopts: + evolveopts = ['all'] + ui.setconfig('experimental', 'evolution', evolveopts) + +@eh.uisetup +def _configurecmdoptions(ui): + # Unregister evolve commands if the command capability is not specified. + # + # This must be in the same function as the option configuration above to + # guarantee it happens after the above configuration, but before the + # extsetup functions. + evolveopts = ui.configlist('experimental', 'evolution') + if evolveopts and (commandopt not in evolveopts and + 'all' not in evolveopts): + cmdtable.clear() + +##################################################################### ### experimental behavior ### ##################################################################### @@ -590,9 +669,16 @@ @eh.wrapcommand("pull") def wrapmayobsoletewc(origfn, ui, repo, *args, **opts): """Warn that the working directory parent is an obsolete changeset""" - res = origfn(ui, repo, *args, **opts) - if repo['.'].obsolete(): - ui.warn(_('working directory parent is obsolete!\n')) + def warnobsolete(): + if repo['.'].obsolete(): + ui.warn(_('working directory parent is obsolete!\n')) + wlock = None + try: + wlock = repo.wlock() + repo._afterlock(warnobsolete) + res = origfn(ui, repo, *args, **opts) + finally: + lockmod.release(wlock) return res # XXX this could wrap transaction code @@ -962,7 +1048,11 @@ This function is loosely based on the extensions.wrapcommand function. ''' - aliases, entry = cmdutil.findcmd(newalias, cmdtable) + try: + aliases, entry = cmdutil.findcmd(newalias, cmdtable) + except error.UnknownCommand: + # Commands may be disabled + return for alias, e in cmdtable.iteritems(): if e is entry: break @@ -1025,6 +1115,23 @@ @command('debugobsstorestat', [], '') def cmddebugobsstorestat(ui, repo): + def _updateclustermap(nodes, mark, clustersmap): + c = (set(nodes), set([mark])) + toproceed = set(nodes) + while toproceed: + n = toproceed.pop() + other = clustersmap.get(n) + if (other is not None + and other is not c): + other[0].update(c[0]) + other[1].update(c[1]) + for on in c[0]: + if on in toproceed: + continue + clustersmap[on] = other + c = other + clustersmap[n] = c + """print statistic about obsolescence markers in the repo""" store = repo.obsstore unfi = repo.unfiltered() @@ -1054,42 +1161,12 @@ if parents: parentsdata += 1 # cluster handling - nodes = set() + nodes = set(mark[1]) nodes.add(mark[0]) - nodes.update(mark[1]) - c = (set(nodes), set([mark])) - - toproceed = set(nodes) - while toproceed: - n = toproceed.pop() - other = clustersmap.get(n) - if (other is not None - and other is not c): - other[0].update(c[0]) - other[1].update(c[1]) - for on in c[0]: - if on in toproceed: - continue - clustersmap[on] = other - c = other - clustersmap[n] = c + _updateclustermap(nodes, mark, clustersmap) # same with parent data nodes.update(parents) - c = (set(nodes), set([mark])) - toproceed = set(nodes) - while toproceed: - n = toproceed.pop() - other = pclustersmap.get(n) - if (other is not None - and other is not c): - other[0].update(c[0]) - other[1].update(c[1]) - for on in c[0]: - if on in toproceed: - continue - pclustersmap[on] = other - c = other - pclustersmap[n] = c + _updateclustermap(nodes, mark, pclustersmap) # freezing the result for c in clustersmap.values(): @@ -1143,216 +1220,148 @@ mean = sum(len(x[1]) for x in allpclusters) // nbcluster ui.write(' mean length: %9i\n' % mean) -@command('^evolve|stabilize|solve', - [('n', 'dry-run', False, - 'do not perform actions, just print what would be done'), - ('', 'confirm', False, - 'ask for confirmation before performing the action'), - ('A', 'any', False, 'also consider troubled changesets unrelated to current working directory'), - ('a', 'all', False, 'evolve all troubled changesets in the repo ' - '(implies any)'), - ('c', 'continue', False, 'continue an interrupted evolution'), - ] + mergetoolopts, - _('[OPTIONS]...')) -def evolve(ui, repo, **opts): - """solve trouble in your repository - - - rebase unstable changesets to make them stable again, - - create proper diffs from bumped changesets, - - merge divergent changesets, - - update to a successor if the working directory parent is - obsolete - - By default a single changeset is evolved for each invocation and only - troubled changesets that would evolve as a descendant of the current - working directory will be considered. See --all and --any options to change - this behavior. - - - For unstable, this means taking the first which could be rebased as a - child of the working directory parent revision or one of its descendants - and rebasing it. - - - For divergent, this means taking "." if applicable. - - With --any, evolve picks any troubled changeset to repair. - - The working directory is updated to the newly created revision. - """ - - contopt = opts['continue'] - anyopt = opts['any'] - allopt = opts['all'] - dryrunopt = opts['dry_run'] - confirmopt = opts['confirm'] - ui.setconfig('ui', 'forcemerge', opts.get('tool', ''), 'evolve') - - startnode = repo['.'] - - if contopt: - if anyopt: - raise util.Abort('cannot specify both "--any" and "--continue"') - if allopt: - raise util.Abort('cannot specify both "--all" and "--continue"') - graftcmd = commands.table['graft'][0] - return graftcmd(ui, repo, old_obsolete=True, **{'continue': True}) - - tro = _picknexttroubled(ui, repo, anyopt or allopt) - if tro is None: - if repo['.'].obsolete(): - displayer = cmdutil.show_changeset( - ui, repo, {'template': shorttemplate}) - successors = set() - - for successorsset in obsolete.successorssets(repo, repo['.'].node()): - for nodeid in successorsset: - successors.add(repo[nodeid]) - - if not successors: - ui.warn(_('parent is obsolete without successors; ' + - 'likely killed\n')) - return 2 - - elif len(successors) > 1: - ui.warn(_('parent is obsolete with multiple successors:\n')) - - for ctx in sorted(successors, key=lambda ctx: ctx.rev()): - displayer.show(ctx) - - return 2 - +def _solveone(ui, repo, ctx, dryrun, confirm, progresscb, category): + """Resolve the troubles affecting one revision""" + wlock = lock = tr = None + try: + wlock = repo.wlock() + lock = repo.lock() + tr = repo.transaction("evolve") + if 'unstable' == category: + result = _solveunstable(ui, repo, ctx, dryrun, confirm, progresscb) + elif 'bumped' == category: + result = _solvebumped(ui, repo, ctx, dryrun, confirm, progresscb) + elif 'divergent' == category: + result = _solvedivergent(ui, repo, ctx, dryrun, confirm, + progresscb) + else: + assert False, "unknown trouble category: %s" % (category) + tr.close() + return result + finally: + lockmod.release(tr, lock, wlock) + +def _handlenotrouble(ui, repo, allopt, revopt, anyopt, targetcat): + """Used by the evolve function to display an error message when + no troubles can be resolved""" + troublecategories = ['bumped', 'divergent', 'unstable'] + unselectedcategories = [c for c in troublecategories if c != targetcat] + msg = None + hint = None + + troubled = { + "unstable": repo.revs("unstable()"), + "divergent": repo.revs("divergent()"), + "bumped": repo.revs("bumped()"), + "all": repo.revs("troubled()"), + } + + + hintmap = { + 'bumped': _("do you want to use --bumped"), + 'bumped+divergent': _("do you want to use --bumped or --divergent"), + 'bumped+unstable': _("do you want to use --bumped or --unstable"), + 'divergent': _("do you want to use --divergent"), + 'divergent+unstable': _("do you want to use --divergent" + " or --unstable"), + 'unstable': _("do you want to use --unstable"), + 'any+bumped': _("do you want to use --any (or --rev) and --bumped"), + 'any+bumped+divergent': _("do you want to use --any (or --rev) and" + " --bumped or --divergent"), + 'any+bumped+unstable': _("do you want to use --any (or --rev) and" + "--bumped or --unstable"), + 'any+divergent': _("do you want to use --any (or --rev) and" + " --divergent"), + 'any+divergent+unstable': _("do you want to use --any (or --rev)" + " and --divergent or --unstable"), + 'any+unstable': _("do you want to use --any (or --rev)" + "and --unstable"), + } + + if revopt: + revs = scmutil.revrange(repo, revopt) + if not revs: + msg = _("set of specified revisions is empty") + else: + msg = _("no %s changesets in specified revisions") % targetcat + othertroubles = [] + for cat in unselectedcategories: + if revs & troubled[cat]: + othertroubles.append(cat) + if othertroubles: + hint = hintmap['+'.join(othertroubles)] + + elif anyopt: + msg = _("no %s changesets to evolve") % targetcat + othertroubles = [] + for cat in unselectedcategories: + if troubled[cat]: + othertroubles.append(cat) + if othertroubles: + hint = hintmap['+'.join(othertroubles)] + + else: + # evolve without any option = relative to the current wdir + if targetcat == 'unstable': + msg = _("nothing to evolve on current working copy parent") + else: + msg = _("current working copy parent is not %s") % targetcat + + p1 = repo['.'].rev() + othertroubles = [] + for cat in unselectedcategories: + if p1 in troubled[cat]: + othertroubles.append(cat) + if othertroubles: + hint = hintmap['+'.join(othertroubles)] + else: + l = len(troubled[targetcat]) + if l: + hint = (_("%d other %s in the repository, do you want --any or --rev") + % (l, targetcat)) else: - ctx = successors.pop() - - ui.status(_('update:')) - if not ui.quiet: - displayer.show(ctx) - - if dryrunopt: - return 0 + othertroubles = [] + for cat in unselectedcategories: + if troubled[cat]: + othertroubles.append(cat) + if othertroubles: + hint = hintmap['any+'+('+'.join(othertroubles))] else: - res = hg.update(repo, ctx.rev()) - if ctx != startnode: - ui.status(_('working directory is now at %s\n') % ctx) - return res - - troubled = repo.revs('troubled()') - if troubled: - ui.write_err(_('nothing to evolve here\n')) - ui.status(_('(%i troubled changesets, do you want --any ?)\n') - % len(troubled)) - return 2 - else: - ui.write_err(_('no troubled changesets\n')) - return 1 - - def progresscb(): - if allopt: - ui.progress('evolve', seen, unit='changesets', total=count) - seen = 1 - count = allopt and _counttroubled(ui, repo) or 1 - - while tro is not None: - progresscb() - wlock = lock = tr = None - try: - wlock = repo.wlock() - lock = repo.lock() - tr = repo.transaction("evolve") - result = _evolveany(ui, repo, tro, dryrunopt, confirmopt, - progresscb=progresscb) - tr.close() - finally: - lockmod.release(tr, lock, wlock) - progresscb() - seen += 1 - if not allopt: - if repo['.'] != startnode: - ui.status(_('working directory is now at %s\n') % repo['.']) - return result - progresscb() - tro = _picknexttroubled(ui, repo, anyopt or allopt) - - if allopt: + msg = _("no troubled changesets") + + assert msg is not None + ui.write_err(msg+"\n") + if hint: + ui.write_err("("+hint+")\n") + return 2 + else: + return 1 + +def _cleanup(ui, repo, startnode, showprogress): + if showprogress: ui.progress('evolve', None) - if repo['.'] != startnode: ui.status(_('working directory is now at %s\n') % repo['.']) - -def _evolveany(ui, repo, tro, dryrunopt, confirmopt, progresscb): - repo = repo.unfiltered() - tro = repo[tro.rev()] - cmdutil.bailifchanged(repo) - troubles = tro.troubles() - if 'unstable' in troubles: - return _solveunstable(ui, repo, tro, dryrunopt, confirmopt, progresscb) - elif 'bumped' in troubles: - return _solvebumped(ui, repo, tro, dryrunopt, confirmopt, progresscb) - elif 'divergent' in troubles: - return _solvedivergent(ui, repo, tro, dryrunopt, confirmopt, - progresscb) - else: - assert False # WHAT? unknown troubles - -def _counttroubled(ui, repo): - """Count the amount of troubled changesets""" - troubled = set() - troubled.update(getrevs(repo, 'unstable')) - troubled.update(getrevs(repo, 'bumped')) - troubled.update(getrevs(repo, 'divergent')) - return len(troubled) - -def _picknexttroubled(ui, repo, pickany=False, progresscb=None): - """Pick a the next trouble changeset to solve""" - if progresscb: progresscb() - tro = _stabilizableunstable(repo, repo['.']) - if tro is None: - wdp = repo['.'] - if 'divergent' in wdp.troubles(): - tro = wdp - if tro is None and pickany: - troubled = list(repo.set('unstable()')) - if not troubled: - troubled = list(repo.set('bumped()')) - if not troubled: - troubled = list(repo.set('divergent()')) - if troubled: - tro = troubled[0] - - return tro - -def _stabilizableunstable(repo, pctx): - """Return a changectx for an unstable changeset which can be - stabilized on top of pctx or one of its descendants. None if none - can be found. +class MultipleSuccessorsError(RuntimeError): + """Exception raised by _singlesuccessor when multiple sucessors sets exists + + The object contains the list of successorssets in its 'successorssets' + attribute to call to easily recover. """ - def selfanddescendants(repo, pctx): - yield pctx - for prec in repo.set('allprecursors(%d)', pctx): - yield prec - for ctx in pctx.descendants(): - yield ctx - for prec in repo.set('allprecursors(%d)', ctx): - yield prec - - # Look for an unstable which can be stabilized as a child of - # node. The unstable must be a child of one of node predecessors. - directdesc = set([pctx.rev()]) - for ctx in selfanddescendants(repo, pctx): - for child in ctx.children(): - if ctx.rev() in directdesc and not child.obsolete(): - directdesc.add(child.rev()) - elif child.unstable(): - return child - return None - -def _solveunstable(ui, repo, orig, dryrun=False, confirm=False, - progresscb=None): - """Stabilize a unstable changeset""" - obs = orig.parents()[0] - if not obs.obsolete(): - obs = orig.parents()[1] # second parent is obsolete ? - assert obs.obsolete() + + def __init__(self, successorssets): + self.successorssets = successorssets + +def _singlesuccessor(repo, p): + """returns p (as rev) if not obsolete or its unique latest successors + + fail if there are no such successor""" + + if not p.obsolete(): + return p.rev() + obs = repo[p] + ui = repo.ui newer = obsolete.successorssets(repo, obs.node()) # search of a parent which is not killed while not newer or newer == [()]: @@ -1362,11 +1371,309 @@ obs = obs.parents()[0] newer = obsolete.successorssets(repo, obs.node()) if len(newer) > 1: - raise util.Abort(_("conflict rewriting. can't choose destination\n")) + raise MultipleSuccessorsError(newer) + + return repo[newer[0][0]].rev() + +def builddependencies(repo, revs): + """returns dependency graphs giving an order to solve instability of revs + (see _orderrevs for more information on usage)""" + + # For each troubled revision we keep track of what instability if any should + # be resolved in order to resolve it. Example: + # dependencies = {3: [6], 6:[]} + # Means that: 6 has no dependency, 3 depends on 6 to be solved + dependencies = {} + # rdependencies is the inverted dict of dependencies + rdependencies = collections.defaultdict(set) + + for r in revs: + dependencies[r] = set() + for p in repo[r].parents(): + try: + succ = _singlesuccessor(repo, p) + except MultipleSuccessorsError, exc: + dependencies[r] = exc.successorssets + continue + if succ in revs: + dependencies[r].add(succ) + rdependencies[succ].add(r) + return dependencies, rdependencies + +def _selectrevs(repo, allopt, revopt, anyopt, targetcat): + """select troubles in repo matching according to given options""" + revs = set() + if allopt or revopt: + revs = repo.revs(targetcat+'()') + if revopt: + revs = scmutil.revrange(repo, revopt) & revs + elif not anyopt and targetcat == 'unstable': + revs = set(_aspiringdescendant(repo, repo.revs('(.::) - obsolete()::'))) + elif anyopt: + revs = repo.revs('first(%s())' % (targetcat)) + elif targetcat == 'unstable': + revs = set(_aspiringchildren(repo, repo.revs('(.::) - obsolete()::'))) + if 1 < len(revs): + msg = "multiple evolve candidates" + hint = (_("select one of %s with --rev") + % ', '.join([str(repo[r]) for r in sorted(revs)])) + raise error.Abort(msg, hint=hint) + elif targetcat in repo['.'].troubles(): + revs = set([repo['.'].rev()]) + return revs + + +def _orderrevs(repo, revs): + """Compute an ordering to solve instability for the given revs + + - Takes revs a list of instable revisions + + - Returns the same revisions ordered to solve their instability from the + bottom to the top of the stack that the stabilization process will produce + eventually. + + This ensure the minimal number of stabilization as we can stabilize each + revision on its final, stabilized, destination. + """ + # Step 1: Build the dependency graph + dependencies, rdependencies = builddependencies(repo, revs) + # Step 2: Build the ordering + # Remove the revisions with no dependency(A) and add them to the ordering. + # Removing these revisions leads to new revisions with no dependency (the + # one depending on A) that we can remove from the dependency graph and add + # to the ordering. We progress in a similar fashion until the ordering is + # built + solvablerevs = collections.deque([r for r in sorted(dependencies.keys()) + if not dependencies[r]]) + ordering = [] + while solvablerevs: + rev = solvablerevs.popleft() + for dependent in rdependencies[rev]: + dependencies[dependent].remove(rev) + if not dependencies[dependent]: + solvablerevs.append(dependent) + del dependencies[rev] + ordering.append(rev) + + ordering.extend(sorted(dependencies)) + return ordering + +@command('^evolve|stabilize|solve', + [('n', 'dry-run', False, + 'do not perform actions, just print what would be done'), + ('', 'confirm', False, + 'ask for confirmation before performing the action'), + ('A', 'any', False, 'also consider troubled changesets unrelated to current working directory'), + ('r', 'rev', [], 'solves troubles of these revisions'), + ('', 'bumped', False, 'solves only bumped changesets'), + ('', 'divergent', False, 'solves only divergent changesets'), + ('', 'unstable', False, 'solves only unstable changesets (default)'), + ('a', 'all', False, 'evolve all troubled changesets related to the current ' + 'working directory and its descendants'), + ('c', 'continue', False, 'continue an interrupted evolution'), + ] + mergetoolopts, + _('[OPTIONS]...')) +def evolve(ui, repo, **opts): + """solve troubles in your repository + + - rebase unstable changesets to make them stable again, + - create proper diffs from bumped changesets, + - fuse divergent changesets back together, + - update to a successor if the working directory parent is + obsolete + + If no argument are passed and the current working copy parent is obsolete, + :hg:`evolve` will update the working copy to the successors of this working + copy parent. If the working copy parent is not obsolete (and still no + argument passed) each invocation of :hg:`evolve` will evolve a single + unstable changeset, It will only select a changeset to be evolved if it + will result in a new children for the current working copy parent or its + descendants. The working copy will be updated on the result + (this last behavior will most likely to change in the future). + You can evolve all the unstable changesets that will be evolved on the + parent of the working copy and all its descendants recursively by using + :hg:`evolve` --all. + + You can decide to evolve other categories of trouble using the --divergent + and --bumped flags. If no other option are specified, this will try to + solve the specified troubles for the working copy parent. + + You can also evolve changesets affected by troubles of the selected + category using the --rev options. You can pick the next one anywhere in the + repo using --any. + + You can evolve all the changesets affected by troubles of the selected + category using --all --any. + + The working directory is updated to the newly created revision. + """ + + # Options + contopt = opts['continue'] + anyopt = opts['any'] + allopt = opts['all'] + startnode = repo['.'] + dryrunopt = opts['dry_run'] + confirmopt = opts['confirm'] + revopt = opts['rev'] + troublecategories = ['bumped', 'divergent', 'unstable'] + specifiedcategories = [t for t in troublecategories if opts[t]] + targetcat = 'unstable' + if 1 < len(specifiedcategories): + msg = _('cannot specify more than one trouble category to solve (yet)') + raise util.Abort(msg) + elif len(specifiedcategories) == 1: + targetcat = specifiedcategories[0] + elif repo['.'].obsolete(): + displayer = cmdutil.show_changeset(ui, repo, {'template': shorttemplate}) + # no args and parent is obsolete, update to successors + try: + ctx = repo[_singlesuccessor(repo, repo['.'])] + except MultipleSuccessorsError, exc: + repo.ui.write_err('parent is obsolete with multiple successors:\n') + for ln in exc.successorssets: + for n in ln: + displayer.show(repo[n]) + return 2 + + + ui.status(_('update:')) + if not ui.quiet: + displayer.show(ctx) + + if dryrunopt: + return 0 + res = hg.update(repo, ctx.rev()) + if ctx != startnode: + ui.status(_('working directory is now at %s\n') % ctx) + return res + + ui.setconfig('ui', 'forcemerge', opts.get('tool', ''), 'evolve') + troubled = set(repo.revs('troubled()')) + + # Progress handling + seen = 1 + count = allopt and len(troubled) or 1 + showprogress = allopt + + def progresscb(): + if revopt or allopt: + ui.progress('evolve', seen, unit='changesets', total=count) + + # Continuation handling + if contopt: + if anyopt: + raise util.Abort('cannot specify both "--any" and "--continue"') + if allopt: + raise util.Abort('cannot specify both "--all" and "--continue"') + graftcmd = commands.table['graft'][0] + return graftcmd(ui, repo, old_obsolete=True, **{'continue': True}) + cmdutil.bailifchanged(repo) + + + if revopt and allopt: + raise util.Abort('cannot specify both "--rev" and "--all"') + if revopt and anyopt: + raise util.Abort('cannot specify both "--rev" and "--any"') + + revs = _selectrevs(repo, allopt, revopt, anyopt, targetcat) + + if not revs: + return _handlenotrouble(ui, repo, allopt, revopt, anyopt, targetcat) + + # For the progress bar to show + count = len(revs) + # Order the revisions + if targetcat == 'unstable': + revs = _orderrevs(repo, revs) + for rev in revs: + progresscb() + _solveone(ui, repo, repo[rev], dryrunopt, confirmopt, + progresscb, targetcat) + seen += 1 + progresscb() + _cleanup(ui, repo, startnode, showprogress) + +def _possibledestination(repo, rev): + """return all changesets that may be a new parent for REV""" + tonode = repo.changelog.node + parents = repo.changelog.parentrevs + torev = repo.changelog.rev + dest = set() + tovisit = list(parents(rev)) + while tovisit: + r = tovisit.pop() + succsets = obsolete.successorssets(repo, tonode(r)) + if not succsets: + tovisit.extend(parents(r)) + else: + # We should probably pick only one destination from split + # (case where '1 < len(ss)'), This could be the currently tipmost + # but logic is less clear when result of the split are now on + # multiple branches. + for ss in succsets: + for n in ss: + dest.add(torev(n)) + return dest + +def _aspiringchildren(repo, revs): + """Return a list of changectx which can be stabilized on top of pctx or + one of its descendants. Empty list if none can be found.""" + target = set(revs) + result = [] + for r in repo.revs('unstable() - %ld', revs): + dest = _possibledestination(repo, r) + if target & dest: + result.append(r) + return result + +def _aspiringdescendant(repo, revs): + """Return a list of changectx which can be stabilized on top of pctx or + one of its descendants recursively. Empty list if none can be found.""" + target = set(revs) + result = set(target) + paths = collections.defaultdict(set) + for r in repo.revs('unstable() - %ld', revs): + for d in _possibledestination(repo, r): + paths[d].add(r) + + result = set(target) + tovisit = list(revs) + while tovisit: + base = tovisit.pop() + for unstable in paths[base]: + if unstable not in result: + tovisit.append(unstable) + result.add(unstable) + return sorted(result - target) + +def _solveunstable(ui, repo, orig, dryrun=False, confirm=False, + progresscb=None): + """Stabilize a unstable changeset""" + obs = orig.parents()[0] + if not obs.obsolete() and len(orig.parents()) == 2: + obs = orig.parents()[1] # second parent is obsolete ? + + if not obs.obsolete(): + ui.warn("cannot solve instability of %s, skipping\n" % orig) + return False + newer = obsolete.successorssets(repo, obs.node()) + # search of a parent which is not killed + while not newer or newer == [()]: + ui.debug("stabilize target %s is plain dead," + " trying to stabilize on its parent\n" % + obs) + obs = obs.parents()[0] + newer = obsolete.successorssets(repo, obs.node()) + if len(newer) > 1: + msg = _("skipping %s: divergent rewriting. can't choose destination\n" % obs) + ui.write_err(msg) + return 2 targets = newer[0] assert targets if len(targets) > 1: - raise util.Abort(_("does not handle split parents yet\n")) + msg = _("does not handle split parents yet\n") + ui.write_err(msg) return 2 target = targets[0] displayer = cmdutil.show_changeset(ui, repo, {'template': shorttemplate}) @@ -1399,15 +1706,19 @@ def _solvebumped(ui, repo, bumped, dryrun=False, confirm=False, progresscb=None): """Stabilize a bumped changeset""" + repo = repo.unfiltered() + bumped = repo[bumped.rev()] # For now we deny bumped merge if len(bumped.parents()) > 1: - raise util.Abort('late comer stabilization is confused by bumped' - ' %s being a merge' % bumped) + msg = _('skipping %s : we do not handle merge yet\n' % bumped) + ui.write_err(msg) + return 2 prec = repo.set('last(allprecursors(%d) and public())', bumped).next() # For now we deny target merge if len(prec.parents()) > 1: - raise util.Abort('late comer evolution is confused by precursors' - ' %s being a merge' % prec) + msg = _('skipping: %s: public version is a merge, this not handled yet\n' % prec) + ui.write_err(msg) + return 2 displayer = cmdutil.show_changeset(ui, repo, {'template': shorttemplate}) if not ui.quiet or confirm: @@ -1451,6 +1762,9 @@ files = set() copied = copies.pathcopies(prec, bumped) precmanifest = prec.manifest() + # 3.3.2 needs a list. + # future 3.4 don't detect the size change during iteration + # this is fishy for key, val in list(bumped.manifest().iteritems()): precvalue = precmanifest.get(key, None) if precvalue is not None: @@ -1502,40 +1816,51 @@ def _solvedivergent(ui, repo, divergent, dryrun=False, confirm=False, progresscb=None): + repo = repo.unfiltered() + divergent = repo[divergent.rev()] base, others = divergentdata(divergent) if len(others) > 1: othersstr = "[%s]" % (','.join([str(i) for i in others])) - hint = ("changeset %d is divergent with a changeset that got splitted " - "| into multiple ones:\n[%s]\n" - "| This is not handled by automatic evolution yet\n" - "| You have to fallback to manual handling with commands " - "such as:\n" - "| - hg touch -D\n" - "| - hg prune\n" - "| \n" - "| You should contact your local evolution Guru for help.\n" - % (divergent, othersstr)) - raise util.Abort("we do not handle divergence with split yet", - hint=hint) + msg = _("skipping %d:divergent with a changeset that got splitted into multiple ones:\n" + "|[%s]\n" + "| This is not handled by automatic evolution yet\n" + "| You have to fallback to manual handling with commands " + "such as:\n" + "| - hg touch -D\n" + "| - hg prune\n" + "| \n" + "| You should contact your local evolution Guru for help.\n" + % (divergent, othersstr)) + ui.write_err(msg) + return 2 other = others[0] if divergent.phase() <= phases.public: - raise util.Abort("we can't resolve this conflict from the public side", - hint="%s is public, try from %s" % (divergent, other)) + msg = _("skipping %s: we can't resolve divergence from the public side\n") % divergent + ui.write_err(msg) + hint = _("(%s is public, try from %s)\n" % (divergent, other)) + ui.write_err(hint) + return 2 if len(other.parents()) > 1: - raise util.Abort("divergent changeset can't be a merge (yet)", - hint="You have to fallback to solving this by hand...\n" - "| This probably means redoing the merge and using " - "| `hg prune` to kill older version.") + msg = _("skipping %s: divergent changeset can't be a merge (yet)\n" % divergent) + ui.write_err(msg) + hint = _("You have to fallback to solving this by hand...\n" + "| This probably means redoing the merge and using \n" + "| `hg prune` to kill older version.\n") + ui.write_err(hint) + return 2 if other.p1() not in divergent.parents(): - raise util.Abort("parents are not common (not handled yet)", - hint="| %(d)s, %(o)s are not based on the same changeset.\n" - "| With the current state of its implementation, \n" - "| evolve does not work in that case.\n" - "| rebase one of them next to the other and run \n" - "| this command again.\n" - "| - either: hg rebase --dest 'p1(%(d)s)' -r %(o)s\n" - "| - or: hg rebase --dest 'p1(%(o)s)' -r %(d)s" - % {'d': divergent, 'o': other}) + msg = _("skipping %s: have a different parent than %s (not handled yet)\n") % (divergent, other) + hint = _("| %(d)s, %(o)s are not based on the same changeset.\n" + "| With the current state of its implementation, \n" + "| evolve does not work in that case.\n" + "| rebase one of them next to the other and run \n" + "| this command again.\n" + "| - either: hg rebase --dest 'p1(%(d)s)' -r %(o)s\n" + "| - or: hg rebase --dest 'p1(%(o)s)' -r %(d)s\n" + % {'d': divergent, 'o': other}) + ui.write_err(msg) + ui.write_err(hint) + return 2 displayer = cmdutil.show_changeset(ui, repo, {'template': shorttemplate}) if not ui.quiet or confirm: @@ -1740,6 +2065,7 @@ [('n', 'new', [], _("successor changeset (DEPRECATED)")), ('s', 'succ', [], _("successor changeset")), ('r', 'rev', [], _("revisions to prune")), + ('k', 'keep', None, _("does not modify working copy during prune")), ('', 'biject', False, _("do a 1-1 map between rev and successor ranges")), ('B', 'bookmark', '', _("remove revs only reachable from given" " bookmark"))] + metadataopts, @@ -1779,10 +2105,11 @@ if not revs: raise util.Abort(_('nothing to prune')) - wlock = lock = None + wlock = lock = tr = None try: wlock = repo.wlock() lock = repo.lock() + tr = repo.transaction('prune') # defines pruned changesets precs = [] revs.sort() @@ -1796,6 +2123,10 @@ if not precs: raise util.Abort('nothing to prune') + if not obsolete.isenabled(repo, obsolete.allowunstableopt): + if repo.revs("(%ld::) - %ld", revs, revs): + raise util.Abort(_("cannot prune in the middle of a stack")) + # defines successors changesets sucs = scmutil.revrange(repo, succs) sucs.sort() @@ -1813,12 +2144,6 @@ if biject: relations = [(p, (s,)) for p, s in zip(precs, sucs)] - # create markers - obsolete.createmarkers(repo, relations, metadata=metadata) - - # informs that changeset have been pruned - ui.status(_('%i changesets pruned\n') % len(precs)) - wdp = repo['.'] if len(sucs) == 1 and len(precs) == 1 and wdp in precs: @@ -1828,27 +2153,55 @@ # update to an unkilled parent newnode = wdp - while newnode.obsolete(): + while newnode in precs or newnode.obsolete(): newnode = newnode.parents()[0] + if newnode.node() != wdp.node(): - bookactive = bmactive(repo) - # Active bookmark that we don't want to delete (with -B option) - # we deactivate and move it before the update and reactivate it - # after - movebookmark = bookactive and not bookmark - if movebookmark: - bmdeactivate(repo) - repo._bookmarks[bookactive] = newnode.node() - repo._bookmarks.write() - commands.update(ui, repo, newnode.rev()) - ui.status(_('working directory now at %s\n') % newnode) - if movebookmark: - bmactivate(repo, bookactive) + if opts.get('keep', False): + # This is largely the same as the implementation in + # strip.stripcmd(). We might want to refactor this somewhere + # common at some point. + + # only reset the dirstate for files that would actually change + # between the working context and uctx + descendantrevs = repo.revs("%d::." % newnode.rev()) + changedfiles = [] + for rev in descendantrevs: + # blindly reset the files, regardless of what actually changed + changedfiles.extend(repo[rev].files()) + + # reset files that only changed in the dirstate too + dirstate = repo.dirstate + dirchanges = [f for f in dirstate if dirstate[f] != 'n'] + changedfiles.extend(dirchanges) + repo.dirstate.rebuild(newnode.node(), newnode.manifest(), changedfiles) + repo.dirstate.write() + else: + bookactive = bmactive(repo) + # Active bookmark that we don't want to delete (with -B option) + # we deactivate and move it before the update and reactivate it + # after + movebookmark = bookactive and not bookmark + if movebookmark: + bmdeactivate(repo) + repo._bookmarks[bookactive] = newnode.node() + repo._bookmarks.write() + commands.update(ui, repo, newnode.rev()) + ui.status(_('working directory now at %s\n') % newnode) + if movebookmark: + bmactivate(repo, bookactive) # update bookmarks if bookmark: _deletebookmark(ui, marks, bookmark) + + # create markers + obsolete.createmarkers(repo, relations, metadata=metadata) + + # informs that changeset have been pruned + ui.status(_('%i changesets pruned\n') % len(precs)) + for ctx in repo.unfiltered().set('bookmark() and %ld', precs): # used to be: # @@ -1863,8 +2216,10 @@ updatebookmarks = _bookmarksupdater(repo, ctx.node()) updatebookmarks(dest.node()) break + + tr.close() finally: - lockmod.release(lock, wlock) + lockmod.release(tr, lock, wlock) @command('amend|refresh', [('A', 'addremove', None, @@ -2053,11 +2408,16 @@ if ctx.p1() == rev or ctx.p2() == rev: raise util.Abort(_("cannot uncommit to parent changeset")) + onahead = old.rev() in repo.changelog.headrevs() + disallowunstable = not obsolete.isenabled(repo, obsolete.allowunstableopt) + if disallowunstable and not onahead: + raise util.Abort(_("cannot uncommit in the middle of a stack")) + # Recommit the filtered changeset tr = repo.transaction('uncommit') newid = None - if (pats or opts.get('include') or opts.get('exclude') - or opts.get('all')): + includeorexclude = opts.get('include') or opts.get('exclude') + if (pats or includeorexclude or opts.get('all')): match = scmutil.match(old, pats, opts) newid = _commitfiltered(repo, old, match, target=rev) if newid is None: @@ -2107,6 +2467,31 @@ finally: lockmod.release(lock, wlock) +@eh.wrapcommand('strip', extension='strip', opts=[ + ('', 'bundle', None, _("delete the commit entirely and move it to a " + "backup bundle")), + ]) +def stripwrapper(orig, ui, repo, *revs, **kwargs): + if (not ui.configbool('experimental', 'prunestrip') or + kwargs.get('bundle', False)): + return orig(ui, repo, *revs, **kwargs) + + if kwargs.get('force'): + ui.warn(_("warning: --force has no effect during strip with evolve " + "enabled\n")) + if kwargs.get('no_backup', False): + ui.warn(_("warning: --no-backup has no effect during strips with " + "evolve enabled\n")) + + revs = list(revs) + kwargs.pop('rev', []) + revs = set(scmutil.revrange(repo, revs)) + revs = repo.revs("(%ld)::", revs) + kwargs['rev'] = [] + kwargs['new'] = [] + kwargs['succ'] = [] + kwargs['biject'] = False + return cmdprune(ui, repo, *revs, **kwargs) + @command('^touch', [('r', 'rev', [], 'revision to update'), ('D', 'duplicate', False, @@ -2233,6 +2618,11 @@ raise util.Abort(_("cannot fold non-linear revisions " "(multiple heads given)")) head = repo[heads.first()] + disallowunstable = not obsolete.isenabled(repo, obsolete.allowunstableopt) + if disallowunstable: + if repo.revs("(%ld::) - %ld", revs, revs): + raise util.Abort(_("cannot fold chain not ending with a head "\ + "or with branching")) wlock = lock = None try: wlock = repo.wlock() @@ -2299,8 +2689,12 @@ @eh.extsetup def oldevolveextsetup(ui): for cmd in ['kill', 'uncommit', 'touch', 'fold']: - entry = extensions.wrapcommand(cmdtable, cmd, - warnobserrors) + try: + entry = extensions.wrapcommand(cmdtable, cmd, + warnobserrors) + except error.UnknownCommand: + # Commands may be disabled + continue entry = cmdutil.findcmd('commit', commands.table)[1] entry[1].append(('o', 'obsolete', [], @@ -2329,38 +2723,19 @@ topic = 'OBSEXC' ui.progress(topic, *args, **kwargs) -if getattr(exchange, '_pushdiscoveryobsmarkers', None) is not None: - @eh.wrapfunction(exchange, '_pushdiscoveryobsmarkers') - def _pushdiscoveryobsmarkers(orig, pushop): - if (obsolete._enabled - and pushop.repo.obsstore - and 'obsolete' in pushop.remote.listkeys('namespaces')): - repo = pushop.repo - obsexcmsg(repo.ui, "computing relevant nodes\n") - revs = list(repo.revs('::%ln', pushop.futureheads)) - unfi = repo.unfiltered() - cl = unfi.changelog - if not pushop.remote.capable('_evoext_obshash_0'): - # do not trust core yet - # return orig(pushop) - nodes = [cl.node(r) for r in revs] - if nodes: - obsexcmsg(repo.ui, "computing markers relevant to %i nodes\n" - % len(nodes)) - pushop.outobsmarkers = repo.obsstore.relevantmarkers(nodes) - else: - obsexcmsg(repo.ui, "markers already in sync\n") - pushop.outobsmarkers = [] - pushop.outobsmarkers = repo.obsstore.relevantmarkers(nodes) - return - - common = [] - obsexcmsg(repo.ui, "looking for common markers in %i nodes\n" - % len(revs)) - commonrevs = list(unfi.revs('::%ln', pushop.outgoing.commonheads)) - common = findcommonobsmarkers(pushop.ui, unfi, pushop.remote, commonrevs) - - revs = list(unfi.revs('%ld - (::%ln)', revs, common)) +@eh.wrapfunction(exchange, '_pushdiscoveryobsmarkers') +def _pushdiscoveryobsmarkers(orig, pushop): + if (obsolete.isenabled(pushop.repo, obsolete.exchangeopt) + and pushop.repo.obsstore + and 'obsolete' in pushop.remote.listkeys('namespaces')): + repo = pushop.repo + obsexcmsg(repo.ui, "computing relevant nodes\n") + revs = list(repo.revs('::%ln', pushop.futureheads)) + unfi = repo.unfiltered() + cl = unfi.changelog + if not pushop.remote.capable('_evoext_obshash_0'): + # do not trust core yet + # return orig(pushop) nodes = [cl.node(r) for r in revs] if nodes: obsexcmsg(repo.ui, "computing markers relevant to %i nodes\n" @@ -2369,12 +2744,30 @@ else: obsexcmsg(repo.ui, "markers already in sync\n") pushop.outobsmarkers = [] + pushop.outobsmarkers = repo.obsstore.relevantmarkers(nodes) + return + + common = [] + obsexcmsg(repo.ui, "looking for common markers in %i nodes\n" + % len(revs)) + commonrevs = list(unfi.revs('::%ln', pushop.outgoing.commonheads)) + common = findcommonobsmarkers(pushop.ui, unfi, pushop.remote, commonrevs) + + revs = list(unfi.revs('%ld - (::%ln)', revs, common)) + nodes = [cl.node(r) for r in revs] + if nodes: + obsexcmsg(repo.ui, "computing markers relevant to %i nodes\n" + % len(nodes)) + pushop.outobsmarkers = repo.obsstore.relevantmarkers(nodes) + else: + obsexcmsg(repo.ui, "markers already in sync\n") + pushop.outobsmarkers = [] @eh.wrapfunction(wireproto, 'capabilities') def discocapabilities(orig, repo, proto): """wrapper to advertise new capability""" caps = orig(repo, proto) - if obsolete._enabled: + if obsolete.isenabled(repo, obsolete.exchangeopt): caps += ' _evoext_obshash_0' return caps @@ -2534,7 +2927,7 @@ pushop.ui.debug('try to push obsolete markers to remote\n') repo = pushop.repo remote = pushop.remote - if (obsolete._enabled and repo.obsstore and + if (obsolete.isenabled(repo, obsolete.exchangeopt) and repo.obsstore and 'obsolete' in remote.listkeys('namespaces')): markers = pushop.outobsmarkers if not markers: @@ -2609,28 +3002,7 @@ caps.add('_evoext_pushobsmarkers_0') return caps -@eh.addattr(localrepo.localpeer, 'evoext_pushobsmarkers_0') -def local_pushobsmarkers(peer, obsfile): - data = obsfile.read() - tr = lock = None - try: - lock = peer._repo.lock() - tr = peer._repo.transaction('pushkey: obsolete markers') - new = peer._repo.obsstore.mergemarkers(tr, data) - if new is not None: - obsexcmsg(peer._repo.ui, "%i obsolescence markers added\n" % new, True) - tr.close() - finally: - lockmod.release(tr, lock) - peer._repo.hook('evolve_pushobsmarkers') - -def srv_pushobsmarkers(repo, proto): - """wireprotocol command""" - fp = StringIO() - proto.redirect() - proto.getfile(fp) - data = fp.getvalue() - fp.close() +def _pushobsmarkers(repo, data): tr = lock = None try: lock = repo.lock() @@ -2642,6 +3014,20 @@ finally: lockmod.release(tr, lock) repo.hook('evolve_pushobsmarkers') + +@eh.addattr(localrepo.localpeer, 'evoext_pushobsmarkers_0') +def local_pushobsmarkers(peer, obsfile): + data = obsfile.read() + _pushobsmarkers(peer._repo, data) + +def srv_pushobsmarkers(repo, proto): + """wireprotocol command""" + fp = StringIO() + proto.redirect() + proto.getfile(fp) + data = fp.getvalue() + fp.close() + _pushobsmarkers(repo, data) return wireproto.pushres(0) def _buildpullobsmarkersboundaries(pullop): @@ -2674,34 +3060,32 @@ kwargs['evo_obscommon'] = common return ret -if getattr(exchange, '_getbundleobsmarkerpart', None) is not None: - @eh.wrapfunction(exchange, '_getbundleobsmarkerpart') - def _getbundleobsmarkerpart(orig, bundler, repo, source, **kwargs): - if 'evo_obscommon' not in kwargs: - return orig(bundler, repo, source, **kwargs) - - heads = kwargs.get('heads') - if kwargs.get('obsmarkers', False): - if heads is None: - heads = repo.heads() - obscommon = kwargs.get('evo_obscommon', ()) - assert obscommon - obsset = repo.unfiltered().set('::%ln - ::%ln', heads, obscommon) - subset = [c.node() for c in obsset] - markers = repo.obsstore.relevantmarkers(subset) - exchange.buildobsmarkerspart(bundler, markers) - - @eh.uisetup - def installgetbundlepartgen(ui): - origfunc = exchange.getbundle2partsmapping['obsmarkers'] - def newfunc(*args, **kwargs): - return _getbundleobsmarkerpart(origfunc, *args, **kwargs) - exchange.getbundle2partsmapping['obsmarkers'] = newfunc - +@eh.wrapfunction(exchange, '_getbundleobsmarkerpart') +def _getbundleobsmarkerpart(orig, bundler, repo, source, **kwargs): + if 'evo_obscommon' not in kwargs: + return orig(bundler, repo, source, **kwargs) + + heads = kwargs.get('heads') + if kwargs.get('obsmarkers', False): + if heads is None: + heads = repo.heads() + obscommon = kwargs.get('evo_obscommon', ()) + assert obscommon + obsset = repo.unfiltered().set('::%ln - ::%ln', heads, obscommon) + subset = [c.node() for c in obsset] + markers = repo.obsstore.relevantmarkers(subset) + exchange.buildobsmarkerspart(bundler, markers) + +@eh.uisetup +def installgetbundlepartgen(ui): + origfunc = exchange.getbundle2partsmapping['obsmarkers'] + def newfunc(*args, **kwargs): + return _getbundleobsmarkerpart(origfunc, *args, **kwargs) + exchange.getbundle2partsmapping['obsmarkers'] = newfunc @eh.wrapfunction(exchange, '_pullobsolete') def _pullobsolete(orig, pullop): - if not obsolete._enabled: + if not obsolete.isenabled(pullop.repo, obsolete.exchangeopt): return None if 'obsmarkers' not in getattr(pullop, 'todosteps', ['obsmarkers']): return None @@ -2871,21 +3255,20 @@ _bestformat = max(obsolete.formats.keys()) -if getattr(obsolete, '_checkinvalidmarkers', None) is not None: - @eh.wrapfunction(obsolete, '_checkinvalidmarkers') - def _checkinvalidmarkers(orig, markers): - """search for marker with invalid data and raise error if needed - - Exist as a separated function to allow the evolve extension for a more - subtle handling. - """ - if 'debugobsconvert' in sys.argv: - return - for mark in markers: - if node.nullid in mark[1]: - raise util.Abort(_('bad obsolescence marker detected: ' - 'invalid successors nullid'), - hint=_('You should run `hg debugobsconvert`')) +@eh.wrapfunction(obsolete, '_checkinvalidmarkers') +def _checkinvalidmarkers(orig, markers): + """search for marker with invalid data and raise error if needed + + Exist as a separated function to allow the evolve extension for a more + subtle handling. + """ + if 'debugobsconvert' in sys.argv: + return + for mark in markers: + if node.nullid in mark[1]: + raise util.Abort(_('bad obsolescence marker detected: ' + 'invalid successors nullid'), + hint=_('You should run `hg debugobsconvert`')) @command( 'debugobsconvert', @@ -2920,7 +3303,7 @@ def capabilities(orig, repo, proto): """wrapper to advertise new capability""" caps = orig(repo, proto) - if obsolete._enabled: + if obsolete.isenabled(repo, obsolete.exchangeopt): caps += ' _evoext_pushobsmarkers_0' caps += ' _evoext_pullobsmarkers_0' caps += ' _evoext_obshash_0' @@ -2941,3 +3324,16 @@ def newcap(repo, proto): return capabilities(oldcap, repo, proto) wireproto.commands['capabilities'] = (newcap, args) + +def _helploader(): + return help.gettext(evolutionhelptext) + +@eh.uisetup +def _setuphelp(ui): + for entry in help.helptable: + if entry[0] == "evolution": + break + else: + help.helptable.append((["evolution"], _("Safely Rewriting History"), + _helploader)) + help.helptable.sort()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hgext/inhibit.py Tue Jun 23 15:32:47 2015 -0700 @@ -0,0 +1,227 @@ +"""reduce the changesets evolution feature scope for early and noob friendly ui + +the full scale changeset evolution have some massive bleeding edge and it is +very easy for people not very intimate with the concept to end up in intricate +situation. in order to get some of the benefit sooner, this extension is +disabling some of the less polished aspect of evolution. it should gradually +get thinner and thinner as changeset evolution will get more polished. this +extension is only recommended for large scale organisations. individual user +should probably stick on using evolution in its current state, understand its +concept and provide feedback + +This extension provides the ability to "inhibit" obsolescence markers. obsolete +revision can be cheaply brought back to life that way. +However as the inhibitor are not fitting in an append only model, this is +incompatible with sharing mutable history. +""" +from mercurial import localrepo +from mercurial import obsolete +from mercurial import extensions +from mercurial import cmdutil +from mercurial import error +from mercurial import scmutil +from mercurial import commands +from mercurial import lock as lockmod +from mercurial import bookmarks +from mercurial.i18n import _ + +cmdtable = {} +command = cmdutil.command(cmdtable) + +def reposetup(ui, repo): + + class obsinhibitedrepo(repo.__class__): + + @localrepo.storecache('obsinhibit') + def _obsinhibit(self): + # XXX we should make sure it is invalidated by transaction failure + obsinhibit = set() + raw = self.sopener.tryread('obsinhibit') + for i in xrange(0, len(raw), 20): + obsinhibit.add(raw[i:i+20]) + return obsinhibit + + def commit(self, *args, **kwargs): + newnode = super(obsinhibitedrepo, self).commit(*args, **kwargs) + if newnode is not None: + _inhibitmarkers(repo, [newnode]) + return newnode + + repo.__class__ = obsinhibitedrepo + +def _update(orig, ui, repo, *args, **kwargs): + """ + When moving to a commit we want to inhibit any obsolete commit affecting + the changeset we are updating to. In other words we don't want any visible + commit to be obsolete. + """ + wlock = None + try: + # Evolve is running a hook on lock release to display a warning message + # if the workind dir's parent is obsolete. + # We take the lock here to make sure that we inhibit the parent before + # that hook get a chance to run. + wlock = repo.wlock() + res = orig(ui, repo, *args, **kwargs) + newhead = repo['.'].node() + _inhibitmarkers(repo, [newhead]) + return res + finally: + lockmod.release(wlock) + +def _bookmarkchanged(orig, bkmstoreinst, *args, **kwargs): + """ Add inhibition markers to every obsolete bookmarks """ + repo = bkmstoreinst._repo + bkmstorenodes = [repo[v].node() for v in bkmstoreinst.values()] + _inhibitmarkers(repo, bkmstorenodes) + return orig(bkmstoreinst, *args, **kwargs) + +def _bookmark(orig, ui, repo, *bookmarks, **opts): + """ Add a -D option to the bookmark command, map it to prune -B """ + haspruneopt = opts.get('prune', False) + if not haspruneopt: + return orig(ui, repo, *bookmarks, **opts) + + # Call prune -B + evolve = extensions.find('evolve') + optsdict = { + 'new': [], + 'succ': [], + 'rev': [], + 'bookmark': bookmarks[0], + 'keep': None, + 'biject': False, + } + evolve.cmdprune(ui, repo, **optsdict) + +# obsolescence inhibitor +######################## + +def _schedulewrite(tr, obsinhibit): + """Make sure on disk content will be updated on transaction commit""" + def writer(fp): + """Serialize the inhibited list to disk. + """ + raw = ''.join(obsinhibit) + fp.write(raw) + tr.addfilegenerator('obsinhibit', ('obsinhibit',), writer) + tr.hookargs['obs_inbihited'] = '1' + +def _filterpublic(repo, nodes): + """filter out inhibitor on public changeset + + Public changesets are already immune to obsolescence""" + getrev = repo.changelog.nodemap.get + getphase = repo._phasecache.phase + return (n for n in repo._obsinhibit + if getrev(n) is not None and getphase(repo, getrev(n))) + +def _inhibitmarkers(repo, nodes): + """add marker inhibitor for all obsolete revision under <nodes> + + Content of <nodes> and all mutable ancestors are considered. Marker for + obsolete revision only are created. + """ + newinhibit = repo.set('::%ln and obsolete()', nodes) + if newinhibit: + lock = tr = None + try: + lock = repo.lock() + tr = repo.transaction('obsinhibit') + repo._obsinhibit.update(c.node() for c in newinhibit) + _schedulewrite(tr, _filterpublic(repo, repo._obsinhibit)) + repo.invalidatevolatilesets() + tr.close() + finally: + lockmod.release(tr, lock) + +def _deinhibitmarkers(repo, nodes): + """lift obsolescence inhibition on a set of nodes + + This will be triggered when inhibited nodes received new obsolescence + markers. Otherwise the new obsolescence markers would also be inhibited. + """ + deinhibited = repo._obsinhibit & set(nodes) + if deinhibited: + tr = repo.transaction('obsinhibit') + try: + repo._obsinhibit -= deinhibited + _schedulewrite(tr, _filterpublic(repo, repo._obsinhibit)) + repo.invalidatevolatilesets() + tr.close() + finally: + tr.release() + +def _createmarkers(orig, repo, relations, flag=0, date=None, metadata=None): + """wrap markers create to make sure we de-inhibit target nodes""" + # wrapping transactio to unify the one in each function + tr = repo.transaction('add-obsolescence-marker') + try: + orig(repo, relations, flag, date, metadata) + precs = (r[0].node() for r in relations) + _deinhibitmarkers(repo, precs) + tr.close() + finally: + tr.release() + +def transactioncallback(orig, repo, *args, **kwargs): + """ Wrap localrepo.transaction to inhibit new obsolete changes """ + def inhibitposttransaction(transaction): + # At the end of the transaction we catch all the new visible and + # obsolete commit to inhibit them + visibleobsolete = repo.revs('obsolete() - hidden()') + ignoreset = set(getattr(repo, '_rebaseset', [])) + visibleobsolete = list(r for r in visibleobsolete if r not in ignoreset) + if visibleobsolete: + _inhibitmarkers(repo, [repo[r].node() for r in visibleobsolete]) + transaction = orig(repo, *args, **kwargs) + transaction.addpostclose('inhibitposttransaction', inhibitposttransaction) + return transaction + +def extsetup(ui): + # lets wrap the computation of the obsolete set + # We apply inhibition there + obsfunc = obsolete.cachefuncs['obsolete'] + def _computeobsoleteset(repo): + """remove any inhibited nodes from the obsolete set + + This will trickle down to other part of mercurial (hidden, log, etc)""" + obs = obsfunc(repo) + getrev = repo.changelog.nodemap.get + for n in repo._obsinhibit: + obs.discard(getrev(n)) + return obs + try: + extensions.find('directaccess') + except KeyError: + errormsg = _('Cannot use inhibit without the direct access extension') + raise error.Abort(errormsg) + + # Wrapping this to inhibit obsolete revs resulting from a transaction + extensions.wrapfunction(localrepo.localrepository, + 'transaction', transactioncallback) + + obsolete.cachefuncs['obsolete'] = _computeobsoleteset + # wrap create marker to make it able to lift the inhibition + extensions.wrapfunction(obsolete, 'createmarkers', _createmarkers) + # drop divergence computation since it is incompatible with "light revive" + obsolete.cachefuncs['divergent'] = lambda repo: set() + # drop bumped computation since it is incompatible with "light revive" + obsolete.cachefuncs['bumped'] = lambda repo: set() + # wrap update to make sure that no obsolete commit is visible after an + # update + extensions.wrapcommand(commands.table, 'update', _update) + # There are two ways to save bookmark changes during a transation, we + # wrap both to add inhibition markers. + extensions.wrapfunction(bookmarks.bmstore, 'recordchange', _bookmarkchanged) + extensions.wrapfunction(bookmarks.bmstore, 'write', _bookmarkchanged) + # Add bookmark -D option + entry = extensions.wrapcommand(commands.table, 'bookmark', _bookmark) + entry[1].append(('D','prune',None, + _('delete the bookmark and prune the commits underneath'))) + +@command('debugobsinhibit', [], '') +def cmddebugobsinhibit(ui, repo, *revs): + """inhibit obsolescence markers effect on a set of revs""" + nodes = (repo[r].node() for r in scmutil.revrange(repo, revs)) + _inhibitmarkers(repo, nodes)
--- a/hgext/obsolete.py Tue Jun 23 15:32:15 2015 -0700 +++ b/hgext/obsolete.py Tue Jun 23 15:32:47 2015 -0700 @@ -14,8 +14,6 @@ try: from mercurial import obsolete - if not obsolete._enabled: - obsolete._enabled = True except ImportError: raise util.Abort('Obsolete extension requires Mercurial 2.3 (or later)') @@ -40,6 +38,10 @@ """ if not repo.local(): return + evolveopts = ui.configlist('experimental', 'evolution') + if not evolveopts: + evolveopts = 'all' + ui.setconfig('experimental', 'evolution', evolveopts) for arg in sys.argv: if 'debugc' in arg: break
--- a/hgext/pushexperiment.py Tue Jun 23 15:32:15 2015 -0700 +++ b/hgext/pushexperiment.py Tue Jun 23 15:32:47 2015 -0700 @@ -49,7 +49,8 @@ def syncpush(orig, repo, remote): """wraper for obsolete.syncpush to use the fast way if possible""" - if not (obsolete._enabled and repo.obsstore): + if not (obsolete.isenabled(repo, obsolete.exchangeopt) and + repo.obsstore): return if remote.capable('_push_experiment_pushobsmarkers_0'): return # already pushed before changeset @@ -75,7 +76,7 @@ """push wrapped that call the wire protocol command""" if not remote.canpush(): raise util.Abort(_("destination does not support push")) - if (obsolete._enabled and repo.obsstore + if (obsolete.isenabled(repo, obsolete.exchangeopt) and repo.obsstore and remote.capable('_push_experiment_pushobsmarkers_0')): # push marker early to limit damage of pushing too early. try: @@ -94,7 +95,7 @@ def capabilities(orig, repo, proto): """wrapper to advertise new capability""" caps = orig(repo, proto) - if obsolete._enabled: + if obsolete.isenabled(repo, obsolete.exchangeopt): caps += ' _push_experiment_pushobsmarkers_0' caps += ' _push_experiment_notifypushend_0' return caps
--- a/hgext/simple4server.py Tue Jun 23 15:32:15 2015 -0700 +++ b/hgext/simple4server.py Tue Jun 23 15:32:47 2015 -0700 @@ -12,7 +12,6 @@ buglink = 'http://bz.selenic.com/' import mercurial.obsolete -mercurial.obsolete._enabled = True import struct from mercurial import util @@ -31,8 +30,6 @@ gboptslist = gboptsmap = None try: from mercurial import obsolete - if not obsolete._enabled: - obsolete._enabled = True from mercurial import wireproto gboptslist = getattr(wireproto, 'gboptslist', None) gboptsmap = getattr(wireproto, 'gboptsmap', None) @@ -247,7 +244,7 @@ """wrapper to advertise new capability""" caps = orig(repo, proto) advertise = repo.ui.configbool('__temporary__', 'advertiseobsolete', True) - if obsolete._enabled and advertise: + if obsolete.isenabled(repo, obsolete.exchangeopt) and advertise: caps += ' _evoext_pushobsmarkers_0' caps += ' _evoext_pullobsmarkers_0' caps += ' _evoext_obshash_0' @@ -302,3 +299,8 @@ extensions.wrapfunction(pushkey, '_nslist', _nslist) pushkey._namespaces['namespaces'] = (lambda *x: False, pushkey._nslist) +def reposetup(ui, repo): + evolveopts = ui.configlist('experimental', 'evolution') + if not evolveopts: + evolveopts = 'all' + ui.setconfig('experimental', 'evolution', evolveopts)
--- a/setup.py Tue Jun 23 15:32:15 2015 -0700 +++ b/setup.py Tue Jun 23 15:32:47 2015 -0700 @@ -1,6 +1,7 @@ # Copied from histedit setup.py # Credit to Augie Fackler <durin42@gmail.com> +import os from distutils.core import setup from os.path import dirname, join @@ -14,6 +15,14 @@ if "'" in line: return line.split("'")[1] +py_modules = [ + 'hgext.evolve', +] + +if os.environ.get('INCLUDE_INHIBIT'): + py_modules.append('hgext.inhibit') + py_modules.append('hgext.directaccess') + setup( name='hg-evolve', version=get_version('hgext/evolve.py'), @@ -25,5 +34,5 @@ long_description=open('README').read(), keywords='hg mercurial', license='GPLv2+', - py_modules=['hgext.evolve'], + py_modules=py_modules )
--- a/tests/killdaemons.py Tue Jun 23 15:32:15 2015 -0700 +++ b/tests/killdaemons.py Tue Jun 23 15:32:47 2015 -0700 @@ -1,25 +1,91 @@ #!/usr/bin/env python -import os, time, errno, signal +import os, sys, time, errno, signal + +if os.name =='nt': + import ctypes + + def _check(ret, expectederr=None): + if ret == 0: + winerrno = ctypes.GetLastError() + if winerrno == expectederr: + return True + raise ctypes.WinError(winerrno) -# Kill off any leftover daemon processes -try: - fp = open(os.environ['DAEMON_PIDS']) - for line in fp: + def kill(pid, logfn, tryhard=True): + logfn('# Killing daemon process %d' % pid) + PROCESS_TERMINATE = 1 + PROCESS_QUERY_INFORMATION = 0x400 + SYNCHRONIZE = 0x00100000 + WAIT_OBJECT_0 = 0 + WAIT_TIMEOUT = 258 + handle = ctypes.windll.kernel32.OpenProcess( + PROCESS_TERMINATE|SYNCHRONIZE|PROCESS_QUERY_INFORMATION, + False, pid) + if handle == 0: + _check(0, 87) # err 87 when process not found + return # process not found, already finished try: - pid = int(line) - except ValueError: - continue + r = ctypes.windll.kernel32.WaitForSingleObject(handle, 100) + if r == WAIT_OBJECT_0: + pass # terminated, but process handle still available + elif r == WAIT_TIMEOUT: + _check(ctypes.windll.kernel32.TerminateProcess(handle, -1)) + else: + _check(r) + + # TODO?: forcefully kill when timeout + # and ?shorter waiting time? when tryhard==True + r = ctypes.windll.kernel32.WaitForSingleObject(handle, 100) + # timeout = 100 ms + if r == WAIT_OBJECT_0: + pass # process is terminated + elif r == WAIT_TIMEOUT: + logfn('# Daemon process %d is stuck') + else: + _check(r) # any error + except: #re-raises + ctypes.windll.kernel32.CloseHandle(handle) # no _check, keep error + raise + _check(ctypes.windll.kernel32.CloseHandle(handle)) + +else: + def kill(pid, logfn, tryhard=True): try: os.kill(pid, 0) + logfn('# Killing daemon process %d' % pid) os.kill(pid, signal.SIGTERM) - for i in range(10): - time.sleep(0.05) + if tryhard: + for i in range(10): + time.sleep(0.05) + os.kill(pid, 0) + else: + time.sleep(0.1) os.kill(pid, 0) + logfn('# Daemon process %d is stuck - really killing it' % pid) os.kill(pid, signal.SIGKILL) except OSError, err: if err.errno != errno.ESRCH: raise - fp.close() -except IOError: - pass + +def killdaemons(pidfile, tryhard=True, remove=False, logfn=None): + if not logfn: + logfn = lambda s: s + # Kill off any leftover daemon processes + try: + fp = open(pidfile) + for line in fp: + try: + pid = int(line) + except ValueError: + continue + kill(pid, logfn, tryhard) + fp.close() + if remove: + os.unlink(pidfile) + except IOError: + pass + +if __name__ == '__main__': + path, = sys.argv[1:] + killdaemons(path)
--- a/tests/test-amend.t Tue Jun 23 15:32:15 2015 -0700 +++ b/tests/test-amend.t Tue Jun 23 15:32:47 2015 -0700 @@ -115,6 +115,7 @@ branch: foo commit: 1 unknown (clean) update: (current) + phases: 3 draft Check the help $ hg amend -h
--- a/tests/test-corrupt.t Tue Jun 23 15:32:15 2015 -0700 +++ b/tests/test-corrupt.t Tue Jun 23 15:32:47 2015 -0700 @@ -110,8 +110,7 @@ adding manifests adding file changes added 1 changesets with 2 changes to 2 files - pushing 2 obsolescence markers (* bytes) (glob) - 2 obsolescence markers added + 2 new obsolescence markers $ hg -R ../other verify checking changesets checking manifests
--- a/tests/test-evolve-bumped.t Tue Jun 23 15:32:15 2015 -0700 +++ b/tests/test-evolve-bumped.t Tue Jun 23 15:32:47 2015 -0700 @@ -1,6 +1,11 @@ $ hg init public $ cd public $ echo a > a + $ mkcommit() { + > echo "$1" > "$1" + > hg add "$1" + > hg ci -m "add $1" + > } $ hg commit -A -m init adding a $ cd .. @@ -11,6 +16,8 @@ $ cat >> .hg/hgrc <<EOF > [extensions] > evolve = $evolvepath + > [ui] + > logtemplate = {rev}:{node|short}@{branch}({phase}) {desc|firstline}\n > [phases] > publish = false > EOF @@ -32,12 +39,7 @@ adding file changes added 1 changesets with 1 changes to 1 files $ hg log -r 'draft()' - changeset: 1:4d1169d82e47 - tag: tip - user: alice - date: Thu Jan 01 00:00:00 1970 +0000 - summary: modify a - + 1:4d1169d82e47@default(draft) modify a $ cd ../bob $ hg pull ../private @@ -47,15 +49,9 @@ adding manifests adding file changes added 1 changesets with 1 changes to 1 files - pull obsolescence markers (run 'hg update' to get a working copy) $ hg log -r 'draft()' - changeset: 1:4d1169d82e47 - tag: tip - user: alice - date: Thu Jan 01 00:00:00 1970 +0000 - summary: modify a - + 1:4d1169d82e47@default(draft) modify a $ hg push ../public pushing to ../public searching for changes @@ -71,12 +67,59 @@ pulling from ../public searching for changes no changes found - pull obsolescence markers 1 new bumped changesets - $ hg evolve -a + $ hg evolve -a -A --bumped recreate:[2] tweak a atop:[1] modify a computing new diff committed as 4d1169d82e47 working directory is now at 4d1169d82e47 + +Bumped Merge changeset: +----------------------- + +We currently cannot automatically solve bumped changeset that is the +product of a merge, we add a test for it. + + $ mkcommit _a + $ hg up .^ + 0 files updated, 0 files merged, 1 files removed, 0 files unresolved + $ mkcommit _b + created new head + $ mkcommit _c + $ hg log -G + @ 5:eeaf70969381@default(draft) add _c + | + o 4:6612fc0ddeb6@default(draft) add _b + | + | o 3:154ad198ff4a@default(draft) add _a + |/ + o 1:4d1169d82e47@default(public) modify a + | + o 0:d3873e73d99e@default(public) init + + $ hg merge 3 + 1 files updated, 0 files merged, 0 files removed, 0 files unresolved + (branch merge, don't forget to commit) + $ hg commit -m "merge" + $ hg commit --amend -m "New message" + $ hg phase --public 551127da2a8a --hidden + 1 new bumped changesets + $ hg log -G + @ 7:b28e84916d8c@default(draft) New message + |\ + +---o 6:551127da2a8a@default(public) merge + | |/ + | o 5:eeaf70969381@default(public) add _c + | | + | o 4:6612fc0ddeb6@default(public) add _b + | | + o | 3:154ad198ff4a@default(public) add _a + |/ + o 1:4d1169d82e47@default(public) modify a + | + o 0:d3873e73d99e@default(public) init + + $ hg evolve --all --bumped + skipping b28e84916d8c : we do not handle merge yet
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-evolve-order.t Tue Jun 23 15:32:47 2015 -0700 @@ -0,0 +1,263 @@ +evolve --rev reordering +----------------------- + + $ cat >> $HGRCPATH <<EOF + > [defaults] + > amend=-d "0 0" + > fold=-d "0 0" + > [web] + > push_ssl = false + > allow_push = * + > [phases] + > publish = False + > [diff] + > git = 1 + > unified = 0 + > [ui] + > logtemplate = {rev}:{node|short}@{branch}({phase}) {desc|firstline}\n + > [extensions] + > hgext.graphlog= + > EOF + $ echo "evolve=$(echo $(dirname $TESTDIR))/hgext/evolve.py" >> $HGRCPATH + $ mkcommit() { + > echo "$1" > "$1" + > hg add "$1" + > hg ci -m "add $1" + > } + + $ mkstack() { + > # Creates a stack of commit based on $1 with messages from $2, $3 .. + > hg update "$1" -C + > shift + > mkcommits $* + > } + + $ mkcommits() { + > for i in $@; do mkcommit $i ; done + > } + +Initial setup + $ hg init testrevorder + $ cd testrevorder + $ mkcommits p _a _b _c + $ hg phase --public 0 + $ hg up 'desc(_a)' + 0 files updated, 0 files merged, 2 files removed, 0 files unresolved + $ echo "aaa" > _a + $ hg amend + 2 new unstable changesets + $ hg log -G + @ 5:12d519679175@default(draft) add _a + | + | o 3:4d156641b718@default(draft) add _c + | | + | o 2:4d7242ebb004@default(draft) add _b + | | + | x 1:2d73fcd7f07d@default(draft) add _a + |/ + o 0:f92638be10c7@default(public) add p + + +evolve --rev reorders the rev to solve instability, trivial case 2 revs wrong order + $ hg evolve --rev 'desc(_c) + desc(_b)' + move:[2] add _b + atop:[5] add _a + move:[3] add _c + atop:[6] add _b + working directory is now at 52b8f9b04f83 + +evolve --rev reorders the rev to solve instability. Harder case, obsolescence +accross three stacks in growing rev numbers. + $ hg up "desc(_c)" + 0 files updated, 0 files merged, 0 files removed, 0 files unresolved + $ mkcommit _d + $ hg up "desc(_a)" + 0 files updated, 0 files merged, 3 files removed, 0 files unresolved + $ hg amend -m "aprime" + 3 new unstable changesets + $ hg evolve --rev "desc(_b)" + move:[6] add _b + atop:[9] aprime + working directory is now at 476c9c052aae + $ hg up "desc(_b) - obsolete()" + 0 files updated, 0 files merged, 0 files removed, 0 files unresolved + $ hg amend -m "bprime" + $ hg up "desc(aprime)" + 0 files updated, 0 files merged, 1 files removed, 0 files unresolved + $ hg amend -m "asecond" + 1 new unstable changesets + $ hg log -G + @ 12:9a584314f3f3@default(draft) asecond + | + | o 11:a59c79776f7c@default(draft) bprime + | | + | x 9:81a687b96d4d@default(draft) aprime + |/ + | o 8:464731bc0ed0@default(draft) add _d + | | + | o 7:52b8f9b04f83@default(draft) add _c + | | + | x 6:59476c3836ef@default(draft) add _b + | | + | x 5:12d519679175@default(draft) add _a + |/ + o 0:f92638be10c7@default(public) add p + + $ hg evolve --rev "unstable()" + move:[11] bprime + atop:[12] asecond + move:[7] add _c + atop:[13] bprime + move:[8] add _d + atop:[14] add _c + working directory is now at 225d2cc5d3fc + $ hg log -G + @ 15:225d2cc5d3fc@default(draft) add _d + | + o 14:0fc229278e4d@default(draft) add _c + | + o 13:c3741b9eafae@default(draft) bprime + | + o 12:9a584314f3f3@default(draft) asecond + | + o 0:f92638be10c7@default(public) add p + + +Evolve --rev more complex case: two sets of stacks one with prune an no successor, the other one +partially solvable + +First set of stack: + $ mkstack "desc(_d)" c1_ c2_ c3_ c4_ >/dev/null + $ mkstack "desc(_d)" c1prime c2prime >/dev/null + $ mkstack "desc(_d)" c1second >/dev/null + $ hg prune "desc(c1_)" -s "desc(c1prime)" + 1 changesets pruned + 3 new unstable changesets + $ hg prune "desc(c2_)" -s "desc(c2prime)" + 1 changesets pruned + $ hg prune "desc(c1prime)" -s "desc(c1second)" + 1 changesets pruned + 1 new unstable changesets + $ hg log -G -r "desc(_d)::" + @ 22:a329855d0bc1@default(draft) add c1second + | + | o 21:072276ece1bf@default(draft) add c2prime + | | + | x 20:f137acd06692@default(draft) add c1prime + |/ + | o 19:0a1d9b2ce733@default(draft) add c4_ + | | + | o 18:e2874f41c56c@default(draft) add c3_ + | | + | x 17:3247c33339fa@default(draft) add c2_ + | | + | x 16:df322257c182@default(draft) add c1_ + |/ + o 15:225d2cc5d3fc@default(draft) add _d + | + +Second set of stack with no successor for b2_: + $ mkstack "desc(_d)" b1_ b2_ b3_ b4_ >/dev/null + $ mkstack "desc(_d)" b1prime b3prime >/dev/null + $ hg prune "desc(b1_)" -s "desc(b1prime)" + 1 changesets pruned + 3 new unstable changesets + $ hg prune "desc(b3_)" -s "desc(b3prime)" + 1 changesets pruned + $ hg prune "desc(b2_)" + 1 changesets pruned + + $ hg log -G -r "desc(_d)::" + @ 28:ba4c348b6d5e@default(draft) add b3prime + | + o 27:8fe985f5d0aa@default(draft) add b1prime + | + | o 26:1d9ba2e75c93@default(draft) add b4_ + | | + | x 25:aec6a9657b6c@default(draft) add b3_ + | | + | x 24:a69b58575918@default(draft) add b2_ + | | + | x 23:3564eb18e448@default(draft) add b1_ + |/ + | o 22:a329855d0bc1@default(draft) add c1second + |/ + | o 21:072276ece1bf@default(draft) add c2prime + | | + | x 20:f137acd06692@default(draft) add c1prime + |/ + | o 19:0a1d9b2ce733@default(draft) add c4_ + | | + | o 18:e2874f41c56c@default(draft) add c3_ + | | + | x 17:3247c33339fa@default(draft) add c2_ + | | + | x 16:df322257c182@default(draft) add c1_ + |/ + o 15:225d2cc5d3fc@default(draft) add _d + | + +Solve the full second stack and only part of the first one + $ echo "(desc(_d)::) - desc(c3_)" + (desc(_d)::) - desc(c3_) + $ hg evolve --rev "(desc(_d)::) - desc(c3_)" + cannot solve instability of 0a1d9b2ce733, skipping + move:[21] add c2prime + atop:[22] add c1second + move:[26] add b4_ + atop:[28] add b3prime + working directory is now at 4897c8ed7645 + +Cleanup + $ hg evolve --rev "(desc(_d)::)" + move:[18] add c3_ + atop:[29] add c2prime + move:[19] add c4_ + atop:[31] add c3_ + working directory is now at 4ee8feb52325 + $ hg log -G -r "desc(_d)::" + @ 32:4ee8feb52325@default(draft) add c4_ + | + o 31:08a530ce67e1@default(draft) add c3_ + | + | o 30:4897c8ed7645@default(draft) add b4_ + | | + o | 29:3abc7618dd5f@default(draft) add c2prime + | | + | o 28:ba4c348b6d5e@default(draft) add b3prime + | | + | o 27:8fe985f5d0aa@default(draft) add b1prime + | | + o | 22:a329855d0bc1@default(draft) add c1second + |/ + o 15:225d2cc5d3fc@default(draft) add _d + | + +Test multiple revision with some un-evolvable because parent is splitted +------------------------------------------------------------------------ + + $ hg up 'desc(c2prime)' + 0 files updated, 0 files merged, 2 files removed, 0 files unresolved + $ mkcommit c3part1 + created new head + $ hg prev + 0 files updated, 0 files merged, 1 files removed, 0 files unresolved + [29] add c2prime + $ mkcommit c3part2 + created new head + $ hg prune -s 'desc(c3part1)' 'desc(c3_)' + 1 changesets pruned + 1 new unstable changesets + $ hg prune -s 'desc(c3part2)' 'desc(c3_)' + 1 changesets pruned + 2 new divergent changesets + $ hg up 'desc(b3prime)' + 2 files updated, 0 files merged, 3 files removed, 0 files unresolved + $ hg amend -m 'b3second' + 1 new unstable changesets + $ hg evolve --rev 'unstable()' + move:[30] add b4_ + atop:[35] b3second + skipping 08a530ce67e1: divergent rewriting. can't choose destination + working directory is now at a51a8a82fdba +
--- a/tests/test-evolve.t Tue Jun 23 15:32:15 2015 -0700 +++ b/tests/test-evolve.t Tue Jun 23 15:32:47 2015 -0700 @@ -22,10 +22,65 @@ > hg ci -m "add $1" > } + $ mkstack() { + > # Creates a stack of commit based on $1 with messages from $2, $3 .. + > hg update $1 -C + > shift + > mkcommits $* + > } + $ glog() { > hg glog --template '{rev}:{node|short}@{branch}({phase}) {desc|firstline}\n' "$@" > } + $ shaof() { + > hg log -T {node} -r "first(desc($1))" + > } + + $ mkcommits() { + > for i in $@; do mkcommit $i ; done + > } + +Test the evolution test topic is installed + + $ hg help evolution + Safely Rewriting History + """""""""""""""""""""""" + + Obsolescence markers make it possible to mark changesets that have been + deleted or superset in a new version of the changeset. + + Unlike the previous way of handling such changes, by stripping the old + changesets from the repository, obsolescence markers can be propagated + between repositories. This allows for a safe and simple way of exchanging + mutable history and altering it after the fact. Changeset phases are + respected, such that only draft and secret changesets can be altered (see + "hg hg phases" for details). + + Obsolescence is tracked using "obsolete markers", a piece of metadata + tracking which changesets have been made obsolete, potential successors + for a given changeset, the moment the changeset was marked as obsolete, + and the user who performed the rewriting operation. The markers are stored + separately from standard changeset data can be exchanged without any of + the precursor changesets, preventing unnecessary exchange of obsolescence + data. + + The complete set of obsolescence markers describes a history of changeset + modifications that is orthogonal to the repository history of file + modifications. This changeset history allows for detection and automatic + resolution of edge cases arising from multiple users rewriting the same + part of history concurrently. + + Current feature status + ====================== + + This feature is still in development. If you see this help, you have + enable an extension that turned this feature on. + + Obsolescence markers will be exchanged between repositories that + explicitly assert support for the obsolescence feature (this can currently + only be done via an extension). + various init $ hg init local @@ -69,9 +124,9 @@ $ hg id -n 5 $ hg kill . - 1 changesets pruned 0 files updated, 0 files merged, 1 files removed, 0 files unresolved working directory now at fbb94e3a0ecf + 1 changesets pruned $ hg qlog 4 - fbb94e3a0ecf add e (draft) 3 - 47d2a3944de8 add d (draft) @@ -82,9 +137,9 @@ test multiple kill $ hg kill 4 -r 3 - 2 changesets pruned 0 files updated, 0 files merged, 1 files removed, 0 files unresolved working directory now at 7c3bad9141dc + 2 changesets pruned $ hg qlog 2 - 4538525df7e2 add c (draft) 1 - 7c3bad9141dc add b (public) @@ -97,9 +152,9 @@ $ echo 4 > g $ hg add g $ hg kill . - 1 changesets pruned 0 files updated, 0 files merged, 1 files removed, 0 files unresolved working directory now at 7c3bad9141dc + 1 changesets pruned $ hg st A g @@ -317,7 +372,7 @@ | o 0 : base - test - $ hg evolve --any --traceback + $ hg evolve --any --traceback --bumped recreate:[8] another feature that rox atop:[7] another feature (child of ba0ec09b1bab) computing new diff @@ -419,7 +474,6 @@ adding manifests adding file changes added 1 changesets with 1 changes to 1 files - pull obsolescence markers $ cd alpha $ cat << EOF > A @@ -476,8 +530,7 @@ adding manifests adding file changes added 1 changesets with 1 changes to 1 files - pull obsolescence markers - 2 obsolescence markers added + 2 new obsolescence markers (run 'hg update' to get a working copy) $ hg up 2 files updated, 0 files merged, 0 files removed, 0 files unresolved @@ -866,9 +919,20 @@ $ hg up 8 1 files updated, 0 files merged, 0 files removed, 0 files unresolved + $ hg log -G --template '{rev} [{branch}] {desc|firstline}\n' + o 10 [default] a1__ + | + | o 9 [mybranch] a3 + | | + | @ 8 [mybranch] a2 + | | + | x 7 [default] a1_ + |/ + o 0 [default] a0 + $ hg evolve - nothing to evolve here - (2 troubled changesets, do you want --any ?) + nothing to evolve on current working copy parent + (2 other unstable in the repository, do you want --any or --rev) [2] @@ -887,3 +951,291 @@ working directory is now at f37ed7a60f43 $ ls .hg/bookmarks* .hg/bookmarks + +Possibility to select what trouble to solve first, asking for bumped before +divergent + $ hg up 10 + 1 files updated, 0 files merged, 0 files removed, 0 files unresolved + $ hg revert -r 11 --all + reverting a + $ hg log -G --template '{rev} [{branch}] {desc|firstline}\n' + o 11 [mybranch] a2 + | + @ 10 [default] a1__ + | + | o 9 [mybranch] a3 + | | + | x 8 [mybranch] a2 + | | + | x 7 [default] a1_ + |/ + o 0 [default] a0 + + $ echo "hello world" > newfile + $ hg add newfile + $ hg commit -m "add new file bumped" -o 11 + $ hg phase --public --hidden 11 + 1 new bumped changesets + $ hg glog + @ 12 : add new file bumped - test + | + | o 11 : a2 - test + |/ + o 10 testbookmark: a1__ - test + | + | o 9 : a3 - test + | | + | x 8 : a2 - test + | | + | x 7 : a1_ - test + |/ + o 0 : a0 - test + + +Now we have a bumped and an unstable changeset, we solve the bumped first +normally the unstable changeset would be solve first + + $ hg glog + @ 12 : add new file bumped - test + | + | o 11 : a2 - test + |/ + o 10 testbookmark: a1__ - test + | + | o 9 : a3 - test + | | + | x 8 : a2 - test + | | + | x 7 : a1_ - test + |/ + o 0 : a0 - test + + $ hg evolve -r 12 --bumped + recreate:[12] add new file bumped + atop:[11] a2 + computing new diff + committed as d66b1e328488 + working directory is now at d66b1e328488 + $ hg evolve --any + move:[9] a3 + atop:[13] bumped update to f37ed7a60f43: + working directory is now at 7d2ce5f38f9b +Check that we can resolve troubles in a revset with more than one commit + $ hg up 14 -C + 0 files updated, 0 files merged, 0 files removed, 0 files unresolved + $ mkcommit gg + $ hg up 14 + 0 files updated, 0 files merged, 1 files removed, 0 files unresolved + $ mkcommit gh + created new head + $ hg up 14 + 0 files updated, 0 files merged, 1 files removed, 0 files unresolved + $ printf "newline\nnewline\n" >> a + $ hg glog + o 16 : add gh - test + | + | o 15 : add gg - test + |/ + @ 14 : a3 - test + | + o 13 : bumped update to f37ed7a60f43: - test + | + o 11 : a2 - test + | + o 10 testbookmark: a1__ - test + | + o 0 : a0 - test + + $ hg amend + 2 new unstable changesets + $ hg glog + @ 18 : a3 - test + | + | o 16 : add gh - test + | | + | | o 15 : add gg - test + | |/ + | x 14 : a3 - test + |/ + o 13 : bumped update to f37ed7a60f43: - test + | + o 11 : a2 - test + | + o 10 testbookmark: a1__ - test + | + o 0 : a0 - test + + +Evolving an empty revset should do nothing + $ hg evolve --rev "16 and 15" + set of specified revisions is empty + [1] + + $ hg evolve --rev "14::" --bumped + no bumped changesets in specified revisions + (do you want to use --unstable) + [2] + $ hg evolve --rev "14::" --unstable + move:[15] add gg + atop:[18] a3 + move:[16] add gh + atop:[18] a3 + working directory is now at db3d894869b0 + $ hg glog + @ 20 : add gh - test + | + | o 19 : add gg - test + |/ + o 18 : a3 - test + | + o 13 : bumped update to f37ed7a60f43: - test + | + o 11 : a2 - test + | + o 10 testbookmark: a1__ - test + | + o 0 : a0 - test + +Check hg evolve --rev on singled out commit + + + $ hg up 19 -C + 1 files updated, 0 files merged, 1 files removed, 0 files unresolved + $ mkcommit j1 + $ mkcommit j2 + $ mkcommit j3 + $ hg up .^^ + 0 files updated, 0 files merged, 2 files removed, 0 files unresolved + $ echo "hello" > j4 + $ hg add j4 + $ hg amend + 2 new unstable changesets + $ glog -r "18::" + @ 25:4c0bc042ef3b@default(draft) add j1 + | + | o 23:c70048fd3350@default(draft) add j3 + | | + | o 22:714e60ca57b7@default(draft) add j2 + | | + | x 21:b430835af718@default(draft) add j1 + |/ + | o 20:db3d894869b0@default(draft) add gh + | | + o | 19:10ffdd7e3cc9@default(draft) add gg + |/ + o 18:0bb66d4c1968@default(draft) a3 + | + + $ hg evolve --rev 23 --any + abort: cannot specify both "--rev" and "--any" + [255] + $ hg evolve --rev 23 + cannot solve instability of c70048fd3350, skipping + +Check that uncommit respects the allowunstable option +With only createmarkers we can only uncommit on a head + $ cat >> $HGRCPATH <<EOF + > [experimental] + > evolution=createmarkers, allnewcommands + > EOF + $ hg up 4c0bc042ef3b^ + 0 files updated, 0 files merged, 2 files removed, 0 files unresolved + $ hg uncommit --all + abort: cannot uncommit in the middle of a stack + [255] + $ hg up 4c0bc042ef3b + 2 files updated, 0 files merged, 0 files removed, 0 files unresolved + $ hg uncommit --all + new changeset is empty + (use "hg prune ." to remove it) + $ glog -r "18::" + @ 26:04b32348803e@default(draft) add j1 + | + | o 23:c70048fd3350@default(draft) add j3 + | | + | o 22:714e60ca57b7@default(draft) add j2 + | | + | x 21:b430835af718@default(draft) add j1 + |/ + | o 20:db3d894869b0@default(draft) add gh + | | + o | 19:10ffdd7e3cc9@default(draft) add gg + |/ + o 18:0bb66d4c1968@default(draft) a3 + | + +Check that prune respects the allowunstable option + $ hg up -C . + 0 files updated, 0 files merged, 0 files removed, 0 files unresolved + $ hg up 20 + 1 files updated, 0 files merged, 1 files removed, 0 files unresolved + $ hg evolve --all + nothing to evolve on current working copy parent + (2 other unstable in the repository, do you want --any or --rev) + [2] + $ hg evolve --all --any + move:[22] add j2 + atop:[26] add j1 + move:[23] add j3 + atop:[27] add j2 + working directory is now at 920a35e8dbd0 + $ glog -r "18::" + @ 28:920a35e8dbd0@default(draft) add j3 + | + o 27:31e050d895dd@default(draft) add j2 + | + o 26:04b32348803e@default(draft) add j1 + | + | o 20:db3d894869b0@default(draft) add gh + | | + o | 19:10ffdd7e3cc9@default(draft) add gg + |/ + o 18:0bb66d4c1968@default(draft) a3 + | + $ hg up 19 + 0 files updated, 0 files merged, 2 files removed, 0 files unresolved + $ mkcommit c5_ + created new head + $ hg prune '26 + 27' + abort: cannot prune in the middle of a stack + [255] + $ hg prune '19::28' + abort: cannot prune in the middle of a stack + [255] + $ hg prune '26::' + 3 changesets pruned + $ glog -r "18::" + @ 29:5a6c53544778@default(draft) add c5_ + | + | o 20:db3d894869b0@default(draft) add gh + | | + o | 19:10ffdd7e3cc9@default(draft) add gg + |/ + o 18:0bb66d4c1968@default(draft) a3 + | + +Check that fold respects the allowunstable option + $ hg up 0bb66d4c1968 + 0 files updated, 0 files merged, 2 files removed, 0 files unresolved + $ mkcommit unstableifparentisfolded + created new head + $ glog -r "18::" + @ 30:30ecefd67c0a@default(draft) add unstableifparentisfolded + | + | o 29:5a6c53544778@default(draft) add c5_ + | | + +---o 20:db3d894869b0@default(draft) add gh + | | + | o 19:10ffdd7e3cc9@default(draft) add gg + |/ + o 18:0bb66d4c1968@default(draft) a3 + | + + $ hg fold --exact "19 + 18" + abort: cannot fold chain not ending with a head or with branching + [255] + $ hg fold --exact "18::29" + abort: cannot fold chain not ending with a head or with branching + [255] + $ hg fold --exact "19::" + 2 changesets folded
--- a/tests/test-exchange-D2.t Tue Jun 23 15:32:15 2015 -0700 +++ b/tests/test-exchange-D2.t Tue Jun 23 15:32:47 2015 -0700 @@ -37,9 +37,9 @@ created new head $ hg debugobsolete `getid 'desc(A0)'` `getid 'desc(A1)'` $ hg prune --date '0 0' . - 1 changesets pruned 0 files updated, 0 files merged, 1 files removed, 0 files unresolved working directory now at a9bdc8b26820 + 1 changesets pruned $ hg strip --hidden -q 'desc(A1)' $ hg log -G --hidden x 28b51eb45704 (draft): A0
--- a/tests/test-exchange-D3.t Tue Jun 23 15:32:15 2015 -0700 +++ b/tests/test-exchange-D3.t Tue Jun 23 15:32:47 2015 -0700 @@ -39,9 +39,9 @@ $ mkcommit A1 $ hg debugobsolete `getid 'desc(A0)'` `getid 'desc(A1)'` $ hg prune -d '0 0' . - 1 changesets pruned 0 files updated, 0 files merged, 1 files removed, 0 files unresolved working directory now at 35b183996678 + 1 changesets pruned $ hg strip --hidden -q 'desc(A1)' $ hg log -G --hidden @ 35b183996678 (draft): B
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-inhibit.t Tue Jun 23 15:32:47 2015 -0700 @@ -0,0 +1,757 @@ + $ cat >> $HGRCPATH <<EOF + > [ui] + > logtemplate = {rev}:{node|short} {desc}\n + > [experimental] + > prunestrip=True + > evolution=createmarkers + > [extensions] + > rebase= + > strip= + > EOF + $ echo "evolve=$(echo $(dirname $TESTDIR))/hgext/evolve.py" >> $HGRCPATH + $ echo "directaccess=$(echo $(dirname $TESTDIR))/hgext/directaccess.py" >> $HGRCPATH + $ echo "inhibit=$(echo $(dirname $TESTDIR))/hgext/inhibit.py" >> $HGRCPATH + $ mkcommit() { + > echo "$1" > "$1" + > hg add "$1" + > hg ci -m "add $1" + > } + + $ hg init inhibit + $ cd inhibit + $ mkcommit cA + $ mkcommit cB + $ mkcommit cC + $ mkcommit cD + $ hg up 'desc(cA)' + 0 files updated, 0 files merged, 3 files removed, 0 files unresolved + $ mkcommit cE + created new head + $ mkcommit cG + $ mkcommit cH + $ mkcommit cJ + $ hg log -G + @ 7:18214586bf78 add cJ + | + o 6:cf5c4f4554ce add cH + | + o 5:5419eb264a33 add cG + | + o 4:98065434e5c6 add cE + | + | o 3:2db36d8066ff add cD + | | + | o 2:7df62a38b9bf add cC + | | + | o 1:02bcbc3f6e56 add cB + |/ + o 0:54ccbc537fc2 add cA + + +plain prune + + $ hg strip 1:: + 3 changesets pruned + $ hg log -G + @ 7:18214586bf78 add cJ + | + o 6:cf5c4f4554ce add cH + | + o 5:5419eb264a33 add cG + | + o 4:98065434e5c6 add cE + | + o 0:54ccbc537fc2 add cA + + $ hg debugobsinhibit --hidden 1:: + $ hg log -G + @ 7:18214586bf78 add cJ + | + o 6:cf5c4f4554ce add cH + | + o 5:5419eb264a33 add cG + | + o 4:98065434e5c6 add cE + | + | o 3:2db36d8066ff add cD + | | + | o 2:7df62a38b9bf add cC + | | + | o 1:02bcbc3f6e56 add cB + |/ + o 0:54ccbc537fc2 add cA + + $ hg strip --hidden 1:: + 3 changesets pruned + $ hg log -G + @ 7:18214586bf78 add cJ + | + o 6:cf5c4f4554ce add cH + | + o 5:5419eb264a33 add cG + | + o 4:98065434e5c6 add cE + | + o 0:54ccbc537fc2 add cA + + +after amend + + $ echo babar > cJ + $ hg commit --amend + $ hg log -G + @ 9:55c73a90e4b4 add cJ + | + o 6:cf5c4f4554ce add cH + | + o 5:5419eb264a33 add cG + | + o 4:98065434e5c6 add cE + | + o 0:54ccbc537fc2 add cA + + $ hg debugobsinhibit --hidden 18214586bf78 + $ hg log -G + @ 9:55c73a90e4b4 add cJ + | + | o 7:18214586bf78 add cJ + |/ + o 6:cf5c4f4554ce add cH + | + o 5:5419eb264a33 add cG + | + o 4:98065434e5c6 add cE + | + o 0:54ccbc537fc2 add cA + + +and no divergence + + $ hg summary + parent: 9:55c73a90e4b4 tip + add cJ + branch: default + commit: (clean) + update: 1 new changesets, 2 branch heads (merge) + phases: 6 draft + +check public revision got cleared +(when adding the second inhibitor, the first one is removed because it is public) + + $ wc -m .hg/store/obsinhibit | sed -e 's/^[ \t]*//' + 20 .hg/store/obsinhibit + $ hg strip 7 + 1 changesets pruned + $ hg debugobsinhibit --hidden 18214586bf78 + $ wc -m .hg/store/obsinhibit | sed -e 's/^[ \t]*//' + 20 .hg/store/obsinhibit + $ hg log -G + @ 9:55c73a90e4b4 add cJ + | + | o 7:18214586bf78 add cJ + |/ + o 6:cf5c4f4554ce add cH + | + o 5:5419eb264a33 add cG + | + o 4:98065434e5c6 add cE + | + o 0:54ccbc537fc2 add cA + + $ hg phase --public 7 + $ hg strip 9 + 0 files updated, 0 files merged, 1 files removed, 0 files unresolved + working directory now at cf5c4f4554ce + 1 changesets pruned + $ hg log -G + o 7:18214586bf78 add cJ + | + @ 6:cf5c4f4554ce add cH + | + o 5:5419eb264a33 add cG + | + o 4:98065434e5c6 add cE + | + o 0:54ccbc537fc2 add cA + + $ hg debugobsinhibit --hidden 55c73a90e4b4 + $ wc -m .hg/store/obsinhibit | sed -e 's/^[ \t]*//' + 20 .hg/store/obsinhibit + $ hg log -G + o 9:55c73a90e4b4 add cJ + | + | o 7:18214586bf78 add cJ + |/ + @ 6:cf5c4f4554ce add cH + | + o 5:5419eb264a33 add cG + | + o 4:98065434e5c6 add cE + | + o 0:54ccbc537fc2 add cA + +Update should inhibit all related unstable commits + + $ hg update 2 --hidden + 2 files updated, 0 files merged, 3 files removed, 0 files unresolved + $ hg log -G + o 9:55c73a90e4b4 add cJ + | + | o 7:18214586bf78 add cJ + |/ + o 6:cf5c4f4554ce add cH + | + o 5:5419eb264a33 add cG + | + o 4:98065434e5c6 add cE + | + | @ 2:7df62a38b9bf add cC + | | + | o 1:02bcbc3f6e56 add cB + |/ + o 0:54ccbc537fc2 add cA + + + $ hg update 9 + 4 files updated, 0 files merged, 2 files removed, 0 files unresolved + $ hg log -G + @ 9:55c73a90e4b4 add cJ + | + | o 7:18214586bf78 add cJ + |/ + o 6:cf5c4f4554ce add cH + | + o 5:5419eb264a33 add cG + | + o 4:98065434e5c6 add cE + | + | o 2:7df62a38b9bf add cC + | | + | o 1:02bcbc3f6e56 add cB + |/ + o 0:54ccbc537fc2 add cA + + $ hg strip --hidden 1:: + 3 changesets pruned + $ hg log -G + @ 9:55c73a90e4b4 add cJ + | + | o 7:18214586bf78 add cJ + |/ + o 6:cf5c4f4554ce add cH + | + o 5:5419eb264a33 add cG + | + o 4:98065434e5c6 add cE + | + o 0:54ccbc537fc2 add cA + + +Bookmark should inhibit all related unstable commits + $ hg bookmark -r 2 book1 --hidden + $ hg log -G + @ 9:55c73a90e4b4 add cJ + | + | o 7:18214586bf78 add cJ + |/ + o 6:cf5c4f4554ce add cH + | + o 5:5419eb264a33 add cG + | + o 4:98065434e5c6 add cE + | + | o 2:7df62a38b9bf add cC + | | + | o 1:02bcbc3f6e56 add cB + |/ + o 0:54ccbc537fc2 add cA + + +Removing a bookmark with bookmark -D should prune the changes underneath +that are not reachable from another bookmark or head + + $ hg bookmark -r 1 book2 + $ hg bookmark -D book1 --config experimental.evolution=createmarkers #--config to make sure prune is not registered as a command. + bookmark 'book1' deleted + 1 changesets pruned + $ hg log -G + @ 9:55c73a90e4b4 add cJ + | + | o 7:18214586bf78 add cJ + |/ + o 6:cf5c4f4554ce add cH + | + o 5:5419eb264a33 add cG + | + o 4:98065434e5c6 add cE + | + | o 1:02bcbc3f6e56 add cB + |/ + o 0:54ccbc537fc2 add cA + + $ hg bookmark -D book2 + bookmark 'book2' deleted + 1 changesets pruned + $ hg log -G + @ 9:55c73a90e4b4 add cJ + | + | o 7:18214586bf78 add cJ + |/ + o 6:cf5c4f4554ce add cH + | + o 5:5419eb264a33 add cG + | + o 4:98065434e5c6 add cE + | + o 0:54ccbc537fc2 add cA + +Test that direct access make changesets visible + + $ hg export 2db36d8066ff 02bcbc3f6e56 + # HG changeset patch + # User test + # Date 0 0 + # Thu Jan 01 00:00:00 1970 +0000 + # Node ID 2db36d8066ff50e8be3d3e6c2da1ebc0a8381d82 + # Parent 7df62a38b9bf9daf968de235043ba88a8ef43393 + add cD + + diff -r 7df62a38b9bf -r 2db36d8066ff cD + --- /dev/null Thu Jan 01 00:00:00 1970 +0000 + +++ b/cD Thu Jan 01 00:00:00 1970 +0000 + @@ -0,0 +1,1 @@ + +cD + # HG changeset patch + # User test + # Date 0 0 + # Thu Jan 01 00:00:00 1970 +0000 + # Node ID 02bcbc3f6e56fb2928efec2c6e24472720bf5511 + # Parent 54ccbc537fc2d6845a5d61337c1cfb80d1d2815e + add cB + + diff -r 54ccbc537fc2 -r 02bcbc3f6e56 cB + --- /dev/null Thu Jan 01 00:00:00 1970 +0000 + +++ b/cB Thu Jan 01 00:00:00 1970 +0000 + @@ -0,0 +1,1 @@ + +cB + +But only with hash + + $ hg export 2db36d8066ff:: + # HG changeset patch + # User test + # Date 0 0 + # Thu Jan 01 00:00:00 1970 +0000 + # Node ID 2db36d8066ff50e8be3d3e6c2da1ebc0a8381d82 + # Parent 7df62a38b9bf9daf968de235043ba88a8ef43393 + add cD + + diff -r 7df62a38b9bf -r 2db36d8066ff cD + --- /dev/null Thu Jan 01 00:00:00 1970 +0000 + +++ b/cD Thu Jan 01 00:00:00 1970 +0000 + @@ -0,0 +1,1 @@ + +cD + + $ hg export 1 3 + abort: hidden revision '1'! + (use --hidden to access hidden revisions) + [255] + + +With severals hidden sha, rebase of one hidden stack onto another one: + $ hg update -C 0 + 0 files updated, 0 files merged, 4 files removed, 0 files unresolved + $ mkcommit cK + created new head + $ mkcommit cL + $ hg update -C 9 + 4 files updated, 0 files merged, 2 files removed, 0 files unresolved + $ hg log -G + o 11:53a94305e133 add cL + | + o 10:ad78ff7d621f add cK + | + | @ 9:55c73a90e4b4 add cJ + | | + | | o 7:18214586bf78 add cJ + | |/ + | o 6:cf5c4f4554ce add cH + | | + | o 5:5419eb264a33 add cG + | | + | o 4:98065434e5c6 add cE + |/ + o 0:54ccbc537fc2 add cA + + $ hg strip --hidden 10: + 2 changesets pruned + $ hg log -G + @ 9:55c73a90e4b4 add cJ + | + | o 7:18214586bf78 add cJ + |/ + o 6:cf5c4f4554ce add cH + | + o 5:5419eb264a33 add cG + | + o 4:98065434e5c6 add cE + | + o 0:54ccbc537fc2 add cA + + $ hg rebase -s 10 -d 3 + abort: hidden revision '3'! + (use --hidden to access hidden revisions) + [255] + $ hg rebase -r ad78ff7d621f -r 53a94305e133 -d 2db36d8066ff + Warning: accessing hidden changesets 2db36d8066ff for write operation + Warning: accessing hidden changesets ad78ff7d621f for write operation + Warning: accessing hidden changesets 53a94305e133 for write operation + rebasing 10:ad78ff7d621f "add cK" + rebasing 11:53a94305e133 "add cL" + $ hg log -G + o 13:2f7b7704d714 add cL + | + o 12:fe1634cbe235 add cK + | + | @ 9:55c73a90e4b4 add cJ + | | + | | o 7:18214586bf78 add cJ + | |/ + | o 6:cf5c4f4554ce add cH + | | + | o 5:5419eb264a33 add cG + | | + | o 4:98065434e5c6 add cE + | | + o | 3:2db36d8066ff add cD + | | + o | 2:7df62a38b9bf add cC + | | + o | 1:02bcbc3f6e56 add cB + |/ + o 0:54ccbc537fc2 add cA + +Check that amending in the middle of a stack does not show obsolete revs +Since we are doing operation in the middle of the stack we cannot just +have createmarkers as we are creating instability + + $ cat >> $HGRCPATH <<EOF + > [experimental] + > evolution=all + > EOF + + $ hg strip --hidden 1:: + 5 changesets pruned + $ hg log -G + @ 9:55c73a90e4b4 add cJ + | + | o 7:18214586bf78 add cJ + |/ + o 6:cf5c4f4554ce add cH + | + o 5:5419eb264a33 add cG + | + o 4:98065434e5c6 add cE + | + o 0:54ccbc537fc2 add cA + + $ hg up 7 + 1 files updated, 0 files merged, 0 files removed, 0 files unresolved + $ mkcommit cL + $ mkcommit cM + $ mkcommit cN + $ hg log -G + @ 16:a438c045eb37 add cN + | + o 15:2d66e189f5b5 add cM + | + o 14:d66ccb8c5871 add cL + | + | o 9:55c73a90e4b4 add cJ + | | + o | 7:18214586bf78 add cJ + |/ + o 6:cf5c4f4554ce add cH + | + o 5:5419eb264a33 add cG + | + o 4:98065434e5c6 add cE + | + o 0:54ccbc537fc2 add cA + + $ hg up 15 + 0 files updated, 0 files merged, 1 files removed, 0 files unresolved + $ echo "mmm" >> cM + $ hg amend + $ hg log -G + @ 18:210589181b14 add cM + | + | o 16:a438c045eb37 add cN + | | + | o 15:2d66e189f5b5 add cM + |/ + o 14:d66ccb8c5871 add cL + | + | o 9:55c73a90e4b4 add cJ + | | + o | 7:18214586bf78 add cJ + |/ + o 6:cf5c4f4554ce add cH + | + o 5:5419eb264a33 add cG + | + o 4:98065434e5c6 add cE + | + o 0:54ccbc537fc2 add cA + +Check that rebasing a commit twice makes the commit visible again + + $ hg rebase -d 18 -r 16 --keep + rebasing 16:a438c045eb37 "add cN" + $ hg log -r 14:: -G + o 19:104eed5354c7 add cN + | + @ 18:210589181b14 add cM + | + | o 16:a438c045eb37 add cN + | | + | o 15:2d66e189f5b5 add cM + |/ + o 14:d66ccb8c5871 add cL + | + $ hg strip -r 104eed5354c7 + 1 changesets pruned + $ hg rebase -d 18 -r 16 --keep + rebasing 16:a438c045eb37 "add cN" + $ hg log -r 14:: -G + o 19:104eed5354c7 add cN + | + @ 18:210589181b14 add cM + | + | o 16:a438c045eb37 add cN + | | + | o 15:2d66e189f5b5 add cM + |/ + o 14:d66ccb8c5871 add cL + | + +Test prunestrip + + $ hg book foo -r 104eed5354c7 + $ hg strip -r 210589181b14 + 0 files updated, 0 files merged, 1 files removed, 0 files unresolved + working directory now at d66ccb8c5871 + 2 changesets pruned + $ hg log -r 14:: -G -T '{rev}:{node|short} {desc|firstline} {bookmarks}\n' + o 16:a438c045eb37 add cN + | + o 15:2d66e189f5b5 add cM + | + @ 14:d66ccb8c5871 add cL foo + | + +Check that --hidden used with inhibit does not hide every obsolete commit +We show the log before and after a log -G --hidden, they should be the same + $ hg log -G + o 16:a438c045eb37 add cN + | + o 15:2d66e189f5b5 add cM + | + @ 14:d66ccb8c5871 add cL + | + | o 9:55c73a90e4b4 add cJ + | | + o | 7:18214586bf78 add cJ + |/ + o 6:cf5c4f4554ce add cH + | + o 5:5419eb264a33 add cG + | + o 4:98065434e5c6 add cE + | + o 0:54ccbc537fc2 add cA + + $ hg log -G --hidden + x 19:104eed5354c7 add cN + | + x 18:210589181b14 add cM + | + | x 17:b3c3274523f9 temporary amend commit for 2d66e189f5b5 + | | + | | o 16:a438c045eb37 add cN + | |/ + | o 15:2d66e189f5b5 add cM + |/ + @ 14:d66ccb8c5871 add cL + | + | x 13:2f7b7704d714 add cL + | | + | x 12:fe1634cbe235 add cK + | | + | | x 11:53a94305e133 add cL + | | | + | | x 10:ad78ff7d621f add cK + | | | + | | | o 9:55c73a90e4b4 add cJ + | | | | + +-------x 8:e84f73d9ad36 temporary amend commit for 18214586bf78 + | | | | + o-----+ 7:18214586bf78 add cJ + / / / + | | o 6:cf5c4f4554ce add cH + | | | + | | o 5:5419eb264a33 add cG + | | | + | | o 4:98065434e5c6 add cE + | |/ + x | 3:2db36d8066ff add cD + | | + x | 2:7df62a38b9bf add cC + | | + x | 1:02bcbc3f6e56 add cB + |/ + o 0:54ccbc537fc2 add cA + + + $ hg log -G + o 16:a438c045eb37 add cN + | + o 15:2d66e189f5b5 add cM + | + @ 14:d66ccb8c5871 add cL + | + | o 9:55c73a90e4b4 add cJ + | | + o | 7:18214586bf78 add cJ + |/ + o 6:cf5c4f4554ce add cH + | + o 5:5419eb264a33 add cG + | + o 4:98065434e5c6 add cE + | + o 0:54ccbc537fc2 add cA + + +check that pruning and inhibited node does not confuse anything + + $ hg up --hidden 210589181b14 + 1 files updated, 0 files merged, 0 files removed, 0 files unresolved + $ hg strip --bundle 210589181b14 + 0 files updated, 0 files merged, 1 files removed, 0 files unresolved + saved backup bundle to $TESTTMP/inhibit/.hg/strip-backup/210589181b14-e09c7b88-backup.hg (glob) + $ hg unbundle .hg/strip-backup/210589181b14-e09c7b88-backup.hg # restore state + adding changesets + adding manifests + adding file changes + added 2 changesets with 1 changes to 2 files (+1 heads) + (run 'hg heads' to see heads, 'hg merge' to merge) + + Only allow direct access and check that evolve works like before + $ cat >> $HGRCPATH <<EOF + > [extensions] + > inhibit=! + > EOF + + $ hg up 15 + 1 files updated, 0 files merged, 0 files removed, 0 files unresolved + working directory parent is obsolete! + $ echo "CM" > cM + $ hg amend + $ hg log -G + @ 21:721c3c279519 add cM + | + | o 16:a438c045eb37 add cN + | | + | x 15:2d66e189f5b5 add cM + |/ + o 14:d66ccb8c5871 add cL + | + o 7:18214586bf78 add cJ + | + o 6:cf5c4f4554ce add cH + | + o 5:5419eb264a33 add cG + | + o 4:98065434e5c6 add cE + | + o 0:54ccbc537fc2 add cA + + $ cat >> $HGRCPATH <<EOF + > [extensions] + > EOF + $ echo "inhibit=$(echo $(dirname $TESTDIR))/hgext/inhibit.py" >> $HGRCPATH + +Empty commit + $ hg amend + nothing changed + [1] + +Directaccess should load after some extensions precised in the conf +With no extension specified: + + $ cat >$TESTTMP/test_extension.py << EOF + > from mercurial import extensions + > def uisetup(ui): + > print extensions._order + > EOF + $ cat >> $HGRCPATH << EOF + > [extensions] + > testextension=$TESTTMP/test_extension.py + > EOF + $ hg id + ['rebase', 'strip', 'evolve', 'directaccess', 'inhibit', 'testextension'] + 721c3c279519 tip + +With test_extension specified: + $ cat >> $HGRCPATH << EOF + > [directaccess] + > loadsafter=testextension + > EOF + $ hg id + ['rebase', 'strip', 'evolve', 'inhibit', 'testextension', 'directaccess'] + 721c3c279519 tip + +Inhibit should not work without directaccess + $ cat >> $HGRCPATH <<EOF + > [extensions] + > directaccess=! + > testextension=! + > EOF + $ hg up 15 + abort: Cannot use inhibit without the direct access extension + [255] + $ echo "directaccess=$(echo $(dirname $TESTDIR))/hgext/directaccess.py" >> $HGRCPATH + $ cd .. + + +hg push should not allow directaccess unless forced with --hidden +We copy the inhibhit repo to inhibit2 and make some changes to push to inhibit + + $ cp -r inhibit inhibit2 + $ pwd=$(pwd) + $ cd inhibit + $ mkcommit pk + $ hg id + 003a4735afde tip + $ echo "OO" > pk + $ hg amend + $ hg id + 71eb4f100663 tip + +Hidden commits cannot be pushed without --hidden + $ hg push -r 003a4735afde file://$pwd/inhibit2 + pushing to file://$TESTTMP/inhibit2 + abort: hidden revision '003a4735afde'! + (use --hidden to access hidden revisions) + [255] + +Visible commits can still be pushed + $ hg push -r 71eb4f100663 file://$pwd/inhibit2 + pushing to file://$TESTTMP/inhibit2 + searching for changes + adding changesets + adding manifests + adding file changes + added 1 changesets with 1 changes to 1 files + 2 new obsolescence markers
--- a/tests/test-obsolete.t Tue Jun 23 15:32:15 2015 -0700 +++ b/tests/test-obsolete.t Tue Jun 23 15:32:47 2015 -0700 @@ -183,8 +183,7 @@ adding manifests adding file changes added 5 changesets with 5 changes to 5 files (+1 heads) - pushing 2 obsolescence markers (* bytes) (glob) - 2 obsolescence markers added + 2 new obsolescence markers $ hg -R ../other-new verify checking changesets checking manifests @@ -238,8 +237,7 @@ adding manifests adding file changes added 1 changesets with 1 changes to 1 files (+1 heads) - pushing 3 obsolescence markers (* bytes) (glob) - 1 obsolescence markers added + 1 new obsolescence markers $ qlog -R ../other-new 5 - 95de7fc6918d @@ -261,8 +259,6 @@ pushing to ../other-new searching for changes no changes found - pushing 3 obsolescence markers (* bytes) (glob) - 0 obsolescence markers added [1] $ hg up --hidden -q .^ # 3 @@ -278,9 +274,8 @@ adding manifests adding file changes added 1 changesets with 1 changes to [12] files \(\+1 heads\) (re) - pull obsolescence markers - 1 obsolescence markers added - (run 'hg heads' to see heads, 'hg merge' to merge) + 1 new obsolescence markers + (run 'hg heads .' to see heads, 'hg merge' to merge) $ qlog -R ../other-new 6 - 909a0fb57e5d @@ -369,9 +364,8 @@ adding manifests adding file changes added 1 changesets with 1 changes to [12] files \(\+1 heads\) (re) - pull obsolescence markers - 1 obsolescence markers added - (run 'hg heads' to see heads, 'hg merge' to merge) + 1 new obsolescence markers + (run 'hg heads .' to see heads, 'hg merge' to merge) $ hg up -q 7 # to check rollback update behavior $ qlog @@ -394,6 +388,7 @@ branch: default commit: 1 deleted, 2 unknown (clean) update: 2 new changesets, 2 branch heads (merge) + phases: 4 draft unstable: 1 changesets $ qlog 6 @@ -543,8 +538,7 @@ adding manifests adding file changes added 2 changesets with 1 changes to [12] files (re) - pushing 7 obsolescence markers (* bytes) (glob) - 3 obsolescence markers added + 3 new obsolescence markers $ hg up -q 10 $ mkcommit "obsol_d'''" created new head @@ -556,8 +550,7 @@ adding manifests adding file changes added 1 changesets with 1 changes to 1 files (+1 heads) - pushing 8 obsolescence markers (* bytes) (glob) - 1 obsolescence markers added + 1 new obsolescence markers $ cd .. check bumped detection @@ -669,6 +662,7 @@ branch: default commit: (clean) update: (2|9|11) new changesets, (3|9|10) branch heads \(merge\) (re) + phases: 3 draft bumped: 1 changesets $ hg debugobsolete `getid a7a6f2b5d8a5` `getid 50f11e5e3a63` $ hg log -r 'divergent()'
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-options.t Tue Jun 23 15:32:47 2015 -0700 @@ -0,0 +1,30 @@ + $ cat >> $HGRCPATH <<EOF + > [ui] + > logtemplate={rev}:{node|short}[{bookmarks}] ({obsolete}/{phase}) {desc|firstline}\n + > [extensions] + > EOF + $ echo "evolve=$(echo $(dirname $TESTDIR))/hgext/evolve.py" >> $HGRCPATH + + $ mkcommit() { + > echo "$1" > "$1" + > hg add "$1" + > hg ci -m "add $1" + > } + + $ hg init repo + $ cd repo + $ mkcommit a + $ mkcommit b + +test disabling commands + + $ cat >> .hg/hgrc <<EOF + > [experimental] + > evolution=createmarkers + > allowunstable + > exchange + > EOF + $ hg prune | head -n 2 + hg: unknown command 'prune' + Mercurial Distributed SCM +
--- a/tests/test-prune.t Tue Jun 23 15:32:15 2015 -0700 +++ b/tests/test-prune.t Tue Jun 23 15:32:47 2015 -0700 @@ -38,9 +38,9 @@ prune current and tip changeset $ hg prune --user blah --date '1979-12-15' . - 1 changesets pruned 0 files updated, 0 files merged, 1 files removed, 0 files unresolved working directory now at 47d2a3944de8 + 1 changesets pruned $ hg bookmark * BABAR 3:47d2a3944de8 $ hg debugobsolete @@ -59,9 +59,9 @@ pruning multiple changeset at once $ hg prune 2: - 2 changesets pruned 0 files updated, 0 files merged, 3 files removed, 0 files unresolved working directory now at 1f0dee641bb7 + 2 changesets pruned $ hg debugobsolete 9d206ffc875e1bc304590549be293be36821e66c 0 {47d2a3944de8b013de3be9578e8e344ea2e6c097} (Sat Dec 15 00:00:00 1979 +0000) {'user': 'blah'} 7c3bad9141dcb46ff89abf5f61856facd56e476c 0 {1f0dee641bb7258c56bd60e93edfa2405381c41e} (*) {'user': 'test'} (glob) @@ -120,9 +120,9 @@ $ hg up 'desc("add ee")' 4 files updated, 0 files merged, 4 files removed, 0 files unresolved $ hg prune 'desc("add ee")' -s 'desc("add nE")' - 1 changesets pruned 4 files updated, 0 files merged, 4 files removed, 0 files unresolved working directory now at 6e8148413dd5 + 1 changesets pruned $ hg debugobsolete 9d206ffc875e1bc304590549be293be36821e66c 0 {47d2a3944de8b013de3be9578e8e344ea2e6c097} (Sat Dec 15 00:00:00 1979 +0000) {'user': 'blah'} 7c3bad9141dcb46ff89abf5f61856facd56e476c 0 {1f0dee641bb7258c56bd60e93edfa2405381c41e} (*) {'user': 'test'} (glob) @@ -210,9 +210,9 @@ $ mkcommit n2 $ hg prune 'desc("add n1")::desc("add n2")' -s 'desc("add nD")::desc("add nE")' --biject - 2 changesets pruned 0 files updated, 0 files merged, 2 files removed, 0 files unresolved working directory now at 1f0dee641bb7 + 2 changesets pruned $ hg debugobsolete 9d206ffc875e1bc304590549be293be36821e66c 0 {47d2a3944de8b013de3be9578e8e344ea2e6c097} (Sat Dec 15 00:00:00 1979 +0000) {'user': 'blah'} 7c3bad9141dcb46ff89abf5f61856facd56e476c 0 {1f0dee641bb7258c56bd60e93edfa2405381c41e} (*) {'user': 'test'} (glob) @@ -225,6 +225,35 @@ cb7f8f706a6532967b98cf8583a81baab79a0fa7 8ee176ff1d4b2034ce51e3efc579c2de346b631d 0 (*) {'user': 'test'} (glob) 21b6f2f1cece8c10326e575dd38239189d467190 6e8148413dd541855b72a920a90c06fca127c7e7 0 (*) {'user': 'test'} (glob) +test hg strip replacement + + $ hg up 10 + 2 files updated, 0 files merged, 0 files removed, 0 files unresolved + $ mkcommit n1 + created new head + $ mkcommit n2 + $ hg --config extensions.strip= --config experimental.prunestrip=True strip -r . + 0 files updated, 0 files merged, 1 files removed, 0 files unresolved + working directory now at c7e58696a948 + 1 changesets pruned + $ hg --config extensions.strip= --config experimental.prunestrip=True strip -r . --bundle + 0 files updated, 0 files merged, 1 files removed, 0 files unresolved + saved backup bundle to $TESTTMP/repo/.hg/strip-backup/c7e58696a948-69ca36d3-backup.hg (glob) + +test hg prune --keep + $ mkcommit n1 + created new head + $ hg diff -r .^ + diff -r aa96dc3f04c2 n1 + --- /dev/null Thu Jan 01 00:00:00 1970 +0000 + +++ b/n1 * +0000 (glob) + @@ -0,0 +1,1 @@ + +n1 + $ hg prune -r . --keep + 1 changesets pruned + $ hg status + ? n1 + test hg prune -B bookmark yoinked from test-mq-strip.t @@ -245,11 +274,11 @@ [255] $ hg tag --remove --local a $ hg prune -B todelete - 1 changesets pruned 0 files updated, 0 files merged, 0 files removed, 0 files unresolved (leaving bookmark todelete) working directory now at d62d843c9a01 bookmark 'todelete' deleted + 1 changesets pruned $ hg id -ir dcbb326fdec2 abort: hidden revision 'dcbb326fdec2'! (use --hidden to access hidden revisions) @@ -260,8 +289,8 @@ B 10:ff43616e5d0f delete 6:2702dd0c91e7 $ hg prune -B delete + bookmark 'delete' deleted 3 changesets pruned - bookmark 'delete' deleted $ hg tag --remove --local c $ hg id -ir 6:2702dd0c91e7 abort: hidden revision '6'! @@ -332,3 +361,12 @@ |/ o 0:1ea73414a91b[] (stable/draft) r0 + $ hg book CELESTE + $ hg prune -r . --keep + 1 changesets pruned + $ hg book + B 8:d62d843c9a01 + * CELESTE 8:d62d843c9a01 + r10 8:d62d843c9a01 + rg 8:d62d843c9a01 +
--- a/tests/test-sharing.t Tue Jun 23 15:32:15 2015 -0700 +++ b/tests/test-sharing.t Tue Jun 23 15:32:47 2015 -0700 @@ -46,7 +46,6 @@ adding manifests adding file changes added 1 changesets with 1 changes to 1 files - pull obsolescence markers 1 files updated, 0 files merged, 0 files removed, 0 files unresolved Let's commit a preliminary change and push it to ``test-repo`` for @@ -88,8 +87,7 @@ adding manifests adding file changes added 1 changesets with 1 changes to 1 files (+1 heads) - pull obsolescence markers - 2 obsolescence markers added + 2 new obsolescence markers 1 files updated, 0 files merged, 0 files removed, 0 files unresolved Figure SG03 @@ -140,8 +138,7 @@ adding manifests adding file changes added 1 changesets with 1 changes to 1 files - pushing 4 obsolescence markers (* bytes) (glob) - 4 obsolescence markers added + 4 new obsolescence markers Now that the fix is public, we cannot amend it any more. $ hg amend -m 'fix bug 37' @@ -161,8 +158,6 @@ pushing to ../dev-repo searching for changes no changes found - pushing 4 obsolescence markers (* bytes) (glob) - 0 obsolescence markers added [1] $ hg -R ../dev-repo shortlog -r 'draft()' @@ -196,8 +191,6 @@ adding manifests adding file changes added 1 changesets with 1 changes to 1 files - pushing 4 obsolescence markers (* bytes) (glob) - 0 obsolescence markers added exporting bookmark bug15 $ hg -R ../review bookmarks bug15 2:f91e97234c2b @@ -213,8 +206,7 @@ adding manifests adding file changes added 1 changesets with 1 changes to 1 files (+1 heads) - pushing 6 obsolescence markers (* bytes) (glob) - 2 obsolescence markers added + 2 new obsolescence markers updating bookmark bug15 $ hg -R ../review bookmarks bug15 3:cbdfbd5a5db2 @@ -241,8 +233,6 @@ adding manifests adding file changes added 1 changesets with 1 changes to 1 files (+1 heads) - pushing 4 obsolescence markers (* bytes) (glob) - 0 obsolescence markers added exporting bookmark featureX $ hg -R ../review bookmarks bug15 3:cbdfbd5a5db2 @@ -259,8 +249,7 @@ adding manifests adding file changes added 1 changesets with 1 changes to 1 files (+1 heads) - pushing 6 obsolescence markers (* bytes) (glob) - 2 obsolescence markers added + 2 new obsolescence markers updating bookmark featureX Bob receives second review, amends, and pushes to public: @@ -274,8 +263,7 @@ adding manifests adding file changes added 1 changesets with 1 changes to 1 files - pushing 8 obsolescence markers (* bytes) (glob) - 4 obsolescence markers added + 4 new obsolescence markers $ hg -R ../public bookmarks no bookmarks set $ hg push ../review @@ -286,8 +274,7 @@ adding manifests adding file changes added 1 changesets with 1 changes to 1 files (+1 heads) - pushing 8 obsolescence markers (* bytes) (glob) - 2 obsolescence markers added + 2 new obsolescence markers updating bookmark featureX $ hg -R ../review bookmarks bug15 3:cbdfbd5a5db2 @@ -357,8 +344,7 @@ adding manifests adding file changes added 1 changesets with 1 changes to 1 files (+1 heads) - pull obsolescence markers - 4 obsolescence markers added + 4 new obsolescence markers (run 'hg heads' to see heads, 'hg merge' to merge) $ hg log -G -q -r 'head()' o 5:540ba8f317e6 @@ -388,8 +374,7 @@ adding manifests adding file changes added 1 changesets with 1 changes to 1 files - pushing 11 obsolescence markers (* bytes) (glob) - 3 obsolescence markers added + 3 new obsolescence markers $ hg push ../review pushing to ../review searching for changes @@ -397,8 +382,7 @@ adding manifests adding file changes added 1 changesets with 0 changes to 1 files - pushing 11 obsolescence markers (* bytes) (glob) - 1 obsolescence markers added + 1 new obsolescence markers updating bookmark bug15 Figure SG08: review and public changesets after Alice pushes. @@ -460,8 +444,6 @@ adding manifests adding file changes added 1 changesets with 1 changes to 1 files - pull obsolescence markers - 0 obsolescence markers added 1 files updated, 0 files merged, 0 files removed, 0 files unresolved $ echo 'better fix (alice)' >> file1 $ hg amend -u alice -m 'fix bug 24 (v2 by alice)' @@ -489,8 +471,7 @@ adding manifests adding file changes added 1 changesets with 1 changes to 1 files (+1 heads) - pull obsolescence markers - 2 obsolescence markers added + 2 new obsolescence markers (run 'hg heads' to see heads, 'hg merge' to merge) 2 new divergent changesets @@ -511,7 +492,7 @@ 7:e3f99ce9d9cd draft fix bug 24 (v2 by alice) Use evolve to fix the divergence. - $ HGMERGE=internal:other hg evolve + $ HGMERGE=internal:other hg evolve --divergent merge:[6] fix bug 24 (v2 by bob) with: [7] fix bug 24 (v2 by alice) base: [4] fix bug 24 (v1)
--- a/tests/test-simple4server-bundle2.t Tue Jun 23 15:32:15 2015 -0700 +++ b/tests/test-simple4server-bundle2.t Tue Jun 23 15:32:47 2015 -0700 @@ -72,9 +72,9 @@ =================== $ wget -q -O - http://localhost:$HGPORT/?cmd=hello - capabilities: lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch stream bundle2=HG20%0Achangegroup%3D01%2C02%0Adigests%3Dmd5%2Csha1%2Csha512%0Alistkeys%0Aobsmarkers%3DV0%2CV1%0Apushkey%0Aremote-changegroup%3Dhttp%2Chttps unbundle=HG10GZ,HG10BZ,HG10UN httpheader=1024 _evoext_pushobsmarkers_0 _evoext_pullobsmarkers_0 _evoext_obshash_0 _evoext_obshash_1 _evoext_getbundle_obscommon + capabilities: * _evoext_pushobsmarkers_0 _evoext_pullobsmarkers_0 _evoext_obshash_0 _evoext_obshash_1 _evoext_getbundle_obscommon (glob) $ wget -q -O - http://localhost:$HGPORT/?cmd=capabilities - lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch stream bundle2=HG20%0Achangegroup%3D01%2C02%0Adigests%3Dmd5%2Csha1%2Csha512%0Alistkeys%0Aobsmarkers%3DV0%2CV1%0Apushkey%0Aremote-changegroup%3Dhttp%2Chttps unbundle=HG10GZ,HG10BZ,HG10UN httpheader=1024 _evoext_pushobsmarkers_0 _evoext_pullobsmarkers_0 _evoext_obshash_0 _evoext_obshash_1 _evoext_getbundle_obscommon (no-eol) + * _evoext_pushobsmarkers_0 _evoext_pullobsmarkers_0 _evoext_obshash_0 _evoext_obshash_1 _evoext_getbundle_obscommon (no-eol) (glob) $ wget -q -O - "http://localhost:$HGPORT/?cmd=listkeys&namespace=namespaces" | sort bookmarks @@ -134,13 +134,13 @@ obsolete phases $ wget -q -O - http://localhost:$HGPORT/?cmd=hello - capabilities: lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch stream bundle2=HG20%0Achangegroup%3D01%2C02%0Adigests%3Dmd5%2Csha1%2Csha512%0Alistkeys%0Aobsmarkers%3DV0%2CV1%0Apushkey%0Aremote-changegroup%3Dhttp%2Chttps unbundle=HG10GZ,HG10BZ,HG10UN httpheader=1024 _evoext_pushobsmarkers_0 _evoext_pullobsmarkers_0 _evoext_obshash_0 _evoext_obshash_1 _evoext_getbundle_obscommon + capabilities: * _evoext_pushobsmarkers_0 _evoext_pullobsmarkers_0 _evoext_obshash_0 _evoext_obshash_1 _evoext_getbundle_obscommon (glob) $ wget -q -O - http://localhost:$HGPORT/?cmd=capabilities - lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch stream bundle2=HG20%0Achangegroup%3D01%2C02%0Adigests%3Dmd5%2Csha1%2Csha512%0Alistkeys%0Aobsmarkers%3DV0%2CV1%0Apushkey%0Aremote-changegroup%3Dhttp%2Chttps unbundle=HG10GZ,HG10BZ,HG10UN httpheader=1024 _evoext_pushobsmarkers_0 _evoext_pullobsmarkers_0 _evoext_obshash_0 _evoext_obshash_1 _evoext_getbundle_obscommon (no-eol) + * _evoext_pushobsmarkers_0 _evoext_pullobsmarkers_0 _evoext_obshash_0 _evoext_obshash_1 _evoext_getbundle_obscommon (no-eol) (glob) $ echo '[__temporary__]' >> server/.hg/hgrc $ echo 'advertiseobsolete=False' >> server/.hg/hgrc - $ $TESTDIR/killdaemons.py + $ $TESTDIR/killdaemons.py $DAEMON_PIDS $ hg serve -R server -n test -p $HGPORT -d --pid-file=hg.pid -A access.log -E errors.log $ cat hg.pid >> $DAEMON_PIDS @@ -148,13 +148,9 @@ bookmarks namespaces phases - $ wget -q -O - http://localhost:$HGPORT/?cmd=hello - capabilities: lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch stream bundle2=HG20%0Achangegroup%3D01%2C02%0Adigests%3Dmd5%2Csha1%2Csha512%0Alistkeys%0Aobsmarkers%3DV0%2CV1%0Apushkey%0Aremote-changegroup%3Dhttp%2Chttps unbundle=HG10GZ,HG10BZ,HG10UN httpheader=1024 - $ wget -q -O - http://localhost:$HGPORT/?cmd=capabilities - lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch stream bundle2=HG20%0Achangegroup%3D01%2C02%0Adigests%3Dmd5%2Csha1%2Csha512%0Alistkeys%0Aobsmarkers%3DV0%2CV1%0Apushkey%0Aremote-changegroup%3Dhttp%2Chttps unbundle=HG10GZ,HG10BZ,HG10UN httpheader=1024 (no-eol) $ echo 'advertiseobsolete=True' >> server/.hg/hgrc - $ $TESTDIR/killdaemons.py + $ $TESTDIR/killdaemons.py $DAEMON_PIDS $ hg serve -R server -n test -p $HGPORT -d --pid-file=hg.pid -A access.log -E errors.log $ cat hg.pid >> $DAEMON_PIDS @@ -163,7 +159,8 @@ namespaces obsolete phases + $ wget -q -O - http://localhost:$HGPORT/?cmd=hello - capabilities: lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch stream bundle2=HG20%0Achangegroup%3D01%2C02%0Adigests%3Dmd5%2Csha1%2Csha512%0Alistkeys%0Aobsmarkers%3DV0%2CV1%0Apushkey%0Aremote-changegroup%3Dhttp%2Chttps unbundle=HG10GZ,HG10BZ,HG10UN httpheader=1024 _evoext_pushobsmarkers_0 _evoext_pullobsmarkers_0 _evoext_obshash_0 _evoext_obshash_1 _evoext_getbundle_obscommon + capabilities: * _evoext_pushobsmarkers_0 _evoext_pullobsmarkers_0 _evoext_obshash_0 _evoext_obshash_1 _evoext_getbundle_obscommon (glob) $ wget -q -O - http://localhost:$HGPORT/?cmd=capabilities - lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch stream bundle2=HG20%0Achangegroup%3D01%2C02%0Adigests%3Dmd5%2Csha1%2Csha512%0Alistkeys%0Aobsmarkers%3DV0%2CV1%0Apushkey%0Aremote-changegroup%3Dhttp%2Chttps unbundle=HG10GZ,HG10BZ,HG10UN httpheader=1024 _evoext_pushobsmarkers_0 _evoext_pullobsmarkers_0 _evoext_obshash_0 _evoext_obshash_1 _evoext_getbundle_obscommon (no-eol) + * _evoext_pushobsmarkers_0 _evoext_pullobsmarkers_0 _evoext_obshash_0 _evoext_obshash_1 _evoext_getbundle_obscommon (no-eol) (glob)
--- a/tests/test-simple4server.t Tue Jun 23 15:32:15 2015 -0700 +++ b/tests/test-simple4server.t Tue Jun 23 15:32:47 2015 -0700 @@ -7,6 +7,8 @@ > allow_push = * > [phases] > publish = False + > [experimental] + > bundle2-exp=False > [extensions] > EOF @@ -72,9 +74,9 @@ =================== $ wget -q -O - http://localhost:$HGPORT/?cmd=hello - capabilities: lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch stream bundle2=HG20%0Achangegroup%3D01%2C02%0Adigests%3Dmd5%2Csha1%2Csha512%0Alistkeys%0Aobsmarkers%3DV0%2CV1%0Apushkey%0Aremote-changegroup%3Dhttp%2Chttps unbundle=HG10GZ,HG10BZ,HG10UN httpheader=1024 _evoext_pushobsmarkers_0 _evoext_pullobsmarkers_0 _evoext_obshash_0 _evoext_obshash_1 _evoext_getbundle_obscommon + capabilities: * _evoext_pushobsmarkers_0 _evoext_pullobsmarkers_0 _evoext_obshash_0 _evoext_obshash_1 _evoext_getbundle_obscommon (glob) $ wget -q -O - http://localhost:$HGPORT/?cmd=capabilities - lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch stream bundle2=HG20%0Achangegroup%3D01%2C02%0Adigests%3Dmd5%2Csha1%2Csha512%0Alistkeys%0Aobsmarkers%3DV0%2CV1%0Apushkey%0Aremote-changegroup%3Dhttp%2Chttps unbundle=HG10GZ,HG10BZ,HG10UN httpheader=1024 _evoext_pushobsmarkers_0 _evoext_pullobsmarkers_0 _evoext_obshash_0 _evoext_obshash_1 _evoext_getbundle_obscommon (no-eol) + * _evoext_pushobsmarkers_0 _evoext_pullobsmarkers_0 _evoext_obshash_0 _evoext_obshash_1 _evoext_getbundle_obscommon (no-eol) (glob) $ wget -q -O - "http://localhost:$HGPORT/?cmd=listkeys&namespace=namespaces" | sort bookmarks @@ -135,13 +137,13 @@ obsolete phases $ wget -q -O - http://localhost:$HGPORT/?cmd=hello - capabilities: lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch stream bundle2=HG20%0Achangegroup%3D01%2C02%0Adigests%3Dmd5%2Csha1%2Csha512%0Alistkeys%0Aobsmarkers%3DV0%2CV1%0Apushkey%0Aremote-changegroup%3Dhttp%2Chttps unbundle=HG10GZ,HG10BZ,HG10UN httpheader=1024 _evoext_pushobsmarkers_0 _evoext_pullobsmarkers_0 _evoext_obshash_0 _evoext_obshash_1 _evoext_getbundle_obscommon + capabilities: * _evoext_pushobsmarkers_0 _evoext_pullobsmarkers_0 _evoext_obshash_0 _evoext_obshash_1 _evoext_getbundle_obscommon (glob) $ wget -q -O - http://localhost:$HGPORT/?cmd=capabilities - lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch stream bundle2=HG20%0Achangegroup%3D01%2C02%0Adigests%3Dmd5%2Csha1%2Csha512%0Alistkeys%0Aobsmarkers%3DV0%2CV1%0Apushkey%0Aremote-changegroup%3Dhttp%2Chttps unbundle=HG10GZ,HG10BZ,HG10UN httpheader=1024 _evoext_pushobsmarkers_0 _evoext_pullobsmarkers_0 _evoext_obshash_0 _evoext_obshash_1 _evoext_getbundle_obscommon (no-eol) + * _evoext_pushobsmarkers_0 _evoext_pullobsmarkers_0 _evoext_obshash_0 _evoext_obshash_1 _evoext_getbundle_obscommon (no-eol) (glob) $ echo '[__temporary__]' >> server/.hg/hgrc $ echo 'advertiseobsolete=False' >> server/.hg/hgrc - $ $TESTDIR/killdaemons.py + $ $TESTDIR/killdaemons.py $DAEMON_PIDS $ hg serve -R server -n test -p $HGPORT -d --pid-file=hg.pid -A access.log -E errors.log $ cat hg.pid >> $DAEMON_PIDS @@ -149,13 +151,13 @@ bookmarks namespaces phases - $ wget -q -O - http://localhost:$HGPORT/?cmd=hello - capabilities: lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch stream bundle2=HG20%0Achangegroup%3D01%2C02%0Adigests%3Dmd5%2Csha1%2Csha512%0Alistkeys%0Aobsmarkers%3DV0%2CV1%0Apushkey%0Aremote-changegroup%3Dhttp%2Chttps unbundle=HG10GZ,HG10BZ,HG10UN httpheader=1024 - $ wget -q -O - http://localhost:$HGPORT/?cmd=capabilities - lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch stream bundle2=HG20%0Achangegroup%3D01%2C02%0Adigests%3Dmd5%2Csha1%2Csha512%0Alistkeys%0Aobsmarkers%3DV0%2CV1%0Apushkey%0Aremote-changegroup%3Dhttp%2Chttps unbundle=HG10GZ,HG10BZ,HG10UN httpheader=1024 (no-eol) + $ wget -q -O - http://localhost:$HGPORT/?cmd=hello | grep _evoext_pushobsmarkers_0 + [1] + $ wget -q -O - http://localhost:$HGPORT/?cmd=capabilities | grep _evoext_pushobsmarkers_0 + [1] $ echo 'advertiseobsolete=True' >> server/.hg/hgrc - $ $TESTDIR/killdaemons.py + $ $TESTDIR/killdaemons.py $DAEMON_PIDS $ hg serve -R server -n test -p $HGPORT -d --pid-file=hg.pid -A access.log -E errors.log $ cat hg.pid >> $DAEMON_PIDS @@ -165,6 +167,6 @@ obsolete phases $ wget -q -O - http://localhost:$HGPORT/?cmd=hello - capabilities: lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch stream bundle2=HG20%0Achangegroup%3D01%2C02%0Adigests%3Dmd5%2Csha1%2Csha512%0Alistkeys%0Aobsmarkers%3DV0%2CV1%0Apushkey%0Aremote-changegroup%3Dhttp%2Chttps unbundle=HG10GZ,HG10BZ,HG10UN httpheader=1024 _evoext_pushobsmarkers_0 _evoext_pullobsmarkers_0 _evoext_obshash_0 _evoext_obshash_1 _evoext_getbundle_obscommon + capabilities: * _evoext_pushobsmarkers_0 _evoext_pullobsmarkers_0 _evoext_obshash_0 _evoext_obshash_1 _evoext_getbundle_obscommon (glob) $ wget -q -O - http://localhost:$HGPORT/?cmd=capabilities - lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch stream bundle2=HG20%0Achangegroup%3D01%2C02%0Adigests%3Dmd5%2Csha1%2Csha512%0Alistkeys%0Aobsmarkers%3DV0%2CV1%0Apushkey%0Aremote-changegroup%3Dhttp%2Chttps unbundle=HG10GZ,HG10BZ,HG10UN httpheader=1024 _evoext_pushobsmarkers_0 _evoext_pullobsmarkers_0 _evoext_obshash_0 _evoext_obshash_1 _evoext_getbundle_obscommon (no-eol) + * _evoext_pushobsmarkers_0 _evoext_pullobsmarkers_0 _evoext_obshash_0 _evoext_obshash_1 _evoext_getbundle_obscommon (no-eol) (glob)
--- a/tests/test-stabilize-order.t Tue Jun 23 15:32:15 2015 -0700 +++ b/tests/test-stabilize-order.t Tue Jun 23 15:32:47 2015 -0700 @@ -153,8 +153,8 @@ $ hg up 9 2 files updated, 0 files merged, 0 files removed, 0 files unresolved $ hg evolve -v - nothing to evolve here - (1 troubled changesets, do you want --any ?) + nothing to evolve on current working copy parent + (1 other unstable in the repository, do you want --any or --rev) [2] $ hg evolve --any -v move:[9] addc @@ -180,5 +180,68 @@ o 0:c471ef929e6a@default(draft) addroot $ hg evolve --any -v - no troubled changesets + no unstable changesets to evolve [1] + +Ambiguous evolution + $ echo a > k + $ hg add k + $ hg ci -m firstambiguous + $ hg up .^ + 0 files updated, 0 files merged, 1 files removed, 0 files unresolved + $ echo a > l + $ hg add l + $ hg ci -m secondambiguous + created new head + $ hg up .^ + 0 files updated, 0 files merged, 1 files removed, 0 files unresolved + $ hg commit --amend -m "newmessage" + 2 new unstable changesets + $ hg log -G + @ changeset: 15:49773ccde390 + | tag: tip + | parent: 11:036cf654e942 + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: newmessage + | + | o changeset: 14:a9892777b519 + | | parent: 12:e99ecf51c867 + | | user: test + | | date: Thu Jan 01 00:00:00 1970 +0000 + | | summary: secondambiguous + | | + | | o changeset: 13:0b6e26b2472d + | |/ user: test + | | date: Thu Jan 01 00:00:00 1970 +0000 + | | summary: firstambiguous + | | + | x changeset: 12:e99ecf51c867 + |/ user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: addc + | + o changeset: 11:036cf654e942 + | parent: 7:005fe5914f78 + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: addb + | + o changeset: 7:005fe5914f78 + | parent: 0:c471ef929e6a + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: adda + | + o changeset: 0:c471ef929e6a + user: test + date: Thu Jan 01 00:00:00 1970 +0000 + summary: addroot + + $ hg evolve + abort: multiple evolve candidates + (select one of *, * with --rev) (glob) + [255] + + +
--- a/tests/test-stabilize-result.t Tue Jun 23 15:32:15 2015 -0700 +++ b/tests/test-stabilize-result.t Tue Jun 23 15:32:47 2015 -0700 @@ -158,20 +158,20 @@ Stabilize! - $ hg evolve --any --dry-run + $ hg evolve --any --dry-run --bumped recreate:[12] newer a atop:[8] newer a hg rebase --rev (73b15c7566e9|d5c7ef82d003) --dest 66719795a494; (re) hg update 1cf0aacfd363; hg revert --all --rev (73b15c7566e9|d5c7ef82d003); (re) hg commit --msg "bumped update to %s" (no-eol) - $ hg evolve --any --confirm + $ hg evolve --any --confirm --bumped recreate:[12] newer a atop:[8] newer a perform evolve? [Ny] n abort: evolve aborted by user [255] - $ echo y | hg evolve --any --confirm --config ui.interactive=True + $ echo y | hg evolve --any --confirm --config ui.interactive=True --bumped recreate:[12] newer a atop:[8] newer a perform evolve? [Ny] y @@ -248,14 +248,14 @@ Stabilize it - $ hg evolve -qn --confirm + $ hg evolve -qn --confirm --divergent merge:[19] More addition with: [17] More addition base: [15] More addition perform evolve? [Ny] n abort: evolve aborted by user [255] - $ echo y | hg evolve -qn --confirm --config ui.interactive=True + $ echo y | hg evolve -qn --confirm --config ui.interactive=True --divergent merge:[19] More addition with: [17] More addition base: [15] More addition @@ -266,7 +266,7 @@ hg up -C 3932c176bbaa && hg revert --all --rev tip && hg commit -m "`hg log -r eacc9c8240fe --template={desc}`"; - $ hg evolve -v + $ hg evolve -v --divergent merge:[19] More addition with: [17] More addition base: [15] More addition @@ -306,6 +306,7 @@ branch: default commit: (clean) update: 2 new changesets, 2 branch heads (merge) + phases: 3 draft $ hg export . # HG changeset patch # User test @@ -343,14 +344,14 @@ $ hg phase 'divergent()' 21: draft 24: draft - $ hg evolve -qn + $ hg evolve -qn --divergent hg update -c 0b336205a5d0 && hg merge f344982e63c4 && hg commit -m "auto merge resolving conflict between 0b336205a5d0 and f344982e63c4"&& hg up -C 3932c176bbaa && hg revert --all --rev tip && hg commit -m "`hg log -r 0b336205a5d0 --template={desc}`"; - $ hg evolve + $ hg evolve --divergent merge:[24] More addition (2) with: [21] More addition base: [15] More addition
--- a/tests/test-tutorial.t Tue Jun 23 15:32:15 2015 -0700 +++ b/tests/test-tutorial.t Tue Jun 23 15:32:47 2015 -0700 @@ -224,7 +224,6 @@ adding manifests adding file changes added 1 changesets with 1 changes to 1 files (+1 heads) - pull obsolescence markers (run 'hg heads' to see heads, 'hg merge' to merge) I now have a new heads. Note that this remote head is immutable @@ -289,9 +288,9 @@ not fit well in my standard shopping list) $ hg prune . # "." is for working directory parent - 1 changesets pruned 1 files updated, 0 files merged, 0 files removed, 0 files unresolved working directory now at 41aff6a42b75 + 1 changesets pruned The silly changeset is gone. @@ -406,8 +405,7 @@ adding manifests adding file changes added 3 changesets with 3 changes to 1 files - pushing 6 obsolescence markers (* bytes) (glob) - 6 obsolescence markers added + 6 new obsolescence markers for simplicity sake we get the bathroom change in line again @@ -528,8 +526,7 @@ adding manifests adding file changes added 1 changesets with 1 changes to 1 files - pull obsolescence markers - 1 obsolescence markers added + 1 new obsolescence markers (run 'hg update' to get a working copy) $ hg log -G o 75954b8cd933 (public): bathroom stuff @@ -586,8 +583,7 @@ adding manifests adding file changes added 1 changesets with 1 changes to 1 files - pull obsolescence markers - 1 obsolescence markers added + 1 new obsolescence markers (run 'hg update' to get a working copy) $ hg log -G o 75954b8cd933 (draft): bathroom stuff @@ -647,8 +643,6 @@ adding manifests adding file changes added 1 changesets with 1 changes to 1 files (+1 heads) - pull obsolescence markers - 0 obsolescence markers added (run 'hg heads' to see heads, 'hg merge' to merge) 1 new unstable changesets @@ -738,8 +732,7 @@ adding manifests adding file changes added 2 changesets with 2 changes to 1 files (+1 heads) - pushing 10 obsolescence markers (* bytes) (glob) - 3 obsolescence markers added + 3 new obsolescence markers remote get a warning that current working directory is based on an obsolete changeset @@ -748,8 +741,6 @@ pulling from $TESTTMP/local (glob) searching for changes no changes found - pull obsolescence markers - 0 obsolescence markers added working directory parent is obsolete! now let's see where we are, and update to the successor @@ -780,8 +771,6 @@ adding manifests adding file changes added 1 changesets with 1 changes to 1 files - pull obsolescence markers - 0 obsolescence markers added (run 'hg update' to get a working copy) $ hg log -G o 99f039c5ec9e (draft): SPAM SPAM SPAM @@ -804,9 +793,9 @@ In the mean time I noticed you can't buy animals in a super market and I prune the animal changeset: $ hg prune ee942144f952 - 1 changesets pruned 1 files updated, 0 files merged, 0 files removed, 0 files unresolved working directory now at a44c85f957d3 + 1 changesets pruned 1 new unstable changesets
--- a/tests/test-uncommit.t Tue Jun 23 15:32:15 2015 -0700 +++ b/tests/test-uncommit.t Tue Jun 23 15:32:47 2015 -0700 @@ -138,7 +138,6 @@ $ hg branch foo marked working directory as branch foo - (branches are permanent and global, did you want a bookmark?) $ hg mv ff f $ hg mv h i $ hg rm j
--- a/tests/test-userguide.t Tue Jun 23 15:32:15 2015 -0700 +++ b/tests/test-userguide.t Tue Jun 23 15:32:47 2015 -0700 @@ -66,9 +66,9 @@ $ echo 'debug hack' >> file1.c $ hg commit -m 'debug hack' $ hg prune . - 1 changesets pruned 1 files updated, 0 files merged, 0 files removed, 0 files unresolved working directory now at 934359450037 + 1 changesets pruned $ hg parents --template '{rev}:{node|short} {desc|firstline}\n' 3:934359450037 implement feature Y $ hg --hidden shortlog -G -r 3: @@ -219,7 +219,7 @@ | o 18:1f33e68b18b9 draft useful work | - $ hg evolve -q --all + $ hg evolve -q --all --any $ hg --hidden shortlog -G -r 18:: @ 21:4393e5877437 draft more work | @@ -251,7 +251,7 @@ $ hg status M file2.c $ hg revert file2.c - $ hg evolve --all + $ hg evolve --all --any move:[23] fix bug 67 atop:[24] fix bug 53 working directory is now at 0d972d6888e6 @@ -300,7 +300,7 @@ |/ o 25:0d972d6888e6 draft fix bug 67 | - $ hg evolve --all + $ hg evolve --all --any move:[27] new feature atop:[28] fix a bug working directory is now at 166c1c368ab6
--- a/tests/test-wireproto-bundle1.t Tue Jun 23 15:32:15 2015 -0700 +++ b/tests/test-wireproto-bundle1.t Tue Jun 23 15:32:47 2015 -0700 @@ -50,7 +50,6 @@ adding manifests adding file changes added 2 changesets with 2 changes to 2 files - pull obsolescence markers (run 'hg update' to get a working copy) $ hg push -R ../other pushing to ssh://user@dummy/server @@ -70,8 +69,7 @@ remote: adding manifests remote: adding file changes remote: added 1 changesets with 1 changes to 1 files (+1 heads) - pushing 2 obsolescence markers (* bytes) (glob) - remote: 2 obsolescence markers added + remote: 2 new obsolescence markers $ hg push pushing to ssh://user@dummy/server searching for changes @@ -88,9 +86,8 @@ adding manifests adding file changes added 1 changesets with 1 changes to [12] files \(\+1 heads\) (re) - pull obsolescence markers - 2 obsolescence markers added - (run 'hg heads' to see heads) + 2 new obsolescence markers + (run 'hg heads' to see heads, 'hg merge' to merge) $ hg -R ../other pull pulling from ssh://user@dummy/server searching for changes