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