changeset 224:2ab42323f149

client: handle commit messages with \0 characters for all commands Each of the impacted commands will now use the 'json' template, which they all support as of Mercurial 3.7.3 (the first version tested in the regression tests). Note: I tried to add a test with null bytes, but both hglib and using hg directly through subprocess rejected adding a commit message with a null byte.
author Mathias De Mare <mathias.de_mare@nokia.com>
date Mon, 13 Mar 2023 15:32:20 +0100
parents 3f9dd44be8c2
children fba806958dba
files hglib/client.py
diffstat 1 files changed, 23 insertions(+), 29 deletions(-) [+]
line wrap: on
line diff
--- a/hglib/client.py	Thu Mar 09 16:18:54 2023 +0100
+++ b/hglib/client.py	Mon Mar 13 15:32:20 2023 +0100
@@ -159,20 +159,6 @@
             return channel, self.server.stdout.read(length)
 
     @staticmethod
-    def _parserevs(splitted):
-        '''splitted is a list of fields according to our rev.style, where
-        each 6 fields compose one revision.
-        '''
-        revs = []
-        for rev in util.grouper(7, splitted):
-            # truncate the timezone and convert to a local datetime
-            posixtime = float(rev[6].split(b('.'), 1)[0])
-            dt = datetime.datetime.fromtimestamp(posixtime)
-            revs.append(revision(rev[0], rev[1], rev[2], rev[3],
-                                 rev[4], rev[5], dt))
-        return revs
-
-    @staticmethod
     def _parsejsonrevs(jsonrevs):
         revs = []
         for rev in jsonrevs:
@@ -929,7 +915,7 @@
             rev = [rev]
 
         args = cmdbuilder(b('heads'), r=startrev, t=topological, c=closed,
-                          template=templates.changeset, hidden=self.hidden,
+                          template="json", hidden=self.hidden,
                           *rev)
 
         def eh(ret, out, err):
@@ -937,8 +923,12 @@
                 raise error.CommandError(args, ret, out, err)
             return b('')
 
-        out = self.rawcommand(args, eh=eh).split(b('\0'))[:-1]
-        return self._parserevs(out)
+        out = self.rawcommand(args, eh=eh)
+        if not out:
+            return []
+        json_out = json.loads(out)
+
+        return self._parsejsonrevs(json_out)
 
     def identify(self, rev=None, source=None, num=False, id=False, branch=False,
                  tags=False, bookmarks=False):
@@ -1036,7 +1026,7 @@
 
         """
         args = cmdbuilder(b('incoming'), path,
-                          template=templates.changeset, r=revrange,
+                          template="json", r=revrange,
                           f=force, n=newest, bundle=bundle,
                           B=bookmarks, b=branch, l=limit, M=nomerges,
                           S=subrepos)
@@ -1050,14 +1040,15 @@
             return []
 
         out = util.eatlines(out, 2)
+
         if bookmarks:
             bms = []
             for line in out.splitlines():
                 bms.append(tuple(line.split()))
             return bms
         else:
-            out = out.split(b('\0'))[:-1]
-            return self._parserevs(out)
+            json_out = json.loads(out)
+            return self._parsejsonrevs(json_out)
 
     def log(self, revrange=None, files=[], follow=False,
             followfirst=False, date=None, copies=False, keyword=None,
@@ -1235,7 +1226,7 @@
         """
         args = cmdbuilder(b('outgoing'),
                           path,
-                          template=templates.changeset, r=revrange,
+                          template="json", r=revrange,
                           f=force, n=newest, B=bookmarks,
                           b=branch, S=subrepos)
 
@@ -1254,8 +1245,8 @@
                 bms.append(tuple(line.split()))
             return bms
         else:
-            out = out.split(b('\0'))[:-1]
-            return self._parserevs(out)
+            json_out = json.loads(out)
+            return self._parsejsonrevs(json_out)
 
     def parents(self, rev=None, file=None):
         """Return the working directory's parent revisions. If rev is given,
@@ -1265,16 +1256,19 @@
         is returned.
 
         """
-        args = cmdbuilder(b('parents'), file, template=templates.changeset,
+        args = cmdbuilder(b('parents'), file, template="json",
                           r=rev, hidden=self.hidden)
 
         out = self.rawcommand(args)
         if not out:
             return
 
-        out = out.split(b('\0'))[:-1]
+        json_out = json.loads(out)
 
-        return self._parserevs(out)
+        if not json_out:
+            return
+
+        return self._parsejsonrevs(json_out)
 
     def paths(self, name=None):
         """
@@ -1682,12 +1676,12 @@
         changeset most recently added to the repository (and therefore the most
         recently changed head).
         """
-        args = cmdbuilder(b('tip'), template=templates.changeset,
+        args = cmdbuilder(b('tip'), template="json",
                           hidden=self.hidden)
         out = self.rawcommand(args)
-        out = out.split(b('\0'))
+        json_out = json.loads(out)
 
-        return self._parserevs(out)[0]
+        return self._parsejsonrevs(json_out)[0]
 
     def update(self, rev=None, clean=False, check=False, date=None):
         """