# HG changeset patch # User Pierre-Yves David # Date 1548171962 18000 # Node ID a71f2271ed767c37d1bfe29105be803b4948fd4c # Parent 75db6a9d0b540f0af61c0957e4a83c87981854e3# Parent cb39b767ad3cae1de8392a4cebeb0a92eb89d996 branching: merge with future version diff -r 75db6a9d0b54 -r a71f2271ed76 CHANGELOG --- a/CHANGELOG Tue Jan 22 10:43:44 2019 -0500 +++ b/CHANGELOG Tue Jan 22 10:46:02 2019 -0500 @@ -1,6 +1,29 @@ Changelog ========= +8.4.0 - in progress +------------------- + + * split: improve and update the user prompt (BC) + * split: make it possible to drop change during a split + * split: no longer accept revision with --rev (BC) + * split: accept file patterns + * split: support for non interactive splits + * evolve: avoid potential crash when stabilizing orphan merges + * evolve: pick right destination in split+prune cases issue5686 (4.9 only) + * evolve: prioritize --rev/--any/--all option over obsolete working directory + * fold: concatenate commit message in revision order + * push: have `--publish` overrule the `auto-publish` config + * next: evolve aspiring children by default (use --no-evolve to skip) + * next: pick lower part of a split as destination + * compat: drop compatibility with Mercurial 4.3 + * compat: add compatibility with Mercurial 4.9 + * topics: improve the message around topic changing + * stack: introduce a --children flag (see help for details) + * topic: make --age compatible with the usual other display for `hg topic` + * stack: support for '#stack[idx]' absolute indexing in revset (4.9+ only) + * topic: support for '#topic[idx]' relative indexing in revset (4.9+ only) + 8.3.3 -- 2017-12-24 ------------------- diff -r 75db6a9d0b54 -r a71f2271ed76 hgext3rd/evolve/__init__.py --- a/hgext3rd/evolve/__init__.py Tue Jan 22 10:43:44 2019 -0500 +++ b/hgext3rd/evolve/__init__.py Tue Jan 22 10:46:02 2019 -0500 @@ -2,6 +2,7 @@ # Logilab SA # Pierre-Yves David # Patrick Mezard +# Octobus # # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. @@ -32,7 +33,7 @@ backported to older version of Mercurial by this extension. Some older experimental protocol are also supported for a longer time in the extensions to help people transitioning. (The extensions is currently compatible down to -Mercurial version 4.3). +Mercurial version 4.4). New Config:: @@ -285,7 +286,6 @@ context, dirstate, error, - extensions, help, hg, lock as lockmod, @@ -364,20 +364,18 @@ eh.merge(compat.eh) eh.merge(cmdrewrite.eh) eh.merge(rewind.eh) -uisetup = eh.final_uisetup -extsetup = eh.final_extsetup -reposetup = eh.final_reposetup +uisetup = eh.finaluisetup +extsetup = eh.finalextsetup +reposetup = eh.finalreposetup cmdtable = eh.cmdtable configtable = eh.configtable +revsetpredicate = eh.revsetpredicate +templatekeyword = eh.templatekeyword # Configuration -eh.configitem('experimental', 'evolutioncommands') -eh.configitem('experimental', 'evolution.allnewcommands') -eh.configitem('experimental', 'prunestrip') - -# hack around because we need an actual default there -if configtable: - configtable['experimental']['evolution.allnewcommands'].default = None +eh.configitem('experimental', 'evolutioncommands', []) +eh.configitem('experimental', 'evolution.allnewcommands', None) +eh.configitem('experimental', 'prunestrip', False) # pre hg 4.0 compat @@ -435,7 +433,7 @@ # 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. - evolvecommands = ui.configlist('experimental', 'evolutioncommands', []) + evolvecommands = ui.configlist('experimental', 'evolutioncommands') evolveopts = ui.configlist('experimental', 'evolution') if evolveopts and (commandopt not in evolveopts and 'all' not in evolveopts): @@ -516,7 +514,7 @@ ### Troubled revset symbol -@eh.revset('troubled()') +@eh.revsetpredicate('troubled()') def revsettroubled(repo, subset, x): """Changesets with troubles. """ @@ -624,7 +622,7 @@ ### XXX I'm not sure this revset is useful -@eh.revset('suspended()') +@eh.revsetpredicate('suspended()') def revsetsuspended(repo, subset, x): """Obsolete changesets with non-obsolete descendants. """ @@ -634,7 +632,7 @@ return subset & suspended -@eh.revset('precursors(set)') +@eh.revsetpredicate('precursors(set)') def revsetprecursors(repo, subset, x): """Immediate precursors of changesets in set. """ @@ -644,7 +642,7 @@ return subset & s -@eh.revset('allprecursors(set)') +@eh.revsetpredicate('allprecursors(set)') def revsetallprecursors(repo, subset, x): """Transitive precursors of changesets in set. """ @@ -654,7 +652,7 @@ return subset & s -@eh.revset('successors(set)') +@eh.revsetpredicate('successors(set)') def revsetsuccessors(repo, subset, x): """Immediate successors of changesets in set. """ @@ -663,7 +661,7 @@ s.sort() return subset & s -@eh.revset('allsuccessors(set)') +@eh.revsetpredicate('allsuccessors(set)') def revsetallsuccessors(repo, subset, x): """Transitive successors of changesets in set. """ @@ -781,45 +779,6 @@ _warnobsoletewc(ui, repo) return res -# XXX this could wrap transaction code -# XXX (but this is a bit a layer violation) -@eh.wrapcommand("commit") -@eh.wrapcommand("import") -@eh.wrapcommand("push") -@eh.wrapcommand("pull") -@eh.wrapcommand("graft") -@eh.wrapcommand("phase") -@eh.wrapcommand("unbundle") -def warnobserrors(orig, ui, repo, *args, **kwargs): - """display warning is the command resulted in more instable changeset""" - # hg < 4.4 does not have the feature built in. bail out otherwise. - if util.safehasattr(scmutil, '_reportstroubledchangesets'): - return orig(ui, repo, *args, **kwargs) - - # part of the troubled stuff may be filtered (stash ?) - # This needs a better implementation but will probably wait for core. - filtered = repo.changelog.filteredrevs - priorunstables = len(set(getrevs(repo, 'orphan')) - filtered) - priorbumpeds = len(set(getrevs(repo, 'phasedivergent')) - filtered) - priordivergents = len(set(getrevs(repo, 'contentdivergent')) - filtered) - ret = orig(ui, repo, *args, **kwargs) - filtered = repo.changelog.filteredrevs - newunstables = \ - len(set(getrevs(repo, 'orphan')) - filtered) - priorunstables - newbumpeds = \ - len(set(getrevs(repo, 'phasedivergent')) - filtered) - priorbumpeds - newdivergents = \ - len(set(getrevs(repo, 'contentdivergent')) - filtered) - priordivergents - - base_msg = _('%i new %s changesets\n') - if newunstables > 0: - ui.warn(base_msg % (newunstables, compat.TROUBLES['ORPHAN'])) - if newbumpeds > 0: - ui.warn(base_msg % (newbumpeds, compat.TROUBLES['PHASEDIVERGENT'])) - if newdivergents > 0: - ui.warn(base_msg % (newdivergents, compat.TROUBLES['CONTENTDIVERGENT'])) - return ret - @eh.wrapfunction(mercurial.exchange, 'push') def push(orig, repo, *args, **opts): """Add a hint for "hg evolve" when troubles make push fails @@ -845,28 +804,6 @@ def obssummarysetup(ui): cmdutil.summaryhooks.add('evolve', summaryhook) - -##################################################################### -### Core Other extension compat ### -##################################################################### - - -@eh.extsetup -def _rebasewrapping(ui): - # warning about more obsolete - try: - rebase = extensions.find('rebase') - if rebase: - extensions.wrapcommand(rebase.cmdtable, 'rebase', warnobserrors) - except KeyError: - pass # rebase not found - try: - histedit = extensions.find('histedit') - if histedit: - extensions.wrapcommand(histedit.cmdtable, 'histedit', warnobserrors) - except KeyError: - pass # histedit not found - ##################################################################### ### Old Evolve extension content ### ##################################################################### @@ -1071,11 +1008,7 @@ if ui.config('commands', 'update.check') == 'noconflict': pass else: - try: - cmdutil.bailifchanged(repo) - except error.Abort as exc: - exc.hint = _('do you want --merge?') - raise + cmdutil.bailifchanged(repo, hint=_('do you want --merge?')) topic = not opts.get("no_topic", False) hastopic = bool(_getcurrenttopic(repo)) @@ -1109,7 +1042,7 @@ [('B', 'move-bookmark', False, _('move active bookmark after update')), ('m', 'merge', False, _('bring uncommitted change along')), - ('', 'evolve', False, _('evolve the next changeset if necessary')), + ('', 'evolve', True, _('evolve the next changeset if necessary')), ('', 'no-topic', False, _('ignore topic and move topologically')), ('n', 'dry-run', False, _('do not perform actions, just print what would be done'))], @@ -1118,7 +1051,8 @@ def cmdnext(ui, repo, **opts): """update to next child revision - Use the ``--evolve`` flag to evolve unstable children on demand. + If necessary, evolve the next changeset. Use --no-evolve to disable this + behavior. Displays the summary line of the destination for clarity. """ @@ -1132,21 +1066,6 @@ if len(wparents) != 1: raise error.Abort(_('merge in progress')) - # check for dirty wdir if --evolve is passed - if opts['evolve']: - cmdutil.bailifchanged(repo) - - if not opts['merge']: - # we only skip the check if noconflict is set - if ui.config('commands', 'update.check') == 'noconflict': - pass - else: - try: - cmdutil.bailifchanged(repo) - except error.Abort as exc: - exc.hint = _('do you want --merge?') - raise - children = [ctx for ctx in wparents[0].children() if not ctx.obsolete()] topic = _getcurrenttopic(repo) filtered = set() @@ -1155,7 +1074,41 @@ filtered = set(ctx for ctx in children if ctx.topic() != topic) children = [ctx for ctx in children if ctx not in filtered] template = utility.stacktemplate + opts['stacktemplate'] = True displayer = compat.changesetdisplayer(ui, repo, {'template': template}) + + # check if we need to evolve while updating to the next child revision + needevolve = False + aspchildren = evolvecmd._aspiringchildren(repo, [repo['.'].rev()]) + if topic: + filtered.update(repo[c] for c in aspchildren + if repo[c].topic() != topic) + aspchildren = [ctx for ctx in aspchildren if ctx not in filtered] + + # To catch and prevent the case when `next` would get confused by split, + # lets filter those aspiring children which can be stablized on one of + # the aspiring children itself. + aspirants = set(aspchildren) + for aspchild in aspchildren: + possdests = evolvecmd._possibledestination(repo, aspchild) + if possdests & aspirants: + filtered.add(aspchild) + aspchildren = [ctx for ctx in aspchildren if ctx not in filtered] + if aspchildren: + needevolve = True + + # check if working directory is clean before we evolve the next cset + if needevolve and opts['evolve']: + hint = _('use `hg amend`, `hg revert` or `hg shelve`') + cmdutil.bailifchanged(repo, hint=hint) + + if not (opts['merge'] or (needevolve and opts['evolve'])): + # we only skip the check if noconflict is set + if ui.config('commands', 'update.check') == 'noconflict': + pass + else: + cmdutil.bailifchanged(repo, hint=_('do you want --merge?')) + if len(children) == 1: c = children[0] return _updatetonext(ui, repo, c, displayer, opts) @@ -1172,11 +1125,6 @@ else: return _updatetonext(ui, repo, repo[choosedrev], displayer, opts) else: - aspchildren = evolvecmd._aspiringchildren(repo, [repo['.'].rev()]) - if topic: - filtered.update(repo[c] for c in aspchildren - if repo[c].topic() != topic) - aspchildren = [ctx for ctx in aspchildren if ctx not in filtered] if not opts['evolve'] or not aspchildren: if filtered: ui.warn(_('no children on topic "%s"\n') % topic) @@ -1188,7 +1136,7 @@ 'do you want --evolve?)\n') ui.warn(msg % len(aspchildren)) return 1 - elif 1 < len(aspchildren): + elif len(aspchildren) > 1: cheader = _("ambiguous next (unstable) changeset, choose one to" " evolve and update:") choosedrev = utility.revselectionprompt(ui, repo, @@ -1214,7 +1162,8 @@ 'bookmarkchanges': []}) result = evolvecmd._solveone(ui, repo, repo[aspchildren], evolvestate, opts.get('dry_run'), False, - lambda: None, category='orphan') + lambda: None, category='orphan', + stacktmplt=opts.get('stacktemplate', False)) # making sure a next commit is formed if result[0] and result[1]: ui.status(_('working directory now at %s\n') @@ -1303,7 +1252,7 @@ "backup bundle")), ]) def stripwrapper(orig, ui, repo, *revs, **kwargs): - if (not ui.configbool('experimental', 'prunestrip', False) + if (not ui.configbool('experimental', 'prunestrip') or kwargs.get('bundle', False)): return orig(ui, repo, *revs, **kwargs) @@ -1325,14 +1274,6 @@ @eh.extsetup def oldevolveextsetup(ui): - for cmd in ['prune', 'uncommit', 'touch', 'fold']: - 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', [], _("make commit obsolete this revision (DEPRECATED)"))) diff -r 75db6a9d0b54 -r a71f2271ed76 hgext3rd/evolve/cmdrewrite.py --- a/hgext3rd/evolve/cmdrewrite.py Tue Jan 22 10:43:44 2019 -0500 +++ b/hgext3rd/evolve/cmdrewrite.py Tue Jan 22 10:46:02 2019 -0500 @@ -105,7 +105,7 @@ ('', 'close-branch', None, _('mark a branch as closed, hiding it from the branch list')), ('s', 'secret', None, _('use the secret phase for committing')), - ('n', 'note', '', _('store a note on amend')), + ('n', 'note', '', _('store a note on amend'), _('TEXT')), ] + walkopts + commitopts + commitopts2 + commitopts3 + interactiveopt, _('[OPTION]... [FILE]...'), helpbasic=True) @@ -462,9 +462,9 @@ 'uncommit', [('a', 'all', None, _('uncommit all changes when no arguments given')), ('i', 'interactive', False, _('interactive mode to uncommit (EXPERIMENTAL)')), - ('r', 'rev', '', _('revert commit content to REV instead')), + ('r', 'rev', '', _('revert commit content to REV instead'), _('REV')), ('', 'revert', False, _('discard working directory changes after uncommit')), - ('n', 'note', '', _('store a note on uncommit')), + ('n', 'note', '', _('store a note on uncommit'), _('TEXT')), ] + commands.walkopts + commitopts + commitopts2 + commitopts3, _('[OPTION]... [NAME]')) def uncommit(ui, repo, *pats, **opts): @@ -662,10 +662,10 @@ @eh.command( 'fold|squash', - [('r', 'rev', [], _("revision to fold")), + [('r', 'rev', [], _("revision to fold"), _('REV')), ('', 'exact', None, _("only fold specified revisions")), ('', 'from', None, _("fold revisions linearly to working copy parent")), - ('n', 'note', '', _('store a note on fold')), + ('n', 'note', '', _('store a note on fold'), _('TEXT')), ] + commitopts + commitopts2 + commitopts3, _('hg fold [OPTION]... [-r] REV'), helpbasic=True) @@ -740,6 +740,10 @@ ui.write_err(_('single revision specified, nothing to fold\n')) return 1 + # Sort so combined commit message of `hg fold --exact -r . -r .^` is + # in topological order. + revs.sort() + wlock = lock = None try: wlock = repo.wlock() @@ -791,9 +795,9 @@ @eh.command( 'metaedit', - [('r', 'rev', [], _("revision to edit")), + [('r', 'rev', [], _("revision to edit"), _('REV')), ('', 'fold', None, _("also fold specified revisions into one")), - ('n', 'note', '', _('store a note on metaedit')), + ('n', 'note', '', _('store a note on metaedit'), _('TEXT')), ] + commitopts + commitopts2 + commitopts3, _('hg metaedit [OPTION]... [-r] [REV]')) def metaedit(ui, repo, *revs, **opts): @@ -941,10 +945,10 @@ @eh.command( 'prune|obsolete', [('n', 'new', [], _("successor changeset (DEPRECATED)")), - ('s', 'succ', [], _("successor changeset")), - ('r', 'rev', [], _("revisions to prune")), + ('s', 'succ', [], _("successor changeset"), _('REV')), + ('r', 'rev', [], _("revisions to prune"), _('REV')), ('k', 'keep', None, _("does not modify working copy during prune")), - ('n', 'note', '', _('store a note on prune')), + ('n', 'note', '', _('store a note on prune'), _('TEXT')), ('', 'pair', False, _("record a pairing, such as a rebase or divergence resolution " "(pairing multiple precursors to multiple successors)")), ('', 'biject', False, _("alias to --pair (DEPRECATED)")), @@ -953,7 +957,7 @@ ('', 'split', False, _("record a split (on precursor, multiple successors)")), ('B', 'bookmark', [], _("remove revs only reachable from given" - " bookmark"))] + metadataopts, + " bookmark"), _('BOOKMARK'))] + metadataopts, _('[OPTION] [-r] REV...'), helpbasic=True) # XXX -U --noupdate option to prevent wc update and or bookmarks update ? @@ -1132,29 +1136,39 @@ @eh.command( 'split', - [('r', 'rev', [], _("revision to split")), - ('n', 'note', '', _("store a note on split")), + [('i', 'interactive', True, _('use interactive mode')), + ('r', 'rev', [], _("revision to split"), _('REV')), + ('n', 'note', '', _("store a note on split"), _('TEXT')), ] + commitopts + commitopts2 + commitopts3, - _('hg split [OPTION]... [-r] REV'), + _('hg split [OPTION] [-r REV] [FILES]'), helpbasic=True) -def cmdsplit(ui, repo, *revs, **opts): +def cmdsplit(ui, repo, *pats, **opts): """split a changeset into smaller changesets By default, split the current revision by prompting for all its hunks to be redistributed into new changesets. Use --rev to split a given changeset instead. + + If file patterns are specified only files matching these patterns will be + considered to be split in earlier changesets. The files that doesn't match + will be gathered in the last changeset. """ _checknotesize(ui, opts) _resolveoptions(ui, opts) tr = wlock = lock = None newcommits = [] + iselect = opts.pop('interactive') - revarg = (list(revs) + opts.get('rev')) or ['.'] - if len(revarg) != 1: - msg = _("more than one revset is given") - hnt = _("use either `hg split ` or `hg split --rev `, not both") - raise error.Abort(msg, hint=hnt) + revs = opts.get('rev') or '.' + if not revs: + revarg = '.' + elif len(revs) == 1: + revarg = revs[0] + else: + # XXX --rev often accept multiple value, it seems safer to explicitly + # complains here instead of just taking the last value. + raise error.Abort(_('more than one revset is given')) # Save the current branch to restore it in the end savedbranch = repo.dirstate.branch() @@ -1162,7 +1176,7 @@ try: wlock = repo.wlock() lock = repo.lock() - ctx = scmutil.revsingle(repo, revarg[0]) + ctx = scmutil.revsingle(repo, revarg) rev = ctx.rev() cmdutil.bailifchanged(repo) rewriteutil.precheck(repo, [rev], action='split') @@ -1180,8 +1194,8 @@ # Prepare the working directory rewriteutil.presplitupdate(repo, ui, prev, ctx) - def haschanges(): - modified, added, removed, deleted = repo.status()[:4] + def haschanges(matcher=None): + modified, added, removed, deleted = repo.status(match=matcher)[:4] return modified or added or removed or deleted msg = ("HG: This is the original pre-split commit message. " "Edit it as appropriate.\n\n") @@ -1195,21 +1209,79 @@ # XXX-TODO: Find a way to set the branch without altering the dirstate repo.dirstate.setbranch(ctx.branch()) + if pats: + # refresh the wctx used for the matcher + matcher = scmutil.match(repo[None], pats) + else: + matcher = scmutil.matchall(repo) + while haschanges(): - pats = () - cmdutil.dorecord(ui, repo, commands.commit, 'commit', False, - cmdutil.recordfilter, *pats, **opts) - # TODO: Does no seem like the best way to do this - # We should make dorecord return the newly created commit - newcommits.append(repo['.']) - if haschanges(): - if ui.prompt('Done splitting? [yN]', default='n') == 'y': + + if haschanges(matcher): + if iselect: + cmdutil.dorecord(ui, repo, commands.commit, 'commit', False, + cmdutil.recordfilter, *pats, **opts) + # TODO: Does no seem like the best way to do this + # We should make dorecord return the newly created commit + newcommits.append(repo['.']) + elif not pats: + msg = _("no files of directories specified") + hint = _("do you want --interactive") + raise error.Abort(msg, hint=hint) + else: + commands.commit(ui, repo, *pats, **opts) + newcommits.append(repo['.']) + if pats: + # refresh the wctx used for the matcher + matcher = scmutil.match(repo[None], pats) + else: + matcher = scmutil.matchall(repo) + + if haschanges(matcher): + nextaction = None + while nextaction is None: + nextaction = ui.prompt('continue splitting? [Ycdq?]', default='y') + if nextaction == 'c': + commands.commit(ui, repo, **opts) + newcommits.append(repo['.']) + break + elif nextaction == 'q': + raise error.Abort(_('user quit')) + elif nextaction == 'd': + # TODO: We should offer a way for the user to confirm + # what is the remaining changes, either via a separate + # diff action or by showing the remaining and + # prompting for confirmation + ui.status(_('discarding remaining changes\n')) + target = newcommits[0] + if pats: + status = repo.status(match=matcher)[:4] + dirty = set() + for i in status: + dirty.update(i) + dirty = sorted(dirty) + cmdutil.revert(ui, repo, repo[target], + (target, node.nullid), *dirty) + else: + cmdutil.revert(ui, repo, repo[target], + (target, node.nullid), all=True) + elif nextaction == '?': + nextaction = None + ui.write(_("y - yes, continue selection\n")) + ui.write(_("c - commit, select all remaining changes\n")) + ui.write(_("d - discard, discard remaining changes\n")) + ui.write(_("q - quit, abort the split\n")) + ui.write(_("? - ?, display help\n")) + else: + continue + break # propagate the previous break + else: + ui.status(_("no more change to split\n")) + if haschanges(): + # XXX: Should we show a message for informing the user + # that we create another commit with remaining changes? commands.commit(ui, repo, **opts) newcommits.append(repo['.']) - break - else: - ui.status(_("no more change to split\n")) - if newcommits: tip = repo[newcommits[-1]] bmupdate(tip.node()) @@ -1229,8 +1301,8 @@ @eh.command( 'touch', - [('r', 'rev', [], 'revision to update'), - ('n', 'note', '', _('store a note on touch')), + [('r', 'rev', [], _('revision to update'), _('REV')), + ('n', 'note', '', _('store a note on touch'), _('TEXT')), ('D', 'duplicate', False, 'do not mark the new revision as successor of the old one'), ('A', 'allowdivergence', False, @@ -1322,7 +1394,7 @@ @eh.command( 'pick|grab', - [('r', 'rev', '', 'revision to pick'), + [('r', 'rev', '', _('revision to pick'), _('REV')), ('c', 'continue', False, 'continue interrupted pick'), ('a', 'abort', False, 'abort interrupted pick'), ], diff -r 75db6a9d0b54 -r a71f2271ed76 hgext3rd/evolve/compat.py --- a/hgext3rd/evolve/compat.py Tue Jan 22 10:43:44 2019 -0500 +++ b/hgext3rd/evolve/compat.py Tue Jan 22 10:46:02 2019 -0500 @@ -19,6 +19,7 @@ revset, scmutil, util, + ui as uimod, vfs as vfsmod, ) from mercurial.hgweb import hgweb_mod @@ -77,20 +78,31 @@ context.basectx.isunstable = context.basectx.troubled if not util.safehasattr(revset, 'orphan'): - @eh.revset('orphan') + @eh.revsetpredicate('orphan') def oprhanrevset(*args, **kwargs): return revset.unstable(*args, **kwargs) if not util.safehasattr(revset, 'contentdivergent'): - @eh.revset('contentdivergent') + @eh.revsetpredicate('contentdivergent') def contentdivergentrevset(*args, **kwargs): return revset.divergent(*args, **kwargs) if not util.safehasattr(revset, 'phasedivergent'): - @eh.revset('phasedivergent') + @eh.revsetpredicate('phasedivergent') def phasedivergentrevset(*args, **kwargs): return revset.bumped(*args, **kwargs) +if util.safehasattr(uimod.ui, 'makeprogress'): + def progress(ui, topic, pos, item="", unit="", total=None): + progress = ui.makeprogress(topic, unit, total) + if pos is not None: + progress.update(pos, item=item) + else: + progress.complete() +else: + def progress(ui, topic, pos, item="", unit="", total=None): + ui.progress(topic, pos, item="", unit="", total=None) + if not util.safehasattr(context.basectx, 'instabilities'): def instabilities(self): """return the list of instabilities affecting this changeset. @@ -466,256 +478,8 @@ return copy, movewithdir, diverge, renamedelete, dirmove -# code imported from Mercurial core at 4.3 + patch -def fixoldmergecopies(repo, c1, c2, base): - - from mercurial import pathutil - - # avoid silly behavior for update from empty dir - if not c1 or not c2 or c1 == c2: - return {}, {}, {}, {}, {} - - # avoid silly behavior for parent -> working dir - if c2.node() is None and c1.node() == repo.dirstate.p1(): - return repo.dirstate.copies(), {}, {}, {}, {} - - # Copy trace disabling is explicitly below the node == p1 logic above - # because the logic above is required for a simple copy to be kept across a - # rebase. - if repo.ui.configbool('experimental', 'disablecopytrace'): - return {}, {}, {}, {}, {} - - # In certain scenarios (e.g. graft, update or rebase), base can be - # overridden We still need to know a real common ancestor in this case We - # can't just compute _c1.ancestor(_c2) and compare it to ca, because there - # can be multiple common ancestors, e.g. in case of bidmerge. Because our - # caller may not know if the revision passed in lieu of the CA is a genuine - # common ancestor or not without explicitly checking it, it's better to - # determine that here. - # - # base.descendant(wc) and base.descendant(base) are False, work around that - _c1 = c1.p1() if c1.rev() is None else c1 - _c2 = c2.p1() if c2.rev() is None else c2 - # an endpoint is "dirty" if it isn't a descendant of the merge base - # if we have a dirty endpoint, we need to trigger graft logic, and also - # keep track of which endpoint is dirty - dirtyc1 = not (base == _c1 or base.descendant(_c1)) - dirtyc2 = not (base == _c2 or base.descendant(_c2)) - graft = dirtyc1 or dirtyc2 - tca = base - if graft: - tca = _c1.ancestor(_c2) - - limit = copies._findlimit(repo, c1.rev(), c2.rev()) - if limit is None: - # no common ancestor, no copies - return {}, {}, {}, {}, {} - repo.ui.debug(" searching for copies back to rev %d\n" % limit) - - m1 = c1.manifest() - m2 = c2.manifest() - mb = base.manifest() - - # gather data from _checkcopies: - # - diverge = record all diverges in this dict - # - copy = record all non-divergent copies in this dict - # - fullcopy = record all copies in this dict - # - incomplete = record non-divergent partial copies here - # - incompletediverge = record divergent partial copies here - diverge = {} # divergence data is shared - incompletediverge = {} - data1 = {'copy': {}, - 'fullcopy': {}, - 'incomplete': {}, - 'diverge': diverge, - 'incompletediverge': incompletediverge, - } - data2 = {'copy': {}, - 'fullcopy': {}, - 'incomplete': {}, - 'diverge': diverge, - 'incompletediverge': incompletediverge, - } - - # find interesting file sets from manifests - addedinm1 = m1.filesnotin(mb) - addedinm2 = m2.filesnotin(mb) - bothnew = sorted(addedinm1 & addedinm2) - if tca == base: - # unmatched file from base - u1r, u2r = copies._computenonoverlap(repo, c1, c2, addedinm1, addedinm2) - u1u, u2u = u1r, u2r - else: - # unmatched file from base (DAG rotation in the graft case) - u1r, u2r = copies._computenonoverlap(repo, c1, c2, addedinm1, addedinm2, - baselabel='base') - # unmatched file from topological common ancestors (no DAG rotation) - # need to recompute this for directory move handling when grafting - mta = tca.manifest() - u1u, u2u = copies._computenonoverlap(repo, c1, c2, m1.filesnotin(mta), - m2.filesnotin(mta), - baselabel='topological common ancestor') - - for f in u1u: - copies._checkcopies(c1, c2, f, base, tca, dirtyc1, limit, data1) - - for f in u2u: - copies._checkcopies(c2, c1, f, base, tca, dirtyc2, limit, data2) - - copy = dict(data1['copy']) - copy.update(data2['copy']) - fullcopy = dict(data1['fullcopy']) - fullcopy.update(data2['fullcopy']) - - if dirtyc1: - copies._combinecopies(data2['incomplete'], data1['incomplete'], copy, diverge, - incompletediverge) - else: - copies._combinecopies(data1['incomplete'], data2['incomplete'], copy, diverge, - incompletediverge) - - renamedelete = {} - renamedeleteset = set() - divergeset = set() - for of, fl in diverge.items(): - if len(fl) == 1 or of in c1 or of in c2: - del diverge[of] # not actually divergent, or not a rename - if of not in c1 and of not in c2: - # renamed on one side, deleted on the other side, but filter - # out files that have been renamed and then deleted - renamedelete[of] = [f for f in fl if f in c1 or f in c2] - renamedeleteset.update(fl) # reverse map for below - else: - divergeset.update(fl) # reverse map for below - - if bothnew: - repo.ui.debug(" unmatched files new in both:\n %s\n" - % "\n ".join(bothnew)) - bothdiverge = {} - bothincompletediverge = {} - remainder = {} - both1 = {'copy': {}, - 'fullcopy': {}, - 'incomplete': {}, - 'diverge': bothdiverge, - 'incompletediverge': bothincompletediverge - } - both2 = {'copy': {}, - 'fullcopy': {}, - 'incomplete': {}, - 'diverge': bothdiverge, - 'incompletediverge': bothincompletediverge - } - for f in bothnew: - copies._checkcopies(c1, c2, f, base, tca, dirtyc1, limit, both1) - copies._checkcopies(c2, c1, f, base, tca, dirtyc2, limit, both2) - if dirtyc1 and dirtyc2: - pass - elif dirtyc1: - # incomplete copies may only be found on the "dirty" side for bothnew - assert not both2['incomplete'] - remainder = copies._combinecopies({}, both1['incomplete'], copy, bothdiverge, - bothincompletediverge) - elif dirtyc2: - assert not both1['incomplete'] - remainder = copies._combinecopies({}, both2['incomplete'], copy, bothdiverge, - bothincompletediverge) - else: - # incomplete copies and divergences can't happen outside grafts - assert not both1['incomplete'] - assert not both2['incomplete'] - assert not bothincompletediverge - for f in remainder: - assert f not in bothdiverge - ic = remainder[f] - if ic[0] in (m1 if dirtyc1 else m2): - # backed-out rename on one side, but watch out for deleted files - bothdiverge[f] = ic - for of, fl in bothdiverge.items(): - if len(fl) == 2 and fl[0] == fl[1]: - copy[fl[0]] = of # not actually divergent, just matching renames - - if fullcopy and repo.ui.debugflag: - repo.ui.debug(" all copies found (* = to merge, ! = divergent, " - "% = renamed and deleted):\n") - for f in sorted(fullcopy): - note = "" - if f in copy: - note += "*" - if f in divergeset: - note += "!" - if f in renamedeleteset: - note += "%" - repo.ui.debug(" src: '%s' -> dst: '%s' %s\n" % (fullcopy[f], f, - note)) - del divergeset - - if not fullcopy: - return copy, {}, diverge, renamedelete, {} - - repo.ui.debug(" checking for directory renames\n") - - # generate a directory move map - d1, d2 = c1.dirs(), c2.dirs() - # Hack for adding '', which is not otherwise added, to d1 and d2 - d1.addpath('/') - d2.addpath('/') - invalid = set() - dirmove = {} - - # examine each file copy for a potential directory move, which is - # when all the files in a directory are moved to a new directory - for dst, src in fullcopy.iteritems(): - dsrc, ddst = pathutil.dirname(src), pathutil.dirname(dst) - if dsrc in invalid: - # already seen to be uninteresting - continue - elif dsrc in d1 and ddst in d1: - # directory wasn't entirely moved locally - invalid.add(dsrc + "/") - elif dsrc in d2 and ddst in d2: - # directory wasn't entirely moved remotely - invalid.add(dsrc + "/") - elif dsrc + "/" in dirmove and dirmove[dsrc + "/"] != ddst + "/": - # files from the same directory moved to two different places - invalid.add(dsrc + "/") - else: - # looks good so far - dirmove[dsrc + "/"] = ddst + "/" - - for i in invalid: - if i in dirmove: - del dirmove[i] - del d1, d2, invalid - - if not dirmove: - return copy, {}, diverge, renamedelete, {} - - for d in dirmove: - repo.ui.debug(" discovered dir src: '%s' -> dst: '%s'\n" % - (d, dirmove[d])) - - movewithdir = {} - # check unaccounted nonoverlapping files against directory moves - for f in u1r + u2r: - if f not in fullcopy: - for d in dirmove: - if f.startswith(d): - # new file added in a directory that was moved, move it - df = dirmove[d] + f[len(d):] - if df not in copy: - movewithdir[f] = df - repo.ui.debug((" pending file src: '%s' -> " - "dst: '%s'\n") % (f, df)) - break - - return copy, movewithdir, diverge, renamedelete, dirmove - if util.safehasattr(copies, '_fullcopytracing'): copies._fullcopytracing = fixedcopytracing -elif util.safehasattr(copies, 'mergecopies'): - # compat fix for hg <= 4.3 - copies.mergecopies = fixoldmergecopies if not util.safehasattr(obsutil, "_succs"): class _succs(list): diff -r 75db6a9d0b54 -r a71f2271ed76 hgext3rd/evolve/depthcache.py --- a/hgext3rd/evolve/depthcache.py Tue Jan 22 10:43:44 2019 -0500 +++ b/hgext3rd/evolve/depthcache.py Tue Jan 22 10:46:02 2019 -0500 @@ -113,8 +113,8 @@ total = len(data) def progress(pos, rev): - repo.ui.progress('updating depth cache', - pos, 'rev %s' % rev, unit='revision', total=total) + compat.progress(repo.ui, 'updating depth cache', + pos, 'rev %s' % rev, unit='revision', total=total) progress(0, '') for idx, rev in enumerate(data, 1): assert rev == len(self._data), (rev, len(self._data)) diff -r 75db6a9d0b54 -r a71f2271ed76 hgext3rd/evolve/evolvecmd.py --- a/hgext3rd/evolve/evolvecmd.py Tue Jan 22 10:43:44 2019 -0500 +++ b/hgext3rd/evolve/evolvecmd.py Tue Jan 22 10:46:02 2019 -0500 @@ -716,10 +716,7 @@ " content-divergent changesets.\nHG: Resolve conflicts" " in commit messages to continue.\n\n") - if 5 <= len(ui.edit.im_func.func_defaults): # <= hg-4.3 - resolveddesc = ui.edit(prefixes + desc, ui.username(), action='desc') - else: - resolveddesc = ui.edit(prefixes + desc, ui.username()) + resolveddesc = ui.edit(prefixes + desc, ui.username(), action='desc') # make sure we remove the prefixes part from final commit message if prefixes in resolveddesc: # hack, we should find something better @@ -1297,7 +1294,7 @@ def _cleanup(ui, repo, startnode, showprogress, shouldupdate): if showprogress: - ui.progress(_('evolve'), None) + compat.progress(ui, _('evolve'), None) if not shouldupdate: unfi = repo.unfiltered() @@ -1310,10 +1307,7 @@ """Compute sets of commits divergent with a given one""" cache = {} base = {} - allpredecessors = getattr(obsutil, 'allpredecessors', None) - if allpredecessors is None: # <= Mercurial 4.3 - allpredecessors = obsutil.allprecursors - for n in allpredecessors(repo.obsstore, [ctx.node()]): + for n in obsutil.allpredecessors(repo.obsstore, [ctx.node()]): if n == ctx.node(): # a node can't be a base for divergence with itself continue @@ -1344,7 +1338,7 @@ ('A', 'any', False, _('also consider troubled changesets unrelated to current working ' 'directory')), - ('r', 'rev', [], _('solves troubles of these revisions')), + ('r', 'rev', [], _('solves troubles of these revisions'), _('REV')), ('', 'bumped', False, _('solves only bumped changesets (DEPRECATED)')), ('', 'phase-divergent', False, _('solves only phase-divergent changesets')), ('', 'divergent', False, _('solves only divergent changesets (DEPRECATED)')), @@ -1484,36 +1478,9 @@ raise error.Abort(msg) elif len(specifiedcategories) == 1: targetcat = specifiedcategories[0] - elif repo['.'].obsolete(): - oldid = repo['.'].node() - displayer = compat.changesetdisplayer(ui, repo, - {'template': shorttemplate}) - # no args and parent is obsolete, update to successors - try: - ctx = repo[utility._singlesuccessor(repo, repo['.'])] - except utility.MultipleSuccessorsError as 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()) - newid = ctx.node() - - if ctx != startnode: - with repo.wlock(), repo.lock(), repo.transaction('evolve') as tr: - bmupdater = rewriteutil.bookmarksupdater(repo, oldid, tr) - bmupdater(newid) - ui.status(_('working directory is now at %s\n') % ctx) - return res + elif repo['.'].obsolete() and not(revopt or anyopt or allopt): + # if no args and parent is obsolete, update to successors + return solveobswdp(ui, repo, opts) ui.setconfig('ui', 'forcemerge', opts.get('tool', ''), 'evolve') troubled = set(repo.revs('troubled()')) @@ -1525,7 +1492,7 @@ def progresscb(): if revopt or allopt: - ui.progress(_('evolve'), seen, unit=_('changesets'), total=count) + compat.progress(ui, _('evolve'), seen, unit=_('changesets'), total=count) evolvestate = state.cmdstate(repo) # Continuation handling @@ -1565,6 +1532,15 @@ revs = _selectrevs(repo, allopt, revopt, anyopt, targetcat) + # Case: when wdir parent is obsolete and args passed. + # Handling it here otherwise `revs` set would change, after + # performing update to successor of obsolete wdir parent. + # (in case when user passes a revset related to wdir parent '.::') + if repo['.'].obsolete(): + result = solveobswdp(ui, repo, opts) + if result != 0 or result is True: + return result + if not revs: return _handlenotrouble(ui, repo, allopt, revopt, anyopt, targetcat) @@ -1584,14 +1560,14 @@ # to confirm that if atop msg should be suppressed to remove redundancy lastsolved = None - # check if revs to be evolved are in active topic to make sure that we - # can use stack aliases s# in evolve msgs. activetopic = getattr(repo, 'currenttopic', '') for rev in revs: curctx = repo[rev] revtopic = getattr(curctx, 'topic', lambda: '')() topicidx = getattr(curctx, 'topicidx', lambda: None)() stacktmplt = False + # check if revision being evolved is in active topic to make sure + # that we can use stack aliases s# in evolve msgs. if activetopic and (activetopic == revtopic) and topicidx is not None: stacktmplt = True progresscb() @@ -1613,6 +1589,7 @@ stacktmplt=stacktmplt) if ret[0]: evolvestate['replacements'][curctx.node()] = ret[1] + lastsolved = ret[1] else: evolvestate['skippedrevs'].append(curctx.node()) @@ -1621,6 +1598,38 @@ progresscb() _cleanup(ui, repo, startnode, showprogress, shouldupdate) +def solveobswdp(ui, repo, opts): + oldid = repo['.'].node() + startnode = repo['.'] + dryrunopt = opts.get('dry_run', False) + displayer = compat.changesetdisplayer(ui, repo, + {'template': shorttemplate}) + try: + ctx = repo[utility._singlesuccessor(repo, repo['.'])] + except utility.MultipleSuccessorsError as 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()) + newid = ctx.node() + + if ctx != startnode: + with repo.wlock(), repo.lock(), repo.transaction('evolve') as tr: + bmupdater = rewriteutil.bookmarksupdater(repo, oldid, tr) + bmupdater(newid) + ui.status(_('working directory is now at %s\n') % ctx) + return res + def stopevolve(ui, repo, evolvestate): """logic for handling of `hg evolve --stop`""" updated = False @@ -1753,14 +1762,25 @@ # evolved to confirm that if atop msg should be suppressed to remove # redundancy lastsolved = None + activetopic = getattr(repo, 'currenttopic', '') for rev in evolvestate['revs']: # XXX: prevent this lookup by storing nodes instead of revnums curctx = unfi[rev] + + # check if we can use stack template + revtopic = getattr(curctx, 'topic', lambda: '')() + topicidx = getattr(curctx, 'topicidx', lambda: None)() + stacktmplt = False + if (activetopic and (activetopic == revtopic) + and topicidx is not None): + stacktmplt = True + if (curctx.node() not in evolvestate['replacements'] and curctx.node() not in evolvestate['skippedrevs']): newnode = _solveone(ui, repo, curctx, evolvestate, False, confirm, progresscb, category, - lastsolved=lastsolved) + lastsolved=lastsolved, + stacktmplt=stacktmplt) if newnode[0]: evolvestate['replacements'][curctx.node()] = newnode[1] lastsolved = newnode[1] diff -r 75db6a9d0b54 -r a71f2271ed76 hgext3rd/evolve/exthelper.py --- a/hgext3rd/evolve/exthelper.py Tue Jan 22 10:43:44 2019 -0500 +++ b/hgext3rd/evolve/exthelper.py Tue Jan 22 10:46:02 2019 -0500 @@ -1,38 +1,82 @@ +# Copyright 2012 Logilab SA +# Pierre-Yves David +# Octobus +# +# This software may be used and distributed according to the terms of the +# GNU General Public License version 2 or any later version. + ##################################################################### ### Extension helper ### ##################################################################### from mercurial import ( commands, + error, extensions, registrar, - revset, - templatekw, - util, ) -configitem = None -dynamicdefault = None -if util.safehasattr(registrar, 'configitem'): - configitem = registrar.configitem - from mercurial import configitems - dynamicdefault = configitems.dynamicdefault - class exthelper(object): """Helper for modular extension setup - A single helper should be instantiated for each extension. Helper - methods are then used as decorators for various purpose. + A single helper should be instantiated for each module of an + extension, where a command or function needs to be wrapped, or a + command, extension hook, fileset, revset or template needs to be + registered. Helper methods are then used as decorators for + these various purposes. If an extension spans multiple modules, + all helper instances should be merged in the main module. All decorators return the original function and may be chained. + + Aside from the helper functions with examples below, several + registrar method aliases are available for adding commands, + configitems, filesets, revsets, and templates. Simply decorate + the appropriate methods, and assign the corresponding exthelper + variable to a module level variable of the extension. The + extension loading mechanism will handle the rest. + + example:: + + # ext.py + eh = exthelper.exthelper() + + # As needed: + cmdtable = eh.cmdtable + configtable = eh.configtable + filesetpredicate = eh.filesetpredicate + revsetpredicate = eh.revsetpredicate + templatekeyword = eh.templatekeyword + + @eh.command('mynewcommand', + [('r', 'rev', [], _('operate on these revisions'))], + _('-r REV...'), + helpcategory=command.CATEGORY_XXX) + def newcommand(ui, repo, *revs, **opts): + # implementation goes here + + eh.configitem('experimental', 'foo', + default=False, + ) + + @eh.filesetpredicate('lfs()') + def filesetbabar(mctx, x): + return mctx.predicate(...) + + @eh.revsetpredicate('hidden') + def revsetbabar(repo, subset, x): + args = revset.getargs(x, 0, 0, 'babar accept no argument') + return [r for r in subset if 'babar' in repo[r].description()] + + @eh.templatekeyword('babar') + def kwbabar(ctx): + return 'babar' """ def __init__(self): + self._uipopulatecallables = [] self._uicallables = [] self._extcallables = [] self._repocallables = [] - self._revsetsymbols = [] - self._templatekws = [] self._commandwrappers = [] self._extcommandwrappers = [] self._functionwrappers = [] @@ -49,27 +93,19 @@ self.command._doregister = _newdoregister self.configtable = {} - self._configitem = None - if configitem is not None: - self._configitem = configitem(self.configtable) - - def configitem(self, section, config): - """For Mercurial 4.4 and above, register a config item - - For now constraint to 'dynamicdefault' until we only support version with the feature. - Older version would otherwise not use the declare default. - - For older version no-op fallback for old Mercurial versions - """ - if self._configitem is not None: - self._configitem(section, config, default=dynamicdefault) + self.configitem = registrar.configitem(self.configtable) + self.filesetpredicate = registrar.filesetpredicate() + self.revsetpredicate = registrar.revsetpredicate() + self.templatekeyword = registrar.templatekeyword() def merge(self, other): self._uicallables.extend(other._uicallables) + self._uipopulatecallables.extend(other._uipopulatecallables) self._extcallables.extend(other._extcallables) self._repocallables.extend(other._repocallables) - self._revsetsymbols.extend(other._revsetsymbols) - self._templatekws.extend(other._templatekws) + self.filesetpredicate._table.update(other.filesetpredicate._table) + self.revsetpredicate._table.update(other.revsetpredicate._table) + self.templatekeyword._table.update(other.templatekeyword._table) self._commandwrappers.extend(other._commandwrappers) self._extcommandwrappers.extend(other._extcommandwrappers) self._functionwrappers.extend(other._functionwrappers) @@ -81,7 +117,7 @@ else: self.configtable[section] = items - def final_uisetup(self, ui): + def finaluisetup(self, ui): """Method to be used as the extension uisetup The following operations belong here: @@ -105,14 +141,26 @@ 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 opt in opts: + entry[1].append(opt) for cont, funcname, wrapper in self._functionwrappers: extensions.wrapfunction(cont, funcname, wrapper) for c in self._uicallables: c(ui) - def final_extsetup(self, ui): + def finaluipopulate(self, ui): + """Method to be used as the extension uipopulate + + This is called once per ui instance to: + + - Set up additional ui members + - Update configuration by ``ui.setconfig()`` + - Extend the class dynamically + """ + for c in self._uipopulatecallables: + c(ui) + + def finalextsetup(self, ui): """Method to be used as a the extension extsetup The following operations belong here: @@ -120,23 +168,9 @@ - Changes depending on the status of other extensions. (if extensions.find('mq')) - Add a global option to all commands - - Register revset functions """ knownexts = {} - revsetpredicate = registrar.revsetpredicate() - for name, symbol in self._revsetsymbols: - revsetpredicate(name)(symbol) - revset.loadpredicate(ui, 'evolve', revsetpredicate) - - templatekeyword = registrar.templatekeyword() - for name, kw, requires in self._templatekws: - if requires is not None: - templatekeyword(name, requires=requires)(kw) - else: - templatekeyword(name)(kw) - templatekw.loadkeyword(ui, 'evolve', templatekeyword) - for ext, command, wrapper, opts in self._extcommandwrappers: if ext not in knownexts: try: @@ -148,13 +182,13 @@ knownexts[ext] = e.cmdtable entry = extensions.wrapcommand(knownexts[ext], command, wrapper) if opts: - for short, long, val, msg in opts: - entry[1].append((short, long, val, msg)) + for opt in opts: + entry[1].append(opt) for c in self._extcallables: c(ui) - def final_reposetup(self, ui, repo): + def finalreposetup(self, ui, repo): """Method to be used as the extension reposetup The following operations belong here: @@ -178,6 +212,18 @@ self._uicallables.append(call) return call + def uipopulate(self, call): + """Decorated function will be executed during uipopulate + + example:: + + @eh.uipopulate + def setupfoo(ui): + print 'this is uipopulate!' + """ + self._uipopulatecallables.append(call) + return call + def extsetup(self, call): """Decorated function will be executed during extsetup @@ -202,42 +248,7 @@ self._repocallables.append(call) return call - def revset(self, symbolname): - """Decorated function is a revset symbol - - The name of the symbol must be given as the decorator argument. - The symbol is added during `extsetup`. - - example:: - - @eh.revset('hidden') - def revsetbabar(repo, subset, x): - args = revset.getargs(x, 0, 0, 'babar accept no argument') - return [r for r in subset if 'babar' in repo[r].description()] - """ - def dec(symbol): - self._revsetsymbols.append((symbolname, symbol)) - return symbol - return dec - - def templatekw(self, keywordname, requires=None): - """Decorated function is a template keyword - - The name of the keyword must be given as the decorator argument. - The symbol is added during `extsetup`. - - example:: - - @eh.templatekw('babar') - def kwbabar(ctx): - return 'babar' - """ - def dec(keyword): - self._templatekws.append((keywordname, keyword, requires)) - return keyword - return dec - - def wrapcommand(self, command, extension=None, opts=[]): + def wrapcommand(self, command, extension=None, opts=None): """Decorated function is a command wrapper The name of the command must be given as the decorator argument. @@ -256,10 +267,21 @@ ui.note('Barry!') return orig(ui, repo, *args, **kwargs) - The `opts` argument allows specifying additional arguments for the - command. + The `opts` argument allows specifying a list of tuples for additional + arguments for the command. See ``mercurial.fancyopts.fancyopts()`` for + the format of the tuple. """ + if opts is None: + opts = [] + else: + for opt in opts: + if not isinstance(opt, tuple): + raise error.ProgrammingError('opts must be list of tuples') + if len(opt) not in (4, 5): + msg = 'each opt tuple must contain 4 or 5 values' + raise error.ProgrammingError(msg) + def dec(wrapper): if extension is None: self._commandwrappers.append((command, wrapper, opts)) @@ -294,9 +316,18 @@ This function takes two arguments, the container and the name of the function to wrap. The wrapping is performed during `uisetup`. + Adding attributes to a container like this is discouraged, because the + container modification is visible even in repositories that do not + have the extension loaded. Therefore, care must be taken that the + function doesn't make assumptions that the extension was loaded for the + current repository. For `ui` and `repo` instances, a better option is + to subclass the instance in `uipopulate` and `reposetup` respectively. + + https://www.mercurial-scm.org/wiki/WritingExtensions + example:: - @eh.function(context.changectx, 'babar') + @eh.addattr(context.changectx, 'babar') def babar(ctx): return 'babar' in ctx.description """ diff -r 75db6a9d0b54 -r a71f2271ed76 hgext3rd/evolve/firstmergecache.py --- a/hgext3rd/evolve/firstmergecache.py Tue Jan 22 10:43:44 2019 -0500 +++ b/hgext3rd/evolve/firstmergecache.py Tue Jan 22 10:46:02 2019 -0500 @@ -75,8 +75,8 @@ total = len(data) def progress(pos, rev): - repo.ui.progress('updating firstmerge cache', - pos, 'rev %s' % rev, unit='revision', total=total) + compat.progress(repo.ui, 'updating firstmerge cache', + pos, 'rev %s' % rev, unit='revision', total=total) progress(0, '') for idx, rev in enumerate(data, 1): assert rev == len(self._data), (rev, len(self._data)) diff -r 75db6a9d0b54 -r a71f2271ed76 hgext3rd/evolve/metadata.py --- a/hgext3rd/evolve/metadata.py Tue Jan 22 10:43:44 2019 -0500 +++ b/hgext3rd/evolve/metadata.py Tue Jan 22 10:46:02 2019 -0500 @@ -5,7 +5,7 @@ # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. -__version__ = '8.3.4.dev' -testedwith = '4.3.2 4.4.2 4.5.2 4.6.2 4.7 4.8' -minimumhgversion = '4.3' +__version__ = '8.4.0.dev' +testedwith = '4.4.2 4.5.2 4.6.2 4.7 4.8 4.9' +minimumhgversion = '4.4' buglink = 'https://bz.mercurial-scm.org/' diff -r 75db6a9d0b54 -r a71f2271ed76 hgext3rd/evolve/obsdiscovery.py --- a/hgext3rd/evolve/obsdiscovery.py Tue Jan 22 10:43:44 2019 -0500 +++ b/hgext3rd/evolve/obsdiscovery.py Tue Jan 22 10:46:02 2019 -0500 @@ -74,11 +74,11 @@ obsexcmsg = utility.obsexcmsg # Config -eh.configitem('experimental', 'evolution.obsdiscovery') -eh.configitem('experimental', 'obshashrange') -eh.configitem('experimental', 'obshashrange.warm-cache') -eh.configitem('experimental', 'obshashrange.max-revs') -eh.configitem('experimental', 'obshashrange.lru-size') +eh.configitem('experimental', 'evolution.obsdiscovery', True) +eh.configitem('experimental', 'obshashrange', True) +eh.configitem('experimental', 'obshashrange.warm-cache', 'auto') +eh.configitem('experimental', 'obshashrange.max-revs', None) +eh.configitem('experimental', 'obshashrange.lru-size', 2000) ################################## ### Code performing discovery ### @@ -95,8 +95,8 @@ common = set() undecided = set(probeset) totalnb = len(undecided) - ui.progress(_("comparing with other"), 0, total=totalnb, - unit=_("changesets")) + compat.progress(ui, _("comparing with other"), 0, total=totalnb, + unit=_("changesets")) _takefullsample = setdiscovery._takefullsample if remote.capable('_evoext_obshash_1'): getremotehash = remote.evoext_obshash1 @@ -120,8 +120,8 @@ sample = _takefullsample(dag, undecided, size=fullsamplesize) roundtrips += 1 - ui.progress(_("comparing with other"), totalnb - len(undecided), - total=totalnb, unit=_("changesets")) + compat.progress(ui, _("comparing with other"), totalnb - len(undecided), + total=totalnb, unit=_("changesets")) ui.debug("query %i; still undecided: %i, sample size is: %i\n" % (roundtrips, len(undecided), len(sample))) # indices between sample and externalized version must match @@ -140,7 +140,7 @@ undecided.difference_update(missing) undecided.difference_update(common) - ui.progress(_("comparing with other"), None) + compat.progress(ui, _("comparing with other"), None) result = dag.headsetofconnecteds(common) ui.debug("%d total queries\n" % roundtrips) @@ -182,8 +182,8 @@ local.obsstore.rangeobshashcache.update(local) querycount = 0 - ui.progress(_("comparing obsmarker with other"), querycount, - unit=_("queries")) + compat.progress(ui, _("comparing obsmarker with other"), querycount, + unit=_("queries")) overflow = [] while sample or overflow: if overflow: @@ -238,9 +238,9 @@ addentry(new) assert nbsample == nbreplies querycount += 1 - ui.progress(_("comparing obsmarker with other"), querycount, - unit=_("queries")) - ui.progress(_("comparing obsmarker with other"), None) + compat.progress(ui, _("comparing obsmarker with other"), querycount, + unit=_("queries")) + compat.progress(ui, _("comparing obsmarker with other"), None) local.obsstore.rangeobshashcache.save(local) duration = util.timer() - starttime logmsg = ('obsdiscovery, %d/%d mismatch' @@ -545,8 +545,8 @@ total = len(revs) def progress(pos, rev): - repo.ui.progress('updating obshashrange cache', - pos, 'rev %s' % rev, unit='revision', total=total) + compat.progress(repo.ui, 'updating obshashrange cache', + pos, 'rev %s' % rev, unit='revision', total=total) # warm the cache for the new revs progress(0, '') for idx, r in enumerate(revs): @@ -775,9 +775,9 @@ return encodelist(hashes) def _useobshashrange(repo): - base = repo.ui.configbool('experimental', 'obshashrange', True) + base = repo.ui.configbool('experimental', 'obshashrange') if base: - maxrevs = repo.ui.configint('experimental', 'obshashrange.max-revs', None) + maxrevs = repo.ui.configint('experimental', 'obshashrange.max-revs') if maxrevs is not None and maxrevs < len(repo.unfiltered()): base = False return base @@ -861,8 +861,8 @@ cache = [] unfi = repo.unfiltered() markercache = {} - repo.ui.progress(_("preparing locally"), 0, total=len(unfi), - unit=_("changesets")) + compat.progress(repo.ui, _("preparing locally"), 0, total=len(unfi), + unit=_("changesets")) for i in unfi: ctx = unfi[i] entry = 0 @@ -892,9 +892,9 @@ cache.append((ctx.node(), sha.digest())) else: cache.append((ctx.node(), node.nullid)) - repo.ui.progress(_("preparing locally"), i, total=len(unfi), - unit=_("changesets")) - repo.ui.progress(_("preparing locally"), None) + compat.progress(repo.ui, _("preparing locally"), i, total=len(unfi), + unit=_("changesets")) + compat.progress(repo.ui, _("preparing locally"), None) return cache def _obshash(repo, nodes, version=0): @@ -945,7 +945,7 @@ """wrapper to advertise new capability""" caps = orig(repo, proto) if (obsolete.isenabled(repo, obsolete.exchangeopt) - and repo.ui.configbool('experimental', 'evolution.obsdiscovery', True)): + and repo.ui.configbool('experimental', 'evolution.obsdiscovery')): # Compat hg 4.6+ (2f7290555c96) bytesresponse = False @@ -1015,7 +1015,7 @@ """ def usediscovery(repo): - return repo.ui.configbool('experimental', 'evolution.obsdiscovery', True) + return repo.ui.configbool('experimental', 'evolution.obsdiscovery') @eh.wrapfunction(exchange, '_pushdiscoveryobsmarkers') def _pushdiscoveryobsmarkers(orig, pushop): @@ -1088,12 +1088,12 @@ if bundle2 and _canobshashrange(repo, remote): obsexcmsg(repo.ui, "looking for common markers in %i nodes\n" % len(revs)) - boundaries['missing'] = findmissingrange(repo.ui, repo, pullop.remote, + boundaries['missing'] = findmissingrange(repo.ui, unfi, pullop.remote, revs) elif remote.capable('_evoext_obshash_0'): obsexcmsg(repo.ui, "looking for common markers in %i nodes\n" % len(revs)) - boundaries['common'] = findcommonobsmarkers(repo.ui, repo, remote, revs) + boundaries['common'] = findcommonobsmarkers(repo.ui, unfi, remote, revs) else: boundaries['common'] = [node.nullid] return boundaries diff -r 75db6a9d0b54 -r a71f2271ed76 hgext3rd/evolve/obsexchange.py --- a/hgext3rd/evolve/obsexchange.py Tue Jan 22 10:43:44 2019 -0500 +++ b/hgext3rd/evolve/obsexchange.py Tue Jan 22 10:46:02 2019 -0500 @@ -39,7 +39,7 @@ obsexcmsg = utility.obsexcmsg obsexcprg = utility.obsexcprg -eh.configitem('experimental', 'verbose-obsolescence-exchange') +eh.configitem('experimental', 'verbose-obsolescence-exchange', False) _bestformat = max(obsolete.formats.keys()) diff -r 75db6a9d0b54 -r a71f2271ed76 hgext3rd/evolve/obshistory.py --- a/hgext3rd/evolve/obshistory.py Tue Jan 22 10:43:44 2019 -0500 +++ b/hgext3rd/evolve/obshistory.py Tue Jan 22 10:46:02 2019 -0500 @@ -14,7 +14,6 @@ error, graphmod, patch, - obsolete, obsutil, node as nodemod, scmutil, @@ -863,49 +862,6 @@ return False return True -# Wrap pre Mercurial 4.4 createmarkers that didn't included effect-flag -if not util.safehasattr(obsutil, 'geteffectflag'): - @eh.wrapfunction(obsolete, 'createmarkers') - def createmarkerswithbits(orig, repo, relations, flag=0, date=None, - metadata=None, **kwargs): - """compute 'effect-flag' and augment the created markers - - Wrap obsolete.createmarker in order to compute the effect of each - relationship and store them as flag in the metadata. - - While we experiment, we store flag in a metadata field. This field is - "versionned" to easilly allow moving to other meaning for flags. - - The comparison of description or other infos just before creating the obs - marker might induce overhead in some cases. However it is a good place to - start since it automatically makes all markers creation recording more - meaningful data. In the future, we can introduce way for commands to - provide precomputed effect to avoid the overhead. - """ - if not repo.ui.configbool('experimental', 'evolution.effect-flags', **efd): - return orig(repo, relations, flag, date, metadata, **kwargs) - if metadata is None: - metadata = {} - tr = repo.transaction('add-obsolescence-marker') - try: - for r in relations: - # Compute the effect flag for each obsmarker - effect = geteffectflag(r) - - # Copy the metadata in order to add them, we copy because the - # effect flag might be different per relation - m = metadata.copy() - # we store the effect even if "0". This disctinct markers created - # without the feature with markers recording a no-op. - m['ef1'] = "%d" % effect - - # And call obsolete.createmarkers for creating the obsmarker for real - orig(repo, [r], flag, date, m, **kwargs) - - tr.close() - finally: - tr.release() - def _getobsfate(successorssets): """ Compute a changeset obsolescence fate based on his successorssets. Successors can be the tipmost ones or the immediate ones. diff -r 75db6a9d0b54 -r a71f2271ed76 hgext3rd/evolve/rewind.py --- a/hgext3rd/evolve/rewind.py Tue Jan 22 10:43:44 2019 -0500 +++ b/hgext3rd/evolve/rewind.py Tue Jan 22 10:46:02 2019 -0500 @@ -27,10 +27,11 @@ @eh.command( 'rewind|undo', - [('', 'to', [], _("rewind to these revisions")), + [('', 'to', [], _("rewind to these revisions"), _('REV')), ('', 'as-divergence', None, _("preserve current latest successors")), ('', 'exact', None, _("only rewind explicitly selected revisions")), - ('', 'from', [], _("rewind these revisions to their predecessors")), + ('', 'from', [], + _("rewind these revisions to their predecessors"), _('REV')), ], _(''), helpbasic=True) diff -r 75db6a9d0b54 -r a71f2271ed76 hgext3rd/evolve/safeguard.py --- a/hgext3rd/evolve/safeguard.py Tue Jan 22 10:43:44 2019 -0500 +++ b/hgext3rd/evolve/safeguard.py Tue Jan 22 10:46:02 2019 -0500 @@ -8,37 +8,48 @@ # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. -from mercurial import error +from mercurial.i18n import _ -from mercurial.i18n import _ +from mercurial import ( + configitems, + error, +) from . import exthelper eh = exthelper.exthelper() -eh.configitem('experimental', 'auto-publish') +# hg <= 4.8 +if 'auto-publish' not in configitems.coreitems.get('experimental', {}): + + eh.configitem('experimental', 'auto-publish', 'publish') -@eh.reposetup -def setuppublishprevention(ui, repo): + @eh.reposetup + def setuppublishprevention(ui, repo): - class noautopublishrepo(repo.__class__): + class noautopublishrepo(repo.__class__): - def checkpush(self, pushop): - super(noautopublishrepo, self).checkpush(pushop) - behavior = self.ui.config('experimental', 'auto-publish', 'default') - remotephases = pushop.remote.listkeys('phases') - publishing = remotephases.get('publishing', False) - if behavior in ('warn', 'abort') and publishing: - if pushop.revs is None: - published = self.filtered('served').revs("not public()") - else: - published = self.revs("::%ln - public()", pushop.revs) - if published: - if behavior == 'warn': - self.ui.warn(_('%i changesets about to be published\n') % len(published)) - elif behavior == 'abort': - msg = _('push would publish 1 changesets') - hint = _("behavior controlled by 'experimental.auto-publish' config") - raise error.Abort(msg, hint=hint) + def checkpush(self, pushop): + super(noautopublishrepo, self).checkpush(pushop) + behavior = self.ui.config('experimental', 'auto-publish') + nocheck = behavior not in ('warn', 'abort') + if nocheck or getattr(pushop, 'publish', False): + return + remotephases = pushop.remote.listkeys('phases') + publishing = remotephases.get('publishing', False) + if publishing: + if pushop.revs is None: + published = self.filtered('served').revs("not public()") + else: + published = self.revs("::%ln - public()", pushop.revs) + if published: + if behavior == 'warn': + self.ui.warn(_('%i changesets about to be published\n') + % len(published)) + elif behavior == 'abort': + msg = _('push would publish 1 changesets') + hint = _("behavior controlled by " + "'experimental.auto-publish' config") + raise error.Abort(msg, hint=hint) - repo.__class__ = noautopublishrepo + repo.__class__ = noautopublishrepo diff -r 75db6a9d0b54 -r a71f2271ed76 hgext3rd/evolve/serveronly.py --- a/hgext3rd/evolve/serveronly.py Tue Jan 22 10:43:44 2019 -0500 +++ b/hgext3rd/evolve/serveronly.py Tue Jan 22 10:46:02 2019 -0500 @@ -45,9 +45,9 @@ eh.merge(compat.eh) eh.merge(obscache.eh) eh.merge(obsexchange.eh) -uisetup = eh.final_uisetup -extsetup = eh.final_extsetup -reposetup = eh.final_reposetup +uisetup = eh.finaluisetup +extsetup = eh.finalextsetup +reposetup = eh.finalreposetup cmdtable = eh.cmdtable configtable = eh.configtable diff -r 75db6a9d0b54 -r a71f2271ed76 hgext3rd/evolve/stablerange.py --- a/hgext3rd/evolve/stablerange.py Tue Jan 22 10:43:44 2019 -0500 +++ b/hgext3rd/evolve/stablerange.py Tue Jan 22 10:46:02 2019 -0500 @@ -23,6 +23,7 @@ from mercurial.i18n import _ from . import ( + compat, exthelper, firstmergecache, stablesort, @@ -619,12 +620,12 @@ rangeheap = [] for idx, r in enumerate(revs): if not idx % 1000: - ui.progress(_("filling depth cache"), idx, total=nbrevs, - unit=_("changesets")) + compat.progress(ui, _("filling depth cache"), idx, total=nbrevs, + unit=_("changesets")) # warm up depth self.depthrev(repo, r) rangeheap.append((-r, (r, 0))) - ui.progress(_("filling depth cache"), None, total=nbrevs) + compat.progress(ui, _("filling depth cache"), None, total=nbrevs) heappop = heapq.heappop heappush = heapq.heappush @@ -645,8 +646,8 @@ progress_new = time.time() if (1 < progress_each) and (0.1 < progress_new - progress_last): progress_each /= 10 - ui.progress(_("filling stablerange cache"), seen, - total=nbrevs, unit=_("changesets")) + compat.progress(ui, _("filling stablerange cache"), seen, + total=nbrevs, unit=_("changesets")) progress_last = progress_new seen += 1 original.remove(value) # might have been added from other source @@ -655,7 +656,7 @@ for sub in self.subranges(repo, rangeid): if self._getsub(sub) is None: heappush(rangeheap, (-sub[0], sub)) - ui.progress(_("filling stablerange cache"), None, total=nbrevs) + compat.progress(ui, _("filling stablerange cache"), None, total=nbrevs) self._tiprev = upto self._tipnode = cl.node(upto) diff -r 75db6a9d0b54 -r a71f2271ed76 hgext3rd/evolve/stablerangecache.py --- a/hgext3rd/evolve/stablerangecache.py Tue Jan 22 10:43:44 2019 -0500 +++ b/hgext3rd/evolve/stablerangecache.py Tue Jan 22 10:46:02 2019 -0500 @@ -21,6 +21,7 @@ ) from . import ( + compat, exthelper, genericcaches, stablerange, @@ -97,8 +98,8 @@ warned_long = True if (1 < progress_each) and (0.1 < progress_new - progress_last): progress_each /= 10 - ui.progress(_("filling stablerange cache"), seen, - total=total, unit=_("changesets")) + compat.progress(ui, _("filling stablerange cache"), seen, + total=total, unit=_("changesets")) progress_last = progress_new seen += 1 original.remove(rangeid) # might have been added from other source @@ -107,7 +108,7 @@ for sub in self.subranges(repo, rangeid): if self._getsub(sub) is None: heappush(rangeheap, sub) - ui.progress(_("filling stablerange cache"), None, total=total) + compat.progress(ui, _("filling stablerange cache"), None, total=total) def clear(self, reset=False): super(stablerangeondiskbase, self).clear() diff -r 75db6a9d0b54 -r a71f2271ed76 hgext3rd/evolve/stablesort.py --- a/hgext3rd/evolve/stablesort.py Tue Jan 22 10:43:44 2019 -0500 +++ b/hgext3rd/evolve/stablesort.py Tue Jan 22 10:46:02 2019 -0500 @@ -577,8 +577,8 @@ total = len(data) def progress(pos, rev): - repo.ui.progress('updating stablesort cache', - pos, 'rev %s' % rev, unit='revision', total=total) + compat.progress(repo.ui, 'updating stablesort cache', + pos, 'rev %s' % rev, unit='revision', total=total) progress(0, '') for idx, rev in enumerate(data): diff -r 75db6a9d0b54 -r a71f2271ed76 hgext3rd/evolve/templatekw.py --- a/hgext3rd/evolve/templatekw.py Tue Jan 22 10:43:44 2019 -0500 +++ b/hgext3rd/evolve/templatekw.py Tue Jan 22 10:46:02 2019 -0500 @@ -9,7 +9,6 @@ """ from . import ( - compat, error, exthelper, obshistory @@ -17,7 +16,6 @@ from mercurial import ( templatekw, - node, util ) @@ -26,14 +24,14 @@ ### template keywords if util.safehasattr(templatekw, 'compatlist'): - @eh.templatekw('troubles', requires=set(['ctx', 'templ'])) + @eh.templatekeyword('troubles', requires=set(['ctx', 'templ'])) def showtroubles(context, mapping): ctx = context.resource(mapping, 'ctx') return templatekw.compatlist(context, mapping, 'trouble', ctx.instabilities(), plural='troubles') else: # older template API in hg < 4.6 - @eh.templatekw('troubles') + @eh.templatekeyword('troubles') def showtroubles(**args): """List of strings. Evolution troubles affecting the changeset (zero or more of "unstable", "divergent" or "bumped").""" @@ -41,79 +39,14 @@ return templatekw.showlist('trouble', ctx.instabilities(), args, plural='troubles') -if util.safehasattr(templatekw, 'showpredecessors'): - templatekw.keywords["precursors"] = templatekw.showpredecessors -else: - # for version <= hg4.3 - def closestprecursors(repo, nodeid): - """ Yield the list of next precursors pointing on visible changectx nodes - """ - - precursors = repo.obsstore.predecessors - stack = [nodeid] - seen = set(stack) - - while stack: - current = stack.pop() - currentpreccs = precursors.get(current, ()) - - for prec in currentpreccs: - precnodeid = prec[0] - - # Basic cycle protection - if precnodeid in seen: - continue - seen.add(precnodeid) - - if precnodeid in repo: - yield precnodeid - else: - stack.append(precnodeid) - - @eh.templatekw("precursors") - def shownextvisibleprecursors(repo, ctx, **args): - """Returns a string containing the list of the closest precursors - """ - precursors = sorted(closestprecursors(repo, ctx.node())) - precursors = [node.hex(p) for p in precursors] - - return templatekw._hybrid(None, precursors, lambda x: {'precursor': x}, - lambda d: d['precursor'][:12]) +templatekw.keywords["precursors"] = templatekw.showpredecessors def closestsuccessors(repo, nodeid): """ returns the closest visible successors sets instead. """ return directsuccessorssets(repo, nodeid) -if util.safehasattr(templatekw, 'showsuccessorssets'): - templatekw.keywords["successors"] = templatekw.showsuccessorssets -else: - # for version <= hg4.3 - - @eh.templatekw("successors") - def shownextvisiblesuccessors(repo, ctx, templ, **args): - """Returns a string of sets of successors for a changectx - - Format used is: [ctx1, ctx2], [ctx3] if ctx has been splitted into ctx1 and - ctx2 while also diverged into ctx3""" - if not ctx.obsolete(): - return '' - - ssets, _ = closestsuccessors(repo, ctx.node()) - ssets = [[node.hex(n) for n in ss] for ss in ssets] - - data = [] - gen = [] - for ss in ssets: - subgen = '[%s]' % ', '.join(n[:12] for n in ss) - gen.append(subgen) - h = templatekw._hybrid(iter(subgen), ss, lambda x: {'successor': x}, - lambda d: "%s" % d["successor"]) - data.append(h) - - gen = ', '.join(gen) - return templatekw._hybrid(iter(gen), data, lambda x: {'successorset': x}, - lambda d: d["successorset"]) +templatekw.keywords["successors"] = templatekw.showsuccessorssets def _getusername(ui): """the default username in the config or None""" @@ -241,25 +174,8 @@ return "\n".join(lines) -if util.safehasattr(templatekw, 'obsfateverb'): - # Individuals fragments are available in core - pass -elif util.safehasattr(templatekw, 'compatlist'): - @eh.templatekw('obsfatedata', requires=set(['ctx', 'templ'])) - def showobsfatedata(context, mapping): - ctx = context.resource(mapping, 'ctx') - repo = ctx.repo() - values = obsfatedata(repo, ctx) - - if values is None: - return templatekw.compatlist(context, mapping, "obsfatedata", []) - args = mapping.copy() - args.pop('ctx') - args['templ'] = context - return _showobsfatedata(repo, ctx, values, **args) -else: - # pre hg-4.6 - @eh.templatekw("obsfatedata") +if not util.safehasattr(templatekw, 'obsfateverb'): # <= hg-4.5 + @eh.templatekeyword("obsfatedata") def showobsfatedata(repo, ctx, **args): # Get the needed obsfate data values = obsfatedata(repo, ctx) @@ -325,30 +241,6 @@ return templatekw._hybrid(gen, values, lambda x: {name: x}, fmt) -# rely on core mercurial starting from 4.4 for the obsfate template -if not util.safehasattr(templatekw, 'showobsfate'): - - @eh.templatekw("obsfate") - def showobsfate(*args, **kwargs): - return showobsfatedata(*args, **kwargs) - -if util.safehasattr(compat.changesetprinter, '_showobsfate'): - pass # already included by default -elif util.safehasattr(compat.changesetprinter, '_exthook'): - @eh.wrapfunction(compat.changesetprinter, '_exthook') - def exthook(original, self, ctx): - # Call potential other extensions - original(self, ctx) - - obsfate = obsfatedata(self.repo, ctx) - if obsfate is None: - return "" - - output = obsfateprinter(obsfate, self.ui, prefix="obsolete: ") - - self.ui.write(output, label='log.obsfate') - self.ui.write("\n") - # copy from mercurial.obsolete with a small change to stop at first known changeset. def directsuccessorssets(repo, initialnode, cache=None): diff -r 75db6a9d0b54 -r a71f2271ed76 hgext3rd/evolve/utility.py --- a/hgext3rd/evolve/utility.py Tue Jan 22 10:43:44 2019 -0500 +++ b/hgext3rd/evolve/utility.py Tue Jan 22 10:46:02 2019 -0500 @@ -13,12 +13,15 @@ from mercurial.node import nullrev +from . import ( + compat, +) + shorttemplate = "[{label('evolve.rev', rev)}] {desc|firstline}\n" stacktemplate = """[{label('evolve.rev', if(topicidx, "s{topicidx}", rev))}] {desc|firstline}\n""" def obsexcmsg(ui, message, important=False): - verbose = ui.configbool('experimental', 'verbose-obsolescence-exchange', - False) + verbose = ui.configbool('experimental', 'verbose-obsolescence-exchange') if verbose: message = 'OBSEXC: ' + message if important or verbose: @@ -26,9 +29,9 @@ def obsexcprg(ui, *args, **kwargs): topic = 'obsmarkers exchange' - if ui.configbool('experimental', 'verbose-obsolescence-exchange', False): + if ui.configbool('experimental', 'verbose-obsolescence-exchange'): topic = 'OBSEXC' - ui.progress(topic, *args, **kwargs) + compat.progress(ui, topic, *args, **kwargs) def filterparents(parents): """filter nullrev parents @@ -63,12 +66,12 @@ warm = autocase else: # note: we should not get to the default case - warm = configbool('experimental', 'obshashrange.warm-cache', True) - if not configbool('experimental', 'obshashrange', True): + warm = configbool('experimental', 'obshashrange.warm-cache') + if not configbool('experimental', 'obshashrange'): return False if not warm: return False - maxrevs = repo.ui.configint('experimental', 'obshashrange.max-revs', None) + maxrevs = repo.ui.configint('experimental', 'obshashrange.max-revs') if maxrevs is not None and maxrevs < len(repo.unfiltered()): return False return True diff -r 75db6a9d0b54 -r a71f2271ed76 hgext3rd/topic/__init__.py --- a/hgext3rd/topic/__init__.py Tue Jan 22 10:43:44 2019 -0500 +++ b/hgext3rd/topic/__init__.py Tue Jan 22 10:46:02 2019 -0500 @@ -177,10 +177,10 @@ 'topic.active': 'green', } -__version__ = '0.12.4.dev' +__version__ = '0.13.0.dev' -testedwith = '4.3.3 4.4.2 4.5.2 4.6.2 4.7 4.8' -minimumhgversion = '4.3' +testedwith = '4.4.2 4.5.2 4.6.2 4.7 4.8' +minimumhgversion = '4.4' buglink = 'https://bz.mercurial-scm.org/' if util.safehasattr(registrar, 'configitem'): @@ -680,7 +680,11 @@ txn = repo.transaction('rewrite-topics') rewrote = _changetopics(ui, repo, touchedrevs, topic) txn.close() - ui.status('changed topic on %d changes\n' % rewrote) + if topic is None: + ui.status('cleared topic on %d changesets\n' % rewrote) + else: + ui.status('changed topic on %d changesets to "%s"\n' % (rewrote, + topic)) finally: lockmod.release(txn, lock, wl) repo.invalidate() @@ -717,6 +721,8 @@ return ret @command('stack', [ + ('c', 'children', None, + _('display data about children outside of the stack')) ] + commands.formatteropts, _('hg stack [TOPIC]')) def cmdstack(ui, repo, topic='', **opts): @@ -950,17 +956,21 @@ def _listtopics(ui, repo, opts): fm = ui.formatter('topics', opts) - showlast = opts.get('age') - if showlast: - # we have a new function as plugging logic into existing function is - # pretty much difficult - return _showlasttouched(repo, fm, opts) activetopic = repo.currenttopic namemask = '%s' if repo.topics: maxwidth = max(len(t) for t in repo.topics) namemask = '%%-%is' % maxwidth - for topic in sorted(repo.topics): + if opts.get('age'): + # here we sort by age and topic name + topicsdata = sorted(_getlasttouched(repo, repo.topics)) + else: + # here we sort by topic name only + topicsdata = ( + (None, topic, None, None) + for topic in sorted(repo.topics) + ) + for age, topic, date, user in topicsdata: fm.startitem() marker = ' ' label = 'topic' @@ -977,8 +987,18 @@ if ui.quiet: fm.plain('\n') continue + fm.plain(' (') + if date: + if age == -1: + timestr = 'empty and active' + else: + timestr = templatefilters.age(date) + fm.write('lasttouched', '%s', timestr, label='topic.list.time') + if user: + fm.write('usertouched', ' by %s', user, label='topic.list.user') + if date: + fm.plain(', ') data = stack.stack(repo, topic=topic) - fm.plain(' (') if ui.verbose: fm.write('branches+', 'on branch: %s', '+'.join(data.branches), # XXX use list directly after 4.0 is released @@ -1018,52 +1038,17 @@ fm.plain(')\n') fm.end() -def _showlasttouched(repo, fm, opts): - topics = repo.topics - timedict = _getlasttouched(repo, topics) - times = timedict.keys() - times.sort() - if topics: - maxwidth = max(len(t) for t in topics) - namemask = '%%-%is' % maxwidth - activetopic = repo.currenttopic - for timevalue in times: - curtopics = sorted(timedict[timevalue][1]) - for topic, user in curtopics: - fm.startitem() - marker = ' ' - label = 'topic' - active = (topic == activetopic) - if active: - marker = '*' - label = 'topic.active' - fm.plain(' %s ' % marker, label=label) - fm.write('topic', namemask, topic, label=label) - fm.data(active=active) - fm.plain(' (') - if timevalue == -1: - timestr = 'empty and active' - else: - timestr = templatefilters.age(timedict[timevalue][0]) - fm.write('lasttouched', '%s', timestr, label='topic.list.time') - if user: - fm.write('usertouched', ' by %s', user, label='topic.list.user') - fm.plain(')') - fm.plain('\n') - fm.end() - def _getlasttouched(repo, topics): """ - Calculates the last time a topic was used. Returns a dictionary of seconds - passed from current time for a topic as keys and topic name as values. + Calculates the last time a topic was used. Returns a generator of 4-tuples: + (age in seconds, topic name, date, and user who last touched the topic). """ - topicstime = {} curtime = time.time() - for t in topics: - secspassed = -1 + for topic in topics: + age = -1 user = None maxtime = (0, 0) - trevs = repo.revs("topic(%s)", t) + trevs = repo.revs("topic(%s)", topic) # Need to check for the time of all changesets in the topic, whether # they are obsolete of non-heads # XXX: can we just rely on the max rev number for this @@ -1084,16 +1069,10 @@ maxtime = rt username = stack.parseusername(user) - topicuser = (t, username) + if trevs: + age = curtime - maxtime[0] - if trevs: - secspassed = (curtime - maxtime[0]) - try: - topicstime[secspassed][1].append(topicuser) - except KeyError: - topicstime[secspassed] = (maxtime, [topicuser]) - - return topicstime + yield (age, topic, maxtime, username) def summaryhook(ui, repo): t = getattr(repo, 'currenttopic', '') diff -r 75db6a9d0b54 -r a71f2271ed76 hgext3rd/topic/discovery.py --- a/hgext3rd/topic/discovery.py Tue Jan 22 10:43:44 2019 -0500 +++ b/hgext3rd/topic/discovery.py Tue Jan 22 10:46:02 2019 -0500 @@ -33,32 +33,30 @@ publishedset = () remotebranchmap = None origremotebranchmap = remote.branchmap - # < hg-4.4 do not have a --publish flag anyway - if util.safehasattr(pushop, 'remotephases'): - publishednode = [c.node() for c in pushop.outdatedphases] - publishedset = repo.revs('ancestors(%ln + %ln)', - publishednode, - pushop.remotephases.publicheads) + publishednode = [c.node() for c in pushop.outdatedphases] + publishedset = repo.revs('ancestors(%ln + %ln)', + publishednode, + pushop.remotephases.publicheads) - rev = repo.unfiltered().changelog.nodemap.get + rev = repo.unfiltered().changelog.nodemap.get - def remotebranchmap(): - # drop topic information from changeset about to be published - result = collections.defaultdict(list) - for branch, heads in origremotebranchmap().iteritems(): - if ':' not in branch: - result[branch].extend(heads) - else: - namedbranch = branch.split(':', 1)[0] - for h in heads: - r = rev(h) - if r is not None and r in publishedset: - result[namedbranch].append(h) - else: - result[branch].append(h) - for heads in result.itervalues(): - heads.sort() - return result + def remotebranchmap(): + # drop topic information from changeset about to be published + result = collections.defaultdict(list) + for branch, heads in origremotebranchmap().iteritems(): + if ':' not in branch: + result[branch].extend(heads) + else: + namedbranch = branch.split(':', 1)[0] + for h in heads: + r = rev(h) + if r is not None and r in publishedset: + result[namedbranch].append(h) + else: + result[branch].append(h) + for heads in result.itervalues(): + heads.sort() + return result class repocls(repo.__class__): # awful hack to see branch as "branch:topic" diff -r 75db6a9d0b54 -r a71f2271ed76 hgext3rd/topic/flow.py --- a/hgext3rd/topic/flow.py Tue Jan 22 10:43:44 2019 -0500 +++ b/hgext3rd/topic/flow.py Tue Jan 22 10:46:02 2019 -0500 @@ -7,7 +7,6 @@ extensions, node, phases, - util, ) from mercurial.i18n import _ @@ -75,9 +74,6 @@ def wrapphasediscovery(orig, pushop): orig(pushop) if getattr(pushop, 'publish', False): - if not util.safehasattr(pushop, 'remotephases'): - msg = _('--publish flag only supported from Mercurial 4.4 and higher') - raise error.Abort(msg) if not pushop.remotephases.publishing: unfi = pushop.repo.unfiltered() droots = pushop.remotephases.draftroots @@ -87,8 +83,9 @@ def installpushflag(ui): entry = extensions.wrapcommand(commands.table, 'push', wrappush) - entry[1].append(('', 'publish', False, - _('push the changeset as public'))) + if not any(opt for opt in entry[1] if opt[1] == 'publish'): # hg <= 4.9 + entry[1].append(('', 'publish', False, + _('push the changeset as public'))) extensions.wrapfunction(exchange.pushoperation, '__init__', extendpushoperation) extensions.wrapfunction(exchange, '_pushdiscoveryphase', wrapphasediscovery) diff -r 75db6a9d0b54 -r a71f2271ed76 hgext3rd/topic/randomname.py --- a/hgext3rd/topic/randomname.py Tue Jan 22 10:43:44 2019 -0500 +++ b/hgext3rd/topic/randomname.py Tue Jan 22 10:46:02 2019 -0500 @@ -189,7 +189,6 @@ 'pony', 'porcupine', 'porpoise', - 'prairie', 'puffin', 'pug', 'quagga', diff -r 75db6a9d0b54 -r a71f2271ed76 hgext3rd/topic/revset.py --- a/hgext3rd/topic/revset.py Tue Jan 22 10:43:44 2019 -0500 +++ b/hgext3rd/topic/revset.py Tue Jan 22 10:46:02 2019 -0500 @@ -106,3 +106,52 @@ else: branch = repo[None].branch() return revset.baseset(stack.stack(repo, branch=branch, topic=topic)[1:]) & subset + +if util.safehasattr(revset, 'subscriptrelations'): + def stackrel(repo, subset, x, rel, n, order): + """This is a revset-flavored implementation of stack aliases. + + The syntax is: rev#stack[n] or rev#s[n]. Plenty of logic is borrowed + from topic._namemap, but unlike that function, which prefers to abort + (e.g. when stack index is too high), this returns empty set to be more + revset-friendly. + """ + s = revset.getset(repo, revset.fullreposet(repo), x) + if not s: + return revset.baseset() + revs = [] + for r in s: + topic = repo[r].topic() + if topic: + st = stack.stack(repo, topic=topic) + else: + st = stack.stack(repo, branch=repo[r].branch()) + if n < 0: + st = list(st)[1:] + else: + st = list(st) + try: + rev = st[n] + except IndexError: + continue + if rev == -1 and n == 0: + continue + if rev not in revs: + revs.append(rev) + return subset & revset.baseset(revs) + + revset.subscriptrelations['stack'] = stackrel + revset.subscriptrelations['s'] = stackrel + + def topicrel(repo, subset, x, rel, n, order): + ancestors = revset._ancestors + descendants = revset._descendants + subset = topicset(repo, subset, x) + if n <= 0: + n = -n + return ancestors(repo, subset, x, startdepth=n, stopdepth=n + 1) + else: + return descendants(repo, subset, x, startdepth=n, stopdepth=n + 1) + + revset.subscriptrelations['topic'] = topicrel + revset.subscriptrelations['t'] = topicrel diff -r 75db6a9d0b54 -r a71f2271ed76 hgext3rd/topic/stack.py --- a/hgext3rd/topic/stack.py Tue Jan 22 10:43:44 2019 -0500 +++ b/hgext3rd/topic/stack.py Tue Jan 22 10:46:02 2019 -0500 @@ -329,15 +329,32 @@ symbol = None states = [] + msg = '' iscurrentrevision = repo.revs('%d and parents()', ctx.rev()) + if opts.get('children'): + if branch: + t_msg = '-branch("%s")' % branch + if topic: + t_msg = '-topic("%s")' % topic + rev_msg = 'children(%s) and merge() %s' + revisions = repo.revs(rev_msg % (ctx.rev(), t_msg)) + len_rev = len(revisions) + if len_rev > 0: + msg = 'external-children' if iscurrentrevision: - states.append('current') symbol = '@' + if msg: + states.append('current - ' + msg) + else: + states.append('current') if ctx.orphan(): symbol = '$' - states.append('unstable') + if msg: + states.append('unstable - ' + msg) + else: + states.append('unstable') if not isentry: symbol = '^' @@ -347,7 +364,10 @@ # none of the above if statments get executed if not symbol: symbol = ':' - states.append('clean') + if msg: + states.append(msg) + else: + states.append('clean') states.sort() diff -r 75db6a9d0b54 -r a71f2271ed76 tests/test-amend.t --- a/tests/test-amend.t Tue Jan 22 10:43:44 2019 -0500 +++ b/tests/test-amend.t Tue Jan 22 10:46:02 2019 -0500 @@ -153,7 +153,7 @@ --close-branch mark a branch as closed, hiding it from the branch list -s --secret use the secret phase for committing - -n --note VALUE store a note on amend + -n --note TEXT store a note on amend -I --include PATTERN [+] include names matching the given patterns -X --exclude PATTERN [+] exclude names matching the given patterns -m --message TEXT use text as commit message diff -r 75db6a9d0b54 -r a71f2271ed76 tests/test-discovery-obshashrange-cache.t --- a/tests/test-discovery-obshashrange-cache.t Tue Jan 22 10:43:44 2019 -0500 +++ b/tests/test-discovery-obshashrange-cache.t Tue Jan 22 10:46:02 2019 -0500 @@ -28,7 +28,7 @@ $ hg -R main debugbuilddag '.+7' $ for node in `hg -R main log -T '{node}\n'`; do - > echo -n $node | grep -o . | sort |tr -d "\n" > ancfile + > printf $node | grep -o . | sort |tr -d "\n" > ancfile > anc=`cat ancfile` > rm ancfile > echo "marking $anc as predecessors of $node" diff -r 75db6a9d0b54 -r a71f2271ed76 tests/test-discovery-obshashrange.t --- a/tests/test-discovery-obshashrange.t Tue Jan 22 10:43:44 2019 -0500 +++ b/tests/test-discovery-obshashrange.t Tue Jan 22 10:46:02 2019 -0500 @@ -9,6 +9,10 @@ > blackbox = > [defaults] > blackbox = -l 100 + > [blackbox] + > track = backupbundle, branchcache, cache, command, commandalias, + > commandfinish, debug, discovery, evoext-cache, evoext-obsdiscovery, + > incoming, tagscache > [experimental] > obshashrange=1 > verbose-obsolescence-exchange=1 @@ -190,9 +194,6 @@ remote: capabilities: _evoext_getbundle_obscommon _evoext_obshash_0 _evoext_obshash_1 _evoext_obshashrange_v1 batch * (glob) remote: 1 sending protocaps command - preparing listkeys for "phases" - sending listkeys command - received listkey for "phases": 58 bytes query 1; heads sending batch command searching for changes @@ -322,9 +323,6 @@ * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> remote: capabilities: _evoext_getbundle_obscommon _evoext_obshash_0 _evoext_obshash_1 _evoext_obshashrange_v1 batch branchmap bundle2=HG20%0Abookmarks%0Achangegroup%3D01%2C02%0Adigests%3Dmd5%2Csha1%2Csha512%0Aerror%3Dabort%2Cunsupportedcontent%2Cpushraced%2Cpushkey%0Ahgtagsfnodes%0Alistkeys%0Aobsmarkers%3DV0%2CV1%0Aphases%3Dheads%0Apushkey%0Aremote-changegroup%3Dhttp%2Chttps%0Arev-branch-cache%0Astream%3Dv2 changegroupsubset getbundle known lookup protocaps pushkey streamreqs=generaldelta,revlogv1 unbundle=HG10GZ,HG10BZ,HG10UN unbundlehash (glob) * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> remote: 1 (glob) * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> sending protocaps command (glob) - * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> preparing listkeys for "phases" (glob) - * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> sending listkeys command (glob) - * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> received listkey for "phases": 58 bytes (glob) * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> query 1; heads (glob) * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> sending batch command (glob) * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> taking quick initial sample (glob) diff -r 75db6a9d0b54 -r a71f2271ed76 tests/test-evolve-issue5881.t --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-evolve-issue5881.t Tue Jan 22 10:46:02 2019 -0500 @@ -0,0 +1,53 @@ +Test for issue 5881 present at https://bz.mercurial-scm.org/show_bug.cgi?id=5881 +=============================================================================== +which is about that if the working copy parent is obsolete then evolve update +to its successor revision and stop; it doesn't continue to evolve remaining +revisions those were suppossed to evovle. + +Setup +===== + + $ cat >> $HGRCPATH < [phases] + > publish = False + > [alias] + > glog = log -GT "{rev}:{node|short} {desc}\n ({bookmarks}) {phase}" + > [extensions] + > EOF + $ echo "evolve=$(echo $(dirname $TESTDIR))/hgext3rd/evolve/" >> $HGRCPATH + + $ hg init issue5881 + $ cd issue5881 + +Prepare the directory by creating an orphan and update to its obsolete parent: + + $ for ch in a b c; do echo $ch > $ch; hg ci -Am "added "$ch; done; + adding a + adding b + adding c + $ hg up 1 -q + $ hg ci --amend -m "updated b" + 1 new orphan changesets + $ hg up 1 + 0 files updated, 0 files merged, 0 files removed, 0 files unresolved + working directory parent is obsolete! (5f6d8a4bf34a) + (use 'hg evolve' to update to its successor: e6048a693c0d) + + $ hg glog + o 3:e6048a693c0d updated b + | () draft + | * 2:155349b645be added c + | | () draft + | @ 1:5f6d8a4bf34a added b + |/ () draft + o 0:9092f1db7931 added a + () draft + +Test `hg evolve` evolve all the revisions specified by user: + $ hg evolve -r .:: + update:[3] updated b + 0 files updated, 0 files merged, 0 files removed, 0 files unresolved + working directory is now at e6048a693c0d + move:[2] added c + atop:[3] updated b + working directory is now at c3a628eb9aaf diff -r 75db6a9d0b54 -r a71f2271ed76 tests/test-evolve-obshistory-complex.t --- a/tests/test-evolve-obshistory-complex.t Tue Jan 22 10:43:44 2019 -0500 +++ b/tests/test-evolve-obshistory-complex.t Tue Jan 22 10:46:02 2019 -0500 @@ -140,11 +140,11 @@ Then split ---------- - $ hg split "desc(fold0)" -d "0 0" << EOF + $ hg split --rev "desc(fold0)" -d "0 0" << EOF > Y > Y > N - > N + > Y > Y > Y > EOF @@ -164,7 +164,7 @@ examine changes to 'B'? [Ynesfdaq?] N created new head - Done splitting? [yN] N + continue splitting? [Ycdq?] Y diff --git a/B b/B new file mode 100644 examine changes to 'B'? [Ynesfdaq?] Y @@ -174,11 +174,11 @@ record this change to 'B'? [Ynesfdaq?] Y no more change to split - $ hg split "desc(fold1)" -d "0 0" << EOF + $ hg split --rev "desc(fold1)" -d "0 0" << EOF > Y > Y > N - > N + > Y > Y > Y > EOF @@ -198,7 +198,7 @@ examine changes to 'D'? [Ynesfdaq?] N created new head - Done splitting? [yN] N + continue splitting? [Ycdq?] Y diff --git a/D b/D new file mode 100644 examine changes to 'D'? [Ynesfdaq?] Y @@ -209,11 +209,11 @@ no more change to split 1 new orphan changesets - $ hg split "desc(fold2)" -d "0 0" << EOF + $ hg split --rev "desc(fold2)" -d "0 0" << EOF > Y > Y > N - > N + > Y > Y > Y > EOF @@ -233,7 +233,7 @@ examine changes to 'F'? [Ynesfdaq?] N created new head - Done splitting? [yN] N + continue splitting? [Ycdq?] Y diff --git a/F b/F new file mode 100644 examine changes to 'F'? [Ynesfdaq?] Y diff -r 75db6a9d0b54 -r a71f2271ed76 tests/test-evolve-obshistory-lots-of-splits.t --- a/tests/test-evolve-obshistory-lots-of-splits.t Tue Jan 22 10:43:44 2019 -0500 +++ b/tests/test-evolve-obshistory-lots-of-splits.t Tue Jan 22 10:46:02 2019 -0500 @@ -43,16 +43,16 @@ > n > n > n - > n + > y > y > y > n > n - > n + > y > y > y > n - > n + > y > y > y > EOF @@ -82,7 +82,7 @@ examine changes to 'd'? [Ynesfdaq?] n created new head - Done splitting? [yN] n + continue splitting? [Ycdq?] y diff --git a/b b/b new file mode 100644 examine changes to 'b'? [Ynesfdaq?] y @@ -99,7 +99,7 @@ new file mode 100644 examine changes to 'd'? [Ynesfdaq?] n - Done splitting? [yN] n + continue splitting? [Ycdq?] y diff --git a/c b/c new file mode 100644 examine changes to 'c'? [Ynesfdaq?] y @@ -112,7 +112,7 @@ new file mode 100644 examine changes to 'd'? [Ynesfdaq?] n - Done splitting? [yN] n + continue splitting? [Ycdq?] y diff --git a/d b/d new file mode 100644 examine changes to 'd'? [Ynesfdaq?] y diff -r 75db6a9d0b54 -r a71f2271ed76 tests/test-evolve-obshistory-split.t --- a/tests/test-evolve-obshistory-split.t Tue Jan 22 10:43:44 2019 -0500 +++ b/tests/test-evolve-obshistory-split.t Tue Jan 22 10:46:02 2019 -0500 @@ -38,7 +38,7 @@ > y > y > n - > n + > y > y > y > EOF @@ -58,7 +58,7 @@ examine changes to 'b'? [Ynesfdaq?] n created new head - Done splitting? [yN] n + continue splitting? [Ycdq?] y diff --git a/b b/b new file mode 100644 examine changes to 'b'? [Ynesfdaq?] y diff -r 75db6a9d0b54 -r a71f2271ed76 tests/test-evolve-orphan-split.t --- a/tests/test-evolve-orphan-split.t Tue Jan 22 10:43:44 2019 -0500 +++ b/tests/test-evolve-orphan-split.t Tue Jan 22 10:46:02 2019 -0500 @@ -41,7 +41,7 @@ > y > y > n - > y + > c > EOF 0 files updated, 0 files merged, 3 files removed, 0 files unresolved adding a @@ -59,7 +59,7 @@ examine changes to 'b'? [Ynesfdaq?] n created new head - Done splitting? [yN] y + continue splitting? [Ycdq?] c 1 new orphan changesets $ hg glog @@ -121,7 +121,7 @@ > y > y > y - > y + > c > EOF 0 files updated, 0 files merged, 3 files removed, 0 files unresolved adding a @@ -152,7 +152,7 @@ record change 3/3 to 'c'? [Ynesfdaq?] y created new head - Done splitting? [yN] y + continue splitting? [Ycdq?] c 1 new orphan changesets $ hg glog diff -r 75db6a9d0b54 -r a71f2271ed76 tests/test-evolve-phase-divergence.t --- a/tests/test-evolve-phase-divergence.t Tue Jan 22 10:43:44 2019 -0500 +++ b/tests/test-evolve-phase-divergence.t Tue Jan 22 10:46:02 2019 -0500 @@ -893,7 +893,7 @@ 1 new phase-divergent changesets $ hg glog -r f3794e5a91dc:: - @ 24:e450d05b7d27 added g + @ 24:390acb97e50a added f | () draft | o 23:428f7900a969 added g | | () public @@ -906,14 +906,14 @@ ~ $ hg evolve --list - e450d05b7d27: added g + 390acb97e50a: added f phase-divergent: 21ae52e414e6 (immutable precursor) phase-divergent: 428f7900a969 (immutable precursor) Resolving phase divergence using `hg evolve` $ hg evolve --phase-divergent --all - recreate:[24] added g + recreate:[24] added f atop:[23] added g rebasing to destination parent: 21ae52e414e6 computing new diff diff -r 75db6a9d0b54 -r a71f2271ed76 tests/test-evolve-stop-orphan.t --- a/tests/test-evolve-stop-orphan.t Tue Jan 22 10:43:44 2019 -0500 +++ b/tests/test-evolve-stop-orphan.t Tue Jan 22 10:46:02 2019 -0500 @@ -109,9 +109,8 @@ Checking working dir $ hg status Checking for incomplete mergestate - $ ls .hg/merge - ls: cannot access .?\.hg/merge.?: No such file or directory (re) - [2] + $ ls .hg/ | grep merge + [1] Checking graph $ hg glog diff -r 75db6a9d0b54 -r a71f2271ed76 tests/test-evolve-templates.t --- a/tests/test-evolve-templates.t Tue Jan 22 10:43:44 2019 -0500 +++ b/tests/test-evolve-templates.t Tue Jan 22 10:46:02 2019 -0500 @@ -272,7 +272,7 @@ > y > y > n - > n + > y > y > y > EOF @@ -292,7 +292,7 @@ examine changes to 'b'? [Ynesfdaq?] n created new head - Done splitting? [yN] n + continue splitting? [Ycdq?] y diff --git a/b b/b new file mode 100644 examine changes to 'b'? [Ynesfdaq?] y diff -r 75db6a9d0b54 -r a71f2271ed76 tests/test-evolve-topic.t --- a/tests/test-evolve-topic.t Tue Jan 22 10:43:44 2019 -0500 +++ b/tests/test-evolve-topic.t Tue Jan 22 10:46:02 2019 -0500 @@ -257,7 +257,7 @@ $ hg topic -r 070c5573d8f9 bar 4 new orphan changesets - changed topic on 1 changes + changed topic on 1 changesets to "bar" $ hg up 16d6f664b17c switching to topic bar 2 files updated, 0 files merged, 0 files removed, 0 files unresolved @@ -381,3 +381,63 @@ $ hg prev 0 files updated, 0 files merged, 1 files removed, 0 files unresolved [s3] add eee + +Check stackaliases(s#) works with --continue case also, while evolving: +------------------------------------------------------------------------ + $ hg up 18 + switching to topic bar + 1 files updated, 0 files merged, 0 files removed, 0 files unresolved + $ hg evolve --all + move:[s2] add ggg + atop:[s1] add fff + move:[s3] add hhh + move:[s4] add iii + move:[s5] add jjj + working directory is now at 38a82cbb794a + $ hg up 18 + 0 files updated, 0 files merged, 4 files removed, 0 files unresolved + $ echo "changes in hhh" > hhh + $ hg add hhh + $ hg ci --amend + 4 new orphan changesets + $ hg log -G + @ 26 - {bar} 2c295936ac04 add fff (draft) + | + | * 25 - {bar} 38a82cbb794a add jjj (draft) + | | + | * 24 - {bar} 4a44eba0fdb3 add iii (draft) + | | + | * 23 - {bar} 7acd9ea5d677 add hhh (draft) + | | + | * 22 - {bar} 735c7bd8f133 add ggg (draft) + | | + | x 18 - {bar} 793eb6370b2d add fff (draft) + |/ + o 12 - {foo} 42b49017ff90 add eee (draft) + | + o 10 - {foo} d9cacd156ffc add ddd (draft) + | + o 2 - {foo} cced9bac76e3 add ccc (draft) + | + o 1 - {} a4dbed0837ea add bbb (draft) + | + o 0 - {} 199cc73e9a0b add aaa (draft) + + $ hg evolve --all + move:[s2] add ggg + atop:[s1] add fff + move:[s3] add hhh + merging hhh + warning: conflicts while merging hhh! (edit, then use 'hg resolve --mark') + fix conflicts and see `hg help evolve.interrupted` + [1] + $ echo "resolved hhh" > hhh + $ hg resolve --mark hhh + (no more unresolved files) + continue: hg evolve --continue + $ hg evolve --continue + evolving 23:7acd9ea5d677 "add hhh" + move:[s4] add iii + atop:[s3] add hhh + move:[s5] add jjj + working directory is now at 119e4c126fb2 diff -r 75db6a9d0b54 -r a71f2271ed76 tests/test-evolve.t --- a/tests/test-evolve.t Tue Jan 22 10:43:44 2019 -0500 +++ b/tests/test-evolve.t Tue Jan 22 10:46:02 2019 -0500 @@ -1523,3 +1523,110 @@ x 0:f7ad41964313 added a () + $ cd .. + +Test which shows that orphanmerge evolution can result to crash because of +lastsolved not being updated in case of orphanmerge: +(It will be fixed in next patch) + +Prepare the repo: + $ hg init orphanmergerepo + $ cd orphanmergerepo + $ echo a > a + $ for fn in a b c; do echo foo > $fn; hg ci -Am "added "$fn; done; + adding a + adding b + adding c +Lets create a merge commit so that we can create orhpan merge later: + $ hg up 1 -q + $ echo feature > f + $ hg ci -Am "added feature f" + adding f + created new head + $ hg merge + 1 files updated, 0 files merged, 0 files removed, 0 files unresolved + (branch merge, don't forget to commit) + $ hg ci -m "merge feature branch" + $ glog + @ 4:2c0a98d38026@default(draft) merge feature branch + |\ + | o 3:4c33e511041e@default(draft) added feature f + | | + o | 2:8be98ac1a569@default(draft) added c + |/ + o 1:80e6d2c47cfe@default(draft) added b + | + o 0:f7ad41964313@default(draft) added a + + +Now make the parents of merge commit obsolete to get a orphan merge: + $ hg up 2 -q + $ echo "fixit" > c + $ hg ci --amend -m "updated c" + 1 new orphan changesets + $ hg up 3 -q + $ echo "fixit" > c + $ hg ci --amend -m "updated f" + $ glog + @ 6:086d9bedcd75@default(draft) updated f + | + | o 5:f84f2c548fbc@default(draft) updated c + |/ + | * 4:2c0a98d38026@default(draft) merge feature branch + | |\ + +---x 3:4c33e511041e@default(draft) added feature f + | | + | x 2:8be98ac1a569@default(draft) added c + |/ + o 1:80e6d2c47cfe@default(draft) added b + | + o 0:f7ad41964313@default(draft) added a + + +To check `lastsolved` contain right value after completion of orphan-merge +resolution there should be one more trouble to be evolved; lets create one: + $ hg up 1 -q + $ echo d > d + $ hg ci -Am "added d" + adding c + adding d + created new head + $ echo e > e + $ hg ci -Am "added e" + adding e + $ hg up .^ + 0 files updated, 0 files merged, 1 files removed, 0 files unresolved + $ echo "updated d" >> d + $ hg ci --amend -m "updated d" + 1 new orphan changesets + $ glog + @ 9:7c4d1834c346@default(draft) updated d + | + | * 8:421f7614462a@default(draft) added e + | | + | x 7:afe5acea1990@default(draft) added d + |/ + | o 6:086d9bedcd75@default(draft) updated f + |/ + | o 5:f84f2c548fbc@default(draft) updated c + |/ + | * 4:2c0a98d38026@default(draft) merge feature branch + | |\ + +---x 3:4c33e511041e@default(draft) added feature f + | | + | x 2:8be98ac1a569@default(draft) added c + |/ + o 1:80e6d2c47cfe@default(draft) added b + | + o 0:f7ad41964313@default(draft) added a + +Now we have one orphan merge and one more orphan cset that we just created. +Lets evolve: + $ hg evolve --all --any + move:[4] merge feature branch + atop:[5] updated c + move:[10] merge feature branch + atop:[6] updated f + move:[8] added e + atop:[9] updated d + working directory is now at 7c67cee06242 diff -r 75db6a9d0b54 -r a71f2271ed76 tests/test-fold.t --- a/tests/test-fold.t Tue Jan 22 10:43:44 2019 -0500 +++ b/tests/test-fold.t Tue Jan 22 10:46:02 2019 -0500 @@ -244,5 +244,29 @@ | o 0 - 1ea73414a91b r0 [debugbuilddag] (public) +Test order of proposed commit message + + $ hg fold --exact --hidden -r 4 -r 5 -r 6 + 2 new content-divergent changesets + 3 changesets folded + $ hg log -r tip -T '{desc}' + r4 + + + r5 + + + r6 (no-eol) + $ hg fold --exact --hidden -r 6 -r 4 -r 5 + 3 changesets folded + $ hg log -r tip -T '{desc}' + r4 + + + r5 + + + r6 (no-eol) + $ cd .. diff -r 75db6a9d0b54 -r a71f2271ed76 tests/test-grab.t --- a/tests/test-grab.t Tue Jan 22 10:43:44 2019 -0500 +++ b/tests/test-grab.t Tue Jan 22 10:46:02 2019 -0500 @@ -24,9 +24,9 @@ options: - -r --rev VALUE revision to pick - -c --continue continue interrupted pick - -a --abort abort interrupted pick + -r --rev REV revision to pick + -c --continue continue interrupted pick + -a --abort abort interrupted pick (some details hidden, use --verbose to show complete help) diff -r 75db6a9d0b54 -r a71f2271ed76 tests/test-obsolete-push.t --- a/tests/test-obsolete-push.t Tue Jan 22 10:43:44 2019 -0500 +++ b/tests/test-obsolete-push.t Tue Jan 22 10:46:02 2019 -0500 @@ -4,6 +4,7 @@ > [extensions] > EOF $ echo "evolve=$(echo $(dirname $TESTDIR))/hgext3rd/evolve/" >> $HGRCPATH + $ echo "topic=$(echo $(dirname $TESTDIR))/hgext3rd/topic/" >> $HGRCPATH $ template='{rev}:{node|short}@{branch}({separate("/", obsolete, phase)}) {desc|firstline}\n' $ glog() { @@ -72,12 +73,12 @@ $ hg push -r . pushing to $TESTTMP/source abort: push would publish 1 changesets - (behavior controlled by 'experimental.auto-publish' config) + (* 'experimental.auto-publish' config) (glob) [255] $ hg push pushing to $TESTTMP/source abort: push would publish 1 changesets - (behavior controlled by 'experimental.auto-publish' config) + (* 'experimental.auto-publish' config) (glob) [255] warning behavior @@ -91,3 +92,15 @@ adding manifests adding file changes added 0 changesets with 0 changes to 1 files + +--publish overrides auto-publish + + $ echo d > d + $ hg ci -qAm D d + $ hg push -r . --publish --config experimental.auto-publish=abort + pushing to $TESTTMP/source + searching for changes + adding changesets + adding manifests + adding file changes + added 1 changesets with 1 changes to 1 files diff -r 75db6a9d0b54 -r a71f2271ed76 tests/test-prev-next.t --- a/tests/test-prev-next.t Tue Jan 22 10:43:44 2019 -0500 +++ b/tests/test-prev-next.t Tue Jan 22 10:46:02 2019 -0500 @@ -184,7 +184,7 @@ $ hg amend -m 'added b (2)' 1 new orphan changesets - $ hg next + $ hg next --no-evolve no children (1 unstable changesets to be evolved here, do you want --evolve?) [1] @@ -231,7 +231,7 @@ $ hg am -m 'added b (3)' 2 new orphan changesets - $ hg next + $ hg next --no-evolve no children (2 unstable changesets to be evolved here, do you want --evolve?) [1] @@ -375,6 +375,7 @@ $ hg next --evolve abort: uncommitted changes + (use `hg amend`, `hg revert` or `hg shelve`) [255] $ cd .. @@ -482,3 +483,107 @@ 0 files updated, 0 files merged, 0 files removed, 1 files unresolved use 'hg resolve' to retry unresolved file merges [2] added bar + +Add test which shows that now `next` command does not get confused by split: +---------------------------------------------------------------------------- + $ cd .. + $ mkdir nextconfused + $ cd nextconfused + $ hg init + $ echo firstline > a + $ hg add a + $ hg ci -qm A + $ echo bbbbb > b + $ echo secondline >> a + $ hg add b + $ hg ci -qm B + $ echo ccccc > c + $ hg add c + $ hg ci -qm C + $ hg log -GT "{rev}:{node|short} {desc}\n" + @ 2:fdc998261dcb C + | + o 1:cc0edb0cc2b1 B + | + o 0:cae96ff49c84 A + + $ hg up 1 + 0 files updated, 0 files merged, 1 files removed, 0 files unresolved + $ hg split << EOF + > y + > y + > n + > Y + > y + > y + > EOF + 1 files updated, 0 files merged, 1 files removed, 0 files unresolved + reverting a + adding b + diff --git a/a b/a + 1 hunks, 1 lines changed + examine changes to 'a'? [Ynesfdaq?] y + + @@ -1,1 +1,2 @@ + firstline + +secondline + record change 1/2 to 'a'? [Ynesfdaq?] y + + diff --git a/b b/b + new file mode 100644 + examine changes to 'b'? [Ynesfdaq?] n + + created new head + continue splitting? [Ycdq?] Y + diff --git a/b b/b + new file mode 100644 + examine changes to 'b'? [Ynesfdaq?] y + + @@ -0,0 +1,1 @@ + +bbbbb + record this change to 'b'? [Ynesfdaq?] y + + no more change to split + 1 new orphan changesets + + $ hg up 3 -q + $ hg log -GT "{rev}:{node|short} {desc}\n" + o 4:279f6cab32b5 B + | + | + | new desc + @ 3:a9f74d07e45c B + | + | + | new desc + | * 2:fdc998261dcb C + | | + | x 1:cc0edb0cc2b1 B + |/ + o 0:cae96ff49c84 A + + $ hg ci --amend -m "B modified" + 1 new orphan changesets + $ hg log -GT "{rev}:{node|short} {desc}\n" + @ 5:64ab03d3110c B modified + | + | * 4:279f6cab32b5 B + | | + | | + | | new desc + | x 3:a9f74d07e45c B + |/ + | + | new desc + | * 2:fdc998261dcb C + | | + | x 1:cc0edb0cc2b1 B + |/ + o 0:cae96ff49c84 A + + $ hg next --evolve << EOF + > q + > EOF + move:[4] B + atop:[5] B modified + working directory now at 1b434459c7e7 diff -r 75db6a9d0b54 -r a71f2271ed76 tests/test-rewind.t --- a/tests/test-rewind.t Tue Jan 22 10:43:44 2019 -0500 +++ b/tests/test-rewind.t Tue Jan 22 10:46:02 2019 -0500 @@ -460,7 +460,7 @@ > y > f > d - > y + > c > EOF 0 files updated, 0 files merged, 2 files removed, 0 files unresolved adding C @@ -478,7 +478,7 @@ examine changes to 'D'? [Ynesfdaq?] d created new head - Done splitting? [yN] y + continue splitting? [Ycdq?] c $ hg log -G @ changeset: 5:9576e80d6851 | tag: tip diff -r 75db6a9d0b54 -r a71f2271ed76 tests/test-split.t --- a/tests/test-split.t Tue Jan 22 10:43:44 2019 -0500 +++ b/tests/test-split.t Tue Jan 22 10:46:02 2019 -0500 @@ -4,6 +4,8 @@ $ . $TESTDIR/testlib/common.sh $ cat >> $HGRCPATH < [alias] + > glog = log -G -T "{rev}:{node|short} {desc|firstline} ({phase})\n" > [defaults] > amend=-d "0 0" > fold=-d "0 0" @@ -55,7 +57,7 @@ > y > y > n - > N + > Y > y > y > EOF @@ -79,7 +81,7 @@ record change 2/2 to '_d'? [Ynesfdaq?] n created new head - Done splitting? [yN] N + continue splitting? [Ycdq?] Y diff --git a/_d b/_d new file mode 100644 examine changes to '_d'? [Ynesfdaq?] y @@ -179,7 +181,7 @@ > y > y > n - > y + > c > EOF 2 files updated, 0 files merged, 2 files removed, 0 files unresolved reverting _b @@ -201,7 +203,7 @@ record change 2/2 to '_c'? [Ynesfdaq?] n created new head - Done splitting? [yN] y + continue splitting? [Ycdq?] c 2 new orphan changesets Stop before splitting the commit completely creates a commit with all the @@ -281,7 +283,7 @@ > y > y > n - > y + > c > EOF (leaving bookmark bookB) 1 files updated, 0 files merged, 1 files removed, 0 files unresolved @@ -302,7 +304,7 @@ examine changes to '_d'? [Ynesfdaq?] n created new head - Done splitting? [yN] y + continue splitting? [Ycdq?] c $ hg log -G -r "3f134f739075::" @ changeset: 16:452a26648478 | bookmark: bookA @@ -366,7 +368,7 @@ [255] Running split with tip revision, specified as unnamed argument - $ hg split . << EOF + $ hg split --rev . << EOF > q > EOF 0 files updated, 0 files merged, 1 files removed, 0 files unresolved @@ -379,11 +381,10 @@ [255] Running split with both unnamed and named revision arguments shows an error msg - $ hg split . --rev .^ << EOF + $ hg split --rev . --rev .^ << EOF > q > EOF abort: more than one revset is given - (use either `hg split ` or `hg split --rev `, not both) [255] Split empty commit (issue5191) @@ -435,7 +436,7 @@ > Y > Y > N - > Y + > c > Y > Y > EOF @@ -454,16 +455,7 @@ new file mode 100644 examine changes to 'celeste'? [Ynesfdaq?] N - Done splitting? [yN] Y - diff --git a/celeste b/celeste - new file mode 100644 - examine changes to 'celeste'? [Ynesfdaq?] Y - - @@ -0,0 +1,1 @@ - +celeste - record this change to 'celeste'? [Ynesfdaq?] Y - - no more change to split + continue splitting? [Ycdq?] c Check that the topic is still here @@ -537,7 +529,7 @@ $ hg split -r . << EOF > Y > N - > N + > Y > Y > EOF 0 files updated, 0 files merged, 2 files removed, 0 files unresolved @@ -551,7 +543,7 @@ new file mode 100644 examine changes to 'SPLIT2'? [Ynesfdaq?] N - Done splitting? [yN] N + continue splitting? [Ycdq?] Y diff --git a/SPLIT2 b/SPLIT2 new file mode 100644 examine changes to 'SPLIT2'? [Ynesfdaq?] Y @@ -651,3 +643,479 @@ date: Thu Jan 01 00:00:00 1970 +0000 summary: split10 + +Check prompt options +-------------------- + +Look at the help (both record and split helps) + + $ hg split -r tip << EOF + > Y + > ? + > d + > ? + > q + > EOF + 0 files updated, 0 files merged, 0 files removed, 0 files unresolved + adding SPLIT3 + adding SPLIT4 + diff --git a/SPLIT3 b/SPLIT3 + new file mode 100644 + examine changes to 'SPLIT3'? [Ynesfdaq?] Y + + diff --git a/SPLIT4 b/SPLIT4 + new file mode 100644 + examine changes to 'SPLIT4'? [Ynesfdaq?] ? + + y - yes, record this change + n - no, skip this change + e - edit this change manually + s - skip remaining changes to this file + f - record remaining changes to this file + d - done, skip remaining changes and files + a - record all changes to all remaining files + q - quit, recording no changes + ? - ? (display help) + examine changes to 'SPLIT4'? [Ynesfdaq?] d + + continue splitting? [Ycdq?] ? + y - yes, continue selection + c - commit, select all remaining changes + d - discard, discard remaining changes + q - quit, abort the split + ? - ?, display help + continue splitting? [Ycdq?] q + transaction abort! + rollback completed + abort: user quit + [255] + +discard some of changeset during split + + $ cat >> $HGRCPATH < [experimental] + > evolution=all + > evolutioncommands= + > EOF + + $ hg export + # HG changeset patch + # User test + # Date 0 0 + # Thu Jan 01 00:00:00 1970 +0000 + # Branch another-branch + # Node ID 56a59faa8af70dc104faa905231731ffece5f18a + # Parent 75695e3e2300d316cc515c4c25bab8b825ef1433 + # EXP-Topic mytopic + split10 + + diff --git a/SPLIT2 b/SPLIT2 + new file mode 100644 + $ hg add SPLIT3 + $ hg amend + 1 new orphan changesets + $ hg export + # HG changeset patch + # User test + # Date 0 0 + # Thu Jan 01 00:00:00 1970 +0000 + # Branch another-branch + # Node ID 3acb634dc68ddb4dea75a9cee982955bc1f3e8cd + # Parent 75695e3e2300d316cc515c4c25bab8b825ef1433 + # EXP-Topic mytopic + split10 + + diff --git a/SPLIT2 b/SPLIT2 + new file mode 100644 + diff --git a/SPLIT3 b/SPLIT3 + new file mode 100644 + $ hg split << EOF + > Y + > d + > d + > EOF + 0 files updated, 0 files merged, 2 files removed, 0 files unresolved + adding SPLIT2 + adding SPLIT3 + diff --git a/SPLIT2 b/SPLIT2 + new file mode 100644 + examine changes to 'SPLIT2'? [Ynesfdaq?] Y + + diff --git a/SPLIT3 b/SPLIT3 + new file mode 100644 + examine changes to 'SPLIT3'? [Ynesfdaq?] d + + continue splitting? [Ycdq?] d + discarding remaining changes + forgetting SPLIT3 + $ hg export + # HG changeset patch + # User test + # Date 0 0 + # Thu Jan 01 00:00:00 1970 +0000 + # Branch another-branch + # Node ID db690d5566962489d65945c90b468b44e0b1507a + # Parent 75695e3e2300d316cc515c4c25bab8b825ef1433 + # EXP-Topic mytopic + split12 + + diff --git a/SPLIT2 b/SPLIT2 + new file mode 100644 + $ hg status + ? SPLIT3 + ? SPLIT4 + ? editor.sh + ? num + +Test restricting the split to a subset of files +----------------------------------------------- + + $ hg add SPLIT3 SPLIT4 + $ hg amend + +Only run on 2 files + +(remaining changes gathered with unmatched one) + + $ hg split SPLIT2 SPLIT3 << EOF + > y + > n + > c + > EOF + 0 files updated, 0 files merged, 3 files removed, 0 files unresolved + adding SPLIT2 + adding SPLIT3 + adding SPLIT4 + diff --git a/SPLIT2 b/SPLIT2 + new file mode 100644 + examine changes to 'SPLIT2'? [Ynesfdaq?] y + + diff --git a/SPLIT3 b/SPLIT3 + new file mode 100644 + examine changes to 'SPLIT3'? [Ynesfdaq?] n + + continue splitting? [Ycdq?] c + $ hg status --change '.~1' + A SPLIT2 + $ hg status --change '.' + A SPLIT3 + A SPLIT4 + $ hg fold --from '.~1' + 2 changesets folded + 0 files updated, 0 files merged, 0 files removed, 0 files unresolved + +(no remaining changes) + + $ hg split SPLIT2 SPLIT3 << EOF + > y + > n + > y + > y + > EOF + 0 files updated, 0 files merged, 3 files removed, 0 files unresolved + adding SPLIT2 + adding SPLIT3 + adding SPLIT4 + diff --git a/SPLIT2 b/SPLIT2 + new file mode 100644 + examine changes to 'SPLIT2'? [Ynesfdaq?] y + + diff --git a/SPLIT3 b/SPLIT3 + new file mode 100644 + examine changes to 'SPLIT3'? [Ynesfdaq?] n + + continue splitting? [Ycdq?] y + diff --git a/SPLIT3 b/SPLIT3 + new file mode 100644 + examine changes to 'SPLIT3'? [Ynesfdaq?] y + + no more change to split + $ hg status --change '.~2' + A SPLIT2 + $ hg status --change '.~1' + A SPLIT3 + $ hg status --change '.' + A SPLIT4 + $ hg fold --from '.~2' + 3 changesets folded + 0 files updated, 0 files merged, 0 files removed, 0 files unresolved + +(only all matched selected) + + $ hg split SPLIT2 SPLIT3 << EOF + > y + > y + > EOF + 0 files updated, 0 files merged, 3 files removed, 0 files unresolved + adding SPLIT2 + adding SPLIT3 + adding SPLIT4 + diff --git a/SPLIT2 b/SPLIT2 + new file mode 100644 + examine changes to 'SPLIT2'? [Ynesfdaq?] y + + diff --git a/SPLIT3 b/SPLIT3 + new file mode 100644 + examine changes to 'SPLIT3'? [Ynesfdaq?] y + + no more change to split + $ hg status --change '.~1' + A SPLIT2 + A SPLIT3 + $ hg status --change '.' + A SPLIT4 + $ hg fold --from '.~1' + 2 changesets folded + 0 files updated, 0 files merged, 0 files removed, 0 files unresolved + +Check that discard does not alter unmatched files + + $ hg split SPLIT2 SPLIT3 << EOF + > y + > n + > d + > EOF + 0 files updated, 0 files merged, 3 files removed, 0 files unresolved + adding SPLIT2 + adding SPLIT3 + adding SPLIT4 + diff --git a/SPLIT2 b/SPLIT2 + new file mode 100644 + examine changes to 'SPLIT2'? [Ynesfdaq?] y + + diff --git a/SPLIT3 b/SPLIT3 + new file mode 100644 + examine changes to 'SPLIT3'? [Ynesfdaq?] n + + continue splitting? [Ycdq?] d + discarding remaining changes + no more change to split + $ hg status --change '.~1' + A SPLIT2 + $ hg status --change '.' + A SPLIT4 + $ hg fold --from '.~1' + 2 changesets folded + 0 files updated, 0 files merged, 0 files removed, 0 files unresolved + $ hg add SPLIT3 + $ hg amend + +Non interractive run +-------------------- + +No patterns + + $ hg split --no-interactive + 0 files updated, 0 files merged, 3 files removed, 0 files unresolved + adding SPLIT2 + adding SPLIT3 + adding SPLIT4 + abort: no files of directories specified + (do you want --interactive) + [255] + +Selecting unrelated file +(should we abort?) + + $ hg split --no-interactive SPLIT1 + 0 files updated, 0 files merged, 3 files removed, 0 files unresolved + adding SPLIT2 + adding SPLIT3 + adding SPLIT4 + no more change to split + $ hg status --change '.' + A SPLIT2 + A SPLIT3 + A SPLIT4 + +Selecting one file + + $ hg split --no-interactive SPLIT2 + 0 files updated, 0 files merged, 3 files removed, 0 files unresolved + adding SPLIT2 + adding SPLIT3 + adding SPLIT4 + no more change to split + $ hg status --change '.~1' + A SPLIT2 + $ hg status --change '.' + A SPLIT3 + A SPLIT4 + $ hg fold --from '.~1' + 2 changesets folded + 0 files updated, 0 files merged, 0 files removed, 0 files unresolved + +Selecting two files + + $ hg split --no-interactive SPLIT2 SPLIT3 + 0 files updated, 0 files merged, 3 files removed, 0 files unresolved + adding SPLIT2 + adding SPLIT3 + adding SPLIT4 + no more change to split + $ hg status --change '.~1' + A SPLIT2 + A SPLIT3 + $ hg status --change '.' + A SPLIT4 + $ hg fold --from '.~1' + 2 changesets folded + 0 files updated, 0 files merged, 0 files removed, 0 files unresolved + +Selecting all files +(should we abort?) + + $ hg split --no-interactive . + 0 files updated, 0 files merged, 3 files removed, 0 files unresolved + adding SPLIT2 + adding SPLIT3 + adding SPLIT4 + no more change to split + $ hg status --change '.' + A SPLIT2 + A SPLIT3 + A SPLIT4 + + $ cd .. + +Testing that `hg evolve` choose right destination after split && prune (issue5686) +-------------------------------------------------------------------------------- + +Prepare the repository: + $ hg init issue5686 + $ cd issue5686 + $ echo p > p + $ hg ci -Amp + adding p + + $ for ch in a b; do echo $ch > $ch; done; + $ hg ci -Am "added a and b" + adding a + adding b + $ echo c > c + $ hg ci -Amc + adding c + $ hg glog + @ 2:ab6ca3ebca74 c (draft) + | + o 1:79f47e067e66 added a and b (draft) + | + o 0:a5a1faba8d26 p (draft) + + +To create commits with the number of split + $ echo 0 > num + $ cat > editor.sh << '__EOF__' + > NUM=$(cat num) + > NUM=`expr "$NUM" + 1` + > echo "$NUM" > num + > echo "split$NUM" > "$1" + > __EOF__ + $ export HGEDITOR="\"sh\" \"editor.sh\"" + +Splitting the revision 1 to SPLIT1 and SPLIT2 which contains file a and b resp: + $ hg split -r 1 < y + > y + > n + > y + > y + > y + > EOF + 0 files updated, 0 files merged, 3 files removed, 0 files unresolved + adding a + adding b + diff --git a/a b/a + new file mode 100644 + examine changes to 'a'? [Ynesfdaq?] y + + @@ -0,0 +1,1 @@ + +a + record change 1/2 to 'a'? [Ynesfdaq?] y + + diff --git a/b b/b + new file mode 100644 + examine changes to 'b'? [Ynesfdaq?] n + + created new head + (consider using topic for lightweight branches. See 'hg help topic') + continue splitting? [Ycdq?] y + diff --git a/b b/b + new file mode 100644 + examine changes to 'b'? [Ynesfdaq?] y + + @@ -0,0 +1,1 @@ + +b + record this change to 'b'? [Ynesfdaq?] y + + no more change to split + 1 new orphan changesets + + $ hg glog -p + @ 4:5cf253fa63fa split2 (draft) + | diff --git a/b b/b + | new file mode 100644 + | --- /dev/null + | +++ b/b + | @@ -0,0 +1,1 @@ + | +b + | + o 3:88437e073cd4 split1 (draft) + | diff --git a/a b/a + | new file mode 100644 + | --- /dev/null + | +++ b/a + | @@ -0,0 +1,1 @@ + | +a + | + | * 2:ab6ca3ebca74 c (draft) + | | diff --git a/c b/c + | | new file mode 100644 + | | --- /dev/null + | | +++ b/c + | | @@ -0,0 +1,1 @@ + | | +c + | | + | x 1:79f47e067e66 added a and b (draft) + |/ diff --git a/a b/a + | new file mode 100644 + | --- /dev/null + | +++ b/a + | @@ -0,0 +1,1 @@ + | +a + | diff --git a/b b/b + | new file mode 100644 + | --- /dev/null + | +++ b/b + | @@ -0,0 +1,1 @@ + | +b + | + o 0:a5a1faba8d26 p (draft) + diff --git a/p b/p + new file mode 100644 + --- /dev/null + +++ b/p + @@ -0,0 +1,1 @@ + +p + +Now if we prune revision 4 the expected destination of orphan cset 2 is 3. Lets +check evolve does as expected: + +The fix is 4.9 only, so we simply cheat on older version + +Pruning revision 4 (current one): + $ hg prune . + 0 files updated, 0 files merged, 1 files removed, 0 files unresolved + working directory now at 88437e073cd4 + 1 changesets pruned + $ hg rebase -r 2 -d 3 --config extensions.rebase= + rebasing 2:ab6ca3ebca74 "c" + $ hg up + 1 files updated, 0 files merged, 0 files removed, 0 files unresolved + $ hg glog + @ 5:21a63bd6ee88 c (draft) + | + o 3:88437e073cd4 split1 (draft) + | + o 0:a5a1faba8d26 p (draft) + diff -r 75db6a9d0b54 -r a71f2271ed76 tests/test-stack-branch.t --- a/tests/test-stack-branch.t Tue Jan 22 10:43:44 2019 -0500 +++ b/tests/test-stack-branch.t Tue Jan 22 10:46:02 2019 -0500 @@ -309,7 +309,7 @@ ---------------------------------------------------- $ hg topic --rev b4::b5 sometopic - changed topic on 2 changes + changed topic on 2 changesets to "sometopic" $ hg stack ### target: foo (branch) s3$ c_f (unstable) diff -r 75db6a9d0b54 -r a71f2271ed76 tests/test-topic-change.t --- a/tests/test-topic-change.t Tue Jan 22 10:43:44 2019 -0500 +++ b/tests/test-topic-change.t Tue Jan 22 10:46:02 2019 -0500 @@ -51,7 +51,7 @@ Clearing topic from revision without topic $ hg topic -r . --clear - changed topic on 0 changes + cleared topic on 0 changesets Clearing current topic when no active topic is not error @@ -62,7 +62,7 @@ $ hg topic -r 0:: foo switching to topic foo - changed topic on 8 changes + changed topic on 8 changesets to "foo" $ hg glog @ 15:05095f607171 {foo} | Added h () @@ -100,7 +100,7 @@ $ hg topic -r abcedffeae90:: bar switching to topic bar - changed topic on 4 changes + changed topic on 4 changesets to "bar" $ hg glog @ 19:d7d36e193ea7 {bar} | Added h () @@ -139,7 +139,7 @@ $ hg topic -r . --current active topic 'foobar' grew its first changeset (see 'hg help topics' for more information) - changed topic on 1 changes + changed topic on 1 changesets to "foobar" $ hg glog -r . @ 20:c2d6b7df5dcf {foobar} | Added h () @@ -149,7 +149,7 @@ $ hg topic -r 9::10 --current 5 new orphan changesets - changed topic on 2 changes + changed topic on 2 changesets to "foobar" $ hg glog o 22:1b88140feefe {foobar} | Added c () @@ -302,7 +302,7 @@ $ hg topic -r . --clear clearing empty topic "watwat" active topic 'watwat' is now empty - changed topic on 1 changes + cleared topic on 1 changesets $ hg glog @ 31:c48d6d71b2d9 {} @@ -335,7 +335,7 @@ $ hg bookmark bookboo $ hg topic -r . movebook switching to topic movebook - changed topic on 1 changes + changed topic on 1 changesets to "movebook" $ hg glog @ 32:1b83d11095b9 {movebook} | Added h (book bookboo) @@ -376,7 +376,7 @@ $ hg topic -r . watwat switching to topic watwat 1 new orphan changesets - changed topic on 1 changes + changed topic on 1 changesets to "watwat" $ hg glog @ 33:894983f69e69 {watwat} diff -r 75db6a9d0b54 -r a71f2271ed76 tests/test-topic-stack-complex.t --- a/tests/test-topic-stack-complex.t Tue Jan 22 10:43:44 2019 -0500 +++ b/tests/test-topic-stack-complex.t Tue Jan 22 10:46:02 2019 -0500 @@ -63,7 +63,7 @@ > y > y > n - > y + > c > EOF 0 files updated, 0 files merged, 2 files removed, 0 files unresolved adding c @@ -80,7 +80,7 @@ new file mode 100644 examine changes to 'd'? [Ynesfdaq?] n - Done splitting? [yN] y + continue splitting? [Ycdq?] c 1 new orphan changesets $ hg stack diff -r 75db6a9d0b54 -r a71f2271ed76 tests/test-topic-stack.t --- a/tests/test-topic-stack.t Tue Jan 22 10:43:44 2019 -0500 +++ b/tests/test-topic-stack.t Tue Jan 22 10:46:02 2019 -0500 @@ -229,8 +229,52 @@ s1: c_c s0^ c_b (base) +merge case (displaying info about external) +------------------------------------------- + + $ hg up default + 0 files updated, 0 files merged, 4 files removed, 0 files unresolved + $ hg topics zzz + marked working directory as topic: zzz + $ echo zzz > zzz + $ hg add zzz + $ hg commit -m zzz_a + active topic 'zzz' grew its first changeset + (see 'hg help topics' for more information) + $ hg merge foo + 4 files updated, 0 files merged, 0 files removed, 0 files unresolved + (branch merge, don't forget to commit) + $ hg commit -m "merged foo" + +stack -m display data about child + + $ hg stack foo + ### topic: foo + ### target: default (branch) + s4: c_f + s3: c_e + s2: c_d + s1: c_c + s0^ c_b (base) + + $ hg stack foo --children + ### topic: foo + ### target: default (branch) + s4: c_f (external-children) + s3: c_e + s2: c_d + s1: c_c + s0^ c_b (base) + error case, nothing to list + $ hg strip --config extensions.strip= t1 --no-backup + 0 files updated, 0 files merged, 5 files removed, 0 files unresolved + + $ hg up foo + switching to topic foo + 4 files updated, 0 files merged, 0 files removed, 0 files unresolved + $ hg topic --clear $ hg stack ### target: default (branch) @@ -495,7 +539,7 @@ $ hg topic foobar -r 'desc(c_e) + desc(c_D)' switching to topic foobar 4 new orphan changesets - changed topic on 2 changes + changed topic on 2 changesets to "foobar" $ hg log -G @ 17 default {foobar} draft c_D | @@ -850,7 +894,7 @@ > y > y > n - > y + > c > EOF 0 files updated, 0 files merged, 2 files removed, 0 files unresolved adding Z @@ -867,7 +911,7 @@ new file mode 100644 examine changes to 'ggg'? [Ynesfdaq?] n - Done splitting? [yN] y + continue splitting? [Ycdq?] c $ hg --config extensions.evolve= obslog --all o dde94df880e9 (21) c_G diff -r 75db6a9d0b54 -r a71f2271ed76 tests/test-topic-tutorial.t --- a/tests/test-topic-tutorial.t Tue Jan 22 10:43:44 2019 -0500 +++ b/tests/test-topic-tutorial.t Tue Jan 22 10:46:02 2019 -0500 @@ -1368,8 +1368,8 @@ to do that rebase by hand.: $ hg next --evolve - move:[14] Adding saw - atop:[18] Adding hammer to the shopping list + move:[s2] Adding saw + atop:[s1] Adding hammer to the shopping list working directory now at d5c51ee5762a $ hg stack @@ -1383,8 +1383,8 @@ One more to go: $ hg next --evolve - move:[15] Adding drill - atop:[19] Adding saw + move:[s3] Adding drill + atop:[s2] Adding saw working directory now at bae3758e46bf $ hg stack diff -r 75db6a9d0b54 -r a71f2271ed76 tests/test-topic.t --- a/tests/test-topic.t Tue Jan 22 10:43:44 2019 -0500 +++ b/tests/test-topic.t Tue Jan 22 10:46:02 2019 -0500 @@ -819,7 +819,40 @@ s0^ Add file delta (base current) $ hg topics --age - * fran (1970-01-01 by test) + * fran (1970-01-01 by test, 1 changesets) + + $ cd .. + +Relation subscript in revsets +============================= + + $ hg init more-than-one-commit-per-topic + $ cd more-than-one-commit-per-topic + $ cat > .hg/hgrc << EOF + > [phases] + > publish=false + > EOF + + $ echo 0 > foo + $ hg ci -qAm 0 + $ hg topic featureA + marked working directory as topic: featureA + $ echo 1 > foo + $ hg ci -qm 1 + $ echo 2 > foo + $ hg ci -qm 2 + $ echo 3 > foo + $ hg ci -qm 3 + $ hg topic --clear + $ echo 4 > foo + $ hg ci -qm 4 + + $ tlog 'all()' + 0: + 1: featureA + 2: featureA + 3: featureA + 4: $ cd .. @@ -863,7 +896,7 @@ $ hg topic topic1970 --rev 0 switching to topic topic1970 - changed topic on 1 changes + changed topic on 1 changesets to "topic1970" $ hg add b $ hg topic topic1990 @@ -903,18 +936,23 @@ * topic2010 (1 changesets) $ hg topics --age - * topic2010 (2010-01-01 by bar) - topic1990 (1990-01-01 by foo) - topic1970 (1970-01-01 by test) + * topic2010 (2010-01-01 by bar, 1 changesets) + topic1990 (1990-01-01 by foo, 1 changesets) + topic1970 (1970-01-01 by test, 1 changesets) + + $ hg topics --age --verbose + * topic2010 (2010-01-01 by bar, on branch: default, 1 changesets) + topic1990 (1990-01-01 by foo, on branch: default, 1 changesets) + topic1970 (1970-01-01 by test, on branch: default, 1 changesets) $ hg up topic1970 switching to topic topic1970 0 files updated, 0 files merged, 2 files removed, 0 files unresolved $ hg topics --age - topic2010 (2010-01-01 by bar) - topic1990 (1990-01-01 by foo) - * topic1970 (1970-01-01 by test) + topic2010 (2010-01-01 by bar, 1 changesets) + topic1990 (1990-01-01 by foo, 1 changesets) + * topic1970 (1970-01-01 by test, 1 changesets) $ hg topics --age random abort: cannot use --age while setting a topic diff -r 75db6a9d0b54 -r a71f2271ed76 tests/test-tutorial.t --- a/tests/test-tutorial.t Tue Jan 22 10:43:44 2019 -0500 +++ b/tests/test-tutorial.t Tue Jan 22 10:46:02 2019 -0500 @@ -934,9 +934,9 @@ options ([+] can be repeated): -a --all uncommit all changes when no arguments given - -r --rev VALUE revert commit content to REV instead + -r --rev REV revert commit content to REV instead --revert discard working directory changes after uncommit - -n --note VALUE store a note on uncommit + -n --note TEXT store a note on uncommit -I --include PATTERN [+] include names matching the given patterns -X --exclude PATTERN [+] exclude names matching the given patterns -m --message TEXT use text as commit message @@ -973,16 +973,16 @@ options ([+] can be repeated): - -r --rev VALUE [+] revision to fold - --exact only fold specified revisions - --from fold revisions linearly to working copy parent - -n --note VALUE store a note on fold - -m --message TEXT use text as commit message - -l --logfile FILE read commit message from file - -d --date DATE record the specified date as commit date - -u --user USER record the specified user as committer - -D --current-date record the current date as commit date - -U --current-user record the current user as committer + -r --rev REV [+] revision to fold + --exact only fold specified revisions + --from fold revisions linearly to working copy parent + -n --note TEXT store a note on fold + -m --message TEXT use text as commit message + -l --logfile FILE read commit message from file + -d --date DATE record the specified date as commit date + -u --user USER record the specified user as committer + -D --current-date record the current date as commit date + -U --current-user record the current user as committer (some details hidden, use --verbose to show complete help)