Mercurial > python-hglib
comparison hglib/client.py @ 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 | 68588c652ac6 |
children | 484b56ac4aec |
comparison
equal
deleted
inserted
replaced
223:3f9dd44be8c2 | 224:2ab42323f149 |
---|---|
157 return channel, length | 157 return channel, length |
158 else: | 158 else: |
159 return channel, self.server.stdout.read(length) | 159 return channel, self.server.stdout.read(length) |
160 | 160 |
161 @staticmethod | 161 @staticmethod |
162 def _parserevs(splitted): | |
163 '''splitted is a list of fields according to our rev.style, where | |
164 each 6 fields compose one revision. | |
165 ''' | |
166 revs = [] | |
167 for rev in util.grouper(7, splitted): | |
168 # truncate the timezone and convert to a local datetime | |
169 posixtime = float(rev[6].split(b('.'), 1)[0]) | |
170 dt = datetime.datetime.fromtimestamp(posixtime) | |
171 revs.append(revision(rev[0], rev[1], rev[2], rev[3], | |
172 rev[4], rev[5], dt)) | |
173 return revs | |
174 | |
175 @staticmethod | |
176 def _parsejsonrevs(jsonrevs): | 162 def _parsejsonrevs(jsonrevs): |
177 revs = [] | 163 revs = [] |
178 for rev in jsonrevs: | 164 for rev in jsonrevs: |
179 # truncate the timezone and convert to a local datetime | 165 # truncate the timezone and convert to a local datetime |
180 posixtime = float(rev["date"][0]) | 166 posixtime = float(rev["date"][0]) |
927 """ | 913 """ |
928 if not isinstance(rev, list): | 914 if not isinstance(rev, list): |
929 rev = [rev] | 915 rev = [rev] |
930 | 916 |
931 args = cmdbuilder(b('heads'), r=startrev, t=topological, c=closed, | 917 args = cmdbuilder(b('heads'), r=startrev, t=topological, c=closed, |
932 template=templates.changeset, hidden=self.hidden, | 918 template="json", hidden=self.hidden, |
933 *rev) | 919 *rev) |
934 | 920 |
935 def eh(ret, out, err): | 921 def eh(ret, out, err): |
936 if ret != 1: | 922 if ret != 1: |
937 raise error.CommandError(args, ret, out, err) | 923 raise error.CommandError(args, ret, out, err) |
938 return b('') | 924 return b('') |
939 | 925 |
940 out = self.rawcommand(args, eh=eh).split(b('\0'))[:-1] | 926 out = self.rawcommand(args, eh=eh) |
941 return self._parserevs(out) | 927 if not out: |
928 return [] | |
929 json_out = json.loads(out) | |
930 | |
931 return self._parsejsonrevs(json_out) | |
942 | 932 |
943 def identify(self, rev=None, source=None, num=False, id=False, branch=False, | 933 def identify(self, rev=None, source=None, num=False, id=False, branch=False, |
944 tags=False, bookmarks=False): | 934 tags=False, bookmarks=False): |
945 """Return a summary string identifying the repository state at rev | 935 """Return a summary string identifying the repository state at rev |
946 using one or two parent hash identifiers, followed by a "+" if | 936 using one or two parent hash identifiers, followed by a "+" if |
1034 insecure- do not verify server certificate (ignoring web.cacerts config) | 1024 insecure- do not verify server certificate (ignoring web.cacerts config) |
1035 subrepos - recurse into subrepositories | 1025 subrepos - recurse into subrepositories |
1036 | 1026 |
1037 """ | 1027 """ |
1038 args = cmdbuilder(b('incoming'), path, | 1028 args = cmdbuilder(b('incoming'), path, |
1039 template=templates.changeset, r=revrange, | 1029 template="json", r=revrange, |
1040 f=force, n=newest, bundle=bundle, | 1030 f=force, n=newest, bundle=bundle, |
1041 B=bookmarks, b=branch, l=limit, M=nomerges, | 1031 B=bookmarks, b=branch, l=limit, M=nomerges, |
1042 S=subrepos) | 1032 S=subrepos) |
1043 | 1033 |
1044 def eh(ret, out, err): | 1034 def eh(ret, out, err): |
1048 out = self.rawcommand(args, eh=eh) | 1038 out = self.rawcommand(args, eh=eh) |
1049 if not out: | 1039 if not out: |
1050 return [] | 1040 return [] |
1051 | 1041 |
1052 out = util.eatlines(out, 2) | 1042 out = util.eatlines(out, 2) |
1043 | |
1053 if bookmarks: | 1044 if bookmarks: |
1054 bms = [] | 1045 bms = [] |
1055 for line in out.splitlines(): | 1046 for line in out.splitlines(): |
1056 bms.append(tuple(line.split())) | 1047 bms.append(tuple(line.split())) |
1057 return bms | 1048 return bms |
1058 else: | 1049 else: |
1059 out = out.split(b('\0'))[:-1] | 1050 json_out = json.loads(out) |
1060 return self._parserevs(out) | 1051 return self._parsejsonrevs(json_out) |
1061 | 1052 |
1062 def log(self, revrange=None, files=[], follow=False, | 1053 def log(self, revrange=None, files=[], follow=False, |
1063 followfirst=False, date=None, copies=False, keyword=None, | 1054 followfirst=False, date=None, copies=False, keyword=None, |
1064 removed=False, onlymerges=False, user=None, branch=None, | 1055 removed=False, onlymerges=False, user=None, branch=None, |
1065 prune=None, hidden=None, limit=None, nomerges=False, | 1056 prune=None, hidden=None, limit=None, nomerges=False, |
1233 subrepositories | 1224 subrepositories |
1234 | 1225 |
1235 """ | 1226 """ |
1236 args = cmdbuilder(b('outgoing'), | 1227 args = cmdbuilder(b('outgoing'), |
1237 path, | 1228 path, |
1238 template=templates.changeset, r=revrange, | 1229 template="json", r=revrange, |
1239 f=force, n=newest, B=bookmarks, | 1230 f=force, n=newest, B=bookmarks, |
1240 b=branch, S=subrepos) | 1231 b=branch, S=subrepos) |
1241 | 1232 |
1242 def eh(ret, out, err): | 1233 def eh(ret, out, err): |
1243 if ret != 1: | 1234 if ret != 1: |
1252 bms = [] | 1243 bms = [] |
1253 for line in out.splitlines(): | 1244 for line in out.splitlines(): |
1254 bms.append(tuple(line.split())) | 1245 bms.append(tuple(line.split())) |
1255 return bms | 1246 return bms |
1256 else: | 1247 else: |
1257 out = out.split(b('\0'))[:-1] | 1248 json_out = json.loads(out) |
1258 return self._parserevs(out) | 1249 return self._parsejsonrevs(json_out) |
1259 | 1250 |
1260 def parents(self, rev=None, file=None): | 1251 def parents(self, rev=None, file=None): |
1261 """Return the working directory's parent revisions. If rev is given, | 1252 """Return the working directory's parent revisions. If rev is given, |
1262 the parent of that revision will be printed. If file is given, | 1253 the parent of that revision will be printed. If file is given, |
1263 the revision in which the file was last changed (before the | 1254 the revision in which the file was last changed (before the |
1264 working directory revision or the revision specified by rev) | 1255 working directory revision or the revision specified by rev) |
1265 is returned. | 1256 is returned. |
1266 | 1257 |
1267 """ | 1258 """ |
1268 args = cmdbuilder(b('parents'), file, template=templates.changeset, | 1259 args = cmdbuilder(b('parents'), file, template="json", |
1269 r=rev, hidden=self.hidden) | 1260 r=rev, hidden=self.hidden) |
1270 | 1261 |
1271 out = self.rawcommand(args) | 1262 out = self.rawcommand(args) |
1272 if not out: | 1263 if not out: |
1273 return | 1264 return |
1274 | 1265 |
1275 out = out.split(b('\0'))[:-1] | 1266 json_out = json.loads(out) |
1276 | 1267 |
1277 return self._parserevs(out) | 1268 if not json_out: |
1269 return | |
1270 | |
1271 return self._parsejsonrevs(json_out) | |
1278 | 1272 |
1279 def paths(self, name=None): | 1273 def paths(self, name=None): |
1280 """ | 1274 """ |
1281 Return the definition of given symbolic path name. If no name is given, | 1275 Return the definition of given symbolic path name. If no name is given, |
1282 return a dictionary of pathname : url of all available names. | 1276 return a dictionary of pathname : url of all available names. |
1680 """ | 1674 """ |
1681 Return the tip revision (usually just called the tip) which is the | 1675 Return the tip revision (usually just called the tip) which is the |
1682 changeset most recently added to the repository (and therefore the most | 1676 changeset most recently added to the repository (and therefore the most |
1683 recently changed head). | 1677 recently changed head). |
1684 """ | 1678 """ |
1685 args = cmdbuilder(b('tip'), template=templates.changeset, | 1679 args = cmdbuilder(b('tip'), template="json", |
1686 hidden=self.hidden) | 1680 hidden=self.hidden) |
1687 out = self.rawcommand(args) | 1681 out = self.rawcommand(args) |
1688 out = out.split(b('\0')) | 1682 json_out = json.loads(out) |
1689 | 1683 |
1690 return self._parserevs(out)[0] | 1684 return self._parsejsonrevs(json_out)[0] |
1691 | 1685 |
1692 def update(self, rev=None, clean=False, check=False, date=None): | 1686 def update(self, rev=None, clean=False, check=False, date=None): |
1693 """ | 1687 """ |
1694 Update the repository's working directory to changeset specified by rev. | 1688 Update the repository's working directory to changeset specified by rev. |
1695 If rev isn't specified, update to the tip of the current named branch. | 1689 If rev isn't specified, update to the tip of the current named branch. |