##// END OF EJS Templates
changelog: drop unnecessary override of "hasnode"...
Yuya Nishihara -
r24961:8d81b36f default
parent child Browse files
Show More
@@ -1,387 +1,379 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 hasnode(self, node):
176 """filtered version of revlog.hasnode"""
177 try:
178 i = self.rev(node)
179 return i not in self.filteredrevs
180 except KeyError:
181 return False
182
183 def headrevs(self):
175 def headrevs(self):
184 if self.filteredrevs:
176 if self.filteredrevs:
185 try:
177 try:
186 return self.index.headrevsfiltered(self.filteredrevs)
178 return self.index.headrevsfiltered(self.filteredrevs)
187 # AttributeError covers non-c-extension environments and
179 # AttributeError covers non-c-extension environments and
188 # old c extensions without filter handling.
180 # old c extensions without filter handling.
189 except AttributeError:
181 except AttributeError:
190 return self._headrevs()
182 return self._headrevs()
191
183
192 return super(changelog, self).headrevs()
184 return super(changelog, self).headrevs()
193
185
194 def strip(self, *args, **kwargs):
186 def strip(self, *args, **kwargs):
195 # XXX make something better than assert
187 # XXX make something better than assert
196 # We can't expect proper strip behavior if we are filtered.
188 # We can't expect proper strip behavior if we are filtered.
197 assert not self.filteredrevs
189 assert not self.filteredrevs
198 super(changelog, self).strip(*args, **kwargs)
190 super(changelog, self).strip(*args, **kwargs)
199
191
200 def rev(self, node):
192 def rev(self, node):
201 """filtered version of revlog.rev"""
193 """filtered version of revlog.rev"""
202 r = super(changelog, self).rev(node)
194 r = super(changelog, self).rev(node)
203 if r in self.filteredrevs:
195 if r in self.filteredrevs:
204 raise error.FilteredLookupError(hex(node), self.indexfile,
196 raise error.FilteredLookupError(hex(node), self.indexfile,
205 _('filtered node'))
197 _('filtered node'))
206 return r
198 return r
207
199
208 def node(self, rev):
200 def node(self, rev):
209 """filtered version of revlog.node"""
201 """filtered version of revlog.node"""
210 if rev in self.filteredrevs:
202 if rev in self.filteredrevs:
211 raise error.FilteredIndexError(rev)
203 raise error.FilteredIndexError(rev)
212 return super(changelog, self).node(rev)
204 return super(changelog, self).node(rev)
213
205
214 def linkrev(self, rev):
206 def linkrev(self, rev):
215 """filtered version of revlog.linkrev"""
207 """filtered version of revlog.linkrev"""
216 if rev in self.filteredrevs:
208 if rev in self.filteredrevs:
217 raise error.FilteredIndexError(rev)
209 raise error.FilteredIndexError(rev)
218 return super(changelog, self).linkrev(rev)
210 return super(changelog, self).linkrev(rev)
219
211
220 def parentrevs(self, rev):
212 def parentrevs(self, rev):
221 """filtered version of revlog.parentrevs"""
213 """filtered version of revlog.parentrevs"""
222 if rev in self.filteredrevs:
214 if rev in self.filteredrevs:
223 raise error.FilteredIndexError(rev)
215 raise error.FilteredIndexError(rev)
224 return super(changelog, self).parentrevs(rev)
216 return super(changelog, self).parentrevs(rev)
225
217
226 def flags(self, rev):
218 def flags(self, rev):
227 """filtered version of revlog.flags"""
219 """filtered version of revlog.flags"""
228 if rev in self.filteredrevs:
220 if rev in self.filteredrevs:
229 raise error.FilteredIndexError(rev)
221 raise error.FilteredIndexError(rev)
230 return super(changelog, self).flags(rev)
222 return super(changelog, self).flags(rev)
231
223
232 def delayupdate(self, tr):
224 def delayupdate(self, tr):
233 "delay visibility of index updates to other readers"
225 "delay visibility of index updates to other readers"
234
226
235 if not self._delayed:
227 if not self._delayed:
236 if len(self) == 0:
228 if len(self) == 0:
237 self._divert = True
229 self._divert = True
238 if self._realopener.exists(self.indexfile + '.a'):
230 if self._realopener.exists(self.indexfile + '.a'):
239 self._realopener.unlink(self.indexfile + '.a')
231 self._realopener.unlink(self.indexfile + '.a')
240 self.opener = _divertopener(self._realopener, self.indexfile)
232 self.opener = _divertopener(self._realopener, self.indexfile)
241 else:
233 else:
242 self._delaybuf = []
234 self._delaybuf = []
243 self.opener = _delayopener(self._realopener, self.indexfile,
235 self.opener = _delayopener(self._realopener, self.indexfile,
244 self._delaybuf)
236 self._delaybuf)
245 self._delayed = True
237 self._delayed = True
246 tr.addpending('cl-%i' % id(self), self._writepending)
238 tr.addpending('cl-%i' % id(self), self._writepending)
247 tr.addfinalize('cl-%i' % id(self), self._finalize)
239 tr.addfinalize('cl-%i' % id(self), self._finalize)
248
240
249 def _finalize(self, tr):
241 def _finalize(self, tr):
250 "finalize index updates"
242 "finalize index updates"
251 self._delayed = False
243 self._delayed = False
252 self.opener = self._realopener
244 self.opener = self._realopener
253 # move redirected index data back into place
245 # move redirected index data back into place
254 if self._divert:
246 if self._divert:
255 assert not self._delaybuf
247 assert not self._delaybuf
256 tmpname = self.indexfile + ".a"
248 tmpname = self.indexfile + ".a"
257 nfile = self.opener.open(tmpname)
249 nfile = self.opener.open(tmpname)
258 nfile.close()
250 nfile.close()
259 self.opener.rename(tmpname, self.indexfile)
251 self.opener.rename(tmpname, self.indexfile)
260 elif self._delaybuf:
252 elif self._delaybuf:
261 fp = self.opener(self.indexfile, 'a')
253 fp = self.opener(self.indexfile, 'a')
262 fp.write("".join(self._delaybuf))
254 fp.write("".join(self._delaybuf))
263 fp.close()
255 fp.close()
264 self._delaybuf = None
256 self._delaybuf = None
265 self._divert = False
257 self._divert = False
266 # split when we're done
258 # split when we're done
267 self.checkinlinesize(tr)
259 self.checkinlinesize(tr)
268
260
269 def readpending(self, file):
261 def readpending(self, file):
270 if not self.opener.exists(file):
262 if not self.opener.exists(file):
271 return # no pending data for changelog
263 return # no pending data for changelog
272 r = revlog.revlog(self.opener, file)
264 r = revlog.revlog(self.opener, file)
273 self.index = r.index
265 self.index = r.index
274 self.nodemap = r.nodemap
266 self.nodemap = r.nodemap
275 self._nodecache = r._nodecache
267 self._nodecache = r._nodecache
276 self._chunkcache = r._chunkcache
268 self._chunkcache = r._chunkcache
277
269
278 def _writepending(self, tr):
270 def _writepending(self, tr):
279 "create a file containing the unfinalized state for pretxnchangegroup"
271 "create a file containing the unfinalized state for pretxnchangegroup"
280 if self._delaybuf:
272 if self._delaybuf:
281 # make a temporary copy of the index
273 # make a temporary copy of the index
282 fp1 = self._realopener(self.indexfile)
274 fp1 = self._realopener(self.indexfile)
283 pendingfilename = self.indexfile + ".a"
275 pendingfilename = self.indexfile + ".a"
284 # register as a temp file to ensure cleanup on failure
276 # register as a temp file to ensure cleanup on failure
285 tr.registertmp(pendingfilename)
277 tr.registertmp(pendingfilename)
286 # write existing data
278 # write existing data
287 fp2 = self._realopener(pendingfilename, "w")
279 fp2 = self._realopener(pendingfilename, "w")
288 fp2.write(fp1.read())
280 fp2.write(fp1.read())
289 # add pending data
281 # add pending data
290 fp2.write("".join(self._delaybuf))
282 fp2.write("".join(self._delaybuf))
291 fp2.close()
283 fp2.close()
292 # switch modes so finalize can simply rename
284 # switch modes so finalize can simply rename
293 self._delaybuf = None
285 self._delaybuf = None
294 self._divert = True
286 self._divert = True
295 self.opener = _divertopener(self._realopener, self.indexfile)
287 self.opener = _divertopener(self._realopener, self.indexfile)
296
288
297 if self._divert:
289 if self._divert:
298 return True
290 return True
299
291
300 return False
292 return False
301
293
302 def checkinlinesize(self, tr, fp=None):
294 def checkinlinesize(self, tr, fp=None):
303 if not self._delayed:
295 if not self._delayed:
304 revlog.revlog.checkinlinesize(self, tr, fp)
296 revlog.revlog.checkinlinesize(self, tr, fp)
305
297
306 def read(self, node):
298 def read(self, node):
307 """
299 """
308 format used:
300 format used:
309 nodeid\n : manifest node in ascii
301 nodeid\n : manifest node in ascii
310 user\n : user, no \n or \r allowed
302 user\n : user, no \n or \r allowed
311 time tz extra\n : date (time is int or float, timezone is int)
303 time tz extra\n : date (time is int or float, timezone is int)
312 : extra is metadata, encoded and separated by '\0'
304 : extra is metadata, encoded and separated by '\0'
313 : older versions ignore it
305 : older versions ignore it
314 files\n\n : files modified by the cset, no \n or \r allowed
306 files\n\n : files modified by the cset, no \n or \r allowed
315 (.*) : comment (free text, ideally utf-8)
307 (.*) : comment (free text, ideally utf-8)
316
308
317 changelog v0 doesn't use extra
309 changelog v0 doesn't use extra
318 """
310 """
319 text = self.revision(node)
311 text = self.revision(node)
320 if not text:
312 if not text:
321 return (nullid, "", (0, 0), [], "", _defaultextra)
313 return (nullid, "", (0, 0), [], "", _defaultextra)
322 last = text.index("\n\n")
314 last = text.index("\n\n")
323 desc = encoding.tolocal(text[last + 2:])
315 desc = encoding.tolocal(text[last + 2:])
324 l = text[:last].split('\n')
316 l = text[:last].split('\n')
325 manifest = bin(l[0])
317 manifest = bin(l[0])
326 user = encoding.tolocal(l[1])
318 user = encoding.tolocal(l[1])
327
319
328 tdata = l[2].split(' ', 2)
320 tdata = l[2].split(' ', 2)
329 if len(tdata) != 3:
321 if len(tdata) != 3:
330 time = float(tdata[0])
322 time = float(tdata[0])
331 try:
323 try:
332 # various tools did silly things with the time zone field.
324 # various tools did silly things with the time zone field.
333 timezone = int(tdata[1])
325 timezone = int(tdata[1])
334 except ValueError:
326 except ValueError:
335 timezone = 0
327 timezone = 0
336 extra = _defaultextra
328 extra = _defaultextra
337 else:
329 else:
338 time, timezone = float(tdata[0]), int(tdata[1])
330 time, timezone = float(tdata[0]), int(tdata[1])
339 extra = decodeextra(tdata[2])
331 extra = decodeextra(tdata[2])
340
332
341 files = l[3:]
333 files = l[3:]
342 return (manifest, user, (time, timezone), files, desc, extra)
334 return (manifest, user, (time, timezone), files, desc, extra)
343
335
344 def add(self, manifest, files, desc, transaction, p1, p2,
336 def add(self, manifest, files, desc, transaction, p1, p2,
345 user, date=None, extra=None):
337 user, date=None, extra=None):
346 # Convert to UTF-8 encoded bytestrings as the very first
338 # Convert to UTF-8 encoded bytestrings as the very first
347 # thing: calling any method on a localstr object will turn it
339 # thing: calling any method on a localstr object will turn it
348 # into a str object and the cached UTF-8 string is thus lost.
340 # into a str object and the cached UTF-8 string is thus lost.
349 user, desc = encoding.fromlocal(user), encoding.fromlocal(desc)
341 user, desc = encoding.fromlocal(user), encoding.fromlocal(desc)
350
342
351 user = user.strip()
343 user = user.strip()
352 # An empty username or a username with a "\n" will make the
344 # An empty username or a username with a "\n" will make the
353 # revision text contain two "\n\n" sequences -> corrupt
345 # revision text contain two "\n\n" sequences -> corrupt
354 # repository since read cannot unpack the revision.
346 # repository since read cannot unpack the revision.
355 if not user:
347 if not user:
356 raise error.RevlogError(_("empty username"))
348 raise error.RevlogError(_("empty username"))
357 if "\n" in user:
349 if "\n" in user:
358 raise error.RevlogError(_("username %s contains a newline")
350 raise error.RevlogError(_("username %s contains a newline")
359 % repr(user))
351 % repr(user))
360
352
361 desc = stripdesc(desc)
353 desc = stripdesc(desc)
362
354
363 if date:
355 if date:
364 parseddate = "%d %d" % util.parsedate(date)
356 parseddate = "%d %d" % util.parsedate(date)
365 else:
357 else:
366 parseddate = "%d %d" % util.makedate()
358 parseddate = "%d %d" % util.makedate()
367 if extra:
359 if extra:
368 branch = extra.get("branch")
360 branch = extra.get("branch")
369 if branch in ("default", ""):
361 if branch in ("default", ""):
370 del extra["branch"]
362 del extra["branch"]
371 elif branch in (".", "null", "tip"):
363 elif branch in (".", "null", "tip"):
372 raise error.RevlogError(_('the name \'%s\' is reserved')
364 raise error.RevlogError(_('the name \'%s\' is reserved')
373 % branch)
365 % branch)
374 if extra:
366 if extra:
375 extra = encodeextra(extra)
367 extra = encodeextra(extra)
376 parseddate = "%s %s" % (parseddate, extra)
368 parseddate = "%s %s" % (parseddate, extra)
377 l = [hex(manifest), user, parseddate] + sorted(files) + ["", desc]
369 l = [hex(manifest), user, parseddate] + sorted(files) + ["", desc]
378 text = "\n".join(l)
370 text = "\n".join(l)
379 return self.addrevision(text, transaction, len(self), p1, p2)
371 return self.addrevision(text, transaction, len(self), p1, p2)
380
372
381 def branchinfo(self, rev):
373 def branchinfo(self, rev):
382 """return the branch name and open/close state of a revision
374 """return the branch name and open/close state of a revision
383
375
384 This function exists because creating a changectx object
376 This function exists because creating a changectx object
385 just to access this is costly."""
377 just to access this is costly."""
386 extra = self.read(rev)[5]
378 extra = self.read(rev)[5]
387 return encoding.tolocal(extra.get("branch")), 'close' in extra
379 return encoding.tolocal(extra.get("branch")), 'close' in extra
General Comments 0
You need to be logged in to leave comments. Login now