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