##// 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 1 # changelog.py - changelog class for mercurial
2 2 #
3 3 # Copyright 2005, 2006 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 19 >>> s == _string_unescape(res)
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 26 def _string_unescape(text):
27 27 return text.decode('string_escape')
28 28
29 29 class appender:
30 30 '''the changelog index must be update last on disk, so we use this class
31 31 to delay writes to it'''
32 32 def __init__(self, fp, buf):
33 33 self.data = buf
34 34 self.fp = fp
35 35 self.offset = fp.tell()
36 36 self.size = util.fstat(fp).st_size
37 37
38 38 def end(self):
39 39 return self.size + len("".join(self.data))
40 40 def tell(self):
41 41 return self.offset
42 42 def flush(self):
43 43 pass
44 44 def close(self):
45 45 close(self.fp)
46 46
47 47 def seek(self, offset, whence=0):
48 48 '''virtual file offset spans real file and data'''
49 49 if whence == 0:
50 50 self.offset = offset
51 51 elif whence == 1:
52 52 self.offset += offset
53 53 elif whence == 2:
54 54 self.offset = self.end() + offset
55 55 if self.offset < self.size:
56 56 self.fp.seek(self.offset)
57 57
58 58 def read(self, count=-1):
59 59 '''only trick here is reads that span real file and data'''
60 60 ret = ""
61 61 old_offset = self.offset
62 62 if self.offset < self.size:
63 63 s = self.fp.read(count)
64 64 ret = s
65 65 self.offset += len(s)
66 66 if count > 0:
67 67 count -= len(s)
68 68 if count != 0:
69 69 doff = self.offset - self.size
70 70 self.data.insert(0, "".join(self.data))
71 71 del self.data[1:]
72 72 s = self.data[0][doff:doff+count]
73 73 self.offset += len(s)
74 74 ret += s
75 75 return ret
76 76
77 77 def write(self, s):
78 78 self.data.append(s)
79 79 self.offset += len(s)
80 80
81 81 class changelog(revlog):
82 82 def __init__(self, opener):
83 83 revlog.__init__(self, opener, "00changelog.i")
84 84
85 85 def delayupdate(self):
86 86 "delay visibility of index updates to other readers"
87 87 self._realopener = self.opener
88 self.opener = self._appendopener
88 self.opener = self._delayopener
89 self._delaycount = self.count()
89 90 self._delaybuf = []
91 self._delayname = None
90 92
91 93 def finalize(self, tr):
92 94 "finalize index updates"
93 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 100 fp = self.opener(self.indexfile, 'a')
96 101 fp.write("".join(self._delaybuf))
97 102 fp.close()
98 103 del self._delaybuf
104 # split when we're done
99 105 self.checkinlinesize(tr)
100 106
101 def _appendopener(self, name, mode='r'):
107 def _delayopener(self, name, mode='r'):
102 108 fp = self._realopener(name, mode)
109 # only divert the index
103 110 if not name == self.indexfile:
104 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 117 return appender(fp, self._delaybuf)
106 118
107 119 def checkinlinesize(self, tr, fp=None):
108 if self.opener == self._appendopener:
120 if self.opener == self._delayopener:
109 121 return
110 122 return revlog.checkinlinesize(self, tr, fp)
111 123
112 124 def decode_extra(self, text):
113 125 extra = {}
114 126 for l in text.split('\0'):
115 127 if not l:
116 128 continue
117 129 k, v = _string_unescape(l).split(':', 1)
118 130 extra[k] = v
119 131 return extra
120 132
121 133 def encode_extra(self, d):
122 134 items = [_string_escape(":".join(t)) for t in d.iteritems()]
123 135 return "\0".join(items)
124 136
125 137 def extract(self, text):
126 138 """
127 139 format used:
128 140 nodeid\n : manifest node in ascii
129 141 user\n : user, no \n or \r allowed
130 142 time tz extra\n : date (time is int or float, timezone is int)
131 143 : extra is metadatas, encoded and separated by '\0'
132 144 : older versions ignore it
133 145 files\n\n : files modified by the cset, no \n or \r allowed
134 146 (.*) : comment (free text, ideally utf-8)
135 147
136 148 changelog v0 doesn't use extra
137 149 """
138 150 if not text:
139 151 return (nullid, "", (0, 0), [], "", {'branch': 'default'})
140 152 last = text.index("\n\n")
141 153 desc = util.tolocal(text[last + 2:])
142 154 l = text[:last].split('\n')
143 155 manifest = bin(l[0])
144 156 user = util.tolocal(l[1])
145 157
146 158 extra_data = l[2].split(' ', 2)
147 159 if len(extra_data) != 3:
148 160 time = float(extra_data.pop(0))
149 161 try:
150 162 # various tools did silly things with the time zone field.
151 163 timezone = int(extra_data[0])
152 164 except:
153 165 timezone = 0
154 166 extra = {}
155 167 else:
156 168 time, timezone, extra = extra_data
157 169 time, timezone = float(time), int(timezone)
158 170 extra = self.decode_extra(extra)
159 171 if not extra.get('branch'):
160 172 extra['branch'] = 'default'
161 173 files = l[3:]
162 174 return (manifest, user, (time, timezone), files, desc, extra)
163 175
164 176 def read(self, node):
165 177 return self.extract(self.revision(node))
166 178
167 179 def add(self, manifest, list, desc, transaction, p1=None, p2=None,
168 180 user=None, date=None, extra={}):
169 181
170 182 user, desc = util.fromlocal(user), util.fromlocal(desc)
171 183
172 184 if date:
173 185 parseddate = "%d %d" % util.parsedate(date)
174 186 else:
175 187 parseddate = "%d %d" % util.makedate()
176 188 if extra and extra.get("branch") in ("default", ""):
177 189 del extra["branch"]
178 190 if extra:
179 191 extra = self.encode_extra(extra)
180 192 parseddate = "%s %s" % (parseddate, extra)
181 193 list.sort()
182 194 l = [hex(manifest), user, parseddate] + list + ["", desc]
183 195 text = "\n".join(l)
184 196 return self.addrevision(text, transaction, self.count(), p1, p2)
@@ -1,7 +1,7 b''
1 1 0
2 2 0
3 3 adding changesets
4 4 killed!
5 5 transaction abort!
6 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