# HG changeset patch # User Mathias De Mare # Date 1678717940 -3600 # Node ID 2ab42323f1499669fe8b110b0d28b93bcd3044e4 # Parent 3f9dd44be8c2b2290c7e3109dcb92639c99d635f 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. diff -r 3f9dd44be8c2 -r 2ab42323f149 hglib/client.py --- 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): """