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