##// END OF EJS Templates
changelog: optimize delayed updates for clone vs pull...
Matt Mackall -
r4269:73c918c7 default
parent child Browse files
Show More
@@ -1,184 +1,196 b''
1 # changelog.py - changelog class for mercurial
1 # changelog.py - changelog class for mercurial
2 #
2 #
3 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005, 2006 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 close(self.fp)
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._appendopener
88 self.opener = self._delayopener
89 self._delaycount = self.count()
89 self._delaybuf = []
90 self._delaybuf = []
91 self._delayname = None
90
92
91 def finalize(self, tr):
93 def finalize(self, tr):
92 "finalize index updates"
94 "finalize index updates"
93 self.opener = self._realopener
95 self.opener = self._realopener
94 if self._delaybuf:
96 # move redirected index data back into place
97 if self._delayname:
98 util.rename(self._delayname + ".a", self._delayname)
99 elif self._delaybuf:
95 fp = self.opener(self.indexfile, 'a')
100 fp = self.opener(self.indexfile, 'a')
96 fp.write("".join(self._delaybuf))
101 fp.write("".join(self._delaybuf))
97 fp.close()
102 fp.close()
98 del self._delaybuf
103 del self._delaybuf
104 # split when we're done
99 self.checkinlinesize(tr)
105 self.checkinlinesize(tr)
100
106
101 def _appendopener(self, name, mode='r'):
107 def _delayopener(self, name, mode='r'):
102 fp = self._realopener(name, mode)
108 fp = self._realopener(name, mode)
109 # only divert the index
103 if not name == self.indexfile:
110 if not name == self.indexfile:
104 return fp
111 return fp
112 # if we're doing an initial clone, divert to another file
113 if self._delaycount == 0:
114 self._delayname = fp.name
115 return self._realopener(name + ".a", mode)
116 # otherwise, divert to memory
105 return appender(fp, self._delaybuf)
117 return appender(fp, self._delaybuf)
106
118
107 def checkinlinesize(self, tr, fp=None):
119 def checkinlinesize(self, tr, fp=None):
108 if self.opener == self._appendopener:
120 if self.opener == self._delayopener:
109 return
121 return
110 return revlog.checkinlinesize(self, tr, fp)
122 return revlog.checkinlinesize(self, tr, fp)
111
123
112 def decode_extra(self, text):
124 def decode_extra(self, text):
113 extra = {}
125 extra = {}
114 for l in text.split('\0'):
126 for l in text.split('\0'):
115 if not l:
127 if not l:
116 continue
128 continue
117 k, v = _string_unescape(l).split(':', 1)
129 k, v = _string_unescape(l).split(':', 1)
118 extra[k] = v
130 extra[k] = v
119 return extra
131 return extra
120
132
121 def encode_extra(self, d):
133 def encode_extra(self, d):
122 items = [_string_escape(":".join(t)) for t in d.iteritems()]
134 items = [_string_escape(":".join(t)) for t in d.iteritems()]
123 return "\0".join(items)
135 return "\0".join(items)
124
136
125 def extract(self, text):
137 def extract(self, text):
126 """
138 """
127 format used:
139 format used:
128 nodeid\n : manifest node in ascii
140 nodeid\n : manifest node in ascii
129 user\n : user, no \n or \r allowed
141 user\n : user, no \n or \r allowed
130 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)
131 : extra is metadatas, encoded and separated by '\0'
143 : extra is metadatas, encoded and separated by '\0'
132 : older versions ignore it
144 : older versions ignore it
133 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
134 (.*) : comment (free text, ideally utf-8)
146 (.*) : comment (free text, ideally utf-8)
135
147
136 changelog v0 doesn't use extra
148 changelog v0 doesn't use extra
137 """
149 """
138 if not text:
150 if not text:
139 return (nullid, "", (0, 0), [], "", {'branch': 'default'})
151 return (nullid, "", (0, 0), [], "", {'branch': 'default'})
140 last = text.index("\n\n")
152 last = text.index("\n\n")
141 desc = util.tolocal(text[last + 2:])
153 desc = util.tolocal(text[last + 2:])
142 l = text[:last].split('\n')
154 l = text[:last].split('\n')
143 manifest = bin(l[0])
155 manifest = bin(l[0])
144 user = util.tolocal(l[1])
156 user = util.tolocal(l[1])
145
157
146 extra_data = l[2].split(' ', 2)
158 extra_data = l[2].split(' ', 2)
147 if len(extra_data) != 3:
159 if len(extra_data) != 3:
148 time = float(extra_data.pop(0))
160 time = float(extra_data.pop(0))
149 try:
161 try:
150 # various tools did silly things with the time zone field.
162 # various tools did silly things with the time zone field.
151 timezone = int(extra_data[0])
163 timezone = int(extra_data[0])
152 except:
164 except:
153 timezone = 0
165 timezone = 0
154 extra = {}
166 extra = {}
155 else:
167 else:
156 time, timezone, extra = extra_data
168 time, timezone, extra = extra_data
157 time, timezone = float(time), int(timezone)
169 time, timezone = float(time), int(timezone)
158 extra = self.decode_extra(extra)
170 extra = self.decode_extra(extra)
159 if not extra.get('branch'):
171 if not extra.get('branch'):
160 extra['branch'] = 'default'
172 extra['branch'] = 'default'
161 files = l[3:]
173 files = l[3:]
162 return (manifest, user, (time, timezone), files, desc, extra)
174 return (manifest, user, (time, timezone), files, desc, extra)
163
175
164 def read(self, node):
176 def read(self, node):
165 return self.extract(self.revision(node))
177 return self.extract(self.revision(node))
166
178
167 def add(self, manifest, list, desc, transaction, p1=None, p2=None,
179 def add(self, manifest, list, desc, transaction, p1=None, p2=None,
168 user=None, date=None, extra={}):
180 user=None, date=None, extra={}):
169
181
170 user, desc = util.fromlocal(user), util.fromlocal(desc)
182 user, desc = util.fromlocal(user), util.fromlocal(desc)
171
183
172 if date:
184 if date:
173 parseddate = "%d %d" % util.parsedate(date)
185 parseddate = "%d %d" % util.parsedate(date)
174 else:
186 else:
175 parseddate = "%d %d" % util.makedate()
187 parseddate = "%d %d" % util.makedate()
176 if extra and extra.get("branch") in ("default", ""):
188 if extra and extra.get("branch") in ("default", ""):
177 del extra["branch"]
189 del extra["branch"]
178 if extra:
190 if extra:
179 extra = self.encode_extra(extra)
191 extra = self.encode_extra(extra)
180 parseddate = "%s %s" % (parseddate, extra)
192 parseddate = "%s %s" % (parseddate, extra)
181 list.sort()
193 list.sort()
182 l = [hex(manifest), user, parseddate] + list + ["", desc]
194 l = [hex(manifest), user, parseddate] + list + ["", desc]
183 text = "\n".join(l)
195 text = "\n".join(l)
184 return self.addrevision(text, transaction, self.count(), p1, p2)
196 return self.addrevision(text, transaction, self.count(), p1, p2)
@@ -1,7 +1,7 b''
1 0
1 0
2 0
2 0
3 adding changesets
3 adding changesets
4 killed!
4 killed!
5 transaction abort!
5 transaction abort!
6 rollback completed
6 rollback completed
7 .hg/00changelog.i .hg/journal.dirstate .hg/requires .hg/store .hg/store/00changelog.i
7 .hg/00changelog.i .hg/journal.dirstate .hg/requires .hg/store .hg/store/00changelog.i .hg/store/00changelog.i.a
General Comments 0
You need to be logged in to leave comments. Login now