##// END OF EJS Templates
changelog: remove redundant parentheses...
Gregory Szorc -
r28282:d698c11b default
parent child Browse files
Show More
@@ -1,417 +1,417 b''
1 # changelog.py - changelog class for mercurial
1 # changelog.py - changelog class for mercurial
2 #
2 #
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from __future__ import absolute_import
8 from __future__ import absolute_import
9
9
10 from .i18n import _
10 from .i18n import _
11 from .node import (
11 from .node import (
12 bin,
12 bin,
13 hex,
13 hex,
14 nullid,
14 nullid,
15 )
15 )
16
16
17 from . import (
17 from . import (
18 encoding,
18 encoding,
19 error,
19 error,
20 revlog,
20 revlog,
21 util,
21 util,
22 )
22 )
23
23
24 _defaultextra = {'branch': 'default'}
24 _defaultextra = {'branch': 'default'}
25
25
26 def _string_escape(text):
26 def _string_escape(text):
27 """
27 """
28 >>> d = {'nl': chr(10), 'bs': chr(92), 'cr': chr(13), 'nul': chr(0)}
28 >>> d = {'nl': chr(10), 'bs': chr(92), 'cr': chr(13), 'nul': chr(0)}
29 >>> s = "ab%(nl)scd%(bs)s%(bs)sn%(nul)sab%(cr)scd%(bs)s%(nl)s" % d
29 >>> s = "ab%(nl)scd%(bs)s%(bs)sn%(nul)sab%(cr)scd%(bs)s%(nl)s" % d
30 >>> s
30 >>> s
31 'ab\\ncd\\\\\\\\n\\x00ab\\rcd\\\\\\n'
31 'ab\\ncd\\\\\\\\n\\x00ab\\rcd\\\\\\n'
32 >>> res = _string_escape(s)
32 >>> res = _string_escape(s)
33 >>> s == res.decode('string_escape')
33 >>> s == res.decode('string_escape')
34 True
34 True
35 """
35 """
36 # subset of the string_escape codec
36 # subset of the string_escape codec
37 text = text.replace('\\', '\\\\').replace('\n', '\\n').replace('\r', '\\r')
37 text = text.replace('\\', '\\\\').replace('\n', '\\n').replace('\r', '\\r')
38 return text.replace('\0', '\\0')
38 return text.replace('\0', '\\0')
39
39
40 def decodeextra(text):
40 def decodeextra(text):
41 """
41 """
42 >>> sorted(decodeextra(encodeextra({'foo': 'bar', 'baz': chr(0) + '2'})
42 >>> sorted(decodeextra(encodeextra({'foo': 'bar', 'baz': chr(0) + '2'})
43 ... ).iteritems())
43 ... ).iteritems())
44 [('baz', '\\x002'), ('branch', 'default'), ('foo', 'bar')]
44 [('baz', '\\x002'), ('branch', 'default'), ('foo', 'bar')]
45 >>> sorted(decodeextra(encodeextra({'foo': 'bar',
45 >>> sorted(decodeextra(encodeextra({'foo': 'bar',
46 ... 'baz': chr(92) + chr(0) + '2'})
46 ... 'baz': chr(92) + chr(0) + '2'})
47 ... ).iteritems())
47 ... ).iteritems())
48 [('baz', '\\\\\\x002'), ('branch', 'default'), ('foo', 'bar')]
48 [('baz', '\\\\\\x002'), ('branch', 'default'), ('foo', 'bar')]
49 """
49 """
50 extra = _defaultextra.copy()
50 extra = _defaultextra.copy()
51 for l in text.split('\0'):
51 for l in text.split('\0'):
52 if l:
52 if l:
53 if '\\0' in l:
53 if '\\0' in l:
54 # fix up \0 without getting into trouble with \\0
54 # fix up \0 without getting into trouble with \\0
55 l = l.replace('\\\\', '\\\\\n')
55 l = l.replace('\\\\', '\\\\\n')
56 l = l.replace('\\0', '\0')
56 l = l.replace('\\0', '\0')
57 l = l.replace('\n', '')
57 l = l.replace('\n', '')
58 k, v = l.decode('string_escape').split(':', 1)
58 k, v = l.decode('string_escape').split(':', 1)
59 extra[k] = v
59 extra[k] = v
60 return extra
60 return extra
61
61
62 def encodeextra(d):
62 def encodeextra(d):
63 # keys must be sorted to produce a deterministic changelog entry
63 # keys must be sorted to produce a deterministic changelog entry
64 items = [_string_escape('%s:%s' % (k, d[k])) for k in sorted(d)]
64 items = [_string_escape('%s:%s' % (k, d[k])) for k in sorted(d)]
65 return "\0".join(items)
65 return "\0".join(items)
66
66
67 def stripdesc(desc):
67 def stripdesc(desc):
68 """strip trailing whitespace and leading and trailing empty lines"""
68 """strip trailing whitespace and leading and trailing empty lines"""
69 return '\n'.join([l.rstrip() for l in desc.splitlines()]).strip('\n')
69 return '\n'.join([l.rstrip() for l in desc.splitlines()]).strip('\n')
70
70
71 class appender(object):
71 class appender(object):
72 '''the changelog index must be updated last on disk, so we use this class
72 '''the changelog index must be updated last on disk, so we use this class
73 to delay writes to it'''
73 to delay writes to it'''
74 def __init__(self, vfs, name, mode, buf):
74 def __init__(self, vfs, name, mode, buf):
75 self.data = buf
75 self.data = buf
76 fp = vfs(name, mode)
76 fp = vfs(name, mode)
77 self.fp = fp
77 self.fp = fp
78 self.offset = fp.tell()
78 self.offset = fp.tell()
79 self.size = vfs.fstat(fp).st_size
79 self.size = vfs.fstat(fp).st_size
80
80
81 def end(self):
81 def end(self):
82 return self.size + len("".join(self.data))
82 return self.size + len("".join(self.data))
83 def tell(self):
83 def tell(self):
84 return self.offset
84 return self.offset
85 def flush(self):
85 def flush(self):
86 pass
86 pass
87 def close(self):
87 def close(self):
88 self.fp.close()
88 self.fp.close()
89
89
90 def seek(self, offset, whence=0):
90 def seek(self, offset, whence=0):
91 '''virtual file offset spans real file and data'''
91 '''virtual file offset spans real file and data'''
92 if whence == 0:
92 if whence == 0:
93 self.offset = offset
93 self.offset = offset
94 elif whence == 1:
94 elif whence == 1:
95 self.offset += offset
95 self.offset += offset
96 elif whence == 2:
96 elif whence == 2:
97 self.offset = self.end() + offset
97 self.offset = self.end() + offset
98 if self.offset < self.size:
98 if self.offset < self.size:
99 self.fp.seek(self.offset)
99 self.fp.seek(self.offset)
100
100
101 def read(self, count=-1):
101 def read(self, count=-1):
102 '''only trick here is reads that span real file and data'''
102 '''only trick here is reads that span real file and data'''
103 ret = ""
103 ret = ""
104 if self.offset < self.size:
104 if self.offset < self.size:
105 s = self.fp.read(count)
105 s = self.fp.read(count)
106 ret = s
106 ret = s
107 self.offset += len(s)
107 self.offset += len(s)
108 if count > 0:
108 if count > 0:
109 count -= len(s)
109 count -= len(s)
110 if count != 0:
110 if count != 0:
111 doff = self.offset - self.size
111 doff = self.offset - self.size
112 self.data.insert(0, "".join(self.data))
112 self.data.insert(0, "".join(self.data))
113 del self.data[1:]
113 del self.data[1:]
114 s = self.data[0][doff:doff + count]
114 s = self.data[0][doff:doff + count]
115 self.offset += len(s)
115 self.offset += len(s)
116 ret += s
116 ret += s
117 return ret
117 return ret
118
118
119 def write(self, s):
119 def write(self, s):
120 self.data.append(str(s))
120 self.data.append(str(s))
121 self.offset += len(s)
121 self.offset += len(s)
122
122
123 def _divertopener(opener, target):
123 def _divertopener(opener, target):
124 """build an opener that writes in 'target.a' instead of 'target'"""
124 """build an opener that writes in 'target.a' instead of 'target'"""
125 def _divert(name, mode='r'):
125 def _divert(name, mode='r'):
126 if name != target:
126 if name != target:
127 return opener(name, mode)
127 return opener(name, mode)
128 return opener(name + ".a", mode)
128 return opener(name + ".a", mode)
129 return _divert
129 return _divert
130
130
131 def _delayopener(opener, target, buf):
131 def _delayopener(opener, target, buf):
132 """build an opener that stores chunks in 'buf' instead of 'target'"""
132 """build an opener that stores chunks in 'buf' instead of 'target'"""
133 def _delay(name, mode='r'):
133 def _delay(name, mode='r'):
134 if name != target:
134 if name != target:
135 return opener(name, mode)
135 return opener(name, mode)
136 return appender(opener, name, mode, buf)
136 return appender(opener, name, mode, buf)
137 return _delay
137 return _delay
138
138
139 class changelog(revlog.revlog):
139 class changelog(revlog.revlog):
140 def __init__(self, opener):
140 def __init__(self, opener):
141 revlog.revlog.__init__(self, opener, "00changelog.i")
141 revlog.revlog.__init__(self, opener, "00changelog.i")
142 if self._initempty:
142 if self._initempty:
143 # changelogs don't benefit from generaldelta
143 # changelogs don't benefit from generaldelta
144 self.version &= ~revlog.REVLOGGENERALDELTA
144 self.version &= ~revlog.REVLOGGENERALDELTA
145 self._generaldelta = False
145 self._generaldelta = False
146 self._realopener = opener
146 self._realopener = opener
147 self._delayed = False
147 self._delayed = False
148 self._delaybuf = None
148 self._delaybuf = None
149 self._divert = False
149 self._divert = False
150 self.filteredrevs = frozenset()
150 self.filteredrevs = frozenset()
151
151
152 def tip(self):
152 def tip(self):
153 """filtered version of revlog.tip"""
153 """filtered version of revlog.tip"""
154 for i in xrange(len(self) -1, -2, -1):
154 for i in xrange(len(self) -1, -2, -1):
155 if i not in self.filteredrevs:
155 if i not in self.filteredrevs:
156 return self.node(i)
156 return self.node(i)
157
157
158 def __contains__(self, rev):
158 def __contains__(self, rev):
159 """filtered version of revlog.__contains__"""
159 """filtered version of revlog.__contains__"""
160 return (0 <= rev < len(self)
160 return (0 <= rev < len(self)
161 and rev not in self.filteredrevs)
161 and rev not in self.filteredrevs)
162
162
163 def __iter__(self):
163 def __iter__(self):
164 """filtered version of revlog.__iter__"""
164 """filtered version of revlog.__iter__"""
165 if len(self.filteredrevs) == 0:
165 if len(self.filteredrevs) == 0:
166 return revlog.revlog.__iter__(self)
166 return revlog.revlog.__iter__(self)
167
167
168 def filterediter():
168 def filterediter():
169 for i in xrange(len(self)):
169 for i in xrange(len(self)):
170 if i not in self.filteredrevs:
170 if i not in self.filteredrevs:
171 yield i
171 yield i
172
172
173 return filterediter()
173 return filterediter()
174
174
175 def revs(self, start=0, stop=None):
175 def revs(self, start=0, stop=None):
176 """filtered version of revlog.revs"""
176 """filtered version of revlog.revs"""
177 for i in super(changelog, self).revs(start, stop):
177 for i in super(changelog, self).revs(start, stop):
178 if i not in self.filteredrevs:
178 if i not in self.filteredrevs:
179 yield i
179 yield i
180
180
181 @util.propertycache
181 @util.propertycache
182 def nodemap(self):
182 def nodemap(self):
183 # XXX need filtering too
183 # XXX need filtering too
184 self.rev(self.node(0))
184 self.rev(self.node(0))
185 return self._nodecache
185 return self._nodecache
186
186
187 def reachableroots(self, minroot, heads, roots, includepath=False):
187 def reachableroots(self, minroot, heads, roots, includepath=False):
188 return self.index.reachableroots2(minroot, heads, roots, includepath)
188 return self.index.reachableroots2(minroot, heads, roots, includepath)
189
189
190 def headrevs(self):
190 def headrevs(self):
191 if self.filteredrevs:
191 if self.filteredrevs:
192 try:
192 try:
193 return self.index.headrevsfiltered(self.filteredrevs)
193 return self.index.headrevsfiltered(self.filteredrevs)
194 # AttributeError covers non-c-extension environments and
194 # AttributeError covers non-c-extension environments and
195 # old c extensions without filter handling.
195 # old c extensions without filter handling.
196 except AttributeError:
196 except AttributeError:
197 return self._headrevs()
197 return self._headrevs()
198
198
199 return super(changelog, self).headrevs()
199 return super(changelog, self).headrevs()
200
200
201 def strip(self, *args, **kwargs):
201 def strip(self, *args, **kwargs):
202 # XXX make something better than assert
202 # XXX make something better than assert
203 # We can't expect proper strip behavior if we are filtered.
203 # We can't expect proper strip behavior if we are filtered.
204 assert not self.filteredrevs
204 assert not self.filteredrevs
205 super(changelog, self).strip(*args, **kwargs)
205 super(changelog, self).strip(*args, **kwargs)
206
206
207 def rev(self, node):
207 def rev(self, node):
208 """filtered version of revlog.rev"""
208 """filtered version of revlog.rev"""
209 r = super(changelog, self).rev(node)
209 r = super(changelog, self).rev(node)
210 if r in self.filteredrevs:
210 if r in self.filteredrevs:
211 raise error.FilteredLookupError(hex(node), self.indexfile,
211 raise error.FilteredLookupError(hex(node), self.indexfile,
212 _('filtered node'))
212 _('filtered node'))
213 return r
213 return r
214
214
215 def node(self, rev):
215 def node(self, rev):
216 """filtered version of revlog.node"""
216 """filtered version of revlog.node"""
217 if rev in self.filteredrevs:
217 if rev in self.filteredrevs:
218 raise error.FilteredIndexError(rev)
218 raise error.FilteredIndexError(rev)
219 return super(changelog, self).node(rev)
219 return super(changelog, self).node(rev)
220
220
221 def linkrev(self, rev):
221 def linkrev(self, rev):
222 """filtered version of revlog.linkrev"""
222 """filtered version of revlog.linkrev"""
223 if rev in self.filteredrevs:
223 if rev in self.filteredrevs:
224 raise error.FilteredIndexError(rev)
224 raise error.FilteredIndexError(rev)
225 return super(changelog, self).linkrev(rev)
225 return super(changelog, self).linkrev(rev)
226
226
227 def parentrevs(self, rev):
227 def parentrevs(self, rev):
228 """filtered version of revlog.parentrevs"""
228 """filtered version of revlog.parentrevs"""
229 if rev in self.filteredrevs:
229 if rev in self.filteredrevs:
230 raise error.FilteredIndexError(rev)
230 raise error.FilteredIndexError(rev)
231 return super(changelog, self).parentrevs(rev)
231 return super(changelog, self).parentrevs(rev)
232
232
233 def flags(self, rev):
233 def flags(self, rev):
234 """filtered version of revlog.flags"""
234 """filtered version of revlog.flags"""
235 if rev in self.filteredrevs:
235 if rev in self.filteredrevs:
236 raise error.FilteredIndexError(rev)
236 raise error.FilteredIndexError(rev)
237 return super(changelog, self).flags(rev)
237 return super(changelog, self).flags(rev)
238
238
239 def delayupdate(self, tr):
239 def delayupdate(self, tr):
240 "delay visibility of index updates to other readers"
240 "delay visibility of index updates to other readers"
241
241
242 if not self._delayed:
242 if not self._delayed:
243 if len(self) == 0:
243 if len(self) == 0:
244 self._divert = True
244 self._divert = True
245 if self._realopener.exists(self.indexfile + '.a'):
245 if self._realopener.exists(self.indexfile + '.a'):
246 self._realopener.unlink(self.indexfile + '.a')
246 self._realopener.unlink(self.indexfile + '.a')
247 self.opener = _divertopener(self._realopener, self.indexfile)
247 self.opener = _divertopener(self._realopener, self.indexfile)
248 else:
248 else:
249 self._delaybuf = []
249 self._delaybuf = []
250 self.opener = _delayopener(self._realopener, self.indexfile,
250 self.opener = _delayopener(self._realopener, self.indexfile,
251 self._delaybuf)
251 self._delaybuf)
252 self._delayed = True
252 self._delayed = True
253 tr.addpending('cl-%i' % id(self), self._writepending)
253 tr.addpending('cl-%i' % id(self), self._writepending)
254 tr.addfinalize('cl-%i' % id(self), self._finalize)
254 tr.addfinalize('cl-%i' % id(self), self._finalize)
255
255
256 def _finalize(self, tr):
256 def _finalize(self, tr):
257 "finalize index updates"
257 "finalize index updates"
258 self._delayed = False
258 self._delayed = False
259 self.opener = self._realopener
259 self.opener = self._realopener
260 # move redirected index data back into place
260 # move redirected index data back into place
261 if self._divert:
261 if self._divert:
262 assert not self._delaybuf
262 assert not self._delaybuf
263 tmpname = self.indexfile + ".a"
263 tmpname = self.indexfile + ".a"
264 nfile = self.opener.open(tmpname)
264 nfile = self.opener.open(tmpname)
265 nfile.close()
265 nfile.close()
266 self.opener.rename(tmpname, self.indexfile)
266 self.opener.rename(tmpname, self.indexfile)
267 elif self._delaybuf:
267 elif self._delaybuf:
268 fp = self.opener(self.indexfile, 'a')
268 fp = self.opener(self.indexfile, 'a')
269 fp.write("".join(self._delaybuf))
269 fp.write("".join(self._delaybuf))
270 fp.close()
270 fp.close()
271 self._delaybuf = None
271 self._delaybuf = None
272 self._divert = False
272 self._divert = False
273 # split when we're done
273 # split when we're done
274 self.checkinlinesize(tr)
274 self.checkinlinesize(tr)
275
275
276 def readpending(self, file):
276 def readpending(self, file):
277 """read index data from a "pending" file
277 """read index data from a "pending" file
278
278
279 During a transaction, the actual changeset data is already stored in the
279 During a transaction, the actual changeset data is already stored in the
280 main file, but not yet finalized in the on-disk index. Instead, a
280 main file, but not yet finalized in the on-disk index. Instead, a
281 "pending" index is written by the transaction logic. If this function
281 "pending" index is written by the transaction logic. If this function
282 is running, we are likely in a subprocess invoked in a hook. The
282 is running, we are likely in a subprocess invoked in a hook. The
283 subprocess is informed that it is within a transaction and needs to
283 subprocess is informed that it is within a transaction and needs to
284 access its content.
284 access its content.
285
285
286 This function will read all the index data out of the pending file and
286 This function will read all the index data out of the pending file and
287 overwrite the main index."""
287 overwrite the main index."""
288
288
289 if not self.opener.exists(file):
289 if not self.opener.exists(file):
290 return # no pending data for changelog
290 return # no pending data for changelog
291 r = revlog.revlog(self.opener, file)
291 r = revlog.revlog(self.opener, file)
292 self.index = r.index
292 self.index = r.index
293 self.nodemap = r.nodemap
293 self.nodemap = r.nodemap
294 self._nodecache = r._nodecache
294 self._nodecache = r._nodecache
295 self._chunkcache = r._chunkcache
295 self._chunkcache = r._chunkcache
296
296
297 def _writepending(self, tr):
297 def _writepending(self, tr):
298 "create a file containing the unfinalized state for pretxnchangegroup"
298 "create a file containing the unfinalized state for pretxnchangegroup"
299 if self._delaybuf:
299 if self._delaybuf:
300 # make a temporary copy of the index
300 # make a temporary copy of the index
301 fp1 = self._realopener(self.indexfile)
301 fp1 = self._realopener(self.indexfile)
302 pendingfilename = self.indexfile + ".a"
302 pendingfilename = self.indexfile + ".a"
303 # register as a temp file to ensure cleanup on failure
303 # register as a temp file to ensure cleanup on failure
304 tr.registertmp(pendingfilename)
304 tr.registertmp(pendingfilename)
305 # write existing data
305 # write existing data
306 fp2 = self._realopener(pendingfilename, "w")
306 fp2 = self._realopener(pendingfilename, "w")
307 fp2.write(fp1.read())
307 fp2.write(fp1.read())
308 # add pending data
308 # add pending data
309 fp2.write("".join(self._delaybuf))
309 fp2.write("".join(self._delaybuf))
310 fp2.close()
310 fp2.close()
311 # switch modes so finalize can simply rename
311 # switch modes so finalize can simply rename
312 self._delaybuf = None
312 self._delaybuf = None
313 self._divert = True
313 self._divert = True
314 self.opener = _divertopener(self._realopener, self.indexfile)
314 self.opener = _divertopener(self._realopener, self.indexfile)
315
315
316 if self._divert:
316 if self._divert:
317 return True
317 return True
318
318
319 return False
319 return False
320
320
321 def checkinlinesize(self, tr, fp=None):
321 def checkinlinesize(self, tr, fp=None):
322 if not self._delayed:
322 if not self._delayed:
323 revlog.revlog.checkinlinesize(self, tr, fp)
323 revlog.revlog.checkinlinesize(self, tr, fp)
324
324
325 def read(self, node):
325 def read(self, node):
326 """
326 """
327 format used:
327 format used:
328 nodeid\n : manifest node in ascii
328 nodeid\n : manifest node in ascii
329 user\n : user, no \n or \r allowed
329 user\n : user, no \n or \r allowed
330 time tz extra\n : date (time is int or float, timezone is int)
330 time tz extra\n : date (time is int or float, timezone is int)
331 : extra is metadata, encoded and separated by '\0'
331 : extra is metadata, encoded and separated by '\0'
332 : older versions ignore it
332 : older versions ignore it
333 files\n\n : files modified by the cset, no \n or \r allowed
333 files\n\n : files modified by the cset, no \n or \r allowed
334 (.*) : comment (free text, ideally utf-8)
334 (.*) : comment (free text, ideally utf-8)
335
335
336 changelog v0 doesn't use extra
336 changelog v0 doesn't use extra
337 """
337 """
338 text = self.revision(node)
338 text = self.revision(node)
339 if not text:
339 if not text:
340 return (nullid, "", (0, 0), [], "", _defaultextra)
340 return nullid, "", (0, 0), [], "", _defaultextra
341 last = text.index("\n\n")
341 last = text.index("\n\n")
342 desc = encoding.tolocal(text[last + 2:])
342 desc = encoding.tolocal(text[last + 2:])
343 l = text[:last].split('\n')
343 l = text[:last].split('\n')
344 manifest = bin(l[0])
344 manifest = bin(l[0])
345 user = encoding.tolocal(l[1])
345 user = encoding.tolocal(l[1])
346
346
347 tdata = l[2].split(' ', 2)
347 tdata = l[2].split(' ', 2)
348 if len(tdata) != 3:
348 if len(tdata) != 3:
349 time = float(tdata[0])
349 time = float(tdata[0])
350 try:
350 try:
351 # various tools did silly things with the time zone field.
351 # various tools did silly things with the time zone field.
352 timezone = int(tdata[1])
352 timezone = int(tdata[1])
353 except ValueError:
353 except ValueError:
354 timezone = 0
354 timezone = 0
355 extra = _defaultextra
355 extra = _defaultextra
356 else:
356 else:
357 time, timezone = float(tdata[0]), int(tdata[1])
357 time, timezone = float(tdata[0]), int(tdata[1])
358 extra = decodeextra(tdata[2])
358 extra = decodeextra(tdata[2])
359
359
360 files = l[3:]
360 files = l[3:]
361 return (manifest, user, (time, timezone), files, desc, extra)
361 return manifest, user, (time, timezone), files, desc, extra
362
362
363 def readfiles(self, node):
363 def readfiles(self, node):
364 """
364 """
365 short version of read that only returns the files modified by the cset
365 short version of read that only returns the files modified by the cset
366 """
366 """
367 text = self.revision(node)
367 text = self.revision(node)
368 if not text:
368 if not text:
369 return []
369 return []
370 last = text.index("\n\n")
370 last = text.index("\n\n")
371 l = text[:last].split('\n')
371 l = text[:last].split('\n')
372 return l[3:]
372 return l[3:]
373
373
374 def add(self, manifest, files, desc, transaction, p1, p2,
374 def add(self, manifest, files, desc, transaction, p1, p2,
375 user, date=None, extra=None):
375 user, date=None, extra=None):
376 # Convert to UTF-8 encoded bytestrings as the very first
376 # Convert to UTF-8 encoded bytestrings as the very first
377 # thing: calling any method on a localstr object will turn it
377 # thing: calling any method on a localstr object will turn it
378 # into a str object and the cached UTF-8 string is thus lost.
378 # into a str object and the cached UTF-8 string is thus lost.
379 user, desc = encoding.fromlocal(user), encoding.fromlocal(desc)
379 user, desc = encoding.fromlocal(user), encoding.fromlocal(desc)
380
380
381 user = user.strip()
381 user = user.strip()
382 # An empty username or a username with a "\n" will make the
382 # An empty username or a username with a "\n" will make the
383 # revision text contain two "\n\n" sequences -> corrupt
383 # revision text contain two "\n\n" sequences -> corrupt
384 # repository since read cannot unpack the revision.
384 # repository since read cannot unpack the revision.
385 if not user:
385 if not user:
386 raise error.RevlogError(_("empty username"))
386 raise error.RevlogError(_("empty username"))
387 if "\n" in user:
387 if "\n" in user:
388 raise error.RevlogError(_("username %s contains a newline")
388 raise error.RevlogError(_("username %s contains a newline")
389 % repr(user))
389 % repr(user))
390
390
391 desc = stripdesc(desc)
391 desc = stripdesc(desc)
392
392
393 if date:
393 if date:
394 parseddate = "%d %d" % util.parsedate(date)
394 parseddate = "%d %d" % util.parsedate(date)
395 else:
395 else:
396 parseddate = "%d %d" % util.makedate()
396 parseddate = "%d %d" % util.makedate()
397 if extra:
397 if extra:
398 branch = extra.get("branch")
398 branch = extra.get("branch")
399 if branch in ("default", ""):
399 if branch in ("default", ""):
400 del extra["branch"]
400 del extra["branch"]
401 elif branch in (".", "null", "tip"):
401 elif branch in (".", "null", "tip"):
402 raise error.RevlogError(_('the name \'%s\' is reserved')
402 raise error.RevlogError(_('the name \'%s\' is reserved')
403 % branch)
403 % branch)
404 if extra:
404 if extra:
405 extra = encodeextra(extra)
405 extra = encodeextra(extra)
406 parseddate = "%s %s" % (parseddate, extra)
406 parseddate = "%s %s" % (parseddate, extra)
407 l = [hex(manifest), user, parseddate] + sorted(files) + ["", desc]
407 l = [hex(manifest), user, parseddate] + sorted(files) + ["", desc]
408 text = "\n".join(l)
408 text = "\n".join(l)
409 return self.addrevision(text, transaction, len(self), p1, p2)
409 return self.addrevision(text, transaction, len(self), p1, p2)
410
410
411 def branchinfo(self, rev):
411 def branchinfo(self, rev):
412 """return the branch name and open/close state of a revision
412 """return the branch name and open/close state of a revision
413
413
414 This function exists because creating a changectx object
414 This function exists because creating a changectx object
415 just to access this is costly."""
415 just to access this is costly."""
416 extra = self.read(rev)[5]
416 extra = self.read(rev)[5]
417 return encoding.tolocal(extra.get("branch")), 'close' in extra
417 return encoding.tolocal(extra.get("branch")), 'close' in extra
General Comments 0
You need to be logged in to leave comments. Login now