diff --git a/.hgtags b/.hgtags index 1c2e75c9b593d4303ccc78c9609f9e931d3783c6_LmhndGFncw==..94c6158b3bfaf8d3462f17b70fcd16a74fb521b5_LmhndGFncw== 100644 --- a/.hgtags +++ b/.hgtags @@ -7,6 +7,7 @@ fe410e259ed0c62730e97b41daed46b37e203467 heptapod-0.8.1 923cadf135af823d5ee64ffe8a713fb7a9c89b38 heptapod-0.8.2 3aa13f3969d61d6722364a14f670a70dda91e117 heptapod-0.8.3 +98527b5cac76f2e9fc5bcb18127fd6cbb2905d2f heptapod-0.8.4 5b382276649ef2f6daa00f4a570e5ff83fd4020f heptapod-0.12.0.dev0 085bf3333fb6e18e29e2808fa319f24ffc35bafa heptapod-0.12.0rc1 62c6cc9cae756436531b9cb7e8c8fdf934b616d9 heptapod-0.12.0rc2 @@ -15,3 +16,5 @@ 8685ff269a05a2bfbcf0548d2710aadbbf9d2fc6 heptapod-0.12.0rc5 7f98fa9ae5a3282f2df38b1e81f549f1621ccab6 heptapod-0.12.0rc6 6dbc6fcd63b9b2119c68cf55bec4db4416f3c331 heptapod-0.12.0 +8eb2109d97fbba92b618f10a66f4c4d07e558184 heptapod-0.12.1 +94dbb4a5d46c00d108078146351fb6a9fd69c150 heptapod-0.12.2 diff --git a/heptapod/required.hgrc b/heptapod/required.hgrc index 1c2e75c9b593d4303ccc78c9609f9e931d3783c6_aGVwdGFwb2QvcmVxdWlyZWQuaGdyYw==..94c6158b3bfaf8d3462f17b70fcd16a74fb521b5_aGVwdGFwb2QvcmVxdWlyZWQuaGdyYw== 100644 --- a/heptapod/required.hgrc +++ b/heptapod/required.hgrc @@ -5,6 +5,7 @@ topic= loggingmod = share = +clonebundles = [logging] # see https://dev.heptapod.net/heptapod/hgext-loggingmod/blob/branch/default/README.md diff --git a/hgext3rd/heptapod/__init__.py b/hgext3rd/heptapod/__init__.py index 1c2e75c9b593d4303ccc78c9609f9e931d3783c6_aGdleHQzcmQvaGVwdGFwb2QvX19pbml0X18ucHk=..94c6158b3bfaf8d3462f17b70fcd16a74fb521b5_aGdleHQzcmQvaGVwdGFwb2QvX19pbml0X18ucHk= 100644 --- a/hgext3rd/heptapod/__init__.py +++ b/hgext3rd/heptapod/__init__.py @@ -24,6 +24,7 @@ from . import ( topic as topicmod, git, + obsutil, ) # these have conditional imports and subsequent `None` testing in @@ -123,6 +124,40 @@ git.HeptapodGitHandler(repo, repo.ui).export_commits() +@command(b'hpd-unique-successor', + [(b'r', b'rev', b'', _(b'specified revision'), _(b'REV')), + ]) +def unique_successor(ui, repo, rev=None, **opts): + """Display the node ID of the obsolescence successor of REV if unique. + + This can be useful after a simple rebase or fold, as a direct way to + find the resulting changeset. + + If REV isn't obsolete, the output is REV. + If there is any divergence, the command will fail. + + The same functionality can be accomplished with + ``hg log -T {successorsets}`` but the latter + + 1. won't fail on divergence + 2. will present as with ``{rev}:{node|short}``, making a second ``hg log`` + call necessary to get the full hash. + + In the context of the Rails app 1) could be almost acceptable by + detecting multiple results and refusing them (that's already some parsing), + but together with 2) that's too much, we'll have a + better robustness with this simple, fully tested command. + + See also: https://foss.heptapod.net/mercurial/evolve/issues/13 + """ + if not rev: + raise error.Abort(_(b"Please specify a revision")) + # rev would typically be an obsolete revision, we need to see them + ctx = scmutil.revsingle(repo.unfiltered(), rev) + succ_ctx = obsutil.latest_unique_successor(ctx) + ui.write(succ_ctx.hex()) + + def runsystem(orig, ui, cmd, environ, cwd, out): heptapod_env = {k: v for k, v in ui.environ.items() if k.startswith('HEPTAPOD_')} diff --git a/hgext3rd/heptapod/git.py b/hgext3rd/heptapod/git.py index 1c2e75c9b593d4303ccc78c9609f9e931d3783c6_aGdleHQzcmQvaGVwdGFwb2QvZ2l0LnB5..94c6158b3bfaf8d3462f17b70fcd16a74fb521b5_aGdleHQzcmQvaGVwdGFwb2QvZ2l0LnB5 100644 --- a/hgext3rd/heptapod/git.py +++ b/hgext3rd/heptapod/git.py @@ -18,10 +18,9 @@ from mercurial import ( error, hg, - obsutil, phases, pycompat, ) import re import traceback @@ -22,9 +21,11 @@ phases, pycompat, ) import re import traceback +from . import obsutil + class MultipleDescendants(LookupError): pass @@ -295,7 +296,7 @@ return self.published_topic_latest_hg_sha(topic, before_ctx) try: - succctx = self.latest_unique_successor(before_ctx) + succctx = obsutil.latest_unique_successor(before_ctx) except error.Abort as exc: if initial_import: # we don't want to break an initial import because @@ -596,25 +597,3 @@ "Can't find public descendants of %r in topic %r" % ( ctx.hex(), topic)) return self.repo[rev] - - def latest_unique_successor(self, ctx): - """Return the latest of unique set of successors of a changeset. - - The changeset is given by its context, and a context is also returned, - or `None` if there is no successor. - - `Abort` gets raised if there are several sets of successors. - """ - successorssets = obsutil.successorssets(self.repo, - ctx.node()) - if not successorssets: - return None - if len(successorssets) > 1: - # There is more than one set of successors, so we have - # a divergence, and we cannot handle it automatically - raise error.Abort(("Changeset %r evolved into a " - "divergent state") % ctx.hex()) - - # Single successor set: it can be one or many (split) - # In all cases, we can take the last one. - return self.repo[successorssets[0][-1]] diff --git a/hgext3rd/heptapod/obsutil.py b/hgext3rd/heptapod/obsutil.py new file mode 100644 index 0000000000000000000000000000000000000000..94c6158b3bfaf8d3462f17b70fcd16a74fb521b5_aGdleHQzcmQvaGVwdGFwb2Qvb2JzdXRpbC5weQ== --- /dev/null +++ b/hgext3rd/heptapod/obsutil.py @@ -0,0 +1,27 @@ +from mercurial import ( + error, + obsutil as hgobsutil, +) + + +def latest_unique_successor(ctx): + """Return the latest of unique set of successors of a changeset. + + The changeset is given by its context, and a context is also returned, + or `None` if there is no successor. + + `Abort` gets raised if there are several sets of successors. + """ + repo = ctx.repo() + successorssets = hgobsutil.successorssets(repo, ctx.node()) + if not successorssets: + return None + if len(successorssets) > 1: + # There is more than one set of successors, so we have + # a divergence, and we cannot handle it automatically + raise error.Abort(("Changeset %r evolved into a " + "divergent state") % ctx.hex()) + + # Single successor set: it can be one or many (split) + # In all cases, we can take the last one. + return repo[successorssets[0][-1]] diff --git a/hgext3rd/heptapod/tests/test_commands_misc.py b/hgext3rd/heptapod/tests/test_commands_misc.py new file mode 100644 index 0000000000000000000000000000000000000000..94c6158b3bfaf8d3462f17b70fcd16a74fb521b5_aGdleHQzcmQvaGVwdGFwb2QvdGVzdHMvdGVzdF9jb21tYW5kc19taXNjLnB5 --- /dev/null +++ b/hgext3rd/heptapod/tests/test_commands_misc.py @@ -0,0 +1,70 @@ +# Copyright 2019-2020 Georges Racinet <georges.racinet@octobus.net> +# +# This software may be used and distributed according to the terms of the +# GNU General Public License version 2 or any later version. +# +# SPDX-License-Identifier: GPL-2.0-or-later +from __future__ import absolute_import + +from mercurial import ( + error, + scmutil, +) +import pytest + +from heptapod.testhelpers import ( + LocalRepoWrapper, +) +from .utils import common_config + + +def test_hpd_unique_successor(tmpdir, monkeypatch): + repo_path = tmpdir.join('repo') + wrapper = LocalRepoWrapper.init(repo_path, config=common_config()) + ctx = wrapper.write_commit('foo', message="default0", + return_ctx=True) + repo_path.join('foo').write('amend 1') + wrapper.command('amend', message='amend1') + repo_path.join('foo').write('amend 2') + wrapper.command('amend', message='amend2') + + records = [] + + def write(*args, **opts): + records.append((args, opts)) + + wrapper.repo.ui.write = write + wrapper.command('hpd-unique-successor', rev=ctx.hex()) + out = records[0][0][0] + + succ_ctx = scmutil.revsingle(wrapper.repo, out) + assert succ_ctx.description() == 'amend2' + + +def test_hpd_unique_successor_divergence(tmpdir, monkeypatch): + repo_path = tmpdir.join('repo') + config = common_config() + config.setdefault('experimental', {})['evolution.allowdivergence'] = 'yes' + wrapper = LocalRepoWrapper.init(repo_path, config=config) + ctx = wrapper.write_commit('foo', message="default0", + return_ctx=True) + repo_path.join('foo').write('amend 1') + wrapper.command('amend', message='amend1') + + # let's create the divergence + wrapper.update(ctx.hex(), hidden=True) + repo_path.join('foo').write('amend 2') + wrapper.command('amend', message='amend2') + + with pytest.raises(error.Abort) as exc_info: + wrapper.command('hpd-unique-successor', rev=ctx.hex()) + assert 'divergent' in exc_info.value.args[0] + + +def test_hpd_unique_successor_missing_rev(tmpdir, monkeypatch): + repo_path = tmpdir.join('repo') + wrapper = LocalRepoWrapper.init(repo_path, config=common_config()) + + with pytest.raises(error.Abort) as exc_info: + wrapper.command('hpd-unique-successor') + assert 'specify a revision' in exc_info.value.args[0] diff --git a/install-requirements.txt b/install-requirements.txt index 1c2e75c9b593d4303ccc78c9609f9e931d3783c6_aW5zdGFsbC1yZXF1aXJlbWVudHMudHh0..94c6158b3bfaf8d3462f17b70fcd16a74fb521b5_aW5zdGFsbC1yZXF1aXJlbWVudHMudHh0 100644 --- a/install-requirements.txt +++ b/install-requirements.txt @@ -1,4 +1,4 @@ -Mercurial>=5.2 +Mercurial>=5.2,<5.4 hg-evolve>=9.2.1 hg-git>=0.8.13 requests