##// END OF EJS Templates
fix bogus close spotted by pychecker (no close() in global scope)
Benoit Boissinot -
r4961:3fdd09ad default
parent child Browse files
Show More
@@ -1,196 +1,196 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
5 # This software may be used and distributed according to the terms
6 # of the GNU General Public License, incorporated herein by reference.
6 # of the GNU General Public License, incorporated herein by reference.
7
7
8 from revlog import *
8 from revlog import *
9 from i18n import _
9 from i18n import _
10 import os, time, util
10 import os, time, util
11
11
12 def _string_escape(text):
12 def _string_escape(text):
13 """
13 """
14 >>> d = {'nl': chr(10), 'bs': chr(92), 'cr': chr(13), 'nul': chr(0)}
14 >>> d = {'nl': chr(10), 'bs': chr(92), 'cr': chr(13), 'nul': chr(0)}
15 >>> s = "ab%(nl)scd%(bs)s%(bs)sn%(nul)sab%(cr)scd%(bs)s%(nl)s" % d
15 >>> s = "ab%(nl)scd%(bs)s%(bs)sn%(nul)sab%(cr)scd%(bs)s%(nl)s" % d
16 >>> s
16 >>> s
17 'ab\\ncd\\\\\\\\n\\x00ab\\rcd\\\\\\n'
17 'ab\\ncd\\\\\\\\n\\x00ab\\rcd\\\\\\n'
18 >>> res = _string_escape(s)
18 >>> res = _string_escape(s)
19 >>> s == _string_unescape(res)
19 >>> s == _string_unescape(res)
20 True
20 True
21 """
21 """
22 # subset of the string_escape codec
22 # subset of the string_escape codec
23 text = text.replace('\\', '\\\\').replace('\n', '\\n').replace('\r', '\\r')
23 text = text.replace('\\', '\\\\').replace('\n', '\\n').replace('\r', '\\r')
24 return text.replace('\0', '\\0')
24 return text.replace('\0', '\\0')
25
25
26 def _string_unescape(text):
26 def _string_unescape(text):
27 return text.decode('string_escape')
27 return text.decode('string_escape')
28
28
29 class appender:
29 class appender:
30 '''the changelog index must be update last on disk, so we use this class
30 '''the changelog index must be update last on disk, so we use this class
31 to delay writes to it'''
31 to delay writes to it'''
32 def __init__(self, fp, buf):
32 def __init__(self, fp, buf):
33 self.data = buf
33 self.data = buf
34 self.fp = fp
34 self.fp = fp
35 self.offset = fp.tell()
35 self.offset = fp.tell()
36 self.size = util.fstat(fp).st_size
36 self.size = util.fstat(fp).st_size
37
37
38 def end(self):
38 def end(self):
39 return self.size + len("".join(self.data))
39 return self.size + len("".join(self.data))
40 def tell(self):
40 def tell(self):
41 return self.offset
41 return self.offset
42 def flush(self):
42 def flush(self):
43 pass
43 pass
44 def close(self):
44 def close(self):
45 close(self.fp)
45 self.fp.close()
46
46
47 def seek(self, offset, whence=0):
47 def seek(self, offset, whence=0):
48 '''virtual file offset spans real file and data'''
48 '''virtual file offset spans real file and data'''
49 if whence == 0:
49 if whence == 0:
50 self.offset = offset
50 self.offset = offset
51 elif whence == 1:
51 elif whence == 1:
52 self.offset += offset
52 self.offset += offset
53 elif whence == 2:
53 elif whence == 2:
54 self.offset = self.end() + offset
54 self.offset = self.end() + offset
55 if self.offset < self.size:
55 if self.offset < self.size:
56 self.fp.seek(self.offset)
56 self.fp.seek(self.offset)
57
57
58 def read(self, count=-1):
58 def read(self, count=-1):
59 '''only trick here is reads that span real file and data'''
59 '''only trick here is reads that span real file and data'''
60 ret = ""
60 ret = ""
61 old_offset = self.offset
61 old_offset = self.offset
62 if self.offset < self.size:
62 if self.offset < self.size:
63 s = self.fp.read(count)
63 s = self.fp.read(count)
64 ret = s
64 ret = s
65 self.offset += len(s)
65 self.offset += len(s)
66 if count > 0:
66 if count > 0:
67 count -= len(s)
67 count -= len(s)
68 if count != 0:
68 if count != 0:
69 doff = self.offset - self.size
69 doff = self.offset - self.size
70 self.data.insert(0, "".join(self.data))
70 self.data.insert(0, "".join(self.data))
71 del self.data[1:]
71 del self.data[1:]
72 s = self.data[0][doff:doff+count]
72 s = self.data[0][doff:doff+count]
73 self.offset += len(s)
73 self.offset += len(s)
74 ret += s
74 ret += s
75 return ret
75 return ret
76
76
77 def write(self, s):
77 def write(self, s):
78 self.data.append(s)
78 self.data.append(s)
79 self.offset += len(s)
79 self.offset += len(s)
80
80
81 class changelog(revlog):
81 class changelog(revlog):
82 def __init__(self, opener):
82 def __init__(self, opener):
83 revlog.__init__(self, opener, "00changelog.i")
83 revlog.__init__(self, opener, "00changelog.i")
84
84
85 def delayupdate(self):
85 def delayupdate(self):
86 "delay visibility of index updates to other readers"
86 "delay visibility of index updates to other readers"
87 self._realopener = self.opener
87 self._realopener = self.opener
88 self.opener = self._delayopener
88 self.opener = self._delayopener
89 self._delaycount = self.count()
89 self._delaycount = self.count()
90 self._delaybuf = []
90 self._delaybuf = []
91 self._delayname = None
91 self._delayname = None
92
92
93 def finalize(self, tr):
93 def finalize(self, tr):
94 "finalize index updates"
94 "finalize index updates"
95 self.opener = self._realopener
95 self.opener = self._realopener
96 # move redirected index data back into place
96 # move redirected index data back into place
97 if self._delayname:
97 if self._delayname:
98 util.rename(self._delayname + ".a", self._delayname)
98 util.rename(self._delayname + ".a", self._delayname)
99 elif self._delaybuf:
99 elif self._delaybuf:
100 fp = self.opener(self.indexfile, 'a')
100 fp = self.opener(self.indexfile, 'a')
101 fp.write("".join(self._delaybuf))
101 fp.write("".join(self._delaybuf))
102 fp.close()
102 fp.close()
103 del self._delaybuf
103 del self._delaybuf
104 # split when we're done
104 # split when we're done
105 self.checkinlinesize(tr)
105 self.checkinlinesize(tr)
106
106
107 def _delayopener(self, name, mode='r'):
107 def _delayopener(self, name, mode='r'):
108 fp = self._realopener(name, mode)
108 fp = self._realopener(name, mode)
109 # only divert the index
109 # only divert the index
110 if not name == self.indexfile:
110 if not name == self.indexfile:
111 return fp
111 return fp
112 # if we're doing an initial clone, divert to another file
112 # if we're doing an initial clone, divert to another file
113 if self._delaycount == 0:
113 if self._delaycount == 0:
114 self._delayname = fp.name
114 self._delayname = fp.name
115 return self._realopener(name + ".a", mode)
115 return self._realopener(name + ".a", mode)
116 # otherwise, divert to memory
116 # otherwise, divert to memory
117 return appender(fp, self._delaybuf)
117 return appender(fp, self._delaybuf)
118
118
119 def checkinlinesize(self, tr, fp=None):
119 def checkinlinesize(self, tr, fp=None):
120 if self.opener == self._delayopener:
120 if self.opener == self._delayopener:
121 return
121 return
122 return revlog.checkinlinesize(self, tr, fp)
122 return revlog.checkinlinesize(self, tr, fp)
123
123
124 def decode_extra(self, text):
124 def decode_extra(self, text):
125 extra = {}
125 extra = {}
126 for l in text.split('\0'):
126 for l in text.split('\0'):
127 if not l:
127 if not l:
128 continue
128 continue
129 k, v = _string_unescape(l).split(':', 1)
129 k, v = _string_unescape(l).split(':', 1)
130 extra[k] = v
130 extra[k] = v
131 return extra
131 return extra
132
132
133 def encode_extra(self, d):
133 def encode_extra(self, d):
134 items = [_string_escape(":".join(t)) for t in d.iteritems()]
134 items = [_string_escape(":".join(t)) for t in d.iteritems()]
135 return "\0".join(items)
135 return "\0".join(items)
136
136
137 def extract(self, text):
137 def extract(self, text):
138 """
138 """
139 format used:
139 format used:
140 nodeid\n : manifest node in ascii
140 nodeid\n : manifest node in ascii
141 user\n : user, no \n or \r allowed
141 user\n : user, no \n or \r allowed
142 time tz extra\n : date (time is int or float, timezone is int)
142 time tz extra\n : date (time is int or float, timezone is int)
143 : extra is metadatas, encoded and separated by '\0'
143 : extra is metadatas, encoded and separated by '\0'
144 : older versions ignore it
144 : older versions ignore it
145 files\n\n : files modified by the cset, no \n or \r allowed
145 files\n\n : files modified by the cset, no \n or \r allowed
146 (.*) : comment (free text, ideally utf-8)
146 (.*) : comment (free text, ideally utf-8)
147
147
148 changelog v0 doesn't use extra
148 changelog v0 doesn't use extra
149 """
149 """
150 if not text:
150 if not text:
151 return (nullid, "", (0, 0), [], "", {'branch': 'default'})
151 return (nullid, "", (0, 0), [], "", {'branch': 'default'})
152 last = text.index("\n\n")
152 last = text.index("\n\n")
153 desc = util.tolocal(text[last + 2:])
153 desc = util.tolocal(text[last + 2:])
154 l = text[:last].split('\n')
154 l = text[:last].split('\n')
155 manifest = bin(l[0])
155 manifest = bin(l[0])
156 user = util.tolocal(l[1])
156 user = util.tolocal(l[1])
157
157
158 extra_data = l[2].split(' ', 2)
158 extra_data = l[2].split(' ', 2)
159 if len(extra_data) != 3:
159 if len(extra_data) != 3:
160 time = float(extra_data.pop(0))
160 time = float(extra_data.pop(0))
161 try:
161 try:
162 # various tools did silly things with the time zone field.
162 # various tools did silly things with the time zone field.
163 timezone = int(extra_data[0])
163 timezone = int(extra_data[0])
164 except:
164 except:
165 timezone = 0
165 timezone = 0
166 extra = {}
166 extra = {}
167 else:
167 else:
168 time, timezone, extra = extra_data
168 time, timezone, extra = extra_data
169 time, timezone = float(time), int(timezone)
169 time, timezone = float(time), int(timezone)
170 extra = self.decode_extra(extra)
170 extra = self.decode_extra(extra)
171 if not extra.get('branch'):
171 if not extra.get('branch'):
172 extra['branch'] = 'default'
172 extra['branch'] = 'default'
173 files = l[3:]
173 files = l[3:]
174 return (manifest, user, (time, timezone), files, desc, extra)
174 return (manifest, user, (time, timezone), files, desc, extra)
175
175
176 def read(self, node):
176 def read(self, node):
177 return self.extract(self.revision(node))
177 return self.extract(self.revision(node))
178
178
179 def add(self, manifest, list, desc, transaction, p1=None, p2=None,
179 def add(self, manifest, list, desc, transaction, p1=None, p2=None,
180 user=None, date=None, extra={}):
180 user=None, date=None, extra={}):
181
181
182 user, desc = util.fromlocal(user), util.fromlocal(desc)
182 user, desc = util.fromlocal(user), util.fromlocal(desc)
183
183
184 if date:
184 if date:
185 parseddate = "%d %d" % util.parsedate(date)
185 parseddate = "%d %d" % util.parsedate(date)
186 else:
186 else:
187 parseddate = "%d %d" % util.makedate()
187 parseddate = "%d %d" % util.makedate()
188 if extra and extra.get("branch") in ("default", ""):
188 if extra and extra.get("branch") in ("default", ""):
189 del extra["branch"]
189 del extra["branch"]
190 if extra:
190 if extra:
191 extra = self.encode_extra(extra)
191 extra = self.encode_extra(extra)
192 parseddate = "%s %s" % (parseddate, extra)
192 parseddate = "%s %s" % (parseddate, extra)
193 list.sort()
193 list.sort()
194 l = [hex(manifest), user, parseddate] + list + ["", desc]
194 l = [hex(manifest), user, parseddate] + list + ["", desc]
195 text = "\n".join(l)
195 text = "\n".join(l)
196 return self.addrevision(text, transaction, self.count(), p1, p2)
196 return self.addrevision(text, transaction, self.count(), p1, p2)
General Comments 0
You need to be logged in to leave comments. Login now