changeset 6752:44e41905b289 mercurial-6.6

test-compat: merge stable into mercurial-6.6
author Anton Shestakov <av6@dwimlabs.net>
date Thu, 11 Apr 2024 01:58:03 -0300
parents 07a9cd7cbfc6 (current diff) 21b51b2bbf09 (diff)
children fdfd10e5705f dc39bd5d9bb8
files .gitlab-ci.yml
diffstat 9 files changed, 290 insertions(+), 29 deletions(-) [+]
line wrap: on
line diff
--- a/.hgtags	Thu Feb 29 14:18:08 2024 -0300
+++ b/.hgtags	Thu Apr 11 01:58:03 2024 -0300
@@ -108,3 +108,4 @@
 b9355f6f3093c0cf9698215f05059321880f28da 11.0.2
 a625eb5acea4d682becd21759170306ab769afb2 11.1.0
 369e248b6312cc3b0777033a4632f2c9e18a0897 11.1.1
+c31c6638381080bc5905fad37545610fde3b98bc 11.1.2
--- a/CHANGELOG	Thu Feb 29 14:18:08 2024 -0300
+++ b/CHANGELOG	Thu Apr 11 01:58:03 2024 -0300
@@ -1,7 +1,23 @@
 Changelog
 =========
 
-11.1.2 - in progress
+11.1.3 - in progress
+--------------------
+
+topic (1.1.3)
+
+  * topic namespaces: add hg debug-default-topic-namespace command that can
+    list changesets with problematic topic namespaces (i.e. "none" and
+    "default") and rewrite them
+
+  * topic namespaces: new experimental.tns-reject-push config to make servers
+    reject pushes that contain changesets with any topic namespace in commit
+    extras
+
+  * topic namespaces: slightly change .hg/topic-namespace cleanup code to be
+    executed on acquiring a wlock instead of on reading .hg/topic-namespace
+
+11.1.2 -- 2024-03-03
 --------------------
 
   * compatibility with Mercurial 6.7
--- a/debian/changelog	Thu Feb 29 14:18:08 2024 -0300
+++ b/debian/changelog	Thu Apr 11 01:58:03 2024 -0300
@@ -1,3 +1,9 @@
+mercurial-evolve (11.1.2-1) unstable; urgency=medium
+
+  * new upstream release
+
+ -- Anton Shestakov <av6@dwimlabs.net>  Sun, 03 Mar 2024 15:09:28 -0300
+
 mercurial-evolve (11.1.1-1) unstable; urgency=medium
 
   * new upstream release
--- a/hgext3rd/evolve/metadata.py	Thu Feb 29 14:18:08 2024 -0300
+++ b/hgext3rd/evolve/metadata.py	Thu Apr 11 01:58:03 2024 -0300
@@ -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'11.1.2.dev0'
+__version__ = b'11.1.3.dev0'
 testedwith = b'4.9 5.0 5.1 5.2 5.3 5.4 5.5 5.6 5.7 5.8 5.9 6.0 6.1 6.2 6.3 6.4 6.5 6.6 6.7'
 minimumhgversion = b'4.9'
 buglink = b'https://bz.mercurial-scm.org/'
--- a/hgext3rd/topic/__init__.py	Thu Feb 29 14:18:08 2024 -0300
+++ b/hgext3rd/topic/__init__.py	Thu Apr 11 01:58:03 2024 -0300
@@ -179,6 +179,7 @@
     hg,
     localrepo,
     lock as lockmod,
+    logcmdutil,
     merge,
     namespaces,
     node,
@@ -237,7 +238,7 @@
               b'log.topic': b'green_background',
               }
 
-__version__ = b'1.1.2.dev0'
+__version__ = b'1.1.3.dev0'
 
 testedwith = b'4.9 5.0 5.1 5.2 5.3 5.4 5.5 5.6 5.7 5.8 5.9 6.0 6.1 6.2 6.3 6.4 6.5 6.6 6.7'
 minimumhgversion = b'4.9'
@@ -295,6 +296,9 @@
 configitem(b'experimental', b'tns-default-pull-namespaces',
            default=configitems.dynamicdefault,
 )
+configitem(b'experimental', b'tns-reject-push',
+           default=False,
+)
 configitem(b'experimental', b'topic-mode.server',
            default=configitems.dynamicdefault,
 )
@@ -715,31 +719,23 @@
             self._topic_namespaces = namespaces
             return namespaces
 
-        @property
-        def currenttns(self):
-            tns = self.vfs.tryread(b'topic-namespace')
+        def wlock(self, wait=True):
+            wlock = super(topicrepo, self).wlock(wait=wait)
             # we should definitely drop this at some point, but it depends on
             # our own release schedule, not core's, so here's hg 1.0
             # hg <= 1.0 (cfa08c88a5c4)
-            if tns == b'none':
+            if wlock is not None and wlock.held:
                 try:
-                    with self.wlock(wait=False):
-                        try:
-                            # we make sure the file contains what we expect
-                            if self.vfs.read(b'topic-namespace') == b'none':
-                                repo.vfs.unlinkpath(b'topic-namespace')
-                        except IOError as err:
-                            if err.errno != errno.ENOENT:
-                                raise
-                except error.LockError:
-                    # if we cannot acquire wdir lock, then we shouldn't do
-                    # anything at all, since it'd be unsafe to modify wdir
-                    pass
-            elif tns == b'':
-                # technically, if user creates an empty file, it should be
-                # handled differently than non-existing file, but the
-                # distinction is probably not that important
-                tns = b'none'
+                    if self.vfs.read(b'topic-namespace') == b'none':
+                        repo.vfs.unlinkpath(b'topic-namespace')
+                except IOError as err:
+                    if err.errno != errno.ENOENT:
+                        raise
+            return wlock
+
+        @property
+        def currenttns(self):
+            tns = self.vfs.tryread(b'topic-namespace') or b'none'
             return encoding.tolocal(tns)
 
         @util.propertycache
@@ -921,6 +917,25 @@
                 else:
                     tr.addvalidator(b'000-reject-publish', _validate_publish)
 
+            if self.ui.configbool(b'experimental', b'tns-reject-push'):
+                if util.safehasattr(tr, '_validator'):
+                    # hg <= 5.3 (36f08ae87ef6)
+                    origvalidator_publish = tr._validator
+
+                def _validate_csets_with_tns(tr2):
+                    repo = reporef()
+                    flow.reject_csets_with_tns(repo, tr2)
+
+                def validator(tr2):
+                    _validate_csets_with_tns(tr2)
+                    return origvalidator_publish(tr2)
+
+                if util.safehasattr(tr, '_validator'):
+                    # hg <= 5.3 (36f08ae87ef6)
+                    tr._validator = validator
+                else:
+                    tr.addvalidator(b'000-reject-csets-with-tns', _validate_csets_with_tns)
+
             if util.safehasattr(tr, '_validator'):
                 # hg <= 5.3 (36f08ae87ef6)
                 origvalidator_affected_tns = tr._validator
@@ -1911,6 +1926,73 @@
     for tns in repo.topic_namespaces:
         ui.write(b'%s\n' % (tns,))
 
+@command(b'debug-default-topic-namespace', [
+        (b'', b'none', True, b'find changesets with topic-namespace=none'),
+        (b'', b'default', False, b'find changesets with topic-namespace=default'),
+        (b'', b'clear', False, b'remove topic namespace from commit extras'),
+    ] + commands.formatteropts)
+def debugdefaulttns(ui, repo, **opts):
+    """list changesets with the default topic namespace in commit extras"""
+    opts = pycompat.byteskwargs(opts)
+    condition = []
+    if opts[b'none']:
+        condition += [b'extra("topic-namespace", "none")']
+    if opts[b'default']:
+        condition += [b'extra("topic-namespace", "default")']
+    if not condition:
+        condition = [b'none()']
+    revs = repo.revs(b'not public() and not obsolete() and (%lr)', condition)
+    if opts[b'clear']:
+        with repo.wlock(), repo.lock(), repo.transaction(b'debug-default-topic-namespace'):
+            successors = {}
+            for rev in revs:
+                _clear_tns_extras(ui, repo, rev, successors)
+            scmutil.cleanupnodes(repo, successors, b'debug-default-topic-namespace')
+        return
+    displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
+    logcmdutil.displayrevs(ui, repo, revs, displayer, None)
+
+def _clear_tns_extras(ui, repo, rev, successors):
+    ctx = repo[rev]
+
+    if len(ctx.parents()) > 1:
+        # ctx.files() isn't reliable for merges, so fall back to the
+        # slower repo.status() method
+        st = ctx.p1().status(ctx)
+        files = set(st.modified) | set(st.added) | set(st.removed)
+    else:
+        files = set(ctx.files())
+
+    def filectxfn(repo, unused, path):
+        try:
+            return ctx[path]
+        except error.ManifestLookupError:
+            return None
+
+    extra = ctx.extra().copy()
+    del extra[b'topic-namespace']
+
+    p1 = ctx.p1().node()
+    p2 = ctx.p2().node()
+    if p1 in successors:
+        p1 = successors[p1][0]
+    if p2 in successors:
+        p2 = successors[p2][0]
+    mc = context.memctx(repo,
+                        (p1, p2),
+                        ctx.description(),
+                        files,
+                        filectxfn,
+                        user=ctx.user(),
+                        date=ctx.date(),
+                        extra=extra)
+
+    overrides = {(b'phases', b'new-commit'): ctx.phase()}
+    with repo.ui.configoverride(overrides, b'debug-default-topic-namespace'):
+        newnode = repo.commitctx(mc)
+
+    successors[ctx.node()] = (newnode,)
+
 @command(b'debug-parse-fqbn', commands.formatteropts, _(b'FQBN'), optionalrepo=True)
 def debugparsefqbn(ui, repo, fqbn, **opts):
     """parse branch//namespace/topic string into its components"""
--- a/hgext3rd/topic/flow.py	Thu Feb 29 14:18:08 2024 -0300
+++ b/hgext3rd/topic/flow.py	Thu Apr 11 01:58:03 2024 -0300
@@ -47,7 +47,8 @@
     untopiced = repo.revs(b'not public() and (%n:) - hidden() - topic()', startnode)
     if untopiced:
         num = len(untopiced)
-        fnode = repo[untopiced.first()].hex()[:10]
+        cl = repo.changelog
+        fnode = node.short(cl.node(untopiced.first()))
         if num == 1:
             msg = _(b"%s") % fnode
         else:
@@ -82,6 +83,29 @@
             msg += b' and %d others' % (len(published) - 1)
         raise error.Abort(msg)
 
+def reject_csets_with_tns(repo, tr):
+    """Reject the push if there are changesets with any topic namespace"""
+    if b'node' not in tr.hookargs: # no new revs
+        return
+
+    reject = repo.ui.config(b'experimental', b'tns-reject-push')
+    if not reject:
+        return
+
+    startnode = node.bin(tr.hookargs[b'node'])
+    repo = repo.unfiltered()
+    with_tns = repo.revs(b'not public() and extra("topic-namespace") and (%n:) - hidden()', startnode)
+    if with_tns:
+        num = len(with_tns)
+        cl = repo.changelog
+        fnode = node.short(cl.node(with_tns.first()))
+        if num == 1:
+            msg = _(b"%s") % fnode
+        else:
+            msg = _(b"%s and %d more") % (fnode, num - 1)
+        fullmsg = _(b"rejecting draft changesets with topic namespace: %s")
+        raise error.Abort(fullmsg % msg)
+
 def wrappush(orig, repo, remote, *args, **kwargs):
     """interpret the --publish flag and pass it to the push operation"""
     newargs = kwargs.copy()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-namespaces-reject.t	Thu Apr 11 01:58:03 2024 -0300
@@ -0,0 +1,80 @@
+Rejecting changesets with any topic namespaces during push
+
+  $ . "$TESTDIR/testlib/common.sh"
+
+  $ cat >> $HGRCPATH << EOF
+  > [extensions]
+  > topic =
+  > [phases]
+  > publish = no
+  > [devel]
+  > tns-report-transactions = push
+  > [ui]
+  > logtemplate = "{rev}: {desc} {fqbn} ({phase})\n"
+  > EOF
+
+  $ hg init orig
+  $ hg clone orig clone -q
+
+  $ cd clone
+
+changesets without topic namespace are freely exchanged
+
+  $ echo apple > a
+  $ hg debug-topic-namespace --clear
+  $ hg topic apple
+  marked working directory as topic: apple
+  $ hg ci -qAm apple
+
+  $ hg log -r . -T '{rev}: {join(extras, " ")}\n'
+  0: branch=default topic=apple
+
+  $ hg push
+  pushing to * (glob)
+  searching for changes
+  adding changesets
+  adding manifests
+  adding file changes
+  added 1 changesets with 1 changes to 1 files
+
+changesets with topic namespaces are rejected when server configuration disallows
+
+  $ cat >> ../orig/.hg/hgrc << EOF
+  > [experimental]
+  > tns-reject-push = yes
+  > EOF
+
+  $ echo banana > b
+  $ hg debug-topic-namespace bob
+  marked working directory as topic namespace: bob
+  $ hg topic banana
+  $ hg ci -qAm 'banana'
+
+  $ hg push
+  pushing to $TESTTMP/orig
+  searching for changes
+  adding changesets
+  adding manifests
+  adding file changes
+  transaction abort!
+  rollback completed
+  abort: rejecting draft changesets with topic namespace: ed9751f04a18
+  [255]
+
+changesets with topic namespaces are only exchanged if server configuration allows
+
+  $ cat >> ../orig/.hg/hgrc << EOF
+  > [experimental]
+  > tns-reject-push = no
+  > EOF
+
+  $ hg push
+  pushing to $TESTTMP/orig
+  searching for changes
+  adding changesets
+  adding manifests
+  adding file changes
+  topic namespaces affected: bob
+  added 1 changesets with 1 changes to 1 files
+
+  $ cd ..
--- a/tests/test-namespaces.t	Thu Feb 29 14:18:08 2024 -0300
+++ b/tests/test-namespaces.t	Thu Apr 11 01:58:03 2024 -0300
@@ -286,6 +286,58 @@
   abort: topic namespace 'nonsense' does not exist
   [10]
 
+Debug command related to the default/empty topic namespace
+
+  $ hg debug-topic-namespace --clear
+
+  $ echo none > none
+  $ hg ci -qAm 'tns=none' \
+  >    --config extensions.topic=! \
+  >    --config extensions.commitextras= \
+  >    --extra topic-namespace=none
+
+
+  $ echo default > default
+  $ hg ci -qAm 'tns=default' \
+  >   --config extensions.topic=! \
+  >   --config extensions.commitextras= \
+  >   --extra topic-namespace=default
+
+  $ hg debug-default-topic-namespace \
+  >   --debug \
+  >   | grep extra
+  extra:       branch=stable
+  extra:       topic-namespace=none
+
+  $ hg debug-default-topic-namespace \
+  >   --no-none \
+  >   --default \
+  >   --debug \
+  >   | grep extra
+  extra:       branch=stable
+  extra:       topic-namespace=default
+
+  $ hg debug-default-topic-namespace \
+  >   --default \
+  >   -T '{rev}:{node|short} {join(extras, " ")}\n'
+  4:29a2d0acd473 branch=stable topic-namespace=none
+  5:16d6061fce0c branch=stable topic-namespace=default
+
+  $ hg debug-default-topic-namespace --none --default --clear
+
+  $ hg debug-default-topic-namespace --none --default
+
+  $ hg evolve --config extensions.evolve= --list
+
+  $ hg evolve --config extensions.evolve= --any
+  update:[7] tns=default
+  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  working directory is now at 38c9ea9d27a7
+
+  $ hg debug-default-topic-namespace --none --default
+
+  $ hg verify --quiet
+
 Parsing
 
   $ hg debugparsefqbn foo/bar//user26/feature -T '[{branch}] <{topic_namespace}> ({topic})\n'
--- a/tests/test-topic-flow-reject-untopiced.t	Thu Feb 29 14:18:08 2024 -0300
+++ b/tests/test-topic-flow-reject-untopiced.t	Thu Apr 11 01:58:03 2024 -0300
@@ -75,7 +75,7 @@
   adding file changes
   transaction abort!
   rollback completed
-  abort: rejecting draft changesets: 4e8b0e0237
+  abort: rejecting draft changesets: 4e8b0e0237c6
   [255]
 
   $ hg push ../server -f
@@ -86,7 +86,7 @@
   adding file changes
   transaction abort!
   rollback completed
-  abort: rejecting draft changesets: 4e8b0e0237
+  abort: rejecting draft changesets: 4e8b0e0237c6
   [255]
 
 Grow the stack with more changesets having topic
@@ -121,7 +121,7 @@
   adding file changes
   transaction abort!
   rollback completed
-  abort: rejecting draft changesets: 4e8b0e0237
+  abort: rejecting draft changesets: 4e8b0e0237c6
   [255]
 
 Testing case when both experimental.topic-mode.server and
@@ -138,7 +138,7 @@
   adding file changes
   transaction abort!
   rollback completed
-  abort: rejecting draft changesets: 4e8b0e0237
+  abort: rejecting draft changesets: 4e8b0e0237c6
   [255]
 
 Turning the changeset public and testing push