Mercurial > evolve
changeset 4866:2a500c5e293b mercurial-4.7
test-compat: merge mercurial-4.8 into mercurial-4.7
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/.hg-format-source Fri Sep 27 13:03:18 2019 +0200 @@ -0,0 +1,1 @@ +{"pattern": "glob:hgext3rd/**/*.py", "tool": "byteify-strings"}
--- a/.hgtags Mon Jul 29 14:43:15 2019 +0200 +++ b/.hgtags Fri Sep 27 13:03:18 2019 +0200 @@ -80,3 +80,4 @@ 33c617626fd90a0a00e831b4762f64fecb609317 8.5.0 05c9dcf5512ed77490a35b4d6b1c3fe860259f48 8.5.1 756db65030c64b22836fe236d1db3b95477e3ef7 9.0.0 +6f37fdad7ac123ca0a76872ac4639bd1f3c248f7 9.1.0
--- a/CHANGELOG Mon Jul 29 14:43:15 2019 +0200 +++ b/CHANGELOG Fri Sep 27 13:03:18 2019 +0200 @@ -1,27 +1,53 @@ Changelog ========= -9.1.0 - in progress +9.2.0 -- 2019-09-28 +------------------- + + * evolve: check that relocating makes sense in _solvedivergent() (issue5958) + * evolve: test that target is not orig in _solveunstable() (issue6097) + * fold: check allowdivergence before folding obsolete changesets (issue5817) + * obslog: correct spacing of patch output with word-diff=yes (issue6175) + * stack: make sure to preserve dependencies, fixes certain complex cases + * prune: improve documentation for `--pair` + + * python3: beta support for Python 3.6+ + (thanks to ludovicchabant, martinvonz and rgomes for their hard work) + * prune: clarify error message when no revision were passed, + * evolve: avoid possible race conditions bu locking earlier + * abort: add support for `evolve` and `pick` to `hg abort` (hg-5.1+) + * rewind: add --keep flag to preserve working copy + +9.1.0 -- 2019-07-29 ------------------- - * evolve: use the same wording as core in case of unresolved conflict - * evolve: minor output message improvements - * evolve: improve `hg evolve --all` behavior when "." is obsolete - * topic: fix confusion in branch heads checking logic - * touch: now works on merge commit too - * rewind: fix behavior for merge commit - * fold: allow fold with merge commit - * metaedit: now also operates on merge commit + * compatibility with upcoming Mercurial 5.1, + + * pick: no longer forget file in case of conflict (issue6037), + * pick: properly report and cleanup "unfinished state", + + * prune: don't update wcp if pruned revision are unrelated (issue6137), + * prune: spell --successor flag without any unnecessary shortcuts, + * prune: update to the successor of wdir also with --pair/--biject (issue6142) + + * evolve: properly prune changeset with no change in case of conflict (issue5967), + * evolve: use the same wording as core in case of unresolved conflict, + * evolve: minor output message improvements, + * evolve: improve `hg evolve --all` behavior when "." is obsolete, -9.0.1 - in progress -------------------- + * touch: detect resulting divergence in more cases (issue6107), + * touch: now works on merge commit too, + + * rewind: fix behavior for merge commit, + + * fold: allow fold with merge commit - * pick: no longer forget file in case of conflict (issue6037) - * pick: properly report and cleanup "unfinished state" - * prune: don't update wcp if pruned revision are unrelated (issue6137) - * prune: spell --successor flag without any unnecessary shortcuts - * evolve: properly prune changeset with no change in case of conflict (issue5967) - * touch: detect resulting divergence in more cases (issue6107) + * metaedit: now also operates on merge commit. + +(topic 0.16.0) + + * topic: fix confusion in branch heads checking logic. + 9.0.0 -- 2019-06-06 -------------------
--- a/MANIFEST.in Mon Jul 29 14:43:15 2019 +0200 +++ b/MANIFEST.in Fri Sep 27 13:03:18 2019 +0200 @@ -1,4 +1,5 @@ exclude contrib +exclude .hg-format-source recursive-exclude contrib * exclude hgext3rd/evolve/hack recursive-exclude hgext3rd/evolve/hack *
--- a/README Mon Jul 29 14:43:15 2019 +0200 +++ b/README Fri Sep 27 13:03:18 2019 +0200 @@ -96,6 +96,15 @@ needed. During the upstreaming process, we can use this clearer picture to clean up the code and upgrade it to an appropriate quality for Mercurial core. +Python 3 support +================ + +Mercurial announced beta support for Python 3 starting with its 5.0 release. +Since 9.1.0, ``evolve`` has beta support for Python 3.6+. + +Support will stay in beta while Mercurial's support for Python 3 remains in +beta and until it is a bit more battle-tested. + How to Contribute ================= @@ -145,3 +154,23 @@ test output change from a changeset in core should adds the following line to their description: CORE-TEST-OUTPUT-UPDATE: <CORE-NODE-ID> + + +Format-source config +==================== + +Format source helps smooth out the pain of merging after auto-formatting. +Follow the instructions for install here: + +.. _`format-source`: https://bitbucket.org/octobus/format-source + +Then update both your global and repo config files:: + + $ hg config -l # add the lines below + [extensions] + formatsource = + + [format-source] + byteify-strings = python3 ~/workspace/octobus/mercurial-devel/contrib/byteify-strings.py --dictiter --treat-as-kwargs kwargs opts commitopts TROUBLES --allow-attr-methods + byteify-strings:mode.input = file + byteify-strings:mode.output = pipe
--- a/contrib/hammerclient.py Mon Jul 29 14:43:15 2019 +0200 +++ b/contrib/hammerclient.py Fri Sep 27 13:03:18 2019 +0200 @@ -5,7 +5,7 @@ if len(sys.argv) < 2: execname = os.path.basename(sys.argv[0]) - print >> sys.stderr, "usage: %s CLIENT_ID" % execname + sys.stderr.write("usage: %s CLIENT_ID\n" % execname) client_id = sys.argv[1]
--- a/debian/changelog Mon Jul 29 14:43:15 2019 +0200 +++ b/debian/changelog Fri Sep 27 13:03:18 2019 +0200 @@ -1,3 +1,15 @@ +mercurial-evolve (9.2.0-1) unstable; urgency=medium + + * new upstream release + + -- Pierre-Yves David <pierre-yves.david@ens-lyon.org> Sat, 28 Sep 2019 12:49:41 +0200 + +mercurial-evolve (9.1.0-1) unstable; urgency=medium + + * new upstream release + + -- Pierre-Yves David <pierre-yves.david@ens-lyon.org> Mon, 29 Jul 2019 16:46:26 +0200 + mercurial-evolve (9.0.0-1) unstable; urgency=medium * new upstream release
--- a/docs/test2rst.py Mon Jul 29 14:43:15 2019 +0200 +++ b/docs/test2rst.py Fri Sep 27 13:03:18 2019 +0200 @@ -14,10 +14,10 @@ ''' ignored_patterns = [ - re.compile('^#if'), - re.compile('^#else'), - re.compile('^#endif'), - re.compile('#rest-ignore$'), + re.compile(r'^#if'), + re.compile(r'^#else'), + re.compile(r'^#endif'), + re.compile(r'#rest-ignore$'), ]
--- a/hgext3rd/evolve/__init__.py Mon Jul 29 14:43:15 2019 +0200 +++ b/hgext3rd/evolve/__init__.py Fri Sep 27 13:03:18 2019 +0200 @@ -184,7 +184,7 @@ - "markers" the raw list of changesets. """ -evolutionhelptext = """ +evolutionhelptext = b""" Obsolescence markers make it possible to mark changesets that have been deleted or superset in a new version of the changeset. @@ -258,7 +258,7 @@ registrar.templatekeyword # new in hg-3.8 except ImportError: from . import metadata - raise ImportError('evolve needs Mercurial version %s or above' % + raise ImportError(b'evolve needs Mercurial version %s or above' % min(metadata.testedwith.split())) import mercurial @@ -311,19 +311,19 @@ buglink = metadata.buglink # Flags for enabling optional parts of evolve -commandopt = 'allnewcommands' +commandopt = b'allnewcommands' obsexcmsg = utility.obsexcmsg shorttemplate = utility.shorttemplate -colortable = {'evolve.node': 'yellow', - 'evolve.user': 'green', - 'evolve.rev': 'blue', - 'evolve.short_description': '', - 'evolve.date': 'cyan', - 'evolve.current_rev': 'bold', - 'evolve.verb': '', - 'evolve.operation': 'bold' +colortable = {b'evolve.node': b'yellow', + b'evolve.user': b'green', + b'evolve.rev': b'blue', + b'evolve.short_description': b'', + b'evolve.date': b'cyan', + b'evolve.current_rev': b'bold', + b'evolve.verb': b'', + b'evolve.operation': b'bold' } _pack = struct.pack @@ -359,9 +359,9 @@ templatekeyword = eh.templatekeyword # Configuration -eh.configitem('experimental', 'evolutioncommands', []) -eh.configitem('experimental', 'evolution.allnewcommands', None) -eh.configitem('experimental', 'prunestrip', False) +eh.configitem(b'experimental', b'evolutioncommands', []) +eh.configitem(b'experimental', b'evolution.allnewcommands', None) +eh.configitem(b'experimental', b'prunestrip', False) # pre hg 4.0 compat @@ -394,14 +394,14 @@ def _configureoptions(ui, repo): # If no capabilities are specified, enable everything. # This is so existing evolve users don't need to change their config. - evolveopts = repo.ui.configlist('experimental', 'evolution') + evolveopts = repo.ui.configlist(b'experimental', b'evolution') if not evolveopts: - evolveopts = ['all'] - repo.ui.setconfig('experimental', 'evolution', evolveopts, 'evolve') - if obsolete.isenabled(repo, 'exchange'): + evolveopts = [b'all'] + repo.ui.setconfig(b'experimental', b'evolution', evolveopts, b'evolve') + if obsolete.isenabled(repo, b'exchange'): # if no config explicitly set, disable bundle1 - if not isinstance(repo.ui.config('server', 'bundle1'), bytes): - repo.ui.setconfig('server', 'bundle1', False) + if not isinstance(repo.ui.config(b'server', b'bundle1'), bytes): + repo.ui.setconfig(b'server', b'bundle1', False) class trdescrepo(repo.__class__): @@ -419,19 +419,19 @@ # 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') - evolveopts = ui.configlist('experimental', 'evolution') + evolvecommands = ui.configlist(b'experimental', b'evolutioncommands') + evolveopts = ui.configlist(b'experimental', b'evolution') if evolveopts and (commandopt not in evolveopts - and 'all' not in evolveopts): + and b'all' not in evolveopts): # We build whitelist containing the commands we want to enable whitelist = set() for cmd in evolvecommands: matchingevolvecommands = [e for e in cmdtable.keys() if cmd in e] if not matchingevolvecommands: - raise error.Abort(_('unknown command: %s') % cmd) + raise error.Abort(_(b'unknown command: %s') % cmd) elif len(matchingevolvecommands) > 1: - matchstr = ', '.join(matchingevolvecommands) - msg = _("ambiguous command specification: '%s' matches [%s]") + matchstr = b', '.join(matchingevolvecommands) + msg = _(b"ambiguous command specification: '%s' matches [%s]") raise error.Abort(msg % (cmd, matchstr)) else: whitelist.add(matchingevolvecommands[0]) @@ -462,8 +462,8 @@ @eh.uisetup def setupparentcommand(ui): - _alias, statuscmd = cmdutil.findcmd('status', commands.table) - pstatusopts = [o for o in statuscmd[1] if o[1] != 'rev'] + _alias, statuscmd = cmdutil.findcmd(b'status', commands.table) + pstatusopts = [o for o in statuscmd[1] if o[1] != b'rev'] @eh.command(b'pstatus', pstatusopts) def pstatus(ui, repo, *args, **kwargs): @@ -474,11 +474,11 @@ match the content of the commit that a bare :hg:`amend` will creates. See :hg:`help status` for details.""" - kwargs['rev'] = ['.^'] + kwargs['rev'] = [b'.^'] return statuscmd[0](ui, repo, *args, **kwargs) - _alias, diffcmd = cmdutil.findcmd('diff', commands.table) - pdiffopts = [o for o in diffcmd[1] if o[1] != 'rev'] + _alias, diffcmd = cmdutil.findcmd(b'diff', commands.table) + pdiffopts = [o for o in diffcmd[1] if o[1] != b'rev'] @eh.command(b'pdiff', pdiffopts) def pdiff(ui, repo, *args, **kwargs): @@ -489,15 +489,15 @@ match the content of the commit that a bare :hg:`amend` will creates. See :hg:`help diff` for details.""" - kwargs['rev'] = ['.^'] + kwargs['rev'] = [b'.^'] return diffcmd[0](ui, repo, *args, **kwargs) @eh.uisetup def _installalias(ui): - if ui.config('alias', 'odiff', None) is None: - ui.setconfig('alias', 'odiff', - "diff --hidden --rev 'limit(predecessors(.),1)' --rev .", - 'evolve') + if ui.config(b'alias', b'odiff', None) is None: + ui.setconfig(b'alias', b'odiff', + b"diff --hidden --rev 'limit(predecessors(.),1)' --rev .", + b'evolve') ### Unstable revset symbol @@ -505,11 +505,11 @@ def revsetunstable(repo, subset, x): """Changesets with instabilities. """ - revset.getargs(x, 0, 0, 'unstable takes no arguments') + revset.getargs(x, 0, 0, b'unstable takes no arguments') troubled = set() - troubled.update(getrevs(repo, 'orphan')) - troubled.update(getrevs(repo, 'phasedivergent')) - troubled.update(getrevs(repo, 'contentdivergent')) + troubled.update(getrevs(repo, b'orphan')) + troubled.update(getrevs(repo, b'phasedivergent')) + troubled.update(getrevs(repo, b'contentdivergent')) troubled = revset.baseset(troubled) troubled.sort() # set is non-ordered, enforce order return subset & troubled @@ -617,8 +617,8 @@ def revsetsuspended(repo, subset, x): """Obsolete changesets with non-obsolete descendants. """ - revset.getargs(x, 0, 0, 'suspended takes no arguments') - suspended = revset.baseset(getrevs(repo, 'suspended')) + revset.getargs(x, 0, 0, b'suspended takes no arguments') + suspended = revset.baseset(getrevs(repo, b'suspended')) suspended.sort() return subset & suspended @@ -679,49 +679,49 @@ # This section take care of issue warning to the user when troubles appear def _warnobsoletewc(ui, repo, prevnode=None, wasobs=None): - rev = repo['.'] + rev = repo[b'.'] if not rev.obsolete(): return if rev.node() == prevnode and wasobs: return - msg = _("working directory parent is obsolete! (%s)\n") + msg = _(b"working directory parent is obsolete! (%s)\n") shortnode = node.short(rev.node()) ui.warn(msg % shortnode) # Check that evolve is activated for performance reasons - evolvecommandenabled = any('evolve' in e for e in cmdtable) + evolvecommandenabled = any(b'evolve' in e for e in cmdtable) if ui.quiet or not evolvecommandenabled: return # Show a warning for helping the user to solve the issue reason, successors = obshistory._getobsfateandsuccs(repo, rev.node()) - if reason == 'pruned': - solvemsg = _("use 'hg evolve' to update to its parent successor") - elif reason == 'diverged': - debugcommand = "hg evolve --list --content-divergent" - basemsg = _("%s has diverged, use '%s' to resolve the issue") + if reason == b'pruned': + solvemsg = _(b"use 'hg evolve' to update to its parent successor") + elif reason == b'diverged': + debugcommand = b"hg evolve --list --content-divergent" + basemsg = _(b"%s has diverged, use '%s' to resolve the issue") solvemsg = basemsg % (shortnode, debugcommand) - elif reason == 'superseed': - msg = _("use 'hg evolve' to update to its successor: %s") + elif reason == b'superseed': + msg = _(b"use 'hg evolve' to update to its successor: %s") solvemsg = msg % successors[0] - elif reason == 'superseed_split': - msg = _("use 'hg evolve' to update to its tipmost successor: %s") + elif reason == b'superseed_split': + msg = _(b"use 'hg evolve' to update to its tipmost successor: %s") if len(successors) <= 2: - solvemsg = msg % ", ".join(successors) + solvemsg = msg % b", ".join(successors) else: - firstsuccessors = ", ".join(successors[:2]) + firstsuccessors = b", ".join(successors[:2]) remainingnumber = len(successors) - 2 - successorsmsg = _("%s and %d more") % (firstsuccessors, remainingnumber) + successorsmsg = _(b"%s and %d more") % (firstsuccessors, remainingnumber) solvemsg = msg % successorsmsg else: raise ValueError(reason) - ui.warn("(%s)\n" % solvemsg) + ui.warn(b"(%s)\n" % solvemsg) if util.safehasattr(context, '_filterederror'): # <= hg-4.5 @eh.wrapfunction(context, '_filterederror') @@ -730,36 +730,36 @@ This is extracted in a function to help extensions (eg: evolve) to experiment with various message variants.""" - if repo.filtername.startswith('visible'): + if repo.filtername.startswith(b'visible'): unfilteredrepo = repo.unfiltered() rev = repo[scmutil.revsingle(unfilteredrepo, changeid)] reason, successors = obshistory._getobsfateandsuccs(unfilteredrepo, rev.node()) # Be more precise in case the revision is superseed - if reason == 'superseed': - reason = _("successor: %s") % successors[0] - elif reason == 'superseed_split': + if reason == b'superseed': + reason = _(b"successor: %s") % successors[0] + elif reason == b'superseed_split': if len(successors) <= 2: - reason = _("successors: %s") % ", ".join(successors) + reason = _(b"successors: %s") % b", ".join(successors) else: - firstsuccessors = ", ".join(successors[:2]) + firstsuccessors = b", ".join(successors[:2]) remainingnumber = len(successors) - 2 - successorsmsg = _("%s and %d more") % (firstsuccessors, remainingnumber) - reason = _("successors: %s") % successorsmsg + successorsmsg = _(b"%s and %d more") % (firstsuccessors, remainingnumber) + reason = _(b"successors: %s") % successorsmsg - msg = _("hidden revision '%s'") % changeid - hint = _('use --hidden to access hidden revisions; %s') % reason + msg = _(b"hidden revision '%s'") % changeid + hint = _(b'use --hidden to access hidden revisions; %s') % reason return error.FilteredRepoLookupError(msg, hint=hint) - msg = _("filtered revision '%s' (not in '%s' subset)") + msg = _(b"filtered revision '%s' (not in '%s' subset)") msg %= (changeid, repo.filtername) return error.FilteredRepoLookupError(msg) -@eh.wrapcommand("update") -@eh.wrapcommand("pull") +@eh.wrapcommand(b"update") +@eh.wrapcommand(b"pull") def wrapmayobsoletewc(origfn, ui, repo, *args, **opts): """Warn that the working directory parent is an obsolete changeset""" - ctx = repo['.'] + ctx = repo[b'.'] node = ctx.node() isobs = ctx.obsolete() @@ -774,7 +774,7 @@ lockmod.release(wlock) return res -@eh.wrapcommand("parents") +@eh.wrapcommand(b"parents") def wrapparents(origfn, ui, repo, *args, **opts): res = origfn(ui, repo, *args, **opts) _warnobsoletewc(ui, repo) @@ -787,10 +787,10 @@ try: return orig(repo, *args, **opts) except error.Abort as ex: - hint = _("use 'hg evolve' to get a stable history " - "or --force to ignore warnings") + hint = _(b"use 'hg evolve' to get a stable history " + b"or --force to ignore warnings") if (len(ex.args) >= 1 - and ex.args[0].startswith('push includes ') + and ex.args[0].startswith(b'push includes ') and ex.hint is None): ex.hint = hint raise @@ -799,11 +799,11 @@ evolvestate = state.cmdstate(repo) if evolvestate: # i18n: column positioning for "hg summary" - ui.status(_('evolve: (evolve --continue)\n')) + ui.status(_(b'evolve: (evolve --continue)\n')) @eh.extsetup def obssummarysetup(ui): - cmdutil.summaryhooks.add('evolve', summaryhook) + cmdutil.summaryhooks.add(b'evolve', summaryhook) ##################################################################### ### Old Evolve extension content ### @@ -814,19 +814,19 @@ @eh.uisetup def _installimportobsolete(ui): - entry = cmdutil.findcmd('import', commands.table)[1] - entry[1].append(('', 'obsolete', False, - _('mark the old node as obsoleted by ' - 'the created commit'))) + entry = cmdutil.findcmd(b'import', commands.table)[1] + entry[1].append((b'', b'obsolete', False, + _(b'mark the old node as obsoleted by ' + b'the created commit'))) def _getnodefrompatch(patch, dest): - patchnode = patch.get('nodeid') + patchnode = patch.get(b'nodeid') if patchnode is not None: - dest['node'] = node.bin(patchnode) + dest[b'node'] = node.bin(patchnode) @eh.wrapfunction(mercurial.cmdutil, 'tryimportone') def tryimportone(orig, ui, repo, hunk, parents, opts, *args, **kwargs): - expected = {'node': None} + expected = {b'node': None} if not util.safehasattr(hunk, 'get'): # hg < 4.6 oldextract = patch.extract @@ -843,12 +843,12 @@ _getnodefrompatch(hunk, expected) ret = orig(ui, repo, hunk, parents, opts, *args, **kwargs) created = ret[1] - if (opts['obsolete'] and None not in (created, expected['node']) - and created != expected['node']): - tr = repo.transaction('import-obs') + if (opts[b'obsolete'] and None not in (created, expected[b'node']) + and created != expected[b'node']): + tr = repo.transaction(b'import-obs') try: - metadata = {'user': ui.username()} - repo.obsstore.create(tr, expected['node'], (created,), + metadata = {b'user': ui.username()} + repo.obsstore.create(tr, expected[b'node'], (created,), metadata=metadata) tr.close() finally: @@ -876,62 +876,62 @@ if e is entry: break - synopsis = '(DEPRECATED)' + synopsis = b'(DEPRECATED)' if len(entry) > 2: fn, opts, _syn = entry else: fn, opts, = entry - deprecationwarning = _('%s have been deprecated in favor of %s\n') % ( + deprecationwarning = _(b'%s have been deprecated in favor of %s\n') % ( oldalias, newalias) def newfn(*args, **kwargs): ui = args[0] ui.warn(deprecationwarning) util.checksignature(fn)(*args, **kwargs) - newfn.__doc__ = pycompat.sysstr(deprecationwarning + ' (DEPRECATED)') + newfn.__doc__ = pycompat.sysstr(deprecationwarning + b' (DEPRECATED)') cmdwrapper = eh.command(oldalias, opts, synopsis) cmdwrapper(newfn) @eh.extsetup def deprecatealiases(ui): - _deprecatealias('gup', 'next') - _deprecatealias('gdown', 'previous') + _deprecatealias(b'gup', b'next') + _deprecatealias(b'gdown', b'previous') def _gettopic(ctx): """handle topic fetching with or without the extension""" - return getattr(ctx, 'topic', lambda: '')() + return getattr(ctx, 'topic', lambda: b'')() def _gettopicidx(ctx): """handle topic fetching with or without the extension""" return getattr(ctx, 'topicidx', lambda: None)() def _getcurrenttopic(repo): - return getattr(repo, 'currenttopic', '') + return getattr(repo, 'currenttopic', b'') def _prevupdate(repo, displayer, target, bookmark, dryrun, mergeopt): if dryrun: - repo.ui.write(_('hg update %s;\n') % target) + repo.ui.write(_(b'hg update %s;\n') % target) if bookmark is not None: - repo.ui.write(_('hg bookmark %s -r %s;\n') + repo.ui.write(_(b'hg bookmark %s -r %s;\n') % (bookmark, target)) else: updatecheck = None # --merge is passed, we don't need to care about commands.update.check # config option if mergeopt: - updatecheck = 'none' + updatecheck = b'none' try: ret = hg.updatetotally(repo.ui, repo, target.node(), None, updatecheck=updatecheck) except error.Abort as exc: # replace the hint to mention about --merge option - exc.hint = _('do you want --merge?') + exc.hint = _(b'do you want --merge?') raise if not ret: tr = lock = None try: lock = repo.lock() - tr = repo.transaction('previous') + tr = repo.transaction(b'previous') if bookmark is not None: bmchanges = [(bookmark, target.node())] repo._bookmarks.applychanges(repo, tr, bmchanges) @@ -959,23 +959,23 @@ # issue message for the various case if p1.node() == node.nullid: - repo.ui.warn(_('already at repository root\n')) + repo.ui.warn(_(b'already at repository root\n')) elif not parents and currenttopic: - repo.ui.warn(_('no parent in topic "%s"\n') % currenttopic) - repo.ui.warn(_('(do you want --no-topic)\n')) + repo.ui.warn(_(b'no parent in topic "%s"\n') % currenttopic) + repo.ui.warn(_(b'(do you want --no-topic)\n')) elif len(parents) == 1: target = parents[0] bookmark = None if movebookmark: bookmark = repo._activebookmark else: - header = _("multiple parents, choose one to update:") + header = _(b"multiple parents, choose one to update:") prevs = [p.rev() for p in parents] choosedrev = utility.revselectionprompt(repo.ui, repo, prevs, header) if choosedrev is None: for p in parents: displayer.show(p) - repo.ui.warn(_('multiple parents, explicitly update to one\n')) + repo.ui.warn(_(b'multiple parents, explicitly update to one\n')) else: target = repo[choosedrev] return target, bookmark @@ -1003,13 +1003,13 @@ wkctx = repo[None] wparents = wkctx.parents() if len(wparents) != 1: - raise error.Abort(_('merge in progress')) + raise error.Abort(_(b'merge in progress')) if not mergeopt: # we only skip the check if noconflict is set - if ui.config('commands', 'update.check') == 'noconflict': + if ui.config(b'commands', b'update.check') == b'noconflict': pass else: - cmdutil.bailifchanged(repo, hint=_('do you want --merge?')) + cmdutil.bailifchanged(repo, hint=_(b'do you want --merge?')) topic = not opts.get("no_topic", False) hastopic = bool(_getcurrenttopic(repo)) @@ -1018,16 +1018,16 @@ if topic and hastopic: template = utility.stacktemplate - displayer = compat.changesetdisplayer(ui, repo, {'template': template}) + displayer = compat.changesetdisplayer(ui, repo, {b'template': template}) target, bookmark = _findprevtarget(repo, displayer, opts.get('move_bookmark'), topic) if target is not None: - backup = repo.ui.backupconfig('_internal', 'keep-topic') + backup = repo.ui.backupconfig(b'_internal', b'keep-topic') try: if topic and _getcurrenttopic(repo) != _gettopic(target): - repo.ui.setconfig('_internal', 'keep-topic', 'yes', - source='topic-extension') + repo.ui.setconfig(b'_internal', b'keep-topic', b'yes', + source=b'topic-extension') _prevupdate(repo, displayer, target, bookmark, dryrunopt, mergeopt) finally: @@ -1065,7 +1065,7 @@ wkctx = repo[None] wparents = wkctx.parents() if len(wparents) != 1: - raise error.Abort(_('merge in progress')) + raise error.Abort(_(b'merge in progress')) children = [ctx for ctx in wparents[0].children() if not ctx.obsolete()] topic = _getcurrenttopic(repo) @@ -1076,11 +1076,11 @@ children = [ctx for ctx in children if ctx not in filtered] template = utility.stacktemplate opts['stacktemplate'] = True - displayer = compat.changesetdisplayer(ui, repo, {'template': template}) + displayer = compat.changesetdisplayer(ui, repo, {b'template': template}) # check if we need to evolve while updating to the next child revision needevolve = False - aspchildren = evolvecmd._aspiringchildren(repo, [repo['.'].rev()]) + aspchildren = evolvecmd._aspiringchildren(repo, [repo[b'.'].rev()]) if topic: filtered.update(repo[c] for c in aspchildren if repo[c].topic() != topic) @@ -1100,53 +1100,53 @@ # 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`') + hint = _(b'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': + if ui.config(b'commands', b'update.check') == b'noconflict': pass else: - cmdutil.bailifchanged(repo, hint=_('do you want --merge?')) + cmdutil.bailifchanged(repo, hint=_(b'do you want --merge?')) if len(children) == 1: c = children[0] return _updatetonext(ui, repo, c, displayer, opts) elif children: - cheader = _("ambiguous next changeset, choose one to update:") + cheader = _(b"ambiguous next changeset, choose one to update:") crevs = [c.rev() for c in children] choosedrev = utility.revselectionprompt(ui, repo, crevs, cheader) if choosedrev is None: - ui.warn(_("ambiguous next changeset:\n")) + ui.warn(_(b"ambiguous next changeset:\n")) for c in children: displayer.show(c) - ui.warn(_("explicitly update to one of them\n")) + ui.warn(_(b"explicitly update to one of them\n")) return 1 else: return _updatetonext(ui, repo, repo[choosedrev], displayer, opts) else: if not opts['evolve'] or not aspchildren: if filtered: - ui.warn(_('no children on topic "%s"\n') % topic) - ui.warn(_('do you want --no-topic\n')) + ui.warn(_(b'no children on topic "%s"\n') % topic) + ui.warn(_(b'do you want --no-topic\n')) else: - ui.warn(_('no children\n')) + ui.warn(_(b'no children\n')) if aspchildren: - msg = _('(%i unstable changesets to be evolved here, ' - 'do you want --evolve?)\n') + msg = _(b'(%i unstable changesets to be evolved here, ' + b'do you want --evolve?)\n') ui.warn(msg % len(aspchildren)) return 1 elif len(aspchildren) > 1: - cheader = _("ambiguous next (unstable) changeset, choose one to" - " evolve and update:") + cheader = _(b"ambiguous next (unstable) changeset, choose one to" + b" evolve and update:") choosedrev = utility.revselectionprompt(ui, repo, aspchildren, cheader) if choosedrev is None: - ui.warn(_("ambiguous next (unstable) changeset:\n")) + ui.warn(_(b"ambiguous next (unstable) changeset:\n")) for c in aspchildren: displayer.show(repo[c]) - ui.warn(_("(run 'hg evolve --rev REV' on one of them)\n")) + ui.warn(_(b"(run 'hg evolve --rev REV' on one of them)\n")) return 1 else: return _nextevolve(ui, repo, repo[choosedrev], opts) @@ -1159,21 +1159,21 @@ """logic for hg next command to evolve and update to an aspiring children""" cmdutil.bailifchanged(repo) - evolvestate = state.cmdstate(repo, opts={'command': 'next', - 'bookmarkchanges': []}) + evolvestate = state.cmdstate(repo, opts={b'command': b'next', + b'bookmarkchanges': []}) with repo.wlock(), repo.lock(): - tr = repo.transaction("evolve") + tr = repo.transaction(b"evolve") with util.acceptintervention(tr): result = evolvecmd._solveone(ui, repo, repo[aspchildren], evolvestate, opts.get('dry_run'), False, - lambda: None, category='orphan', + lambda: None, category=b'orphan', stacktmplt=opts.get('stacktemplate', False)) # making sure a next commit is formed if result[0] and result[1]: - ui.status(_('working directory is now at %s\n') - % ui.label(bytes(repo['.']), 'evolve.node')) + ui.status(_(b'working directory is now at %s\n') + % ui.label(bytes(repo[b'.']), b'evolve.node')) return 0 def _updatetonext(ui, repo, child, displayer, opts): @@ -1182,28 +1182,28 @@ bm = repo._activebookmark shouldmove = opts.get('move_bookmark') and bm is not None if opts.get('dry_run'): - ui.write(_('hg update %s;\n') % child) + ui.write(_(b'hg update %s;\n') % child) if shouldmove: - ui.write(_('hg bookmark %s -r %s;\n') % (bm, child)) + ui.write(_(b'hg bookmark %s -r %s;\n') % (bm, child)) else: updatecheck = None # --merge is passed, we don't need to care about commands.update.check # config option if opts['merge']: - updatecheck = 'none' + updatecheck = b'none' try: ret = hg.updatetotally(ui, repo, child.node(), None, updatecheck=updatecheck) except error.Abort as exc: # replace the hint to mention about --merge option - exc.hint = _('do you want --merge?') + exc.hint = _(b'do you want --merge?') raise if not ret: lock = tr = None try: lock = repo.lock() - tr = repo.transaction('next') + tr = repo.transaction(b'next') if shouldmove: bmchanges = [(bm, child.node())] repo._bookmarks.applychanges(repo, tr, bmchanges) @@ -1216,7 +1216,7 @@ displayer.show(child) return 0 -@eh.wrapcommand('commit') +@eh.wrapcommand(b'commit') def commitwrapper(orig, ui, repo, *arg, **kwargs): tr = None if kwargs.get('amend', False): @@ -1227,17 +1227,17 @@ try: obsoleted = kwargs.get('obsolete', []) if obsoleted: - obsoleted = repo.set('%lr', obsoleted) + obsoleted = repo.set(b'%lr', obsoleted) result = orig(ui, repo, *arg, **kwargs) if not result: # commit succeeded - new = repo['tip'] + new = repo[b'tip'] oldbookmarks = [] markers = [] for old in obsoleted: oldbookmarks.extend(repo.nodebookmarks(old.node())) markers.append((old, (new,))) if markers: - obsolete.createmarkers(repo, markers, operation="amend") + obsolete.createmarkers(repo, markers, operation=b"amend") bmchanges = [] for book in oldbookmarks: bmchanges.append((book, new.node())) @@ -1246,32 +1246,32 @@ wlock = repo.wlock() if not lock: lock = repo.lock() - tr = repo.transaction('commit') + tr = repo.transaction(b'commit') repo._bookmarks.applychanges(repo, tr, bmchanges) tr.close() return result finally: lockmod.release(tr, lock, wlock) -@eh.wrapcommand('strip', extension='strip', opts=[ - ('', 'bundle', None, _("delete the commit entirely and move it to a " - "backup bundle")), +@eh.wrapcommand(b'strip', extension=b'strip', opts=[ + (b'', b'bundle', None, _(b"delete the commit entirely and move it to a " + b"backup bundle")), ]) def stripwrapper(orig, ui, repo, *revs, **kwargs): - if (not ui.configbool('experimental', 'prunestrip') + if (not ui.configbool(b'experimental', b'prunestrip') or kwargs.get('bundle', False)): return orig(ui, repo, *revs, **kwargs) if kwargs.get('force'): - ui.warn(_("warning: --force has no effect during strip with evolve " - "enabled\n")) + ui.warn(_(b"warning: --force has no effect during strip with evolve " + b"enabled\n")) if kwargs.get('no_backup', False): - ui.warn(_("warning: --no-backup has no effect during strips with " - "evolve enabled\n")) + ui.warn(_(b"warning: --no-backup has no effect during strips with " + b"evolve enabled\n")) revs = list(revs) + kwargs.pop('rev', []) revs = set(scmutil.revrange(repo, revs)) - revs = repo.revs("(%ld)::", revs) + revs = repo.revs(b"(%ld)::", revs) kwargs['rev'] = [] kwargs['new'] = [] kwargs['successor'] = [] @@ -1280,9 +1280,9 @@ @eh.extsetup def oldevolveextsetup(ui): - entry = cmdutil.findcmd('commit', commands.table)[1] - entry[1].append(('o', 'obsolete', [], - _("make commit obsolete this revision (DEPRECATED)"))) + entry = cmdutil.findcmd(b'commit', commands.table)[1] + entry[1].append((b'o', b'obsolete', [], + _(b"make commit obsolete this revision (DEPRECATED)"))) @eh.wrapfunction(obsolete, '_checkinvalidmarkers') def _checkinvalidmarkers(orig, markers): @@ -1291,12 +1291,12 @@ Exist as a separated function to allow the evolve extension for a more subtle handling. """ - if 'debugobsconvert' in sys.argv: + if r'debugobsconvert' in sys.argv: return for mark in markers: if node.nullid in mark[1]: - msg = _('bad obsolescence marker detected: invalid successors nullid') - hint = _('You should run `hg debugobsconvert`') + msg = _(b'bad obsolescence marker detected: invalid successors nullid') + hint = _(b'You should run `hg debugobsconvert`') raise error.Abort(msg, hint=hint) @eh.command( @@ -1306,10 +1306,10 @@ def debugobsconvert(ui, repo, new_format): origmarkers = repo.obsstore._all # settle version if new_format == repo.obsstore._version: - msg = _('New format is the same as the old format, not upgrading!') + msg = _(b'New format is the same as the old format, not upgrading!') raise error.Abort(msg) with repo.lock(): - f = repo.svfs('obsstore', 'wb', atomictemp=True) + f = repo.svfs(b'obsstore', b'wb', atomictemp=True) known = set() markers = [] for m in origmarkers: @@ -1322,12 +1322,12 @@ continue known.add(m) markers.append(m) - ui.write(_('Old store is version %d, will rewrite in version %d\n') % ( + ui.write(_(b'Old store is version %d, will rewrite in version %d\n') % ( repo.obsstore._version, new_format)) for data in obsolete.encodemarkers(markers, True, new_format): f.write(data) f.close() - ui.write(_('Done!\n')) + ui.write(_(b'Done!\n')) def _helploader(ui): @@ -1336,55 +1336,57 @@ @eh.uisetup def _setuphelp(ui): for entry in help.helptable: - if entry[0] == "evolution": + if entry[0] == b"evolution": break else: - help.helptable.append((["evolution"], _("Safely Rewriting History"), - _helploader)) + help.helptable.append(([b"evolution"], _(b"Safely Rewriting History"), + _helploader)) help.helptable.sort() evolvestateversion = 0 def _evolvemessage(): - _msg = _('To continue: hg evolve --continue\n' - 'To abort: hg evolve --abort\n' - 'To stop: hg evolve --stop\n' - '(also see `hg help evolve.interrupted`)') + _msg = _(b'To continue: hg evolve --continue\n' + b'To abort: hg evolve --abort\n' + b'To stop: hg evolve --stop\n' + b'(also see `hg help evolve.interrupted`)') return cmdutil._commentlines(_msg) @eh.uisetup def setupevolveunfinished(ui): if not util.safehasattr(cmdutil, 'unfinishedstates'): from mercurial import state as statemod - _msg = _('To continue: hg evolve --continue\n' - 'To abort: hg evolve --abort\n' - 'To stop: hg evolve --stop\n' - '(also see `hg help evolve.interrupted`)') - statemod.addunfinished('evolve', fname='evolvestate', + _msg = _(b'To continue: hg evolve --continue\n' + b'To abort: hg evolve --abort\n' + b'To stop: hg evolve --stop\n' + b'(also see `hg help evolve.interrupted`)') + statemod.addunfinished(b'evolve', fname=b'evolvestate', continueflag=True, stopflag=True, - statushint=_msg) - statemod.addunfinished('pick', fname='pickstate', continueflag=True) + statushint=_msg, + abortfunc=evolvecmd.hgabortevolve) + statemod.addunfinished(b'pick', fname=b'pickstate', continueflag=True, + abortfunc=cmdrewrite.hgabortpick) else: # compat <= hg-5.0 (5f2f6912c9e6) - estate = ('evolvestate', False, False, _('evolve in progress'), - _("use 'hg evolve --continue' or 'hg evolve --abort' to abort")) + estate = (b'evolvestate', False, False, _(b'evolve in progress'), + _(b"use 'hg evolve --continue' or 'hg evolve --abort' to abort")) cmdutil.unfinishedstates.append(estate) - pstate = ('pickstate', False, False, _('pick in progress'), - _("use 'hg pick --continue' or 'hg pick --abort' to abort")) + pstate = (b'pickstate', False, False, _(b'pick in progress'), + _(b"use 'hg pick --continue' or 'hg pick --abort' to abort")) cmdutil.unfinishedstates.append(pstate) - afterresolved = ('evolvestate', _('hg evolve --continue')) - pickresolved = ('pickstate', _('hg pick --continue')) + afterresolved = (b'evolvestate', _(b'hg evolve --continue')) + pickresolved = (b'pickstate', _(b'hg pick --continue')) cmdutil.afterresolvedstates.append(afterresolved) cmdutil.afterresolvedstates.append(pickresolved) if util.safehasattr(cmdutil, 'STATES'): - statedata = ('evolve', cmdutil.fileexistspredicate('evolvestate'), + statedata = (b'evolve', cmdutil.fileexistspredicate(b'evolvestate'), _evolvemessage) cmdutil.STATES = (statedata, ) + cmdutil.STATES @eh.wrapfunction(hg, 'clean') def clean(orig, repo, *args, **kwargs): ret = orig(repo, *args, **kwargs) - util.unlinkpath(repo.vfs.join('evolvestate'), ignoremissing=True) + util.unlinkpath(repo.vfs.join(b'evolvestate'), ignoremissing=True) return ret
--- a/hgext3rd/evolve/cmdrewrite.py Mon Jul 29 14:43:15 2019 +0200 +++ b/hgext3rd/evolve/cmdrewrite.py Fri Sep 27 13:03:18 2019 +0200 @@ -67,12 +67,12 @@ return if not compat.isobsnotesupported(): - ui.warn(_("current hg version does not support storing" - " note in obsmarker\n")) + ui.warn(_(b"current hg version does not support storing" + b" note in obsmarker\n")) if len(note) > 255: - raise error.Abort(_("cannot store a note of more than 255 bytes")) - if '\n' in note: - raise error.Abort(_("note cannot contain a newline")) + raise error.Abort(_(b"cannot store a note of more than 255 bytes")) + if b'\n' in note: + raise error.Abort(_(b"note cannot contain a newline")) def _resolveoptions(ui, opts): """modify commit options dict to handle related options @@ -82,7 +82,7 @@ """ # N.B. this is extremely similar to setupheaderopts() in mq.py if not opts.get('date') and opts.get('current_date'): - opts['date'] = '%d %d' % compat.makedate() + opts['date'] = b'%d %d' % compat.makedate() if not opts.get('user') and opts.get('current_user'): opts['user'] = ui.username() @@ -136,26 +136,26 @@ if opts.pop('all', False): # add an include for all include = list(opts.get('include')) - include.append('re:.*') + include.append(b're:.*') edit = opts.pop('edit', False) log = opts.get('logfile') opts['amend'] = True _resolveoptions(ui, opts) - _alias, commitcmd = cmdutil.findcmd('commit', commands.table) + _alias, commitcmd = cmdutil.findcmd(b'commit', commands.table) with repo.wlock(), repo.lock(): if not (edit or opts['message'] or log): - opts['message'] = repo['.'].description() - rewriteutil.precheck(repo, [repo['.'].rev()], action='amend') + opts['message'] = repo[b'.'].description() + rewriteutil.precheck(repo, [repo[b'.'].rev()], action=b'amend') return commitcmd[0](ui, repo, *pats, **opts) def amendpatch(ui, repo, *pats, **opts): """logic for --patch flag of `hg amend` command.""" - with repo.wlock(), repo.lock(), repo.transaction('amend') as tr: + with repo.wlock(), repo.lock(), repo.transaction(b'amend') as tr: cmdutil.bailifchanged(repo) # first get the patch - old = repo['.'] + old = repo[b'.'] p1 = old.p1() - rewriteutil.precheck(repo, [old.rev()], 'amend') + rewriteutil.precheck(repo, [old.rev()], b'amend') diffopts = patch.difffeatureopts(repo.ui, whitespace=True) diffopts.nodates = True diffopts.git = True @@ -168,12 +168,12 @@ fp.write(chunk) newnode = _editandapply(ui, repo, pats, old, p1, fp, diffopts) if newnode == old.node(): - raise error.Abort(_("nothing changed")) + raise error.Abort(_(b"nothing changed")) metadata = {} if opts.get('note'): - metadata['note'] = opts['note'] + metadata[b'note'] = opts['note'] replacements = {old.node(): [newnode]} - scmutil.cleanupnodes(repo, replacements, operation='amend', + scmutil.cleanupnodes(repo, replacements, operation=b'amend', metadata=metadata) phases.retractboundary(repo, tr, old.phase(), [newnode]) hg.updaterepo(repo, newnode, True) @@ -183,13 +183,13 @@ while newnode is None: fp.seek(0) previous_patch = fp.getvalue() - newpatch = ui.edit(fp.getvalue(), old.user(), action="diff") + newpatch = ui.edit(fp.getvalue(), old.user(), action=b"diff") afp = stringio() afp.write(newpatch) if pats: # write rest of the files in the patch - restmatcher = scmutil.match(old, [], opts={'exclude': pats}) + restmatcher = scmutil.match(old, [], opts={b'exclude': pats}) for chunk, label in patch.diffui(repo, p1.node(), old.node(), match=restmatcher, opts=diffopts): @@ -197,21 +197,21 @@ user_patch = afp.getvalue() if not user_patch: - raise error.Abort(_("empty patch file, amend aborted")) + raise error.Abort(_(b"empty patch file, amend aborted")) if user_patch == previous_patch: - raise error.Abort(_("patch unchanged")) + raise error.Abort(_(b"patch unchanged")) afp.seek(0) # write the patch to repo and get the newnode try: newnode = _writepatch(ui, repo, old, afp) except patch.PatchError as err: - ui.write_err(_("failed to apply edited patch: %s\n") % err) + ui.write_err(_(b"failed to apply edited patch: %s\n") % err) defaultchoice = 0 # yes if not ui.interactive: defaultchoice = 1 # no - retrychoice = _('try to fix the patch (yn)?$$ &Yes $$ &No') + retrychoice = _(b'try to fix the patch (yn)?$$ &Yes $$ &No') if ui.promptchoice(retrychoice, default=defaultchoice): - raise error.Abort(_("Could not apply amended path")) + raise error.Abort(_(b"Could not apply amended path")) else: # consider a third choice where we restore the original patch fp = stringio() @@ -233,20 +233,20 @@ with patchcontext as metadata: # store the metadata from the patch to variables - parents = (metadata.get('p1'), metadata.get('p2')) - date = metadata.get('date') or old.date() - branch = metadata.get('branch') or old.branch() - user = metadata.get('user') or old.user() + parents = (metadata.get(b'p1'), metadata.get(b'p2')) + date = metadata.get(b'date') or old.date() + branch = metadata.get(b'branch') or old.branch() + user = metadata.get(b'user') or old.user() # XXX: we must extract extras from the patchfile too extra = old.extra() - message = metadata.get('message') or old.description() + message = metadata.get(b'message') or old.description() store = patch.filestore() fp.seek(0) try: files = set() # beware: next line may raise a PatchError to be handled by the caller # of this function - patch.patchrepo(ui, repo, pold, store, fp, 1, '', + patch.patchrepo(ui, repo, pold, store, fp, 1, b'', files=files, eolmode=None) memctx = context.memctx(repo, parents, message, files=files, @@ -269,23 +269,23 @@ else: prev = node.nullid - fp.write("# HG changeset patch\n") - fp.write("# User %s\n" % ctx.user()) - fp.write("# Date %d %d\n" % ctx.date()) - fp.write("# %s\n" % datestr(ctx.date())) - if branch and branch != 'default': - fp.write("# Branch %s\n" % branch) - fp.write("# Node ID %s\n" % node.hex(nodeval)) - fp.write("# Parent %s\n" % node.hex(prev)) + fp.write(b"# HG changeset patch\n") + fp.write(b"# User %s\n" % ctx.user()) + fp.write(b"# Date %d %d\n" % ctx.date()) + fp.write(b"# %s\n" % datestr(ctx.date())) + if branch and branch != b'default': + fp.write(b"# Branch %s\n" % branch) + fp.write(b"# Node ID %s\n" % node.hex(nodeval)) + fp.write(b"# Parent %s\n" % node.hex(prev)) if len(parents) > 1: - fp.write("# Parent %s\n" % node.hex(parents[1])) + fp.write(b"# Parent %s\n" % node.hex(parents[1])) for headerid in cmdutil.extraexport: header = cmdutil.extraexportmap[headerid](1, ctx) if header is not None: - fp.write('# %s\n' % header) + fp.write(b'# %s\n' % header) fp.write(ctx.description().rstrip()) - fp.write("\n\n") + fp.write(b"\n\n") def _touchedbetween(repo, source, dest, match=None): touched = set() @@ -354,7 +354,7 @@ oldctx to a copy of oldctx not containing changed files matched by match. """ - ctx = repo['.'] + ctx = repo[b'.'] ds = repo.dirstate copies = dict(ds.copies()) if interactive: @@ -373,7 +373,7 @@ # Also any modifications to a removed file will result the status as # added, so we have only two cases. So in either of the cases, the # resulting status can be modified or clean. - if ds[f] == 'r': + if ds[f] == b'r': # But the file is removed in the working directory, leaving that # as removed continue @@ -387,7 +387,7 @@ # does not adds it back. If it's adds it back, we do a normallookup. # The file can't be removed in working directory, because it was # removed in oldctx - if ds[f] == 'a': + if ds[f] == b'a': ds.normallookup(f) continue ds.remove(f) @@ -399,7 +399,7 @@ # would have resulted in modified status, not removed. # So a file added in a commit, and uncommitting that addition must # result in file being stated as unknown. - if ds[f] == 'r': + if ds[f] == b'r': # The working directory say it's removed, so lets make the file # unknown ds.drop(f) @@ -408,23 +408,23 @@ else: m, a, r = repo.status(oldctx.p1(), oldctx, match=match)[:3] for f in m: - if ds[f] == 'r': + if ds[f] == b'r': # modified + removed -> removed continue ds.normallookup(f) for f in a: - if ds[f] == 'r': + if ds[f] == b'r': # added + removed -> unknown ds.drop(f) - elif ds[f] != 'a': + elif ds[f] != b'a': ds.add(f) for f in r: - if ds[f] == 'a': + if ds[f] == b'a': # removed + added -> normal ds.normallookup(f) - elif ds[f] != 'r': + elif ds[f] != b'r': ds.remove(f) # Merge old parent and old working dir copies @@ -442,7 +442,7 @@ for dst, src in oldcopies.items()) # Adjust the dirstate copies for dst, src in copies.items(): - if (src not in ctx or dst in ctx or ds[dst] != 'a'): + if (src not in ctx or dst in ctx or ds[dst] != b'a'): src = None ds.copy(src, dst) @@ -487,13 +487,13 @@ lock = repo.lock() wctx = repo[None] if len(wctx.parents()) <= 0: - raise error.Abort(_("cannot uncommit null changeset")) + raise error.Abort(_(b"cannot uncommit null changeset")) if len(wctx.parents()) > 1: - raise error.Abort(_("cannot uncommit while merging")) - old = repo['.'] - rewriteutil.precheck(repo, [repo['.'].rev()], action='uncommit') + raise error.Abort(_(b"cannot uncommit while merging")) + old = repo[b'.'] + rewriteutil.precheck(repo, [repo[b'.'].rev()], action=b'uncommit') if len(old.parents()) > 1: - raise error.Abort(_("cannot uncommit merge changeset")) + raise error.Abort(_(b"cannot uncommit merge changeset")) oldphase = old.phase() rev = None @@ -501,13 +501,13 @@ rev = scmutil.revsingle(repo, opts.get('rev')) ctx = repo[None] if ctx.p1() == rev or ctx.p2() == rev: - raise error.Abort(_("cannot uncommit to parent changeset")) + raise error.Abort(_(b"cannot uncommit to parent changeset")) onahead = old.rev() in repo.changelog.headrevs() disallowunstable = not obsolete.isenabled(repo, obsolete.allowunstableopt) if disallowunstable and not onahead: - raise error.Abort(_("cannot uncommit in the middle of a stack")) + raise error.Abort(_(b"cannot uncommit in the middle of a stack")) match = scmutil.match(old, pats, pycompat.byteskwargs(opts)) @@ -542,7 +542,7 @@ % uipathfn(f), hint=hint) # Recommit the filtered changeset - tr = repo.transaction('uncommit') + tr = repo.transaction(b'uncommit') if interactive: opts['all'] = True newid = _interactiveuncommit(ui, repo, old, match) @@ -557,16 +557,16 @@ message=message, user=opts.get('user'), date=opts.get('date')) if newid is None: - raise error.Abort(_('nothing to uncommit'), - hint=_("use --all to uncommit all files")) + raise error.Abort(_(b'nothing to uncommit'), + hint=_(b"use --all to uncommit all files")) # metadata to be stored in obsmarker metadata = {} if opts.get('note'): - metadata['note'] = opts['note'] + metadata[b'note'] = opts['note'] replacements = {old.node(): [newid]} - scmutil.cleanupnodes(repo, replacements, operation="uncommit", + scmutil.cleanupnodes(repo, replacements, operation=b"uncommit", metadata=metadata) phases.retractboundary(repo, tr, oldphase, [newid]) if opts.get('revert'): @@ -576,8 +576,8 @@ repo.dirstate.setparents(newid, node.nullid) _uncommitdirstate(repo, old, match, interactive) if not repo[newid].files(): - ui.warn(_("new changeset is empty\n")) - ui.status(_("(use 'hg prune .' to remove it)\n")) + ui.warn(_(b"new changeset is empty\n")) + ui.status(_(b"(use 'hg prune .' to remove it)\n")) tr.close() finally: lockmod.release(tr, lock, wlock) @@ -604,7 +604,7 @@ fp.seek(0) newnode = _patchtocommit(ui, repo, old, fp) # creating obs marker temp -> () - obsolete.createmarkers(repo, [(repo[tempnode], ())], operation="uncommit") + obsolete.createmarkers(repo, [(repo[tempnode], ())], operation=b"uncommit") return newnode def _createtempcommit(ui, repo, old, match): @@ -626,20 +626,20 @@ # to add uncommit as an operation taking care of BC. try: chunks, opts = cmdutil.recordfilter(repo.ui, originalchunks, match, - operation='discard') + operation=b'discard') except TypeError: # hg <= 4.9 (db72f9f6580e) chunks, opts = cmdutil.recordfilter(repo.ui, originalchunks, - operation='discard') + operation=b'discard') if not chunks: - raise error.Abort(_("nothing selected to uncommit")) + raise error.Abort(_(b"nothing selected to uncommit")) fp = stringio() for c in chunks: c.write(fp) fp.seek(0) oldnode = node.hex(old.node())[:12] - message = 'temporary commit for uncommiting %s' % oldnode + message = b'temporary commit for uncommiting %s' % oldnode tempnode = _patchtocommit(ui, repo, old, fp, message, oldnode) return tempnode @@ -656,14 +656,14 @@ user = old.user() extra = old.extra() if extras: - extra['uncommit_source'] = extras + extra[b'uncommit_source'] = extras if not message: message = old.description() store = patch.filestore() try: files = set() try: - patch.patchrepo(ui, repo, pold, store, fp, 1, '', + patch.patchrepo(ui, repo, pold, store, fp, 1, b'', files=files, eolmode=None) except patch.PatchError as err: raise error.Abort(pycompat.bytestr(err)) @@ -733,33 +733,33 @@ revs = list(revs) revs.extend(opts['rev']) if not revs: - raise error.Abort(_('no revisions specified')) + raise error.Abort(_(b'no revisions specified')) revs = scmutil.revrange(repo, revs) if opts['from'] and opts['exact']: - raise error.Abort(_('cannot use both --from and --exact')) + raise error.Abort(_(b'cannot use both --from and --exact')) elif opts['from']: # Try to extend given revision starting from the working directory - extrevs = repo.revs('(%ld::.) or (.::%ld)', revs, revs) + extrevs = repo.revs(b'(%ld::.) or (.::%ld)', revs, revs) discardedrevs = [r for r in revs if r not in extrevs] if discardedrevs: - msg = _("cannot fold non-linear revisions") - hint = _("given revisions are unrelated to parent of working" - " directory") + msg = _(b"cannot fold non-linear revisions") + hint = _(b"given revisions are unrelated to parent of working" + b" directory") raise error.Abort(msg, hint=hint) revs = extrevs elif opts['exact']: # Nothing to do; "revs" is already set correctly pass else: - raise error.Abort(_('must specify either --from or --exact')) + raise error.Abort(_(b'must specify either --from or --exact')) if not revs: - raise error.Abort(_('specified revisions evaluate to an empty set'), - hint=_('use different revision arguments')) + raise error.Abort(_(b'specified revisions evaluate to an empty set'), + hint=_(b'use different revision arguments')) elif len(revs) == 1: - ui.write_err(_('single revision specified, nothing to fold\n')) + ui.write_err(_(b'single revision specified, nothing to fold\n')) return 1 # Sort so combined commit message of `hg fold --exact -r . -r .^` is @@ -773,7 +773,7 @@ root, head, p2 = rewriteutil.foldcheck(repo, revs) - tr = repo.transaction('fold') + tr = repo.transaction(b'fold') try: commitopts = opts.copy() allctx = [repo[r] for r in revs] @@ -782,15 +782,15 @@ if commitopts.get('message') or commitopts.get('logfile'): commitopts['edit'] = False else: - msgs = ["HG: This is a fold of %d changesets." % len(allctx)] - msgs += ["HG: Commit message of changeset %d.\n\n%s\n" % + msgs = [b"HG: This is a fold of %d changesets." % len(allctx)] + msgs += [b"HG: Commit message of changeset %d.\n\n%s\n" % (c.rev(), c.description()) for c in allctx] - commitopts['message'] = "\n".join(msgs) + commitopts['message'] = b"\n".join(msgs) commitopts['edit'] = True metadata = {} if opts.get('note'): - metadata['note'] = opts['note'] + metadata[b'note'] = opts['note'] updates = allctx[:] if p2 is not None and root.p2() != p2: @@ -803,13 +803,13 @@ commitopts=commitopts) phases.retractboundary(repo, tr, targetphase, [newid]) replacements = {ctx.node(): [newid] for ctx in allctx} - scmutil.cleanupnodes(repo, replacements, operation="fold", + scmutil.cleanupnodes(repo, replacements, operation=b"fold", metadata=metadata) tr.close() finally: tr.release() - ui.status('%i changesets folded\n' % len(revs)) - if repo['.'].rev() in revs: + ui.status(b'%i changesets folded\n' % len(revs)) + if repo[b'.'].rev() in revs: hg.update(repo, newid) finally: lockmod.release(lock, wlock) @@ -856,8 +856,8 @@ revs.extend(opts['rev']) if not revs: if opts['fold']: - raise error.Abort(_('revisions must be specified with --fold')) - revs = ['.'] + raise error.Abort(_(b'revisions must be specified with --fold')) + revs = [b'.'] with repo.wlock(), repo.lock(): revs = scmutil.revrange(repo, revs) @@ -870,21 +870,21 @@ # we need to rewrite a first, then directly rewrite b on top of the # new a, then rewrite c on top of the new b. So we need to handle # revisions in topological order. - raise error.Abort(_('editing multiple revisions without --fold is ' - 'not currently supported')) + raise error.Abort(_(b'editing multiple revisions without --fold is ' + b'not currently supported')) if opts['fold']: root, head, p2 = rewriteutil.foldcheck(repo, revs) else: - if repo.revs("%ld and public()", revs): - raise error.Abort(_('cannot edit commit information for public ' - 'revisions')) + if repo.revs(b"%ld and public()", revs): + raise error.Abort(_(b'cannot edit commit information for public ' + b'revisions')) newunstable = rewriteutil.disallowednewunstable(repo, revs) if newunstable: - msg = _('cannot edit commit information in the middle' - ' of a stack') - hint = _('%s will become unstable and new unstable changes' - ' are not allowed') + msg = _(b'cannot edit commit information in the middle' + b' of a stack') + hint = _(b'%s will become unstable and new unstable changes' + b' are not allowed') hint %= repo[newunstable.first()] raise error.Abort(msg, hint=hint) root = head = repo[revs.first()] @@ -892,7 +892,7 @@ wctx = repo[None] p1 = wctx.p1() - tr = repo.transaction('metaedit') + tr = repo.transaction(b'metaedit') newp1 = None try: commitopts = opts.copy() @@ -903,12 +903,12 @@ commitopts['edit'] = False else: if opts['fold']: - msgs = ["HG: This is a fold of %d changesets." % len(allctx)] - msgs += ["HG: Commit message of changeset %d.\n\n%s\n" % + msgs = [b"HG: This is a fold of %d changesets." % len(allctx)] + msgs += [b"HG: Commit message of changeset %d.\n\n%s\n" % (c.rev(), c.description()) for c in allctx] else: msgs = [head.description()] - commitopts['message'] = "\n".join(msgs) + commitopts['message'] = b"\n".join(msgs) commitopts['edit'] = True updates = allctx[:] @@ -928,20 +928,20 @@ # metadata to be stored on obsmarker metadata = {} if opts.get('note'): - metadata['note'] = opts['note'] + metadata[b'note'] = opts['note'] phases.retractboundary(repo, tr, targetphase, [newid]) obsolete.createmarkers(repo, [(ctx, (repo[newid],)) for ctx in allctx], - metadata=metadata, operation="metaedit") + metadata=metadata, operation=b"metaedit") else: - ui.status(_("nothing changed\n")) + ui.status(_(b"nothing changed\n")) tr.close() finally: tr.release() if opts['fold']: - ui.status('%i changesets folded\n' % len(revs)) + ui.status(b'%i changesets folded\n' % len(revs)) if newp1 is not None: hg.update(repo, newp1) @@ -957,9 +957,9 @@ date = opts.get('date') user = opts.get('user') if date: - metadata['date'] = '%i %i' % compat.parsedate(date) + metadata[b'date'] = b'%i %i' % compat.parsedate(date) if user: - metadata['user'] = user + metadata[b'user'] = user return metadata @eh.command( @@ -1005,6 +1005,12 @@ ``--pair`` option to pair the pruned precursor and successor changesets. This is commonly useful for resolving history divergence, or when someone else edits history without obsolescence enabled. + + .. container:: verbose + + ``hg prune A::B -s C::D --pair`` will mark all revisions in the A::B + range as superseded by the revisions in C::D. Both revsets need to have + the same number of changesets. """ _checknotesize(ui, opts) revs = scmutil.revrange(repo, list(revs) + opts.get('rev')) @@ -1015,10 +1021,10 @@ fold = opts.get('fold') split = opts.get('split') - options = [o for o in ('pair', 'fold', 'split') if opts.get(o)] + options = [o for o in (r'pair', r'fold', r'split') if opts.get(o)] if 1 < len(options): - _opts = pycompat.sysbytes(', '.join(options)) - raise error.Abort(_("can only specify one of %s") % _opts) + _opts = pycompat.sysbytes(r', '.join(options)) + raise error.Abort(_(b"can only specify one of %s") % _opts) if bookmarks: reachablefrombookmark = rewriteutil.reachablefrombookmark @@ -1028,14 +1034,14 @@ rewriteutil.deletebookmark(repo, repomarks, bookmarks) if not revs: - raise error.Abort(_('nothing to prune')) + raise error.Abort(_(b'no revisions specified to prune')) wlock = lock = tr = None try: wlock = repo.wlock() lock = repo.lock() - rewriteutil.precheck(repo, revs, 'prune') - tr = repo.transaction('prune') + rewriteutil.precheck(repo, revs, b'prune') + tr = repo.transaction(b'prune') # defines pruned changesets precs = [] revs.sort() @@ -1043,33 +1049,33 @@ cp = repo[p] precs.append(cp) if not precs: - raise error.Abort('nothing to prune') + raise error.Abort(b'nothing to prune') # defines successors changesets sucs = scmutil.revrange(repo, succs) sucs.sort() sucs = tuple(repo[n] for n in sucs) if not biject and len(sucs) > 1 and len(precs) > 1: - msg = "Can't use multiple successors for multiple precursors" - hint = _("use --pair to mark a series as a replacement" - " for another") + msg = b"Can't use multiple successors for multiple precursors" + hint = _(b"use --pair to mark a series as a replacement" + b" for another") raise error.Abort(msg, hint=hint) elif biject and len(sucs) != len(precs): - msg = "Can't use %d successors for %d precursors" \ + msg = b"Can't use %d successors for %d precursors"\ % (len(sucs), len(precs)) raise error.Abort(msg) elif (len(precs) == 1 and len(sucs) > 1) and not split: - msg = "please add --split if you want to do a split" + msg = b"please add --split if you want to do a split" raise error.Abort(msg) elif len(sucs) == 1 and len(precs) > 1 and not fold: - msg = "please add --fold if you want to do a fold" + msg = b"please add --fold if you want to do a fold" raise error.Abort(msg) elif biject: replacements = {p.node(): [s.node()] for p, s in zip(precs, sucs)} else: replacements = {p.node(): [s.node() for s in sucs] for p in precs} - wdp = repo['.'] + wdp = repo[b'.'] if wdp in precs: if len(sucs) == 1 and len(precs) == 1: @@ -1097,7 +1103,7 @@ # only reset the dirstate for files that would actually change # between the working context and uctx - descendantrevs = repo.revs("%d::." % newnode.rev()) + descendantrevs = repo.revs(b"%d::." % newnode.rev()) changedfiles = [] for rev in descendantrevs: # blindly reset the files, regardless of what actually @@ -1106,7 +1112,7 @@ # reset files that only changed in the dirstate too dirstate = repo.dirstate - dirchanges = [f for f in dirstate if dirstate[f] != 'n'] + dirchanges = [f for f in dirstate if dirstate[f] != b'n'] changedfiles.extend(dirchanges) repo.dirstate.rebuild(newnode.node(), newnode.manifest(), changedfiles) @@ -1122,8 +1128,8 @@ bmchanges = [(bookactive, newnode.node())] repo._bookmarks.applychanges(repo, tr, bmchanges) commands.update(ui, repo, newnode.hex()) - ui.status(_('working directory is now at %s\n') - % ui.label(bytes(newnode), 'evolve.node')) + ui.status(_(b'working directory is now at %s\n') + % ui.label(bytes(newnode), b'evolve.node')) if movebookmark: bookmarksmod.activate(repo, bookactive) @@ -1133,11 +1139,11 @@ # store note in metadata if opts.get('note'): - metadata['note'] = opts['note'] + metadata[b'note'] = opts['note'] precrevs = (precursor.rev() for precursor in precs) moves = {} - for ctx in repo.unfiltered().set('bookmark() and %ld', precrevs): + for ctx in repo.unfiltered().set(b'bookmark() and %ld', precrevs): # used to be: # # ldest = list(repo.set('max((::%d) - obsolete())', ctx)) @@ -1150,11 +1156,11 @@ if not dest.obsolete() and dest.node() not in replacements: moves[ctx.node()] = dest.node() break - scmutil.cleanupnodes(repo, replacements, operation="prune", moves=moves, + scmutil.cleanupnodes(repo, replacements, operation=b"prune", moves=moves, metadata=metadata) # informs that changeset have been pruned - ui.status(_('%i changesets pruned\n') % len(precs)) + ui.status(_(b'%i changesets pruned\n') % len(precs)) tr.close() finally: @@ -1188,13 +1194,13 @@ revs = opts.get('rev') if not revs: - revarg = '.' + revarg = b'.' 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')) + raise error.Abort(_(b'more than one revset is given')) # Save the current branch to restore it in the end savedbranch = repo.dirstate.branch() @@ -1205,18 +1211,18 @@ ctx = scmutil.revsingle(repo, revarg) rev = ctx.rev() cmdutil.bailifchanged(repo) - rewriteutil.precheck(repo, [rev], action='split') - tr = repo.transaction('split') + rewriteutil.precheck(repo, [rev], action=b'split') + tr = repo.transaction(b'split') # make sure we respect the phase while splitting - overrides = {('phases', 'new-commit'): ctx.phase()} + overrides = {(b'phases', b'new-commit'): ctx.phase()} if len(ctx.parents()) > 1: - raise error.Abort(_("cannot split merge commits")) + raise error.Abort(_(b"cannot split merge commits")) prev = ctx.p1() bmupdate = rewriteutil.bookmarksupdater(repo, ctx.node(), tr) bookactive = repo._activebookmark if bookactive is not None: - repo.ui.status(_("(leaving bookmark %s)\n") % repo._activebookmark) + repo.ui.status(_(b"(leaving bookmark %s)\n") % repo._activebookmark) bookmarksmod.deactivate(repo) # Prepare the working directory @@ -1225,8 +1231,8 @@ 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") + msg = (b"HG: This is the original pre-split commit message. " + b"Edit it as appropriate.\n\n") msg += ctx.description() opts['message'] = msg opts['edit'] = True @@ -1247,21 +1253,21 @@ if haschanges(matcher): if iselect: - with repo.ui.configoverride(overrides, 'split'): - cmdutil.dorecord(ui, repo, commands.commit, 'commit', + with repo.ui.configoverride(overrides, b'split'): + cmdutil.dorecord(ui, repo, commands.commit, b'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['.']) + newcommits.append(repo[b'.']) elif not pats: - msg = _("no files of directories specified") - hint = _("do you want --interactive") + msg = _(b"no files of directories specified") + hint = _(b"do you want --interactive") raise error.Abort(msg, hint=hint) else: - with repo.ui.configoverride(overrides, 'split'): + with repo.ui.configoverride(overrides, b'split'): commands.commit(ui, repo, *pats, **opts) - newcommits.append(repo['.']) + newcommits.append(repo[b'.']) if pats: # refresh the wctx used for the matcher matcher = scmutil.match(repo[None], pats) @@ -1271,20 +1277,20 @@ if haschanges(matcher): nextaction = None while nextaction is None: - nextaction = ui.prompt('continue splitting? [Ycdq?]', default='y') - if nextaction == 'c': - with repo.ui.configoverride(overrides, 'split'): + nextaction = ui.prompt(b'continue splitting? [Ycdq?]', default=b'y') + if nextaction == b'c': + with repo.ui.configoverride(overrides, b'split'): commands.commit(ui, repo, **opts) - newcommits.append(repo['.']) + newcommits.append(repo[b'.']) break - elif nextaction == 'q': - raise error.Abort(_('user quit')) - elif nextaction == 'd': + elif nextaction == b'q': + raise error.Abort(_(b'user quit')) + elif nextaction == b'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')) + ui.status(_(b'discarding remaining changes\n')) target = newcommits[0] if pats: status = repo.status(match=matcher)[:4] @@ -1297,24 +1303,24 @@ else: cmdutil.revert(ui, repo, repo[target], (target, node.nullid), all=True) - elif nextaction == '?': + elif nextaction == b'?': 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")) + ui.write(_(b"y - yes, continue selection\n")) + ui.write(_(b"c - commit, select all remaining changes\n")) + ui.write(_(b"d - discard, discard remaining changes\n")) + ui.write(_(b"q - quit, abort the split\n")) + ui.write(_(b"? - ?, display help\n")) else: continue break # propagate the previous break else: - ui.status(_("no more change to split\n")) + ui.status(_(b"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? - with repo.ui.configoverride(overrides, 'split'): + with repo.ui.configoverride(overrides, b'split'): commands.commit(ui, repo, **opts) - newcommits.append(repo['.']) + newcommits.append(repo[b'.']) if newcommits: tip = repo[newcommits[-1]] bmupdate(tip.node()) @@ -1322,9 +1328,9 @@ bookmarksmod.activate(repo, bookactive) metadata = {} if opts.get('note'): - metadata['note'] = opts['note'] + metadata[b'note'] = opts['note'] obsolete.createmarkers(repo, [(repo[rev], newcommits)], - metadata=metadata, operation="split") + metadata=metadata, operation=b"split") tr.close() finally: # Restore the old branch @@ -1342,7 +1348,7 @@ b'mark the new revision as successor of the old one potentially creating ' b'divergence')], # allow to choose the seed ? - _('[-r] revs')) + _(b'[-r] revs')) def touch(ui, repo, *revs, **opts): """create successors identical to their predecessors but the changeset ID @@ -1352,18 +1358,18 @@ revs = list(revs) revs.extend(opts['rev']) if not revs: - revs = ['.'] + revs = [b'.'] revs = scmutil.revrange(repo, revs) if not revs: - ui.write_err('no revision to touch\n') + ui.write_err(b'no revision to touch\n') return 1 duplicate = opts['duplicate'] if not duplicate: - rewriteutil.precheck(repo, revs, 'touch') + rewriteutil.precheck(repo, revs, b'touch') tmpl = utility.shorttemplate - displayer = compat.changesetdisplayer(ui, repo, {'template': tmpl}) - with repo.wlock(), repo.lock(), repo.transaction('touch'): + displayer = compat.changesetdisplayer(ui, repo, {b'template': tmpl}) + with repo.wlock(), repo.lock(), repo.transaction(b'touch'): touchnodes(ui, repo, revs, displayer, **opts) def touchnodes(ui, repo, revs, displayer, **opts): @@ -1374,7 +1380,7 @@ for r in revs: ctx = repo[r] extra = ctx.extra().copy() - extra['__touch-noise__'] = random.randint(0, 0xffffffff) + extra[b'__touch-noise__'] = random.randint(0, 0xffffffff) # search for touched parent p1 = ctx.p1().node() p2 = ctx.p2().node() @@ -1405,12 +1411,12 @@ else: displayer.show(ctx) index = ui.promptchoice( - _("reviving this changeset will create divergence" - " unless you make a duplicate.\n(a)llow divergence or" - " (d)uplicate the changeset? $$ &Allowdivergence $$ " - "&Duplicate"), 0) - choice = ['allowdivergence', 'duplicate'][index] - if choice == 'allowdivergence': + _(b"reviving this changeset will create divergence" + b" unless you make a duplicate.\n(a)llow divergence or" + b" (d)uplicate the changeset? $$ &Allowdivergence $$ " + b"&Duplicate"), 0) + choice = [b'allowdivergence', b'duplicate'][index] + if choice == b'allowdivergence': duplicate = False else: duplicate = True @@ -1418,7 +1424,7 @@ updates = [] if len(ctx.parents()) > 1: updates = ctx.parents() - extradict = {'extra': extra} + extradict = {b'extra': extra} new, unusedvariable = rewriteutil.rewrite(repo, ctx, updates, ctx, [p1, p2], commitopts=extradict) @@ -1428,9 +1434,9 @@ if not duplicate: metadata = {} if opts.get('note'): - metadata['note'] = opts['note'] + metadata[b'note'] = opts['note'] obsolete.createmarkers(repo, [(ctx, (repo[new],))], - metadata=metadata, operation="touch") + metadata=metadata, operation=b"touch") tr = repo.currenttransaction() phases.retractboundary(repo, tr, ctx.phase(), [new]) if ctx in repo[None].parents(): @@ -1442,7 +1448,7 @@ [(b'r', b'rev', b'', _(b'revision to pick'), _(b'REV')), (b'c', b'continue', False, b'continue interrupted pick'), (b'a', b'abort', False, b'abort interrupted pick'), - ] + mergetoolopts, + ] + mergetoolopts, _(b'[-r] rev')) def cmdpick(ui, repo, *revs, **opts): """move a commit on the top of working directory parent and updates to it.""" @@ -1451,69 +1457,61 @@ abort = opts.get('abort') if cont and abort: - raise error.Abort(_("cannot specify both --continue and --abort")) + raise error.Abort(_(b"cannot specify both --continue and --abort")) revs = list(revs) if opts.get('rev'): revs.append(opts['rev']) with repo.wlock(), repo.lock(): - pickstate = state.cmdstate(repo, path='pickstate') - pctx = repo['.'] + pickstate = state.cmdstate(repo, path=b'pickstate') + pctx = repo[b'.'] if not cont and not abort: cmdutil.bailifchanged(repo) revs = scmutil.revrange(repo, revs) if len(revs) > 1: - raise error.Abort(_("specify just one revision")) + raise error.Abort(_(b"specify just one revision")) elif not revs: - raise error.Abort(_("empty revision set")) + raise error.Abort(_(b"empty revision set")) origctx = repo[revs.first()] if origctx in pctx.ancestors() or origctx.node() == pctx.node(): - raise error.Abort(_("cannot pick an ancestor revision")) + raise error.Abort(_(b"cannot pick an ancestor revision")) - rewriteutil.precheck(repo, [origctx.rev()], 'pick') + rewriteutil.precheck(repo, [origctx.rev()], b'pick') - ui.status(_('picking %d:%s "%s"\n') % + ui.status(_(b'picking %d:%s "%s"\n') % (origctx.rev(), origctx, - origctx.description().split("\n", 1)[0])) - overrides = {('ui', 'forcemerge'): opts.get('tool', '')} - with ui.configoverride(overrides, 'pick'): + origctx.description().split(b"\n", 1)[0])) + overrides = {(b'ui', b'forcemerge'): opts.get('tool', b'')} + with ui.configoverride(overrides, b'pick'): stats = merge.graft(repo, origctx, origctx.p1(), - ['local', 'destination']) + [b'local', b'destination']) if compat.hasconflict(stats): - pickstate.addopts({'orignode': origctx.node(), - 'oldpctx': pctx.node()}) + pickstate.addopts({b'orignode': origctx.node(), + b'oldpctx': pctx.node()}) pickstate.save() - raise error.InterventionRequired(_("unresolved merge conflicts" - " (see hg help resolve)")) + raise error.InterventionRequired(_(b"unresolved merge conflicts" + b" (see hg help resolve)")) elif abort: - if not pickstate: - raise error.Abort(_("no interrupted pick state exists")) - pickstate.load() - pctxnode = pickstate['oldpctx'] - ui.status(_("aborting pick, updating to %s\n") % - node.hex(pctxnode)[:12]) - hg.updaterepo(repo, pctxnode, True) - pickstate.delete() - return 0 + return abortpick(ui, repo, pickstate) else: if revs: - raise error.Abort(_("cannot specify both --continue and " - "revision")) + raise error.Abort(_(b"cannot specify both --continue and " + b"revision")) if not pickstate: - raise error.Abort(_("no interrupted pick state exists")) + raise error.Abort(_(b"no interrupted pick state exists")) pickstate.load() - orignode = pickstate['orignode'] + orignode = pickstate[b'orignode'] origctx = repo[orignode] - overrides = {('phases', 'new-commit'): origctx.phase()} - with repo.ui.configoverride(overrides, 'pick'): + overrides = {(b'phases', b'new-commit'): origctx.phase()} + with repo.ui.configoverride(overrides, b'pick'): newnode = repo.commit(text=origctx.description(), user=origctx.user(), date=origctx.date(), extra=origctx.extra()) @@ -1523,11 +1521,29 @@ pickstate.delete() newctx = repo[newnode] if newnode else pctx replacements = {origctx.node(): [newctx.node()]} - scmutil.cleanupnodes(repo, replacements, operation="pick") + scmutil.cleanupnodes(repo, replacements, operation=b"pick") if newnode is None: - ui.warn(_("note: picking %d:%s created no changes to commit\n") % + ui.warn(_(b"note: picking %d:%s created no changes to commit\n") % (origctx.rev(), origctx)) return 0 return 0 + +def abortpick(ui, repo, pickstate, abortcmd=False): + """logic to abort pick""" + if not pickstate and not abortcmd: + raise error.Abort(_(b"no interrupted pick state exists")) + pickstate.load() + pctxnode = pickstate[b'oldpctx'] + ui.status(_(b"aborting pick, updating to %s\n") % + node.hex(pctxnode)[:12]) + hg.updaterepo(repo, pctxnode, True) + pickstate.delete() + return 0 + +def hgabortpick(ui, repo): + """logic to abort pick using 'hg abort'""" + with repo.wlock(), repo.lock(): + pickstate = state.cmdstate(repo, path=b'pickstate') + return abortpick(ui, repo, pickstate, abortcmd=True)
--- a/hgext3rd/evolve/compat.py Mon Jul 29 14:43:15 2019 +0200 +++ b/hgext3rd/evolve/compat.py Fri Sep 27 13:03:18 2019 +0200 @@ -62,24 +62,24 @@ # Evolution renaming compat TROUBLES = { - 'ORPHAN': 'orphan', - 'CONTENTDIVERGENT': 'content-divergent', - 'PHASEDIVERGENT': 'phase-divergent', + r'ORPHAN': b'orphan', + r'CONTENTDIVERGENT': b'content-divergent', + r'PHASEDIVERGENT': b'phase-divergent', } if util.safehasattr(uimod.ui, 'makeprogress'): - def progress(ui, topic, pos, item="", unit="", total=None): + def progress(ui, topic, pos, item=b"", unit=b"", 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): + def progress(ui, topic, pos, item=b"", unit=b"", total=None): ui.progress(topic, pos, item, unit, total) # XXX: Better detection of property cache -if 'predecessors' not in dir(obsolete.obsstore): +if r'predecessors' not in dir(obsolete.obsstore): @property def predecessors(self): return self.precursors @@ -90,36 +90,36 @@ # XXX Would it be better at the module level? varnames = context.memfilectx.__init__.__code__.co_varnames - if "copysource" in varnames: + if r"copysource" in varnames: mctx = context.memfilectx(repo, ctx, fctx.path(), fctx.data(), - islink='l' in flags, - isexec='x' in flags, + islink=b'l' in flags, + isexec=b'x' in flags, copysource=copied.get(path)) # compat with hg <- 4.9 - elif varnames[2] == "changectx": + elif varnames[2] == r"changectx": mctx = context.memfilectx(repo, ctx, fctx.path(), fctx.data(), - islink='l' in flags, - isexec='x' in flags, + islink=b'l' in flags, + isexec=b'x' in flags, copied=copied.get(path)) else: mctx = context.memfilectx(repo, fctx.path(), fctx.data(), - islink='l' in flags, - isexec='x' in flags, + islink=b'l' in flags, + isexec=b'x' in flags, copied=copied.get(path)) return mctx def strdiff(a, b, fn1, fn2): """ A version of mdiff.unidiff for comparing two strings """ - args = [a, '', b, '', fn1, fn2] + args = [a, b'', b, b'', fn1, fn2] # hg < 4.6 compat 8b6dd3922f70 if util.safehasattr(inspect, 'signature'): signature = inspect.signature(mdiff.unidiff) - needsbinary = 'binary' in signature.parameters + needsbinary = r'binary' in signature.parameters else: argspec = inspect.getargspec(mdiff.unidiff) - needsbinary = 'binary' in argspec.args + needsbinary = r'binary' in argspec.args if needsbinary: args.append(False) @@ -218,7 +218,7 @@ if limit is None: # no common ancestor, no copies return {}, {}, {}, {}, {} - repo.ui.debug(" searching for copies back to rev %d\n" % limit) + repo.ui.debug(b" searching for copies back to rev %d\n" % limit) m1 = c1.manifest() m2 = c2.manifest() @@ -232,18 +232,18 @@ # - 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, - } + data1 = {b'copy': {}, + b'fullcopy': {}, + b'incomplete': {}, + b'diverge': diverge, + b'incompletediverge': incompletediverge, + } + data2 = {b'copy': {}, + b'fullcopy': {}, + b'incomplete': {}, + b'diverge': diverge, + b'incompletediverge': incompletediverge, + } # find interesting file sets from manifests if hg48: @@ -260,20 +260,20 @@ else: # unmatched file from base (DAG rotation in the graft case) u1r, u2r = copies._computenonoverlap(repo, c1, c2, addedinm1, addedinm2, - baselabel='base') + baselabel=b'base') # unmatched file from topological common ancestors (no DAG rotation) # need to recompute this for directory move handling when grafting mta = tca.manifest() if hg48: m1f = m1.filesnotin(mta, repo.narrowmatch()) m2f = m2.filesnotin(mta, repo.narrowmatch()) - baselabel = 'topological common ancestor' + baselabel = b'topological common ancestor' u1u, u2u = copies._computenonoverlap(repo, c1, c2, m1f, m2f, baselabel=baselabel) else: u1u, u2u = copies._computenonoverlap(repo, c1, c2, m1.filesnotin(mta), m2.filesnotin(mta), - baselabel='topological common ancestor') + baselabel=b'topological common ancestor') for f in u1u: copies._checkcopies(c1, c2, f, base, tca, dirtyc1, limit, data1) @@ -281,16 +281,16 @@ 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']) + copy = dict(data1[b'copy']) + copy.update(data2[b'copy']) + fullcopy = dict(data1[b'fullcopy']) + fullcopy.update(data2[b'fullcopy']) if dirtyc1: - copies._combinecopies(data2['incomplete'], data1['incomplete'], copy, diverge, + copies._combinecopies(data2[b'incomplete'], data1[b'incomplete'], copy, diverge, incompletediverge) else: - copies._combinecopies(data1['incomplete'], data2['incomplete'], copy, diverge, + copies._combinecopies(data1[b'incomplete'], data2[b'incomplete'], copy, diverge, incompletediverge) renamedelete = {} @@ -308,23 +308,23 @@ divergeset.update(fl) # reverse map for below if bothnew: - repo.ui.debug(" unmatched files new in both:\n %s\n" - % "\n ".join(bothnew)) + repo.ui.debug(b" unmatched files new in both:\n %s\n" + % b"\n ".join(bothnew)) bothdiverge = {} bothincompletediverge = {} remainder = {} - both1 = {'copy': {}, - 'fullcopy': {}, - 'incomplete': {}, - 'diverge': bothdiverge, - 'incompletediverge': bothincompletediverge - } - both2 = {'copy': {}, - 'fullcopy': {}, - 'incomplete': {}, - 'diverge': bothdiverge, - 'incompletediverge': bothincompletediverge - } + both1 = {b'copy': {}, + b'fullcopy': {}, + b'incomplete': {}, + b'diverge': bothdiverge, + b'incompletediverge': bothincompletediverge + } + both2 = {b'copy': {}, + b'fullcopy': {}, + b'incomplete': {}, + b'diverge': bothdiverge, + b'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) @@ -333,17 +333,17 @@ 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, + assert not both2[b'incomplete'] + remainder = copies._combinecopies({}, both1[b'incomplete'], copy, bothdiverge, bothincompletediverge) elif dirtyc2: - assert not both1['incomplete'] - remainder = copies._combinecopies({}, both2['incomplete'], copy, bothdiverge, + assert not both1[b'incomplete'] + remainder = copies._combinecopies({}, both2[b'incomplete'], copy, bothdiverge, bothincompletediverge) else: # incomplete copies and divergences can't happen outside grafts - assert not both1['incomplete'] - assert not both2['incomplete'] + assert not both1[b'incomplete'] + assert not both2[b'incomplete'] assert not bothincompletediverge for f in remainder: assert f not in bothdiverge @@ -356,30 +356,30 @@ 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") + repo.ui.debug(b" all copies found (* = to merge, ! = divergent, " + b"% = renamed and deleted):\n") for f in sorted(fullcopy): - note = "" + note = b"" if f in copy: - note += "*" + note += b"*" if f in divergeset: - note += "!" + note += b"!" if f in renamedeleteset: - note += "%" - repo.ui.debug(" src: '%s' -> dst: '%s' %s\n" % (fullcopy[f], f, - note)) + note += b"%" + repo.ui.debug(b" 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") + repo.ui.debug(b" 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('/') + d1.addpath(b'/') + d2.addpath(b'/') invalid = set() dirmove = {} @@ -392,16 +392,16 @@ continue elif dsrc in d1 and ddst in d1: # directory wasn't entirely moved locally - invalid.add(dsrc + "/") + invalid.add(dsrc + b"/") 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 + "/": + invalid.add(dsrc + b"/") + elif dsrc + b"/" in dirmove and dirmove[dsrc + b"/"] != ddst + b"/": # files from the same directory moved to two different places - invalid.add(dsrc + "/") + invalid.add(dsrc + b"/") else: # looks good so far - dirmove[dsrc + "/"] = ddst + "/" + dirmove[dsrc + b"/"] = ddst + b"/" for i in invalid: if i in dirmove: @@ -412,7 +412,7 @@ return copy, {}, diverge, renamedelete, {} for d in dirmove: - repo.ui.debug(" discovered dir src: '%s' -> dst: '%s'\n" % + repo.ui.debug(b" discovered dir src: '%s' -> dst: '%s'\n" % (d, dirmove[d])) movewithdir = {} @@ -425,8 +425,8 @@ 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)) + repo.ui.debug((b" pending file src: '%s' -> " + b"dst: '%s'\n") % (f, df)) break return copy, movewithdir, diverge, renamedelete, dirmove @@ -494,8 +494,8 @@ return obsutil.markersusers(markers) markersmeta = [dict(m[3]) for m in markers] - users = set(encoding.tolocal(meta['user']) for meta in markersmeta - if meta.get('user')) + users = set(encoding.tolocal(meta[b'user']) for meta in markersmeta + if meta.get(b'user')) return sorted(users) @@ -511,7 +511,7 @@ return obsutil.markersoperations(markers) markersmeta = [dict(m[3]) for m in markers] - operations = set(meta.get('operation') for meta in markersmeta - if meta.get('operation')) + operations = set(meta.get(b'operation') for meta in markersmeta + if meta.get(b'operation')) return sorted(operations)
--- a/hgext3rd/evolve/dagutil.py Mon Jul 29 14:43:15 2019 +0200 +++ b/hgext3rd/evolve/dagutil.py Fri Sep 27 13:03:18 2019 +0200 @@ -140,7 +140,7 @@ def _internalize(self, id): ix = self._revlog.rev(id) if ix == nullrev: - raise LookupError(id, self._revlog.indexfile, _('nullid')) + raise LookupError(id, self._revlog.indexfile, _(b'nullid')) return ix def _internalizeall(self, ids, filterunknown):
--- a/hgext3rd/evolve/debugcmd.py Mon Jul 29 14:43:15 2019 +0200 +++ b/hgext3rd/evolve/debugcmd.py Fri Sep 27 13:03:18 2019 +0200 @@ -45,7 +45,7 @@ unfi = repo.unfiltered() nm = unfi.changelog.nodemap nbmarkers = len(store._all) - ui.write(_('markers total: %9i\n') % nbmarkers) + ui.write(_(b'markers total: %9i\n') % nbmarkers) sucscount = [0, 0, 0, 0] known = 0 parentsdata = 0 @@ -67,7 +67,7 @@ metakeys.setdefault(key, 0) metakeys[key] += 1 meta = dict(meta) - parents = [meta.get('p1'), meta.get('p2')] + parents = [meta.get(b'p1'), meta.get(b'p2')] parents = [node.bin(p) for p in parents if p is not None] if parents: parentsdata += 1 @@ -91,71 +91,71 @@ fc = (frozenset(c[0]), frozenset(c[1])) for n in fc[0]: pclustersmap[n] = fc - numobs = len(unfi.revs('obsolete()')) + numobs = len(unfi.revs(b'obsolete()')) numtotal = len(unfi) - ui.write((' for known precursors: %9i' % known)) - ui.write((' (%i/%i obsolete changesets)\n' % (numobs, numtotal))) - ui.write((' with parents data: %9i\n' % parentsdata)) + ui.write((b' for known precursors: %9i' % known)) + ui.write((b' (%i/%i obsolete changesets)\n' % (numobs, numtotal))) + ui.write((b' with parents data: %9i\n' % parentsdata)) # successors data - ui.write(('markers with no successors: %9i\n' % sucscount[0])) - ui.write((' 1 successors: %9i\n' % sucscount[1])) - ui.write((' 2 successors: %9i\n' % sucscount[2])) - ui.write((' more than 2 successors: %9i\n' % sucscount[3])) + ui.write((b'markers with no successors: %9i\n' % sucscount[0])) + ui.write((b' 1 successors: %9i\n' % sucscount[1])) + ui.write((b' 2 successors: %9i\n' % sucscount[2])) + ui.write((b' more than 2 successors: %9i\n' % sucscount[3])) # meta data info - ui.write((' available keys:\n')) + ui.write((b' available keys:\n')) for key in sorted(metakeys): - ui.write((' %15s: %9i\n' % (key, metakeys[key]))) + ui.write((b' %15s: %9i\n' % (key, metakeys[key]))) size_v0.sort() size_v1.sort() if size_v0: - ui.write('marker size:\n') + ui.write(b'marker size:\n') # format v1 - ui.write(' format v1:\n') - ui.write((' smallest length: %9i\n' % size_v1[0])) - ui.write((' longer length: %9i\n' % size_v1[-1])) + ui.write(b' format v1:\n') + ui.write((b' smallest length: %9i\n' % size_v1[0])) + ui.write((b' longer length: %9i\n' % size_v1[-1])) median = size_v1[nbmarkers // 2] - ui.write((' median length: %9i\n' % median)) + ui.write((b' median length: %9i\n' % median)) mean = sum(size_v1) // nbmarkers - ui.write((' mean length: %9i\n' % mean)) + ui.write((b' mean length: %9i\n' % mean)) # format v0 - ui.write(' format v0:\n') - ui.write((' smallest length: %9i\n' % size_v0[0])) - ui.write((' longer length: %9i\n' % size_v0[-1])) + ui.write(b' format v0:\n') + ui.write((b' smallest length: %9i\n' % size_v0[0])) + ui.write((b' longer length: %9i\n' % size_v0[-1])) median = size_v0[nbmarkers // 2] - ui.write((' median length: %9i\n' % median)) + ui.write((b' median length: %9i\n' % median)) mean = sum(size_v0) // nbmarkers - ui.write((' mean length: %9i\n' % mean)) + ui.write((b' mean length: %9i\n' % mean)) allclusters = list(set(clustersmap.values())) allclusters.sort(key=lambda x: len(x[1])) - ui.write(('disconnected clusters: %9i\n' % len(allclusters))) + ui.write((b'disconnected clusters: %9i\n' % len(allclusters))) - ui.write(' any known node: %9i\n' + ui.write(b' any known node: %9i\n' % len([c for c in allclusters if [n for n in c[0] if nm.get(n) is not None]])) if allclusters: nbcluster = len(allclusters) - ui.write((' smallest length: %9i\n' % len(allclusters[0][1]))) - ui.write((' longer length: %9i\n' - % len(allclusters[-1][1]))) + ui.write((b' smallest length: %9i\n' % len(allclusters[0][1]))) + ui.write((b' longer length: %9i\n' + % len(allclusters[-1][1]))) median = len(allclusters[nbcluster // 2][1]) - ui.write((' median length: %9i\n' % median)) + ui.write((b' median length: %9i\n' % median)) mean = sum(len(x[1]) for x in allclusters) // nbcluster - ui.write((' mean length: %9i\n' % mean)) + ui.write((b' mean length: %9i\n' % mean)) allpclusters = list(set(pclustersmap.values())) allpclusters.sort(key=lambda x: len(x[1])) - ui.write((' using parents data: %9i\n' % len(allpclusters))) - ui.write(' any known node: %9i\n' + ui.write((b' using parents data: %9i\n' % len(allpclusters))) + ui.write(b' any known node: %9i\n' % len([c for c in allclusters if [n for n in c[0] if nm.get(n) is not None]])) if allpclusters: nbcluster = len(allpclusters) - ui.write((' smallest length: %9i\n' - % len(allpclusters[0][1]))) - ui.write((' longer length: %9i\n' - % len(allpclusters[-1][1]))) + ui.write((b' smallest length: %9i\n' + % len(allpclusters[0][1]))) + ui.write((b' longer length: %9i\n' + % len(allpclusters[-1][1]))) median = len(allpclusters[nbcluster // 2][1]) - ui.write((' median length: %9i\n' % median)) + ui.write((b' median length: %9i\n' % median)) mean = sum(len(x[1]) for x in allpclusters) // nbcluster - ui.write((' mean length: %9i\n' % mean)) + ui.write((b' mean length: %9i\n' % mean))
--- a/hgext3rd/evolve/depthcache.py Mon Jul 29 14:43:15 2019 +0200 +++ b/hgext3rd/evolve/depthcache.py Fri Sep 27 13:03:18 2019 +0200 @@ -32,7 +32,7 @@ def simpledepth(repo, rev): """simple but obviously right implementation of depth""" - return len(repo.revs('::%d', rev)) + return len(repo.revs(b'::%d', rev)) @eh.command( b'debugdepth', @@ -46,25 +46,25 @@ """ revs = scmutil.revrange(repo, opts['rev']) method = opts['method'] - if method in ('cached', 'compare'): + if method in (b'cached', b'compare'): cache = repo.depthcache cache.save(repo) for r in revs: ctx = repo[r] - if method == 'simple': + if method == b'simple': depth = simpledepth(repo, r) - elif method == 'cached': + elif method == b'cached': depth = cache.get(r) - elif method == 'compare': + elif method == b'compare': simple = simpledepth(repo, r) cached = cache.get(r) if simple != cached: - raise error.Abort('depth differ for revision %s: %d != %d' + raise error.Abort(b'depth differ for revision %s: %d != %d' % (ctx, simple, cached)) depth = simple else: - raise error.Abort('unknown method "%s"' % method) - ui.write('%s %d\n' % (ctx, depth)) + raise error.Abort(b'unknown method "%s"' % method) + ui.write(b'%s %d\n' % (ctx, depth)) @eh.reposetup def setupcache(ui, repo): @@ -79,7 +79,7 @@ @localrepo.unfilteredmethod def destroyed(self): - if 'depthcache' in vars(self): + if r'depthcache' in vars(self): self.depthcache.clear() super(depthcacherepo, self).destroyed() @@ -94,16 +94,16 @@ class depthcache(genericcaches.changelogsourcebase): - _filepath = 'evoext-depthcache-00' - _cachename = 'evo-ext-depthcache' + _filepath = b'evoext-depthcache-00' + _cachename = b'evo-ext-depthcache' def __init__(self): super(depthcache, self).__init__() - self._data = array.array('l') + self._data = array.array(r'l') def get(self, rev): if len(self._data) <= rev: - raise error.ProgrammingError('depthcache must be warmed before use') + raise error.ProgrammingError(b'depthcache must be warmed before use') return self._data[rev] def _updatefrom(self, repo, data): @@ -113,9 +113,9 @@ total = len(data) def progress(pos, rev=None): - revstr = '' if rev is None else ('rev %d' % rev) - compat.progress(repo.ui, 'updating depth cache', - pos, revstr, unit='revision', total=total) + revstr = b'' if rev is None else (b'rev %d' % rev) + compat.progress(repo.ui, b'updating depth cache', + pos, revstr, unit=b'revision', total=total) progress(0) for idx, rev in enumerate(data, 1): assert rev == len(self._data), (rev, len(self._data)) @@ -171,7 +171,7 @@ Subclasses MUST overide this method to actually affect the cache data. """ super(depthcache, self).clear() - self._data = array.array('l') + self._data = array.array(r'l') # crude version of a cache, to show the kind of information we have to store @@ -180,7 +180,7 @@ assert repo.filtername is None data = repo.cachevfs.tryread(self._filepath) - self._data = array.array('l') + self._data = array.array(r'l') if not data: self._cachekey = self.emptykey else: @@ -199,12 +199,12 @@ return try: - cachefile = repo.cachevfs(self._filepath, 'w', atomictemp=True) + cachefile = repo.cachevfs(self._filepath, b'w', atomictemp=True) headerdata = self._serializecachekey() cachefile.write(headerdata) cachefile.write(compat.arraytobytes(self._data)) cachefile.close() self._ondiskkey = self._cachekey except (IOError, OSError) as exc: - repo.ui.log('depthcache', 'could not write update %s\n' % exc) - repo.ui.debug('depthcache: could not write update %s\n' % exc) + repo.ui.log(b'depthcache', b'could not write update %s\n' % exc) + repo.ui.debug(b'depthcache: could not write update %s\n' % exc)
--- a/hgext3rd/evolve/evolvecmd.py Mon Jul 29 14:43:15 2019 +0200 +++ b/hgext3rd/evolve/evolvecmd.py Fri Sep 27 13:03:18 2019 +0200 @@ -48,12 +48,12 @@ shorttemplate = utility.shorttemplate stacktemplate = utility.stacktemplate _bookmarksupdater = rewriteutil.bookmarksupdater -sha1re = re.compile(r'\b[0-9a-f]{6,40}\b') +sha1re = re.compile(br'\b[0-9a-f]{6,40}\b') eh = exthelper.exthelper() mergetoolopts = commands.mergetoolopts -abortmessage = _("see `hg help evolve.interrupted`\n") +abortmessage = _(b"see `hg help evolve.interrupted`\n") def _solveone(ui, repo, ctx, evolvestate, dryrun, confirm, progresscb, category, lastsolved=None, stacktmplt=False): @@ -70,23 +70,23 @@ displayer = None if stacktmplt: displayer = compat.changesetdisplayer(ui, repo, - {'template': stacktemplate}) + {b'template': stacktemplate}) else: displayer = compat.changesetdisplayer(ui, repo, - {'template': shorttemplate}) - if 'orphan' == category: + {b'template': shorttemplate}) + if b'orphan' == category: result = _solveunstable(ui, repo, ctx, evolvestate, displayer, dryrun, confirm, progresscb, lastsolved=lastsolved) - elif 'phasedivergent' == category: + elif b'phasedivergent' == category: result = _solvephasedivergence(ui, repo, ctx, evolvestate, displayer, dryrun, confirm, progresscb) - elif 'contentdivergent' == category: + elif b'contentdivergent' == category: result = _solvedivergent(ui, repo, ctx, evolvestate, displayer, dryrun, confirm, progresscb) else: - assert False, "unknown trouble category: %s" % (category) + assert False, b"unknown trouble category: %s" % (category) return result def _solveunstable(ui, repo, orig, evolvestate, displayer, dryrun=False, @@ -112,7 +112,7 @@ else: # store that we are resolving an orphan merge with both parents # obsolete and proceed with first parent - evolvestate['orphanmerge'] = True + evolvestate[b'orphanmerge'] = True # we should process the second parent first, so that in case of # no-conflicts the first parent is processed later and preserved as # first parent @@ -120,40 +120,40 @@ keepbranch = orig.p2().branch() != orig.branch() if not pctx.obsolete(): - ui.warn(_("cannot solve instability of %s, skipping\n") % orig) - return (False, ".") + ui.warn(_(b"cannot solve instability of %s, skipping\n") % orig) + return (False, b".") obs = pctx newer = obsutil.successorssets(repo, obs.node()) - # search of a parent which is not killed - while not newer or newer == [()]: - ui.debug("stabilize target %s is plain dead," - " trying to stabilize on its parent\n" % + # search of a parent which is not killed, but also isn't the orig + while not newer or newer == [()] or newer[0][0] == orig.node(): + ui.debug(b"stabilize target %s is plain dead," + b" trying to stabilize on its parent\n" % obs) obs = obs.parents()[0] newer = obsutil.successorssets(repo, obs.node()) if len(newer) > 1: - msg = _("skipping %s: divergent rewriting. can't choose " - "destination\n") % obs + msg = _(b"skipping %s: divergent rewriting. can't choose " + b"destination\n") % obs ui.write_err(msg) - return (False, ".") + return (False, b".") targets = newer[0] assert targets if len(targets) > 1: # split target, figure out which one to pick, are they all in line? targetrevs = [repo[r].rev() for r in targets] - roots = repo.revs('roots(%ld)', targetrevs) - heads = repo.revs('heads(%ld)', targetrevs) + roots = repo.revs(b'roots(%ld)', targetrevs) + heads = repo.revs(b'heads(%ld)', targetrevs) if len(roots) > 1 or len(heads) > 1: - cheader = _("ancestor '%s' split over multiple topological" - " branches.\nchoose an evolve destination:") % orig + cheader = _(b"ancestor '%s' split over multiple topological" + b" branches.\nchoose an evolve destination:") % orig selectedrev = utility.revselectionprompt(ui, repo, list(heads), cheader) if selectedrev is None: - msg = _("could not solve instability, " - "ambiguous destination: " - "parent split across two branches\n") + msg = _(b"could not solve instability, " + b"ambiguous destination: " + b"parent split across two branches\n") ui.write_err(msg) - return (False, ".") + return (False, b".") target = repo[selectedrev] else: target = repo[heads.first()] @@ -161,32 +161,27 @@ target = targets[0] target = repo[target] if not ui.quiet or confirm: - repo.ui.write(_('move:'), label='evolve.operation') + repo.ui.write(_(b'move:'), label=b'evolve.operation') displayer.show(orig) if lastsolved is None or target != repo[lastsolved]: - repo.ui.write(_('atop:')) + repo.ui.write(_(b'atop:')) displayer.show(target) - if confirm and ui.prompt('perform evolve? [Ny]', 'n') != 'y': - raise error.Abort(_('evolve aborted by user')) - todo = 'hg rebase -r %s -d %s\n' % (orig, target) + if confirm and ui.prompt(b'perform evolve? [Ny]', b'n') != b'y': + raise error.Abort(_(b'evolve aborted by user')) + todo = b'hg rebase -r %s -d %s\n' % (orig, target) if dryrun: if progresscb: progresscb() repo.ui.write(todo) - return (False, ".") + return (False, b".") else: repo.ui.note(todo) if progresscb: progresscb() - try: + with state.saver(evolvestate, {b'current': orig.node()}): newid = relocate(repo, orig, target, evolvestate, pctx, - keepbranch, 'orphan') + keepbranch, b'orphan') return (True, newid) - except error.InterventionRequired: - ops = {'current': orig.node()} - evolvestate.addopts(ops) - evolvestate.save() - raise def _solvephasedivergence(ui, repo, bumped, evolvestate, displayer, dryrun=False, confirm=False, progresscb=None): @@ -202,32 +197,32 @@ bumped = repo[bumped.rev()] # For now we deny bumped merge if len(bumped.parents()) > 1: - msg = _('skipping %s : we do not handle merge yet\n') % bumped + msg = _(b'skipping %s : we do not handle merge yet\n') % bumped ui.write_err(msg) - return (False, ".") - prec = next(repo.set('last(allpredecessors(%d) and public())', bumped.rev())) + return (False, b".") + prec = next(repo.set(b'last(allpredecessors(%d) and public())', bumped.rev())) # For now we deny target merge if len(prec.parents()) > 1: - msg = _('skipping: %s: public version is a merge, ' - 'this is not handled yet\n') % prec + msg = _(b'skipping: %s: public version is a merge, ' + b'this is not handled yet\n') % prec ui.write_err(msg) - return (False, ".") + return (False, b".") if not ui.quiet or confirm: - repo.ui.write(_('recreate:'), label='evolve.operation') + repo.ui.write(_(b'recreate:'), label=b'evolve.operation') displayer.show(bumped) - repo.ui.write(_('atop:')) + repo.ui.write(_(b'atop:')) displayer.show(prec) - if confirm and ui.prompt(_('perform evolve? [Ny]'), 'n') != 'y': - raise error.Abort(_('evolve aborted by user')) + if confirm and ui.prompt(_(b'perform evolve? [Ny]'), b'n') != b'y': + raise error.Abort(_(b'evolve aborted by user')) if dryrun: - todo = 'hg rebase --rev %s --dest %s;\n' % (bumped, prec.p1()) + todo = b'hg rebase --rev %s --dest %s;\n' % (bumped, prec.p1()) repo.ui.write(todo) - repo.ui.write(('hg update %s;\n' % prec)) - repo.ui.write(('hg revert --all --rev %s;\n' % bumped)) - repo.ui.write(('hg commit --msg "%s update to %s"\n' % + repo.ui.write((b'hg update %s;\n' % prec)) + repo.ui.write((b'hg revert --all --rev %s;\n' % bumped)) + repo.ui.write((b'hg commit --msg "%s update to %s"\n' % (TROUBLES['PHASEDIVERGENT'], bumped))) - return (False, ".") + return (False, b".") if progresscb: progresscb() @@ -237,24 +232,20 @@ # evolved or any other operation which can change parent. In such cases, # when parents are not same, we first rebase the divergent changeset onto # parent or precursor and then perform later steps - if not list(repo.set('parents(%d) and parents(%d)', bumped.rev(), prec.rev())): + if not list(repo.set(b'parents(%d) and parents(%d)', bumped.rev(), prec.rev())): # Need to rebase the changeset at the right place repo.ui.status( - _('rebasing to destination parent: %s\n') % prec.p1()) - try: + _(b'rebasing to destination parent: %s\n') % prec.p1()) + with state.saver(evolvestate, {b'current': bumped.hex(), + b'precursor': prec.hex()}): newnode = relocate(repo, bumped, prec.p1(), evolvestate, - category='phasedivergent') + category=b'phasedivergent') if newnode is not None: new = repo[newnode] obsolete.createmarkers(repo, [(bumped, (new,))], - operation='evolve') + operation=b'evolve') bumped = new - evolvestate['temprevs'].append(newnode) - except error.InterventionRequired: - evolvestate['current'] = bumped.hex() - evolvestate['precursor'] = prec.hex() - evolvestate.save() - raise + evolvestate[b'temprevs'].append(newnode) return _resolvephasedivergent(ui, repo, prec, bumped) @@ -284,7 +275,7 @@ merge.update(repo, bumped.node(), ancestor=prec, mergeancestor=True, branchmerge=True, force=False, wc=wctx) if not wctx.isempty(): - text = '%s update to %s:\n\n' % (TROUBLES['PHASEDIVERGENT'], prec) + text = b'%s update to %s:\n\n' % (TROUBLES['PHASEDIVERGENT'], prec) text += bumped.description() memctx = wctx.tomemctx(text, parents=(prec.node(), nodemod.nullid), @@ -294,14 +285,14 @@ newid = repo.commitctx(memctx) replacementnode = newid if newid is None: - repo.ui.status(_('no changes to commit\n')) - obsolete.createmarkers(repo, [(bumped, ())], operation='evolve') + repo.ui.status(_(b'no changes to commit\n')) + obsolete.createmarkers(repo, [(bumped, ())], operation=b'evolve') newid = prec.node() else: - repo.ui.status(_('committed as %s\n') % nodemod.short(newid)) + repo.ui.status(_(b'committed as %s\n') % nodemod.short(newid)) phases.retractboundary(repo, tr, bumped.phase(), [newid]) obsolete.createmarkers(repo, [(bumped, (repo[newid],))], - flag=obsolete.bumpedfix, operation='evolve') + flag=obsolete.bumpedfix, operation=b'evolve') bmupdate(newid) # reroute the working copy parent to the new changeset with repo.dirstate.parentchange(): @@ -320,45 +311,45 @@ """ repo = repo.unfiltered() divergent = repo[divergent.rev()] - evolvestate['divergent'] = divergent.node() - evolvestate['orig-divergent'] = divergent.node() + evolvestate[b'divergent'] = divergent.node() + evolvestate[b'orig-divergent'] = divergent.node() # sometimes we will relocate a node in case of different parents and we can # encounter conflicts after relocation is done while solving # content-divergence and if the user calls `hg evolve --stop`, we need to # strip that relocated commit. However if `--all` is passed, we need to # reset this value for each content-divergence resolution which we are doing # below. - evolvestate['relocated'] = None - evolvestate['relocating'] = False + evolvestate[b'relocated'] = None + evolvestate[b'relocating'] = False # in case or relocation we get a new other node, we need to store the old # other for purposes like `--abort` or `--stop` - evolvestate['old-other'] = None + evolvestate[b'old-other'] = None base, others = divergentdata(divergent) # we don't handle split in content-divergence yet if len(others) > 1: - othersstr = "[%s]" % (','.join([bytes(i) for i in others])) - msg = _("skipping %s: %s with a changeset that got split" - " into multiple ones:\n" - "|[%s]\n" - "| This is not handled by automatic evolution yet\n" - "| You have to fallback to manual handling with commands " - "such as:\n" - "| - hg touch -D\n" - "| - hg prune\n" - "| \n" - "| You should contact your local evolution Guru for help.\n" + othersstr = b"[%s]" % (b','.join([bytes(i) for i in others])) + msg = _(b"skipping %s: %s with a changeset that got split" + b" into multiple ones:\n" + b"|[%s]\n" + b"| This is not handled by automatic evolution yet\n" + b"| You have to fallback to manual handling with commands " + b"such as:\n" + b"| - hg touch -D\n" + b"| - hg prune\n" + b"| \n" + b"| You should contact your local evolution Guru for help.\n" ) % (divergent, TROUBLES['CONTENTDIVERGENT'], othersstr) ui.write_err(msg) - return (False, ".") + return (False, b".") other = others[0] - evolvestate['other-divergent'] = other.node() - evolvestate['base'] = base.node() + evolvestate[b'other-divergent'] = other.node() + evolvestate[b'base'] = base.node() def swapnodes(div, other): div, other = other, div - evolvestate['divergent'] = div.node() - evolvestate['other-divergent'] = other.node() + evolvestate[b'divergent'] = div.node() + evolvestate[b'other-divergent'] = other.node() return div, other # haspubdiv: to keep track if we are solving public content-divergence haspubdiv = False @@ -371,17 +362,17 @@ divergent, other = swapnodes(divergent, other) else: publicdiv = divergent - evolvestate['public-divergent'] = publicdiv.node() + evolvestate[b'public-divergent'] = publicdiv.node() # we don't handle merge content-divergent changesets yet if len(other.parents()) > 1: - msg = _("skipping %s: %s changeset can't be " - "a merge (yet)\n") % (divergent, TROUBLES['CONTENTDIVERGENT']) + msg = _(b"skipping %s: %s changeset can't be " + b"a merge (yet)\n") % (divergent, TROUBLES['CONTENTDIVERGENT']) ui.write_err(msg) - hint = _("You have to fallback to solving this by hand...\n" - "| This probably means redoing the merge and using \n" - "| `hg prune` to kill older version.\n") + hint = _(b"You have to fallback to solving this by hand...\n" + b"| This probably means redoing the merge and using \n" + b"| `hg prune` to kill older version.\n") ui.write_err(hint) - return (False, ".") + return (False, b".") otherp1 = other.p1().rev() divp1 = divergent.p1().rev() @@ -400,15 +391,15 @@ # the changeset on which resolution changeset will be based on resolutionparent = repo[divp1].node() - gca = repo.revs("ancestor(%d, %d)" % (otherp1, divp1)) + gca = repo.revs(b"ancestor(%d, %d)" % (otherp1, divp1)) # divonly: non-obsolete csets which are topological ancestor of "divergent" # but not "other" - divonly = repo.revs("only(%d, %d) - obsolete()" % (divergent.rev(), - other.rev())) + divonly = repo.revs(b"only(%d, %d) - obsolete()" % (divergent.rev(), + other.rev())) # otheronly: non-obsolete csets which are topological ancestor of "other" # but not "div" - otheronly = repo.revs("only(%d, %d) - obsolete()" % (other.rev(), - divergent.rev())) + otheronly = repo.revs(b"only(%d, %d) - obsolete()" % (other.rev(), + divergent.rev())) # make it exclusive set divonly = set(divonly) - {divergent.rev()} otheronly = set(otheronly) - {other.rev()} @@ -468,62 +459,62 @@ divergent, other = swapnodes(divergent, other) resolutionparent = divergent.p1().node() else: - msg = _("skipping %s: have a different parent than %s " - "(not handled yet)\n") % (divergent, other) - hint = _("| %(d)s, %(o)s are not based on the same changeset.\n" - "| With the current state of its implementation, \n" - "| evolve does not work in that case.\n" - "| rebase one of them next to the other and run \n" - "| this command again.\n" - "| - either: hg rebase --dest 'p1(%(d)s)' -r %(o)s\n" - "| - or: hg rebase --dest 'p1(%(o)s)' -r %(d)s\n" - ) % {'d': divergent, 'o': other} + msg = _(b"skipping %s: have a different parent than %s " + b"(not handled yet)\n") % (divergent, other) + hint = _(b"| %(d)s, %(o)s are not based on the same changeset.\n" + b"| With the current state of its implementation, \n" + b"| evolve does not work in that case.\n" + b"| rebase one of them next to the other and run \n" + b"| this command again.\n" + b"| - either: hg rebase --dest 'p1(%(d)s)' -r %(o)s\n" + b"| - or: hg rebase --dest 'p1(%(o)s)' -r %(d)s\n" + ) % {b'd': divergent, b'o': other} ui.write_err(msg) ui.write_err(hint) - return (False, ".") + return (False, b".") if not ui.quiet or confirm: - ui.write(_('merge:'), label='evolve.operation') + ui.write(_(b'merge:'), label=b'evolve.operation') displayer.show(divergent) - ui.write(_('with: ')) + ui.write(_(b'with: ')) displayer.show(other) - ui.write(_('base: ')) + ui.write(_(b'base: ')) displayer.show(base) - if confirm and ui.prompt(_('perform evolve? [Ny]'), 'n') != 'y': - raise error.Abort(_('evolve aborted by user')) + if confirm and ui.prompt(_(b'perform evolve? [Ny]'), b'n') != b'y': + raise error.Abort(_(b'evolve aborted by user')) if dryrun: - ui.write(('hg update -c %s &&\n' % divergent)) - ui.write(('hg merge %s &&\n' % other)) - ui.write(('hg commit -m "auto merge resolving conflict between ' - '%s and %s"&&\n' % (divergent, other))) - ui.write(('hg up -C %s &&\n' % base)) - ui.write(('hg revert --all --rev tip &&\n')) - ui.write(('hg commit -m "`hg log -r %s --template={desc}`";\n' - % divergent)) - return (False, ".") + ui.write((b'hg update -c %s &&\n' % divergent)) + ui.write((b'hg merge %s &&\n' % other)) + ui.write((b'hg commit -m "auto merge resolving conflict between ' + b'%s and %s"&&\n' % (divergent, other))) + ui.write((b'hg up -C %s &&\n' % base)) + ui.write((b'hg revert --all --rev tip &&\n')) + ui.write((b'hg commit -m "`hg log -r %s --template={desc}`";\n' + % divergent)) + return (False, b".") - evolvestate['resolutionparent'] = resolutionparent + # Sometimes we already have the other cset where we want it + if relocatereq and other == divergent.p1(): + relocatereq = False + + evolvestate[b'resolutionparent'] = resolutionparent # relocate the other divergent if required if relocatereq: # relocating will help us understand during the time of conflicts that # whether conflicts occur at reloacting or they occured at merging # content divergent changesets - evolvestate['relocating'] = True - ui.status(_('rebasing "other" content-divergent changeset %s on' - ' %s\n' % (other, divergent.p1()))) - try: + evolvestate[b'relocating'] = True + ui.status(_(b'rebasing "other" content-divergent changeset %s on' + b' %s\n' % (other, divergent.p1()))) + with state.saver(evolvestate, {b'current': other.node()}): newother = relocate(repo, other, divergent.p1(), evolvestate, keepbranch=True) - except error.InterventionRequired: - evolvestate['current'] = other.node() - evolvestate.save() - raise - evolvestate['old-other'] = other.node() + evolvestate[b'old-other'] = other.node() other = repo[newother] - evolvestate['relocating'] = False - evolvestate['relocated'] = other.node() - evolvestate['temprevs'].append(other.node()) - evolvestate['other-divergent'] = other.node() + evolvestate[b'relocating'] = False + evolvestate[b'relocated'] = other.node() + evolvestate[b'temprevs'].append(other.node()) + evolvestate[b'other-divergent'] = other.node() _mergecontentdivergents(repo, progresscb, divergent, other, base, evolvestate) @@ -545,9 +536,9 @@ # case 2) pubstr = bytes(publicdiv) othstr = bytes(other) - msg = _('content divergence resolution between %s ' - '(public) and %s has same content as %s, ' - 'discarding %s\n') + msg = _(b'content divergence resolution between %s ' + b'(public) and %s has same content as %s, ' + b'discarding %s\n') msg %= (pubstr, othstr, pubstr, othstr) repo.ui.status(msg) return (res, newnode) @@ -559,29 +550,30 @@ def _mergecontentdivergents(repo, progresscb, divergent, other, base, evolvestate): if divergent not in repo[None].parents(): - repo.ui.note(_("updating to \"local\" side of the conflict: %s\n") % + repo.ui.note(_(b"updating to \"local\" side of the conflict: %s\n") % divergent.hex()[:12]) hg.updaterepo(repo, divergent.node(), False) # merging the two content-divergent changesets - repo.ui.note(_("merging \"other\" %s changeset '%s'\n") % + repo.ui.note(_(b"merging \"other\" %s changeset '%s'\n") % (TROUBLES['CONTENTDIVERGENT'], other.hex()[:12])) if progresscb: progresscb() - mergeancestor = repo.changelog.isancestor(divergent.node(), other.node()) - stats = merge.update(repo, - other.node(), - branchmerge=True, - force=False, - ancestor=base.node(), - mergeancestor=mergeancestor) - hg._showstats(repo, stats) + with state.saver(evolvestate): + mergeancestor = repo.changelog.isancestor(divergent.node(), + other.node()) + stats = merge.update(repo, + other.node(), + branchmerge=True, + force=False, + ancestor=base.node(), + mergeancestor=mergeancestor) + hg._showstats(repo, stats) - # conflicts while merging content-divergent changesets - if compat.hasconflict(stats): - evolvestate.save() - hint = _("see 'hg help evolve.interrupted'") - raise error.InterventionRequired(_("unresolved merge conflicts"), - hint=hint) + # conflicts while merging content-divergent changesets + if compat.hasconflict(stats): + hint = _(b"see 'hg help evolve.interrupted'") + raise error.InterventionRequired(_(b"unresolved merge conflicts"), + hint=hint) def _completecontentdivergent(ui, repo, progresscb, divergent, other, base, evolvestate): @@ -590,20 +582,20 @@ # resume resolution if progresscb: progresscb() - emtpycommitallowed = repo.ui.backupconfig('ui', 'allowemptycommit') + emtpycommitallowed = repo.ui.backupconfig(b'ui', b'allowemptycommit') tr = repo.currenttransaction() assert tr is not None # whether to store the obsmarker in the evolvestate storemarker = False - resparent = evolvestate['resolutionparent'] + resparent = evolvestate[b'resolutionparent'] # whether we are solving public divergence haspubdiv = False - if evolvestate.get('public-divergent'): + if evolvestate.get(b'public-divergent'): haspubdiv = True - publicnode = evolvestate['public-divergent'] + publicnode = evolvestate[b'public-divergent'] publicdiv = repo[publicnode] - othernode = evolvestate['other-divergent'] + othernode = evolvestate[b'other-divergent'] otherdiv = repo[othernode] with repo.dirstate.parentchange(): @@ -616,7 +608,7 @@ warnmetadataloss(repo, publicdiv, otherdiv) # no changes, create markers to resolve divergence obsolete.createmarkers(repo, [(otherdiv, (publicdiv,))], - operation='evolve') + operation=b'evolve') return (True, publicnode) try: with repo.dirstate.parentchange(): @@ -642,29 +634,29 @@ # no changes new = divergent storemarker = True - repo.ui.status(_("nothing changed\n")) + repo.ui.status(_(b"nothing changed\n")) hg.updaterepo(repo, divergent.rev(), False) else: new = repo[newnode] newnode = new.node() hg.updaterepo(repo, new.rev(), False) if haspubdiv and publicdiv == divergent: - bypassphase(repo, (divergent, new), operation='evolve') + bypassphase(repo, (divergent, new), operation=b'evolve') else: obsolete.createmarkers(repo, [(divergent, (new,))], - operation='evolve') + operation=b'evolve') # creating markers and moving phases post-resolution if haspubdiv and publicdiv == other: - bypassphase(repo, (other, new), operation='evolve') + bypassphase(repo, (other, new), operation=b'evolve') else: - obsolete.createmarkers(repo, [(other, (new,))], operation='evolve') + obsolete.createmarkers(repo, [(other, (new,))], operation=b'evolve') if storemarker: # storing the marker in the evolvestate # we just store the precursors and successor pair for now, we might # want to store more data and serialize obsmarker in a better way in # future - evolvestate['obsmarkers'].append((other.node(), new.node())) + evolvestate[b'obsmarkers'].append((other.node(), new.node())) phases.retractboundary(repo, tr, other.phase(), [new.node()]) return (True, newnode) @@ -676,7 +668,7 @@ public content-divergence""" # needtowarn: aspects where we need to warn user - needtowarn = ['branch', 'topic', 'close'] + needtowarn = [b'branch', b'topic', b'close'] aspects = set() localextra = local.extra() otherextra = other.extra() @@ -688,48 +680,48 @@ aspects.add(asp) if other.description() != local.description(): - aspects.add('description') + aspects.add(b'description') if aspects: # warn user locstr = bytes(local) othstr = bytes(other) - if 'close' in aspects: - filteredasp = aspects - {'close'} + if b'close' in aspects: + filteredasp = aspects - {b'close'} if filteredasp: - msg = _('other divergent changeset %s is a closed branch head ' - 'and differs from local %s by "%s" only,' % - (othstr, locstr, ', '.join(sorted(filteredasp)))) + msg = _(b'other divergent changeset %s is a closed branch head ' + b'and differs from local %s by "%s" only,' % + (othstr, locstr, b', '.join(sorted(filteredasp)))) else: - msg = _('other divergent changeset %s is a closed branch head ' - 'and has same content as local %s,' % (othstr, locstr)) + msg = _(b'other divergent changeset %s is a closed branch head ' + b'and has same content as local %s,' % (othstr, locstr)) else: - msg = _('other divergent changeset %s has same content as local %s' - ' and differs by "%s" only,' % - (othstr, locstr, ', '.join(sorted(aspects)))) - msg += _(' discarding %s\n' % othstr) + msg = _(b'other divergent changeset %s has same content as local %s' + b' and differs by "%s" only,' % + (othstr, locstr, b', '.join(sorted(aspects)))) + msg += _(b' discarding %s\n' % othstr) repo.ui.warn(msg) -def bypassphase(repo, relation, flag=0, metadata=None, operation='evolve'): +def bypassphase(repo, relation, flag=0, metadata=None, operation=b'evolve'): """function to create a single obsmarker relation even for public csets where relation should be a single pair (prec, succ)""" # prepare metadata if metadata is None: metadata = {} - if 'user' not in metadata: - luser = repo.ui.config('devel', 'user.obsmarker') or repo.ui.username() - metadata['user'] = encoding.fromlocal(luser) + if b'user' not in metadata: + luser = repo.ui.config(b'devel', b'user.obsmarker') or repo.ui.username() + metadata[b'user'] = encoding.fromlocal(luser) # Operation metadata handling - useoperation = repo.ui.configbool('experimental', - 'evolution.track-operation') + useoperation = repo.ui.configbool(b'experimental', + b'evolution.track-operation') if useoperation and operation: - metadata['operation'] = operation + metadata[b'operation'] = operation # Effect flag metadata handling - saveeffectflag = repo.ui.configbool('experimental', - 'evolution.effect-flags') - with repo.transaction('add-obsolescence-marker') as tr: + saveeffectflag = repo.ui.configbool(b'experimental', + b'evolution.effect-flags') + with repo.transaction(b'add-obsolescence-marker') as tr: prec, succ = relation nprec = prec.node() npare = None @@ -737,7 +729,7 @@ if not nsucs: npare = tuple(p.node() for p in prec.parents()) if nprec in nsucs: - raise error.Abort(_("changeset %s cannot obsolete itself") % prec) + raise error.Abort(_(b"changeset %s cannot obsolete itself") % prec) if saveeffectflag: # The effect flag is saved in a versioned field name for @@ -747,7 +739,7 @@ except TypeError: # hg <= 4.7 effectflag = obsutil.geteffectflag((prec, (succ,))) - metadata[obsutil.EFFECTFLAGFIELD] = "%d" % effectflag + metadata[obsutil.EFFECTFLAGFIELD] = b"%d" % effectflag # create markers repo.obsstore.create(tr, nprec, nsucs, flag, parents=npare, @@ -849,11 +841,11 @@ repo.dirstate.setbranch(othbranch) else: # all the three branches are different - index = repo.ui.promptchoice(_("content divergent changesets on " - "different branches.\nchoose branch" - " for the resolution changeset. (a) " - "%s or (b) %s or (c) %s? $$ &a $$ &b" - " $$ &c") % + index = repo.ui.promptchoice(_(b"content divergent changesets on " + b"different branches.\nchoose branch" + b" for the resolution changeset. (a) " + b"%s or (b) %s or (c) %s? $$ &a $$ &b" + b" $$ &c") % (basebranch, divbranch, othbranch), 0) if index == 0: @@ -870,20 +862,20 @@ merger = simplemerge.Merge3Text(basedesc, divdesc, othdesc) mdesc = [] kwargs = {} - kwargs['name_base'] = 'base' - kwargs['base_marker'] = '|||||||' - for line in merger.merge_lines(name_a='divergent', name_b='other', + kwargs['name_base'] = b'base' + kwargs['base_marker'] = b'|||||||' + for line in merger.merge_lines(name_a=b'divergent', name_b=b'other', **kwargs): mdesc.append(line) - desc = ''.join(mdesc) + desc = b''.join(mdesc) if merger.conflicts: - prefixes = ("HG: Conflicts while merging changeset description of" - " content-divergent changesets.\nHG: Resolve conflicts" - " in commit messages to continue.\n\n") + prefixes = (b"HG: Conflicts while merging changeset description of" + b" content-divergent changesets.\nHG: Resolve conflicts" + b" in commit messages to continue.\n\n") - resolveddesc = ui.edit(prefixes + desc, ui.username(), action='desc') + resolveddesc = ui.edit(prefixes + desc, ui.username(), action=b'desc') # make sure we remove the prefixes part from final commit message if prefixes in resolveddesc: # hack, we should find something better @@ -934,17 +926,17 @@ returns the node of new commit which is formed """ if orig.rev() == dest.rev(): - msg = _('tried to relocate a node on top of itself') - hint = _("This shouldn't happen. If you still need to move changesets, " - "please do so manually with nothing to rebase - working " - "directory parent is also destination") + msg = _(b'tried to relocate a node on top of itself') + hint = _(b"This shouldn't happen. If you still need to move changesets, " + b"please do so manually with nothing to rebase - working " + b"directory parent is also destination") raise error.ProgrammingError(msg, hint=hint) if pctx is None: if len(orig.parents()) == 2: - msg = _("tried to relocate a merge commit without specifying which " - "parent should be moved") - hint = _("Specify the parent by passing in pctx") + msg = _(b"tried to relocate a merge commit without specifying which " + b"parent should be moved") + hint = _(b"Specify the parent by passing in pctx") raise error.ProgrammingError(msg, hint) pctx = orig.p1() @@ -972,8 +964,8 @@ newsha1 = nodemod.hex(successors[0][0]) commitmsg = commitmsg.replace(sha1, newsha1[:len(sha1)]) else: - repo.ui.note(_('The stale commit message reference to %s could ' - 'not be updated\n') % sha1) + repo.ui.note(_(b'The stale commit message reference to %s could ' + b'not be updated\n') % sha1) tr = repo.currenttransaction() assert tr is not None @@ -986,8 +978,8 @@ copies.duplicatecopies(repo, repo[None], dest.rev(), orig.p1().rev()) dirstatedance(repo, dest, orig.node(), None) - hint = _("see 'hg help evolve.interrupted'") - raise error.InterventionRequired(_("unresolved merge conflicts"), + hint = _(b"see 'hg help evolve.interrupted'") + raise error.InterventionRequired(_(b"unresolved merge conflicts"), hint=hint) nodenew = _relocatecommit(repo, orig, commitmsg) _finalizerelocate(repo, orig, dest, nodenew, tr, category, evolvestate) @@ -997,14 +989,14 @@ if commitmsg is None: commitmsg = orig.description() extra = dict(orig.extra()) - if 'branch' in extra: - del extra['branch'] - extra['rebase_source'] = orig.hex() + if b'branch' in extra: + del extra[b'branch'] + extra[b'rebase_source'] = orig.hex() - backup = repo.ui.backupconfig('phases', 'new-commit') + backup = repo.ui.backupconfig(b'phases', b'new-commit') try: targetphase = max(orig.phase(), phases.draft) - repo.ui.setconfig('phases', 'new-commit', targetphase, 'evolve') + repo.ui.setconfig(b'phases', b'new-commit', targetphase, b'evolve') # Commit might fail if unresolved files exist nodenew = repo.commit(text=commitmsg, user=orig.user(), date=orig.date(), extra=extra) @@ -1020,18 +1012,18 @@ if nodenew is not None: obsolete.createmarkers(repo, [(repo[nodesrc], (repo[nodenew],))], - operation='evolve') + operation=b'evolve') for book in oldbookmarks: bmchanges.append((book, nodenew)) - evolvestate['bookmarkchanges'].append((book, nodesrc)) + evolvestate[b'bookmarkchanges'].append((book, nodesrc)) else: - if category == 'orphan': - repo.ui.status(_("evolution of %d:%s created no changes " - "to commit\n") % (orig.rev(), orig)) - obsolete.createmarkers(repo, [(repo[nodesrc], ())], operation='evolve') + if category == b'orphan': + repo.ui.status(_(b"evolution of %d:%s created no changes " + b"to commit\n") % (orig.rev(), orig)) + obsolete.createmarkers(repo, [(repo[nodesrc], ())], operation=b'evolve') # Behave like rebase, move bookmarks to dest for book in oldbookmarks: - evolvestate['bookmarkchanges'].append((book, nodesrc)) + evolvestate[b'bookmarkchanges'].append((book, nodesrc)) bmchanges.append((book, dest.node())) for book in destbookmarks: # restore bookmark that rebase move bmchanges.append((book, dest.node())) @@ -1041,61 +1033,61 @@ def _evolvemerge(repo, orig, dest, pctx, keepbranch): """Used by the evolve function to merge dest on top of pctx. return the same tuple as merge.graft""" - if repo['.'].rev() != dest.rev(): + if repo[b'.'].rev() != dest.rev(): merge.update(repo, dest, branchmerge=False, force=True) if repo._activebookmark: - repo.ui.status(_("(leaving bookmark %s)\n") % repo._activebookmark) + repo.ui.status(_(b"(leaving bookmark %s)\n") % repo._activebookmark) bookmarksmod.deactivate(repo) if keepbranch: repo.dirstate.setbranch(orig.branch()) if util.safehasattr(repo, 'currenttopic'): # uurrgs # there no other topic setter yet - if not orig.topic() and repo.vfs.exists('topic'): - repo.vfs.unlink('topic') + if not orig.topic() and repo.vfs.exists(b'topic'): + repo.vfs.unlink(b'topic') else: - with repo.vfs.open('topic', 'w') as f: + with repo.vfs.open(b'topic', b'w') as f: f.write(orig.topic()) - return merge.graft(repo, orig, pctx, ['destination', 'evolving'], True) + return merge.graft(repo, orig, pctx, [b'destination', b'evolving'], True) instabilities_map = { - 'contentdivergent': "content-divergent", - 'phasedivergent': "phase-divergent" + b'contentdivergent': b"content-divergent", + b'phasedivergent': b"phase-divergent" } def _selectrevs(repo, allopt, revopt, anyopt, targetcat): """select troubles in repo matching according to given options""" revs = set() if allopt or revopt: - revs = repo.revs("%s()" % targetcat) + revs = repo.revs(b"%s()" % targetcat) if revopt: revs = scmutil.revrange(repo, revopt) & revs elif not anyopt: - topic = getattr(repo, 'currenttopic', '') + topic = getattr(repo, 'currenttopic', b'') if topic: - revs = repo.revs('topic(%s)', topic) & revs - elif targetcat == 'orphan': + revs = repo.revs(b'topic(%s)', topic) & revs + elif targetcat == b'orphan': revs = _aspiringdescendant(repo, - repo.revs('(.::) - obsolete()::')) + repo.revs(b'(.::) - obsolete()::')) revs = set(revs) - if targetcat == 'contentdivergent': + if targetcat == b'contentdivergent': # Pick one divergent per group of divergents revs = _dedupedivergents(repo, revs) elif anyopt: - revs = repo.revs('first(%s())' % (targetcat)) - elif targetcat == 'orphan': - revs = set(_aspiringchildren(repo, repo.revs('(.::) - obsolete()::'))) + revs = repo.revs(b'first(%s())' % (targetcat)) + elif targetcat == b'orphan': + revs = set(_aspiringchildren(repo, repo.revs(b'(.::) - obsolete()::'))) if 1 < len(revs): - msg = "multiple evolve candidates" - hint = (_("select one of %s with --rev") - % ', '.join([bytes(repo[r]) for r in sorted(revs)])) + msg = b"multiple evolve candidates" + hint = (_(b"select one of %s with --rev") + % b', '.join([bytes(repo[r]) for r in sorted(revs)])) raise error.Abort(msg, hint=hint) - elif instabilities_map.get(targetcat, targetcat) in repo['.'].instabilities(): - revs = set([repo['.'].rev()]) + elif instabilities_map.get(targetcat, targetcat) in repo[b'.'].instabilities(): + revs = set([repo[b'.'].rev()]) return revs def _dedupedivergents(repo, revs): @@ -1124,14 +1116,14 @@ XXX this woobly function won't survive XXX """ repo = ctx._repo.unfiltered() - for base in repo.set('reverse(allpredecessors(%d))', ctx.rev()): + for base in repo.set(b'reverse(allpredecessors(%d))', ctx.rev()): newer = obsutil.successorssets(ctx._repo, base.node()) # drop filter and solution including the original ctx newer = [n for n in newer if n and ctx.node() not in n] if newer: return base, tuple(ctx._repo[o] for o in newer[0]) - raise error.Abort(_("base of divergent changeset %s not found") % ctx, - hint=_('this case is not yet handled')) + raise error.Abort(_(b"base of divergent changeset %s not found") % ctx, + hint=_(b'this case is not yet handled')) def _aspiringdescendant(repo, revs): """Return a list of changectx which can be stabilized on top of pctx or @@ -1139,7 +1131,7 @@ target = set(revs) result = set(target) paths = collections.defaultdict(set) - for r in repo.revs('orphan() - %ld', revs): + for r in repo.revs(b'orphan() - %ld', revs): for d in _possibledestination(repo, r): paths[d].add(r) @@ -1158,7 +1150,7 @@ one of its descendants. Empty list if none can be found.""" target = set(revs) result = [] - for r in repo.revs('orphan() - %ld', revs): + for r in repo.revs(b'orphan() - %ld', revs): dest = _possibledestination(repo, r) if target & dest: result.append(r) @@ -1191,104 +1183,104 @@ def _handlenotrouble(ui, repo, allopt, revopt, anyopt, targetcat): """Used by the evolve function to display an error message when no troubles can be resolved""" - troublecategories = ['phasedivergent', 'contentdivergent', 'orphan'] + troublecategories = [b'phasedivergent', b'contentdivergent', b'orphan'] unselectedcategories = [c for c in troublecategories if c != targetcat] msg = None hint = None retoverride = None troubled = { - "orphan": repo.revs("orphan()"), - "contentdivergent": repo.revs("contentdivergent()"), - "phasedivergent": repo.revs("phasedivergent()"), - "all": repo.revs("unstable()"), + b"orphan": repo.revs(b"orphan()"), + b"contentdivergent": repo.revs(b"contentdivergent()"), + b"phasedivergent": repo.revs(b"phasedivergent()"), + b"all": repo.revs(b"unstable()"), } hintmap = { - 'phasedivergent': _("do you want to use --phase-divergent"), - 'phasedivergent+contentdivergent': _("do you want to use " - "--phase-divergent or" - " --content-divergent"), - 'phasedivergent+orphan': _("do you want to use --phase-divergent" - " or --orphan"), - 'contentdivergent': _("do you want to use --content-divergent"), - 'contentdivergent+orphan': _("do you want to use --content-divergent" - " or --orphan"), - 'orphan': _("do you want to use --orphan"), - 'any+phasedivergent': _("do you want to use --any (or --rev) and" - " --phase-divergent"), - 'any+phasedivergent+contentdivergent': _("do you want to use --any" - " (or --rev) and" - " --phase-divergent or" - " --content-divergent"), - 'any+phasedivergent+orphan': _("do you want to use --any (or --rev)" - " and --phase-divergent or --orphan"), - 'any+contentdivergent': _("do you want to use --any (or --rev) and" - " --content-divergent"), - 'any+contentdivergent+orphan': _("do you want to use --any (or --rev)" - " and --content-divergent or " - "--orphan"), - 'any+orphan': _("do you want to use --any (or --rev)" - "and --orphan"), + b'phasedivergent': _(b"do you want to use --phase-divergent"), + b'phasedivergent+contentdivergent': _(b"do you want to use " + b"--phase-divergent or" + b" --content-divergent"), + b'phasedivergent+orphan': _(b"do you want to use --phase-divergent" + b" or --orphan"), + b'contentdivergent': _(b"do you want to use --content-divergent"), + b'contentdivergent+orphan': _(b"do you want to use --content-divergent" + b" or --orphan"), + b'orphan': _(b"do you want to use --orphan"), + b'any+phasedivergent': _(b"do you want to use --any (or --rev) and" + b" --phase-divergent"), + b'any+phasedivergent+contentdivergent': _(b"do you want to use --any" + b" (or --rev) and" + b" --phase-divergent or" + b" --content-divergent"), + b'any+phasedivergent+orphan': _(b"do you want to use --any (or --rev)" + b" and --phase-divergent or --orphan"), + b'any+contentdivergent': _(b"do you want to use --any (or --rev) and" + b" --content-divergent"), + b'any+contentdivergent+orphan': _(b"do you want to use --any (or --rev)" + b" and --content-divergent or " + b"--orphan"), + b'any+orphan': _(b"do you want to use --any (or --rev)" + b"and --orphan"), } if revopt: revs = scmutil.revrange(repo, revopt) if not revs: - msg = _("set of specified revisions is empty") + msg = _(b"set of specified revisions is empty") else: - msg = _("no %s changesets in specified revisions") % targetcat + msg = _(b"no %s changesets in specified revisions") % targetcat othertroubles = [] for cat in unselectedcategories: if revs & troubled[cat]: othertroubles.append(cat) if othertroubles: - hint = hintmap['+'.join(othertroubles)] + hint = hintmap[b'+'.join(othertroubles)] elif anyopt: - msg = _("no %s changesets to evolve") % targetcat + msg = _(b"no %s changesets to evolve") % targetcat othertroubles = [] for cat in unselectedcategories: if troubled[cat]: othertroubles.append(cat) if othertroubles: - hint = hintmap['+'.join(othertroubles)] + hint = hintmap[b'+'.join(othertroubles)] else: # evolve without any option = relative to the current wdir - if targetcat == 'orphan': - msg = _("nothing to evolve on current working copy parent") + if targetcat == b'orphan': + msg = _(b"nothing to evolve on current working copy parent") else: - msg = _("current working copy parent is not %s") % targetcat + msg = _(b"current working copy parent is not %s") % targetcat - p1 = repo['.'].rev() + p1 = repo[b'.'].rev() othertroubles = [] for cat in unselectedcategories: if p1 in troubled[cat]: othertroubles.append(cat) if othertroubles: - hint = hintmap['+'.join(othertroubles)] + hint = hintmap[b'+'.join(othertroubles)] else: length = len(troubled[targetcat]) if length: - hint = _("%d other %s in the repository, do you want --any " - "or --rev") % (length, targetcat) + hint = _(b"%d other %s in the repository, do you want --any " + b"or --rev") % (length, targetcat) else: othertroubles = [] for cat in unselectedcategories: if troubled[cat]: othertroubles.append(cat) if othertroubles: - hint = hintmap['any+' + ('+'.join(othertroubles))] + hint = hintmap[b'any+' + (b'+'.join(othertroubles))] else: - msg = _("no troubled changesets") + msg = _(b"no troubled changesets") # Exit with a 0 (success) status in this case. retoverride = 0 assert msg is not None - ui.write_err("%s\n" % msg) + ui.write_err(b"%s\n" % msg) if hint: - ui.write_err("(%s)\n" % hint) + ui.write_err(b"(%s)\n" % hint) ret = 2 else: ret = 1 @@ -1308,21 +1300,21 @@ def listtroubles(ui, repo, troublecategories, **opts): """Print all the troubles for the repo (or given revset)""" - troublecategories = troublecategories or ['contentdivergent', 'orphan', 'phasedivergent'] - showunstable = 'orphan' in troublecategories - showbumped = 'phasedivergent' in troublecategories - showdivergent = 'contentdivergent' in troublecategories + troublecategories = troublecategories or [b'contentdivergent', b'orphan', b'phasedivergent'] + showunstable = b'orphan' in troublecategories + showbumped = b'phasedivergent' in troublecategories + showdivergent = b'contentdivergent' in troublecategories - revs = repo.revs('+'.join("%s()" % t for t in troublecategories)) + revs = repo.revs(b'+'.join(b"%s()" % t for t in troublecategories)) if opts.get('rev'): revs = scmutil.revrange(repo, opts.get('rev')) - fm = ui.formatter('evolvelist', pycompat.byteskwargs(opts)) + fm = ui.formatter(b'evolvelist', pycompat.byteskwargs(opts)) for rev in revs: ctx = repo[rev] unpars = _preparelistctxs(ctx.parents(), lambda p: p.orphan()) obspars = _preparelistctxs(ctx.parents(), lambda p: p.obsolete()) - imprecs = _preparelistctxs(repo.set("allpredecessors(%n)", ctx.node()), + imprecs = _preparelistctxs(repo.set(b"allpredecessors(%n)", ctx.node()), lambda p: not p.mutable()) dsets = divergentsets(repo, ctx) @@ -1332,55 +1324,55 @@ desc = ctx.description() if desc: desc = desc.splitlines()[0] - desc = (desc[:desclen] + '...') if len(desc) > desclen else desc - fm.plain('%s: ' % ctx.hex()[:hashlen]) - fm.plain('%s\n' % desc) + desc = (desc[:desclen] + b'...') if len(desc) > desclen else desc + fm.plain(b'%s: ' % ctx.hex()[:hashlen]) + fm.plain(b'%s\n' % desc) fm.data(node=ctx.hex(), rev=ctx.rev(), desc=desc, phase=ctx.phasestr()) for unpar in unpars if showunstable else []: - fm.plain(' %s: %s (%s parent)\n' % (TROUBLES['ORPHAN'], - unpar[:hashlen], - TROUBLES['ORPHAN'])) + fm.plain(b' %s: %s (%s parent)\n' % (TROUBLES['ORPHAN'], + unpar[:hashlen], + TROUBLES['ORPHAN'])) for obspar in obspars if showunstable else []: - fm.plain(' %s: %s (obsolete parent)\n' % (TROUBLES['ORPHAN'], - obspar[:hashlen])) + fm.plain(b' %s: %s (obsolete parent)\n' % (TROUBLES['ORPHAN'], + obspar[:hashlen])) for imprec in imprecs if showbumped else []: - fm.plain(' %s: %s (immutable precursor)\n' % + fm.plain(b' %s: %s (immutable precursor)\n' % (TROUBLES['PHASEDIVERGENT'], imprec[:hashlen])) if dsets and showdivergent: for dset in dsets: - fm.plain(' %s: ' % TROUBLES['CONTENTDIVERGENT']) + fm.plain(b' %s: ' % TROUBLES['CONTENTDIVERGENT']) first = True - for n in dset['divergentnodes']: - t = "%s (%s)" if first else " %s (%s)" + for n in dset[b'divergentnodes']: + t = b"%s (%s)" if first else b" %s (%s)" first = False fm.plain(t % (nodemod.hex(n)[:hashlen], repo[n].phasestr())) - comprec = nodemod.hex(dset['commonprecursor'])[:hashlen] - fm.plain(" (precursor %s)\n" % comprec) - fm.plain("\n") + comprec = nodemod.hex(dset[b'commonprecursor'])[:hashlen] + fm.plain(b" (precursor %s)\n" % comprec) + fm.plain(b"\n") # templater-friendly section _formatctx(fm, ctx) troubles = [] for unpar in unpars: - troubles.append({'troubletype': TROUBLES['ORPHAN'], - 'sourcenode': unpar, 'sourcetype': 'orphanparent'}) + troubles.append({b'troubletype': TROUBLES['ORPHAN'], + b'sourcenode': unpar, b'sourcetype': b'orphanparent'}) for obspar in obspars: - troubles.append({'troubletype': TROUBLES['ORPHAN'], - 'sourcenode': obspar, - 'sourcetype': 'obsoleteparent'}) + troubles.append({b'troubletype': TROUBLES['ORPHAN'], + b'sourcenode': obspar, + b'sourcetype': b'obsoleteparent'}) for imprec in imprecs: - troubles.append({'troubletype': TROUBLES['PHASEDIVERGENT'], - 'sourcenode': imprec, - 'sourcetype': 'immutableprecursor'}) + troubles.append({b'troubletype': TROUBLES['PHASEDIVERGENT'], + b'sourcenode': imprec, + b'sourcetype': b'immutableprecursor'}) for dset in dsets: - divnodes = [{'node': nodemod.hex(n), - 'phase': repo[n].phasestr(), - } for n in dset['divergentnodes']] - troubles.append({'troubletype': TROUBLES['CONTENTDIVERGENT'], - 'commonprecursor': nodemod.hex(dset['commonprecursor']), - 'divergentnodes': divnodes}) + divnodes = [{b'node': nodemod.hex(n), + b'phase': repo[n].phasestr(), + } for n in dset[b'divergentnodes']] + troubles.append({b'troubletype': TROUBLES['CONTENTDIVERGENT'], + b'commonprecursor': nodemod.hex(dset[b'commonprecursor']), + b'divergentnodes': divnodes}) fm.data(troubles=troubles) fm.end() @@ -1391,61 +1383,61 @@ if opts['continue']: if opts['any']: - raise error.Abort(_('cannot specify both "--any" and "--continue"')) + raise error.Abort(_(b'cannot specify both "--any" and "--continue"')) if opts['all']: - raise error.Abort(_('cannot specify both "--all" and "--continue"')) + raise error.Abort(_(b'cannot specify both "--all" and "--continue"')) if opts['rev']: - raise error.Abort(_('cannot specify both "--rev" and "--continue"')) + raise error.Abort(_(b'cannot specify both "--rev" and "--continue"')) if opts['stop']: - raise error.Abort(_('cannot specify both "--stop" and' - ' "--continue"')) + raise error.Abort(_(b'cannot specify both "--stop" and' + b' "--continue"')) if opts['abort']: - raise error.Abort(_('cannot specify both "--abort" and' - ' "--continue"')) + raise error.Abort(_(b'cannot specify both "--abort" and' + b' "--continue"')) if opts['stop']: if opts['any']: - raise error.Abort(_('cannot specify both "--any" and "--stop"')) + raise error.Abort(_(b'cannot specify both "--any" and "--stop"')) if opts['all']: - raise error.Abort(_('cannot specify both "--all" and "--stop"')) + raise error.Abort(_(b'cannot specify both "--all" and "--stop"')) if opts['rev']: - raise error.Abort(_('cannot specify both "--rev" and "--stop"')) + raise error.Abort(_(b'cannot specify both "--rev" and "--stop"')) if opts['abort']: - raise error.Abort(_('cannot specify both "--abort" and "--stop"')) + raise error.Abort(_(b'cannot specify both "--abort" and "--stop"')) if opts['abort']: if opts['any']: - raise error.Abort(_('cannot specify both "--any" and "--abort"')) + raise error.Abort(_(b'cannot specify both "--any" and "--abort"')) if opts['all']: - raise error.Abort(_('cannot specify both "--all" and "--abort"')) + raise error.Abort(_(b'cannot specify both "--all" and "--abort"')) if opts['rev']: - raise error.Abort(_('cannot specify both "--rev" and "--abort"')) + raise error.Abort(_(b'cannot specify both "--rev" and "--abort"')) if opts['rev']: if opts['any']: - raise error.Abort(_('cannot specify both "--rev" and "--any"')) + raise error.Abort(_(b'cannot specify both "--rev" and "--any"')) if opts['all']: - raise error.Abort(_('cannot specify both "--rev" and "--all"')) + raise error.Abort(_(b'cannot specify both "--rev" and "--all"')) # Backward compatibility if opts['unstable']: - msg = ("'evolve --unstable' is deprecated, " - "use 'evolve --orphan'") - repo.ui.deprecwarn(msg, '4.4') + msg = (b"'evolve --unstable' is deprecated, " + b"use 'evolve --orphan'") + repo.ui.deprecwarn(msg, b'4.4') opts['orphan'] = opts['divergent'] if opts['divergent']: - msg = ("'evolve --divergent' is deprecated, " - "use 'evolve --content-divergent'") - repo.ui.deprecwarn(msg, '4.4') + msg = (b"'evolve --divergent' is deprecated, " + b"use 'evolve --content-divergent'") + repo.ui.deprecwarn(msg, b'4.4') opts['content_divergent'] = opts['divergent'] if opts['bumped']: - msg = ("'evolve --bumped' is deprecated, " - "use 'evolve --phase-divergent'") - repo.ui.deprecwarn(msg, '4.4') + msg = (b"'evolve --bumped' is deprecated, " + b"use 'evolve --phase-divergent'") + repo.ui.deprecwarn(msg, b'4.4') opts['phase_divergent'] = opts['bumped'] @@ -1458,8 +1450,8 @@ unfi = repo.unfiltered() succ = utility._singlesuccessor(repo, unfi[startnode]) hg.updaterepo(repo, repo[succ].node(), False) - if repo['.'].node() != startnode: - ui.status(_('working directory is now at %s\n') % repo['.']) + if repo[b'.'].node() != startnode: + ui.status(_(b'working directory is now at %s\n') % repo[b'.']) def divergentsets(repo, ctx): """Compute sets of commits divergent with a given one""" @@ -1481,38 +1473,38 @@ divergence = [] for divset, b in base.items(): divergence.append({ - 'divergentnodes': divset, - 'commonprecursor': b + b'divergentnodes': divset, + b'commonprecursor': b }) return divergence @eh.command( - 'evolve|stabilize|solve', - [('n', 'dry-run', False, - _('do not perform actions, just print what would be done')), - ('', 'confirm', False, - _('ask for confirmation before performing the action')), - ('A', 'any', False, - _('also consider troubled changesets unrelated to current working ' - 'directory')), - ('r', 'rev', [], _('solves troubles of these revisions'), _('REV')), - ('', 'bumped', False, _('solves only bumped changesets (DEPRECATED)')), - ('', 'phase-divergent', False, _('solves only phase-divergent changesets')), - ('', 'divergent', False, _('solves only divergent changesets (DEPRECATED)')), - ('', 'content-divergent', False, _('solves only content-divergent changesets')), - ('', 'unstable', False, _('solves only unstable changesets (DEPRECATED)')), - ('', 'orphan', False, _('solves only orphan changesets (default)')), - ('a', 'all', None, _('evolve all troubled changesets related to the current' - ' working directory and its descendants (default)')), - ('', 'update', False, _('update to the head of evolved changesets')), - ('c', 'continue', False, _('continue an interrupted evolution')), - ('', 'stop', False, _('stop the interrupted evolution')), - ('', 'abort', False, _('abort the interrupted evolution')), - ('l', 'list', False, _('provide details on troubled changesets' - ' in the repo')), - ] + mergetoolopts, - _('[OPTIONS]...'), + b'evolve|stabilize|solve', + [(b'n', b'dry-run', False, + _(b'do not perform actions, just print what would be done')), + (b'', b'confirm', False, + _(b'ask for confirmation before performing the action')), + (b'A', b'any', False, + _(b'also consider troubled changesets unrelated to current working ' + b'directory')), + (b'r', b'rev', [], _(b'solves troubles of these revisions'), _(b'REV')), + (b'', b'bumped', False, _(b'solves only bumped changesets (DEPRECATED)')), + (b'', b'phase-divergent', False, _(b'solves only phase-divergent changesets')), + (b'', b'divergent', False, _(b'solves only divergent changesets (DEPRECATED)')), + (b'', b'content-divergent', False, _(b'solves only content-divergent changesets')), + (b'', b'unstable', False, _(b'solves only unstable changesets (DEPRECATED)')), + (b'', b'orphan', False, _(b'solves only orphan changesets (default)')), + (b'a', b'all', None, _(b'evolve all troubled changesets related to the current' + b' working directory and its descendants (default)')), + (b'', b'update', False, _(b'update to the head of evolved changesets')), + (b'c', b'continue', False, _(b'continue an interrupted evolution')), + (b'', b'stop', False, _(b'stop the interrupted evolution')), + (b'', b'abort', False, _(b'abort the interrupted evolution')), + (b'l', b'list', False, _(b'provide details on troubled changesets' + b' in the repo')), + ] + mergetoolopts, + _(b'[OPTIONS]...'), helpbasic=True ) def evolve(ui, repo, **opts): @@ -1607,7 +1599,10 @@ aborts the interrupted evolve and undoes all the resolution which have happened """ + with repo.wlock(), repo.lock(): + return _performevolve(ui, repo, **opts) +def _performevolve(ui, repo, **opts): opts = _checkevolveopts(repo, opts) # Options contopt = opts['continue'] @@ -1615,7 +1610,7 @@ allopt = opts['all'] if allopt is None: allopt = True - startnode = repo['.'].node() + startnode = repo[b'.'].node() dryrunopt = opts['dry_run'] confirmopt = opts['confirm'] revopt = opts['rev'] @@ -1624,56 +1619,56 @@ shouldupdate = opts['update'] troublecategories = { - 'phasedivergent': 'phase_divergent', - 'contentdivergent': 'content_divergent', - 'orphan': 'orphan', + b'phasedivergent': r'phase_divergent', + b'contentdivergent': r'content_divergent', + b'orphan': r'orphan', } specifiedcategories = [k for k, v in troublecategories.items() if opts[v]] if opts['list']: - ui.pager('evolve') + ui.pager(b'evolve') listtroubles(ui, repo, specifiedcategories, **opts) return - targetcat = 'orphan' + targetcat = b'orphan' if 1 < len(specifiedcategories): - msg = _('cannot specify more than one trouble category to solve (yet)') + msg = _(b'cannot specify more than one trouble category to solve (yet)') raise error.Abort(msg) elif len(specifiedcategories) == 1: targetcat = specifiedcategories[0] - ui.setconfig('ui', 'forcemerge', opts.get('tool', ''), 'evolve') + ui.setconfig(b'ui', b'forcemerge', opts.get('tool', r''), b'evolve') evolvestate = state.cmdstate(repo) # Continuation handling if contopt: if not evolvestate: - raise error.Abort(_('no interrupted evolve to continue')) + raise error.Abort(_(b'no interrupted evolve to continue')) evolvestate.load() continueevolve(ui, repo, evolvestate) - if evolvestate['command'] != 'evolve': + if evolvestate[b'command'] != b'evolve': evolvestate.delete() return - startnode = repo.unfiltered()[evolvestate['startnode']] - if 'update' in evolvestate: - shouldupdate = evolvestate['update'] + startnode = repo.unfiltered()[evolvestate[b'startnode']] + if b'update' in evolvestate: + shouldupdate = evolvestate[b'update'] evolvestate.delete() elif stopopt: if not evolvestate: - raise error.Abort(_('no interrupted evolve to stop')) + raise error.Abort(_(b'no interrupted evolve to stop')) evolvestate.load() stopevolve(ui, repo, evolvestate) evolvestate.delete() return elif abortopt: if not evolvestate: - raise error.Abort(_('no interrupted evolve to abort')) + raise error.Abort(_(b'no interrupted evolve to abort')) evolvestate.load() # `hg next --evolve` in play - if evolvestate['command'] != 'evolve': - pctx = repo['.'] + if evolvestate[b'command'] != b'evolve': + pctx = repo[b'.'] hg.updaterepo(repo, pctx.node(), True) - ui.status(_('evolve aborted\n')) - ui.status(_('working directory is now at %s\n') + ui.status(_(b'evolve aborted\n')) + ui.status(_(b'working directory is now at %s\n') % pctx.hex()[:12]) evolvestate.delete() return 0 @@ -1681,7 +1676,7 @@ else: cmdutil.bailifchanged(repo) - obswdir = repo['.'].obsolete() + obswdir = repo[b'.'].obsolete() revs = _selectrevs(repo, allopt, revopt, anyopt, targetcat) if not (revs or obswdir): @@ -1703,37 +1698,36 @@ def progresscb(): if showprogress: - compat.progress(ui, _('evolve'), seen, unit=_('changesets'), + compat.progress(ui, _(b'evolve'), seen, unit=_(b'changesets'), total=count) # Order the revisions revs = _orderrevs(repo, revs) # cbor does not know how to serialize sets, using list for skippedrevs - stateopts = {'category': targetcat, 'replacements': {}, - 'revs': list(revs), 'confirm': confirmopt, - 'startnode': startnode, 'skippedrevs': [], - 'command': 'evolve', 'orphanmerge': False, - 'bookmarkchanges': [], 'temprevs': [], 'obsmarkers': [], - 'update': shouldupdate} + stateopts = {b'category': targetcat, b'replacements': {}, + b'revs': list(revs), b'confirm': confirmopt, + b'startnode': startnode, b'skippedrevs': [], + b'command': b'evolve', b'orphanmerge': False, + b'bookmarkchanges': [], b'temprevs': [], b'obsmarkers': [], + b'update': shouldupdate} evolvestate.addopts(stateopts) # lastsolved: keep track of successor of last troubled cset we evolved # to confirm that if atop msg should be suppressed to remove redundancy lastsolved = None - activetopic = getattr(repo, 'currenttopic', '') - with repo.wlock(), repo.lock(): - tr = repo.transaction("evolve") - with util.acceptintervention(tr): - for rev in revs: - lastsolved = _solveonerev(ui, repo, rev, evolvestate, - activetopic, dryrunopt, - confirmopt, progresscb, - targetcat, lastsolved) - seen += 1 + activetopic = getattr(repo, 'currenttopic', b'') + tr = repo.transaction(b"evolve") + with util.acceptintervention(tr): + for rev in revs: + lastsolved = _solveonerev(ui, repo, rev, evolvestate, + activetopic, dryrunopt, + confirmopt, progresscb, + targetcat, lastsolved) + seen += 1 if showprogress: - compat.progress(ui, _('evolve'), None) + compat.progress(ui, _(b'evolve'), None) _cleanup(ui, repo, startnode, shouldupdate) @@ -1745,7 +1739,7 @@ stabilizes for both parents of orphan merges. """ curctx = repo[rev] - revtopic = getattr(curctx, 'topic', lambda: '')() + revtopic = getattr(curctx, 'topic', lambda: b'')() topicidx = getattr(curctx, 'topicidx', lambda: None)() stacktmplt = False # check if revision being evolved is in active topic to make sure @@ -1757,44 +1751,44 @@ confirmopt, progresscb, targetcat, lastsolved=lastsolved, stacktmplt=stacktmplt) if ret[0]: - evolvestate['replacements'][curctx.node()] = ret[1] + evolvestate[b'replacements'][curctx.node()] = ret[1] lastsolved = ret[1] else: - evolvestate['skippedrevs'].append(curctx.node()) + evolvestate[b'skippedrevs'].append(curctx.node()) - if evolvestate['orphanmerge']: + if evolvestate[b'orphanmerge']: # we were processing an orphan merge with both parents obsolete, # stabilized for second parent, re-stabilize for the first parent ret = _solveone(ui, repo, repo[ret[1]], evolvestate, dryrunopt, confirmopt, progresscb, targetcat, stacktmplt=stacktmplt) if ret[0]: - evolvestate['replacements'][curctx.node()] = ret[1] + evolvestate[b'replacements'][curctx.node()] = ret[1] lastsolved = ret[1] else: - evolvestate['skippedrevs'].append(curctx.node()) + evolvestate[b'skippedrevs'].append(curctx.node()) - evolvestate['orphanmerge'] = False + evolvestate[b'orphanmerge'] = False return lastsolved def solveobswdp(ui, repo, opts): """this function updates to the successor of obsolete wdir parent""" - oldid = repo['.'].node() - startctx = repo['.'] + oldid = repo[b'.'].node() + startctx = repo[b'.'] dryrunopt = opts.get('dry_run', False) displayer = compat.changesetdisplayer(ui, repo, - {'template': shorttemplate}) + {b'template': shorttemplate}) try: - ctx = repo[utility._singlesuccessor(repo, repo['.'])] + ctx = repo[utility._singlesuccessor(repo, repo[b'.'])] except utility.MultipleSuccessorsError as exc: - repo.ui.write_err(_('parent is obsolete with multiple' - ' successors:\n')) + repo.ui.write_err(_(b'parent is obsolete with multiple' + b' successors:\n')) for ln in exc.successorssets: for n in ln: displayer.show(repo[n]) return 2 - ui.status(_('update:')) + ui.status(_(b'update:')) if not ui.quiet: displayer.show(ctx) @@ -1804,33 +1798,33 @@ newid = ctx.node() if ctx != startctx: - with repo.wlock(), repo.lock(), repo.transaction('evolve') as tr: + with repo.wlock(), repo.lock(), repo.transaction(b'evolve') as tr: bmupdater = rewriteutil.bookmarksupdater(repo, oldid, tr) bmupdater(newid) - ui.status(_('working directory is now at %s\n') % ctx) + ui.status(_(b'working directory is now at %s\n') % ctx) return res def stopevolve(ui, repo, evolvestate): """logic for handling of `hg evolve --stop`""" updated = False pctx = None - if (evolvestate['command'] == 'evolve' - and evolvestate['category'] == 'contentdivergent' - and evolvestate['relocated']): - oldother = evolvestate['old-other'] + if (evolvestate[b'command'] == b'evolve' + and evolvestate[b'category'] == b'contentdivergent' + and evolvestate[b'relocated']): + oldother = evolvestate[b'old-other'] if oldother: with repo.wlock(), repo.lock(): repo = repo.unfiltered() hg.updaterepo(repo, oldother, True) - strips = [evolvestate['relocated']] + strips = [evolvestate[b'relocated']] repair.strip(ui, repo, strips, False) updated = True pctx = repo[oldother] if not updated: - pctx = repo['.'] + pctx = repo[b'.'] hg.updaterepo(repo, pctx.node(), True) - ui.status(_('stopped the interrupted evolve\n')) - ui.status(_('working directory is now at %s\n') % pctx) + ui.status(_(b'stopped the interrupted evolve\n')) + ui.status(_(b'working directory is now at %s\n') % pctx) def abortevolve(ui, repo, evolvestate): """ logic for handling of `hg evolve --abort`""" @@ -1840,11 +1834,11 @@ evolvedctx = [] # boolean value to say whether we should strip or not cleanup = True - startnode = evolvestate['startnode'] - for old, new in evolvestate['replacements'].items(): + startnode = evolvestate[b'startnode'] + for old, new in evolvestate[b'replacements'].items(): if new: evolvedctx.append(repo[new]) - for temp in evolvestate['temprevs']: + for temp in evolvestate[b'temprevs']: if temp: evolvedctx.append(repo[temp]) evolvedrevs = [c.rev() for c in evolvedctx] @@ -1852,145 +1846,159 @@ # checking if phase changed of any of the evolved rev immutable = [c for c in evolvedctx if not c.mutable()] if immutable: - repo.ui.warn(_("cannot clean up public changesets: %s\n") - % ', '.join(bytes(c) for c in immutable), - hint=_("see 'hg help phases' for details")) - cleanup = False - - # checking no new changesets are created on evolved revs - descendants = set() - if evolvedrevs: - descendants = set(repo.changelog.descendants(evolvedrevs)) - if descendants - set(evolvedrevs): - repo.ui.warn(_("warning: new changesets detected on destination " - "branch\n")) + repo.ui.warn(_(b"cannot clean up public changesets: %s\n") + % b', '.join(bytes(c) for c in immutable), + hint=_(b"see 'hg help phases' for details")) cleanup = False - # finding the indices of the obsmarkers to be stripped and stripping - # them - if evolvestate['obsmarkers']: - stripmarkers = set() - for m in evolvestate['obsmarkers']: - m = (m[0], m[1]) - stripmarkers.add(m) - indices = [] - allmarkers = obsutil.getmarkers(repo) - for i, m in enumerate(allmarkers): - marker = (m.prednode(), m.succnodes()[0]) - if marker in stripmarkers: - indices.append(i) + # checking no new changesets are created on evolved revs + descendants = set() + if evolvedrevs: + descendants = set(repo.changelog.descendants(evolvedrevs)) + if descendants - set(evolvedrevs): + repo.ui.warn(_(b"warning: new changesets detected on destination " + b"branch\n")) + cleanup = False - repair.deleteobsmarkers(repo.obsstore, indices) - repo.ui.debug('deleted %d obsmarkers\n' % len(indices)) + # finding the indices of the obsmarkers to be stripped and stripping + # them + if evolvestate[b'obsmarkers']: + stripmarkers = set() + for m in evolvestate[b'obsmarkers']: + m = (m[0], m[1]) + stripmarkers.add(m) + indices = [] + allmarkers = obsutil.getmarkers(repo) + for i, m in enumerate(allmarkers): + marker = (m.prednode(), m.succnodes()[0]) + if marker in stripmarkers: + indices.append(i) - if cleanup: - if evolvedrevs: - strippoints = [c.node() - for c in repo.set('roots(%ld)', evolvedrevs)] + repair.deleteobsmarkers(repo.obsstore, indices) + repo.ui.debug(b'deleted %d obsmarkers\n' % len(indices)) + + if cleanup: + if evolvedrevs: + strippoints = [c.node() + for c in repo.set(b'roots(%ld)', evolvedrevs)] + + # updating the working directory + hg.updaterepo(repo, startnode, True) - # updating the working directory - hg.updaterepo(repo, startnode, True) + # Strip from the first evolved revision + if evolvedrevs: + # no backup of evolved cset versions needed + repair.strip(repo.ui, repo, strippoints, False) - # Strip from the first evolved revision - if evolvedrevs: - # no backup of evolved cset versions needed - repair.strip(repo.ui, repo, strippoints, False) + with repo.transaction(b'evolve') as tr: + # restoring bookmarks at there original place + bmchanges = evolvestate[b'bookmarkchanges'] + if bmchanges: + repo._bookmarks.applychanges(repo, tr, bmchanges) - with repo.transaction('evolve') as tr: - # restoring bookmarks at there original place - bmchanges = evolvestate['bookmarkchanges'] - if bmchanges: - repo._bookmarks.applychanges(repo, tr, bmchanges) + evolvestate.delete() + ui.status(_(b'evolve aborted\n')) + ui.status(_(b'working directory is now at %s\n') + % nodemod.hex(startnode)[:12]) + else: + raise error.Abort(_(b"unable to abort interrupted evolve, use 'hg " + b"evolve --stop' to stop evolve")) +def hgabortevolve(ui, repo): + """logic for aborting evolve using 'hg abort'""" + with repo.wlock(), repo.lock(): + evolvestate = state.cmdstate(repo) + evolvestate.load() + if evolvestate[b'command'] != b'evolve': + pctx = repo[b'.'] + hg.updaterepo(repo, pctx.node(), True) + ui.status(_(b'evolve aborted\n')) + ui.status(_(b'working directory is now at %s\n') + % pctx.hex()[:12]) evolvestate.delete() - ui.status(_('evolve aborted\n')) - ui.status(_('working directory is now at %s\n') - % nodemod.hex(startnode)[:12]) - else: - raise error.Abort(_("unable to abort interrupted evolve, use 'hg " - "evolve --stop' to stop evolve")) + return 0 + return abortevolve(ui, repo, evolvestate) def continueevolve(ui, repo, evolvestate): """logic for handling of `hg evolve --continue`""" - with repo.wlock(), repo.lock(): - ms = merge.mergestate.read(repo) - mergeutil.checkunresolved(ms) - if (evolvestate['command'] == 'next' - or evolvestate['category'] == 'orphan'): - _completeorphan(ui, repo, evolvestate) - elif evolvestate['category'] == 'phasedivergent': - _completephasedivergent(ui, repo, evolvestate) - elif evolvestate['category'] == 'contentdivergent': - _continuecontentdivergent(ui, repo, evolvestate, None) - else: - repo.ui.status(_("continuing interrupted '%s' resolution is not yet" - " supported\n") % evolvestate['category']) - return + ms = merge.mergestate.read(repo) + mergeutil.checkunresolved(ms) + if (evolvestate[b'command'] == b'next' + or evolvestate[b'category'] == b'orphan'): + _completeorphan(ui, repo, evolvestate) + elif evolvestate[b'category'] == b'phasedivergent': + _completephasedivergent(ui, repo, evolvestate) + elif evolvestate[b'category'] == b'contentdivergent': + _continuecontentdivergent(ui, repo, evolvestate, None) + else: + repo.ui.status(_(b"continuing interrupted '%s' resolution is not yet" + b" supported\n") % evolvestate[b'category']) + return - # make sure we are continuing evolve and not `hg next --evolve` - if evolvestate['command'] != 'evolve': - return + # make sure we are continuing evolve and not `hg next --evolve` + if evolvestate[b'command'] != b'evolve': + return - # Progress handling - seen = 1 - count = len(evolvestate['revs']) + # Progress handling + seen = 1 + count = len(evolvestate[b'revs']) - def progresscb(): - compat.progress(ui, _('evolve'), seen, unit=_('changesets'), - total=count) + def progresscb(): + compat.progress(ui, _(b'evolve'), seen, unit=_(b'changesets'), + total=count) - category = evolvestate['category'] - confirm = evolvestate['confirm'] - unfi = repo.unfiltered() - # lastsolved: keep track of successor of last troubled cset we - # evolved to confirm that if atop msg should be suppressed to remove - # redundancy - lastsolved = None - activetopic = getattr(repo, 'currenttopic', '') - tr = repo.transaction("evolve") - with util.acceptintervention(tr): - for rev in evolvestate['revs']: - # XXX: prevent this lookup by storing nodes instead of revnums - curctx = unfi[rev] + category = evolvestate[b'category'] + confirm = evolvestate[b'confirm'] + unfi = repo.unfiltered() + # lastsolved: keep track of successor of last troubled cset we + # evolved to confirm that if atop msg should be suppressed to remove + # redundancy + lastsolved = None + activetopic = getattr(repo, 'currenttopic', b'') + tr = repo.transaction(b"evolve") + with util.acceptintervention(tr): + for rev in evolvestate[b'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 + # check if we can use stack template + revtopic = getattr(curctx, 'topic', lambda: b'')() + 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, - stacktmplt=stacktmplt) - if newnode[0]: - evolvestate['replacements'][curctx.node()] = newnode[1] - lastsolved = newnode[1] - else: - evolvestate['skippedrevs'].append(curctx.node()) - seen += 1 + if (curctx.node() not in evolvestate[b'replacements'] + and curctx.node() not in evolvestate[b'skippedrevs']): + newnode = _solveone(ui, repo, curctx, evolvestate, False, + confirm, progresscb, category, + lastsolved=lastsolved, + stacktmplt=stacktmplt) + if newnode[0]: + evolvestate[b'replacements'][curctx.node()] = newnode[1] + lastsolved = newnode[1] + else: + evolvestate[b'skippedrevs'].append(curctx.node()) + seen += 1 def _continuecontentdivergent(ui, repo, evolvestate, progresscb): """function to continue the interrupted content-divergence resolution.""" - tr = repo.transaction('evolve') + tr = repo.transaction(b'evolve') with util.acceptintervention(tr): - divergent = evolvestate['divergent'] - base = evolvestate['base'] + divergent = evolvestate[b'divergent'] + base = evolvestate[b'base'] repo = repo.unfiltered() - if evolvestate['relocating']: + if evolvestate[b'relocating']: newother = _completerelocation(ui, repo, evolvestate) - current = repo[evolvestate['current']] + current = repo[evolvestate[b'current']] obsolete.createmarkers(repo, [(current, (repo[newother],))], - operation='evolve') - evolvestate['relocating'] = False - evolvestate['relocated'] = newother - evolvestate['temprevs'].append(newother) - evolvestate['other-divergent'] = newother + operation=b'evolve') + evolvestate[b'relocating'] = False + evolvestate[b'relocated'] = newother + evolvestate[b'temprevs'].append(newother) + evolvestate[b'other-divergent'] = newother # continue the resolution by merging the content-divergence _mergecontentdivergents(repo, progresscb, repo[divergent], @@ -1998,16 +2006,16 @@ repo[base], evolvestate) - other = evolvestate['other-divergent'] + other = evolvestate[b'other-divergent'] ret = _completecontentdivergent(ui, repo, progresscb, repo[divergent], repo[other], repo[base], evolvestate) - origdivergent = evolvestate['orig-divergent'] - evolvestate['replacements'][origdivergent] = ret[1] + origdivergent = evolvestate[b'orig-divergent'] + evolvestate[b'replacements'][origdivergent] = ret[1] # logic to continue the public content-divergent - publicnode = evolvestate.get('public-divergent') + publicnode = evolvestate.get(b'public-divergent') if publicnode: res, newnode = ret if not res: @@ -2030,19 +2038,19 @@ phase-divergence""" # need to start transaction for bookmark changes - with repo.transaction('evolve'): + with repo.transaction(b'evolve'): node = _completerelocation(ui, repo, evolvestate) - evolvestate['temprevs'].append(node) + evolvestate[b'temprevs'].append(node) # resolving conflicts can lead to empty wdir and node can be None in # those cases - ctx = repo[evolvestate['current']] - newctx = repo[node] if node is not None else repo['.'] - obsolete.createmarkers(repo, [(ctx, (newctx,))], operation='evolve') + ctx = repo[evolvestate[b'current']] + newctx = repo[node] if node is not None else repo[b'.'] + obsolete.createmarkers(repo, [(ctx, (newctx,))], operation=b'evolve') # now continuing the phase-divergence resolution part - prec = repo[evolvestate['precursor']] + prec = repo[evolvestate[b'precursor']] retvalue = _resolvephasedivergent(ui, repo, prec, newctx) - evolvestate['replacements'][ctx.node()] = retvalue[1] + evolvestate[b'replacements'][ctx.node()] = retvalue[1] def _completeorphan(ui, repo, evolvestate): """function to complete the interrupted orphan resolution""" @@ -2050,47 +2058,47 @@ node = _completerelocation(ui, repo, evolvestate) # resolving conflicts can lead to empty wdir and node can be None in # those cases - ctx = repo[evolvestate['current']] + ctx = repo[evolvestate[b'current']] if node is None: - repo.ui.status(_("evolution of %d:%s created no changes" - " to commit\n") % (ctx.rev(), ctx)) + repo.ui.status(_(b"evolution of %d:%s created no changes" + b" to commit\n") % (ctx.rev(), ctx)) replacement = () else: replacement = (repo[node],) - obsolete.createmarkers(repo, [(ctx, replacement)], operation='evolve') + obsolete.createmarkers(repo, [(ctx, replacement)], operation=b'evolve') # make sure we are continuing evolve and not `hg next --evolve` - if evolvestate['command'] == 'evolve': - evolvestate['replacements'][ctx.node()] = node - if evolvestate['orphanmerge']: + if evolvestate[b'command'] == b'evolve': + evolvestate[b'replacements'][ctx.node()] = node + if evolvestate[b'orphanmerge']: # processing a merge changeset with both parents obsoleted, # stabilized on second parent, insert in front of list to # re-process to stabilize on first parent - evolvestate['revs'].insert(0, repo[node].rev()) - evolvestate['orphanmerge'] = False + evolvestate[b'revs'].insert(0, repo[node].rev()) + evolvestate[b'orphanmerge'] = False def _completerelocation(ui, repo, evolvestate): """function to complete the interrupted relocation of a commit return the new node formed """ - orig = repo[evolvestate['current']] + orig = repo[evolvestate[b'current']] ctx = orig - source = ctx.extra().get('source') + source = ctx.extra().get(b'source') extra = {} if source: - extra['source'] = source - extra['intermediate-source'] = ctx.hex() + extra[b'source'] = source + extra[b'intermediate-source'] = ctx.hex() else: - extra['source'] = ctx.hex() + extra[b'source'] = ctx.hex() user = ctx.user() date = ctx.date() message = ctx.description() - ui.status(_('evolving %d:%s "%s"\n') % (ctx.rev(), ctx, - message.split('\n', 1)[0])) + ui.status(_(b'evolving %d:%s "%s"\n') % (ctx.rev(), ctx, + message.split(b'\n', 1)[0])) targetphase = max(ctx.phase(), phases.draft) - overrides = {('phases', 'new-commit'): targetphase} + overrides = {(b'phases', b'new-commit'): targetphase} ctxparents = orig.parents() if len(ctxparents) == 2: @@ -2117,7 +2125,7 @@ else: # both the parents were obsoleted, if orphanmerge is set, we # are processing the second parent first (to keep parent order) - if evolvestate.get('orphanmerge'): + if evolvestate.get(b'orphanmerge'): with repo.dirstate.parentchange(): repo.dirstate.setparents(ctxparents[0].node(), currentp1) @@ -2126,7 +2134,7 @@ with repo.dirstate.parentchange(): repo.dirstate.setparents(repo.dirstate.parents()[0], nodemod.nullid) - with repo.ui.configoverride(overrides, 'evolve-continue'): + with repo.ui.configoverride(overrides, b'evolve-continue'): node = repo.commit(text=message, user=user, date=date, extra=extra) return node
--- a/hgext3rd/evolve/exthelper.py Mon Jul 29 14:43:15 2019 +0200 +++ b/hgext3rd/evolve/exthelper.py Fri Sep 27 13:03:18 2019 +0200 @@ -83,12 +83,12 @@ self._duckpunchers = [] self.cmdtable = {} self.command = registrar.command(self.cmdtable) - if '^init' in commands.table: + if b'^init' in commands.table: olddoregister = self.command._doregister def _newdoregister(self, name, *args, **kwargs): if kwargs.pop('helpbasic', False): - name = '^' + name + name = r'^' + name return olddoregister(self, name, *args, **kwargs) self.command._doregister = _newdoregister @@ -277,9 +277,9 @@ else: for opt in opts: if not isinstance(opt, tuple): - raise error.ProgrammingError('opts must be list of tuples') + raise error.ProgrammingError(b'opts must be list of tuples') if len(opt) not in (4, 5): - msg = 'each opt tuple must contain 4 or 5 values' + msg = b'each opt tuple must contain 4 or 5 values' raise error.ProgrammingError(msg) def dec(wrapper):
--- a/hgext3rd/evolve/firstmergecache.py Mon Jul 29 14:43:15 2019 +0200 +++ b/hgext3rd/evolve/firstmergecache.py Fri Sep 27 13:03:18 2019 +0200 @@ -41,7 +41,7 @@ @localrepo.unfilteredmethod def destroyed(self): - if 'firstmergecach' in vars(self): + if r'firstmergecach' in vars(self): self.firstmergecache.clear() super(firstmergecacherepo, self).destroyed() @@ -56,16 +56,16 @@ class firstmergecache(genericcaches.changelogsourcebase): - _filepath = 'evoext-firstmerge-00' - _cachename = 'evo-ext-firstmerge' + _filepath = b'evoext-firstmerge-00' + _cachename = b'evo-ext-firstmerge' def __init__(self): super(firstmergecache, self).__init__() - self._data = array.array('l') + self._data = array.array(r'l') def get(self, rev): if len(self._data) <= rev: - raise error.ProgrammingError('firstmergecache must be warmed before use') + raise error.ProgrammingError(b'firstmergecache must be warmed before use') return self._data[rev] def _updatefrom(self, repo, data): @@ -75,9 +75,9 @@ total = len(data) def progress(pos, rev=None): - revstr = '' if rev is None else ('rev %d' % rev) - compat.progress(repo.ui, 'updating firstmerge cache', - pos, revstr, unit='revision', total=total) + revstr = b'' if rev is None else (b'rev %d' % rev) + compat.progress(repo.ui, b'updating firstmerge cache', + pos, revstr, unit=b'revision', total=total) progress(0) for idx, rev in enumerate(data, 1): assert rev == len(self._data), (rev, len(self._data)) @@ -108,7 +108,7 @@ Subclasses MUST overide this method to actually affect the cache data. """ super(firstmergecache, self).clear() - self._data = array.array('l') + self._data = array.array(r'l') # crude version of a cache, to show the kind of information we have to store @@ -117,7 +117,7 @@ assert repo.filtername is None data = repo.cachevfs.tryread(self._filepath) - self._data = array.array('l') + self._data = array.array(r'l') if not data: self._cachekey = self.emptykey else: @@ -136,12 +136,12 @@ return try: - cachefile = repo.cachevfs(self._filepath, 'w', atomictemp=True) + cachefile = repo.cachevfs(self._filepath, b'w', atomictemp=True) headerdata = self._serializecachekey() cachefile.write(headerdata) cachefile.write(compat.arraytobytes(self._data)) cachefile.close() self._ondiskkey = self._cachekey except (IOError, OSError) as exc: - repo.ui.log('firstmergecache', 'could not write update %s\n' % exc) - repo.ui.debug('firstmergecache: could not write update %s\n' % exc) + repo.ui.log(b'firstmergecache', b'could not write update %s\n' % exc) + repo.ui.debug(b'firstmergecache: could not write update %s\n' % exc)
--- a/hgext3rd/evolve/genericcaches.py Mon Jul 29 14:43:15 2019 +0200 +++ b/hgext3rd/evolve/genericcaches.py Fri Sep 27 13:03:18 2019 +0200 @@ -31,7 +31,7 @@ # default key used for an empty cache emptykey = () - _cachekeyspec = '' # used for serialization + _cachekeyspec = b'' # used for serialization _cachename = None # used for debug message @abc.abstractmethod @@ -42,7 +42,7 @@ @util.propertycache def _cachekeystruct(self): # dynamic property to help subclass to change it - return struct.Struct('>' + self._cachekeyspec) + return struct.Struct(b'>' + self._cachekeyspec) @util.propertycache def _cachekeysize(self): @@ -112,7 +112,7 @@ if newkey == self._cachekey: return if reset or self._cachekey is None: - repo.ui.log('cache', 'strip detected, %s cache reset\n' + repo.ui.log(b'cache', b'strip detected, %s cache reset\n' % self._cachename) self.clear(reset=True) @@ -120,7 +120,7 @@ self._updatefrom(repo, data) duration = util.timer() - starttime summary = self._updatesummary(data) - repo.ui.log('cache', 'updated %s in %.4f seconds (%s)\n', + repo.ui.log(b'cache', b'updated %s in %.4f seconds (%s)\n', self._cachename, duration, summary) self._cachekey = newkey @@ -144,7 +144,7 @@ # default key used for an empty cache emptykey = (0, node.nullid) - _cachekeyspec = 'i20s' + _cachekeyspec = b'i20s' _cachename = None # used for debug message # Useful "public" function (no need to override them) @@ -172,4 +172,4 @@ return self._fetchchangelogdata(self._cachekey, repo.changelog) def _updatesummary(self, data): - return '%ir' % len(data) + return b'%ir' % len(data)
--- a/hgext3rd/evolve/hack/drophack.py Mon Jul 29 14:43:15 2019 +0200 +++ b/hgext3rd/evolve/hack/drophack.py Fri Sep 27 13:03:18 2019 +0200 @@ -34,7 +34,7 @@ user = ostop[0] - ostart[0] sys = ostop[1] - ostart[1] comb = user + sys - ui.write("%s: wall %f comb %f user %f sys %f\n" + ui.write(b"%s: wall %f comb %f user %f sys %f\n" % (caption, wall, comb, user, sys)) def obsmarkerchainfrom(obsstore, nodes): @@ -66,13 +66,13 @@ repo = repo.unfiltered() repo.destroying() oldmarkers = list(repo.obsstore._all) - util.rename(repo.svfs.join('obsstore'), - repo.vfs.join('obsstore.prestrip')) + util.rename(repo.svfs.join(b'obsstore'), + repo.vfs.join(b'obsstore.prestrip')) del repo.obsstore # drop the cache newstore = repo.obsstore assert not newstore # should be empty after rename newmarkers = [m for m in oldmarkers if m not in markers] - tr = repo.transaction('drophack') + tr = repo.transaction(b'drophack') try: newstore.add(tr, newmarkers) tr.close() @@ -81,7 +81,7 @@ repo.destroyed() -@command('drop', [('r', 'rev', [], 'revision to update')], _('[-r] revs')) +@command(b'drop', [(b'r', b'rev', [], b'revision to update')], _(b'[-r] revs')) def cmddrop(ui, repo, *revs, **opts): """I'm hacky do not use me! @@ -97,11 +97,11 @@ revs = list(revs) revs.extend(opts['rev']) if not revs: - revs = ['.'] + revs = [b'.'] # get the changeset revs = scmutil.revrange(repo, revs) if not revs: - ui.write_err('no revision to drop\n') + ui.write_err(b'no revision to drop\n') return 1 # lock from the beginning to prevent race wlock = lock = None @@ -109,49 +109,49 @@ wlock = repo.wlock() lock = repo.lock() # check they have no children - if repo.revs('%ld and public()', revs): - ui.write_err('cannot drop public revision') + if repo.revs(b'%ld and public()', revs): + ui.write_err(b'cannot drop public revision') return 1 - if repo.revs('children(%ld) - %ld', revs, revs): - ui.write_err('cannot drop revision with children') + if repo.revs(b'children(%ld) - %ld', revs, revs): + ui.write_err(b'cannot drop revision with children') return 1 - if repo.revs('. and %ld', revs): - newrevs = repo.revs('max(::. - %ld)', revs) + if repo.revs(b'. and %ld', revs): + newrevs = repo.revs(b'max(::. - %ld)', revs) if newrevs: assert len(newrevs) == 1 newrev = newrevs.first() else: newrev = -1 commands.update(ui, repo, newrev) - ui.status(_('working directory now at %s\n') % repo[newrev]) + ui.status(_(b'working directory now at %s\n') % repo[newrev]) # get all markers and successors up to root nodes = [repo[r].node() for r in revs] - with timed(ui, 'search obsmarker'): + with timed(ui, b'search obsmarker'): markers = set(obsmarkerchainfrom(repo.obsstore, nodes)) - ui.write('%i obsmarkers found\n' % len(markers)) + ui.write(b'%i obsmarkers found\n' % len(markers)) cl = repo.unfiltered().changelog - with timed(ui, 'search nodes'): + with timed(ui, b'search nodes'): allnodes = set(nodes) allnodes.update(m[0] for m in markers if cl.hasnode(m[0])) - ui.write('%i nodes found\n' % len(allnodes)) + ui.write(b'%i nodes found\n' % len(allnodes)) cl = repo.changelog visiblenodes = set(n for n in allnodes if cl.hasnode(n)) # check constraint again - if repo.revs('%ln and public()', visiblenodes): - ui.write_err('cannot drop public revision') + if repo.revs(b'%ln and public()', visiblenodes): + ui.write_err(b'cannot drop public revision') return 1 - if repo.revs('children(%ln) - %ln', visiblenodes, visiblenodes): - ui.write_err('cannot drop revision with children') + if repo.revs(b'children(%ln) - %ln', visiblenodes, visiblenodes): + ui.write_err(b'cannot drop revision with children') return 1 if markers: # strip them - with timed(ui, 'strip obsmarker'): + with timed(ui, b'strip obsmarker'): stripmarker(ui, repo, markers) # strip the changeset - with timed(ui, 'strip nodes'): - repair.strip(ui, repo, list(allnodes), backup="all", - topic='drophack') + with timed(ui, b'strip nodes'): + repair.strip(ui, repo, list(allnodes), backup=b"all", + topic=b'drophack') finally: lockmod.release(lock, wlock)
--- a/hgext3rd/evolve/legacy.py Mon Jul 29 14:43:15 2019 +0200 +++ b/hgext3rd/evolve/legacy.py Fri Sep 27 13:03:18 2019 +0200 @@ -45,20 +45,20 @@ """ if not repo.local(): return - evolveopts = ui.configlist('experimental', 'evolution') + evolveopts = ui.configlist(b'experimental', b'evolution') if not evolveopts: - evolveopts = 'all' - ui.setconfig('experimental', 'evolution', evolveopts) + evolveopts = b'all' + ui.setconfig(b'experimental', b'evolution', evolveopts) for arg in sys.argv: - if 'debugc' in arg: + if r'debugc' in arg: break else: - data = repo.vfs.tryread('obsolete-relations') + data = repo.vfs.tryread(b'obsolete-relations') if not data: - data = repo.svfs.tryread('obsoletemarkers') + data = repo.svfs.tryread(b'obsoletemarkers') if data: - raise error.Abort('old format of obsolete marker detected!\n' - 'run `hg debugconvertobsolete` once.') + raise error.Abort(b'old format of obsolete marker detected!\n' + b'run `hg debugconvertobsolete` once.') def _obsdeserialize(flike): """read a file like object serialized with _obsserialize @@ -77,7 +77,7 @@ cmdtable = {} command = commandfunc(cmdtable) -@command('debugconvertobsolete', [], '') +@command(b'debugconvertobsolete', [], b'') def cmddebugconvertobsolete(ui, repo): """import markers from an .hg/obsolete-relations file""" cnt = 0 @@ -86,13 +86,13 @@ some = False try: unlink = [] - tr = repo.transaction('convert-obsolete') + tr = repo.transaction(b'convert-obsolete') try: repo._importoldobsolete = True store = repo.obsstore ### very first format try: - f = repo.vfs('obsolete-relations') + f = repo.vfs(b'obsolete-relations') try: some = True for line in f: @@ -101,30 +101,30 @@ prec = bin(objhex) sucs = (suc == nullid) and [] or [suc] meta = { - 'date': '%i %i' % makedate(), - 'user': ui.username(), + b'date': b'%i %i' % makedate(), + b'user': ui.username(), } try: store.create(tr, prec, sucs, 0, metadata=meta) cnt += 1 except ValueError: - repo.ui.write_err("invalid old marker line: %s" + repo.ui.write_err(b"invalid old marker line: %s" % (line)) err += 1 finally: f.close() - unlink.append(repo.vfs.join('obsolete-relations')) + unlink.append(repo.vfs.join(b'obsolete-relations')) except IOError: pass ### second (json) format - data = repo.svfs.tryread('obsoletemarkers') + data = repo.svfs.tryread(b'obsoletemarkers') if data: some = True for oldmark in json.loads(data): - del oldmark['id'] # dropped for now - del oldmark['reason'] # unused until then - oldobject = str(oldmark.pop('object')) - oldsubjects = [str(s) for s in oldmark.pop('subjects', [])] + del oldmark[r'id'] # dropped for now + del oldmark[r'reason'] # unused until then + oldobject = str(oldmark.pop(r'object')) + oldsubjects = [str(s) for s in oldmark.pop(r'subjects', [])] lookup_errors = (error.RepoLookupError, error.LookupError) if len(oldobject) != 40: try: @@ -137,7 +137,7 @@ except lookup_errors: pass - oldmark['date'] = '%i %i' % tuple(oldmark['date']) + oldmark[r'date'] = r'%i %i' % tuple(oldmark[r'date']) meta = dict((k.encode('utf-8'), v.encode('utf-8')) for k, v in oldmark.items()) try: @@ -147,11 +147,11 @@ 0, metadata=meta) cnt += 1 except ValueError: - msg = "invalid marker %s -> %s\n" + msg = b"invalid marker %s -> %s\n" msg %= (oldobject, oldsubjects) repo.ui.write_err(msg) err += 1 - unlink.append(repo.svfs.join('obsoletemarkers')) + unlink.append(repo.svfs.join(b'obsoletemarkers')) tr.close() for path in unlink: util.unlink(path) @@ -161,12 +161,12 @@ del repo._importoldobsolete lock.release() if not some: - ui.warn(_('nothing to do\n')) - ui.status('%i obsolete marker converted\n' % cnt) + ui.warn(_(b'nothing to do\n')) + ui.status(b'%i obsolete marker converted\n' % cnt) if err: - ui.write_err('%i conversion failed. check you graph!\n' % err) + ui.write_err(b'%i conversion failed. check you graph!\n' % err) -@command('debugrecordpruneparents', [], '') +@command(b'debugrecordpruneparents', [], b'') def cmddebugrecordpruneparents(ui, repo): """add parent data to prune markers when possible @@ -174,14 +174,14 @@ If the pruned node is locally known, it creates a new marker with parent data. """ - pgop = 'reading markers' + pgop = b'reading markers' # lock from the beginning to prevent race wlock = lock = tr = None try: wlock = repo.wlock() lock = repo.lock() - tr = repo.transaction('recordpruneparents') + tr = repo.transaction(b'recordpruneparents') unfi = repo.unfiltered() nm = unfi.changelog.nodemap store = repo.obsstore @@ -196,7 +196,7 @@ store.create(tr, prec=mark[0], succs=mark[1], flag=mark[2], metadata=dict(mark[3]), parents=parents) if len(store._all) - before: - ui.write(_('created new markers for %i\n') % rev) + ui.write(_(b'created new markers for %i\n') % rev) ui.progress(pgop, idx, total=pgtotal) tr.close() ui.progress(pgop, None)
--- a/hgext3rd/evolve/metadata.py Mon Jul 29 14:43:15 2019 +0200 +++ b/hgext3rd/evolve/metadata.py Fri Sep 27 13:03:18 2019 +0200 @@ -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__ = b'9.1.0.dev' -testedwith = b'4.5.2 4.6.2 4.7 4.8 4.9 5.0' +__version__ = b'9.2.0' +testedwith = b'4.5.2 4.6.2 4.7 4.8 4.9 5.0 5.1' minimumhgversion = b'4.5' buglink = b'https://bz.mercurial-scm.org/'
--- a/hgext3rd/evolve/obscache.py Mon Jul 29 14:43:15 2019 +0200 +++ b/hgext3rd/evolve/obscache.py Fri Sep 27 13:03:18 2019 +0200 @@ -50,10 +50,10 @@ length, cachekey will be set to None.""" # default value obsstoresize = 0 - keydata = '' + keydata = b'' # try to get actual data from the obsstore try: - with self.svfs('obsstore') as obsfile: + with self.svfs(b'obsstore') as obsfile: obsfile.seek(0, 2) obsstoresize = obsfile.tell() if index is None: @@ -82,11 +82,11 @@ def markersfrom(obsstore, byteoffset, firstmarker): if not firstmarker: return list(obsstore) - elif '_all' in vars(obsstore): + elif r'_all' in vars(obsstore): # if the data are in memory, just use that return obsstore._all[firstmarker:] else: - obsdata = obsstore.svfs.tryread('obsstore') + obsdata = obsstore.svfs.tryread(b'obsstore') return obsolete._readmarkers(obsdata, byteoffset)[1] @@ -178,7 +178,7 @@ reset, revs, obsmarkers, obskeypair = upgrade if reset or self._cachekey is None: - repo.ui.log('evoext-cache', 'strip detected, %s cache reset\n' % self._cachename) + repo.ui.log(b'evoext-cache', b'strip detected, %s cache reset\n' % self._cachename) self.clear(reset=True) starttime = util.timer() @@ -186,7 +186,7 @@ obsmarkers = list(obsmarkers) self._updatefrom(repo, revs, obsmarkers) duration = util.timer() - starttime - repo.ui.log('evoext-cache', 'updated %s in %.4f seconds (%dr, %do)\n', + repo.ui.log(b'evoext-cache', b'updated %s in %.4f seconds (%dr, %do)\n', self._cachename, duration, len(revs), len(obsmarkers)) # update the key from the new data @@ -314,10 +314,10 @@ zero. That would be especially useful for the '.pending' overlay. """ - _filepath = 'evoext-obscache-00' - _headerformat = '>q20sQQ20s' + _filepath = b'evoext-obscache-00' + _headerformat = b'>q20sQQ20s' - _cachename = 'evo-ext-obscache' # used for error message + _cachename = b'evo-ext-obscache' # used for error message def __init__(self, repo): super(obscache, self).__init__() @@ -339,7 +339,7 @@ def _setdata(self, data): """set a new bytearray data, invalidating the 'get' shortcut if needed""" self._data = data - if 'get' in vars(self): + if r'get' in vars(self): del self.get def clear(self, reset=False): @@ -403,15 +403,15 @@ return try: - cachefile = repo.cachevfs(self._filepath, 'w', atomictemp=True) + cachefile = repo.cachevfs(self._filepath, b'w', atomictemp=True) headerdata = struct.pack(self._headerformat, *self._cachekey) cachefile.write(headerdata) cachefile.write(self._data) cachefile.close() self._ondiskkey = self._cachekey except (IOError, OSError) as exc: - repo.ui.log('obscache', 'could not write update %s\n' % exc) - repo.ui.debug('obscache: could not write update %s\n' % exc) + repo.ui.log(b'obscache', b'could not write update %s\n' % exc) + repo.ui.debug(b'obscache: could not write update %s\n' % exc) def load(self, repo): """load data from disk""" @@ -447,10 +447,10 @@ # will be about as fast... if not obscache.uptodate(repo): if repo.currenttransaction() is None: - repo.ui.log('evoext-cache', - 'obscache is out of date, ' - 'falling back to slower obsstore version\n') - repo.ui.debug('obscache is out of date\n') + repo.ui.log(b'evoext-cache', + b'obscache is out of date, ' + b'falling back to slower obsstore version\n') + repo.ui.debug(b'obscache is out of date\n') return orig(repo) else: # If a transaction is open, it is worthwhile to update and use @@ -465,9 +465,9 @@ @eh.uisetup def cachefuncs(ui): - orig = obsolete.cachefuncs['obsolete'] + orig = obsolete.cachefuncs[b'obsolete'] wrapped = lambda repo: _computeobsoleteset(orig, repo) - obsolete.cachefuncs['obsolete'] = wrapped + obsolete.cachefuncs[b'obsolete'] = wrapped @eh.reposetup def setupcache(ui, repo): @@ -476,7 +476,7 @@ @localrepo.unfilteredmethod def destroyed(self): - if 'obsstore' in vars(self): + if r'obsstore' in vars(self): self.obsstore.obscache.clear() super(obscacherepo, self).destroyed()
--- a/hgext3rd/evolve/obsdiscovery.py Mon Jul 29 14:43:15 2019 +0200 +++ b/hgext3rd/evolve/obsdiscovery.py Fri Sep 27 13:03:18 2019 +0200 @@ -60,11 +60,11 @@ obsexcmsg = utility.obsexcmsg # Config -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) +eh.configitem(b'experimental', b'evolution.obsdiscovery', True) +eh.configitem(b'experimental', b'obshashrange', True) +eh.configitem(b'experimental', b'obshashrange.warm-cache', b'auto') +eh.configitem(b'experimental', b'obshashrange.max-revs', None) +eh.configitem(b'experimental', b'obshashrange.lru-size', 2000) ################################## ### Code performing discovery ### @@ -76,7 +76,7 @@ missing = set() starttime = util.timer() - heads = local.revs('heads(%ld)', probeset) + heads = local.revs(b'heads(%ld)', probeset) local.stablerange.warmup(local) rangelength = local.stablerange.rangelength @@ -104,8 +104,8 @@ local.obsstore.rangeobshashcache.update(local) querycount = 0 - compat.progress(ui, _("comparing obsmarker with other"), querycount, - unit=_("queries")) + compat.progress(ui, _(b"comparing obsmarker with other"), querycount, + unit=_(b"queries")) overflow = [] while sample or overflow: if overflow: @@ -117,7 +117,7 @@ overflow = sample[samplesize:] sample = sample[:samplesize] elif len(sample) < samplesize: - ui.debug("query %i; add more sample (target %i, current %i)\n" + ui.debug(b"query %i; add more sample (target %i, current %i)\n" % (querycount, samplesize, len(sample))) # we need more sample ! needed = samplesize - len(sample) @@ -143,7 +143,7 @@ nbsample = len(sample) maxsize = max([rangelength(local, r) for r in sample]) - ui.debug("query %i; sample size is %i, largest range %i\n" + ui.debug(b"query %i; sample size is %i, largest range %i\n" % (querycount, nbsample, maxsize)) nbreplies = 0 replies = list(_queryrange(ui, local, remote, sample)) @@ -160,15 +160,15 @@ addentry(new) assert nbsample == nbreplies querycount += 1 - compat.progress(ui, _("comparing obsmarker with other"), querycount, - unit=_("queries")) - compat.progress(ui, _("comparing obsmarker with other"), None) + compat.progress(ui, _(b"comparing obsmarker with other"), querycount, + unit=_(b"queries")) + compat.progress(ui, _(b"comparing obsmarker with other"), None) local.obsstore.rangeobshashcache.save(local) duration = util.timer() - starttime - logmsg = ('obsdiscovery, %d/%d mismatch' - ' - %d obshashrange queries in %.4f seconds\n') + logmsg = (b'obsdiscovery, %d/%d mismatch' + b' - %d obshashrange queries in %.4f seconds\n') logmsg %= (len(missing), len(probeset), querycount, duration) - ui.log('evoext-obsdiscovery', logmsg) + ui.log(b'evoext-obsdiscovery', logmsg) ui.debug(logmsg) return sorted(missing) @@ -208,9 +208,9 @@ ranges = stablerange.subrangesclosure(repo, repo.stablerange, revs) else: ranges = [(r, 0) for r in revs] - headers = ('rev', 'node', 'index', 'size', 'depth', 'obshash') - linetemplate = '%12d %12s %12d %12d %12d %12s\n' - headertemplate = linetemplate.replace('d', 's') + headers = (b'rev', b'node', b'index', b'size', b'depth', b'obshash') + linetemplate = b'%12d %12s %12d %12d %12d %12s\n' + headertemplate = linetemplate.replace(b'd', b's') ui.status(headertemplate % headers) repo.obsstore.rangeobshashcache.update(repo) for r in ranges: @@ -262,12 +262,12 @@ ### sqlite caching _sqliteschema = [ - """CREATE TABLE obshashrange(rev INTEGER NOT NULL, + r"""CREATE TABLE obshashrange(rev INTEGER NOT NULL, idx INTEGER NOT NULL, obshash BLOB NOT NULL, PRIMARY KEY(rev, idx));""", - "CREATE INDEX range_index ON obshashrange(rev, idx);", - """CREATE TABLE meta(schemaversion INTEGER NOT NULL, + r"CREATE INDEX range_index ON obshashrange(rev, idx);", + r"""CREATE TABLE meta(schemaversion INTEGER NOT NULL, tiprev INTEGER NOT NULL, tipnode BLOB NOT NULL, nbobsmarker INTEGER NOT NULL, @@ -275,17 +275,17 @@ obskey BLOB NOT NULL );""", ] -_queryexist = "SELECT name FROM sqlite_master WHERE type='table' AND name='meta';" -_clearmeta = """DELETE FROM meta;""" -_newmeta = """INSERT INTO meta (schemaversion, tiprev, tipnode, nbobsmarker, obssize, obskey) +_queryexist = r"SELECT name FROM sqlite_master WHERE type='table' AND name='meta';" +_clearmeta = r"""DELETE FROM meta;""" +_newmeta = r"""INSERT INTO meta (schemaversion, tiprev, tipnode, nbobsmarker, obssize, obskey) VALUES (?,?,?,?,?,?);""" -_updateobshash = "INSERT INTO obshashrange(rev, idx, obshash) VALUES (?,?,?);" -_querymeta = "SELECT schemaversion, tiprev, tipnode, nbobsmarker, obssize, obskey FROM meta;" -_queryobshash = "SELECT obshash FROM obshashrange WHERE (rev = ? AND idx = ?);" -_query_max_stored = "SELECT MAX(rev) FROM obshashrange" +_updateobshash = r"INSERT INTO obshashrange(rev, idx, obshash) VALUES (?,?,?);" +_querymeta = r"SELECT schemaversion, tiprev, tipnode, nbobsmarker, obssize, obskey FROM meta;" +_queryobshash = r"SELECT obshash FROM obshashrange WHERE (rev = ? AND idx = ?);" +_query_max_stored = r"SELECT MAX(rev) FROM obshashrange" -_reset = "DELETE FROM obshashrange;" -_delete = "DELETE FROM obshashrange WHERE (rev = ? AND idx = ?);" +_reset = r"DELETE FROM obshashrange;" +_delete = r"DELETE FROM obshashrange WHERE (rev = ? AND idx = ?);" def _affectedby(repo, markers): """return all nodes whose relevant set is affected by this changeset @@ -333,8 +333,8 @@ _schemaversion = 3 - _cachename = 'evo-ext-obshashrange' # used for error message - _filename = 'evoext_obshashrange_v2.sqlite' + _cachename = b'evo-ext-obshashrange' # used for error message + _filename = b'evoext_obshashrange_v2.sqlite' def __init__(self, repo): super(_obshashcache, self).__init__() @@ -353,7 +353,7 @@ self._new.clear() if reset: self._valid = False - if '_con' in vars(self): + if r'_con' in vars(self): del self._con def get(self, rangeid): @@ -362,7 +362,7 @@ # XXX there are issue with cache warming, we hack around it for now if not getattr(self, '_updating', False): if self._cachekey[0] < rangeid[0]: - msg = ('using unwarmed obshashrangecache (%s %s)' + msg = (b'using unwarmed obshashrangecache (%s %s)' % (rangeid[0], self._cachekey[0])) raise error.ProgrammingError(msg) @@ -377,7 +377,7 @@ except (sqlite3.DatabaseError, sqlite3.OperationalError): # something is wrong with the sqlite db # Since this is a cache, we ignore it. - if '_con' in vars(self): + if r'_con' in vars(self): del self._con self._new.clear() return value @@ -406,8 +406,8 @@ affected = [] if RESET_ABOVE < len(obsmarkers): # lots of new obsmarkers, probably smarter to reset the cache - repo.ui.log('evoext-cache', 'obshashcache reset - ' - 'many new markers (%d)\n' + repo.ui.log(b'evoext-cache', b'obshashcache reset - ' + b'many new markers (%d)\n' % len(obsmarkers)) reset = True elif obsmarkers: @@ -420,23 +420,23 @@ if r is not None and r <= max_stored] if RESET_ABOVE < len(affected): - repo.ui.log('evoext-cache', 'obshashcache reset - ' - 'new markers affect many changeset (%d)\n' + repo.ui.log(b'evoext-cache', b'obshashcache reset - ' + b'new markers affect many changeset (%d)\n' % len(affected)) reset = True if affected or reset: if not reset: - repo.ui.log('evoext-cache', 'obshashcache clean - ' - 'new markers affect %d changeset and cached ranges\n' + repo.ui.log(b'evoext-cache', b'obshashcache clean - ' + b'new markers affect %d changeset and cached ranges\n' % len(affected)) if con is not None: # always reset for now, the code detecting affect is buggy # so we need to reset more broadly than we would like. try: if repo.stablerange._con is None: - repo.ui.log('evoext-cache', 'obshashcache reset - ' - 'underlying stablerange cache unavailable\n') + repo.ui.log(b'evoext-cache', b'obshashcache reset - ' + b'underlying stablerange cache unavailable\n') reset = True if reset: con.execute(_reset) @@ -447,7 +447,7 @@ for r in ranges: self._data.pop(r, None) except (sqlite3.DatabaseError, sqlite3.OperationalError) as exc: - repo.ui.log('evoext-cache', 'error while updating obshashrange cache: %s' % exc) + repo.ui.log(b'evoext-cache', b'error while updating obshashrange cache: %s' % exc) del self._updating return @@ -457,7 +457,7 @@ # single revision is quite costly) newrevs = [] stop = self._cachekey[0] # tiprev - for h in repo.filtered('immutable').changelog.headrevs(): + for h in repo.filtered(b'immutable').changelog.headrevs(): if h <= stop and h in affected: newrevs.append(h) newrevs.extend(revs) @@ -467,9 +467,9 @@ total = len(revs) def progress(pos, rev=None): - revstr = '' if rev is None else ('rev %d' % rev) - compat.progress(repo.ui, 'updating obshashrange cache', - pos, revstr, unit='revision', total=total) + revstr = b'' if rev is None else (b'rev %d' % rev) + compat.progress(repo.ui, b'updating obshashrange cache', + pos, revstr, unit=b'revision', total=total) # warm the cache for the new revs progress(0) for idx, r in enumerate(revs): @@ -495,7 +495,7 @@ except OSError: return None con = sqlite3.connect(encoding.strfromlocal(self._path), timeout=30, - isolation_level="IMMEDIATE") + isolation_level=r"IMMEDIATE") con.text_factory = bytes return con @@ -528,14 +528,14 @@ repo = repo.unfiltered() try: with repo.lock(): - if 'stablerange' in vars(repo): + if r'stablerange' in vars(repo): repo.stablerange.save(repo) self._save(repo) except error.LockError: # Exceptionnally we are noisy about it since performance impact # is large We should address that before using this more # widely. - msg = _('obshashrange cache: skipping save unable to lock repo\n') + msg = _(b'obshashrange cache: skipping save unable to lock repo\n') repo.ui.warn(msg) def _save(self, repo): @@ -548,22 +548,22 @@ # # operational error catch read-only and locked database # IntegrityError catch Unique constraint error that may arise - if '_con' in vars(self): + if r'_con' in vars(self): del self._con self._new.clear() - repo.ui.log('evoext-cache', 'error while saving new data: %s' % exc) - repo.ui.debug('evoext-cache: error while saving new data: %s' % exc) + repo.ui.log(b'evoext-cache', b'error while saving new data: %s' % exc) + repo.ui.debug(b'evoext-cache: error while saving new data: %s' % exc) def _trysave(self, repo): if self._con is None: util.unlinkpath(self._path, ignoremissing=True) - if '_con' in vars(self): + if r'_con' in vars(self): del self._con con = self._db() if con is None: - repo.ui.log('evoext-cache', 'unable to write obshashrange cache' - ' - cannot create database') + repo.ui.log(b'evoext-cache', b'unable to write obshashrange cache' + b' - cannot create database') return with con: for req in _sqliteschema: @@ -580,12 +580,12 @@ # drifting is currently an issue because this means another # process might have already added the cache line we are about # to add. This will confuse sqlite - msg = _('obshashrange cache: skipping write, ' - 'database drifted under my feet\n') + msg = _(b'obshashrange cache: skipping write, ' + b'database drifted under my feet\n') repo.ui.warn(msg) self._new.clear() self._valid = False - if '_con' in vars(self): + if r'_con' in vars(self): del self._con self._valid = False return @@ -618,7 +618,7 @@ class obshashrepo(repo.__class__): @localrepo.unfilteredmethod def destroyed(self): - if 'obsstore' in vars(self): + if r'obsstore' in vars(self): self.obsstore.rangeobshashcache.clear() toplevel = not util.safehasattr(self, '_destroying') if toplevel: @@ -667,7 +667,7 @@ return _obshashrange_v0(peer._repo, ranges) -_indexformat = '>I' +_indexformat = b'>I' _indexsize = _calcsize(_indexformat) def _encrange(node_rangeid): """encode a (node) range""" @@ -685,11 +685,11 @@ def peer_obshashrange_v0(self, ranges): binranges = [_encrange(r) for r in ranges] encranges = encodelist(binranges) - d = self._call("evoext_obshashrange_v1", ranges=encranges) + d = self._call(b"evoext_obshashrange_v1", ranges=encranges) try: return decodelist(d) except ValueError: - self._abort(error.ResponseError(_("unexpected response:"), d)) + self._abort(error.ResponseError(_(b"unexpected response:"), d)) @compat.wireprotocommand(eh, b'evoext_obshashrange_v1', b'ranges') def srv_obshashrange_v1(repo, proto, ranges): @@ -699,16 +699,16 @@ return encodelist(hashes) def _useobshashrange(repo): - base = repo.ui.configbool('experimental', 'obshashrange') + base = repo.ui.configbool(b'experimental', b'obshashrange') if base: - maxrevs = repo.ui.configint('experimental', 'obshashrange.max-revs') + maxrevs = repo.ui.configint(b'experimental', b'obshashrange.max-revs') if maxrevs is not None and maxrevs < len(repo.unfiltered()): base = False return base def _canobshashrange(local, remote): return (_useobshashrange(local) - and remote.capable('_evoext_obshashrange_v1')) + and remote.capable(b'_evoext_obshashrange_v1')) def _obshashrange_capabilities(orig, repo, proto): """wrapper to advertise new capability""" @@ -738,11 +738,11 @@ extensions.wrapfunction(wireprotov1server, 'capabilities', _obshashrange_capabilities) # wrap command content - oldcap, args = wireprotov1server.commands['capabilities'] + oldcap, args = wireprotov1server.commands[b'capabilities'] def newcap(repo, proto): return _obshashrange_capabilities(oldcap, repo, proto) - wireprotov1server.commands['capabilities'] = (newcap, args) + wireprotov1server.commands[b'capabilities'] = (newcap, args) ########################################## ### trigger discovery during exchange ### @@ -754,7 +754,7 @@ # exchange of obsmarkers is enabled locally and obsolete.isenabled(pushop.repo, obsolete.exchangeopt) # remote server accept markers - and 'obsolete' in pushop.remote.listkeys('namespaces')) + and b'obsolete' in pushop.remote.listkeys(b'namespaces')) def _pushobshashrange(pushop, commonrevs): repo = pushop.repo.unfiltered() @@ -769,21 +769,21 @@ (_canobshashrange, _pushobshashrange), ] -obsdiscovery_skip_message = """\ +obsdiscovery_skip_message = b"""\ (skipping discovery of obsolescence markers, will exchange everything) (controled by 'experimental.evolution.obsdiscovery' configuration) """ def usediscovery(repo): - return repo.ui.configbool('experimental', 'evolution.obsdiscovery') + return repo.ui.configbool(b'experimental', b'evolution.obsdiscovery') @eh.wrapfunction(exchange, '_pushdiscoveryobsmarkers') def _pushdiscoveryobsmarkers(orig, pushop): if _dopushmarkers(pushop): repo = pushop.repo remote = pushop.remote - obsexcmsg(repo.ui, "computing relevant nodes\n") - revs = list(repo.revs('::%ln', pushop.futureheads)) + obsexcmsg(repo.ui, b"computing relevant nodes\n") + revs = list(repo.revs(b'::%ln', pushop.futureheads)) unfi = repo.unfiltered() if not usediscovery(repo): @@ -803,27 +803,27 @@ # obs markers. return orig(pushop) - obsexcmsg(repo.ui, "looking for common markers in %i nodes\n" + obsexcmsg(repo.ui, b"looking for common markers in %i nodes\n" % len(revs)) - commonrevs = list(unfi.revs('::%ln', pushop.outgoing.commonheads)) + commonrevs = list(unfi.revs(b'::%ln', pushop.outgoing.commonheads)) # find the nodes where the relevant obsmarkers mismatches nodes = discovery(pushop, commonrevs) if nodes: - obsexcmsg(repo.ui, "computing markers relevant to %i nodes\n" + obsexcmsg(repo.ui, b"computing markers relevant to %i nodes\n" % len(nodes)) pushop.outobsmarkers = repo.obsstore.relevantmarkers(nodes) else: - obsexcmsg(repo.ui, "markers already in sync\n") + obsexcmsg(repo.ui, b"markers already in sync\n") pushop.outobsmarkers = [] @eh.extsetup def _installobsmarkersdiscovery(ui): - olddisco = exchange.pushdiscoverymapping['obsmarker'] + olddisco = exchange.pushdiscoverymapping[b'obsmarker'] def newdisco(pushop): _pushdiscoveryobsmarkers(olddisco, pushop) - exchange.pushdiscoverymapping['obsmarker'] = newdisco + exchange.pushdiscoverymapping[b'obsmarker'] = newdisco def buildpullobsmarkersboundaries(pullop, bundle2=True): """small function returning the argument for pull markers call @@ -833,25 +833,25 @@ repo = pullop.repo remote = pullop.remote unfi = repo.unfiltered() - revs = unfi.revs('::(%ln - null)', pullop.common) - boundaries = {'heads': pullop.pulledsubset} + revs = unfi.revs(b'::(%ln - null)', pullop.common) + boundaries = {b'heads': pullop.pulledsubset} if not revs: # nothing common - boundaries['common'] = [node.nullid] + boundaries[b'common'] = [node.nullid] return boundaries if not usediscovery(repo): # discovery disabled by users. repo.ui.status(obsdiscovery_skip_message) - boundaries['common'] = [node.nullid] + boundaries[b'common'] = [node.nullid] return boundaries if bundle2 and _canobshashrange(repo, remote): - obsexcmsg(repo.ui, "looking for common markers in %i nodes\n" + obsexcmsg(repo.ui, b"looking for common markers in %i nodes\n" % len(revs)) - boundaries['missing'] = findmissingrange(repo.ui, unfi, pullop.remote, - revs) + boundaries[b'missing'] = findmissingrange(repo.ui, unfi, pullop.remote, + revs) else: - boundaries['common'] = [node.nullid] + boundaries[b'common'] = [node.nullid] return boundaries # merge later for outer layer wrapping
--- a/hgext3rd/evolve/obsexchange.py Mon Jul 29 14:43:15 2019 +0200 +++ b/hgext3rd/evolve/obsexchange.py Fri Sep 27 13:03:18 2019 +0200 @@ -37,7 +37,7 @@ obsexcmsg = utility.obsexcmsg obsexcprg = utility.obsexcprg -eh.configitem('experimental', 'verbose-obsolescence-exchange', False) +eh.configitem(b'experimental', b'verbose-obsolescence-exchange', False) _bestformat = max(obsolete.formats.keys()) @@ -58,46 +58,46 @@ # <= hg 4.5 from mercurial import wireproto gboptsmap = wireproto.gboptsmap - gboptsmap['evo_obscommon'] = 'nodes' - gboptsmap['evo_missing_nodes'] = 'nodes' + gboptsmap[b'evo_obscommon'] = b'nodes' + gboptsmap[b'evo_missing_nodes'] = b'nodes' @eh.wrapfunction(exchange, '_pullbundle2extraprepare') def _addobscommontob2pull(orig, pullop, kwargs): ret = orig(pullop, kwargs) ui = pullop.repo.ui - if ('obsmarkers' in kwargs - and pullop.remote.capable('_evoext_getbundle_obscommon')): + if (b'obsmarkers' in kwargs + and pullop.remote.capable(b'_evoext_getbundle_obscommon')): boundaries = obsdiscovery.buildpullobsmarkersboundaries(pullop) - if 'common' in boundaries: - common = boundaries['common'] + if b'common' in boundaries: + common = boundaries[b'common'] if common != pullop.common: - obsexcmsg(ui, 'request obsmarkers for some common nodes\n') + obsexcmsg(ui, b'request obsmarkers for some common nodes\n') if common != [node.nullid]: - kwargs['evo_obscommon'] = common - elif 'missing' in boundaries: - missing = boundaries['missing'] + kwargs[b'evo_obscommon'] = common + elif b'missing' in boundaries: + missing = boundaries[b'missing'] if missing: - obsexcmsg(ui, 'request obsmarkers for %d common nodes\n' + obsexcmsg(ui, b'request obsmarkers for %d common nodes\n' % len(missing)) - kwargs['evo_missing_nodes'] = missing + kwargs[b'evo_missing_nodes'] = missing return ret def _getbundleobsmarkerpart(orig, bundler, repo, source, **kwargs): - if not (set(['evo_obscommon', 'evo_missing_nodes']) & set(kwargs)): + if not (set([r'evo_obscommon', r'evo_missing_nodes']) & set(kwargs)): return orig(bundler, repo, source, **kwargs) if kwargs.get('obsmarkers', False): heads = kwargs.get('heads') - if 'evo_obscommon' in kwargs: + if r'evo_obscommon' in kwargs: if heads is None: heads = repo.heads() obscommon = kwargs.get('evo_obscommon', ()) assert obscommon - obsset = repo.unfiltered().set('::%ln - ::%ln', heads, obscommon) + obsset = repo.unfiltered().set(b'::%ln - ::%ln', heads, obscommon) subset = [c.node() for c in obsset] else: common = kwargs.get('common') - subset = [c.node() for c in repo.unfiltered().set('only(%ln, %ln)', heads, common)] + subset = [c.node() for c in repo.unfiltered().set(b'only(%ln, %ln)', heads, common)] subset += kwargs['evo_missing_nodes'] markers = repo.obsstore.relevantmarkers(subset) if util.safehasattr(bundle2, 'buildobsmarkerspart'): @@ -138,36 +138,36 @@ from mercurial import wireproto gboptsmap = wireproto.gboptsmap wireprotov1server = wireproto - gboptsmap['evo_obscommon'] = 'nodes' + gboptsmap[b'evo_obscommon'] = b'nodes' # wrap module content - origfunc = exchange.getbundle2partsmapping['obsmarkers'] + origfunc = exchange.getbundle2partsmapping[b'obsmarkers'] def newfunc(*args, **kwargs): return _getbundleobsmarkerpart(origfunc, *args, **kwargs) - exchange.getbundle2partsmapping['obsmarkers'] = newfunc + exchange.getbundle2partsmapping[b'obsmarkers'] = newfunc extensions.wrapfunction(wireprotov1server, 'capabilities', _obscommon_capabilities) # wrap command content - oldcap, args = wireprotov1server.commands['capabilities'] + oldcap, args = wireprotov1server.commands[b'capabilities'] def newcap(repo, proto): return _obscommon_capabilities(oldcap, repo, proto) - wireprotov1server.commands['capabilities'] = (newcap, args) + wireprotov1server.commands[b'capabilities'] = (newcap, args) def _pushobsmarkers(repo, data): tr = lock = None try: lock = repo.lock() - tr = repo.transaction('pushkey: obsolete markers') + tr = repo.transaction(b'pushkey: obsolete markers') new = repo.obsstore.mergemarkers(tr, data) if new is not None: - obsexcmsg(repo.ui, "%i obsolescence markers added\n" % new, True) + obsexcmsg(repo.ui, b"%i obsolescence markers added\n" % new, True) tr.close() finally: lockmod.release(tr, lock) - repo.hook('evolve_pushobsmarkers') + repo.hook(b'evolve_pushobsmarkers') def srv_pushobsmarkers(repo, proto): """wireprotocol command""" @@ -187,18 +187,18 @@ def _getobsmarkersstream(repo, heads=None, common=None): """Get a binary stream for all markers relevant to `::<heads> - ::<common>` """ - revset = '' + revset = b'' args = [] repo = repo.unfiltered() if heads is None: - revset = 'all()' + revset = b'all()' elif heads: - revset += "(::%ln)" + revset += b"(::%ln)" args.append(heads) else: - assert False, 'pulling no heads?' + assert False, b'pulling no heads?' if common: - revset += ' - (::%ln)' + revset += b' - (::%ln)' args.append(common) nodes = [c.node() for c in repo.set(revset, *args)] markers = repo.obsstore.relevantmarkers(nodes) @@ -220,20 +220,20 @@ except (ImportError, AttributeError): from mercurial import wireproto as wireprototypes wireprotov1server = wireprototypes - opts = wireprotov1server.options('', ['heads', 'common'], others) + opts = wireprotov1server.options(b'', [b'heads', b'common'], others) for k, v in opts.items(): - if k in ('heads', 'common'): + if k in (b'heads', b'common'): opts[k] = wireprototypes.decodelist(v) obsdata = _getobsmarkersstream(repo, **opts) finaldata = StringIO() obsdata = obsdata.getvalue() - finaldata.write('%20i' % len(obsdata)) + finaldata.write(b'%20i' % len(obsdata)) finaldata.write(obsdata) finaldata.seek(0) return wireprototypes.streamres(reader=finaldata, v1compressible=True) -abortmsg = "won't exchange obsmarkers through pushkey" -hint = "upgrade your client or server to use the bundle2 protocol" +abortmsg = b"won't exchange obsmarkers through pushkey" +hint = b"upgrade your client or server to use the bundle2 protocol" class HTTPCompatibleAbort(hgwebcommon.ErrorResponse, error.Abort): def __init__(self, message, code, hint=None): @@ -256,4 +256,4 @@ @eh.uisetup def setuppushkeyforbidding(ui): - pushkey._namespaces['obsolete'] = (forbidpushkey, forbidlistkey) + pushkey._namespaces[b'obsolete'] = (forbidpushkey, forbidlistkey)
--- a/hgext3rd/evolve/obshashtree.py Mon Jul 29 14:43:15 2019 +0200 +++ b/hgext3rd/evolve/obshashtree.py Fri Sep 27 13:03:18 2019 +0200 @@ -42,14 +42,14 @@ debug command stayed as an inspection tool. It does not seem supseful to upstream the command with the rest of evolve. We can safely drop it.""" if v0 and v1: - raise error.Abort('cannot only specify one format') + raise error.Abort(b'cannot only specify one format') elif v0: treefunc = _obsrelsethashtreefm0 else: treefunc = _obsrelsethashtreefm1 for chg, obs in treefunc(repo): - ui.status('%s %s\n' % (node.hex(chg), node.hex(obs))) + ui.status(b'%s %s\n' % (node.hex(chg), node.hex(obs))) def _obsrelsethashtreefm0(repo): return _obsrelsethashtree(repo, obsolete._fm0encodeonemarker) @@ -61,8 +61,8 @@ cache = [] unfi = repo.unfiltered() markercache = {} - compat.progress(repo.ui, _("preparing locally"), 0, total=len(unfi), - unit=_("changesets")) + compat.progress(repo.ui, _(b"preparing locally"), 0, total=len(unfi), + unit=_(b"changesets")) for i in unfi: ctx = unfi[i] entry = 0 @@ -92,7 +92,7 @@ cache.append((ctx.node(), sha.digest())) else: cache.append((ctx.node(), node.nullid)) - compat.progress(repo.ui, _("preparing locally"), i, total=len(unfi), - unit=_("changesets")) - compat.progress(repo.ui, _("preparing locally"), None) + compat.progress(repo.ui, _(b"preparing locally"), i, total=len(unfi), + unit=_(b"changesets")) + compat.progress(repo.ui, _(b"preparing locally"), None) return cache
--- a/hgext3rd/evolve/obshistory.py Mon Jul 29 14:43:15 2019 +0200 +++ b/hgext3rd/evolve/obshistory.py Fri Sep 27 13:03:18 2019 +0200 @@ -31,13 +31,13 @@ eh = exthelper.exthelper() # Config -efd = {'default': True} # pass a default value unless the config is registered +efd = {b'default': True} # pass a default value unless the config is registered @eh.extsetup def enableeffectflags(ui): item = (getattr(ui, '_knownconfig', {}) - .get('experimental', {}) - .get('evolution.effect-flags')) + .get(b'experimental', {}) + .get(b'evolution.effect-flags')) if item is not None: item.default = True efd.clear() @@ -79,10 +79,10 @@ Returns 0 on success. """ - ui.pager('obslog') + ui.pager(b'obslog') revs = list(revs) + opts['rev'] if not revs: - revs = ['.'] + revs = [b'.'] revs = scmutil.revrange(repo, revs) if opts['graph']: @@ -131,7 +131,7 @@ values = [] for sset in fullsuccessorsets: - values.append({'successors': sset, 'markers': sset.markers}) + values.append({b'successors': sset, b'markers': sset.markers}) return values @@ -154,10 +154,10 @@ # Compat 4.6 if not util.safehasattr(self, "_includediff"): - self._includediff = diffopts and diffopts.get('patch') + self._includediff = diffopts and diffopts.get(b'patch') - self.template = diffopts and diffopts.get('template') - self.filter = diffopts and diffopts.get('filternonlocal') + self.template = diffopts and diffopts.get(b'template') + self.filter = diffopts and diffopts.get(b'filternonlocal') def show(self, ctx, copies=None, matchfn=None, **props): if self.buffered: @@ -165,12 +165,12 @@ changenode = ctx.node() - _props = {"template": self.template} - fm = self.ui.formatter('debugobshistory', _props) + _props = {b"template": self.template} + fm = self.ui.formatter(b'debugobshistory', _props) _debugobshistorydisplaynode(fm, self.repo, changenode) - markerfm = fm.nested("markers") + markerfm = fm.nested(b"markers") # Succs markers if self.filter is False: @@ -186,21 +186,21 @@ r = _successorsandmarkers(self.repo, ctx) for succset in sorted(r): - markers = succset["markers"] + markers = succset[b"markers"] if not markers: continue - successors = succset["successors"] + successors = succset[b"successors"] _debugobshistorydisplaysuccsandmarkers(markerfm, successors, markers, ctx.node(), self.repo, self._includediff) markerfm.end() - markerfm.plain('\n') + markerfm.plain(b'\n') fm.end() self.hunk[ctx.node()] = self.ui.popbuffer() else: ### graph output is buffered only - msg = 'cannot be used outside of the graphlog (yet)' + msg = b'cannot be used outside of the graphlog (yet)' raise error.ProgrammingError(msg) def flush(self, ctx): @@ -211,43 +211,43 @@ def patchavailable(node, repo, successors): if node not in repo: - return False, "context is not local" + return False, b"context is not local" if len(successors) == 0: - return False, "no successors" + return False, b"no successors" elif len(successors) > 1: - return False, "too many successors (%d)" % len(successors) + return False, b"too many successors (%d)" % len(successors) succ = successors[0] if succ not in repo: - return False, "successor is unknown locally" + return False, b"successor is unknown locally" # Check that both node and succ have the same parents nodep1, nodep2 = repo[node].p1(), repo[node].p2() succp1, succp2 = repo[succ].p1(), repo[succ].p2() if nodep1 != succp1 or nodep2 != succp2: - return False, "changesets rebased" + return False, b"changesets rebased" return True, succ def getmarkerdescriptionpatch(repo, basedesc, succdesc): # description are stored without final new line, # add one to avoid ugly diff - basedesc += '\n' - succdesc += '\n' + basedesc += b'\n' + succdesc += b'\n' # fake file name - basename = "changeset-description" - succname = "changeset-description" + basename = b"changeset-description" + succname = b"changeset-description" d = compat.strdiff(basedesc, succdesc, basename, succname) uheaders, hunks = d # Copied from patch.diff - text = ''.join(sum((list(hlines) for hrange, hlines in hunks), [])) - patch = "\n".join(uheaders + [text]) + text = b''.join(sum((list(hlines) for hrange, hlines in hunks), [])) + patch = b"\n".join(uheaders + [text]) return patch @@ -333,7 +333,7 @@ # Then choose a random node from the cycle breaknode = sorted(cycle)[0] # And display it by force - repo.ui.debug('obs-cycle detected, forcing display of %s\n' + repo.ui.debug(b'obs-cycle detected, forcing display of %s\n' % nodemod.short(breaknode)) validcandidates = [breaknode] @@ -435,7 +435,7 @@ def _debugobshistoryrevs(ui, repo, revs, opts): """ Display the obsolescence history for revset """ - fm = ui.formatter('debugobshistory', pycompat.byteskwargs(opts)) + fm = ui.formatter(b'debugobshistory', pycompat.byteskwargs(opts)) precursors = repo.obsstore.predecessors successors = repo.obsstore.successors nodec = repo.changelog.node @@ -451,7 +451,7 @@ succs = successors.get(ctxnode, ()) - markerfm = fm.nested("markers") + markerfm = fm.nested(b"markers") for successor in sorted(succs): includediff = opts and opts.get("patch") _debugobshistorydisplaymarker(markerfm, successor, ctxnode, unfi, includediff) @@ -477,24 +477,24 @@ shortdescription = shortdescription.splitlines()[0] fm.startitem() - fm.write('node', '%s', bytes(ctx), - label="evolve.node") - fm.plain(' ') + fm.write(b'node', b'%s', bytes(ctx), + label=b"evolve.node") + fm.plain(b' ') - fm.write('rev', '(%d)', ctx.rev(), - label="evolve.rev") - fm.plain(' ') + fm.write(b'rev', b'(%d)', ctx.rev(), + label=b"evolve.rev") + fm.plain(b' ') - fm.write('shortdescription', '%s', shortdescription, - label="evolve.short_description") - fm.plain('\n') + fm.write(b'shortdescription', b'%s', shortdescription, + label=b"evolve.short_description") + fm.plain(b'\n') def _debugobshistorydisplaymissingctx(fm, nodewithoutctx): hexnode = nodemod.short(nodewithoutctx) fm.startitem() - fm.write('node', '%s', hexnode, - label="evolve.node evolve.missing_change_ctx") - fm.plain('\n') + fm.write(b'node', b'%s', hexnode, + label=b"evolve.node evolve.missing_change_ctx") + fm.plain(b'\n') def _debugobshistorydisplaymarker(fm, marker, node, repo, includediff=False): succnodes = marker[1] @@ -502,18 +502,18 @@ metadata = dict(marker[3]) fm.startitem() - fm.plain(' ') + fm.plain(b' ') # Detect pruned revisions if len(succnodes) == 0: - verb = 'pruned' + verb = b'pruned' else: - verb = 'rewritten' + verb = b'rewritten' - fm.write('verb', '%s', verb, - label="evolve.verb") + fm.write(b'verb', b'%s', verb, + label=b"evolve.verb") - effectflag = metadata.get('ef1') + effectflag = metadata.get(b'ef1') if effectflag is not None: try: effectflag = int(effectflag) @@ -524,50 +524,50 @@ # XXX should be a dict if effectflag & DESCCHANGED: - effect.append('description') + effect.append(b'description') if effectflag & METACHANGED: - effect.append('meta') + effect.append(b'meta') if effectflag & USERCHANGED: - effect.append('user') + effect.append(b'user') if effectflag & DATECHANGED: - effect.append('date') + effect.append(b'date') if effectflag & BRANCHCHANGED: - effect.append('branch') + effect.append(b'branch') if effectflag & PARENTCHANGED: - effect.append('parent') + effect.append(b'parent') if effectflag & DIFFCHANGED: - effect.append('content') + effect.append(b'content') if effect: - fmteffect = fm.formatlist(effect, 'effect', sep=', ') - fm.write('effect', '(%s)', fmteffect) + fmteffect = fm.formatlist(effect, b'effect', sep=b', ') + fm.write(b'effect', b'(%s)', fmteffect) if len(succnodes) > 0: - fm.plain(' as ') + fm.plain(b' as ') shortsnodes = (nodemod.short(succnode) for succnode in sorted(succnodes)) - nodes = fm.formatlist(shortsnodes, 'succnodes', sep=', ') - fm.write('succnodes', '%s', nodes, - label="evolve.node") + nodes = fm.formatlist(shortsnodes, b'succnodes', sep=b', ') + fm.write(b'succnodes', b'%s', nodes, + label=b"evolve.node") - operation = metadata.get('operation') + operation = metadata.get(b'operation') if operation: - fm.plain(' using ') - fm.write('operation', '%s', operation, label="evolve.operation") + fm.plain(b' using ') + fm.write(b'operation', b'%s', operation, label=b"evolve.operation") - fm.plain(' by ') + fm.plain(b' by ') - fm.write('user', '%s', metadata['user'], - label="evolve.user") - fm.plain(' ') + fm.write(b'user', b'%s', metadata[b'user'], + label=b"evolve.user") + fm.plain(b' ') - fm.write('date', '(%s)', fm.formatdate(date), - label="evolve.date") + fm.write(b'date', b'(%s)', fm.formatdate(date), + label=b"evolve.date") # initial support for showing note - if metadata.get('note'): - fm.plain('\n note: ') - fm.write('note', "%s", metadata['note'], label="evolve.note") + if metadata.get(b'note'): + fm.plain(b'\n note: ') + fm.write(b'note', b"%s", metadata[b'note'], label=b"evolve.note") # Patch display if includediff is True: @@ -585,40 +585,44 @@ if descriptionpatch: # add the diffheader - diffheader = "diff -r %s -r %s changeset-description\n" % \ + diffheader = b"diff -r %s -r %s changeset-description\n" %\ (basectx, succctx) descriptionpatch = diffheader + descriptionpatch def tolist(text): return [text] - fm.plain("\n") + fm.plain(b"\n") for chunk, label in patch.difflabel(tolist, descriptionpatch): - chunk = chunk.strip('\t') - if chunk and chunk != '\n': - fm.plain(' ') - fm.write('desc-diff', '%s', chunk, label=label) + chunk = chunk.strip(b'\t') + if chunk and chunk != b'\n': + fm.plain(b' ') + fm.write(b'desc-diff', b'%s', chunk, label=label) # Content patch diffopts = patch.diffallopts(repo.ui, {}) matchfn = scmutil.matchall(repo) firstline = True + linestart = True for chunk, label in patch.diffui(repo, node, succ, matchfn, opts=diffopts): if firstline: - fm.plain('\n') + fm.plain(b'\n') firstline = False - if chunk and chunk != '\n': - fm.plain(' ') - fm.write('patch', '%s', chunk, label=label) + if linestart: + fm.plain(b' ') + linestart = False + if chunk == b'\n': + linestart = True + fm.write(b'patch', b'%s', chunk, label=label) else: - nopatch = " (No patch available, %s)" % _patchavailable[1] - fm.plain("\n") + nopatch = b" (No patch available, %s)" % _patchavailable[1] + fm.plain(b"\n") # TODO: should be in json too fm.plain(nopatch) - fm.plain("\n") + fm.plain(b"\n") def _debugobshistorydisplaysuccsandmarkers(fm, succnodes, markers, node, repo, includediff=False): """ @@ -626,17 +630,17 @@ to accept multiple markers as input. """ fm.startitem() - fm.plain(' ') + fm.plain(b' ') # Detect pruned revisions - verb = _successorsetverb(succnodes, markers)["verb"] + verb = _successorsetverb(succnodes, markers)[b"verb"] - fm.write('verb', '%s', verb, - label="evolve.verb") + fm.write(b'verb', b'%s', verb, + label=b"evolve.verb") # Effect flag metadata = [dict(marker[3]) for marker in markers] - ef1 = [data.get('ef1') for data in metadata] + ef1 = [data.get(b'ef1') for data in metadata] effectflag = 0 for ef in ef1: @@ -648,45 +652,45 @@ # XXX should be a dict if effectflag & DESCCHANGED: - effect.append('description') + effect.append(b'description') if effectflag & METACHANGED: - effect.append('meta') + effect.append(b'meta') if effectflag & USERCHANGED: - effect.append('user') + effect.append(b'user') if effectflag & DATECHANGED: - effect.append('date') + effect.append(b'date') if effectflag & BRANCHCHANGED: - effect.append('branch') + effect.append(b'branch') if effectflag & PARENTCHANGED: - effect.append('parent') + effect.append(b'parent') if effectflag & DIFFCHANGED: - effect.append('content') + effect.append(b'content') if effect: - fmteffect = fm.formatlist(effect, 'effect', sep=', ') - fm.write('effect', '(%s)', fmteffect) + fmteffect = fm.formatlist(effect, b'effect', sep=b', ') + fm.write(b'effect', b'(%s)', fmteffect) if len(succnodes) > 0: - fm.plain(' as ') + fm.plain(b' as ') shortsnodes = (nodemod.short(succnode) for succnode in sorted(succnodes)) - nodes = fm.formatlist(shortsnodes, 'succnodes', sep=', ') - fm.write('succnodes', '%s', nodes, - label="evolve.node") + nodes = fm.formatlist(shortsnodes, b'succnodes', sep=b', ') + fm.write(b'succnodes', b'%s', nodes, + label=b"evolve.node") # Operations operations = compat.markersoperations(markers) if operations: - fm.plain(' using ') - fm.write('operation', '%s', ", ".join(operations), label="evolve.operation") + fm.plain(b' using ') + fm.write(b'operation', b'%s', b", ".join(operations), label=b"evolve.operation") - fm.plain(' by ') + fm.plain(b' by ') # Users users = compat.markersusers(markers) - fm.write('user', '%s', ", ".join(users), - label="evolve.user") - fm.plain(' ') + fm.write(b'user', b'%s', b", ".join(users), + label=b"evolve.user") + fm.plain(b' ') # Dates dates = compat.markersdates(markers) @@ -695,10 +699,10 @@ max_date = max(dates) if min_date == max_date: - fm.write("date", "(at %s)", fm.formatdate(min_date), label="evolve.date") + fm.write(b"date", b"(at %s)", fm.formatdate(min_date), label=b"evolve.date") else: - fm.write("date", "(between %s and %s)", fm.formatdate(min_date), - fm.formatdate(max_date), label="evolve.date") + fm.write(b"date", b"(between %s and %s)", fm.formatdate(min_date), + fm.formatdate(max_date), label=b"evolve.date") # initial support for showing note # if metadata.get('note'): @@ -721,40 +725,44 @@ if descriptionpatch: # add the diffheader - diffheader = "diff -r %s -r %s changeset-description\n" % \ + diffheader = b"diff -r %s -r %s changeset-description\n" %\ (basectx, succctx) descriptionpatch = diffheader + descriptionpatch def tolist(text): return [text] - fm.plain("\n") + fm.plain(b"\n") for chunk, label in patch.difflabel(tolist, descriptionpatch): - chunk = chunk.strip('\t') - if chunk and chunk != '\n': - fm.plain(' ') - fm.write('desc-diff', '%s', chunk, label=label) + chunk = chunk.strip(b'\t') + if chunk and chunk != b'\n': + fm.plain(b' ') + fm.write(b'desc-diff', b'%s', chunk, label=label) # Content patch diffopts = patch.diffallopts(repo.ui, {}) matchfn = scmutil.matchall(repo) firstline = True + linestart = True for chunk, label in patch.diffui(repo, node, succ, matchfn, opts=diffopts): if firstline: - fm.plain('\n') + fm.plain(b'\n') firstline = False - if chunk and chunk != '\n': - fm.plain(' ') - fm.write('patch', '%s', chunk, label=label) + if linestart: + fm.plain(b' ') + linestart = False + if chunk == b'\n': + linestart = True + fm.write(b'patch', b'%s', chunk, label=label) else: - nopatch = " (No patch available, %s)" % _patchavailable[1] - fm.plain("\n") + nopatch = b" (No patch available, %s)" % _patchavailable[1] + fm.plain(b"\n") # TODO: should be in json too fm.plain(nopatch) - fm.plain("\n") + fm.plain(b"\n") # logic around storing and using effect flags DESCCHANGED = 1 << 0 # action changed the description @@ -766,11 +774,11 @@ BRANCHCHANGED = 1 << 6 # the branch changed METABLACKLIST = [ - re.compile('^__touch-noise__$'), - re.compile('^branch$'), - re.compile('^.*-source$'), - re.compile('^.*_source$'), - re.compile('^source$'), + re.compile(br'^__touch-noise__$'), + re.compile(br'^branch$'), + re.compile(br'^.*-source$'), + re.compile(br'^.*_source$'), + re.compile(br'^source$'), ] def ismetablacklisted(metaitem): @@ -814,17 +822,17 @@ if len(successorssets) == 0: # The commit has been pruned - return 'pruned' + return b'pruned' elif len(successorssets) > 1: - return 'diverged' + return b'diverged' else: # No divergence, only one set of successors successors = successorssets[0] if len(successors) == 1: - return 'superseed' + return b'superseed' else: - return 'superseed_split' + return b'superseed_split' def _getobsfateandsuccs(repo, revnode, successorssets=None): """ Return a tuple containing: @@ -857,8 +865,8 @@ dates = [m[4] for m in markers] return { - 'min_date': min(dates), - 'max_date': max(dates) + b'min_date': min(dates), + b'max_date': max(dates) } def _successorsetusers(successorset, markers): @@ -869,18 +877,18 @@ # Check that user is present in meta markersmeta = [dict(m[3]) for m in markers] - users = set(meta.get('user') for meta in markersmeta if meta.get('user')) + users = set(meta.get(b'user') for meta in markersmeta if meta.get(b'user')) - return {'users': sorted(users)} + return {b'users': sorted(users)} VERBMAPPING = { - DESCCHANGED: "reworded", - METACHANGED: "meta-changed", - USERCHANGED: "reauthored", - DATECHANGED: "date-changed", - BRANCHCHANGED: "branch-changed", - PARENTCHANGED: "rebased", - DIFFCHANGED: "amended" + DESCCHANGED: b"reworded", + METACHANGED: b"meta-changed", + USERCHANGED: b"reauthored", + DATECHANGED: b"date-changed", + BRANCHCHANGED: b"branch-changed", + PARENTCHANGED: b"rebased", + DIFFCHANGED: b"amended" } def _successorsetverb(successorset, markers): @@ -888,12 +896,12 @@ """ verb = None if not successorset: - verb = 'pruned' + verb = b'pruned' elif len(successorset) == 1: # Check for effect flag metadata = [dict(marker[3]) for marker in markers] - ef1 = [data.get('ef1') for data in metadata] + ef1 = [data.get(b'ef1') for data in metadata] if all(ef1): combined = 0 @@ -905,17 +913,17 @@ verb = VERBMAPPING[combined] if verb is None: - verb = 'rewritten' + verb = b'rewritten' else: - verb = 'split' - return {'verb': verb} + verb = b'split' + return {b'verb': verb} # Use a more advanced version of obsfateverb that uses effect-flag if util.safehasattr(obsutil, 'obsfateverb'): @eh.wrapfunction(obsutil, 'obsfateverb') def obsfateverb(orig, *args, **kwargs): - return _successorsetverb(*args, **kwargs)['verb'] + return _successorsetverb(*args, **kwargs)[b'verb'] # Hijack callers of successorsetverb elif util.safehasattr(obsutil, 'obsfateprinter'): @@ -924,7 +932,7 @@ def obsfateprinter(orig, successors, markers, ui): def closure(successors): - return _successorsetverb(successors, markers)['verb'] + return _successorsetverb(successors, markers)[b'verb'] if not util.safehasattr(obsutil, 'successorsetverb'): return orig(successors, markers, ui) @@ -1000,8 +1008,8 @@ # Format basic data data = { - "successors": sorted(successorset), - "markers": sorted(markers) + b"successors": sorted(successorset), + b"markers": sorted(markers) } # Call an extensible list of functions to override or add new data
--- a/hgext3rd/evolve/rewind.py Mon Jul 29 14:43:15 2019 +0200 +++ b/hgext3rd/evolve/rewind.py Fri Sep 27 13:03:18 2019 +0200 @@ -32,8 +32,10 @@ (b'', b'exact', None, _(b"only rewind explicitly selected revisions")), (b'', b'from', [], _(b"rewind these revisions to their predecessors"), _(b'REV')), + (b'k', b'keep', None, + _(b"do not modify working directory during rewind")), ], - _(b''), + _(b'[--as-divergence] [--exact] [--keep] [--to REV]... [--from REV]...'), helpbasic=True) def rewind(ui, repo, **opts): """rewind a stack of changesets to a previous state @@ -46,7 +48,7 @@ obsolete the changeset you rewind from). Rewinding "to" will restore the changeset you have selected (and obsolete their latest successors). - By default, we rewind from the working copy parents, restoring its + By default, we rewind from the working directory parents, restoring its predecessor. When we rewind to an obsolete version, we also rewind to all its obsolete @@ -90,15 +92,17 @@ ctx = unfi[rev] ssets = obsutil.successorssets(repo, ctx.node(), sscache) if 1 < len(ssets): - msg = _('rewind confused by divergence on %s') % ctx - hint = _('solve divergence first or use "--as-divergence"') + msg = _(b'rewind confused by divergence on %s') % ctx + hint = _(b'solve divergence first or use "--as-divergence"') raise error.Abort(msg, hint=hint) if ssets and ssets[0]: for succ in ssets[0]: successorsmap[succ].add(ctx.node()) # Check that we can rewind these changesets - with repo.transaction('rewind'): + with repo.transaction(b'rewind'): + oldctx = repo[b'.'] + for rev in sorted(rewinded): ctx = unfi[rev] rewindmap[ctx.node()] = _revive_revision(unfi, rev, rewindmap) @@ -113,15 +117,47 @@ relationships.append(rel) if wctxp.node() == source: update_target = newdest[-1] - obsolete.createmarkers(unfi, relationships, operation='rewind') + obsolete.createmarkers(unfi, relationships, operation=b'rewind') if update_target is not None: - hg.updaterepo(repo, update_target, False) + if opts.get('keep'): + hg.updaterepo(repo, oldctx, True) + + # This is largely the same as the implementation in + # strip.stripcmd() and cmdrewrite.cmdprune(). + + # only reset the dirstate for files that would actually + # change between the working context and the revived cset + newctx = repo[update_target] + changedfiles = [] + for ctx in [oldctx, newctx]: + # blindly reset the files, regardless of what actually + # changed + changedfiles.extend(ctx.files()) - repo.ui.status(_('rewinded to %d changesets\n') % len(rewinded)) + # reset files that only changed in the dirstate too + dirstate = repo.dirstate + dirchanges = [f for f in dirstate if dirstate[f] != 'n'] + changedfiles.extend(dirchanges) + repo.dirstate.rebuild(newctx.node(), newctx.manifest(), + changedfiles) + + # TODO: implement restoration of copies/renames + # Ideally this step should be handled by dirstate.rebuild + # or scmutil.movedirstate, but right now there's no copy + # tracing across obsolescence relation (oldctx <-> newctx). + revertopts = {'no_backup': True, 'all': True, + 'rev': oldctx.node()} + with ui.configoverride({(b'ui', b'quiet'): True}): + cmdutil.revert(repo.ui, repo, oldctx, + repo.dirstate.parents(), **revertopts) + else: + hg.updaterepo(repo, update_target, False) + + repo.ui.status(_(b'rewinded to %d changesets\n') % len(rewinded)) if relationships: - repo.ui.status(_('(%d changesets obsoleted)\n') % len(relationships)) - if update_target is not None: - ui.status(_('working directory is now at %s\n') % repo['.']) + repo.ui.status(_(b'(%d changesets obsoleted)\n') % len(relationships)) + if update_target is not None and not opts.get('keep'): + ui.status(_(b'working directory is now at %s\n') % repo[b'.']) def _select_rewinded(repo, opts): """select the revision we shoudl rewind to @@ -131,18 +167,18 @@ revsto = opts.get('to') revsfrom = opts.get('from') if not (revsto or revsfrom): - revsfrom.append('.') + revsfrom.append(b'.') if revsto: rewinded.update(scmutil.revrange(repo, revsto)) if revsfrom: succs = scmutil.revrange(repo, revsfrom) - rewinded.update(unfi.revs('predecessors(%ld)', succs)) + rewinded.update(unfi.revs(b'predecessors(%ld)', succs)) if not rewinded: - raise error.Abort('no revision to rewind to') + raise error.Abort(b'no revision to rewind to') if not opts['exact']: - rewinded = unfi.revs('obsolete() and ::%ld', rewinded) + rewinded = unfi.revs(b'obsolete() and ::%ld', rewinded) return sorted(rewinded) @@ -152,14 +188,14 @@ ctx = unfi[rev] extra = ctx.extra().copy() # rewind hash should be unique over multiple rewind. - user = unfi.ui.config('devel', 'user.obsmarker') + user = unfi.ui.config(b'devel', b'user.obsmarker') if not user: user = unfi.ui.username() - date = unfi.ui.configdate('devel', 'default-date') + date = unfi.ui.configdate(b'devel', b'default-date') if date is None: date = compat.makedate() - noise = "%s\0%s\0%d\0%d" % (ctx.node(), user, date[0], date[1]) - extra['__rewind-hash__'] = hashlib.sha256(noise).hexdigest() + noise = b"%s\0%s\0%d\0%d" % (ctx.node(), user, date[0], date[1]) + extra[b'__rewind-hash__'] = hashlib.sha256(noise).hexdigest() p1 = ctx.p1().node() p1 = rewindmap.get(p1, p1) @@ -169,13 +205,13 @@ updates = [] if len(ctx.parents()) > 1: updates = ctx.parents() - extradict = {'extra': extra} + extradict = {b'extra': extra} new, unusedvariable = rewriteutil.rewrite(unfi, ctx, updates, ctx, [p1, p2], commitopts=extradict) obsolete.createmarkers(unfi, [(ctx, (unfi[new],))], - flag=identicalflag, operation='rewind') + flag=identicalflag, operation=b'rewind') return new
--- a/hgext3rd/evolve/rewriteutil.py Mon Jul 29 14:43:15 2019 +0200 +++ b/hgext3rd/evolve/rewriteutil.py Fri Sep 27 13:03:18 2019 +0200 @@ -44,37 +44,37 @@ numrevs = len(revs) if numrevs < maxrevs: shorts = [node.short(tonode(r)) for r in revs] - summary = ', '.join(shorts) + summary = b', '.join(shorts) else: first = revs.first() - summary = _('%s and %d others') + summary = _(b'%s and %d others') summary %= (node.short(tonode(first)), numrevs - 1) return summary -def precheck(repo, revs, action='rewrite'): +def precheck(repo, revs, action=b'rewrite'): """check if <revs> can be rewritten <action> can be used to control the commit message. """ if node.nullrev in revs: - msg = _("cannot %s the null revision") % (action) - hint = _("no changeset checked out") + msg = _(b"cannot %s the null revision") % (action) + hint = _(b"no changeset checked out") raise error.Abort(msg, hint=hint) if any(util.safehasattr(r, 'rev') for r in revs): - msg = "rewriteutil.precheck called with ctx not revs" + msg = b"rewriteutil.precheck called with ctx not revs" repo.ui.develwarn(msg) revs = (r.rev() for r in revs) - publicrevs = repo.revs('%ld and public()', revs) + publicrevs = repo.revs(b'%ld and public()', revs) if publicrevs: summary = _formatrevs(repo, publicrevs) - msg = _("cannot %s public changesets: %s") % (action, summary) - hint = _("see 'hg help phases' for details") + msg = _(b"cannot %s public changesets: %s") % (action, summary) + hint = _(b"see 'hg help phases' for details") raise error.Abort(msg, hint=hint) newunstable = disallowednewunstable(repo, revs) if newunstable: - msg = _("%s will orphan %i descendants") + msg = _(b"%s will orphan %i descendants") msg %= (action, len(newunstable)) - hint = _("see 'hg help evolution.instability'") + hint = _(b"see 'hg help evolution.instability'") raise error.Abort(msg, hint=hint) def bookmarksupdater(repo, oldid, tr): @@ -96,27 +96,34 @@ allowunstable = obsolete.isenabled(repo, obsolete.allowunstableopt) if allowunstable: return revset.baseset() - return repo.revs("(%ld::) - %ld", revs, revs) + return repo.revs(b"(%ld::) - %ld", revs, revs) def foldcheck(repo, revs): """check that <revs> can be folded""" - precheck(repo, revs, action='fold') - roots = repo.revs('roots(%ld)', revs) + precheck(repo, revs, action=b'fold') + roots = repo.revs(b'roots(%ld)', revs) if len(roots) > 1: - raise error.Abort(_("cannot fold non-linear revisions " - "(multiple roots given)")) + raise error.Abort(_(b"cannot fold non-linear revisions " + b"(multiple roots given)")) root = repo[roots.first()] if root.phase() <= phases.public: - raise error.Abort(_("cannot fold public revisions")) - heads = repo.revs('heads(%ld)', revs) + raise error.Abort(_(b"cannot fold public revisions")) + heads = repo.revs(b'heads(%ld)', revs) if len(heads) > 1: - raise error.Abort(_("cannot fold non-linear revisions " - "(multiple heads given)")) + raise error.Abort(_(b"cannot fold non-linear revisions " + b"(multiple heads given)")) head = repo[heads.first()] - baseparents = repo.revs('parents(%ld) - %ld', revs, revs) + baseparents = repo.revs(b'parents(%ld) - %ld', revs, revs) if len(baseparents) > 2: - raise error.Abort(_("cannot fold revisions that merge with more than " - "one external changeset (not in revisions)")) + raise error.Abort(_(b"cannot fold revisions that merge with more than " + b"one external changeset (not in revisions)")) + if not repo.ui.configbool(b'experimental', b'evolution.allowdivergence'): + obsolete = repo.revs(b'%ld and obsolete()', revs) + if obsolete: + msg = _(b'folding obsolete revisions may cause divergence') + hint = _(b'set experimental.evolution.allowdivergence=yes' + b' to allow folding them') + raise error.Abort(msg, hint=hint) # root's p1 is already used as the target ctx p1 baseparents -= {root.p1().rev()} p2 = repo[baseparents.first()] @@ -127,14 +134,14 @@ try: wlock = repo.wlock() lock = repo.lock() - tr = repo.transaction('prune') + tr = repo.transaction(b'prune') bmchanges = [] for bookmark in bookmarks: bmchanges.append((bookmark, None)) repo._bookmarks.applychanges(repo, tr, bmchanges) tr.close() for bookmark in sorted(bookmarks): - repo.ui.write(_("bookmark '%s' deleted\n") % bookmark) + repo.ui.write(_(b"bookmark '%s' deleted\n") % bookmark) finally: lockmod.release(tr, lock, wlock) @@ -150,8 +157,8 @@ """ repomarks = repo._bookmarks if not bookmarks.issubset(repomarks): - raise error.Abort(_("bookmark '%s' not found") % - ','.join(sorted(bookmarks - set(repomarks.keys())))) + raise error.Abort(_(b"bookmark '%s' not found") % + b','.join(sorted(bookmarks - set(repomarks.keys())))) # If the requested bookmark is not the only one pointing to a # a revision we have to only delete the bookmark and not strip @@ -177,7 +184,7 @@ try: wlock = repo.wlock() lock = repo.lock() - tr = repo.transaction('rewrite') + tr = repo.transaction(b'rewrite') base = old.p1() updatebookmarks = bookmarksupdater(repo, old.node(), tr) @@ -218,13 +225,13 @@ if not message: message = old.description() - user = commitopts.get('user') or old.user() + user = commitopts.get(b'user') or old.user() # TODO: In case not date is given, we should take the old commit date # if we are working one one changeset or mimic the fold behavior about # date - date = commitopts.get('date') or None - extra = dict(commitopts.get('extra', old.extra())) - extra['branch'] = head.branch() + date = commitopts.get(b'date') or None + extra = dict(commitopts.get(b'extra', old.extra())) + extra[b'branch'] = head.branch() new = context.memctx(repo, parents=newbases, @@ -235,7 +242,7 @@ date=date, extra=extra) - if commitopts.get('edit'): + if commitopts.get(b'edit'): new._text = cmdutil.commitforceeditor(repo, new, []) revcount = len(repo) newid = repo.commitctx(new)
--- a/hgext3rd/evolve/safeguard.py Mon Jul 29 14:43:15 2019 +0200 +++ b/hgext3rd/evolve/safeguard.py Fri Sep 27 13:03:18 2019 +0200 @@ -20,9 +20,9 @@ eh = exthelper.exthelper() # hg <= 4.8 -if 'auto-publish' not in configitems.coreitems.get('experimental', {}): +if b'auto-publish' not in configitems.coreitems.get(b'experimental', {}): - eh.configitem('experimental', 'auto-publish', 'publish') + eh.configitem(b'experimental', b'auto-publish', b'publish') @eh.reposetup def setuppublishprevention(ui, repo): @@ -31,25 +31,25 @@ def checkpush(self, pushop): super(noautopublishrepo, self).checkpush(pushop) - behavior = self.ui.config('experimental', 'auto-publish') - nocheck = behavior not in ('warn', 'abort') + behavior = self.ui.config(b'experimental', b'auto-publish') + nocheck = behavior not in (b'warn', b'abort') if nocheck or getattr(pushop, 'publish', False): return - remotephases = pushop.remote.listkeys('phases') - publishing = remotephases.get('publishing', False) + remotephases = pushop.remote.listkeys(b'phases') + publishing = remotephases.get(b'publishing', False) if publishing: if pushop.revs is None: - published = self.filtered('served').revs("not public()") + published = self.filtered(b'served').revs(b"not public()") else: - published = self.revs("::%ln - public()", pushop.revs) + published = self.revs(b"::%ln - public()", pushop.revs) if published: - if behavior == 'warn': - self.ui.warn(_('%i changesets about to be published\n') + if behavior == b'warn': + self.ui.warn(_(b'%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") + elif behavior == b'abort': + msg = _(b'push would publish 1 changesets') + hint = _(b"behavior controlled by " + b"'experimental.auto-publish' config") raise error.Abort(msg, hint=hint) repo.__class__ = noautopublishrepo
--- a/hgext3rd/evolve/serveronly.py Mon Jul 29 14:43:15 2019 +0200 +++ b/hgext3rd/evolve/serveronly.py Fri Sep 27 13:03:18 2019 +0200 @@ -25,7 +25,7 @@ ) except (ValueError, ImportError) as exc: if (isinstance(exc, ValueError) - and str(exc) != 'Attempted relative import in non-package'): + and str(exc) != b'Attempted relative import in non-package'): raise # extension imported using direct path sys.path.insert(0, os.path.dirname(os.path.dirname(__file__))) @@ -54,11 +54,11 @@ @eh.reposetup def default2evolution(ui, repo): - evolveopts = repo.ui.configlist('experimental', 'evolution') + evolveopts = repo.ui.configlist(b'experimental', b'evolution') if not evolveopts: - evolveopts = 'all' - repo.ui.setconfig('experimental', 'evolution', evolveopts) - if obsolete.isenabled(repo, 'exchange'): + evolveopts = b'all' + repo.ui.setconfig(b'experimental', b'evolution', evolveopts) + if obsolete.isenabled(repo, b'exchange'): # if no config explicitly set, disable bundle1 - if not isinstance(repo.ui.config('server', 'bundle1'), bytes): - repo.ui.setconfig('server', 'bundle1', False) + if not isinstance(repo.ui.config(b'server', b'bundle1'), bytes): + repo.ui.setconfig(b'server', b'bundle1', False)
--- a/hgext3rd/evolve/stablerange.py Mon Jul 29 14:43:15 2019 +0200 +++ b/hgext3rd/evolve/stablerange.py Fri Sep 27 13:03:18 2019 +0200 @@ -6,6 +6,352 @@ # # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. +r"""stable range + +General Goals and Properties +--------------------------- + +Stable-ranges get useful when some logic needs a recursive way to slice the +history of a repository in smaller and smaller group of revisions. Here is +example of such use cases: + +* **bundle caching:** + With an easy way to slice any subsets of history into stable-ranges, we + can cache a small number of bundles covering these ranges and reuse them for + other pull operations. Even if the pull operation have different boudaries. + +* **metadata discovery:** + With a simple way to recursively look at smaller and smaller ranges, an + algorithm can do fine-grained discovery of area of history where some mutable + metadata differ from one repository to another. Such meta data can be + obsolescence markers, CI status, lightweight stag, etc... + +To fix these use cases best, stable-ranges need some important properties: + +* the total number of ranges needed to cover a full repository is well bounded. +* the minimal number of ranges to cover an arbitrary subset of the history is well bounded +* for the same section of history, the range will be the same on any + repositories, +* the ranges are cheap to compute iteratively, each new revisions re-uses the + ranges previous revisions uses. + +Simple introduction to the Concepts +----------------------------------- + +To keep things simple, let us look at the issue on a linear history:: + + A -> B -> C -> D -> E -> F -> G -> H + +To make sure we have range that cover each part of the history with a good +granularity we use some binary recursion. The standard stable range will be: + + [A -> B -> C -> D -> E -> F -> G -> H] size 8 + [A -> B -> C -> D] [E -> F -> G -> H] size 4 + [A -> B] [C -> D] [E -> F] [G -> H] size 2 + [A] [B] [C] [D] [E] [F] [G] [H] size 1 + +Well bounded total number of ranges: +```````````````````````````````````` + +This binary slicing make sure we keep the total number of stable ranges under control. + +As you can see, we have N size 1 ranges. They are trivial and we don't care +about them. Then we have: N/2 size 2 ranges + N/4 size 4 ranges + N/8 size 8 +ranges, etc... So a total of about "length(repo)" standard ranges. + + +Well bounded number of range to cover a subset: +``````````````````````````````````````````````` + +Any subset of the history can be expressed with this standard ranges. + +For example, [A, F] subset, can be covered with 2 ranges:: + + [A ->[B -> C -> D] [E -> F] + +A less strivial example [B, F], still requires a small number of ranges (3):: + + [B] [C -> D] [E -> F] + +In practice, any subset can be expressed in at most "2 x log2(length(subset))" +stable range, well bounded value. + +Cheap incremental updates +````````````````````````` + +The scheme describe above result in 2N subranges for a repository is size N. We +do not want to have to recompute these 2N stable-ranges whenever a new revision +is added to the repository. To achieve these, the stable-ranges are defined by +**fixed boundaries** that are independant from the total size of the +repository. Here is how it looks like in our example. + +We start with a repository having only [A, F]. Notice how we still creates +power of two sized stable range:: + + [A -> B -> C -> D] + [A -> B] [C -> D] [E -> F] + [A] [B] [C] [D] [E] [F] + +This we simply adds a new revision G, we reuse more the range we already have:: + + [A -> B -> C -> D] + [A -> B] [C -> D] [E -> F] + [A] [B] [C] [D] [E] [F] [G] + +Adding H is a bigger even as we read a new boundary. + + [A -> B -> C -> D -> E -> F -> G -> H] + [A -> B -> C -> D] [E -> F -> G -> H] + [A -> B] [C -> D] [E -> F] [G -> H] + [A] [B] [C] [D] [E] [F] [G] [H] + +At most, adding a new revision `R` will introduces `log2(length(::R))` new +stable ranges. + +More advanced elements +---------------------- + +Of course, the history of repository is not as simple as our linear example. So +how do we deal with the branching and merging? To do so, we leverage the +"stable sort" algorithm defined in the `stablesort.py` module. To define the +stable range that compose a set of revison `::R`, we linearize the space by +sorting it. The stable sort algorithm has two important property: + +First, it give the same result on different repository. So we can use it to +algorithm involving multiple peers. + +Second, in case of merge, it reuse the same order as the parents as much as possible. +This is important to keep reusing existing stable range as the repository grow. + +How are ranges defined? +``````````````````````` + +To keep things small, and simple, a stable range always contains the final part +of a `stablesort(::R)` set of revision. It is defined by two items: + +* its head revision, the R in `stablesort(::R)` +* the size of that range... well almost, for implementation reason, it uses the + index of the first included item. Or in other word, the number of excluded + initial item in `stablesort(::R)`. + +Lets look at a practical case. In our initial example, `[A, B, C, D, E, F, G, +H]` is H-0; `[E, F, G, H]` is H-4; `[G, H]` is H-6 and `[H]` is H-7. + +Let us look at a non linar graph:: + + A - B - C - E + | / + -D + +and assume that `stablesort(::E) = [A, B, C, D, E]`. Then `[A, B, C]` is C-0, +`[A, B, D]` is D-0; `[D, E]` is E-3, `[E]` is E-4, etc... + +Slicing in a non linear context +``````````````````````````````` + +Branching can also affect the way we slice things. + +The small example above offers a simple example. For a size 5 (starting at the +root), standard slicing will want a size 4 part and size 1 part. So, in a +simple linear space `[A, B, C, D, E]` would be sliced as `[A, B, C, D] + [E]`. +However, in our non-linear case, `[A, B, C, D]` has two heads (C and D) and +cannot be expressed with a single range. As a result the range will be sliced +into more sub ranges:: + + stdslice(A-0) = [A, B, C] + [D] + [E] = C-0 + D-2 + A-4 + +Yet, this does not mean ranges containing a merge will always result in slicing +with many revision. the sub ranges might also silently contains them. Let us +look at an exemple:: + + A - B - C - D - E --- G - H + | / + ---------- F + +with:: + + `stablesort(::H) == [A, B, C, D, E, F, G, H]` + +then:: + + stdslice(H-0) = [A, B, C, D] + [E, F, G, H] = D-0 + H-4 + +As a result the non linearity will increase the number of subranges involved, +but in practice the impact stay limited. + +The total number of standard subranges stay under control with about +`O(log2(N))` new stable range introduced for each new revision. In practice the +total number of stableranges we have is about `O(length(repo))` + +In addition, it is worth nothing that the head of the extra ranges we have to +use will match the destination of the "jump" cached by the stablesort +algorithm. So, all this slicing can usually be done without iterating over the +stable sorted revision. + +Caching Strategy +---------------- + +The current caching strategy use a very space inefficient sqlite database. +testing show it often take 100x more space than what basic binary storage would +take. The sqlite storage was very useful at the proof of concept stage. + +Since all new stable-ranges introduced by a revision R will be "R headed". So we +could easily store their standard subranges alongside the revision information +to reuse the existing revision index. + +Subrange information can be efficiently stored, a naive approach storing all +stable ranges and their subranges would requires just 2 integer per range + 2 +integer for any extra sub-ranges other than the first and last ones. + +We can probably push efficiency further by taking advantage of the large +overlap in subranges for one non-merge revision to the next. This is probably a +premature optimisation until we start getting actual result for a naive binary +storage. + +To use this at a large scale, it would be important to compute these data at +commit time and to exchange them alongside the revision over the network. This +is similar to what we do for other cached data. + +It is also important to note that the smaller ranges can probably be computed +on the fly instead of being cached. The exact tradeoff would requires some +field testing. + +Performance +----------- + +The current implementation has not been especially optimized for performance. +The goal was mostly to get the order of magnitude of the algorithm complexity. +The result are convincing: medium repository get a full cache warming in a +couple of seconds and even very large and branchy repository get a fully warmed +in the order of tens of minutes. We do not observes a complexity explosion +making the algorithm unusable of large repositories. + +A better cache implementation combined with an optimized version of the +algorithm should give much faster performance. Combined with commit-time +computation and exchange over the network, the overall impact of this should be +invisible to the user. + +The stable range is currently successfully used in production for 2 use cases: +* obsolescence markers discovery, +* caching precomputed bundle while serving pulls + +practical data +-------------- + +The evolve repository: + + number of revisions: 4833 + number of heads: 15 + number of merge: 612 ( 12%) + number of range: 4826 + with 2 subranges: 4551 ( 94%) + with 3 subranges: 255 ( 5%) + with 4 subranges: 12 ( 0%) + with 5 subranges: 7 ( 0%) + with 8 subranges: 1 ( 0%) + average range/revs: 0.99 + + Estimated approximative size of a naive compact storage: + 41 056 bytes + Current size of the sqlite cache (for comparison): + 5 312 512 bytes + +The mercurial repository: + + number of revisions: 42849 + number of heads: 2 + number of merge: 2647 ( 6%) + number of range: 41279 + with 2 subranges: 39740 ( 96%) + with 3 subranges: 1494 ( 3%) + with 4 subranges: 39 ( 0%) + with 5 subranges: 5 ( 0%) + with 7 subranges: 1 ( 0%) + average range/revs: 0.96 + Estimated approximative size of a naive compact storage: + 342 968 bytes + Current size of the sqlite cache (for comparison): + 62 803 968 bytes + +The pypy repository (very brancy history): + + number of revisions: 97409 + number of heads: 183 + number of merge: 8371 ( 8%) + number of range: 107025 + with 2 subranges: 100166 ( 93%) + with 3 subranges: 5839 ( 5%) + with 4 subranges: 605 ( 0%) + with 5 subranges: 189 ( 0%) + with 6 subranges: 90 ( 0%) + with 7 subranges: 38 ( 0%) + with 8 subranges: 18 ( 0%) + with 9 subranges: 9 ( 0%) + with 10 subranges: 15 ( 0%) + with 11 subranges: 4 ( 0%) + with 12 subranges: 6 ( 0%) + with 13 subranges: 7 ( 0%) + with 14 subranges: 6 ( 0%) + with 15 subranges: 1 ( 0%) + with 16 subranges: 2 ( 0%) + with 17 subranges: 2 ( 0%) + with 18 subranges: 3 ( 0%) + with 19 subranges: 2 ( 0%) + with 20 subranges: 3 ( 0%) + with 25 subranges: 1 ( 0%) + with 27 subranges: 1 ( 0%) + with 31 subranges: 3 ( 0%) + with 32 subranges: 2 ( 0%) + with 33 subranges: 1 ( 0%) + with 35 subranges: 1 ( 0%) + with 43 subranges: 1 ( 0%) + with 44 subranges: 1 ( 0%) + with 45 subranges: 2 ( 0%) + with 47 subranges: 1 ( 0%) + with 51 subranges: 1 ( 0%) + with 52 subranges: 1 ( 0%) + with 57 subranges: 1 ( 0%) + with 65 subranges: 1 ( 0%) + with 73 subranges: 1 ( 0%) + with 79 subranges: 1 ( 0%) + average range/revs: 1.10 + Estimated approximative size of a naive compact storage: + 934 176 bytes + Current size of the sqlite cache (for comparison): + 201 236 480 bytes + +A private and branchy repository: + + number of revisions: 605011 + number of heads: 14061 + number of merge: 118109 ( 19%) + number of range: 747625 + with 2 subranges: 595985 ( 79%) + with 3 subranges: 130196 ( 17%) + with 4 subranges: 14093 ( 1%) + with 5 subranges: 4090 ( 0%) + with 6 subranges: 741 ( 0%) + with 7 subranges: 826 ( 0%) + with 8 subranges: 1313 ( 0%) + with 9 subranges: 83 ( 0%) + with 10 subranges: 22 ( 0%) + with 11 subranges: 9 ( 0%) + with 12 subranges: 26 ( 0%) + with 13 subranges: 5 ( 0%) + with 14 subranges: 9 ( 0%) + with 15 subranges: 3 ( 0%) + with 16 subranges: 212 ( 0%) + with 18 subranges: 6 ( 0%) + with 19 subranges: 3 ( 0%) + with 24 subranges: 1 ( 0%) + with 27 subranges: 1 ( 0%) + with 32 subranges: 1 ( 0%) + average range/revs: 1.23 + Estimated approximative size of a naive compact storage: + 7 501 928 bytes + Current size of the sqlite cache (for comparison): + 1 950 310 400 bytes +""" import abc import functools @@ -64,11 +410,11 @@ return ranges _stablerangemethodmap = { - 'branchpoint': lambda repo: stablerange(), - 'default': lambda repo: repo.stablerange, - 'basic-branchpoint': lambda repo: stablerangebasic(), - 'basic-mergepoint': lambda repo: stablerangedummy_mergepoint(), - 'mergepoint': lambda repo: stablerange_mergepoint(), + b'branchpoint': lambda repo: stablerange(), + b'default': lambda repo: repo.stablerange, + b'basic-branchpoint': lambda repo: stablerangebasic(), + b'basic-mergepoint': lambda repo: stablerangedummy_mergepoint(), + b'mergepoint': lambda repo: stablerange_mergepoint(), } @eh.command( @@ -90,9 +436,9 @@ short = nodemod.short revs = scmutil.revrange(repo, opts['rev']) if not revs: - raise error.Abort('no revisions specified') + raise error.Abort(b'no revisions specified') if ui.verbose: - template = '%s-%d (%d, %d, %d)' + template = b'%s-%d (%d, %d, %d)' def _rangestring(repo, rangeid): return template % ( @@ -103,7 +449,7 @@ length(unfi, rangeid) ) else: - template = '%s-%d' + template = b'%s-%d' def _rangestring(repo, rangeid): return template % ( @@ -117,7 +463,7 @@ method = opts['method'] getstablerange = _stablerangemethodmap.get(method) if getstablerange is None: - raise error.Abort('unknown stable sort method: "%s"' % method) + raise error.Abort(b'unknown stable sort method: "%s"' % method) stablerange = getstablerange(unfi) depth = stablerange.depthrev @@ -132,21 +478,21 @@ for r in ranges: subs = subranges(unfi, r) - subsstr = ', '.join(_rangestring(unfi, s) for s in subs) + subsstr = b', '.join(_rangestring(unfi, s) for s in subs) rstr = _rangestring(unfi, r) if opts['verify']: - status = 'leaf' + status = b'leaf' if 1 < length(unfi, r): - status = 'complete' + status = b'complete' revs = set(stablerange.revsfromrange(unfi, r)) subrevs = set() for s in subs: subrevs.update(stablerange.revsfromrange(unfi, s)) if revs != subrevs: - status = 'missing' - ui.status('%s [%s] - %s\n' % (rstr, status, subsstr)) + status = b'missing' + ui.status(b'%s [%s] - %s\n' % (rstr, status, subsstr)) else: - ui.status('%s - %s\n' % (rstr, subsstr)) + ui.status(b'%s - %s\n' % (rstr, subsstr)) class abstractstablerange(object): """The official API for a stablerange""" @@ -214,7 +560,7 @@ def depthrev(self, repo, rev): """depth a revision""" - return len(repo.revs('::%d', rev)) + return len(repo.revs(b'::%d', rev)) def revsfromrange(self, repo, rangeid): """return revision contained in a range @@ -620,12 +966,12 @@ rangeheap = [] for idx, r in enumerate(revs): if not idx % 1000: - compat.progress(ui, _("filling depth cache"), idx, total=nbrevs, - unit=_("changesets")) + compat.progress(ui, _(b"filling depth cache"), idx, total=nbrevs, + unit=_(b"changesets")) # warm up depth self.depthrev(repo, r) rangeheap.append((-r, (r, 0))) - compat.progress(ui, _("filling depth cache"), None, total=nbrevs) + compat.progress(ui, _(b"filling depth cache"), None, total=nbrevs) heappop = heapq.heappop heappush = heapq.heappush @@ -646,8 +992,8 @@ progress_new = time.time() if (1 < progress_each) and (0.1 < progress_new - progress_last): progress_each /= 10 - compat.progress(ui, _("filling stablerange cache"), seen, - total=nbrevs, unit=_("changesets")) + compat.progress(ui, _(b"filling stablerange cache"), seen, + total=nbrevs, unit=_(b"changesets")) progress_last = progress_new seen += 1 original.remove(value) # might have been added from other source @@ -656,13 +1002,13 @@ for sub in self.subranges(repo, rangeid): if self._getsub(sub) is None: heappush(rangeheap, (-sub[0], sub)) - compat.progress(ui, _("filling stablerange cache"), None, total=nbrevs) + compat.progress(ui, _(b"filling stablerange cache"), None, total=nbrevs) self._tiprev = upto self._tipnode = cl.node(upto) duration = util.timer() - starttime - repo.ui.log('evoext-cache', 'updated stablerange cache in %.4f seconds\n', + repo.ui.log(b'evoext-cache', b'updated stablerange cache in %.4f seconds\n', duration) def subranges(self, repo, rangeid):
--- a/hgext3rd/evolve/stablerangecache.py Mon Jul 29 14:43:15 2019 +0200 +++ b/hgext3rd/evolve/stablerangecache.py Fri Sep 27 13:03:18 2019 +0200 @@ -14,6 +14,7 @@ import time from mercurial import ( + commands, encoding, error, localrepo, @@ -35,7 +36,7 @@ LONG_WARNING_TIME = 60 -LONG_MESSAGE = """Stable range cache is taking a while to load +LONG_MESSAGE = b"""Stable range cache is taking a while to load Your repository is probably big. @@ -99,8 +100,8 @@ warned_long = True if (1 < progress_each) and (0.1 < progress_new - progress_last): progress_each /= 10 - compat.progress(ui, _("filling stablerange cache"), seen, - total=total, unit=_("changesets")) + compat.progress(ui, _(b"filling stablerange cache"), seen, + total=total, unit=_(b"changesets")) progress_last = progress_new seen += 1 original.remove(rangeid) # might have been added from other source @@ -109,7 +110,7 @@ for sub in self.subranges(repo, rangeid): if self._getsub(sub) is None: heappush(rangeheap, sub) - compat.progress(ui, _("filling stablerange cache"), None, total=total) + compat.progress(ui, _(b"filling stablerange cache"), None, total=total) def clear(self, reset=False): super(stablerangeondiskbase, self).clear() @@ -120,10 +121,10 @@ ############################# _sqliteschema = [ - """CREATE TABLE range(rev INTEGER NOT NULL, + r"""CREATE TABLE range(rev INTEGER NOT NULL, idx INTEGER NOT NULL, PRIMARY KEY(rev, idx));""", - """CREATE TABLE subranges(listidx INTEGER NOT NULL, + r"""CREATE TABLE subranges(listidx INTEGER NOT NULL, suprev INTEGER NOT NULL, supidx INTEGER NOT NULL, subrev INTEGER NOT NULL, @@ -132,37 +133,37 @@ FOREIGN KEY (suprev, supidx) REFERENCES range(rev, idx), FOREIGN KEY (subrev, subidx) REFERENCES range(rev, idx) );""", - "CREATE INDEX subranges_index ON subranges (suprev, supidx);", - "CREATE INDEX superranges_index ON subranges (subrev, subidx);", - "CREATE INDEX range_index ON range (rev, idx);", - """CREATE TABLE meta(schemaversion INTEGER NOT NULL, + r"CREATE INDEX subranges_index ON subranges (suprev, supidx);", + r"CREATE INDEX superranges_index ON subranges (subrev, subidx);", + r"CREATE INDEX range_index ON range (rev, idx);", + r"""CREATE TABLE meta(schemaversion INTEGER NOT NULL, tiprev INTEGER NOT NULL, tipnode BLOB NOT NULL );""", ] -_newmeta = "INSERT INTO meta (schemaversion, tiprev, tipnode) VALUES (?,?,?);" -_updatemeta = "UPDATE meta SET tiprev = ?, tipnode = ?;" -_updaterange = "INSERT INTO range(rev, idx) VALUES (?,?);" -_updatesubranges = """INSERT +_newmeta = r"INSERT INTO meta (schemaversion, tiprev, tipnode) VALUES (?,?,?);" +_updatemeta = r"UPDATE meta SET tiprev = ?, tipnode = ?;" +_updaterange = r"INSERT INTO range(rev, idx) VALUES (?,?);" +_updatesubranges = r"""INSERT INTO subranges(listidx, suprev, supidx, subrev, subidx) VALUES (?,?,?,?,?);""" -_queryexist = "SELECT name FROM sqlite_master WHERE type='table' AND name='meta';" -_querymeta = "SELECT schemaversion, tiprev, tipnode FROM meta;" -_queryrange = "SELECT * FROM range WHERE (rev = ? AND idx = ?);" -_querysubranges = """SELECT subrev, subidx +_queryexist = r"SELECT name FROM sqlite_master WHERE type='table' AND name='meta';" +_querymeta = r"SELECT schemaversion, tiprev, tipnode FROM meta;" +_queryrange = r"SELECT * FROM range WHERE (rev = ? AND idx = ?);" +_querysubranges = r"""SELECT subrev, subidx FROM subranges WHERE (suprev = ? AND supidx = ?) ORDER BY listidx;""" -_querysuperrangesmain = """SELECT DISTINCT suprev, supidx +_querysuperrangesmain = r"""SELECT DISTINCT suprev, supidx FROM subranges WHERE %s;""" -_querysuperrangesbody = '(subrev = %d and subidx = %d)' +_querysuperrangesbody = r'(subrev = %d and subidx = %d)' def _make_querysuperranges(ranges): # building a tree of OR would allow for more ranges - body = ' OR '.join(_querysuperrangesbody % r for r in ranges) + body = r' OR '.join(_querysuperrangesbody % r for r in ranges) return _querysuperrangesmain % body class stablerangesqlbase(stablerange.stablerangecached): @@ -223,7 +224,7 @@ except (sqlite3.DatabaseError, sqlite3.OperationalError): # something is wrong with the sqlite db # Since this is a cache, we ignore it. - if '_con' in vars(self): + if r'_con' in vars(self): del self._con self._unsavedsubranges.clear() @@ -240,7 +241,7 @@ except OSError: return None con = sqlite3.connect(encoding.strfromlocal(self._path), timeout=30, - isolation_level="IMMEDIATE") + isolation_level=r"IMMEDIATE") con.text_factory = bytes return con @@ -278,11 +279,11 @@ # # operational error catch read-only and locked database # IntegrityError catch Unique constraint error that may arise - if '_con' in vars(self): + if r'_con' in vars(self): del self._con self._unsavedsubranges.clear() - repo.ui.log('evoext-cache', 'error while saving new data: %s' % exc) - repo.ui.debug('evoext-cache: error while saving new data: %s' % exc) + repo.ui.log(b'evoext-cache', b'error while saving new data: %s' % exc) + repo.ui.debug(b'evoext-cache: error while saving new data: %s' % exc) def _trysave(self, repo): repo = repo.unfiltered() @@ -294,7 +295,7 @@ if self._con is None: util.unlinkpath(self._path, ignoremissing=True) - if '_con' in vars(self): + if r'_con' in vars(self): del self._con con = self._db() @@ -319,9 +320,9 @@ # drifting is currently an issue because this means another # process might have already added the cache line we are about # to add. This will confuse sqlite - msg = _('stable-range cache: skipping write, ' - 'database drifted under my feet\n') - hint = _('(disk: %s-%s vs mem: %s-%s)\n') + msg = _(b'stable-range cache: skipping write, ' + b'database drifted under my feet\n') + hint = _(b'(disk: %s-%s vs mem: %s-%s)\n') data = (nodemod.hex(meta[2]), meta[1], nodemod.hex(self._ondisktipnode), self._ondisktiprev) repo.ui.warn(msg) @@ -375,7 +376,7 @@ def clear(self, reset=False): super(stablerangesql, self).clear(reset=reset) - if '_con' in vars(self): + if r'_con' in vars(self): del self._con self._subrangescache.clear() @@ -396,13 +397,13 @@ class mergepointsql(stablerangesql, stablerange.stablerange_mergepoint): _schemaversion = 3 - _cachefile = 'evoext_stablerange_v2.sqlite' - _cachename = 'evo-ext-stablerange-mergepoint' + _cachefile = b'evoext_stablerange_v2.sqlite' + _cachename = b'evo-ext-stablerange-mergepoint' class sqlstablerange(stablerangesqlbase, stablerange.stablerange): _schemaversion = 1 - _cachefile = 'evoext_stablerange_v1.sqlite' + _cachefile = b'evoext_stablerange_v1.sqlite' def warmup(self, repo, upto=None): self._con # make sure the data base is loaded @@ -419,10 +420,81 @@ except error.LockError: # Exceptionnally we are noisy about it since performance impact is # large We should address that before using this more widely. - repo.ui.warn('stable-range cache: unable to lock repo while warming\n') - repo.ui.warn('(cache will not be saved)\n') + repo.ui.warn(b'stable-range cache: unable to lock repo while warming\n') + repo.ui.warn(b'(cache will not be saved)\n') super(sqlstablerange, self).warmup(repo, upto) +@eh.command( + b'debugstablerangecache', + [] + commands.formatteropts, + _(b'')) +def debugstablerangecache(ui, repo, **opts): + """display data about the stable sort cache of a repository + """ + unfi = repo.unfiltered() + revs = unfi.revs('all()') + nbrevs = len(revs) + ui.write('number of revisions: %12d\n' % nbrevs) + heads = unfi.revs('heads(all())') + nbheads = len(heads) + ui.write('number of heads: %12d\n' % nbheads) + merge = unfi.revs('merge()') + nbmerge = len(merge) + ui.write('number of merge: %12d (%3d%%)\n' + % (nbmerge, 100 * nbmerge / nbrevs)) + cache = unfi.stablerange + allsubranges = stablerange.subrangesclosure(unfi, cache, heads) + nbsubranges = len(allsubranges) - nbrevs # we remove leafs + ui.write('number of range: %12d\n' % nbsubranges) + import collections + subsizedistrib = collections.defaultdict(lambda: 0) + + def smallsize(r): + # This is computing the size it would take to store a range for a + # revision + # + # one int for the initial/top skip + # two int per middle ranges + # one int for the revision of the bottom part + return 4 * (2 + ((len(r) - 2) * 2)) + + totalsize = 0 + + allmiddleranges = [] + for s in allsubranges: + sr = cache.subranges(repo, s) + srl = len(sr) + if srl == 0: + # leaf range are not interresting + continue + subsizedistrib[srl] += 1 + allmiddleranges.append(s) + totalsize += smallsize(sr) + + for ss in sorted(subsizedistrib): + ssc = subsizedistrib[ss] + ssp = ssc * 100 // nbsubranges + ui.write(' with %3d subranges: %12d (%3d%%)\n' % (ss, ssc, ssp)) + + depth = repo.depthcache.get + stdslice = 0 + oddslice = 0 + + for s in allmiddleranges: + head, skip = s + d = depth(head) + k = d - 1 + if (skip & k) == skip: + stdslice += 1 + else: + oddslice += 1 + + ui.write('standard slice point cut: %12d (%3d%%)\n' + % (stdslice, stdslice * 100 // nbsubranges)) + ui.write('other slice point cut: %12d (%3d%%)\n' + % (oddslice, oddslice * 100 // nbsubranges)) + ui.write('est. naive compact store: %12d bytes\n' % totalsize) + @eh.reposetup def setupcache(ui, repo): @@ -436,7 +508,7 @@ @localrepo.unfilteredmethod def destroyed(self): - if 'stablerange' in vars(self): + if r'stablerange' in vars(self): self.stablerange.clear() del self.stablerange super(stablerangerepo, self).destroyed()
--- a/hgext3rd/evolve/stablesort.py Mon Jul 29 14:43:15 2019 +0200 +++ b/hgext3rd/evolve/stablesort.py Fri Sep 27 13:03:18 2019 +0200 @@ -7,6 +7,245 @@ # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. +r"""Stable sorting for the mercurial graph + +The goal is to provided an efficient, revnum independant way, to sort revisions +in a topologicaly. Having it independant from revnum is important to make it +stable from one repository to another, unlocking various capabilities. For +example it can be used for discovery purposes. + +This docstring describe the currently preferred solution: + +Probleme definition +------------------- + +We want a way to order revision in the graph. For a linear history things are simple:: + + A -> B -> C -> D -> E -> F -> G -> H + + stablesort(::H) = [A, B, C, D, E, F, G, H] + +However, things become more complicated when the graph is not linear:: + + A -> B -> C -> D -> G -> H + \ / + > E -> F + + stablesort(::A) = [A] + stablesort(::B) = [A, B] + stablesort(::C) = [A, B, C] + stablesort(::D) = [A, B, C, D] + stablesort(::E) = [A, B, E] + stablesort(::F) = [A, B, E, F] + stablesort(::G) = [A, B, C, D, E, F, G] + stablesort(::H) = [A, B, C, D, E, F, G, H] + +Basic principle: +---------------- + +We are always talking about set of revision defined by a single heads +(eg: `stablesort(::r)`) + +For non merge revisions, the definition is simple:: + + stablesort(::r) == stablesort(p1(r)) + r + +This is visible in some of the example above: + + stablesort(::B) = stablesort(::A) + [B] + stablesort(::E) = stablesort(::B) + [E] + stablesort(::H) = stablesort(::G) + [H] + +For merge revision, we reuse as much as possible of the parents order: + + pl = stablemin(parents(m)) + ph = stablemax(parents(m)) + stablesort(::m) == stablesort(pl) + + [i for in in stablesort(ph) if in ph % pl] + + m + +This is visible in the example above: + + stablesort(::G) = stablesort(::D) + [stablesort(::F) - ::D] + [G] + stablesort(::G) = [A, B, C, D] + ([A, B, E, F] - [A, B, C ,D]) + [G] + stablesort(::G) = [A, B, C, D] + [E, F] + [G] + +To decide which parent goes first in the stablesort, we need to order them. The +`stablemin/stablemax` function express this. The actual order used is an +implementation details (eg: could be node-order, in the example it is +alphabetical order) + +The `ph % pl` set of revision is called the "exclusive part". It correspond to +all revisions ancestors of `ph` (`ph` included) that are not ancestors of `pl` +(`pl` included). In this area we try to reuse as much as the stable-sorted +order for `ph`. In simple case, the `[i for i in stablesort(ph) if i in ph % +pl]` is just the contiguous final range of `stablesort(ph)`. This is the case +in the example we have looked at so far:: + + stablesort(::F) - ::D = [A, B, E, F] - [A, B, C ,D] = stablesort(::F)[-2:] + + +However in more advance case, this will not be contiguous and we'll need to +skip over multiple parts of `stablesort(ph)` to cover `ph % pl`.Let's have a +look at an example of such case:: + + A - B ----- F - H + \ \ / / + \ E - G + \ / / + C - D + +We have the following stablesort: + + stablesort(::A) = [A] + stablesort(::B) = [A, B] + stablesort(::C) = [A, C] + stablesort(::D) = [A, C, D] + stablesort(::E) = [A, B, C, E] + stablesort(::F) = [A, B, C, E, F] + stablesort(::G) = [A, B, C, D, E, G] + stablesort(::H) = [A, B, C, E, F, D, G, H] + +The stable order of `stablesort(::H)` match our definition:: + + + stablesort(::H) = [A, B, C, E, F] + [D, G] + [H] + stablesort(::F) = [A, B, C, E, F] + stablesort(::G) - ::F = [A, B, C, D, E, G] - [A, B, C, E, F] = [D, G] + +In this order, we reuse all of `stablesort(::H)`, but the subset of +`stablesort(::G)` we reuse is not contiguous, we had to skip over 'E' that is +already contained inside ::F. + +Usage +----- + +An important details is that, in practice, the sorted revision are always +walked backward, from the head of the set of revisions. + +preexisting cached data +----------------------- + +The stable sort assume we already have 2 important property cached for each +changesets: + +1) changeset depth == len(::r) +2) first merge == max(merge() and ::r) + +Caching strategy +---------------- + +Since we always walk from the head, the iteration mostly have to follow the +unique parent of non merge revision. For merge revision, we need to iterate +over the revisions accessible only through one of the parent before coming back +to the other parent eventually. + +To efficiently cache the revision path we need to walk, we records "jumps". A +jump is a revision where the next revision (in the stable sort) will not be a +parent of the current revision, but another revision in the graph. + +In the first (simple) example above, we had:: + + A -> B -> C -> D -> G -> H + \ / + > E -> F + + stablesort(::D) = [A, B, C, D] + stablesort(::F) = [A, B, E, F] + stablesort(::G) = [A, B, C, D, E, F, G] + +In this case, when caching stable sort data for `G`, we need to record the `E +-> D` jump. This correspond to point were we are done iterating over the +revision accessible through `F` and we need to "jump back to the other parent" +of `G`: `D`. + + + +In the second (more advance) example above, we had:: + + A - B ----- F - H + \ \ / / + \ E - G + \ / / + C - D + + stablesort(::F) = [A, B, C, E, F] + stablesort(::G) = [A, B, C, D, E, G] + stablesort(::H) = [A, B, C, E, F, D, G, H] + +In this case, when caching stable sort data for `G`, we need to record the `G +-> D` and the `D -> F` jumps. + +Jumps are recorded using the following formats: + + (jump-point, jump-destination, section-size) + +* jump-point is the last revision number we should iterate over before jumping, +* jump-destination is the next revision we should iterate over after the jump point, +* section-size is the number of revision to be iterated before reaching jump-point. + +the section-size is not directly used when doing a stable-sorted walk. However +it is useful for higher level piece of code to take decision without having to +actually walk the graph, (see stable range documentation). + +For each merge, we store the set of jumps that cover the exclusive side. + +Practical data +-------------- + +The mercurial repository has simple branching and few jumps: + + number of revisions: 69771 + number of merge: 2734 + number of jumps: 2950 + average jumps: 1.079 + median jumps: 1 + 90% jumps: 1 + 99% jumps: 3 + max jumps: 6 + jump cache size: 35 400 bytes + +Mozilla's branching is fairly simple too: + + number of revisions: 435078 + number of merge: 21035 + number of jumps: 31434 + average jumps: 1.494 + median jumps: 1 + 90% jumps: 2 + 99% jumps: 9 + max jumps: 169 + jump cache size: 377 208 bytes + +Pypy has a more complicated branching history but jumps cache remains reasonable + + number of revisions: 95010 + number of merge: 7911 + number of jumps: 24326 + average jumps: 3.075 + median jumps: 1 + 90% jumps: 5 + 99% jumps: 40 + max jumps: 329 + jump cache size: 291 912 bytes + +This still apply to larger private project: + + number of revisions: 605011 + number of merge: 118109 + number of jumps: 314925 + average jumps: 2.667 + median jumps: 1 + 90% jumps: 3 + 99% jumps: 34 + max jumps: 660 + jump cache size: 3 779 100 bytes + +It is worth noting that the last jump could be computed form other information, +removing one jump storage per merge. However this does not seems to be an issue +worth the troubles for now. +""" + import array import collections import struct @@ -69,9 +308,9 @@ method = opts['method'] sorting = _methodmap.get(method) if sorting is None: - valid_method = ', '.join(sorted(_methodmap)) - raise error.Abort('unknown sorting method: "%s"' % method, - hint='pick one of: %s' % valid_method) + valid_method = b', '.join(sorted(_methodmap)) + raise error.Abort(b'unknown sorting method: "%s"' % method, + hint=b'pick one of: %s' % valid_method) displayer = compat.changesetdisplayer(ui, repo, pycompat.byteskwargs(opts), buffered=True) @@ -84,6 +323,46 @@ displayer.flush(ctx) displayer.close() +@eh.command( + b'debugstablesortcache', + [] + commands.formatteropts, + _(b'')) +def debugstablesortcache(ui, repo, **opts): + """display data about the stable sort cache of a repository + """ + unfi = repo.unfiltered() + revs = unfi.revs('all()') + nbrevs = len(revs) + ui.write('number of revisions: %12d\n' % nbrevs) + merge = unfi.revs('merge()') + nbmerge = len(merge) + cache = unfi.stablesort + ui.write('number of merge: %12d\n' % nbmerge) + alljumps = [] + alljumpssize = [] + for r in merge: + jumps = cache.getjumps(unfi, r) + if jumps is None: + continue # not a merge + jumps = list(jumps) + alljumps.append(jumps) + alljumpssize.append(len(jumps)) + nbjumps = sum(alljumpssize) + ui.write('number of jumps: %12d\n' % nbjumps) + if not nbjumps: + return 0 + avgjumps = nbjumps / float(len(alljumpssize)) + ui.write('average jumps: %6.3f\n' % avgjumps) + alljumpssize.sort() + medianjumps = alljumpssize[len(alljumpssize) // 2] + ui.write('median jumps: %12d\n' % medianjumps) + tensjumps = alljumpssize[len(alljumpssize) * 9 // 10] + ui.write('90%% jumps: %12d\n' % tensjumps) + centsjumps = alljumpssize[len(alljumpssize) * 99 // 100] + ui.write('99%% jumps: %12d\n' % centsjumps) + ui.write('max jumps: %12d\n' % max(alljumpssize)) + ui.write('jump cache size: %12d bytes\n' % (nbjumps * 12)) + def stablesort_branchpoint(repo, revs, mergecallback=None): """return '::revs' topologically sorted in "stable" order @@ -180,7 +459,7 @@ heads = list(sorted(revs)) else: # keeps heads only - heads = sorted(repo.revs('sort(heads(%ld::%ld))', revs, revs), key=tiebreaker) + heads = sorted(repo.revs(b'sort(heads(%ld::%ld))', revs, revs), key=tiebreaker) results = [] while heads: @@ -246,24 +525,24 @@ return result def stablesort_mergepoint_head_basic(repo, revs, limit=None): - heads = repo.revs('sort(heads(%ld))', revs) + heads = repo.revs(b'sort(heads(%ld))', revs) if not heads: return [] elif 2 < len(heads): - raise error.Abort('cannot use head based merging, %d heads found' + raise error.Abort(b'cannot use head based merging, %d heads found' % len(heads)) head = heads.first() - revs = stablesort_mergepoint_bounded(repo, head, repo.revs('::%d', head)) + revs = stablesort_mergepoint_bounded(repo, head, repo.revs(b'::%d', head)) if limit is None: return revs return revs[-limit:] def stablesort_mergepoint_head_debug(repo, revs, limit=None): - heads = repo.revs('sort(heads(%ld))', revs) + heads = repo.revs(b'sort(heads(%ld))', revs) if not heads: return [] elif 2 < len(heads): - raise error.Abort('cannot use head based merging, %d heads found' + raise error.Abort(b'cannot use head based merging, %d heads found' % len(heads)) head = heads.first() revs = stablesort_mergepoint_head(repo, head) @@ -294,7 +573,7 @@ ps = sorted(ps, key=tiebreaker) # get the part from the highest parent. This is the part that changes - mid_revs = repo.revs('only(%d, %d)', ps[1], ps[0]) + mid_revs = repo.revs(b'only(%d, %d)', ps[1], ps[0]) if mid_revs: mid = stablesort_mergepoint_bounded(repo, ps[1], mid_revs) @@ -304,20 +583,20 @@ return bottom + mid + top def stablesort_mergepoint_head_cached(repo, revs, limit=None): - heads = repo.revs('sort(heads(%ld))', revs) + heads = repo.revs(b'sort(heads(%ld))', revs) if not heads: return [] elif 2 < len(heads): - raise error.Abort('cannot use head based merging, %d heads found' + raise error.Abort(b'cannot use head based merging, %d heads found' % len(heads)) head = heads.first() cache = stablesortcache() first = list(cache.get(repo, head, limit=limit)) second = list(cache.get(repo, head, limit=limit)) if first != second: - repo.ui.warn('stablesort-cache: initial run different from re-run:\n' - ' %s\n' - ' %s\n' % (first, second)) + repo.ui.warn(b'stablesort-cache: initial run different from re-run:\n' + b' %s\n' + b' %s\n' % (first, second)) return second class stablesortcache(object): @@ -504,11 +783,11 @@ recordjump(previous, lower, size) def stablesort_mergepoint_head_ondisk(repo, revs, limit=None): - heads = repo.revs('sort(heads(%ld))', revs) + heads = repo.revs(b'sort(heads(%ld))', revs) if not heads: return [] elif 2 < len(heads): - raise error.Abort('cannot use head based merging, %d heads found' + raise error.Abort(b'cannot use head based merging, %d heads found' % len(heads)) head = heads.first() unfi = repo.unfiltered() @@ -516,22 +795,22 @@ cache.save(unfi) return cache.get(repo, head, limit=limit) -S_INDEXSIZE = struct.Struct('>I') +S_INDEXSIZE = struct.Struct(b'>I') class ondiskstablesortcache(stablesortcache, genericcaches.changelogsourcebase): - _filepath = 'evoext-stablesortcache-00' - _cachename = 'evo-ext-stablesort' + _filepath = b'evoext-stablesortcache-00' + _cachename = b'evo-ext-stablesort' def __init__(self): super(ondiskstablesortcache, self).__init__() - self._index = array.array('l') - self._data = array.array('l') + self._index = array.array(r'l') + self._data = array.array(r'l') del self._jumps def getjumps(self, repo, rev): if len(self._index) < rev: - msg = 'stablesortcache must be warmed before use (%d < %d)' + msg = b'stablesortcache must be warmed before use (%d < %d)' msg %= (len(self._index), rev) raise error.ProgrammingError(msg) return self._getjumps(rev) @@ -579,10 +858,11 @@ total = len(data) def progress(pos, rev=None): - revstr = '' if rev is None else ('rev %d' % rev) - compat.progress(repo.ui, 'updating stablesort cache', - pos, revstr, unit='revision', total=total) + revstr = b'' if rev is None else (b'rev %d' % rev) + compat.progress(repo.ui, b'updating stablesort cache', + pos, revstr, unit=b'revision', total=total) + progress(0) for idx, rev in enumerate(data): parents = filterparents(repo.changelog.parentrevs(rev)) if len(parents) <= 1: @@ -600,8 +880,8 @@ def clear(self, reset=False): super(ondiskstablesortcache, self).clear() - self._index = array.array('l') - self._data = array.array('l') + self._index = array.array(r'l') + self._data = array.array(r'l') def load(self, repo): """load data from disk @@ -611,8 +891,8 @@ assert repo.filtername is None data = repo.cachevfs.tryread(self._filepath) - self._index = array.array('l') - self._data = array.array('l') + self._index = array.array(r'l') + self._data = array.array(r'l') if not data: self._cachekey = self.emptykey else: @@ -636,7 +916,7 @@ if self._cachekey is None or self._cachekey == self._ondiskkey: return try: - cachefile = repo.cachevfs(self._filepath, 'w', atomictemp=True) + cachefile = repo.cachevfs(self._filepath, b'w', atomictemp=True) # data to write headerdata = self._serializecachekey() @@ -652,8 +932,8 @@ cachefile.close() self._ondiskkey = self._cachekey except (IOError, OSError) as exc: - repo.ui.log('stablesortcache', 'could not write update %s\n' % exc) - repo.ui.debug('stablesortcache: could not write update %s\n' % exc) + repo.ui.log(b'stablesortcache', b'could not write update %s\n' % exc) + repo.ui.debug(b'stablesortcache: could not write update %s\n' % exc) @eh.reposetup def setupcache(ui, repo): @@ -668,7 +948,7 @@ @localrepo.unfilteredmethod def destroyed(self): - if 'stablesort' in vars(self): + if r'stablesort' in vars(self): self.stablesort.clear() super(stablesortrepo, self).destroyed() @@ -682,12 +962,12 @@ repo.__class__ = stablesortrepo _methodmap = { - 'branchpoint': stablesort_branchpoint, - 'basic-mergepoint': stablesort_mergepoint_multirevs, - 'basic-headstart': stablesort_mergepoint_head_basic, - 'headstart': stablesort_mergepoint_head_debug, - 'headcached': stablesort_mergepoint_head_cached, - 'headondisk': stablesort_mergepoint_head_ondisk, + b'branchpoint': stablesort_branchpoint, + b'basic-mergepoint': stablesort_mergepoint_multirevs, + b'basic-headstart': stablesort_mergepoint_head_basic, + b'headstart': stablesort_mergepoint_head_debug, + b'headcached': stablesort_mergepoint_head_cached, + b'headondisk': stablesort_mergepoint_head_ondisk, } # merge last so that repo setup wrap after that one.
--- a/hgext3rd/evolve/state.py Mon Jul 29 14:43:15 2019 +0200 +++ b/hgext3rd/evolve/state.py Fri Sep 27 13:03:18 2019 +0200 @@ -15,6 +15,7 @@ from __future__ import absolute_import +import contextlib import errno import struct @@ -27,7 +28,7 @@ from mercurial.i18n import _ -class cmdstate(): +class cmdstate(object): """a wrapper class to store the state of commands like `evolve`, `pick` All the data for the state is stored in the form of key-value pairs in a @@ -37,9 +38,11 @@ can populate the object data reading that file """ - def __init__(self, repo, path='evolvestate', opts={}): + def __init__(self, repo, path=b'evolvestate', opts=None): self._repo = repo self.path = path + if opts is None: + opts = {} self.opts = opts def __nonzero__(self): @@ -65,7 +68,7 @@ op = self._read() if isinstance(op, dict): self.opts.update(op) - elif self.path == 'evolvestate': + elif self.path == b'evolvestate': # it is the old evolvestate file oldop = _oldevolvestateread(self._repo) self.opts.update(oldop) @@ -79,13 +82,13 @@ we use third-party library cbor to serialize data to write in the file. """ - with self._repo.vfs(self.path, 'wb', atomictemp=True) as fp: + with self._repo.vfs(self.path, b'wb', atomictemp=True) as fp: cbor.dump(self.opts, fp) def _read(self): """reads the evolvestate file and returns a dictionary which contain data in the same format as it was before storing""" - with self._repo.vfs(self.path, 'rb') as fp: + with self._repo.vfs(self.path, b'rb') as fp: return cbor.load(fp) def delete(self): @@ -101,20 +104,20 @@ This exists for BC reasons.""" try: - f = repo.vfs('evolvestate') + f = repo.vfs(b'evolvestate') except IOError as err: if err.errno != errno.ENOENT: raise try: versionblob = f.read(4) if len(versionblob) < 4: - repo.ui.debug('ignoring corrupted evolvestate (file contains %i bits)' + repo.ui.debug(b'ignoring corrupted evolvestate (file contains %i bits)' % len(versionblob)) return None - version = struct._unpack('>I', versionblob)[0] + version = struct._unpack(b'>I', versionblob)[0] if version != 0: - msg = _('unknown evolvestate version %i') % version - raise error.Abort(msg, hint=_('upgrade your evolve')) + msg = _(b'unknown evolvestate version %i') % version + raise error.Abort(msg, hint=_(b'upgrade your evolve')) records = [] data = f.read() off = 0 @@ -122,22 +125,35 @@ while off < end: rtype = data[off] off += 1 - length = struct._unpack('>I', data[off:(off + 4)])[0] + length = struct._unpack(b'>I', data[off:(off + 4)])[0] off += 4 record = data[off:(off + length)] off += length - if rtype == 't': + if rtype == b't': rtype, record = record[0], record[1:] records.append((rtype, record)) state = {} for rtype, rdata in records: - if rtype == 'C': - state['current'] = rdata + if rtype == b'C': + state[b'current'] = rdata elif rtype.lower(): - repo.ui.debug('ignore evolve state record type %s' % rtype) + repo.ui.debug(b'ignore evolve state record type %s' % rtype) else: - raise error.Abort(_("unknown evolvestate field type '%s'") - % rtype, hint=_('upgrade your evolve')) + raise error.Abort(_(b"unknown evolvestate field type '%s'") + % rtype, hint=_(b'upgrade your evolve')) return state finally: f.close() + +@contextlib.contextmanager +def saver(state, opts=None): + """ensure the state is saved on disk during the duration of the context + + The state is preserved if the context is exited through an exception. + """ + if opts: + state.addopts(opts) + state.save() + yield + # delete only if no exception where raised + state.delete()
--- a/hgext3rd/evolve/templatekw.py Mon Jul 29 14:43:15 2019 +0200 +++ b/hgext3rd/evolve/templatekw.py Fri Sep 27 13:03:18 2019 +0200 @@ -28,31 +28,31 @@ def showinstabilities(context, mapping): """List of strings. Evolution instabilities affecting the changeset (zero or more of "orphan", "content-divergent" or "phase-divergent").""" - ctx = context.resource(mapping, 'ctx') - return templatekw.compatlist(context, mapping, 'instability', + ctx = context.resource(mapping, b'ctx') + return templatekw.compatlist(context, mapping, b'instability', ctx.instabilities(), - plural='instabilities') + plural=b'instabilities') @eh.templatekeyword(b'troubles', requires=set([b'ctx', b'templ'])) def showtroubles(context, mapping): # legacy name for instabilities - ctx = context.resource(mapping, 'ctx') - return templatekw.compatlist(context, mapping, 'trouble', - ctx.instabilities(), plural='troubles') + ctx = context.resource(mapping, b'ctx') + return templatekw.compatlist(context, mapping, b'trouble', + ctx.instabilities(), plural=b'troubles') else: # older template API in hg < 4.6 @eh.templatekeyword(b'instabilities') def showinstabilities(**args): """List of strings. Evolution instabilities affecting the changeset (zero or more of "orphan", "content-divergent" or "phase-divergent").""" - ctx = args['ctx'] - return templatekw.showlist('instability', ctx.instabilities(), args, - plural='instabilities') + ctx = args[b'ctx'] + return templatekw.showlist(b'instability', ctx.instabilities(), args, + plural=b'instabilities') @eh.templatekeyword(b'troubles') def showtroubles(**args): - ctx = args['ctx'] - return templatekw.showlist('trouble', ctx.instabilities(), args, - plural='troubles') + ctx = args[b'ctx'] + return templatekw.showlist(b'trouble', ctx.instabilities(), args, + plural=b'troubles') _sp = templatekw.showpredecessors if util.safehasattr(_sp, '_requires'): @@ -91,24 +91,24 @@ """ Returns a dict with the default templates for obs fate """ # Prepare templates - verbtempl = '{verb}' - usertempl = '{if(users, " by {join(users, ", ")}")}' - succtempl = '{if(successors, " as ")}{successors}' # Bypass if limitation - datetempleq = ' (at {min_date|isodate})' - datetemplnoteq = ' (between {min_date|isodate} and {max_date|isodate})' - datetempl = '{if(max_date, "{ifeq(min_date, max_date, "%s", "%s")}")}' % (datetempleq, datetemplnoteq) + verbtempl = b'{verb}' + usertempl = b'{if(users, " by {join(users, ", ")}")}' + succtempl = b'{if(successors, " as ")}{successors}' # Bypass if limitation + datetempleq = b' (at {min_date|isodate})' + datetemplnoteq = b' (between {min_date|isodate} and {max_date|isodate})' + datetempl = b'{if(max_date, "{ifeq(min_date, max_date, "%s", "%s")}")}' % (datetempleq, datetemplnoteq) optionalusertempl = usertempl username = _getusername(ui) if username is not None: - optionalusertempl = ('{ifeq(join(users, "\0"), "%s", "", "%s")}' + optionalusertempl = (b'{ifeq(join(users, "\0"), "%s", "", "%s")}' % (username, usertempl)) # Assemble them return { - 'obsfate_quiet': verbtempl + succtempl, - 'obsfate': verbtempl + succtempl + optionalusertempl, - 'obsfate_verbose': verbtempl + succtempl + usertempl + datetempl, + b'obsfate_quiet': verbtempl + succtempl, + b'obsfate': verbtempl + succtempl + optionalusertempl, + b'obsfate_verbose': verbtempl + succtempl + usertempl + datetempl, } def obsfatedata(repo, ctx): @@ -158,18 +158,18 @@ line = [] # Verb - line.append(obsfateline['verb']) + line.append(obsfateline[b'verb']) # Successors - successors = obsfateline["successors"] + successors = obsfateline[b"successors"] if successors: fmtsuccessors = map(lambda s: s[:12], successors) - line.append(" as %s" % ", ".join(fmtsuccessors)) + line.append(b" as %s" % b", ".join(fmtsuccessors)) # Users - if (verbose or normal) and 'users' in obsfateline: - users = obsfateline['users'] + if (verbose or normal) and b'users' in obsfateline: + users = obsfateline[b'users'] if not verbose: # If current user is the only user, do not show anything if not in @@ -179,24 +179,24 @@ users = None if users: - line.append(" by %s" % ", ".join(users)) + line.append(b" by %s" % b", ".join(users)) # Date if verbose: - min_date = obsfateline['min_date'] - max_date = obsfateline['max_date'] + min_date = obsfateline[b'min_date'] + max_date = obsfateline[b'max_date'] if min_date == max_date: - fmtmin_date = util.datestr(min_date, '%Y-%m-%d %H:%M %1%2') - line.append(" (at %s)" % fmtmin_date) + fmtmin_date = util.datestr(min_date, b'%Y-%m-%d %H:%M %1%2') + line.append(b" (at %s)" % fmtmin_date) else: - fmtmin_date = util.datestr(min_date, '%Y-%m-%d %H:%M %1%2') - fmtmax_date = util.datestr(max_date, '%Y-%m-%d %H:%M %1%2') - line.append(" (between %s and %s)" % (fmtmin_date, fmtmax_date)) + fmtmin_date = util.datestr(min_date, b'%Y-%m-%d %H:%M %1%2') + fmtmax_date = util.datestr(max_date, b'%Y-%m-%d %H:%M %1%2') + line.append(b" (between %s and %s)" % (fmtmin_date, fmtmax_date)) - return "".join(line) + return b"".join(line) -def obsfateprinter(obsfate, ui, prefix=""): +def obsfateprinter(obsfate, ui, prefix=b""): lines = [] for raw in obsfate: lines.append(obsfatelineprinter(raw, ui)) @@ -204,7 +204,7 @@ if prefix: lines = [prefix + line for line in lines] - return "\n".join(lines) + return b"\n".join(lines) if not util.safehasattr(templatekw, 'obsfateverb'): # <= hg-4.5 @eh.templatekeyword(b"obsfatedata") @@ -213,7 +213,7 @@ values = obsfatedata(repo, ctx) if values is None: - return templatekw.showlist("obsfatedata", [], args) + return templatekw.showlist(b"obsfatedata", [], args) return _showobsfatedata(repo, ctx, values, **args) @@ -224,36 +224,36 @@ # As we can't do something like # "{join(map(nodeshort, successors), ', '}" in template, manually # create a correct textual representation - gen = ', '.join(n[:12] for n in raw['successors']) + gen = b', '.join(n[:12] for n in raw[b'successors']) - makemap = lambda x: {'successor': x} - joinfmt = lambda d: "%s" % d['successor'] - raw['successors'] = templatekw._hybrid(gen, raw['successors'], makemap, - joinfmt) + makemap = lambda x: {b'successor': x} + joinfmt = lambda d: b"%s" % d[b'successor'] + raw[b'successors'] = templatekw._hybrid(gen, raw[b'successors'], makemap, + joinfmt) # And then format them # Insert default obsfate templates - args['templ'].cache.update(obsfatedefaulttempl(repo.ui)) + args[b'templ'].cache.update(obsfatedefaulttempl(repo.ui)) if repo.ui.quiet: - name = "obsfate_quiet" + name = b"obsfate_quiet" elif repo.ui.verbose: - name = "obsfate_verbose" + name = b"obsfate_verbose" elif repo.ui.debugflag: - name = "obsfate_debug" + name = b"obsfate_debug" else: - name = "obsfate" + name = b"obsfate" # Format a single value def fmt(d): nargs = args.copy() nargs.update(d[name]) - templ = args['templ'] + templ = args[b'templ'] # HG 4.6 if hasattr(templ, "generate"): return templ.generate(name, nargs) else: - return args['templ'](name, **nargs) + return args[b'templ'](name, **nargs) # Generate a good enough string representation using templater gen = [] @@ -268,8 +268,8 @@ except StopIteration: pass - gen.append("".join(chunkstr)) - gen = "; ".join(gen) + gen.append(b"".join(chunkstr)) + gen = b"; ".join(gen) return templatekw._hybrid(gen, values, lambda x: {name: x}, fmt)
--- a/hgext3rd/evolve/thirdparty/cbor.py Mon Jul 29 14:43:15 2019 +0200 +++ b/hgext3rd/evolve/thirdparty/cbor.py Fri Sep 27 13:03:18 2019 +0200 @@ -79,23 +79,23 @@ CBOR_TAG_MIME = 36 # following text is MIME message, headers, separators and all CBOR_TAG_CBOR_FILEHEADER = 55799 # can open a file with 0xd9d9f7 -_CBOR_TAG_BIGNUM_BYTES = struct.pack('B', CBOR_TAG | CBOR_TAG_BIGNUM) +_CBOR_TAG_BIGNUM_BYTES = struct.pack(b'B', CBOR_TAG | CBOR_TAG_BIGNUM) def dumps_int(val): - "return bytes representing int val in CBOR" + b"return bytes representing int val in CBOR" if val >= 0: # CBOR_UINT is 0, so I'm lazy/efficient about not OR-ing it in. if val <= 23: - return struct.pack('B', val) + return struct.pack(b'B', val) if val <= 0x0ff: - return struct.pack('BB', CBOR_UINT8_FOLLOWS, val) + return struct.pack(b'BB', CBOR_UINT8_FOLLOWS, val) if val <= 0x0ffff: - return struct.pack('!BH', CBOR_UINT16_FOLLOWS, val) + return struct.pack(b'!BH', CBOR_UINT16_FOLLOWS, val) if val <= 0x0ffffffff: - return struct.pack('!BI', CBOR_UINT32_FOLLOWS, val) + return struct.pack(b'!BI', CBOR_UINT32_FOLLOWS, val) if val <= 0x0ffffffffffffffff: - return struct.pack('!BQ', CBOR_UINT64_FOLLOWS, val) + return struct.pack(b'!BQ', CBOR_UINT64_FOLLOWS, val) outb = _dumps_bignum_to_bytearray(val) return _CBOR_TAG_BIGNUM_BYTES + _encode_type_num(CBOR_BYTES, len(outb)) + outb val = -1 - val @@ -119,28 +119,28 @@ def dumps_float(val): - return struct.pack("!Bd", CBOR_FLOAT64, val) + return struct.pack(b"!Bd", CBOR_FLOAT64, val) -_CBOR_TAG_NEGBIGNUM_BYTES = struct.pack('B', CBOR_TAG | CBOR_TAG_NEGBIGNUM) +_CBOR_TAG_NEGBIGNUM_BYTES = struct.pack(b'B', CBOR_TAG | CBOR_TAG_NEGBIGNUM) def _encode_type_num(cbor_type, val): """For some CBOR primary type [0..7] and an auxiliary unsigned number, return CBOR encoded bytes""" assert val >= 0 if val <= 23: - return struct.pack('B', cbor_type | val) + return struct.pack(b'B', cbor_type | val) if val <= 0x0ff: - return struct.pack('BB', cbor_type | CBOR_UINT8_FOLLOWS, val) + return struct.pack(b'BB', cbor_type | CBOR_UINT8_FOLLOWS, val) if val <= 0x0ffff: - return struct.pack('!BH', cbor_type | CBOR_UINT16_FOLLOWS, val) + return struct.pack(b'!BH', cbor_type | CBOR_UINT16_FOLLOWS, val) if val <= 0x0ffffffff: - return struct.pack('!BI', cbor_type | CBOR_UINT32_FOLLOWS, val) + return struct.pack(b'!BI', cbor_type | CBOR_UINT32_FOLLOWS, val) if (((cbor_type == CBOR_NEGINT) and (val <= 0x07fffffffffffffff)) or ((cbor_type != CBOR_NEGINT) and (val <= 0x0ffffffffffffffff))): - return struct.pack('!BQ', cbor_type | CBOR_UINT64_FOLLOWS, val) + return struct.pack(b'!BQ', cbor_type | CBOR_UINT64_FOLLOWS, val) if cbor_type != CBOR_NEGINT: - raise Exception("value too big for CBOR unsigned number: {0!r}".format(val)) + raise Exception(b"value too big for CBOR unsigned number: {0!r}".format(val)) outb = _dumps_bignum_to_bytearray(val) return _CBOR_TAG_NEGBIGNUM_BYTES + _encode_type_num(CBOR_BYTES, len(outb)) + outb @@ -201,8 +201,8 @@ def dumps_bool(b): if b: - return struct.pack('B', CBOR_TRUE) - return struct.pack('B', CBOR_FALSE) + return struct.pack(b'B', CBOR_TRUE) + return struct.pack(b'B', CBOR_FALSE) def dumps_tag(t, sort_keys=False): @@ -223,7 +223,7 @@ def dumps(ob, sort_keys=False): if ob is None: - return struct.pack('B', CBOR_NULL) + return struct.pack(b'B', CBOR_NULL) if isinstance(ob, bool): return dumps_bool(ob) if _is_stringish(ob): @@ -239,7 +239,7 @@ return dumps_int(ob) if isinstance(ob, Tag): return dumps_tag(ob, sort_keys=sort_keys) - raise Exception("don't know how to cbor serialize object of type %s", type(ob)) + raise Exception(b"don't know how to cbor serialize object of type %s", type(ob)) # same basic signature as json.dump, but with no options (yet) @@ -260,7 +260,7 @@ self.value = value def __repr__(self): - return "Tag({0!r}, {1!r})".format(self.tag, self.value) + return b"Tag({0!r}, {1!r})".format(self.tag, self.value) def __eq__(self, other): if not isinstance(other, Tag): @@ -273,7 +273,7 @@ Parse CBOR bytes and return Python objects. """ if data is None: - raise ValueError("got None for buffer to decode in loads") + raise ValueError(b"got None for buffer to decode in loads") fp = StringIO(data) return _loads(fp)[0] @@ -296,22 +296,22 @@ aux = tag_aux elif tag_aux == CBOR_UINT8_FOLLOWS: data = fp.read(1) - aux = struct.unpack_from("!B", data, 0)[0] + aux = struct.unpack_from(b"!B", data, 0)[0] bytes_read += 1 elif tag_aux == CBOR_UINT16_FOLLOWS: data = fp.read(2) - aux = struct.unpack_from("!H", data, 0)[0] + aux = struct.unpack_from(b"!H", data, 0)[0] bytes_read += 2 elif tag_aux == CBOR_UINT32_FOLLOWS: data = fp.read(4) - aux = struct.unpack_from("!I", data, 0)[0] + aux = struct.unpack_from(b"!I", data, 0)[0] bytes_read += 4 elif tag_aux == CBOR_UINT64_FOLLOWS: data = fp.read(8) - aux = struct.unpack_from("!Q", data, 0)[0] + aux = struct.unpack_from(b"!Q", data, 0)[0] bytes_read += 8 else: - assert tag_aux == CBOR_VAR_FOLLOWS, "bogus tag {0:02x}".format(tb) + assert tag_aux == CBOR_VAR_FOLLOWS, b"bogus tag {0:02x}".format(tb) aux = None return tag, tag_aux, aux, bytes_read @@ -385,9 +385,9 @@ return ob, bytes_read def _loads(fp, limit=None, depth=0, returntags=False): - "return (object, bytes read)" + b"return (object, bytes read)" if depth > _MAX_DEPTH: - raise Exception("hit CBOR loads recursion depth limit") + raise Exception(b"hit CBOR loads recursion depth limit") tb = _read_byte(fp) @@ -397,16 +397,16 @@ # Some special cases of CBOR_7 best handled by special struct.unpack logic here if tb == CBOR_FLOAT16: data = fp.read(2) - hibyte, lowbyte = struct.unpack_from("BB", data, 0) + hibyte, lowbyte = struct.unpack_from(b"BB", data, 0) exp = (hibyte >> 2) & 0x1F mant = ((hibyte & 0x03) << 8) | lowbyte if exp == 0: val = mant * (2.0 ** -24) elif exp == 31: if mant == 0: - val = float('Inf') + val = float(b'Inf') else: - val = float('NaN') + val = float(b'NaN') else: val = (mant + 1024.0) * (2 ** (exp - 25)) if hibyte & 0x80: @@ -414,11 +414,11 @@ return (val, 3) elif tb == CBOR_FLOAT32: data = fp.read(4) - pf = struct.unpack_from("!f", data, 0) + pf = struct.unpack_from(b"!f", data, 0) return (pf[0], 5) elif tb == CBOR_FLOAT64: data = fp.read(8) - pf = struct.unpack_from("!d", data, 0) + pf = struct.unpack_from(b"!d", data, 0) return (pf[0], 9) tag, tag_aux, aux, bytes_read = _tag_aux(fp, tb) @@ -461,7 +461,7 @@ return (None, bytes_read) if tb == CBOR_UNDEFINED: return (None, bytes_read) - raise ValueError("unknown cbor tag 7 byte: {:02x}".format(tb)) + raise ValueError(b"unknown cbor tag 7 byte: {:02x}".format(tb)) def loads_bytes(fp, aux, btag=CBOR_BYTES): @@ -481,7 +481,7 @@ total_bytes_read += 1 break tag, tag_aux, aux, bytes_read = _tag_aux(fp, tb) - assert tag == btag, 'variable length value contains unexpected component' + assert tag == btag, b'variable length value contains unexpected component' ob = fp.read(aux) chunklist.append(ob) total_bytes_read += bytes_read + aux
--- a/hgext3rd/evolve/utility.py Mon Jul 29 14:43:15 2019 +0200 +++ b/hgext3rd/evolve/utility.py Fri Sep 27 13:03:18 2019 +0200 @@ -17,20 +17,20 @@ compat, ) -shorttemplate = "[{label('evolve.rev', rev)}] {desc|firstline}\n" -stacktemplate = """[{label('evolve.rev', if(topicidx, "s{topicidx}", rev))}] {desc|firstline}\n""" +shorttemplate = b"[{label('evolve.rev', rev)}] {desc|firstline}\n" +stacktemplate = b"""[{label('evolve.rev', if(topicidx, "s{topicidx}", rev))}] {desc|firstline}\n""" def obsexcmsg(ui, message, important=False): - verbose = ui.configbool('experimental', 'verbose-obsolescence-exchange') + verbose = ui.configbool(b'experimental', b'verbose-obsolescence-exchange') if verbose: - message = 'OBSEXC: ' + message + message = b'OBSEXC: ' + message if important or verbose: ui.status(message) def obsexcprg(ui, *args, **kwargs): - topic = 'obsmarkers exchange' - if ui.configbool('experimental', 'verbose-obsolescence-exchange'): - topic = 'OBSEXC' + topic = b'obsmarkers exchange' + if ui.configbool(b'experimental', b'verbose-obsolescence-exchange'): + topic = b'OBSEXC' compat.progress(ui, topic, *args, **kwargs) def filterparents(parents): @@ -50,28 +50,28 @@ def shouldwarmcache(repo, tr): configbool = repo.ui.configbool config = repo.ui.config - desc = getattr(tr, 'desc', '') + desc = getattr(tr, 'desc', b'') autocase = False if tr is None and not getattr(repo, '_destroying', False): autocase = True - elif desc.startswith('serve'): + elif desc.startswith(b'serve'): autocase = True - elif desc.startswith('push') and not desc.startswith('push-response'): + elif desc.startswith(b'push') and not desc.startswith(b'push-response'): autocase = True - autocache = config('experimental', 'obshashrange.warm-cache', - 'auto') == 'auto' + autocache = config(b'experimental', b'obshashrange.warm-cache', + b'auto') == b'auto' if autocache: warm = autocase else: # note: we should not get to the default case - warm = configbool('experimental', 'obshashrange.warm-cache') - if not configbool('experimental', 'obshashrange'): + warm = configbool(b'experimental', b'obshashrange.warm-cache') + if not configbool(b'experimental', b'obshashrange'): return False if not warm: return False - maxrevs = repo.ui.configint('experimental', 'obshashrange.max-revs') + maxrevs = repo.ui.configint(b'experimental', b'obshashrange.max-revs') if maxrevs is not None and maxrevs < len(repo.unfiltered()): return False return True @@ -123,8 +123,8 @@ newer = obsutil.successorssets(repo, obs.node()) # search of a parent which is not killed while not newer: - ui.debug("stabilize target %s is plain dead," - " trying to stabilize on its parent\n" % + ui.debug(b"stabilize target %s is plain dead," + b" trying to stabilize on its parent\n" % obs) obs = obs.parents()[0] newer = obsutil.successorssets(repo, obs.node()) @@ -141,7 +141,7 @@ for successorsset in exc.successorssets for node in successorsset} -def revselectionprompt(ui, repo, revs, customheader=""): +def revselectionprompt(ui, repo, revs, customheader=b""): """function to prompt user to choose a revision from all the revs and return that revision for further tasks @@ -161,29 +161,29 @@ if not ui.interactive(): return None - promptmsg = customheader + "\n" + promptmsg = customheader + b"\n" for idx, rev in enumerate(revs): curctx = repo[rev] - revmsg = "%d: [%s] %s\n" % (idx + 1, curctx, - curctx.description().split("\n")[0]) + revmsg = b"%d: [%s] %s\n" % (idx + 1, curctx, + curctx.description().split(b"\n")[0]) promptmsg += revmsg - promptmsg += _("q: quit the prompt\n") - promptmsg += _("enter the index of the revision you want to select:") + promptmsg += _(b"q: quit the prompt\n") + promptmsg += _(b"enter the index of the revision you want to select:") idxselected = ui.prompt(promptmsg) intidx = None try: intidx = int(idxselected) except ValueError: - if idxselected == 'q': + if idxselected == b'q': return None - ui.write_err(_("invalid value '%s' entered for index\n") % idxselected) + ui.write_err(_(b"invalid value '%s' entered for index\n") % idxselected) return None if intidx > len(revs) or intidx <= 0: # we can make this error message better - ui.write_err(_("invalid value '%d' entered for index\n") % intidx) + ui.write_err(_(b"invalid value '%d' entered for index\n") % intidx) return None return revs[intidx - 1] @@ -206,7 +206,7 @@ # all three are different, lets concatenate the two authors # XXX: should we let the user know about concatenation of authors # by printing some message (or maybe in verbose mode) - users = set(divuser.split(', ')) - users.update(othuser.split(', ')) - user = ', '.join(sorted(users)) + users = set(divuser.split(b', ')) + users.update(othuser.split(b', ')) + user = b', '.join(sorted(users)) return user
--- a/hgext3rd/pullbundle.py Mon Jul 29 14:43:15 2019 +0200 +++ b/hgext3rd/pullbundle.py Fri Sep 27 13:03:18 2019 +0200 @@ -92,10 +92,10 @@ from mercurial.i18n import _ -__version__ = '0.1.1' -testedwith = '4.4 4.5 4.6 4.7.1' -minimumhgversion = '4.4' -buglink = 'https://bz.mercurial-scm.org/' +__version__ = b'0.1.1' +testedwith = b'4.4 4.5 4.6 4.7.1' +minimumhgversion = b'4.4' +buglink = b'https://bz.mercurial-scm.org/' cmdtable = {} command = registrar.command(cmdtable) @@ -103,14 +103,14 @@ configtable = {} configitem = registrar.configitem(configtable) -configitem('pullbundle', 'cache-directory', +configitem(b'pullbundle', b'cache-directory', default=None, ) # generic wrapping def uisetup(ui): - exchange.getbundle2partsmapping['changegroup'] = _getbundlechangegrouppart + exchange.getbundle2partsmapping[b'changegroup'] = _getbundlechangegrouppart def _getbundlechangegrouppart(bundler, repo, source, bundlecaps=None, b2caps=None, heads=None, common=None, **kwargs): @@ -118,13 +118,13 @@ if not kwargs.get(r'cg', True): return - version = '01' - cgversions = b2caps.get('changegroup') + version = b'01' + cgversions = b2caps.get(b'changegroup') if cgversions: # 3.1 and 3.2 ship with an empty value cgversions = [v for v in cgversions if v in changegroup.supportedoutgoingversions(repo)] if not cgversions: - raise ValueError(_('no common changegroup version')) + raise ValueError(_(b'no common changegroup version')) version = max(cgversions) outgoing = exchange._computeoutgoing(repo, heads, common) @@ -145,20 +145,20 @@ # END OF ALTERED PART if kwargs.get(r'narrow', False) and (include or exclude): - narrowspecpart = bundler.newpart('narrow:spec') + narrowspecpart = bundler.newpart(b'narrow:spec') if include: narrowspecpart.addparam( - 'include', '\n'.join(include), mandatory=True) + b'include', b'\n'.join(include), mandatory=True) if exclude: narrowspecpart.addparam( - 'exclude', '\n'.join(exclude), mandatory=True) + b'exclude', b'\n'.join(exclude), mandatory=True) def makeallcgpart(newpart, repo, outgoing, version, source, bundlecaps, filematcher, cgversions): pullbundle = not filematcher if pullbundle and not util.safehasattr(repo, 'stablerange'): - repo.ui.warn('pullbundle: required extension "evolve" are missing, skipping pullbundle\n') + repo.ui.warn(b'pullbundle: required extension "evolve" are missing, skipping pullbundle\n') pullbundle = False if filematcher: makeonecgpart(newpart, repo, None, outgoing, version, source, bundlecaps, @@ -167,8 +167,8 @@ start = util.timer() slices = sliceoutgoing(repo, outgoing) end = util.timer() - msg = _('pullbundle-cache: "missing" set sliced into %d subranges ' - 'in %f seconds\n') + msg = _(b'pullbundle-cache: "missing" set sliced into %d subranges ' + b'in %f seconds\n') repo.ui.write(msg % (len(slices), end - start)) for sliceid, sliceout in slices: makeonecgpart(newpart, repo, sliceid, sliceout, version, source, bundlecaps, @@ -192,7 +192,7 @@ missingheads = [rev(n) for n in sorted(outgoing.missingheads, reverse=True)] for head in missingheads: localslices = [] - localmissing = set(repo.revs('%ld and ::%d', missingrevs, head)) + localmissing = set(repo.revs(b'%ld and ::%d', missingrevs, head)) thisrunmissing = localmissing.copy() while localmissing: slicerevs = [] @@ -207,11 +207,11 @@ missingrevs.difference_update(slicerevs) localmissing.difference_update(slicerevs) if localmissing: - heads = list(repo.revs('heads(%ld)', localmissing)) + heads = list(repo.revs(b'heads(%ld)', localmissing)) heads.sort(key=node) head = heads.pop() if heads: - thisrunmissing = repo.revs('%ld and only(%d, %ld)', + thisrunmissing = repo.revs(b'%ld and only(%d, %ld)', localmissing, head, heads) @@ -220,15 +220,15 @@ if DEBUG: for s in reversed(ss): ms -= set(s) - missingbase = repo.revs('parents(%ld) and %ld', s, ms) + missingbase = repo.revs(b'parents(%ld) and %ld', s, ms) if missingbase: - repo.ui.write_err('!!! rev bundled while parents missing\n') - repo.ui.write_err(' parent: %s\n' % list(missingbase)) - pb = repo.revs('%ld and children(%ld)', s, missingbase) - repo.ui.write_err(' children: %s\n' % list(pb)) - h = repo.revs('heads(%ld)', s) - repo.ui.write_err(' heads: %s\n' % list(h)) - raise error.ProgrammingError('issuing a range before its parents') + repo.ui.write_err(b'!!! rev bundled while parents missing\n') + repo.ui.write_err(b' parent: %s\n' % list(missingbase)) + pb = repo.revs(b'%ld and children(%ld)', s, missingbase) + repo.ui.write_err(b' children: %s\n' % list(pb)) + h = repo.revs(b'heads(%ld)', s) + repo.ui.write_err(b' heads: %s\n' % list(h)) + raise error.ProgrammingError(b'issuing a range before its parents') for s in reversed(localslices): allslices.extend(s) @@ -381,8 +381,8 @@ # changegroup part construction def _changegroupinfo(repo, nodes, source): - if repo.ui.verbose or source == 'bundle': - repo.ui.status(_("%d changesets found\n") % len(nodes)) + if repo.ui.verbose or source == b'bundle': + repo.ui.status(_(b"%d changesets found\n") % len(nodes)) def _makenewstream(newpart, repo, outgoing, version, source, bundlecaps, filematcher, cgversions): @@ -408,23 +408,23 @@ def _makepartfromstream(newpart, repo, cgstream, nbchanges, version): # same as upstream code - part = newpart('changegroup', data=cgstream) + part = newpart(b'changegroup', data=cgstream) if version: - part.addparam('version', version) + part.addparam(b'version', version) - part.addparam('nbchanges', '%d' % nbchanges, + part.addparam(b'nbchanges', b'%d' % nbchanges, mandatory=False) - if 'treemanifest' in repo.requirements: - part.addparam('treemanifest', '1') + if b'treemanifest' in repo.requirements: + part.addparam(b'treemanifest', b'1') # cache management def cachedir(repo): - cachedir = repo.ui.config('pullbundle', 'cache-directory') + cachedir = repo.ui.config(b'pullbundle', b'cache-directory') if cachedir is not None: return cachedir - return repo.cachevfs.join('pullbundles') + return repo.cachevfs.join(b'pullbundles') def getcache(repo, bundlename): cdir = cachedir(repo) @@ -436,7 +436,7 @@ # opening too many file will not work. def data(): - with open(bundlepath, 'rb') as fd: + with open(bundlepath, r'rb') as fd: for chunk in util.filechunkiter(fd): yield chunk return data() @@ -454,7 +454,7 @@ cachefile.write(chunk) yield chunk -BUNDLEMASK = "%s-%s-%010iskip-%010isize.hg" +BUNDLEMASK = b"%s-%s-%010iskip-%010isize.hg" def makeonecgpart(newpart, repo, rangeid, outgoing, version, source, bundlecaps, filematcher, cgversions): @@ -472,19 +472,19 @@ cgstream = cachewriter(repo, bundlename, partdata[0]) partdata = (cgstream,) + partdata[1:] else: - if repo.ui.verbose or source == 'bundle': - repo.ui.status(_("%d changesets found in caches\n") % nbchanges) + if repo.ui.verbose or source == b'bundle': + repo.ui.status(_(b"%d changesets found in caches\n") % nbchanges) pversion = None if cgversions: pversion = version partdata = (cachedata, nbchanges, pversion) return _makepartfromstream(newpart, repo, *partdata) -@command('debugpullbundlecacheoverlap', - [('', 'count', 100, _('of "client" pulling')), - ('', 'min-cache', 1, _('minimum size of cached bundle')), - ], - _('hg debugpullbundlecacheoverlap [--client 100] REVSET')) +@command(b'debugpullbundlecacheoverlap', + [(b'', b'count', 100, _(b'of "client" pulling')), + (b'', b'min-cache', 1, _(b'minimum size of cached bundle')), + ], + _(b'hg debugpullbundlecacheoverlap [--client 100] REVSET')) def debugpullbundlecacheoverlap(ui, repo, *revs, **opts): '''Display statistic on bundle cache hit @@ -494,7 +494,7 @@ ''' actionrevs = scmutil.revrange(repo, revs) if not revs: - raise error.Abort('No revision selected') + raise error.Abort(b'No revision selected') count = opts['count'] min_cache = opts['min_cache'] @@ -503,12 +503,12 @@ rlen = lambda rangeid: repo.stablerange.rangelength(repo, rangeid) - repo.ui.write("gathering %d sample pulls within %d revisions\n" + repo.ui.write(b"gathering %d sample pulls within %d revisions\n" % (count, len(actionrevs))) if 1 < min_cache: - repo.ui.write(" not caching ranges smaller than %d changesets\n" % min_cache) + repo.ui.write(b" not caching ranges smaller than %d changesets\n" % min_cache) for i in range(count): - repo.ui.progress('gathering data', i, total=count) + repo.ui.progress(b'gathering data', i, total=count) outgoing = takeonesample(repo, actionrevs) ranges = sliceoutgoing(repo, outgoing) hitranges = 0 @@ -532,7 +532,7 @@ hitranges, ) pullstats.append(stats) - repo.ui.progress('gathering data', None) + repo.ui.progress(b'gathering data', None) sizes = [] changesmissing = [] @@ -563,36 +563,36 @@ cachedhits.append(hits) sizesdist = distribution(sizes) - repo.ui.write(fmtdist('pull size', sizesdist)) + repo.ui.write(fmtdist(b'pull size', sizesdist)) changesmissingdist = distribution(changesmissing) - repo.ui.write(fmtdist('non-cached changesets', changesmissingdist)) + repo.ui.write(fmtdist(b'non-cached changesets', changesmissingdist)) changesratiodist = distribution(changesratio) - repo.ui.write(fmtdist('ratio of cached changesets', changesratiodist)) + repo.ui.write(fmtdist(b'ratio of cached changesets', changesratiodist)) bundlecountdist = distribution(bundlecount) - repo.ui.write(fmtdist('bundle count', bundlecountdist)) + repo.ui.write(fmtdist(b'bundle count', bundlecountdist)) rangesratiodist = distribution(rangesratio) - repo.ui.write(fmtdist('ratio of cached bundles', rangesratiodist)) + repo.ui.write(fmtdist(b'ratio of cached bundles', rangesratiodist)) - repo.ui.write('changesets served:\n') - repo.ui.write(' total: %7d\n' % totalchanges) - repo.ui.write(' from cache: %7d (%2d%%)\n' + repo.ui.write(b'changesets served:\n') + repo.ui.write(b' total: %7d\n' % totalchanges) + repo.ui.write(b' from cache: %7d (%2d%%)\n' % (totalcached, (totalcached * 100 // totalchanges))) - repo.ui.write(' bundle: %7d\n' % sum(bundlecount)) + repo.ui.write(b' bundle: %7d\n' % sum(bundlecount)) cachedsizesdist = distribution(cachedsizes) - repo.ui.write(fmtdist('size of cached bundles', cachedsizesdist)) + repo.ui.write(fmtdist(b'size of cached bundles', cachedsizesdist)) cachedhitsdist = distribution(cachedhits) - repo.ui.write(fmtdist('hit on cached bundles', cachedhitsdist)) + repo.ui.write(fmtdist(b'hit on cached bundles', cachedhitsdist)) def takeonesample(repo, revs): node = repo.changelog.node pulled = random.sample(revs, max(4, len(revs) // 1000)) - pulled = repo.revs('%ld::%ld', pulled, pulled) + pulled = repo.revs(b'%ld::%ld', pulled, pulled) nodes = [node(r) for r in pulled] return outgoingfromnodes(repo, nodes) @@ -600,17 +600,17 @@ data.sort() length = len(data) return { - 'min': data[0], - '10%': data[length // 10], - '25%': data[length // 4], - '50%': data[length // 2], - '75%': data[(length // 4) * 3], - '90%': data[(length // 10) * 9], - '95%': data[(length // 20) * 19], - 'max': data[-1], + b'min': data[0], + b'10%': data[length // 10], + b'25%': data[length // 4], + b'50%': data[length // 2], + b'75%': data[(length // 4) * 3], + b'90%': data[(length // 10) * 9], + b'95%': data[(length // 20) * 19], + b'max': data[-1], } -STATSFORMAT = """{name}: +STATSFORMAT = b"""{name}: min: {min} 10%: {10%} 25%: {25%}
--- a/hgext3rd/serverminitopic.py Mon Jul 29 14:43:15 2019 +0200 +++ b/hgext3rd/serverminitopic.py Fri Sep 27 13:03:18 2019 +0200 @@ -33,7 +33,7 @@ configtable = {} configitem = registrar.configitem(configtable) - configitem('experimental', 'server-mini-topic', + configitem(b'experimental', b'server-mini-topic', default=False, ) @@ -44,7 +44,7 @@ """ enabled = getattr(repo, '_hasminitopic', None) if enabled is None: - enabled = (repo.ui.configbool('experimental', 'server-mini-topic') + enabled = (repo.ui.configbool(b'experimental', b'server-mini-topic') and not repo.publishing()) repo._hasminitopic = enabled return enabled @@ -54,10 +54,10 @@ def topicbranch(orig, self): branch = orig(self) if hasminitopic(self._repo) and self.phase(): - topic = self._changeset.extra.get('topic') + topic = self._changeset.extra.get(b'topic') if topic is not None: topic = encoding.tolocal(topic) - branch = '%s:%s' % (branch, topic) + branch = b'%s:%s' % (branch, topic) return branch ### avoid caching topic data in rev-branch-cache @@ -67,7 +67,7 @@ def _init__(self, *args, **kwargs): super(revbranchcacheoverlay, self).__init__(*args, **kwargs) - if 'branchinfo' in vars(self): + if r'branchinfo' in vars(self): del self.branchinfo def branchinfo(self, rev, changelog=None): @@ -95,7 +95,7 @@ class topicawarerbc(revbranchcacheoverlay, cache.__class__): pass cache.__class__ = topicawarerbc - if 'branchinfo' in vars(cache): + if r'branchinfo' in vars(cache): del cache.branchinfo self._revbranchcache = cache return self._revbranchcache @@ -120,7 +120,7 @@ if revs: s = hashlib.sha1() for rev in revs: - s.update('%d;' % rev) + s.update(b'%d;' % rev) key = s.digest() return key @@ -138,8 +138,8 @@ branchmap.branchcache = previous _publiconly = set([ - 'base', - 'immutable', + b'base', + b'immutable', ]) def mighttopic(repo): @@ -216,7 +216,7 @@ def wireprotocaps(orig, repo, proto): caps = orig(repo, proto) if hasminitopic(repo): - caps.append('topics') + caps.append(b'topics') return caps # wrap the necessary bit
--- a/hgext3rd/topic/__init__.py Mon Jul 29 14:43:15 2019 +0200 +++ b/hgext3rd/topic/__init__.py Fri Sep 27 13:03:18 2019 +0200 @@ -160,36 +160,36 @@ cmdtable = {} command = registrar.command(cmdtable) -colortable = {'topic.active': 'green', - 'topic.list.unstablecount': 'red', - 'topic.list.headcount.multiple': 'yellow', - 'topic.list.behindcount': 'cyan', - 'topic.list.behinderror': 'red', - 'stack.index': 'yellow', - 'stack.index.base': 'none dim', - 'stack.desc.base': 'none dim', - 'stack.shortnode.base': 'none dim', - 'stack.state.base': 'dim', - 'stack.state.clean': 'green', - 'stack.index.current': 'cyan', # random pick - 'stack.state.current': 'cyan bold', # random pick - 'stack.desc.current': 'cyan', # random pick - 'stack.shortnode.current': 'cyan', # random pick - 'stack.state.orphan': 'red', - 'stack.state.content-divergent': 'red', - 'stack.state.phase-divergent': 'red', - 'stack.summary.behindcount': 'cyan', - 'stack.summary.behinderror': 'red', - 'stack.summary.headcount.multiple': 'yellow', +colortable = {b'topic.active': b'green', + b'topic.list.unstablecount': b'red', + b'topic.list.headcount.multiple': b'yellow', + b'topic.list.behindcount': b'cyan', + b'topic.list.behinderror': b'red', + b'stack.index': b'yellow', + b'stack.index.base': b'none dim', + b'stack.desc.base': b'none dim', + b'stack.shortnode.base': b'none dim', + b'stack.state.base': b'dim', + b'stack.state.clean': b'green', + b'stack.index.current': b'cyan', # random pick + b'stack.state.current': b'cyan bold', # random pick + b'stack.desc.current': b'cyan', # random pick + b'stack.shortnode.current': b'cyan', # random pick + b'stack.state.orphan': b'red', + b'stack.state.content-divergent': b'red', + b'stack.state.phase-divergent': b'red', + b'stack.summary.behindcount': b'cyan', + b'stack.summary.behinderror': b'red', + b'stack.summary.headcount.multiple': b'yellow', # default color to help log output and thg # (first pick I could think off, update as needed - 'log.topic': 'green_background', - 'topic.active': 'green', - } + b'log.topic': b'green_background', + b'topic.active': b'green', + } -__version__ = b'0.16.0.dev' +__version__ = b'0.17.0' -testedwith = b'4.5.2 4.6.2 4.7 4.8 4.9 5.0' +testedwith = b'4.5.2 4.6.2 4.7 4.8 4.9 5.0 5.1' minimumhgversion = b'4.5' buglink = b'https://bz.mercurial-scm.org/' @@ -200,25 +200,25 @@ configtable = {} configitem = registrar.configitem(configtable) - configitem('experimental', 'enforce-topic', + configitem(b'experimental', b'enforce-topic', default=False, ) - configitem('experimental', 'enforce-single-head', + configitem(b'experimental', b'enforce-single-head', default=False, ) - configitem('experimental', 'topic-mode', + configitem(b'experimental', b'topic-mode', default=None, ) - configitem('experimental', 'topic.publish-bare-branch', + configitem(b'experimental', b'topic.publish-bare-branch', default=False, ) - configitem('experimental', 'topic.allow-publish', + configitem(b'experimental', b'topic.allow-publish', default=configitems.dynamicdefault, ) - configitem('_internal', 'keep-topic', + configitem(b'_internal', b'keep-topic', default=False, ) - configitem('experimental', 'topic-mode.server', + configitem(b'experimental', b'topic-mode.server', default=configitems.dynamicdefault, ) @@ -229,25 +229,25 @@ # nobody else did so far. from mercurial import configitems extraitem = functools.partial(configitems._register, ui._knownconfig) - if ('experimental' not in ui._knownconfig - or not ui._knownconfig['experimental'].get('thg.displaynames')): - extraitem('experimental', 'thg.displaynames', + if (b'experimental' not in ui._knownconfig + or not ui._knownconfig[b'experimental'].get(b'thg.displaynames')): + extraitem(b'experimental', b'thg.displaynames', default=None, ) - if ('devel' not in ui._knownconfig - or not ui._knownconfig['devel'].get('random')): - extraitem('devel', 'randomseed', + if (b'devel' not in ui._knownconfig + or not ui._knownconfig[b'devel'].get(b'random')): + extraitem(b'devel', b'randomseed', default=None, ) # we need to do old style declaration for <= 4.5 templatekeyword = registrar.templatekeyword() -post45template = 'requires=' in templatekeyword.__doc__ +post45template = r'requires=' in templatekeyword.__doc__ def _contexttopic(self, force=False): if not (force or self.mutable()): - return '' - return self.extra().get(constants.extrakey, '') + return b'' + return self.extra().get(constants.extrakey, b'') context.basectx.topic = _contexttopic def _contexttopicidx(self): @@ -264,8 +264,8 @@ return None context.basectx.topicidx = _contexttopicidx -stackrev = re.compile(r'^s\d+$') -topicrev = re.compile(r'^t\d+$') +stackrev = re.compile(br'^s\d+$') +topicrev = re.compile(br'^t\d+$') hastopicext = common.hastopicext @@ -275,38 +275,38 @@ idx = int(name[1:]) tname = topic = repo.currenttopic if topic: - ttype = 'topic' + ttype = b'topic' revs = list(stack.stack(repo, topic=topic)) else: - ttype = 'branch' + ttype = b'branch' tname = branch = repo[None].branch() revs = list(stack.stack(repo, branch=branch)) elif topicrev.match(name): idx = int(name[1:]) - ttype = 'topic' + ttype = b'topic' tname = topic = repo.currenttopic if not tname: - raise error.Abort(_('cannot resolve "%s": no active topic') % name) + raise error.Abort(_(b'cannot resolve "%s": no active topic') % name) revs = list(stack.stack(repo, topic=topic)) if revs is not None: try: r = revs[idx] except IndexError: - if ttype == 'topic': - msg = _('cannot resolve "%s": %s "%s" has only %d changesets') - elif ttype == 'branch': - msg = _('cannot resolve "%s": %s "%s" has only %d non-public changesets') + if ttype == b'topic': + msg = _(b'cannot resolve "%s": %s "%s" has only %d changesets') + elif ttype == b'branch': + msg = _(b'cannot resolve "%s": %s "%s" has only %d non-public changesets') raise error.Abort(msg % (name, ttype, tname, len(revs) - 1)) # t0 or s0 can be None if r == -1 and idx == 0: - msg = _('the %s "%s" has no %s') + msg = _(b'the %s "%s" has no %s') raise error.Abort(msg % (ttype, tname, name)) return [repo[r].node()] if name not in repo.topics: return [] node = repo.changelog.node - return [node(rev) for rev in repo.revs('topic(%s)', name)] + return [node(rev) for rev in repo.revs(b'topic(%s)', name)] def _nodemap(repo, node): ctx = repo[node] @@ -321,22 +321,22 @@ topicmap.modsetup(ui) setupimportexport(ui) - extensions.afterloaded('rebase', _fixrebase) + extensions.afterloaded(b'rebase', _fixrebase) flow.installpushflag(ui) - entry = extensions.wrapcommand(commands.table, 'commit', commitwrap) - entry[1].append(('t', 'topic', '', - _("use specified topic"), _('TOPIC'))) + entry = extensions.wrapcommand(commands.table, b'commit', commitwrap) + entry[1].append((b't', b'topic', b'', + _(b"use specified topic"), _(b'TOPIC'))) - entry = extensions.wrapcommand(commands.table, 'push', pushoutgoingwrap) - entry[1].append(('t', 'topic', '', - _("topic to push"), _('TOPIC'))) + entry = extensions.wrapcommand(commands.table, b'push', pushoutgoingwrap) + entry[1].append((b't', b'topic', b'', + _(b"topic to push"), _(b'TOPIC'))) - entry = extensions.wrapcommand(commands.table, 'outgoing', + entry = extensions.wrapcommand(commands.table, b'outgoing', pushoutgoingwrap) - entry[1].append(('t', 'topic', '', - _("topic to push"), _('TOPIC'))) + entry[1].append((b't', b'topic', b'', + _(b"topic to push"), _(b'TOPIC'))) extensions.wrapfunction(cmdutil, 'buildcommittext', committextwrap) extensions.wrapfunction(merge, 'update', mergeupdatewrap) @@ -344,20 +344,20 @@ # behaviour of changing topic and I can't find a better way # to do that as scmutil.revsingle returns the rev number and hence we can't # plug into logic for this into mergemod.update(). - extensions.wrapcommand(commands.table, 'update', checkt0) + extensions.wrapcommand(commands.table, b'update', checkt0) try: - evolve = extensions.find('evolve') + evolve = extensions.find(b'evolve') extensions.wrapfunction(evolve.rewriteutil, "presplitupdate", presplitupdatetopic) except (KeyError, AttributeError): pass - cmdutil.summaryhooks.add('topic', summaryhook) + cmdutil.summaryhooks.add(b'topic', summaryhook) if not post45template: - templatekw.keywords['topic'] = topickw - templatekw.keywords['topicidx'] = topicidxkw + templatekw.keywords[b'topic'] = topickw + templatekw.keywords[b'topicidx'] = topicidxkw # Wrap workingctx extra to return the topic name extensions.wrapfunction(context.workingctx, '__init__', wrapinit) # Wrap changelog.add to drop empty topic @@ -369,9 +369,9 @@ repo = repo.unfiltered() - if repo.ui.config('experimental', 'thg.displaynames') is None: - repo.ui.setconfig('experimental', 'thg.displaynames', 'topics', - source='topic-extension') + if repo.ui.config(b'experimental', b'thg.displaynames') is None: + repo.ui.setconfig(b'experimental', b'thg.displaynames', b'topics', + source=b'topic-extension') class topicrepo(repo.__class__): @@ -380,51 +380,51 @@ def _restrictcapabilities(self, caps): caps = super(topicrepo, self)._restrictcapabilities(caps) - caps.add('topics') + caps.add(b'topics') return caps def commit(self, *args, **kwargs): - backup = self.ui.backupconfig('ui', 'allowemptycommit') + backup = self.ui.backupconfig(b'ui', b'allowemptycommit') try: - if self.currenttopic != self['.'].topic(): + if self.currenttopic != self[b'.'].topic(): # bypass the core "nothing changed" logic - self.ui.setconfig('ui', 'allowemptycommit', True) + self.ui.setconfig(b'ui', b'allowemptycommit', True) return super(topicrepo, self).commit(*args, **kwargs) finally: self.ui.restoreconfig(backup) - def commitctx(self, ctx, error=None): + def commitctx(self, ctx, *args, **kwargs): topicfilter = topicmap.topicfilter(self.filtername) if topicfilter != self.filtername: other = self.filtered(topicmap.topicfilter(self.filtername)) - other.commitctx(ctx, error=error) + other.commitctx(ctx, *args, **kwargs) if isinstance(ctx, context.workingcommitctx): current = self.currenttopic if current: ctx.extra()[constants.extrakey] = current if (isinstance(ctx, context.memctx) - and ctx.extra().get('amend_source') + and ctx.extra().get(b'amend_source') and ctx.topic() and not self.currenttopic): # we are amending and need to remove a topic del ctx.extra()[constants.extrakey] - return super(topicrepo, self).commitctx(ctx, error=error) + return super(topicrepo, self).commitctx(ctx, *args, **kwargs) @property def topics(self): if self._topics is not None: return self._topics - topics = set(['', self.currenttopic]) - for c in self.set('not public()'): + topics = set([b'', self.currenttopic]) + for c in self.set(b'not public()'): topics.add(c.topic()) - topics.remove('') + topics.remove(b'') self._topics = topics return topics @property def currenttopic(self): - return self.vfs.tryread('topic') + return self.vfs.tryread(b'topic') # overwritten at the instance level by topicmap.py _autobranchmaptopic = True @@ -441,7 +441,7 @@ if branch is None: branch = self[None].branch() if self.currenttopic: - branch = "%s:%s" % (branch, self.currenttopic) + branch = b"%s:%s" % (branch, self.currenttopic) return super(topicrepo, self).branchheads(branch=branch, start=start, closed=closed) @@ -464,11 +464,11 @@ def transaction(self, desc, *a, **k): ctr = self.currenttransaction() tr = super(topicrepo, self).transaction(desc, *a, **k) - if desc in ('strip', 'repair') or ctr is not None: + if desc in (b'strip', b'repair') or ctr is not None: return tr reporef = weakref.ref(self) - if self.ui.configbool('experimental', 'enforce-single-head'): + if self.ui.configbool(b'experimental', b'enforce-single-head'): if util.safehasattr(tr, 'validator'): # hg <= 4.7 origvalidator = tr.validator else: @@ -484,10 +484,10 @@ else: tr._validator = validator - topicmodeserver = self.ui.config('experimental', - 'topic-mode.server', 'ignore') - ispush = (desc.startswith('push') or desc.startswith('serve')) - if (topicmodeserver != 'ignore' and ispush): + topicmodeserver = self.ui.config(b'experimental', + b'topic-mode.server', b'ignore') + ispush = (desc.startswith(b'push') or desc.startswith(b'serve')) + if (topicmodeserver != b'ignore' and ispush): if util.safehasattr(tr, 'validator'): # hg <= 4.7 origvalidator = tr.validator else: @@ -502,9 +502,9 @@ else: tr._validator = validator - elif (self.ui.configbool('experimental', 'topic.publish-bare-branch') - and (desc.startswith('push') - or desc.startswith('serve')) + elif (self.ui.configbool(b'experimental', b'topic.publish-bare-branch') + and (desc.startswith(b'push') + or desc.startswith(b'serve')) ): origclose = tr.close trref = weakref.ref(tr) @@ -515,8 +515,8 @@ flow.publishbarebranch(repo, tr2) origclose() tr.close = close - allow_publish = self.ui.configbool('experimental', - 'topic.allow-publish', + allow_publish = self.ui.configbool(b'experimental', + b'topic.allow-publish', True) if not allow_publish: if util.safehasattr(tr, 'validator'): # hg <= 4.7 @@ -547,62 +547,62 @@ csetcount = stack.stack(repo, topic=ct).changesetcount empty = csetcount == 0 if empty and not ctwasempty: - ui.status("active topic '%s' is now empty\n" % ct) + ui.status(b"active topic '%s' is now empty\n" % ct) trnames = getattr(tr, 'names', getattr(tr, '_names', ())) - if ('phase' in trnames - or any(n.startswith('push-response') + if (b'phase' in trnames + or any(n.startswith(b'push-response') for n in trnames)): - ui.status(_("(use 'hg topic --clear' to clear it if needed)\n")) - hint = _("(see 'hg help topics' for more information)\n") + ui.status(_(b"(use 'hg topic --clear' to clear it if needed)\n")) + hint = _(b"(see 'hg help topics' for more information)\n") if ctwasempty and not empty: if csetcount == 1: - msg = _("active topic '%s' grew its first changeset\n%s") + msg = _(b"active topic '%s' grew its first changeset\n%s") ui.status(msg % (ct, hint)) else: - msg = _("active topic '%s' grew its %s first changesets\n%s") + msg = _(b"active topic '%s' grew its %s first changesets\n%s") ui.status(msg % (ct, csetcount, hint)) - tr.addpostclose('signalcurrenttopicempty', currenttopicempty) + tr.addpostclose(b'signalcurrenttopicempty', currenttopicempty) return tr repo.__class__ = topicrepo repo._topics = None if util.safehasattr(repo, 'names'): repo.names.addnamespace(namespaces.namespace( - 'topics', 'topic', namemap=_namemap, nodemap=_nodemap, + b'topics', b'topic', namemap=_namemap, nodemap=_nodemap, listnames=lambda repo: repo.topics)) if post45template: @templatekeyword(b'topic', requires={b'ctx'}) def topickw(context, mapping): """:topic: String. The topic of the changeset""" - ctx = context.resource(mapping, 'ctx') + ctx = context.resource(mapping, b'ctx') return ctx.topic() @templatekeyword(b'topicidx', requires={b'ctx'}) def topicidxkw(context, mapping): """:topicidx: Integer. Index of the changeset as a stack alias""" - ctx = context.resource(mapping, 'ctx') + ctx = context.resource(mapping, b'ctx') return ctx.topicidx() else: def topickw(**args): """:topic: String. The topic of the changeset""" - return args['ctx'].topic() + return args[b'ctx'].topic() def topicidxkw(**args): """:topicidx: Integer. Index of the changeset as a stack alias""" - return args['ctx'].topicidx() + return args[b'ctx'].topicidx() def wrapinit(orig, self, repo, *args, **kwargs): orig(self, repo, *args, **kwargs) if not hastopicext(repo): return if constants.extrakey not in self._extra: - if getattr(repo, 'currenttopic', ''): + if getattr(repo, 'currenttopic', b''): self._extra[constants.extrakey] = repo.currenttopic else: # Empty key will be dropped from extra by another hack at the changegroup level - self._extra[constants.extrakey] = '' + self._extra[constants.extrakey] = b'' def wrapadd(orig, cl, manifest, files, desc, transaction, p1, p2, user, date=None, extra=None, p1copies=None, p2copies=None, @@ -679,13 +679,13 @@ age = opts.get('age') if current and topic: - raise error.Abort(_("cannot use --current when setting a topic")) + raise error.Abort(_(b"cannot use --current when setting a topic")) if current and clear: - raise error.Abort(_("cannot use --current and --clear")) + raise error.Abort(_(b"cannot use --current and --clear")) if clear and topic: - raise error.Abort(_("cannot use --clear when setting a topic")) + raise error.Abort(_(b"cannot use --clear when setting a topic")) if age and topic: - raise error.Abort(_("cannot use --age while setting a topic")) + raise error.Abort(_(b"cannot use --age while setting a topic")) touchedrevs = set() if rev: @@ -694,50 +694,50 @@ if topic: topic = topic.strip() if not topic: - raise error.Abort(_("topic name cannot consist entirely of whitespaces")) + raise error.Abort(_(b"topic name cannot consist entirely of whitespaces")) # Have some restrictions on the topic name just like bookmark name - scmutil.checknewlabel(repo, topic, 'topic') + scmutil.checknewlabel(repo, topic, b'topic') rmatch = re.match(br'[-_.\w]+', topic) if not rmatch or rmatch.group(0) != topic: - helptxt = _("topic names can only consist of alphanumeric, '-'" - " '_' and '.' characters") - raise error.Abort(_("invalid topic name: '%s'") % topic, hint=helptxt) + helptxt = _(b"topic names can only consist of alphanumeric, '-'" + b" '_' and '.' characters") + raise error.Abort(_(b"invalid topic name: '%s'") % topic, hint=helptxt) if list: - ui.pager('topics') + ui.pager(b'topics') if clear or rev: - raise error.Abort(_("cannot use --clear or --rev with --list")) + raise error.Abort(_(b"cannot use --clear or --rev with --list")) if not topic: topic = repo.currenttopic if not topic: - raise error.Abort(_('no active topic to list')) + raise error.Abort(_(b'no active topic to list')) return stack.showstack(ui, repo, topic=topic, opts=pycompat.byteskwargs(opts)) if touchedrevs: if not obsolete.isenabled(repo, obsolete.createmarkersopt): - raise error.Abort(_('must have obsolete enabled to change topics')) + raise error.Abort(_(b'must have obsolete enabled to change topics')) if clear: topic = None elif opts.get('current'): topic = repo.currenttopic elif not topic: - raise error.Abort('changing topic requires a topic name or --clear') - if repo.revs('%ld and public()', touchedrevs): - raise error.Abort("can't change topic of a public change") + raise error.Abort(b'changing topic requires a topic name or --clear') + if repo.revs(b'%ld and public()', touchedrevs): + raise error.Abort(b"can't change topic of a public change") wl = lock = txn = None try: wl = repo.wlock() lock = repo.lock() - txn = repo.transaction('rewrite-topics') + txn = repo.transaction(b'rewrite-topics') rewrote = _changetopics(ui, repo, touchedrevs, topic) txn.close() if topic is None: - ui.status('cleared topic on %d changesets\n' % rewrote) + ui.status(b'cleared topic on %d changesets\n' % rewrote) else: - ui.status('changed topic on %d changesets to "%s"\n' % (rewrote, - topic)) + ui.status(b'changed topic on %d changesets to "%s"\n' % (rewrote, + topic)) finally: lockmod.release(txn, lock, wl) repo.invalidate() @@ -748,37 +748,37 @@ if ct: st = stack.stack(repo, topic=ct) if not st: - ui.status(_('clearing empty topic "%s"\n') % ct) + ui.status(_(b'clearing empty topic "%s"\n') % ct) return _changecurrenttopic(repo, None) if topic: if not ct: - ui.status(_('marked working directory as topic: %s\n') % topic) + ui.status(_(b'marked working directory as topic: %s\n') % topic) return _changecurrenttopic(repo, topic) - ui.pager('topics') + ui.pager(b'topics') # `hg topic --current` ret = 0 if current and not ct: - ui.write_err(_('no active topic\n')) + ui.write_err(_(b'no active topic\n')) ret = 1 elif current: - fm = ui.formatter('topic', pycompat.byteskwargs(opts)) - namemask = '%s\n' - label = 'topic.active' + fm = ui.formatter(b'topic', pycompat.byteskwargs(opts)) + namemask = b'%s\n' + label = b'topic.active' fm.startitem() - fm.write('topic', namemask, ct, label=label) + fm.write(b'topic', namemask, ct, label=label) fm.end() else: _listtopics(ui, repo, opts) return ret -@command('stack', [ - ('c', 'children', None, - _('display data about children outside of the stack')) +@command(b'stack', [ + (b'c', b'children', None, + _(b'display data about children outside of the stack')) ] + commands.formatteropts, - _('hg stack [TOPIC]')) -def cmdstack(ui, repo, topic='', **opts): + _(b'hg stack [TOPIC]')) +def cmdstack(ui, repo, topic=b'', **opts): """list all changesets in a topic and other information List the current topic by default. @@ -792,15 +792,15 @@ topic = repo.currenttopic if topic is None: branch = repo[None].branch() - ui.pager('stack') + ui.pager(b'stack') return stack.showstack(ui, repo, branch=branch, topic=topic, opts=pycompat.byteskwargs(opts)) -@command('debugcb|debugconvertbookmark', [ - ('b', 'bookmark', '', _('bookmark to convert to topic')), - ('', 'all', None, _('convert all bookmarks to topics')), +@command(b'debugcb|debugconvertbookmark', [ + (b'b', b'bookmark', b'', _(b'bookmark to convert to topic')), + (b'', b'all', None, _(b'convert all bookmarks to topics')), ], - _('[-b BOOKMARK] [--all]')) + _(b'[-b BOOKMARK] [--all]')) def debugconvertbookmark(ui, repo, **opts): """Converts a bookmark to a topic with the same name. """ @@ -809,9 +809,9 @@ convertall = opts.get('all') if convertall and bookmark: - raise error.Abort(_("cannot use '--all' and '-b' together")) + raise error.Abort(_(b"cannot use '--all' and '-b' together")) if not (convertall or bookmark): - raise error.Abort(_("you must specify either '--all' or '-b'")) + raise error.Abort(_(b"you must specify either '--all' or '-b'")) bmstore = repo._bookmarks @@ -836,12 +836,12 @@ try: node = bmstore[bookmark] except KeyError: - raise error.Abort(_("no such bookmark exists: '%s'") % bookmark) + raise error.Abort(_(b"no such bookmark exists: '%s'") % bookmark) revnum = repo[node].rev() if len(nodetobook[node]) > 1: - ui.status(_("skipping revision '%d' as it has multiple bookmarks " - "on it\n") % revnum) + ui.status(_(b"skipping revision '%d' as it has multiple bookmarks " + b"on it\n") % revnum) return targetrevs = _findconvertbmarktopic(repo, bookmark) if targetrevs: @@ -853,11 +853,11 @@ if revnum in skipped: continue if len(nodetobook[revnode]) > 1: - ui.status(_("skipping '%d' as it has multiple bookmarks on" - " it\n") % revnum) + ui.status(_(b"skipping '%d' as it has multiple bookmarks on" + b" it\n") % revnum) skipped.append(revnum) continue - if bmark == '@': + if bmark == b'@': continue targetrevs = _findconvertbmarktopic(repo, bmark) if targetrevs: @@ -865,7 +865,7 @@ if actions: try: - tr = repo.transaction('debugconvertbookmark') + tr = repo.transaction(b'debugconvertbookmark') for ((bmark, revnum), targetrevs) in sorted(actions.items()): _applyconvertbmarktopic(ui, repo, targetrevs, revnum, bmark, tr) tr.close() @@ -875,7 +875,7 @@ lockmod.release(lock, wlock) # inspired from mercurial.repair.stripbmrevset -CONVERTBOOKREVSET = """ +CONVERTBOOKREVSET = b""" not public() and ( ancestors(bookmark(%s)) and not ancestors( @@ -913,9 +913,9 @@ # changeset if rewrote == 0: return - ui.status(_('changed topic to "%s" on %d revisions\n') % (bmark, + ui.status(_(b'changed topic to "%s" on %d revisions\n') % (bmark, rewrote)) - ui.debug('removing bookmark "%s" from "%d"' % (bmark, old)) + ui.debug(b'removing bookmark "%s" from "%d"' % (bmark, old)) bookmarks.delete(repo, tr, [bmark]) def _changecurrenttopic(repo, newtopic): @@ -923,11 +923,11 @@ if newtopic: with repo.wlock(): - with repo.vfs.open('topic', 'w') as f: + with repo.vfs.open(b'topic', b'w') as f: f.write(newtopic) else: - if repo.vfs.exists('topic'): - repo.vfs.unlink('topic') + if repo.vfs.exists(b'topic'): + repo.vfs.unlink(b'topic') def _changetopics(ui, repo, revs, newtopic): """ Changes topic to newtopic of all the revisions in the revset and return @@ -946,8 +946,8 @@ except error.ManifestLookupError: return None fixedextra = dict(c.extra()) - ui.debug('old node id is %s\n' % node.hex(c.node())) - ui.debug('origextra: %r\n' % fixedextra) + ui.debug(b'old node id is %s\n' % node.hex(c.node())) + ui.debug(b'origextra: %r\n' % fixedextra) oldtopic = fixedextra.get(constants.extrakey, None) if oldtopic == newtopic: continue @@ -956,16 +956,16 @@ else: fixedextra[constants.extrakey] = newtopic fixedextra[constants.changekey] = c.hex() - if 'amend_source' in fixedextra: + if b'amend_source' in fixedextra: # TODO: right now the commitctx wrapper in # topicrepo overwrites the topic in extra if # amend_source is set to support 'hg commit # --amend'. Support for amend should be adjusted # to not be so invasive. - del fixedextra['amend_source'] - ui.debug('changing topic of %s from %s to %s\n' % ( - c, oldtopic or '<none>', newtopic or '<none>')) - ui.debug('fixedextra: %r\n' % fixedextra) + del fixedextra[b'amend_source'] + ui.debug(b'changing topic of %s from %s to %s\n' % ( + c, oldtopic or b'<none>', newtopic or b'<none>')) + ui.debug(b'fixedextra: %r\n' % fixedextra) # While changing topic of set of linear commits, make sure that # we base our commits on new parent rather than old parent which # was obsoleted while changing the topic @@ -986,18 +986,18 @@ # phase handling commitphase = c.phase() - overrides = {('phases', 'new-commit'): commitphase} - with repo.ui.configoverride(overrides, 'changetopic'): + overrides = {(b'phases', b'new-commit'): commitphase} + with repo.ui.configoverride(overrides, b'changetopic'): newnode = repo.commitctx(mc) successors[c.node()] = (newnode,) - ui.debug('new node id is %s\n' % node.hex(newnode)) + ui.debug(b'new node id is %s\n' % node.hex(newnode)) rewrote += 1 # create obsmarkers and move bookmarks # XXX we should be creating marker as we go instead of only at the end, # this makes the operations more modulars - scmutil.cleanupnodes(repo, successors, 'changetopics') + scmutil.cleanupnodes(repo, successors, b'changetopics') # move the working copy too wctx = repo[None] @@ -1009,12 +1009,12 @@ return rewrote def _listtopics(ui, repo, opts): - fm = ui.formatter('topics', pycompat.byteskwargs(opts)) + fm = ui.formatter(b'topics', pycompat.byteskwargs(opts)) activetopic = repo.currenttopic - namemask = '%s' + namemask = b'%s' if repo.topics: maxwidth = max(len(t) for t in repo.topics) - namemask = '%%-%is' % maxwidth + namemask = b'%%-%is' % maxwidth if opts.get('age'): # here we sort by age and topic name topicsdata = sorted(_getlasttouched(repo, repo.topics)) @@ -1026,70 +1026,70 @@ ) for age, topic, date, user in topicsdata: fm.startitem() - marker = ' ' - label = 'topic' + marker = b' ' + label = b'topic' active = (topic == activetopic) if active: - marker = '*' - label = 'topic.active' + marker = b'*' + label = b'topic.active' if not ui.quiet: # registering the active data is made explicitly later - fm.plain(' %s ' % marker, label=label) - fm.write('topic', namemask, topic, label=label) + fm.plain(b' %s ' % marker, label=label) + fm.write(b'topic', namemask, topic, label=label) fm.data(active=active) if ui.quiet: - fm.plain('\n') + fm.plain(b'\n') continue - fm.plain(' (') + fm.plain(b' (') if date: if age == -1: - timestr = 'empty and active' + timestr = b'empty and active' else: timestr = templatefilters.age(date) - fm.write('lasttouched', '%s', timestr, label='topic.list.time') + fm.write(b'lasttouched', b'%s', timestr, label=b'topic.list.time') if user: - fm.write('usertouched', ' by %s', user, label='topic.list.user') + fm.write(b'usertouched', b' by %s', user, label=b'topic.list.user') if date: - fm.plain(', ') + fm.plain(b', ') data = stack.stack(repo, topic=topic) if ui.verbose: - fm.write('branches+', 'on branch: %s', - '+'.join(data.branches), # XXX use list directly after 4.0 is released - label='topic.list.branches') + fm.write(b'branches+', b'on branch: %s', + b'+'.join(data.branches), # XXX use list directly after 4.0 is released + label=b'topic.list.branches') - fm.plain(', ') - fm.write('changesetcount', '%d changesets', data.changesetcount, - label='topic.list.changesetcount') + fm.plain(b', ') + fm.write(b'changesetcount', b'%d changesets', data.changesetcount, + label=b'topic.list.changesetcount') if data.unstablecount: - fm.plain(', ') - fm.write('unstablecount', '%d unstable', + fm.plain(b', ') + fm.write(b'unstablecount', b'%d unstable', data.unstablecount, - label='topic.list.unstablecount') + label=b'topic.list.unstablecount') headcount = len(data.heads) if 1 < headcount: - fm.plain(', ') - fm.write('headcount', '%d heads', + fm.plain(b', ') + fm.write(b'headcount', b'%d heads', headcount, - label='topic.list.headcount.multiple') + label=b'topic.list.headcount.multiple') if ui.verbose: # XXX we should include the data even when not verbose behindcount = data.behindcount if 0 < behindcount: - fm.plain(', ') - fm.write('behindcount', '%d behind', + fm.plain(b', ') + fm.write(b'behindcount', b'%d behind', behindcount, - label='topic.list.behindcount') + label=b'topic.list.behindcount') elif -1 == behindcount: - fm.plain(', ') - fm.write('behinderror', '%s', - _('ambiguous destination: %s') % data.behinderror, - label='topic.list.behinderror') - fm.plain(')\n') + fm.plain(b', ') + fm.write(b'behinderror', b'%s', + _(b'ambiguous destination: %s') % data.behinderror, + label=b'topic.list.behinderror') + fm.plain(b')\n') fm.end() def _getlasttouched(repo, topics): @@ -1102,7 +1102,7 @@ age = -1 user = None maxtime = (0, 0) - trevs = repo.revs("topic(%s)", topic) + trevs = repo.revs(b"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 @@ -1119,7 +1119,7 @@ for marker in obsmarkers: rt = marker.date() if rt[0] > maxtime[0]: - user = marker.metadata().get('user', user) + user = marker.metadata().get(b'user', user) maxtime = rt username = stack.parseusername(user) @@ -1129,31 +1129,31 @@ yield (age, topic, maxtime, username) def summaryhook(ui, repo): - t = getattr(repo, 'currenttopic', '') + t = getattr(repo, 'currenttopic', b'') if not t: return # i18n: column positioning for "hg summary" - ui.write(_("topic: %s\n") % ui.label(t, 'topic.active')) + ui.write(_(b"topic: %s\n") % ui.label(t, b'topic.active')) _validmode = [ - 'ignore', - 'warning', - 'enforce', - 'enforce-all', - 'random', - 'random-all', + b'ignore', + b'warning', + b'enforce', + b'enforce-all', + b'random', + b'random-all', ] def _configtopicmode(ui): """ Parse the config to get the topicmode """ - topicmode = ui.config('experimental', 'topic-mode') + topicmode = ui.config(b'experimental', b'topic-mode') # Fallback to read enforce-topic if topicmode is None: - enforcetopic = ui.configbool('experimental', 'enforce-topic') + enforcetopic = ui.configbool(b'experimental', b'enforce-topic') if enforcetopic: - topicmode = "enforce" + topicmode = b"enforce" if topicmode not in _validmode: topicmode = _validmode[0] @@ -1167,37 +1167,37 @@ ismergecommit = len(repo[None].parents()) == 2 notopic = not repo.currenttopic - mayabort = (topicmode == "enforce" and not ismergecommit) - maywarn = (topicmode == "warning" - or (topicmode == "enforce" and ismergecommit)) + mayabort = (topicmode == b"enforce" and not ismergecommit) + maywarn = (topicmode == b"warning" + or (topicmode == b"enforce" and ismergecommit)) mayrandom = False - if topicmode == "random": + if topicmode == b"random": mayrandom = not ismergecommit - elif topicmode == "random-all": + elif topicmode == b"random-all": mayrandom = True - if topicmode == 'enforce-all': + if topicmode == b'enforce-all': ismergecommit = False mayabort = True maywarn = False - hint = _("see 'hg help -e topic.topic-mode' for details") + hint = _(b"see 'hg help -e topic.topic-mode' for details") if opts.get('topic'): t = opts['topic'] - with repo.vfs.open('topic', 'w') as f: + with repo.vfs.open(b'topic', b'w') as f: f.write(t) elif opts.get('amend'): pass elif notopic and mayabort: - msg = _("no active topic") + msg = _(b"no active topic") raise error.Abort(msg, hint=hint) elif notopic and maywarn: - ui.warn(_("warning: new draft commit without topic\n")) + ui.warn(_(b"warning: new draft commit without topic\n")) if not ui.quiet: - ui.warn(("(%s)\n") % hint) + ui.warn((b"(%s)\n") % hint) elif notopic and mayrandom: - with repo.vfs.open('topic', 'w') as f: + with repo.vfs.open(b'topic', b'w') as f: f.write(randomname.randomtopicname(ui)) return orig(ui, repo, *args, **opts) @@ -1206,13 +1206,13 @@ if hastopicext(repo): t = repo.currenttopic if t: - ret = ret.replace("\nHG: branch", - "\nHG: topic '%s'\nHG: branch" % t) + ret = ret.replace(b"\nHG: branch", + b"\nHG: topic '%s'\nHG: branch" % t) return ret def pushoutgoingwrap(orig, ui, repo, *args, **opts): if opts.get('topic'): - topicrevs = repo.revs('topic(%s) - obsolete()', opts['topic']) + topicrevs = repo.revs(b'topic(%s) - obsolete()', opts['topic']) opts.setdefault('rev', []).extend(topicrevs) return orig(ui, repo, *args, **opts) @@ -1232,37 +1232,37 @@ # rebased commit. We have explicitly stored in config if rebase is # running. ot = repo.currenttopic - if repo.ui.hasconfig('experimental', 'topicrebase'): + if repo.ui.hasconfig(b'experimental', b'topicrebase'): isrebase = True - if repo.ui.configbool('_internal', 'keep-topic'): + if repo.ui.configbool(b'_internal', b'keep-topic'): ist0 = True if ((not partial and not branchmerge) or isrebase) and not ist0: - t = '' + t = b'' pctx = repo[node] if pctx.phase() > phases.public: t = pctx.topic() - with repo.vfs.open('topic', 'w') as f: + with repo.vfs.open(b'topic', b'w') as f: f.write(t) if t and t != ot: - repo.ui.status(_("switching to topic %s\n") % t) + repo.ui.status(_(b"switching to topic %s\n") % t) if ot and not t: st = stack.stack(repo, topic=ot) if not st: - repo.ui.status(_('clearing empty topic "%s"\n') % ot) + repo.ui.status(_(b'clearing empty topic "%s"\n') % ot) elif ist0: - repo.ui.status(_("preserving the current topic '%s'\n") % ot) + repo.ui.status(_(b"preserving the current topic '%s'\n") % ot) return ret finally: wlock.release() def checkt0(orig, ui, repo, node=None, rev=None, *args, **kwargs): - thezeros = set(['t0', 'b0', 's0']) - backup = repo.ui.backupconfig('_internal', 'keep-topic') + thezeros = set([b't0', b'b0', b's0']) + backup = repo.ui.backupconfig(b'_internal', b'keep-topic') try: if node in thezeros or rev in thezeros: - repo.ui.setconfig('_internal', 'keep-topic', 'yes', - source='topic-extension') + repo.ui.setconfig(b'_internal', b'keep-topic', b'yes', + source=b'topic-extension') return orig(ui, repo, node=node, rev=rev, *args, **kwargs) finally: repo.ui.restoreconfig(backup) @@ -1276,8 +1276,8 @@ extra[constants.extrakey] = ctx.topic() def setrebaseconfig(orig, ui, repo, **opts): - repo.ui.setconfig('experimental', 'topicrebase', 'yes', - source='topic-extension') + repo.ui.setconfig(b'experimental', b'topicrebase', b'yes', + source=b'topic-extension') return orig(ui, repo, **opts) def new_init(orig, *args, **kwargs): @@ -1289,12 +1289,12 @@ return runtime try: - rebase = extensions.find("rebase") + rebase = extensions.find(b"rebase") extensions.wrapfunction(rebase.rebaseruntime, '__init__', new_init) # This exists to store in the config that rebase is running so that we can # update the topic according to rebase. This is a hack and should be removed # when we have better options. - extensions.wrapcommand(rebase.cmdtable, 'rebase', setrebaseconfig) + extensions.wrapcommand(rebase.cmdtable, b'rebase', setrebaseconfig) except KeyError: pass @@ -1303,20 +1303,20 @@ def _exporttopic(seq, ctx): topic = ctx.topic() if topic: - return 'EXP-Topic %s' % topic + return b'EXP-Topic %s' % topic return None def _importtopic(repo, patchdata, extra, opts): - if 'topic' in patchdata: - extra['topic'] = patchdata['topic'] + if b'topic' in patchdata: + extra[b'topic'] = patchdata[b'topic'] def setupimportexport(ui): """run at ui setup time to install import/export logic""" - cmdutil.extraexport.append('topic') - cmdutil.extraexportmap['topic'] = _exporttopic - cmdutil.extrapreimport.append('topic') - cmdutil.extrapreimportmap['topic'] = _importtopic - patch.patchheadermap.append(('EXP-Topic', 'topic')) + cmdutil.extraexport.append(b'topic') + cmdutil.extraexportmap[b'topic'] = _exporttopic + cmdutil.extrapreimport.append(b'topic') + cmdutil.extrapreimportmap[b'topic'] = _importtopic + patch.patchheadermap.append((b'EXP-Topic', b'topic')) ## preserve topic during split
--- a/hgext3rd/topic/compat.py Mon Jul 29 14:43:15 2019 +0200 +++ b/hgext3rd/topic/compat.py Fri Sep 27 13:03:18 2019 +0200 @@ -30,5 +30,7 @@ def branchmapitems(branchmap): return branchmap.items() else: + # py3-transform: off def branchmapitems(branchmap): return branchmap.iteritems() + # py3-transform: on
--- a/hgext3rd/topic/constants.py Mon Jul 29 14:43:15 2019 +0200 +++ b/hgext3rd/topic/constants.py Fri Sep 27 13:03:18 2019 +0200 @@ -1,2 +1,2 @@ -extrakey = 'topic' -changekey = '_rewrite_noise' +extrakey = b'topic' +changekey = b'_rewrite_noise'
--- a/hgext3rd/topic/destination.py Mon Jul 29 14:43:15 2019 +0200 +++ b/hgext3rd/topic/destination.py Fri Sep 27 13:03:18 2019 +0200 @@ -13,11 +13,11 @@ ) from .evolvebits import builddependencies -def _destmergebranch(orig, repo, action='merge', sourceset=None, +def _destmergebranch(orig, repo, action=b'merge', sourceset=None, onheadcheck=True, destspace=None): # XXX: take destspace into account if sourceset is None: - p1 = repo['.'] + p1 = repo[b'.'] else: # XXX: using only the max here is flacky. That code should eventually # be updated to take care of the whole sourceset. @@ -26,11 +26,11 @@ if common.hastopicext(repo): top = p1.topic() if top: - revs = repo.revs('topic(%s) - obsolete()', top) + revs = repo.revs(b'topic(%s) - obsolete()', top) deps, rdeps = builddependencies(repo, revs) heads = [r for r in revs if not rdeps[r]] if onheadcheck and p1.rev() not in heads: - raise error.Abort(_("not at topic head, update or explicit")) + raise error.Abort(_(b"not at topic head, update or explicit")) # prune heads above the source otherheads = set(heads) @@ -43,20 +43,20 @@ # nothing to do at the topic level bhead = ngtip(repo, p1.branch(), all=True) if not bhead: - raise error.NoMergeDestAbort(_("nothing to merge")) + raise error.NoMergeDestAbort(_(b"nothing to merge")) elif 1 == len(bhead): return bhead[0] else: - msg = _("branch '%s' has %d heads " - "- please merge with an explicit rev") - hint = _("run 'hg heads .' to see heads") + msg = _(b"branch '%s' has %d heads " + b"- please merge with an explicit rev") + hint = _(b"run 'hg heads .' to see heads") raise error.ManyMergeDestAbort(msg % (p1.branch(), len(bhead)), hint=hint) elif len(otherheads) == 1: return otherheads.pop() else: - msg = _("topic '%s' has %d heads " - "- please merge with an explicit rev") % (top, len(heads)) + msg = _(b"topic '%s' has %d heads " + b"- please merge with an explicit rev") % (top, len(heads)) raise error.ManyMergeDestAbort(msg) return orig(repo, action, sourceset, onheadcheck, destspace=destspace) @@ -67,23 +67,23 @@ movemark = node = None topic = repo.currenttopic if topic: - revs = repo.revs('.::topic(%s)', topic) + revs = repo.revs(b'.::topic(%s)', topic) else: revs = [] if not revs: return None, None, None node = revs.last() if bookmarks.isactivewdirparent(repo): - movemark = repo['.'].node() + movemark = repo[b'.'].node() return node, movemark, None def desthistedit(orig, ui, repo): if not common.hastopicext(repo): return None - if not (ui.config('histedit', 'defaultrev', None) is None + if not (ui.config(b'histedit', b'defaultrev', None) is None and repo.currenttopic): return orig(ui, repo) - revs = repo.revs('::. and stack()') + revs = repo.revs(b'::. and stack()') if revs: return revs.min() return None @@ -107,7 +107,7 @@ def modsetup(ui): """run a uisetup time to install all destinations wrapping""" extensions.wrapfunction(destutil, '_destmergebranch', _destmergebranch) - bridx = destutil.destupdatesteps.index('branch') - destutil.destupdatesteps.insert(bridx, 'topic') - destutil.destupdatestepmap['topic'] = _destupdatetopic + bridx = destutil.destupdatesteps.index(b'branch') + destutil.destupdatesteps.insert(bridx, b'topic') + destutil.destupdatestepmap[b'topic'] = _destupdatetopic extensions.wrapfunction(destutil, 'desthistedit', desthistedit)
--- a/hgext3rd/topic/discovery.py Mon Jul 29 14:43:15 2019 +0200 +++ b/hgext3rd/topic/discovery.py Fri Sep 27 13:03:18 2019 +0200 @@ -28,12 +28,12 @@ repo = pushop.repo.unfiltered() remote = pushop.remote - publishing = ('phases' not in remote.listkeys('namespaces') - or bool(remote.listkeys('phases').get('publishing', False))) + publishing = (b'phases' not in remote.listkeys(b'namespaces') + or bool(remote.listkeys(b'phases').get(b'publishing', False))) if not common.hastopicext(pushop.repo): return orig(pushop, *args, **kwargs) - elif ((publishing or not remote.capable('topics')) + elif ((publishing or not remote.capable(b'topics')) and not getattr(pushop, 'publish', False)): return orig(pushop, *args, **kwargs) @@ -41,7 +41,7 @@ remotebranchmap = None origremotebranchmap = remote.branchmap publishednode = [c.node() for c in pushop.outdatedphases] - publishedset = repo.revs('ancestors(%ln + %ln)', + publishedset = repo.revs(b'ancestors(%ln + %ln)', publishednode, pushop.remotephases.publicheads) @@ -51,17 +51,17 @@ # drop topic information from changeset about to be published result = collections.defaultdict(list) for branch, heads in compat.branchmapitems(origremotebranchmap()): - if ':' not in branch: + if b':' not in branch: result[branch].extend(heads) else: - namedbranch = branch.split(':', 1)[0] + namedbranch = branch.split(b':', 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(): + for heads in result.values(): heads.sort() return result @@ -78,7 +78,7 @@ return branch topic = ctx.topic() if topic: - branch = "%s:%s" % (branch, topic) + branch = b"%s:%s" % (branch, topic) return branch ctx.branch = branch @@ -96,7 +96,7 @@ return branch, close topic = repo[rev].topic() if topic: - branch = "%s:%s" % (branch, topic) + branch = b"%s:%s" % (branch, topic) return branch, close rbc.branchinfo = branchinfo @@ -107,17 +107,17 @@ repo.__class__ = repocls if remotebranchmap is not None: remote.branchmap = remotebranchmap - unxx = repo.filtered('unfiltered-topic') + unxx = repo.filtered(b'unfiltered-topic') repo.unfiltered = lambda: unxx pushop.repo = repo summary = orig(pushop) for key, value in summary.items(): - if ':' in key: # This is a topic + if b':' in key: # This is a topic if value[0] is None and value[1]: summary[key] = ([value[1][0]], ) + value[1:] return summary finally: - if 'unfiltered' in vars(repo): + if r'unfiltered' in vars(repo): del repo.unfiltered repo.__class__ = oldrepocls if remotebranchmap is not None: @@ -147,7 +147,7 @@ def _nbheads(repo): data = {} for b in repo.branchmap().iterbranches(): - if ':' in b[0]: + if b':' in b[0]: continue data[b[0]] = len(b[1]) return data @@ -158,7 +158,7 @@ if not common.hastopicext(op.repo) or op.repo.publishing(): return tr = op.gettransaction() - if tr.hookargs['source'] not in ('push', 'serve'): # not a push + if tr.hookargs[b'source'] not in (b'push', b'serve'): # not a push return tr._prepushheads = _nbheads(op.repo) reporef = weakref.ref(op.repo) @@ -175,11 +175,11 @@ for branch, oldnb in tr._prepushheads.items(): newnb = finalheads.pop(branch, 0) if oldnb < newnb: - msg = _('push create a new head on branch "%s"' % branch) + msg = _(b'push create a new head on branch "%s"' % branch) raise error.Abort(msg) for branch, newnb in finalheads.items(): if 1 < newnb: - msg = _('push create more than 1 head on new branch "%s"' + msg = _(b'push create more than 1 head on new branch "%s"' % branch) raise error.Abort(msg) return oldvalidator(tr) @@ -191,7 +191,7 @@ def _pushb2phases(orig, pushop, bundler): if common.hastopicext(pushop.repo): - checktypes = ('check:heads', 'check:updated-heads') + checktypes = (b'check:heads', b'check:updated-heads') hascheck = any(p.type in checktypes for p in bundler._parts) if not hascheck and pushop.outdatedphases: exchange._pushb2ctxcheckheads(pushop, bundler) @@ -199,8 +199,8 @@ def wireprotocaps(orig, repo, proto): caps = orig(repo, proto) - if common.hastopicext(repo) and repo.peer().capable('topics'): - caps.append('topics') + if common.hastopicext(repo) and repo.peer().capable(b'topics'): + caps.append(b'topics') return caps def modsetup(ui): @@ -211,11 +211,11 @@ # we need a proper wrap b2 part stuff extensions.wrapfunction(bundle2, 'handlecheckheads', handlecheckheads) bundle2.handlecheckheads.params = frozenset() - bundle2.parthandlermapping['check:heads'] = bundle2.handlecheckheads + bundle2.parthandlermapping[b'check:heads'] = bundle2.handlecheckheads if util.safehasattr(bundle2, 'handlecheckupdatedheads'): # we still need a proper wrap b2 part stuff extensions.wrapfunction(bundle2, 'handlecheckupdatedheads', handlecheckheads) bundle2.handlecheckupdatedheads.params = frozenset() - bundle2.parthandlermapping['check:updated-heads'] = bundle2.handlecheckupdatedheads + bundle2.parthandlermapping[b'check:updated-heads'] = bundle2.handlecheckupdatedheads extensions.wrapfunction(exchange, '_pushb2phases', _pushb2phases) - exchange.b2partsgenmapping['phase'] = exchange._pushb2phases + exchange.b2partsgenmapping[b'phase'] = exchange._pushb2phases
--- a/hgext3rd/topic/evolvebits.py Mon Jul 29 14:43:15 2019 +0200 +++ b/hgext3rd/topic/evolvebits.py Fri Sep 27 13:03:18 2019 +0200 @@ -78,8 +78,8 @@ newer = obsutil.successorssets(repo, obs.node()) # search of a parent which is not killed while not newer: - ui.debug("stabilize target %s is plain dead," - " trying to stabilize on its parent\n" % + ui.debug(b"stabilize target %s is plain dead," + b" trying to stabilize on its parent\n" % obs) obs = obs.parents()[0] newer = obsutil.successorssets(repo, obs.node()) @@ -88,7 +88,7 @@ # we should pick as arbitrary one raise MultipleSuccessorsError(newer) elif 1 < len(newer[0]): - splitheads = list(repo.revs('heads(%ln::%ln)', newer[0], newer[0])) + splitheads = list(repo.revs(b'heads(%ln::%ln)', newer[0], newer[0])) if 1 < len(splitheads): # split case, See if we can make sense of it. raise MultipleSuccessorsError(newer)
--- a/hgext3rd/topic/flow.py Mon Jul 29 14:43:15 2019 +0200 +++ b/hgext3rd/topic/flow.py Fri Sep 27 13:03:18 2019 +0200 @@ -16,19 +16,19 @@ ) def enforcesinglehead(repo, tr): - branchmap = repo.filtered('visible').branchmap() + branchmap = repo.filtered(b'visible').branchmap() for name, heads in compat.branchmapitems(branchmap): if len(heads) > 1: hexs = [node.short(n) for n in heads] - raise error.Abort(_('%d heads on "%s"') % (len(heads), name), - hint=(', '.join(hexs))) + raise error.Abort(_(b'%d heads on "%s"') % (len(heads), name), + hint=(b', '.join(hexs))) def publishbarebranch(repo, tr): """Publish changeset without topic""" - if 'node' not in tr.hookargs: # no new node + if b'node' not in tr.hookargs: # no new node return - startnode = node.bin(tr.hookargs['node']) - topublish = repo.revs('not public() and (%n:) - hidden() - topic()', startnode) + startnode = node.bin(tr.hookargs[b'node']) + topublish = repo.revs(b'not public() and (%n:) - hidden() - topic()', startnode) if topublish: cl = repo.changelog nodes = [cl.node(r) for r in topublish] @@ -36,41 +36,41 @@ def rejectuntopicedchangeset(repo, tr): """Reject the push if there are changeset without topic""" - if 'node' not in tr.hookargs: # no new revs + if b'node' not in tr.hookargs: # no new revs return - startnode = node.bin(tr.hookargs['node']) + startnode = node.bin(tr.hookargs[b'node']) - mode = repo.ui.config('experimental', 'topic-mode.server', 'ignore') + mode = repo.ui.config(b'experimental', b'topic-mode.server', b'ignore') - untopiced = repo.revs('not public() and (%n:) - hidden() - topic()', startnode) + untopiced = repo.revs(b'not public() and (%n:) - hidden() - topic()', startnode) if untopiced: num = len(untopiced) fnode = repo[untopiced.first()].hex()[:10] if num == 1: - msg = _("%s") % fnode + msg = _(b"%s") % fnode else: - msg = _("%s and %d more") % (fnode, num - 1) - if mode == 'warning': - fullmsg = _("pushed draft changeset without topic: %s\n") + msg = _(b"%s and %d more") % (fnode, num - 1) + if mode == b'warning': + fullmsg = _(b"pushed draft changeset without topic: %s\n") repo.ui.warn(fullmsg % msg) - elif mode == 'enforce': - fullmsg = _("rejecting draft changesets: %s") + elif mode == b'enforce': + fullmsg = _(b"rejecting draft changesets: %s") raise error.Abort(fullmsg % msg) else: - repo.ui.warn(_("unknown 'topic-mode.server': %s\n" % mode)) + repo.ui.warn(_(b"unknown 'topic-mode.server': %s\n" % mode)) def reject_publish(repo, tr): """prevent a transaction to be publish anything""" published = set() - for r, (o, n) in tr.changes['phases'].items(): + for r, (o, n) in tr.changes[b'phases'].items(): if n == phases.public: published.add(r) if published: r = min(published) - msg = "rejecting publishing of changeset %s" % repo[r] + msg = b"rejecting publishing of changeset %s" % repo[r] if len(published) > 1: - msg += ' and %d others' % (len(published) - 1) + msg += b' and %d others' % (len(published) - 1) raise error.Abort(msg) def wrappush(orig, repo, remote, *args, **kwargs): @@ -80,8 +80,8 @@ opargs = kwargs.get('opargs') if opargs is None: opargs = {} - newargs['opargs'] = opargs.copy() - newargs['opargs']['publish'] = True + newargs[r'opargs'] = opargs.copy() + newargs[r'opargs'][b'publish'] = True return orig(repo, remote, *args, **newargs) def extendpushoperation(orig, self, *args, **kwargs): @@ -95,16 +95,16 @@ if not pushop.remotephases.publishing: unfi = pushop.repo.unfiltered() droots = pushop.remotephases.draftroots - revset = '%ln and (not public() or %ln::)' + revset = b'%ln and (not public() or %ln::)' future = list(unfi.set(revset, pushop.futureheads, droots)) pushop.outdatedphases = future def installpushflag(ui): - entry = extensions.wrapcommand(commands.table, 'push', wrappush) - 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'))) + entry = extensions.wrapcommand(commands.table, b'push', wrappush) + if not any(opt for opt in entry[1] if opt[1] == b'publish'): # hg <= 4.9 + entry[1].append((b'', b'publish', False, + _(b'push the changeset as public'))) extensions.wrapfunction(exchange.pushoperation, '__init__', extendpushoperation) extensions.wrapfunction(exchange, '_pushdiscoveryphase', wrapphasediscovery) - exchange.pushdiscoverymapping['phase'] = exchange._pushdiscoveryphase + exchange.pushdiscoverymapping[b'phase'] = exchange._pushdiscoveryphase
--- a/hgext3rd/topic/randomname.py Mon Jul 29 14:43:15 2019 +0200 +++ b/hgext3rd/topic/randomname.py Fri Sep 27 13:03:18 2019 +0200 @@ -8,1006 +8,1007 @@ import random animals = [ - 'aardvark', - 'albatross', - 'alligator', - 'alpaca', - 'ant', - 'anteater', - 'antelope', - 'ape', - 'armadillo', - 'baboon', - 'badger', - 'barracuda', - 'bat', - 'bear', - 'beaver', - 'bee', - 'beetle', - 'bison', - 'boar', - 'buffalo', - 'bushbaby', - 'bustard', - 'butterfly', - 'camel', - 'capuchin', - 'carabao', - 'caribou', - 'cat', - 'caterpillar', - 'cattle', - 'chameleon', - 'chamois', - 'cheetah', - 'chicken', - 'chimpanzee', - 'chinchilla', - 'chipmunk', - 'chough', - 'cicada', - 'clam', - 'cobra', - 'cockroach', - 'cod', - 'cormorant', - 'coyote', - 'crab', - 'crane', - 'cricket', - 'crocodile', - 'crow', - 'curlew', - 'deer', - 'dinosaur', - 'dog', - 'dogfish', - 'dolphin', - 'donkey', - 'dotterel', - 'dove', - 'dragon', - 'dragonfly', - 'duck', - 'dugong', - 'dunlin', - 'eagle', - 'echidna', - 'eel', - 'eland', - 'elephant', - 'elk', - 'emu', - 'falcon', - 'ferret', - 'finch', - 'fish', - 'flamingo', - 'fly', - 'fox', - 'frog', - 'gaur', - 'gazelle', - 'gecko', - 'gerbil', - 'giraffe', - 'gnat', - 'gnu', - 'goat', - 'goldfish', - 'goose', - 'gorilla', - 'goshawk', - 'grasshopper', - 'grouse', - 'guanaco', - 'guinea', - 'gull', - 'hamster', - 'hare', - 'hawk', - 'hedgehog', - 'heron', - 'herring', - 'hippopotamus', - 'hornet', - 'horse', - 'horsecrab', - 'hound', - 'hummingbird', - 'hyena', - 'hyrax', - 'ibex', - 'ibis', - 'iguana', - 'impala', - 'insect', - 'jackal', - 'jaguar', - 'jay', - 'jellyfish', - 'kangaroo', - 'koala', - 'kouprey', - 'kudu', - 'lapwing', - 'lark', - 'lemming', - 'lemur', - 'leopard', - 'lion', - 'lizard', - 'llama', - 'lobster', - 'locust', - 'loris', - 'louse', - 'lynx', - 'lyrebird', - 'magpie', - 'mallard', - 'mammoth', - 'manatee', - 'marten', - 'meerkat', - 'mink', - 'minnow', - 'mole', - 'mongoose', - 'monkey', - 'moose', - 'mosquito', - 'mouse', - 'mule', - 'muskrat', - 'narwhal', - 'newt', - 'nightingale', - 'numbat', - 'octopus', - 'okapi', - 'opossum', - 'oryx', - 'ostrich', - 'otter', - 'owl', - 'ox', - 'oyster', - 'panda', - 'panther', - 'parrot', - 'partridge', - 'peacock', - 'peafowl', - 'pelican', - 'penguin', - 'pheasant', - 'pig', - 'pigeon', - 'platypus', - 'pony', - 'porcupine', - 'porpoise', - 'puffin', - 'pug', - 'quagga', - 'quail', - 'quelea', - 'rabbit', - 'raccoon', - 'ram', - 'rat', - 'raven', - 'reindeer', - 'rhea', - 'rhinoceros', - 'rook', - 'ruff', - 'salamander', - 'salmon', - 'sambar', - 'sandpiper', - 'sardine', - 'scorpion', - 'seahorse', - 'seal', - 'serval', - 'shark', - 'sheep', - 'shrew', - 'shrimp', - 'skink', - 'skunk', - 'snail', - 'snake', - 'spider', - 'squid', - 'squirrel', - 'starling', - 'stinkbug', - 'stork', - 'swan', - 'tapir', - 'tarsier', - 'termite', - 'tern', - 'tiger', - 'toad', - 'trout', - 'turkey', - 'turtle', - 'unicorn', - 'viper', - 'vulture', - 'wallaby', - 'walrus', - 'wasp', - 'weasel', - 'whale', - 'wolf', - 'wolverine', - 'wombat', - 'woodchuck', - 'woodcock', - 'woodpecker', - 'worm', - 'wren', - 'yak', - 'zebra', - 'zorilla' + b'aardvark', + b'albatross', + b'alligator', + b'alpaca', + b'ant', + b'anteater', + b'antelope', + b'ape', + b'armadillo', + b'baboon', + b'badger', + b'barracuda', + b'bat', + b'bear', + b'beaver', + b'bee', + b'beetle', + b'bison', + b'boar', + b'buffalo', + b'bushbaby', + b'bustard', + b'butterfly', + b'camel', + b'capuchin', + b'carabao', + b'caribou', + b'cat', + b'caterpillar', + b'cattle', + b'chameleon', + b'chamois', + b'cheetah', + b'chicken', + b'chimpanzee', + b'chinchilla', + b'chipmunk', + b'chough', + b'cicada', + b'clam', + b'cobra', + b'cockroach', + b'cod', + b'cormorant', + b'coyote', + b'crab', + b'crane', + b'cricket', + b'crocodile', + b'crow', + b'curlew', + b'deer', + b'dinosaur', + b'dog', + b'dogfish', + b'dolphin', + b'donkey', + b'dotterel', + b'dove', + b'dragon', + b'dragonfly', + b'duck', + b'dugong', + b'dunlin', + b'eagle', + b'echidna', + b'eel', + b'eland', + b'elephant', + b'elk', + b'emu', + b'falcon', + b'ferret', + b'finch', + b'fish', + b'flamingo', + b'fly', + b'fox', + b'frog', + b'gaur', + b'gazelle', + b'gecko', + b'gerbil', + b'giraffe', + b'gnat', + b'gnu', + b'goat', + b'goldfish', + b'goose', + b'gorilla', + b'goshawk', + b'grasshopper', + b'grouse', + b'guanaco', + b'guinea', + b'gull', + b'hamster', + b'hare', + b'hawk', + b'hedgehog', + b'heron', + b'herring', + b'hippopotamus', + b'hornet', + b'horse', + b'horsecrab', + b'hound', + b'hummingbird', + b'hyena', + b'hyrax', + b'ibex', + b'ibis', + b'iguana', + b'impala', + b'insect', + b'jackal', + b'jaguar', + b'jay', + b'jellyfish', + b'kangaroo', + b'koala', + b'kouprey', + b'kudu', + b'lapwing', + b'lark', + b'lemming', + b'lemur', + b'leopard', + b'lion', + b'lizard', + b'llama', + b'lobster', + b'locust', + b'loris', + b'louse', + b'lynx', + b'lyrebird', + b'magpie', + b'mallard', + b'mammoth', + b'manatee', + b'marten', + b'meerkat', + b'mink', + b'minnow', + b'mole', + b'mongoose', + b'monkey', + b'moose', + b'mosquito', + b'mouse', + b'mule', + b'muskrat', + b'narwhal', + b'newt', + b'nightingale', + b'numbat', + b'octopus', + b'okapi', + b'opossum', + b'oryx', + b'ostrich', + b'otter', + b'owl', + b'ox', + b'oyster', + b'panda', + b'panther', + b'parrot', + b'partridge', + b'peacock', + b'peafowl', + b'pelican', + b'penguin', + b'pheasant', + b'pig', + b'pigeon', + b'platypus', + b'pony', + b'porcupine', + b'porpoise', + b'puffin', + b'pug', + b'quagga', + b'quail', + b'quelea', + b'rabbit', + b'raccoon', + b'ram', + b'rat', + b'raven', + b'reindeer', + b'rhea', + b'rhinoceros', + b'rook', + b'ruff', + b'salamander', + b'salmon', + b'sambar', + b'sandpiper', + b'sardine', + b'scorpion', + b'seahorse', + b'seal', + b'serval', + b'shark', + b'sheep', + b'shrew', + b'shrimp', + b'skink', + b'skunk', + b'snail', + b'snake', + b'spider', + b'squid', + b'squirrel', + b'starling', + b'stinkbug', + b'stork', + b'swan', + b'tapir', + b'tarsier', + b'termite', + b'tern', + b'tiger', + b'toad', + b'trout', + b'turkey', + b'turtle', + b'unicorn', + b'viper', + b'vulture', + b'wallaby', + b'walrus', + b'wasp', + b'weasel', + b'whale', + b'wolf', + b'wolverine', + b'wombat', + b'woodchuck', + b'woodcock', + b'woodpecker', + b'worm', + b'wren', + b'yak', + b'zebra', + b'zorilla' ] adjectives = [ - 'abiding', - 'abject', - 'ablaze', - 'able', - 'aboard', - 'abounding', - 'absorbed', - 'absorbing', - 'abstracted', - 'abundant', - 'acceptable', - 'accessible', - 'accurate', - 'acoustic', - 'adamant', - 'adaptable', - 'adhesive', - 'adjoining', - 'adorable', - 'adventurous', - 'affable', - 'affectionate', - 'agreeable', - 'alert', - 'alive', - 'alluring', - 'amazing', - 'ambiguous', - 'ambitious', - 'amiable', - 'amicable', - 'amused', - 'amusing', - 'ancient', - 'animated', - 'apricot', - 'aquatic', - 'arctic', - 'arenaceous', - 'aromatic', - 'aspiring', - 'assiduous', - 'assorted', - 'astonishing', - 'attractive', - 'auspicious', - 'automatic', - 'available', - 'average', - 'awake', - 'aware', - 'awesome', - 'axiomatic', - 'bashful', - 'bawdy', - 'beautiful', - 'beefy', - 'befitting', - 'beneficial', - 'benevolent', - 'bent', - 'best', - 'better', - 'bewildered', - 'bewitching', - 'big', - 'billowy', - 'bizarre', - 'black', - 'blithe', - 'blue', - 'blushing', - 'bouncy', - 'boundless', - 'brainy', - 'brash', - 'brave', - 'brawny', - 'brazen', - 'breezy', - 'brief', - 'bright', - 'brilliant', - 'broad', - 'brown', - 'bucolic', - 'bulky', - 'bumpy', - 'burgundy', - 'burly', - 'bustling', - 'busy', - 'calm', - 'capable', - 'capricious', - 'captivating', - 'carefree', - 'careful', - 'caring', - 'carrot', - 'ceaseless', - 'cerise', - 'certain', - 'challenging', - 'changeable', - 'charming', - 'cheerful', - 'chief', - 'chilly', - 'chipper', - 'classy', - 'clean', - 'clear', - 'clever', - 'cloudy', - 'coherent', - 'colorful', - 'colossal', - 'comfortable', - 'common', - 'communicative', - 'compassionate', - 'complete', - 'complex', - 'compulsive', - 'confused', - 'conscientious', - 'conscious', - 'conservative', - 'considerate', - 'convivial', - 'cooing', - 'cool', - 'cooperative', - 'coordinated', - 'courageous', - 'courteous', - 'crazy', - 'creative', - 'crispy', - 'crooked', - 'crowded', - 'cuddly', - 'cultured', - 'cunning', - 'curious', - 'curly', - 'curved', - 'curvy', - 'cut', - 'cute', - 'daily', - 'damp', - 'dapper', - 'dashing', - 'dazzling', - 'dear', - 'debonair', - 'decisive', - 'decorous', - 'deep', - 'defiant', - 'delicate', - 'delicious', - 'delighted', - 'delightful', - 'delirious', - 'descriptive', - 'detached', - 'detailed', - 'determined', - 'different', - 'diligent', - 'diminutive', - 'diplomatic', - 'discreet', - 'distinct', - 'distinctive', - 'dramatic', - 'dry', - 'dynamic', - 'dynamite', - 'eager', - 'early', - 'earthy', - 'easy', - 'easygoing', - 'eatable', - 'economic', - 'ecstatic', - 'educated', - 'efficacious', - 'efficient', - 'effortless', - 'eight', - 'elastic', - 'elated', - 'electric', - 'elegant', - 'elfin', - 'elite', - 'eminent', - 'emotional', - 'enchanted', - 'enchanting', - 'encouraging', - 'endless', - 'energetic', - 'enormous', - 'entertaining', - 'enthusiastic', - 'envious', - 'epicurean', - 'equable', - 'equal', - 'eternal', - 'ethereal', - 'evanescent', - 'even', - 'excellent', - 'excited', - 'exciting', - 'exclusive', - 'exotic', - 'expensive', - 'exquisite', - 'extroverted', - 'exuberant', - 'exultant', - 'fabulous', - 'fair', - 'faithful', - 'familiar', - 'famous', - 'fancy', - 'fantastic', - 'far', - 'fascinated', - 'fast', - 'fearless', - 'female', - 'fertile', - 'festive', - 'few', - 'fine', - 'first', - 'five', - 'fixed', - 'flamboyant', - 'flashy', - 'flat', - 'flawless', - 'flirtatious', - 'florid', - 'flowery', - 'fluffy', - 'fluttering', - 'foamy', - 'foolish', - 'foregoing', - 'fortunate', - 'four', - 'frank', - 'free', - 'frequent', - 'fresh', - 'friendly', - 'full', - 'functional', - 'funny', - 'furry', - 'future', - 'futuristic', - 'fuzzy', - 'gabby', - 'gainful', - 'garrulous', - 'general', - 'generous', - 'gentle', - 'giant', - 'giddy', - 'gifted', - 'gigantic', - 'gilded', - 'glamorous', - 'gleaming', - 'glorious', - 'glossy', - 'glowing', - 'godly', - 'good', - 'goofy', - 'gorgeous', - 'graceful', - 'grandiose', - 'grateful', - 'gratis', - 'gray', - 'great', - 'green', - 'gregarious', - 'grey', - 'groovy', - 'guiltless', - 'gusty', - 'guttural', - 'habitual', - 'half', - 'hallowed', - 'halting', - 'handsome', - 'happy', - 'hard', - 'hardworking', - 'harmonious', - 'heady', - 'healthy', - 'heavenly', - 'helpful', - 'hilarious', - 'historical', - 'holistic', - 'hollow', - 'honest', - 'honorable', - 'hopeful', - 'hospitable', - 'hot', - 'huge', - 'humorous', - 'hungry', - 'hushed', - 'hypnotic', - 'illustrious', - 'imaginary', - 'imaginative', - 'immense', - 'imminent', - 'impartial', - 'important', - 'imported', - 'impossible', - 'incandescent', - 'inconclusive', - 'incredible', - 'independent', - 'industrious', - 'inexpensive', - 'innate', - 'innocent', - 'inquisitive', - 'instinctive', - 'intellectual', - 'intelligent', - 'intense', - 'interesting', - 'internal', - 'intuitive', - 'inventive', - 'invincible', - 'jazzy', - 'jolly', - 'joyful', - 'joyous', - 'judicious', - 'juicy', - 'jumpy', - 'keen', - 'kind', - 'kindhearted', - 'kindly', - 'knotty', - 'knowing', - 'knowledgeable', - 'known', - 'laconic', - 'large', - 'lavish', - 'lean', - 'learned', - 'left', - 'legal', - 'level', - 'light', - 'likeable', - 'literate', - 'little', - 'lively', - 'living', - 'long', - 'longing', - 'loud', - 'lovely', - 'loving', - 'loyal', - 'lucky', - 'luminous', - 'lush', - 'luxuriant', - 'luxurious', - 'lyrical', - 'magenta', - 'magical', - 'magnificent', - 'majestic', - 'male', - 'mammoth', - 'many', - 'marvelous', - 'massive', - 'material', - 'mature', - 'meandering', - 'meaty', - 'medical', - 'mellow', - 'melodic', - 'melted', - 'merciful', - 'mighty', - 'miniature', - 'miniscule', - 'minor', - 'minute', - 'misty', - 'modern', - 'modest', - 'momentous', - 'motionless', - 'mountainous', - 'mute', - 'mysterious', - 'narrow', - 'natural', - 'near', - 'neat', - 'nebulous', - 'necessary', - 'neighborly', - 'new', - 'next', - 'nice', - 'nifty', - 'nimble', - 'nine', - 'nippy', - 'noiseless', - 'noisy', - 'nonchalant', - 'normal', - 'numberless', - 'numerous', - 'nutritious', - 'obedient', - 'observant', - 'obtainable', - 'oceanic', - 'omniscient', - 'one', - 'open', - 'opposite', - 'optimal', - 'optimistic', - 'opulent', - 'orange', - 'ordinary', - 'organic', - 'outgoing', - 'outrageous', - 'outstanding', - 'oval', - 'overjoyed', - 'overt', - 'palatial', - 'panoramic', - 'parallel', - 'passionate', - 'past', - 'pastoral', - 'patient', - 'peaceful', - 'perfect', - 'periodic', - 'permissible', - 'perpetual', - 'persistent', - 'petite', - 'philosophical', - 'physical', - 'picturesque', - 'pink', - 'pioneering', - 'piquant', - 'plausible', - 'pleasant', - 'plucky', - 'poised', - 'polite', - 'possible', - 'powerful', - 'practical', - 'precious', - 'premium', - 'present', - 'pretty', - 'previous', - 'private', - 'probable', - 'productive', - 'profound', - 'profuse', - 'protective', - 'proud', - 'psychedelic', - 'public', - 'pumped', - 'purple', - 'purring', - 'puzzled', - 'puzzling', - 'quaint', - 'quick', - 'quicker', - 'quickest', - 'quiet', - 'quirky', - 'quixotic', - 'quizzical', - 'rainy', - 'rapid', - 'rare', - 'rational', - 'ready', - 'real', - 'rebel', - 'receptive', - 'red', - 'reflective', - 'regular', - 'relaxed', - 'reliable', - 'relieved', - 'remarkable', - 'reminiscent', - 'reserved', - 'resolute', - 'resonant', - 'resourceful', - 'responsible', - 'rich', - 'ridiculous', - 'right', - 'rightful', - 'ripe', - 'ritzy', - 'roasted', - 'robust', - 'romantic', - 'roomy', - 'round', - 'royal', - 'ruddy', - 'rural', - 'rustic', - 'sable', - 'safe', - 'salty', - 'same', - 'satisfying', - 'savory', - 'scientific', - 'scintillating', - 'scrumptious', - 'second', - 'secret', - 'secretive', - 'seemly', - 'selective', - 'sensible', - 'separate', - 'shaggy', - 'shaky', - 'shining', - 'shiny', - 'short', - 'shy', - 'silent', - 'silky', - 'silly', - 'simple', - 'simplistic', - 'sincere', - 'six', - 'sizzling', - 'skillful', - 'sleepy', - 'slick', - 'slim', - 'smart', - 'smiling', - 'smooth', - 'soaring', - 'sociable', - 'soft', - 'solid', - 'sophisticated', - 'sparkling', - 'special', - 'spectacular', - 'speedy', - 'spicy', - 'spiffy', - 'spiritual', - 'splendid', - 'spooky', - 'spotless', - 'spotted', - 'square', - 'standing', - 'statuesque', - 'steadfast', - 'steady', - 'steep', - 'stimulating', - 'straight', - 'straightforward', - 'striking', - 'striped', - 'strong', - 'stunning', - 'stupendous', - 'sturdy', - 'subsequent', - 'substantial', - 'subtle', - 'successful', - 'succinct', - 'sudden', - 'super', - 'superb', - 'supreme', - 'swanky', - 'sweet', - 'swift', - 'sympathetic', - 'synonymous', - 'talented', - 'tall', - 'tame', - 'tan', - 'tangible', - 'tangy', - 'tasteful', - 'tasty', - 'telling', - 'temporary', - 'tempting', - 'ten', - 'tender', - 'terrific', - 'tested', - 'thankful', - 'therapeutic', - 'thin', - 'thinkable', - 'third', - 'thoughtful', - 'three', - 'thrifty', - 'tidy', - 'tiny', - 'toothsome', - 'towering', - 'tranquil', - 'tremendous', - 'tricky', - 'true', - 'truthful', - 'two', - 'typical', - 'ubiquitous', - 'ultra', - 'unassuming', - 'unbiased', - 'uncovered', - 'understanding', - 'understood', - 'unequaled', - 'unique', - 'unusual', - 'unwritten', - 'upbeat', - 'useful', - 'utopian', - 'utter', - 'uttermost', - 'valuable', - 'various', - 'vast', - 'verdant', - 'vermilion', - 'versatile', - 'versed', - 'victorious', - 'vigorous', - 'violet', - 'vivacious', - 'voiceless', - 'voluptuous', - 'wacky', - 'waiting', - 'wakeful', - 'wandering', - 'warm', - 'warmhearted', - 'wealthy', - 'whimsical', - 'whispering', - 'white', - 'whole', - 'wholesale', - 'whopping', - 'wide', - 'wiggly', - 'wild', - 'willing', - 'windy', - 'winsome', - 'wiry', - 'wise', - 'wistful', - 'witty', - 'womanly', - 'wonderful', - 'workable', - 'young', - 'youthful', - 'yummy', - 'zany', - 'zealous', - 'zesty', - 'zippy' + b'abiding', + b'abject', + b'ablaze', + b'able', + b'aboard', + b'abounding', + b'absorbed', + b'absorbing', + b'abstracted', + b'abundant', + b'acceptable', + b'accessible', + b'accurate', + b'acoustic', + b'adamant', + b'adaptable', + b'adhesive', + b'adjoining', + b'adorable', + b'adventurous', + b'affable', + b'affectionate', + b'agreeable', + b'alert', + b'alive', + b'alluring', + b'amazing', + b'ambiguous', + b'ambitious', + b'amiable', + b'amicable', + b'amused', + b'amusing', + b'ancient', + b'animated', + b'apricot', + b'appropriate', + b'aquatic', + b'arctic', + b'arenaceous', + b'aromatic', + b'aspiring', + b'assiduous', + b'assorted', + b'astonishing', + b'attractive', + b'auspicious', + b'automatic', + b'available', + b'average', + b'awake', + b'aware', + b'awesome', + b'axiomatic', + b'bashful', + b'bawdy', + b'beautiful', + b'beefy', + b'befitting', + b'beneficial', + b'benevolent', + b'bent', + b'best', + b'better', + b'bewildered', + b'bewitching', + b'big', + b'billowy', + b'bizarre', + b'black', + b'blithe', + b'blue', + b'blushing', + b'bouncy', + b'boundless', + b'brainy', + b'brash', + b'brave', + b'brawny', + b'brazen', + b'breezy', + b'brief', + b'bright', + b'brilliant', + b'broad', + b'brown', + b'bucolic', + b'bulky', + b'bumpy', + b'burgundy', + b'burly', + b'bustling', + b'busy', + b'calm', + b'capable', + b'capricious', + b'captivating', + b'carefree', + b'careful', + b'caring', + b'carrot', + b'ceaseless', + b'cerise', + b'certain', + b'challenging', + b'changeable', + b'charming', + b'cheerful', + b'chief', + b'chilly', + b'chipper', + b'classy', + b'clean', + b'clear', + b'clever', + b'cloudy', + b'coherent', + b'colorful', + b'colossal', + b'comfortable', + b'common', + b'communicative', + b'compassionate', + b'complete', + b'complex', + b'compulsive', + b'confused', + b'conscientious', + b'conscious', + b'conservative', + b'considerate', + b'convivial', + b'cooing', + b'cool', + b'cooperative', + b'coordinated', + b'courageous', + b'courteous', + b'crazy', + b'creative', + b'crispy', + b'crooked', + b'crowded', + b'cuddly', + b'cultured', + b'cunning', + b'curious', + b'curly', + b'curved', + b'curvy', + b'cut', + b'cute', + b'daily', + b'damp', + b'dapper', + b'dashing', + b'dazzling', + b'dear', + b'debonair', + b'decisive', + b'decorous', + b'deep', + b'defiant', + b'delicate', + b'delicious', + b'delighted', + b'delightful', + b'delirious', + b'descriptive', + b'detached', + b'detailed', + b'determined', + b'different', + b'diligent', + b'diminutive', + b'diplomatic', + b'discreet', + b'distinct', + b'distinctive', + b'dramatic', + b'dry', + b'dynamic', + b'dynamite', + b'eager', + b'early', + b'earthy', + b'easy', + b'easygoing', + b'eatable', + b'economic', + b'ecstatic', + b'educated', + b'efficacious', + b'efficient', + b'effortless', + b'eight', + b'elastic', + b'elated', + b'electric', + b'elegant', + b'elfin', + b'elite', + b'eminent', + b'emotional', + b'enchanted', + b'enchanting', + b'encouraging', + b'endless', + b'energetic', + b'enormous', + b'entertaining', + b'enthusiastic', + b'envious', + b'epicurean', + b'equable', + b'equal', + b'eternal', + b'ethereal', + b'evanescent', + b'even', + b'excellent', + b'excited', + b'exciting', + b'exclusive', + b'exotic', + b'expensive', + b'exquisite', + b'extroverted', + b'exuberant', + b'exultant', + b'fabulous', + b'fair', + b'faithful', + b'familiar', + b'famous', + b'fancy', + b'fantastic', + b'far', + b'fascinated', + b'fast', + b'fearless', + b'female', + b'fertile', + b'festive', + b'few', + b'fine', + b'first', + b'five', + b'fixed', + b'flamboyant', + b'flashy', + b'flat', + b'flawless', + b'flirtatious', + b'florid', + b'flowery', + b'fluffy', + b'fluttering', + b'foamy', + b'foolish', + b'foregoing', + b'fortunate', + b'four', + b'frank', + b'free', + b'frequent', + b'fresh', + b'friendly', + b'full', + b'functional', + b'funny', + b'furry', + b'future', + b'futuristic', + b'fuzzy', + b'gabby', + b'gainful', + b'garrulous', + b'general', + b'generous', + b'gentle', + b'giant', + b'giddy', + b'gifted', + b'gigantic', + b'gilded', + b'glamorous', + b'gleaming', + b'glorious', + b'glossy', + b'glowing', + b'godly', + b'good', + b'goofy', + b'gorgeous', + b'graceful', + b'grandiose', + b'grateful', + b'gratis', + b'gray', + b'great', + b'green', + b'gregarious', + b'grey', + b'groovy', + b'guiltless', + b'gusty', + b'guttural', + b'habitual', + b'half', + b'hallowed', + b'halting', + b'handsome', + b'happy', + b'hard', + b'hardworking', + b'harmonious', + b'heady', + b'healthy', + b'heavenly', + b'helpful', + b'hilarious', + b'historical', + b'holistic', + b'hollow', + b'honest', + b'honorable', + b'hopeful', + b'hospitable', + b'hot', + b'huge', + b'humorous', + b'hungry', + b'hushed', + b'hypnotic', + b'illustrious', + b'imaginary', + b'imaginative', + b'immense', + b'imminent', + b'impartial', + b'important', + b'imported', + b'impossible', + b'incandescent', + b'inconclusive', + b'incredible', + b'independent', + b'industrious', + b'inexpensive', + b'innate', + b'innocent', + b'inquisitive', + b'instinctive', + b'intellectual', + b'intelligent', + b'intense', + b'interesting', + b'internal', + b'intuitive', + b'inventive', + b'invincible', + b'jazzy', + b'jolly', + b'joyful', + b'joyous', + b'judicious', + b'juicy', + b'jumpy', + b'keen', + b'kind', + b'kindhearted', + b'kindly', + b'knotty', + b'knowing', + b'knowledgeable', + b'known', + b'laconic', + b'large', + b'lavish', + b'lean', + b'learned', + b'left', + b'legal', + b'level', + b'light', + b'likeable', + b'literate', + b'little', + b'lively', + b'living', + b'long', + b'longing', + b'loud', + b'lovely', + b'loving', + b'loyal', + b'lucky', + b'luminous', + b'lush', + b'luxuriant', + b'luxurious', + b'lyrical', + b'magenta', + b'magical', + b'magnificent', + b'majestic', + b'male', + b'mammoth', + b'many', + b'marvelous', + b'massive', + b'material', + b'mature', + b'meandering', + b'meaty', + b'medical', + b'mellow', + b'melodic', + b'melted', + b'merciful', + b'mighty', + b'miniature', + b'miniscule', + b'minor', + b'minute', + b'misty', + b'modern', + b'modest', + b'momentous', + b'motionless', + b'mountainous', + b'mute', + b'mysterious', + b'narrow', + b'natural', + b'near', + b'neat', + b'nebulous', + b'necessary', + b'neighborly', + b'new', + b'next', + b'nice', + b'nifty', + b'nimble', + b'nine', + b'nippy', + b'noiseless', + b'noisy', + b'nonchalant', + b'normal', + b'numberless', + b'numerous', + b'nutritious', + b'obedient', + b'observant', + b'obtainable', + b'oceanic', + b'omniscient', + b'one', + b'open', + b'opposite', + b'optimal', + b'optimistic', + b'opulent', + b'orange', + b'ordinary', + b'organic', + b'outgoing', + b'outrageous', + b'outstanding', + b'oval', + b'overjoyed', + b'overt', + b'palatial', + b'panoramic', + b'parallel', + b'passionate', + b'past', + b'pastoral', + b'patient', + b'peaceful', + b'perfect', + b'periodic', + b'permissible', + b'perpetual', + b'persistent', + b'petite', + b'philosophical', + b'physical', + b'picturesque', + b'pink', + b'pioneering', + b'piquant', + b'plausible', + b'pleasant', + b'plucky', + b'poised', + b'polite', + b'possible', + b'powerful', + b'practical', + b'precious', + b'premium', + b'present', + b'pretty', + b'previous', + b'private', + b'probable', + b'productive', + b'profound', + b'profuse', + b'protective', + b'proud', + b'psychedelic', + b'public', + b'pumped', + b'purple', + b'purring', + b'puzzled', + b'puzzling', + b'quaint', + b'quick', + b'quicker', + b'quickest', + b'quiet', + b'quirky', + b'quixotic', + b'quizzical', + b'rainy', + b'rapid', + b'rare', + b'rational', + b'ready', + b'real', + b'rebel', + b'receptive', + b'red', + b'reflective', + b'regular', + b'relaxed', + b'reliable', + b'relieved', + b'remarkable', + b'reminiscent', + b'reserved', + b'resolute', + b'resonant', + b'resourceful', + b'responsible', + b'rich', + b'ridiculous', + b'right', + b'rightful', + b'ripe', + b'ritzy', + b'roasted', + b'robust', + b'romantic', + b'roomy', + b'round', + b'royal', + b'ruddy', + b'rural', + b'rustic', + b'sable', + b'safe', + b'salty', + b'same', + b'satisfying', + b'savory', + b'scientific', + b'scintillating', + b'scrumptious', + b'second', + b'secret', + b'secretive', + b'seemly', + b'selective', + b'sensible', + b'separate', + b'shaggy', + b'shaky', + b'shining', + b'shiny', + b'short', + b'shy', + b'silent', + b'silky', + b'silly', + b'simple', + b'simplistic', + b'sincere', + b'six', + b'sizzling', + b'skillful', + b'sleepy', + b'slick', + b'slim', + b'smart', + b'smiling', + b'smooth', + b'soaring', + b'sociable', + b'soft', + b'solid', + b'sophisticated', + b'sparkling', + b'special', + b'spectacular', + b'speedy', + b'spicy', + b'spiffy', + b'spiritual', + b'splendid', + b'spooky', + b'spotless', + b'spotted', + b'square', + b'standing', + b'statuesque', + b'steadfast', + b'steady', + b'steep', + b'stimulating', + b'straight', + b'straightforward', + b'striking', + b'striped', + b'strong', + b'stunning', + b'stupendous', + b'sturdy', + b'subsequent', + b'substantial', + b'subtle', + b'successful', + b'succinct', + b'sudden', + b'super', + b'superb', + b'supreme', + b'swanky', + b'sweet', + b'swift', + b'sympathetic', + b'synonymous', + b'talented', + b'tall', + b'tame', + b'tan', + b'tangible', + b'tangy', + b'tasteful', + b'tasty', + b'telling', + b'temporary', + b'tempting', + b'ten', + b'tender', + b'terrific', + b'tested', + b'thankful', + b'therapeutic', + b'thin', + b'thinkable', + b'third', + b'thoughtful', + b'three', + b'thrifty', + b'tidy', + b'tiny', + b'toothsome', + b'towering', + b'tranquil', + b'tremendous', + b'tricky', + b'true', + b'truthful', + b'two', + b'typical', + b'ubiquitous', + b'ultra', + b'unassuming', + b'unbiased', + b'uncovered', + b'understanding', + b'understood', + b'unequaled', + b'unique', + b'unusual', + b'unwritten', + b'upbeat', + b'useful', + b'utopian', + b'utter', + b'uttermost', + b'valuable', + b'various', + b'vast', + b'verdant', + b'vermilion', + b'versatile', + b'versed', + b'victorious', + b'vigorous', + b'violet', + b'vivacious', + b'voiceless', + b'voluptuous', + b'wacky', + b'waiting', + b'wakeful', + b'wandering', + b'warm', + b'warmhearted', + b'wealthy', + b'whimsical', + b'whispering', + b'white', + b'whole', + b'wholesale', + b'whopping', + b'wide', + b'wiggly', + b'wild', + b'willing', + b'windy', + b'winsome', + b'wiry', + b'wise', + b'wistful', + b'witty', + b'womanly', + b'wonderful', + b'workable', + b'young', + b'youthful', + b'yummy', + b'zany', + b'zealous', + b'zesty', + b'zippy' ] def randomtopicname(ui): # Re-implement random.choice() in the way it was written in Python 2. def choice(things): return things[int(len(things) * random.random())] - if ui.configint("devel", "randomseed"): - random.seed(ui.configint("devel", "randomseed")) - return choice(adjectives) + "-" + choice(animals) + if ui.configint(b"devel", b"randomseed"): + random.seed(ui.configint(b"devel", b"randomseed")) + return choice(adjectives) + b"-" + choice(animals)
--- a/hgext3rd/topic/revset.py Mon Jul 29 14:43:15 2019 +0200 +++ b/hgext3rd/topic/revset.py Fri Sep 27 13:03:18 2019 +0200 @@ -24,7 +24,7 @@ revsetpredicate = registrar.revsetpredicate() def getstringstrict(x, err): - if x and x[0] == 'string': + if x and x[0] == b'string': return x[1] raise error.ParseError(err) @@ -36,7 +36,7 @@ If `string` starts with `re:` the remainder of the name is treated as a regular expression. """ - args = revset.getargs(x, 0, 1, 'topic takes one or no arguments') + args = revset.getargs(x, 0, 1, b'topic takes one or no arguments') mutable = revset._notpublic(repo, revset.fullreposet(repo), ()) @@ -44,15 +44,15 @@ return (subset & mutable).filter(lambda r: bool(repo[r].topic())) try: - topic = getstringstrict(args[0], '') + topic = getstringstrict(args[0], b'') except error.ParseError: # not a string, but another revset pass else: kind, pattern, matcher = mkmatcher(topic) - if topic.startswith('literal:') and pattern not in repo.topics: - raise error.RepoLookupError("topic '%s' does not exist" % pattern) + if topic.startswith(b'literal:') and pattern not in repo.topics: + raise error.RepoLookupError(b"topic '%s' does not exist" % pattern) def matches(r): topic = repo[r].topic() @@ -64,7 +64,7 @@ s = revset.getset(repo, revset.fullreposet(repo), x) topics = {repo[r].topic() for r in s} - topics.discard('') + topics.discard(b'') def matches(r): if r in s: @@ -82,11 +82,11 @@ Name is horrible so that people change it. """ - args = revset.getargs(x, 1, 1, 'ngtip takes one argument') + args = revset.getargs(x, 1, 1, b'ngtip takes one argument') # match a specific topic - branch = revset.getstring(args[0], 'ngtip requires a string') - if branch == '.': - branch = repo['.'].branch() + branch = revset.getstring(args[0], b'ngtip requires a string') + if branch == b'.': + branch = repo[b'.'].branch() return subset & revset.baseset(destination.ngtip(repo, branch)) @revsetpredicate(b'stack()') @@ -97,7 +97,7 @@ unstable changeset after there future parent (as if evolve where already run). """ - err = 'stack takes no arguments, it works on current topic' + err = b'stack takes no arguments, it works on current topic' revset.getargs(x, 0, 0, err) topic = None branch = None @@ -120,8 +120,8 @@ if isinstance(z, tuple): a, b = revset.getintrange( z, - 'relation subscript must be an integer or a range', - 'relation subscript bounds must be integers', + b'relation subscript must be an integer or a range', + b'relation subscript bounds must be integers', None, None) else: a = b = z @@ -159,12 +159,12 @@ return subset & revset.baseset(revs) - revset.subscriptrelations['stack'] = stackrel - revset.subscriptrelations['s'] = stackrel + revset.subscriptrelations[b'stack'] = stackrel + revset.subscriptrelations[b's'] = stackrel def topicrel(repo, subset, x, *args): subset &= topicset(repo, subset, x) return revset.generationsrel(repo, subset, x, *args) - revset.subscriptrelations['topic'] = topicrel - revset.subscriptrelations['t'] = topicrel + revset.subscriptrelations[b'topic'] = topicrel + revset.subscriptrelations[b't'] = topicrel
--- a/hgext3rd/topic/stack.py Mon Jul 29 14:43:15 2019 +0200 +++ b/hgext3rd/topic/stack.py Fri Sep 27 13:03:18 2019 +0200 @@ -26,7 +26,7 @@ username = None if user: # user is of form "abc <abc@xyz.com>" - username = user.split('<')[0] + username = user.split(b'<')[0] if not username: # assuming user is of form "<abc@xyz.com>" if len(user) > 1: @@ -45,10 +45,10 @@ """ phasesets = repo._phasecache._phasesets if not phasesets or None in phasesets[phases.draft:]: - return repo.revs('(not public()) - obsolete()') + return repo.revs(b'(not public()) - obsolete()') result = set.union(*phasesets[phases.draft:]) - result -= obsolete.getrevs(repo, 'obsolete') + result -= obsolete.getrevs(repo, b'obsolete') return result class stack(object): @@ -63,13 +63,13 @@ subset = _stackcandidates(repo) if topic is not None and branch is not None: - raise error.ProgrammingError('both branch and topic specified (not defined yet)') + raise error.ProgrammingError(b'both branch and topic specified (not defined yet)') elif topic is not None: - trevs = repo.revs("%ld and topic(%s)", subset, topic) + trevs = repo.revs(b"%ld and topic(%s)", subset, topic) elif branch is not None: - trevs = repo.revs("%ld and branch(%s) - topic()", subset, branch) + trevs = repo.revs(b"%ld and branch(%s) - topic()", subset, branch) else: - raise error.ProgrammingError('neither branch and topic specified (not defined yet)') + raise error.ProgrammingError(b'neither branch and topic specified (not defined yet)') self._revs = trevs def __iter__(self): @@ -143,9 +143,18 @@ # processed dependency graph. # Step 1: compute relation of revision with each other - dependencies, rdependencies = self._dependencies - dependencies = dependencies.copy() - rdependencies = rdependencies.copy() + origdeps, rdependencies = self._dependencies + dependencies = {} + # Making a deep copy of origdeps because we modify contents of values + # later on. Checking for list here only because right now + # builddependencies in evolvebits.py can return a list of _succs() + # objects. When that will be dealt with, this deep copy code can be + # simplified a lot. + for k, v in origdeps.items(): + if isinstance(v, list): + dependencies[k] = [i.copy() for i in v] + else: + dependencies[k] = v.copy() # Step 2: Build the ordering # Remove the revisions with no dependency(A) and add them to the ordering. # Removing these revisions leads to new revisions with no dependency (the @@ -169,7 +178,7 @@ if revs: pt1 = self._repo[revs[0]].p1() else: - pt1 = self._repo['.'] + pt1 = self._repo[b'.'] if pt1.obsolete(): pt1 = self._repo[_singlesuccessor(self._repo, pt1)] @@ -197,15 +206,15 @@ if revs: minroot = [min(r for r in revs if not deps[r])] try: - dest = destutil.destmerge(self._repo, action='rebase', + dest = destutil.destmerge(self._repo, action=b'rebase', sourceset=minroot, onheadcheck=False) - return len(self._repo.revs("only(%d, %ld)", dest, minroot)) + return len(self._repo.revs(b"only(%d, %ld)", dest, minroot)) except error.NoMergeDestAbort: return 0 except error.ManyMergeDestAbort as exc: # XXX we should make it easier for upstream to provide the information - self.behinderror = pycompat.bytestr(exc).split('-', 1)[0].rstrip() + self.behinderror = pycompat.bytestr(exc).split(b'-', 1)[0].rstrip() return -1 return 0 @@ -217,68 +226,68 @@ return branches def labelsgen(prefix, parts): - fmt = prefix + '.%s' - return prefix + ' ' + ' '.join(fmt % p.replace(' ', '-') for p in parts) + fmt = prefix + b'.%s' + return prefix + b' ' + b' '.join(fmt % p.replace(b' ', b'-') for p in parts) def showstack(ui, repo, branch=None, topic=None, opts=None): if opts is None: opts = {} if topic is not None and branch is not None: - msg = 'both branch and topic specified [%s]{%s}(not defined yet)' + msg = b'both branch and topic specified [%s]{%s}(not defined yet)' msg %= (branch, topic) raise error.ProgrammingError(msg) elif topic is not None: - prefix = 's' + prefix = b's' if topic not in repo.topics: - raise error.Abort(_('cannot resolve "%s": no such topic found') % topic) + raise error.Abort(_(b'cannot resolve "%s": no such topic found') % topic) elif branch is not None: - prefix = 's' + prefix = b's' else: - raise error.ProgrammingError('neither branch and topic specified (not defined yet)') + raise error.ProgrammingError(b'neither branch and topic specified (not defined yet)') - fm = ui.formatter('topicstack', opts) + fm = ui.formatter(b'topicstack', opts) prev = None entries = [] idxmap = {} - label = 'topic' + label = b'topic' if topic == repo.currenttopic: - label = 'topic.active' + label = b'topic.active' st = stack(repo, branch, topic) if topic is not None: - fm.plain(_('### topic: %s') + fm.plain(_(b'### topic: %s') % ui.label(topic, label), - label='stack.summary.topic') + label=b'stack.summary.topic') if 1 < len(st.heads): - fm.plain(' (') - fm.plain('%d heads' % len(st.heads), - label='stack.summary.headcount.multiple') - fm.plain(')') - fm.plain('\n') - fm.plain(_('### target: %s (branch)') - % '+'.join(st.branches), # XXX handle multi branches - label='stack.summary.branches') + fm.plain(b' (') + fm.plain(b'%d heads' % len(st.heads), + label=b'stack.summary.headcount.multiple') + fm.plain(b')') + fm.plain(b'\n') + fm.plain(_(b'### target: %s (branch)') + % b'+'.join(st.branches), # XXX handle multi branches + label=b'stack.summary.branches') if topic is None: if 1 < len(st.heads): - fm.plain(' (') - fm.plain('%d heads' % len(st.heads), - label='stack.summary.headcount.multiple') - fm.plain(')') + fm.plain(b' (') + fm.plain(b'%d heads' % len(st.heads), + label=b'stack.summary.headcount.multiple') + fm.plain(b')') else: if st.behindcount == -1: - fm.plain(', ') - fm.plain('ambiguous rebase destination - %s' % st.behinderror, - label='stack.summary.behinderror') + fm.plain(b', ') + fm.plain(b'ambiguous rebase destination - %s' % st.behinderror, + label=b'stack.summary.behinderror') elif st.behindcount: - fm.plain(', ') - fm.plain('%d behind' % st.behindcount, label='stack.summary.behindcount') - fm.plain('\n') + fm.plain(b', ') + fm.plain(b'%d behind' % st.behindcount, label=b'stack.summary.behindcount') + fm.plain(b'\n') if not st: - fm.plain(_("(stack is empty)\n")) + fm.plain(_(b"(stack is empty)\n")) st = stack(repo, branch=branch, topic=topic) for idx, r in enumerate(st, 0): @@ -317,40 +326,40 @@ symbol = None states = [] - if opts.get('children'): - expr = 'children(%d) and merge() - %ld' + if opts.get(b'children'): + expr = b'children(%d) and merge() - %ld' revisions = repo.revs(expr, ctx.rev(), st._revs) if len(revisions) > 0: - states.append('external-children') + states.append(b'external-children') if ctx.orphan(): - symbol = '$' - states.append('orphan') + symbol = b'$' + states.append(b'orphan') if ctx.contentdivergent(): - symbol = '$' - states.append('content divergent') + symbol = b'$' + states.append(b'content divergent') if ctx.phasedivergent(): - symbol = '$' - states.append('phase divergent') + symbol = b'$' + states.append(b'phase divergent') - iscurrentrevision = repo.revs('%d and parents()', ctx.rev()) + iscurrentrevision = repo.revs(b'%d and parents()', ctx.rev()) if iscurrentrevision: - symbol = '@' - states.append('current') + symbol = b'@' + states.append(b'current') if not isentry: - symbol = '^' + symbol = b'^' # "base" is kind of a "ghost" entry - states.append('base') + states.append(b'base') # none of the above if statments get executed if not symbol: - symbol = ':' + symbol = b':' if not states: - states.append('clean') + states.append(b'clean') states.sort() @@ -368,22 +377,22 @@ spacewidth = 2 + 40 # s# alias width spacewidth += 2 - fm.plain(' ' * spacewidth) + fm.plain(b' ' * spacewidth) else: - fm.write('stack_index', '%s%%d' % prefix, idx, - label=labelsgen('stack.index', states)) + fm.write(b'stack_index', b'%s%%d' % prefix, idx, + label=labelsgen(b'stack.index', states)) if ui.verbose: - fm.write('node', '(%s)', fm.hexfunc(ctx.node()), - label=labelsgen('stack.shortnode', states)) + fm.write(b'node', b'(%s)', fm.hexfunc(ctx.node()), + label=labelsgen(b'stack.shortnode', states)) else: fm.data(node=fm.hexfunc(ctx.node())) - fm.write('symbol', '%s', symbol, - label=labelsgen('stack.state', states)) - fm.plain(' ') - fm.write('desc', '%s', ctx.description().splitlines()[0], - label=labelsgen('stack.desc', states)) - fm.condwrite(states != ['clean'] and idx is not None, 'state', - ' (%s)', fm.formatlist(states, 'stack.state'), - label=labelsgen('stack.state', states)) - fm.plain('\n') + fm.write(b'symbol', b'%s', symbol, + label=labelsgen(b'stack.state', states)) + fm.plain(b' ') + fm.write(b'desc', b'%s', ctx.description().splitlines()[0], + label=labelsgen(b'stack.desc', states)) + fm.condwrite(states != [b'clean'] and idx is not None, b'state', + b' (%s)', fm.formatlist(states, b'stack.state'), + label=labelsgen(b'stack.state', states)) + fm.plain(b'\n') fm.end()
--- a/hgext3rd/topic/topicmap.py Mon Jul 29 14:43:15 2019 +0200 +++ b/hgext3rd/topic/topicmap.py Fri Sep 27 13:03:18 2019 +0200 @@ -16,22 +16,22 @@ common, ) -basefilter = set(['base', 'immutable']) +basefilter = set([b'base', b'immutable']) def topicfilter(name): """return a "topic" version of a filter level""" if name in basefilter: return name elif name is None: return None - elif name.endswith('-topic'): + elif name.endswith(b'-topic'): return name else: - return name + '-topic' + return name + b'-topic' def istopicfilter(filtername): if filtername is None: return False - return filtername.endswith('-topic') + return filtername.endswith(b'-topic') def gettopicrepo(repo): if not common.hastopicext(repo): @@ -61,8 +61,8 @@ if newfilter not in funcmap: funcmap[newfilter] = revsfunc partialmap[newfilter] = base - funcmap['unfiltered-topic'] = lambda repo: frozenset() - partialmap['unfiltered-topic'] = 'visible-topic' + funcmap[b'unfiltered-topic'] = lambda repo: frozenset() + partialmap[b'unfiltered-topic'] = b'visible-topic' def _phaseshash(repo, maxrev): """uniq ID for a phase matching a set of rev""" @@ -80,7 +80,7 @@ if revs: s = hashlib.sha1() for rev in revs: - s.update('%d;' % rev) + s.update(b'%d;' % rev) key = s.digest() return key @@ -100,7 +100,7 @@ # wrap commit status use the topic branch heads ctx = repo[node] if ctx.topic() and ctx.branch() == branch: - bheads = repo.branchheads("%s:%s" % (branch, ctx.topic())) + bheads = repo.branchheads(b"%s:%s" % (branch, ctx.topic())) ret = orig(repo, node, branch, bheads=bheads, opts=opts) @@ -111,10 +111,10 @@ return ret parents = ctx.parents() - if (not opts.get('amend') and bheads and node not in bheads and not + if (not opts.get(b'amend') and bheads and node not in bheads and not [x for x in parents if x.node() in bheads and x.branch() == branch]): - repo.ui.status(_("(consider using topic for lightweight branches." - " See 'hg help topic')\n")) + repo.ui.status(_(b"(consider using topic for lightweight branches." + b" See 'hg help topic')\n")) return ret @@ -179,17 +179,17 @@ new.phaseshash = self.phaseshash return new - def branchtip(self, branch, topic=''): + def branchtip(self, branch, topic=b''): '''Return the tipmost open head on branch head, otherwise return the tipmost closed head on branch. Raise KeyError for unknown branch.''' if topic: - branch = '%s:%s' % (branch, topic) + branch = b'%s:%s' % (branch, topic) return super(_topiccache, self).branchtip(branch) - def branchheads(self, branch, closed=False, topic=''): + def branchheads(self, branch, closed=False, topic=b''): if topic: - branch = '%s:%s' % (branch, topic) + branch = b'%s:%s' % (branch, topic) return super(_topiccache, self).branchheads(branch, closed=closed) def validfor(self, repo): @@ -229,13 +229,13 @@ def branchinfo(r, changelog=None): info = oldgetbranchinfo(r) - topic = '' + topic = b'' ctx = unfi[r] if ctx.mutable(): topic = ctx.topic() branch = info[0] if topic: - branch = '%s:%s' % (branch, topic) + branch = b'%s:%s' % (branch, topic) return (branch, info[1]) try: unfi.revbranchcache().branchinfo = branchinfo
--- a/setup.py Mon Jul 29 14:43:15 2019 +0200 +++ b/setup.py Fri Sep 27 13:03:18 2019 +0200 @@ -7,7 +7,8 @@ def get_metadata(): meta = {} fullpath = join(dirname(__file__), META_PATH) - execfile(fullpath, meta) + with open(fullpath, 'r') as fp: + exec(fp.read(), meta) return meta def get_version(): @@ -28,6 +29,8 @@ 'hgext3rd.topic', ] +py_versions = '>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*, <4' + if os.environ.get('INCLUDE_INHIBIT'): py_modules.append('hgext3rd.evolve.hack.inhibit') py_modules.append('hgext3rd.evolve.hack.directaccess') @@ -45,5 +48,6 @@ keywords='hg mercurial', license='GPLv2+', py_modules=py_modules, - packages=py_packages + packages=py_packages, + python_requires=py_versions )
--- a/tests/test-evolve-abort-orphan.t Mon Jul 29 14:43:15 2019 +0200 +++ b/tests/test-evolve-abort-orphan.t Fri Sep 27 13:03:18 2019 +0200 @@ -19,6 +19,11 @@ > EOF $ echo "evolve=$(echo $(dirname $TESTDIR))/hgext3rd/evolve/" >> $HGRCPATH + $ cat >> $HGRCPATH <<EOF + > [alias] + > abort = evolve --abort + > EOF + $ hg init abortrepo $ cd abortrepo $ echo ".*\.orig" > .hgignore @@ -104,7 +109,7 @@ summary: added d - $ hg evolve --abort + $ hg abort evolve aborted working directory is now at e93a9161a274 @@ -199,7 +204,7 @@ o 0:8fa14d15e168 added hgignore () draft - $ hg evolve --abort + $ hg abort 1 new orphan changesets evolve aborted working directory is now at 125af0ed8cae @@ -299,7 +304,7 @@ o 0:8fa14d15e168 added hgignore () draft - $ hg evolve --abort + $ hg abort 2 new orphan changesets evolve aborted working directory is now at 807e8e2ca559 @@ -395,7 +400,7 @@ adding file changes added 1 changesets with 1 changes to 1 files $ cd ../repotwo - $ hg evolve --abort + $ hg abort warning: new changesets detected on destination branch abort: unable to abort interrupted evolve, use 'hg evolve --stop' to stop evolve [255] @@ -442,7 +447,7 @@ $ hg phase -r 1c476940790a --public - $ hg evolve --abort + $ hg abort cannot clean up public changesets: 1c476940790a abort: unable to abort interrupted evolve, use 'hg evolve --stop' to stop evolve [255] @@ -510,7 +515,7 @@ o 0:8fa14d15e168 added hgignore () draft - $ hg evolve --abort + $ hg abort 1 new orphan changesets evolve aborted working directory is now at a0086c17bfc7 @@ -543,7 +548,7 @@ (see 'hg help evolve.interrupted') [1] - $ hg evolve --abort + $ hg abort evolve aborted working directory is now at c1f4718020e3
--- a/tests/test-evolve-abort-phasediv.t Mon Jul 29 14:43:15 2019 +0200 +++ b/tests/test-evolve-abort-phasediv.t Fri Sep 27 13:03:18 2019 +0200 @@ -19,6 +19,11 @@ > EOF $ echo "evolve=$(echo $(dirname $TESTDIR))/hgext3rd/evolve/" >> $HGRCPATH + $ cat >> $HGRCPATH <<EOF + > [alias] + > abort = evolve --abort + > EOF + $ hg init abortrepo $ cd abortrepo $ echo ".*\.orig" > .hgignore @@ -124,7 +129,7 @@ summary: added d - $ hg evolve --abort + $ hg abort evolve aborted working directory is now at ddba58020bc0 @@ -218,7 +223,7 @@ (see 'hg help evolve.interrupted') [1] - $ hg evolve --abort + $ hg abort 1 new phase-divergent changesets evolve aborted working directory is now at 28cd06b3f801 @@ -304,7 +309,7 @@ (see 'hg help evolve.interrupted') [1] - $ hg evolve --abort + $ hg abort 1 new phase-divergent changesets evolve aborted working directory is now at ef9b72b9b42c
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-evolve-interrupted.t Fri Sep 27 13:03:18 2019 +0200 @@ -0,0 +1,149 @@ +Quitting an evolve in the middle (via ctrl-c or something) can leave things in a +weird intermediate state where hg thinks we're in the middle of an update +operation (or even just leave the 'merge' directory around without actually +indicating we're in the middle of *any* operation). + + $ . $TESTDIR/testlib/common.sh + + $ cat << EOF >> $HGRCPATH + > [extensions] + > rebase = + > evolve = + > [alias] + > l = log -G -T'{rev} {desc}' + > EOF + + $ hg init interrupted-orphan + $ cd interrupted-orphan + + $ echo apricot > a + $ hg ci -qAm apricot + + $ echo banana > b + $ hg ci -qAm banana + +Let's go back to amend 0 and make an orphan out of 1 (and a merge conflict to +test with) + + $ hg up -q 0 + $ echo blueberry > b + $ hg l + o 1 banana + | + @ 0 apricot + + $ hg ci --amend -qAm 'apricot and blueberry' + 1 new orphan changesets + $ hg l + @ 2 apricot and blueberry + + * 1 banana + | + x 0 apricot + + + $ HGMERGE=internal:other hg evolve --update --config hooks.precommit=false --config ui.merge=internal:other + move:[1] banana + atop:[2] apricot and blueberry + transaction abort! + rollback completed + abort: precommit hook exited with status 1 + [255] + $ hg l + @ 2 apricot and blueberry + + * 1 banana + | + x 0 apricot + + $ cat b + banana + + $ hg status --config commands.status.verbose=True + M b + # The repository is in an unfinished *evolve* state. + + # No unresolved merge conflicts. + + # To continue: hg evolve --continue + # To abort: hg evolve --abort + # To stop: hg evolve --stop + # (also see `hg help evolve.interrupted`) + + + $ ls .hg/evolvestate + .hg/evolvestate + + $ cat b + banana + + $ hg l + @ 2 apricot and blueberry + + * 1 banana + | + x 0 apricot + + +Test various methods of handling that unfinished state + $ hg evolve --abort + evolve aborted + working directory is now at e1989e4b1526 + $ ls .hg/evolvestate + ls: cannot access '?.hg/evolvestate'?: No such file or directory (re) + [2] + $ cat b + blueberry + $ hg l + @ 2 apricot and blueberry + + * 1 banana + | + x 0 apricot + + + $ HGMERGE=internal:other hg evolve --update --config hooks.precommit=false --config ui.merge=:other + move:[1] banana + atop:[2] apricot and blueberry + transaction abort! + rollback completed + abort: precommit hook exited with status 1 + [255] + $ cat b + banana + $ hg evolve --stop + stopped the interrupted evolve + working directory is now at e1989e4b1526 + $ cat .hg/evolvestate + cat: .hg/evolvestate: No such file or directory + [1] + $ cat b + blueberry + $ hg l + @ 2 apricot and blueberry + + * 1 banana + | + x 0 apricot + + + $ HGMERGE=internal:other hg evolve --update --config hooks.precommit=false --config ui.merge=:other + move:[1] banana + atop:[2] apricot and blueberry + transaction abort! + rollback completed + abort: precommit hook exited with status 1 + [255] + $ hg evolve --continue + evolving 1:e0486f65907d "banana" + working directory is now at bd5ec7dfc2af + $ cat .hg/evolvestate + cat: .hg/evolvestate: No such file or directory + [1] + $ cat b + banana + $ hg l + @ 3 banana + | + o 2 apricot and blueberry +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-evolve-issue5958.t Fri Sep 27 13:03:18 2019 +0200 @@ -0,0 +1,88 @@ +Content divergence and trying to relocate a node on top of itself (issue5958) +https://bz.mercurial-scm.org/show_bug.cgi?id=5958 + + $ . $TESTDIR/testlib/common.sh + + $ cat << EOF >> $HGRCPATH + > [extensions] + > rebase = + > evolve = + > EOF + + $ hg init issue5958 + $ cd issue5958 + + $ echo hi > r0 + $ hg ci -qAm 'add r0' + $ echo hi > foo.txt + $ hg ci -qAm 'add foo.txt' + $ hg metaedit -r . -d '0 2' + 0 files updated, 0 files merged, 0 files removed, 0 files unresolved + +(Make changes in unrelated files so that we don't have any merge conflicts +during the rebase, but the two touched revisions aren't identical) + + $ echo hi > bar.txt + $ hg add -q bar.txt + $ hg amend -q + $ hg metaedit -r 1 -d '0 1' --hidden + 2 new content-divergent changesets + $ hg log -r tip + changeset: 4:c17bf400a278 + tag: tip + parent: 0:a24ed8ad918c + user: test + date: Wed Dec 31 23:59:59 1969 -0000 + instability: content-divergent + summary: add foo.txt + + $ echo hi > baz.txt + $ hg add -q baz.txt + $ hg amend -q + $ hg rebase -qr tip -d 4 + $ hg log -G + @ changeset: 6:08bc7ba82799 + | tag: tip + | parent: 4:c17bf400a278 + | user: test + | date: Wed Dec 31 23:59:58 1969 -0000 + | instability: content-divergent + | summary: add foo.txt + | + * changeset: 4:c17bf400a278 + | parent: 0:a24ed8ad918c + | user: test + | date: Wed Dec 31 23:59:59 1969 -0000 + | instability: content-divergent + | summary: add foo.txt + | + o changeset: 0:a24ed8ad918c + user: test + date: Thu Jan 01 00:00:00 1970 +0000 + summary: add r0 + + $ hg obslog -a -r . + @ 08bc7ba82799 (6) add foo.txt + | + | * c17bf400a278 (4) add foo.txt + | | + x | 1d1fc409af98 (5) add foo.txt + | | rewritten(parent, content) as 08bc7ba82799 using rebase by test (Thu Jan 01 00:00:00 1970 +0000) + | | + x | a25dd7af6cf6 (3) add foo.txt + | | rewritten(content) as 1d1fc409af98 using amend by test (Thu Jan 01 00:00:00 1970 +0000) + | | + x | 0065551bd38f (2) add foo.txt + |/ rewritten(content) as a25dd7af6cf6 using amend by test (Thu Jan 01 00:00:00 1970 +0000) + | + x cc71ffbc7c00 (1) add foo.txt + rewritten(date) as 0065551bd38f using metaedit by test (Thu Jan 01 00:00:00 1970 +0000) + rewritten(date) as c17bf400a278 using metaedit by test (Thu Jan 01 00:00:00 1970 +0000) + + $ hg evolve --content-divergent + merge:[6] add foo.txt + with: [4] add foo.txt + base: [1] add foo.txt + 0 files updated, 0 files merged, 0 files removed, 0 files unresolved + 1 new orphan changesets + working directory is now at 2372e6d39855
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-evolve-issue6097.t Fri Sep 27 13:03:18 2019 +0200 @@ -0,0 +1,99 @@ +Orphan changeset and trying to relocate a node on top of itself (issue6097) +https://bz.mercurial-scm.org/show_bug.cgi?id=6097 + + $ . $TESTDIR/testlib/common.sh + + $ cat << EOF >> $HGRCPATH + > [extensions] + > rebase = + > evolve = + > EOF + + $ hg init issue6097 + $ cd issue6097 + + $ echo apricot > a + $ hg ci -qAm apricot + + $ echo banana > b + $ hg ci -qAm banana + +Let's go back to amend 0 and make an orphan out of 1 + + $ hg up -q 0 + $ echo coconut > c + $ hg add -q c + $ hg ci --amend -m 'apricot and coconut' + 1 new orphan changesets + +Now rebase the successor of 0 on top of 1 + + $ hg rebase -r . -d 1 + rebasing 2:32acf8fb1b23 "apricot and coconut" (tip) + 1 new orphan changesets + +Pruning 1 just to get it out of the way + + $ hg prune -q 1 + +Note how both the regular DAG and the obsolescence graph are linear, but the +paths from 3 to 0 are different: 3-1-0 and 3-2-0 + + $ hg log -G + @ changeset: 3:2868fe6df617 + | tag: tip + | parent: 1:e0486f65907d + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | instability: orphan + | summary: apricot and coconut + | + x changeset: 1:e0486f65907d + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | obsolete: pruned using prune + | summary: banana + | + x changeset: 0:692cc7b6212c + user: test + date: Thu Jan 01 00:00:00 1970 +0000 + obsolete: rewritten using amend, rebase as 3:2868fe6df617 + summary: apricot + + + $ hg obslog + @ 2868fe6df617 (3) apricot and coconut + | + x 32acf8fb1b23 (2) apricot and coconut + | rewritten(parent, content) as 2868fe6df617 using rebase by test (Thu Jan 01 00:00:00 1970 +0000) + | + x 692cc7b6212c (0) apricot + rewritten(description, content) as 32acf8fb1b23 using amend by test (Thu Jan 01 00:00:00 1970 +0000) + + + $ hg evolve -r . + move:[3] apricot and coconut + atop:[-1] + working directory is now at bb847d1d3a5f + + $ hg log -G + @ changeset: 4:bb847d1d3a5f + tag: tip + parent: -1:000000000000 + user: test + date: Thu Jan 01 00:00:00 1970 +0000 + summary: apricot and coconut + + + $ hg obslog + @ bb847d1d3a5f (4) apricot and coconut + | + x 2868fe6df617 (3) apricot and coconut + | rewritten(parent) as bb847d1d3a5f using evolve by test (Thu Jan 01 00:00:00 1970 +0000) + | + x 32acf8fb1b23 (2) apricot and coconut + | rewritten(parent, content) as 2868fe6df617 using rebase by test (Thu Jan 01 00:00:00 1970 +0000) + | + x 692cc7b6212c (0) apricot + rewritten(description, content) as 32acf8fb1b23 using amend by test (Thu Jan 01 00:00:00 1970 +0000) +
--- a/tests/test-evolve-obshistory-amend.t Mon Jul 29 14:43:15 2019 +0200 +++ b/tests/test-evolve-obshistory-amend.t Fri Sep 27 13:03:18 2019 +0200 @@ -109,7 +109,7 @@ @@ -1,1 +1,2 @@ A0 +42 - + $ hg obslog 4ae3a4151de9 --graph -T'{label("log.summary", shortdescription)} {if(markers, join(markers % "at {date|hgdate} by {user|person} ", " also "))}' @ A1
--- a/tests/test-evolve-phase-divergence.t Mon Jul 29 14:43:15 2019 +0200 +++ b/tests/test-evolve-phase-divergence.t Fri Sep 27 13:03:18 2019 +0200 @@ -1412,6 +1412,10 @@ $ hg init cancelled-changes $ cd cancelled-changes + $ cat << EOF > .hg/hgrc + > [diff] + > word-diff = yes + > EOF $ cat << EOF > numbers > 1 > 2
--- a/tests/test-fold.t Mon Jul 29 14:43:15 2019 +0200 +++ b/tests/test-fold.t Fri Sep 27 13:03:18 2019 +0200 @@ -254,7 +254,8 @@ Test order of proposed commit message - $ hg fold --exact --hidden -r 4 -r 5 -r 6 + $ hg fold --exact --hidden -r 4 -r 5 -r 6 \ + > --config experimental.evolution.allowdivergence=yes 2 new content-divergent changesets 3 changesets folded $ hg log -r tip -T '{desc}' @@ -265,7 +266,8 @@ r6 (no-eol) - $ hg fold --exact --hidden -r 6 -r 4 -r 5 + $ hg fold --exact --hidden -r 6 -r 4 -r 5 \ + > --config experimental.evolution.allowdivergence=yes 3 changesets folded $ hg log -r tip -T '{desc}' r4 @@ -381,3 +383,37 @@ o 0: r0 $ cd .. + +Fold should respect experimental.evolution.allowdivergence option +https://bz.mercurial-scm.org/show_bug.cgi?id=5817 + + $ hg init issue5817 + $ cd issue5817 + + $ echo A > foo + $ hg ci -qAm A + $ echo B > foo + $ hg ci -m B + $ echo C > foo + $ hg ci -m C + + $ hg fold --exact -r 'desc("A")::desc("B")' -m 'first fold' + 1 new orphan changesets + 2 changesets folded + +fold aborts here because divergence is not allowed + + $ hg fold --exact -r 'desc("A")::desc("B")' -m 'second fold' \ + > --config experimental.evolution.allowdivergence=no + abort: folding obsolete revisions may cause divergence + (set experimental.evolution.allowdivergence=yes to allow folding them) + [255] + +but if we allow divergence, this should work and should create new content-divergent changesets + + $ hg fold --exact -r 'desc("A")::desc("B")' -m 'second fold' \ + > --config experimental.evolution.allowdivergence=yes + 2 new content-divergent changesets + 2 changesets folded + + $ cd ..
--- a/tests/test-issue-6028.t Mon Jul 29 14:43:15 2019 +0200 +++ b/tests/test-issue-6028.t Fri Sep 27 13:03:18 2019 +0200 @@ -25,10 +25,12 @@ $ cd $TESTTMP/issue-6028 create initial commit + $ echo "0" > 0 $ hg ci -Am 0 adding 0 +start new topics "a" and "b" both from 0 $ hg up default 0 files updated, 0 files merged, 0 files removed, 0 files unresolved @@ -40,7 +42,6 @@ active topic 'a' grew its first changeset (see 'hg help topics' for more information) - $ hg up default 0 files updated, 0 files merged, 1 files removed, 0 files unresolved $ hg topics b @@ -51,6 +52,8 @@ active topic 'b' grew its first changeset (see 'hg help topics' for more information) +create branch "integration" from 0, merge topics "a" and "b" into it + $ hg up default 0 files updated, 0 files merged, 1 files removed, 0 files unresolved $ hg branch integration @@ -66,6 +69,8 @@ (branch merge, don't forget to commit) $ hg ci -m "merged b" +commit a bad file on topic "a", merge it into "integration" + $ hg up a switching to topic a 0 files updated, 0 files merged, 1 files removed, 0 files unresolved @@ -79,6 +84,8 @@ (branch merge, don't forget to commit) $ hg ci -m "merged a bad commit" +add more commits on both topics and merge them into "integration" + $ hg up a switching to topic a 0 files updated, 0 files merged, 1 files removed, 0 files unresolved @@ -103,7 +110,12 @@ (branch merge, don't forget to commit) $ hg ci -m "merged bb" -create instability by pruning two changesets, one in a topic, one in a merge +create instability by pruning two changesets, one in a topic, one a merge + + $ hg log -r 5:6 -T '{rev}: {desc}\n' + 5: a bad commit + 6: merged a bad commit + $ hg prune -r 5:6 2 changesets pruned 3 new orphan changesets @@ -112,12 +124,47 @@ 2 files updated, 0 files merged, 1 files removed, 0 files unresolved start the evolve + $ hg evolve --update --no-all move:[8] merged aa atop:[4] merged b working directory is now at c920dd828523 +casually checking issue6141: position of p2 is not changed + + $ hg log -r 'predecessors(.) + .' + changeset: 8:3f6f25057afb + branch: integration + parent: 6:cfc4c333724f + parent: 7:61eff7f7bb6c + user: test + date: Thu Jan 01 00:00:00 1970 +0000 + obsolete: rebased using evolve as 11:c920dd828523 + summary: merged aa + + changeset: 11:c920dd828523 + branch: integration + tag: tip + parent: 4:e33aee2c715e + parent: 7:61eff7f7bb6c + user: test + date: Thu Jan 01 00:00:00 1970 +0000 + instability: orphan + summary: merged aa + + +test that we successfully got rid of the bad file + + $ hg d --git -r 'predecessors(.)' -r '.' + diff --git a/a_bad_commit b/a_bad_commit + deleted file mode 100644 + --- a/a_bad_commit + +++ /dev/null + @@ -1,1 +0,0 @@ + -a bad commit + evolve creates an obsolete changeset above as 11 + $ hg evolve -r . cannot solve instability of c920dd828523, skipping cannot solve instability of c920dd828523, skipping
--- a/tests/test-pick.t Mon Jul 29 14:43:15 2019 +0200 +++ b/tests/test-pick.t Fri Sep 27 13:03:18 2019 +0200 @@ -1,3 +1,4 @@ +#testcases abortflag Test for the pick command $ cat >> $HGRCPATH <<EOF @@ -8,6 +9,13 @@ > EOF $ echo "evolve=$(echo $(dirname $TESTDIR))/hgext3rd/evolve/" >> $HGRCPATH +#if abortflag + $ cat >> $HGRCPATH <<EOF + > [alias] + > abort = pick --abort + > EOF +#endif + $ mkcommit() { > echo "$1" > "$1" > hg add "$1" @@ -235,7 +243,7 @@ unresolved merge conflicts (see hg help resolve) [1] - $ hg pick --abort + $ hg abort aborting pick, updating to c437988de89f $ hg glog
--- a/tests/test-prune.t Mon Jul 29 14:43:15 2019 +0200 +++ b/tests/test-prune.t Fri Sep 27 13:03:18 2019 +0200 @@ -39,7 +39,7 @@ abort: can only specify one of pair, fold [255] $ hg prune --fold --biject - abort: nothing to prune + abort: no revisions specified to prune [255] $ hg prune --split --fold abort: can only specify one of fold, split @@ -307,7 +307,7 @@ (activating bookmark todelete) $ hg prune -B nostrip bookmark 'nostrip' deleted - abort: nothing to prune + abort: no revisions specified to prune [255] $ hg tag --remove --local a $ hg prune -B todelete
--- a/tests/test-rewind.t Mon Jul 29 14:43:15 2019 +0200 +++ b/tests/test-rewind.t Fri Sep 27 13:03:18 2019 +0200 @@ -921,25 +921,11 @@ $ cd .. -Check error cases -================= - - $ hg clone rewind-testing-base rewind-testing-error - updating to branch default - 3 files updated, 0 files merged, 0 files removed, 0 files unresolved - $ cd rewind-testing-error +Merge commits +============= -Uncommited changes ------------------- - - $ echo C > C - $ hg add C - $ hg rewind - abort: uncommitted changes - [255] - -Merge commits -------------- + $ hg clone -q rewind-testing-base rewind-merge + $ cd rewind-merge $ hg up --clean .^ 0 files updated, 0 files merged, 1 files removed, 0 files unresolved @@ -962,7 +948,7 @@ $ hg rewind --from . rewinded to 1 changesets (1 changesets obsoleted) - working directory is now at 006fd8c2fed9 + working directory is now at 9d325190bd87 $ hg st --change . A B @@ -973,9 +959,91 @@ | |/ +---x 4: merge () | |/ - | o 3: foo (C foo) + | o 3: foo (foo) | | | ~ o 2: c_B0 (B) | ~ + + $ cd .. + +Rewind --keep +============= + + $ hg init rewind-keep + $ cd rewind-keep + $ echo root > root + $ hg ci -qAm 'root' + + $ echo apple > a + $ echo banana > b + $ hg ci -qAm initial + + $ hg rm b + $ echo apricot > a + $ echo coconut > c + $ hg add c + $ hg status + M a + A c + R b + $ hg amend -m amended + $ hg glf --hidden + @ 2: amended (a c) + | + | x 1: initial (a b) + |/ + o 0: root (root) + + +Clean wdir + + $ hg rewind --keep --to 'desc("initial")' --hidden + rewinded to 1 changesets + (1 changesets obsoleted) + $ hg obslog + @ b4c97fddc16a (3) initial + |\ + x | 2ea5be2f8751 (2) amended + |/ rewritten(description, meta, content) as b4c97fddc16a using rewind by test (Thu Jan 01 00:00:06 1970 +0000) + | + x 30704102d912 (1) initial + rewritten(description, content) as 2ea5be2f8751 using amend by test (Thu Jan 01 00:00:06 1970 +0000) + rewritten(meta) as b4c97fddc16a using rewind by test (Thu Jan 01 00:00:06 1970 +0000) + + $ hg glf --hidden + @ 3: initial (a b) + | + | x 2: amended (a c) + |/ + | x 1: initial (a b) + |/ + o 0: root (root) + + $ hg st + M a + A c + R b + +Making wdir even more dirty + + $ echo avocado > a + $ echo durian > d + $ hg st + M a + A c + R b + ? d + +No rewinding without --keep + + $ hg rewind --to 'desc("amended")' --hidden + abort: uncommitted changes + [255] + +XXX: Unfortunately, even with --keep it's not allowed + + $ hg rewind --keep --to 'desc("amended")' --hidden + abort: uncommitted changes + [255]
--- a/tests/test-topic-mode.t Mon Jul 29 14:43:15 2019 +0200 +++ b/tests/test-topic-mode.t Fri Sep 27 13:03:18 2019 +0200 @@ -228,7 +228,7 @@ $ touch A $ hg add A $ hg commit -m "Add A" --config devel.randomseed=42 - active topic 'panoramic-antelope' grew its first changeset + active topic 'palatial-antelope' grew its first changeset (see 'hg help topics' for more information) $ hg up -r "desc(ROOT)" @@ -256,8 +256,8 @@ | date: Thu Jan 01 00:00:00 1970 +0000 | summary: Add B | - | o changeset: 1:d4b548f35972 - |/ topic: panoramic-antelope + | o changeset: 1:d502ab6d9d91 + |/ topic: palatial-antelope | user: test | date: Thu Jan 01 00:00:00 1970 +0000 | summary: Add A @@ -267,7 +267,7 @@ date: Thu Jan 01 00:00:00 1970 +0000 summary: ROOT - $ hg merge panoramic-antelope + $ hg merge palatial-antelope 1 files updated, 0 files merged, 0 files removed, 0 files unresolved (branch merge, don't forget to commit) $ hg ci -m 'merge' @@ -292,7 +292,7 @@ $ touch A $ hg add A $ hg commit -m "Add A" --config devel.randomseed=42 - active topic 'panoramic-antelope' grew its first changeset + active topic 'palatial-antelope' grew its first changeset (see 'hg help topics' for more information) $ hg up -r "desc(ROOT)" @@ -320,8 +320,8 @@ | date: Thu Jan 01 00:00:00 1970 +0000 | summary: Add B | - | o changeset: 1:d4b548f35972 - |/ topic: panoramic-antelope + | o changeset: 1:d502ab6d9d91 + |/ topic: palatial-antelope | user: test | date: Thu Jan 01 00:00:00 1970 +0000 | summary: Add A @@ -331,7 +331,7 @@ date: Thu Jan 01 00:00:00 1970 +0000 summary: ROOT - $ hg merge panoramic-antelope + $ hg merge palatial-antelope 1 files updated, 0 files merged, 0 files removed, 0 files unresolved (branch merge, don't forget to commit) $ hg ci -m 'merge' --config devel.randomseed=1337
--- a/tests/test-topic-stack.t Mon Jul 29 14:43:15 2019 +0200 +++ b/tests/test-topic-stack.t Fri Sep 27 13:03:18 2019 +0200 @@ -783,7 +783,7 @@ $ hg stack red ### topic: red - ### target: default (branch), ambiguous rebase destination - topic 'red' has 3 heads + ### target: default (branch), 7 behind s5$ c_H (orphan) ^ c_G ^ c_D @@ -857,7 +857,7 @@ $ hg stack red ### topic: red - ### target: default (branch), ambiguous rebase destination - topic 'red' has 3 heads + ### target: default (branch), 7 behind s5$ c_H (orphan) ^ c_G ^ c_D