diff hgext3rd/topic/__init__.py @ 6679:a1453b601ff1 mercurial-6.4

test-compat: merge mercurial-6.5 into mercurial-6.4
author Anton Shestakov <av6@dwimlabs.net>
date Sun, 04 Feb 2024 15:46:44 -0300
parents 991d78f5c401
children 369e248b6312 752201811d5a
line wrap: on
line diff
--- a/hgext3rd/topic/__init__.py	Fri Oct 13 16:12:31 2023 -0300
+++ b/hgext3rd/topic/__init__.py	Sun Feb 04 15:46:44 2024 -0300
@@ -157,6 +157,7 @@
 
 from __future__ import absolute_import
 
+import errno
 import functools
 import re
 import time
@@ -236,9 +237,9 @@
               b'log.topic': b'green_background',
               }
 
-__version__ = b'1.1.0.dev0'
+__version__ = b'1.1.1.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'
+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'
 minimumhgversion = b'4.9'
 buglink = b'https://bz.mercurial-scm.org/'
 
@@ -277,6 +278,12 @@
 configitem(b'_internal', b'tns-explicit-target',
            default=False,
 )
+# used for selecting what topic and topic namespace values take priority during
+# some history rewriting operations: 'local' prefers active topic and tns,
+# 'other' prefers values in commit extras, if there are any
+configitem(b'_internal', b'topic-source',
+           default=b'other',
+)
 configitem(b'devel', b'tns-report-transactions',
            default=lambda: [],
 )
@@ -516,6 +523,30 @@
     if tr.changes[b'tns']:
         repo.ui.status(b'topic namespaces affected: %s\n' % b' '.join(sorted(tr.changes[b'tns'])))
 
+def wrapmakebundlerepository(orig, ui, repopath, bundlepath):
+    repo = orig(ui, repopath, bundlepath)
+
+    # We want bundle repos to also have caches for topic extension, because we
+    # want to, for example, see topic and topic namespaces in `hg incoming`
+    # regardless if the bundle repo has topic extension, as long as local repo
+    # has topic enabled.
+    class topicbundlerepo(repo.__class__):
+        @util.propertycache
+        def _tnscache(self):
+            return {}
+
+        @util.propertycache
+        def _topiccache(self):
+            return {}
+
+        def invalidatecaches(self):
+            self._tnscache.clear()
+            self._topiccache.clear()
+            super(topicbundlerepo, self).invalidatecaches()
+
+    repo.__class__ = topicbundlerepo
+    return repo
+
 def uisetup(ui):
     destination.modsetup(ui)
     discovery.modsetup(ui)
@@ -601,27 +632,9 @@
     except (KeyError, AttributeError):
         pass
 
-    server.setupserver(ui)
-
-    # We want bundle repos to also have caches for topic extension, because we
-    # want to, for example, see topic and topic namespaces in `hg incoming`
-    # regardless if the bundle repo has topic extension, as long as local repo
-    # has topic enabled.
-    class topicbundlerepo(bundlerepo.bundlerepository):
-        @util.propertycache
-        def _tnscache(self):
-            return {}
+    extensions.wrapfunction(bundlerepo, 'makebundlerepository', wrapmakebundlerepository)
 
-        @util.propertycache
-        def _topiccache(self):
-            return {}
-
-        def invalidatecaches(self):
-            self._tnscache.clear()
-            self._topiccache.clear()
-            super(topicbundlerepo, self).invalidatecaches()
-
-    bundlerepo.bundlerepository = topicbundlerepo
+    server.setupserver(ui)
 
 def reposetup(ui, repo):
     if not isinstance(repo, localrepo.localrepository):
@@ -665,15 +678,26 @@
                 # bypass the core "nothing changed" logic
                 configoverride = self.ui.configoverride({
                     (b'ui', b'allowemptycommit'): True
-                })
+                }, b'topic-extension')
             with configoverride:
                 return super(topicrepo, self).commit(*args, **kwargs)
 
         def commitctx(self, ctx, *args, **kwargs):
             if isinstance(ctx, context.workingcommitctx):
-                current = self.currenttopic
-                if current and constants.extrakey not in ctx.extra():
-                    ctx.extra()[constants.extrakey] = current
+                tns = self.currenttns
+                topic = self.currenttopic
+                # topic source:
+                # - 'local': we need to put currently active tns and topic into
+                #   commit extras in any case
+                # - 'other': we could use active tns and topic, but only if
+                #   commit extras don't already have them
+                ts = self.ui.config(b'_internal', b'topic-source')
+                if ts == b'local' or (tns != b'none' and b'topic-namespace' not in ctx.extra()):
+                    # default value will be dropped from extra later on
+                    ctx.extra()[b'topic-namespace'] = tns
+                if ts == b'local' or (topic and constants.extrakey not in ctx.extra()):
+                    # empty value will be dropped from extra later on
+                    ctx.extra()[constants.extrakey] = topic
             return super(topicrepo, self).commitctx(ctx, *args, **kwargs)
 
         @util.propertycache
@@ -693,7 +717,30 @@
 
         @property
         def currenttns(self):
-            return self.vfs.tryread(b'topic-namespace') or b'none'
+            tns = self.vfs.tryread(b'topic-namespace')
+            # 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':
+                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'
+            return encoding.tolocal(tns)
 
         @util.propertycache
         def _topiccache(self):
@@ -712,7 +759,8 @@
 
         @property
         def currenttopic(self):
-            return self.vfs.tryread(b'topic')
+            topic = self.vfs.tryread(b'topic')
+            return encoding.tolocal(topic)
 
         # overwritten at the instance level by topicmap.py
         _autobranchmaptopic = True
@@ -1092,7 +1140,7 @@
         # Have some restrictions on the topic name just like bookmark name
         scmutil.checknewlabel(repo, topic, b'topic')
 
-        helptxt = _(b"topic names can only consist of alphanumeric, '-'"
+        helptxt = _(b"topic names can only consist of alphanumeric, '-',"
                     b" '_' and '.' characters")
         try:
             utopic = encoding.unifromlocal(topic)
@@ -1679,10 +1727,10 @@
             if pctx.phase() > phases.public:
                 tns = pctx.topic_namespace()
                 t = pctx.topic()
-            repo.vfs.write(b'topic-namespace', tns)
+            _changecurrenttns(repo, tns)
             if tns != b'none' and tns != otns:
                 repo.ui.status(_(b"switching to topic-namespace %s\n") % tns)
-            repo.vfs.write(b'topic', t)
+            _changecurrenttopic(repo, t)
             if t and t != ot:
                 repo.ui.status(_(b"switching to topic %s\n") % t)
             if ot and not t:
@@ -1839,6 +1887,18 @@
         if b'/' in tns:
             raise error.Abort(_(b"topic namespace cannot contain '/' character"))
         scmutil.checknewlabel(repo, tns, b'topic namespace')
+
+        helptxt = _(b"topic namespace names can only consist of alphanumeric, "
+                    b"'-', '_' and '.' characters")
+        try:
+            utns = encoding.unifromlocal(tns)
+        except error.Abort:
+            # Maybe we should allow these topic names as well, as long as they
+            # don't break any other rules
+            utns = ''
+        rmatch = re.match(r'[-_.\w]+', utns, re.UNICODE)
+        if not utns or not rmatch or rmatch.group(0) != utns:
+            raise compat.InputError(_(b"invalid topic namespace name: '%s'") % tns, hint=helptxt)
     ctns = repo.currenttns
     _changecurrenttns(repo, tns)
     if ctns == b'none' and tns != b'none':