comparison hglib/client.py @ 216:68588c652ac6

client: handle commit messages with \0 characters Mercurial allows commit messages containing \0 characters, but hglib does not properly handle them. By using the json template, these characters are correctly escaped. Note: initial change only modifies this for the 'log' command, I'll follow-up for other commands if this is ok.
author Mathias De Mare <mathias.de_mare@nokia.com>
date Wed, 08 Mar 2023 16:22:21 +0100
parents 388820908580
children 2ab42323f149
comparison
equal deleted inserted replaced
215:1e7a64588ab0 216:68588c652ac6
1 import struct, re, datetime 1 import struct, re, datetime, json
2 import hglib 2 import hglib
3 from hglib import error, util, templates, merge, context 3 from hglib import error, util, templates, merge, context
4 4
5 from hglib.util import b, cmdbuilder, BytesIO, strtobytes 5 from hglib.util import b, cmdbuilder, BytesIO, strtobytes
6 6
170 dt = datetime.datetime.fromtimestamp(posixtime) 170 dt = datetime.datetime.fromtimestamp(posixtime)
171 revs.append(revision(rev[0], rev[1], rev[2], rev[3], 171 revs.append(revision(rev[0], rev[1], rev[2], rev[3],
172 rev[4], rev[5], dt)) 172 rev[4], rev[5], dt))
173 return revs 173 return revs
174 174
175 @staticmethod
176 def _parsejsonrevs(jsonrevs):
177 revs = []
178 for rev in jsonrevs:
179 # truncate the timezone and convert to a local datetime
180 posixtime = float(rev["date"][0])
181 dt = datetime.datetime.fromtimestamp(posixtime)
182 revs.append(revision(str(rev["rev"]).encode(), rev["node"].encode(),
183 ' '.join(rev["tags"]).encode(),
184 rev["branch"].encode(), rev["user"].encode(),
185 rev["desc"].encode(), dt))
186 return revs
187
175 def runcommand(self, args, inchannels, outchannels): 188 def runcommand(self, args, inchannels, outchannels):
176 def writeblock(data): 189 def writeblock(data):
177 if self._protocoltracefn is not None: 190 if self._protocoltracefn is not None:
178 self._protocoltracefn('w', None, data) 191 self._protocoltracefn('w', None, data)
179 self.server.stdin.write(struct.pack(self.inputfmt, len(data))) 192 self.server.stdin.write(struct.pack(self.inputfmt, len(data)))
1092 exclude - exclude names matching the given patterns 1105 exclude - exclude names matching the given patterns
1093 1106
1094 """ 1107 """
1095 if hidden is None: 1108 if hidden is None:
1096 hidden = self.hidden 1109 hidden = self.hidden
1097 args = cmdbuilder(b('log'), template=templates.changeset, 1110 args = cmdbuilder(b('log'), template="json",
1098 r=revrange, f=follow, follow_first=followfirst, 1111 r=revrange, f=follow, follow_first=followfirst,
1099 d=date, C=copies, k=keyword, removed=removed, 1112 d=date, C=copies, k=keyword, removed=removed,
1100 m=onlymerges, u=user, b=branch, P=prune, 1113 m=onlymerges, u=user, b=branch, P=prune,
1101 l=limit, M=nomerges, I=include, X=exclude, 1114 l=limit, M=nomerges, I=include, X=exclude,
1102 hidden=hidden, *files) 1115 hidden=hidden, *files)
1103 1116
1104 out = self.rawcommand(args) 1117 out = self.rawcommand(args)
1105 out = out.split(b('\0'))[:-1] 1118 json_out = json.loads(out)
1106 1119
1107 return self._parserevs(out) 1120 return self._parsejsonrevs(json_out)
1108 1121
1109 def manifest(self, rev=None, all=False): 1122 def manifest(self, rev=None, all=False):
1110 """Yields (nodeid, permission, executable, symlink, file path) tuples 1123 """Yields (nodeid, permission, executable, symlink, file path) tuples
1111 for version controlled files for the given revision. If no 1124 for version controlled files for the given revision. If no
1112 revision is given, the first parent of the working directory 1125 revision is given, the first parent of the working directory