##// END OF EJS Templates
changelog: remove extract function
Matt Mackall -
r5744:9db7fd77 default
parent child Browse files
Show More
@@ -1,198 +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 self.fp.close()
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 if self.offset < self.size:
61 if self.offset < self.size:
62 s = self.fp.read(count)
62 s = self.fp.read(count)
63 ret = s
63 ret = s
64 self.offset += len(s)
64 self.offset += len(s)
65 if count > 0:
65 if count > 0:
66 count -= len(s)
66 count -= len(s)
67 if count != 0:
67 if count != 0:
68 doff = self.offset - self.size
68 doff = self.offset - self.size
69 self.data.insert(0, "".join(self.data))
69 self.data.insert(0, "".join(self.data))
70 del self.data[1:]
70 del self.data[1:]
71 s = self.data[0][doff:doff+count]
71 s = self.data[0][doff:doff+count]
72 self.offset += len(s)
72 self.offset += len(s)
73 ret += s
73 ret += s
74 return ret
74 return ret
75
75
76 def write(self, s):
76 def write(self, s):
77 self.data.append(str(s))
77 self.data.append(str(s))
78 self.offset += len(s)
78 self.offset += len(s)
79
79
80 class changelog(revlog):
80 class changelog(revlog):
81 def __init__(self, opener):
81 def __init__(self, opener):
82 revlog.__init__(self, opener, "00changelog.i")
82 revlog.__init__(self, opener, "00changelog.i")
83
83
84 def delayupdate(self):
84 def delayupdate(self):
85 "delay visibility of index updates to other readers"
85 "delay visibility of index updates to other readers"
86 self._realopener = self.opener
86 self._realopener = self.opener
87 self.opener = self._delayopener
87 self.opener = self._delayopener
88 self._delaycount = self.count()
88 self._delaycount = self.count()
89 self._delaybuf = []
89 self._delaybuf = []
90 self._delayname = None
90 self._delayname = None
91
91
92 def finalize(self, tr):
92 def finalize(self, tr):
93 "finalize index updates"
93 "finalize index updates"
94 self.opener = self._realopener
94 self.opener = self._realopener
95 # move redirected index data back into place
95 # move redirected index data back into place
96 if self._delayname:
96 if self._delayname:
97 util.rename(self._delayname + ".a", self._delayname)
97 util.rename(self._delayname + ".a", self._delayname)
98 elif self._delaybuf:
98 elif self._delaybuf:
99 fp = self.opener(self.indexfile, 'a')
99 fp = self.opener(self.indexfile, 'a')
100 fp.write("".join(self._delaybuf))
100 fp.write("".join(self._delaybuf))
101 fp.close()
101 fp.close()
102 del self._delaybuf
102 del self._delaybuf
103 # split when we're done
103 # split when we're done
104 self.checkinlinesize(tr)
104 self.checkinlinesize(tr)
105
105
106 def _delayopener(self, name, mode='r'):
106 def _delayopener(self, name, mode='r'):
107 fp = self._realopener(name, mode)
107 fp = self._realopener(name, mode)
108 # only divert the index
108 # only divert the index
109 if not name == self.indexfile:
109 if not name == self.indexfile:
110 return fp
110 return fp
111 # if we're doing an initial clone, divert to another file
111 # if we're doing an initial clone, divert to another file
112 if self._delaycount == 0:
112 if self._delaycount == 0:
113 self._delayname = fp.name
113 self._delayname = fp.name
114 return self._realopener(name + ".a", mode)
114 return self._realopener(name + ".a", mode)
115 # otherwise, divert to memory
115 # otherwise, divert to memory
116 return appender(fp, self._delaybuf)
116 return appender(fp, self._delaybuf)
117
117
118 def checkinlinesize(self, tr, fp=None):
118 def checkinlinesize(self, tr, fp=None):
119 if self.opener == self._delayopener:
119 if self.opener == self._delayopener:
120 return
120 return
121 return revlog.checkinlinesize(self, tr, fp)
121 return revlog.checkinlinesize(self, tr, fp)
122
122
123 def decode_extra(self, text):
123 def decode_extra(self, text):
124 extra = {}
124 extra = {}
125 for l in text.split('\0'):
125 for l in text.split('\0'):
126 if not l:
126 if not l:
127 continue
127 continue
128 k, v = _string_unescape(l).split(':', 1)
128 k, v = _string_unescape(l).split(':', 1)
129 extra[k] = v
129 extra[k] = v
130 return extra
130 return extra
131
131
132 def encode_extra(self, d):
132 def encode_extra(self, d):
133 # keys must be sorted to produce a deterministic changelog entry
133 # keys must be sorted to produce a deterministic changelog entry
134 keys = d.keys()
134 keys = d.keys()
135 keys.sort()
135 keys.sort()
136 items = [_string_escape('%s:%s' % (k, d[k])) for k in keys]
136 items = [_string_escape('%s:%s' % (k, d[k])) for k in keys]
137 return "\0".join(items)
137 return "\0".join(items)
138
138
139 def extract(self, text):
139 def read(self, node):
140 """
140 """
141 format used:
141 format used:
142 nodeid\n : manifest node in ascii
142 nodeid\n : manifest node in ascii
143 user\n : user, no \n or \r allowed
143 user\n : user, no \n or \r allowed
144 time tz extra\n : date (time is int or float, timezone is int)
144 time tz extra\n : date (time is int or float, timezone is int)
145 : extra is metadatas, encoded and separated by '\0'
145 : extra is metadatas, encoded and separated by '\0'
146 : older versions ignore it
146 : older versions ignore it
147 files\n\n : files modified by the cset, no \n or \r allowed
147 files\n\n : files modified by the cset, no \n or \r allowed
148 (.*) : comment (free text, ideally utf-8)
148 (.*) : comment (free text, ideally utf-8)
149
149
150 changelog v0 doesn't use extra
150 changelog v0 doesn't use extra
151 """
151 """
152 text = self.revision(node)
152 if not text:
153 if not text:
153 return (nullid, "", (0, 0), [], "", {'branch': 'default'})
154 return (nullid, "", (0, 0), [], "", {'branch': 'default'})
154 last = text.index("\n\n")
155 last = text.index("\n\n")
155 desc = util.tolocal(text[last + 2:])
156 desc = util.tolocal(text[last + 2:])
156 l = text[:last].split('\n')
157 l = text[:last].split('\n')
157 manifest = bin(l[0])
158 manifest = bin(l[0])
158 user = util.tolocal(l[1])
159 user = util.tolocal(l[1])
159
160
160 extra_data = l[2].split(' ', 2)
161 extra_data = l[2].split(' ', 2)
161 if len(extra_data) != 3:
162 if len(extra_data) != 3:
162 time = float(extra_data.pop(0))
163 time = float(extra_data.pop(0))
163 try:
164 try:
164 # various tools did silly things with the time zone field.
165 # various tools did silly things with the time zone field.
165 timezone = int(extra_data[0])
166 timezone = int(extra_data[0])
166 except:
167 except:
167 timezone = 0
168 timezone = 0
168 extra = {}
169 extra = {}
169 else:
170 else:
170 time, timezone, extra = extra_data
171 time, timezone, extra = extra_data
171 time, timezone = float(time), int(timezone)
172 time, timezone = float(time), int(timezone)
172 extra = self.decode_extra(extra)
173 extra = self.decode_extra(extra)
173 if not extra.get('branch'):
174 if not extra.get('branch'):
174 extra['branch'] = 'default'
175 extra['branch'] = 'default'
175 files = l[3:]
176 files = l[3:]
176 return (manifest, user, (time, timezone), files, desc, extra)
177 return (manifest, user, (time, timezone), files, desc, extra)
177
178
178 def read(self, node):
179 return self.extract(self.revision(node))
180
181 def add(self, manifest, list, desc, transaction, p1=None, p2=None,
179 def add(self, manifest, list, desc, transaction, p1=None, p2=None,
182 user=None, date=None, extra={}):
180 user=None, date=None, extra={}):
183
181
184 user, desc = util.fromlocal(user), util.fromlocal(desc)
182 user, desc = util.fromlocal(user), util.fromlocal(desc)
185
183
186 if date:
184 if date:
187 parseddate = "%d %d" % util.parsedate(date)
185 parseddate = "%d %d" % util.parsedate(date)
188 else:
186 else:
189 parseddate = "%d %d" % util.makedate()
187 parseddate = "%d %d" % util.makedate()
190 if extra and extra.get("branch") in ("default", ""):
188 if extra and extra.get("branch") in ("default", ""):
191 del extra["branch"]
189 del extra["branch"]
192 if extra:
190 if extra:
193 extra = self.encode_extra(extra)
191 extra = self.encode_extra(extra)
194 parseddate = "%s %s" % (parseddate, extra)
192 parseddate = "%s %s" % (parseddate, extra)
195 list.sort()
193 list.sort()
196 l = [hex(manifest), user, parseddate] + list + ["", desc]
194 l = [hex(manifest), user, parseddate] + list + ["", desc]
197 text = "\n".join(l)
195 text = "\n".join(l)
198 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