diff hgext3rd/topic/__init__.py @ 6825:ba7ede61f6d4

topic: implement dirstate.topic() and dirstate.tns() The implementations closely follow the implementation of dirstate.branch(), including dirstate.setbranch(), from before a6e0b7d4ae9d. So far in this patch we don't write topics or topic namespaces in a transaction. But we do have `tr` argument in settopic() and settns() because it's easier to introduce it now (even though it's simply ignored). Also because of the branch implementation in core, topic and tns values on disk now have a '\n' character at the end.
author Anton Shestakov <av6@dwimlabs.net>
date Sat, 09 Dec 2023 15:26:35 -0300
parents ed00ed185249
children 90f867d5be22
line wrap: on
line diff
--- a/hgext3rd/topic/__init__.py	Fri Jul 12 15:44:12 2024 +0400
+++ b/hgext3rd/topic/__init__.py	Sat Dec 09 15:26:35 2023 -0300
@@ -172,6 +172,7 @@
     commands,
     configitems,
     context,
+    dirstate,
     encoding,
     error,
     exchange,
@@ -720,8 +721,7 @@
 
         @property
         def currenttns(self):
-            tns = self.vfs.tryread(b'topic-namespace') or b'none'
-            return encoding.tolocal(tns)
+            return self.dirstate.tns()
 
         @util.propertycache
         def _topiccache(self):
@@ -740,8 +740,7 @@
 
         @property
         def currenttopic(self):
-            topic = self.vfs.tryread(b'topic')
-            return encoding.tolocal(topic)
+            return self.dirstate.topic()
 
         # overwritten at the instance level by topicmap.py
         _autobranchmaptopic = True
@@ -927,6 +926,59 @@
             b'topics', b'topic', namemap=_namemap, nodemap=_nodemap,
             listnames=lambda repo: repo.topics))
 
+    class topicdirstate(dirstate.dirstate):
+        @dirstate.repocache(b'topic')
+        def _topic(self):
+            try:
+                return self._opener.read(b'topic').strip() or b''
+            except IOError as inst:
+                if inst.errno != errno.ENOENT:
+                    raise
+                return b''
+
+        def topic(self):
+            return encoding.tolocal(self._topic)
+
+        def settopic(self, topic, tr):
+            self.__class__._topic.set(self, encoding.fromlocal(topic))
+            del topic  # safeguard to not use it after adjusting encoding
+            vfs = self._opener
+            if self._topic != b'':
+                with vfs(b'topic', b'w', atomictemp=True, checkambig=True) as f:
+                    f.write(self._topic + b'\n')
+            else:
+                vfs.tryunlink(b'topic')
+            ce = self._filecache[b'_topic']
+            if ce:
+                ce.refresh()
+
+        @dirstate.repocache(b'topic-namespace')
+        def _tns(self):
+            try:
+                return self._opener.read(b'topic-namespace').strip() or b'none'
+            except IOError as inst:
+                if inst.errno != errno.ENOENT:
+                    raise
+                return b'none'
+
+        def tns(self):
+            return encoding.tolocal(self._tns)
+
+        def settns(self, tns, tr):
+            self.__class__._tns.set(self, encoding.fromlocal(tns))
+            del tns  # safeguard to not use it after adjusting encoding
+            vfs = self._opener
+            if self._tns != b'none':
+                with vfs(b'topic-namespace', b'w', atomictemp=True, checkambig=True) as f:
+                    f.write(self._tns + b'\n')
+            else:
+                vfs.tryunlink(b'topic-namespace')
+            ce = self._filecache[b'_tns']
+            if ce:
+                ce.refresh()
+
+    dirstate.dirstate = topicdirstate
+
 templatekeyword = registrar.templatekeyword()
 
 @templatekeyword(b'topic', requires={b'ctx'})
@@ -1304,12 +1356,11 @@
 def _changecurrenttopic(repo, newtopic):
     """changes the current topic."""
 
-    if newtopic:
-        with repo.wlock():
-            repo.vfs.write(b'topic', newtopic)
-    else:
-        if repo.vfs.exists(b'topic'):
-            repo.vfs.unlink(b'topic')
+    # assert newtopic is not None
+    if not newtopic:
+        newtopic = b''
+    with repo.wlock():
+        repo.dirstate.settopic(newtopic, repo.currenttransaction())
 
 def _changetopics(ui, repo, revs, newtopic):
     """ Changes topic to newtopic of all the revisions in the revset and return
@@ -1568,7 +1619,7 @@
         hint = _(b"see 'hg help -e topic.topic-mode' for details")
         if opts.get('topic'):
             t = opts['topic']
-            repo.vfs.write(b'topic', t)
+            _changecurrenttopic(repo, t)
         elif opts.get('amend'):
             pass
         elif notopic and mayabort:
@@ -1579,7 +1630,8 @@
             if not ui.quiet:
                 ui.warn((b"(%s)\n") % hint)
         elif notopic and mayrandom:
-            repo.vfs.write(b'topic', randomname.randomtopicname(ui))
+            t = randomname.randomtopicname(ui)
+            _changecurrenttopic(repo, t)
         return orig(ui, repo, *args, **opts)
 
 def committextwrap(orig, repo, ctx, subs, extramsg):
@@ -1795,11 +1847,8 @@
             raise compat.InputError(msg, hint=hint)
 
 def _changecurrenttns(repo, tns):
-    if tns != b'none':
-        with repo.wlock():
-            repo.vfs.write(b'topic-namespace', tns)
-    else:
-        repo.vfs.unlinkpath(b'topic-namespace', ignoremissing=True)
+    with repo.wlock():
+        repo.dirstate.settns(tns, repo.currenttransaction())
 
 @command(b'debug-topic-namespace', [
         (b'', b'clear', False, b'clear active topic namespace if any'),