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