##// END OF EJS Templates
manifest: add readfast method
Matt Mackall -
r13711:ed913fd7 default
parent child Browse files
Show More
@@ -1,196 +1,203 b''
1 # manifest.py - manifest revision class for mercurial
1 # manifest.py - manifest revision 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 of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from i18n import _
8 from i18n import _
9 import mdiff, parsers, error, revlog
9 import mdiff, parsers, error, revlog
10 import array, struct
10 import array, struct
11
11
12 class manifestdict(dict):
12 class manifestdict(dict):
13 def __init__(self, mapping=None, flags=None):
13 def __init__(self, mapping=None, flags=None):
14 if mapping is None:
14 if mapping is None:
15 mapping = {}
15 mapping = {}
16 if flags is None:
16 if flags is None:
17 flags = {}
17 flags = {}
18 dict.__init__(self, mapping)
18 dict.__init__(self, mapping)
19 self._flags = flags
19 self._flags = flags
20 def flags(self, f):
20 def flags(self, f):
21 return self._flags.get(f, "")
21 return self._flags.get(f, "")
22 def set(self, f, flags):
22 def set(self, f, flags):
23 self._flags[f] = flags
23 self._flags[f] = flags
24 def copy(self):
24 def copy(self):
25 return manifestdict(self, dict.copy(self._flags))
25 return manifestdict(self, dict.copy(self._flags))
26
26
27 class manifest(revlog.revlog):
27 class manifest(revlog.revlog):
28 def __init__(self, opener):
28 def __init__(self, opener):
29 self._mancache = None
29 self._mancache = None
30 revlog.revlog.__init__(self, opener, "00manifest.i")
30 revlog.revlog.__init__(self, opener, "00manifest.i")
31
31
32 def parse(self, lines):
32 def parse(self, lines):
33 mfdict = manifestdict()
33 mfdict = manifestdict()
34 parsers.parse_manifest(mfdict, mfdict._flags, lines)
34 parsers.parse_manifest(mfdict, mfdict._flags, lines)
35 return mfdict
35 return mfdict
36
36
37 def readdelta(self, node):
37 def readdelta(self, node):
38 r = self.rev(node)
38 r = self.rev(node)
39 return self.parse(mdiff.patchtext(self.revdiff(self.deltaparent(r), r)))
39 return self.parse(mdiff.patchtext(self.revdiff(self.deltaparent(r), r)))
40
40
41 def readfast(self, node):
42 '''use the faster of readdelta or read'''
43 r = self.rev(node)
44 if self.deltaparent(r) in self.parentrevs(r):
45 return self.readdelta(node)
46 return self.read(node)
47
41 def read(self, node):
48 def read(self, node):
42 if node == revlog.nullid:
49 if node == revlog.nullid:
43 return manifestdict() # don't upset local cache
50 return manifestdict() # don't upset local cache
44 if self._mancache and self._mancache[0] == node:
51 if self._mancache and self._mancache[0] == node:
45 return self._mancache[1]
52 return self._mancache[1]
46 text = self.revision(node)
53 text = self.revision(node)
47 arraytext = array.array('c', text)
54 arraytext = array.array('c', text)
48 mapping = self.parse(text)
55 mapping = self.parse(text)
49 self._mancache = (node, mapping, arraytext)
56 self._mancache = (node, mapping, arraytext)
50 return mapping
57 return mapping
51
58
52 def _search(self, m, s, lo=0, hi=None):
59 def _search(self, m, s, lo=0, hi=None):
53 '''return a tuple (start, end) that says where to find s within m.
60 '''return a tuple (start, end) that says where to find s within m.
54
61
55 If the string is found m[start:end] are the line containing
62 If the string is found m[start:end] are the line containing
56 that string. If start == end the string was not found and
63 that string. If start == end the string was not found and
57 they indicate the proper sorted insertion point. This was
64 they indicate the proper sorted insertion point. This was
58 taken from bisect_left, and modified to find line start/end as
65 taken from bisect_left, and modified to find line start/end as
59 it goes along.
66 it goes along.
60
67
61 m should be a buffer or a string
68 m should be a buffer or a string
62 s is a string'''
69 s is a string'''
63 def advance(i, c):
70 def advance(i, c):
64 while i < lenm and m[i] != c:
71 while i < lenm and m[i] != c:
65 i += 1
72 i += 1
66 return i
73 return i
67 if not s:
74 if not s:
68 return (lo, lo)
75 return (lo, lo)
69 lenm = len(m)
76 lenm = len(m)
70 if not hi:
77 if not hi:
71 hi = lenm
78 hi = lenm
72 while lo < hi:
79 while lo < hi:
73 mid = (lo + hi) // 2
80 mid = (lo + hi) // 2
74 start = mid
81 start = mid
75 while start > 0 and m[start - 1] != '\n':
82 while start > 0 and m[start - 1] != '\n':
76 start -= 1
83 start -= 1
77 end = advance(start, '\0')
84 end = advance(start, '\0')
78 if m[start:end] < s:
85 if m[start:end] < s:
79 # we know that after the null there are 40 bytes of sha1
86 # we know that after the null there are 40 bytes of sha1
80 # this translates to the bisect lo = mid + 1
87 # this translates to the bisect lo = mid + 1
81 lo = advance(end + 40, '\n') + 1
88 lo = advance(end + 40, '\n') + 1
82 else:
89 else:
83 # this translates to the bisect hi = mid
90 # this translates to the bisect hi = mid
84 hi = start
91 hi = start
85 end = advance(lo, '\0')
92 end = advance(lo, '\0')
86 found = m[lo:end]
93 found = m[lo:end]
87 if s == found:
94 if s == found:
88 # we know that after the null there are 40 bytes of sha1
95 # we know that after the null there are 40 bytes of sha1
89 end = advance(end + 40, '\n')
96 end = advance(end + 40, '\n')
90 return (lo, end + 1)
97 return (lo, end + 1)
91 else:
98 else:
92 return (lo, lo)
99 return (lo, lo)
93
100
94 def find(self, node, f):
101 def find(self, node, f):
95 '''look up entry for a single file efficiently.
102 '''look up entry for a single file efficiently.
96 return (node, flags) pair if found, (None, None) if not.'''
103 return (node, flags) pair if found, (None, None) if not.'''
97 if self._mancache and self._mancache[0] == node:
104 if self._mancache and self._mancache[0] == node:
98 return self._mancache[1].get(f), self._mancache[1].flags(f)
105 return self._mancache[1].get(f), self._mancache[1].flags(f)
99 text = self.revision(node)
106 text = self.revision(node)
100 start, end = self._search(text, f)
107 start, end = self._search(text, f)
101 if start == end:
108 if start == end:
102 return None, None
109 return None, None
103 l = text[start:end]
110 l = text[start:end]
104 f, n = l.split('\0')
111 f, n = l.split('\0')
105 return revlog.bin(n[:40]), n[40:-1]
112 return revlog.bin(n[:40]), n[40:-1]
106
113
107 def add(self, map, transaction, link, p1=None, p2=None,
114 def add(self, map, transaction, link, p1=None, p2=None,
108 changed=None):
115 changed=None):
109 # apply the changes collected during the bisect loop to our addlist
116 # apply the changes collected during the bisect loop to our addlist
110 # return a delta suitable for addrevision
117 # return a delta suitable for addrevision
111 def addlistdelta(addlist, x):
118 def addlistdelta(addlist, x):
112 # start from the bottom up
119 # start from the bottom up
113 # so changes to the offsets don't mess things up.
120 # so changes to the offsets don't mess things up.
114 for start, end, content in reversed(x):
121 for start, end, content in reversed(x):
115 if content:
122 if content:
116 addlist[start:end] = array.array('c', content)
123 addlist[start:end] = array.array('c', content)
117 else:
124 else:
118 del addlist[start:end]
125 del addlist[start:end]
119 return "".join(struct.pack(">lll", start, end, len(content)) + content
126 return "".join(struct.pack(">lll", start, end, len(content)) + content
120 for start, end, content in x)
127 for start, end, content in x)
121
128
122 def checkforbidden(l):
129 def checkforbidden(l):
123 for f in l:
130 for f in l:
124 if '\n' in f or '\r' in f:
131 if '\n' in f or '\r' in f:
125 raise error.RevlogError(
132 raise error.RevlogError(
126 _("'\\n' and '\\r' disallowed in filenames: %r") % f)
133 _("'\\n' and '\\r' disallowed in filenames: %r") % f)
127
134
128 # if we're using the cache, make sure it is valid and
135 # if we're using the cache, make sure it is valid and
129 # parented by the same node we're diffing against
136 # parented by the same node we're diffing against
130 if not (changed and self._mancache and p1 and self._mancache[0] == p1):
137 if not (changed and self._mancache and p1 and self._mancache[0] == p1):
131 files = sorted(map)
138 files = sorted(map)
132 checkforbidden(files)
139 checkforbidden(files)
133
140
134 # if this is changed to support newlines in filenames,
141 # if this is changed to support newlines in filenames,
135 # be sure to check the templates/ dir again (especially *-raw.tmpl)
142 # be sure to check the templates/ dir again (especially *-raw.tmpl)
136 hex, flags = revlog.hex, map.flags
143 hex, flags = revlog.hex, map.flags
137 text = ''.join("%s\000%s%s\n" % (f, hex(map[f]), flags(f))
144 text = ''.join("%s\000%s%s\n" % (f, hex(map[f]), flags(f))
138 for f in files)
145 for f in files)
139 arraytext = array.array('c', text)
146 arraytext = array.array('c', text)
140 cachedelta = None
147 cachedelta = None
141 else:
148 else:
142 added, removed = changed
149 added, removed = changed
143 addlist = self._mancache[2]
150 addlist = self._mancache[2]
144
151
145 checkforbidden(added)
152 checkforbidden(added)
146 # combine the changed lists into one list for sorting
153 # combine the changed lists into one list for sorting
147 work = [(x, False) for x in added]
154 work = [(x, False) for x in added]
148 work.extend((x, True) for x in removed)
155 work.extend((x, True) for x in removed)
149 # this could use heapq.merge() (from python2.6+) or equivalent
156 # this could use heapq.merge() (from python2.6+) or equivalent
150 # since the lists are already sorted
157 # since the lists are already sorted
151 work.sort()
158 work.sort()
152
159
153 delta = []
160 delta = []
154 dstart = None
161 dstart = None
155 dend = None
162 dend = None
156 dline = [""]
163 dline = [""]
157 start = 0
164 start = 0
158 # zero copy representation of addlist as a buffer
165 # zero copy representation of addlist as a buffer
159 addbuf = buffer(addlist)
166 addbuf = buffer(addlist)
160
167
161 # start with a readonly loop that finds the offset of
168 # start with a readonly loop that finds the offset of
162 # each line and creates the deltas
169 # each line and creates the deltas
163 for f, todelete in work:
170 for f, todelete in work:
164 # bs will either be the index of the item or the insert point
171 # bs will either be the index of the item or the insert point
165 start, end = self._search(addbuf, f, start)
172 start, end = self._search(addbuf, f, start)
166 if not todelete:
173 if not todelete:
167 l = "%s\000%s%s\n" % (f, revlog.hex(map[f]), map.flags(f))
174 l = "%s\000%s%s\n" % (f, revlog.hex(map[f]), map.flags(f))
168 else:
175 else:
169 if start == end:
176 if start == end:
170 # item we want to delete was not found, error out
177 # item we want to delete was not found, error out
171 raise AssertionError(
178 raise AssertionError(
172 _("failed to remove %s from manifest") % f)
179 _("failed to remove %s from manifest") % f)
173 l = ""
180 l = ""
174 if dstart is not None and dstart <= start and dend >= start:
181 if dstart is not None and dstart <= start and dend >= start:
175 if dend < end:
182 if dend < end:
176 dend = end
183 dend = end
177 if l:
184 if l:
178 dline.append(l)
185 dline.append(l)
179 else:
186 else:
180 if dstart is not None:
187 if dstart is not None:
181 delta.append([dstart, dend, "".join(dline)])
188 delta.append([dstart, dend, "".join(dline)])
182 dstart = start
189 dstart = start
183 dend = end
190 dend = end
184 dline = [l]
191 dline = [l]
185
192
186 if dstart is not None:
193 if dstart is not None:
187 delta.append([dstart, dend, "".join(dline)])
194 delta.append([dstart, dend, "".join(dline)])
188 # apply the delta to the addlist, and get a delta for addrevision
195 # apply the delta to the addlist, and get a delta for addrevision
189 cachedelta = (self.rev(p1), addlistdelta(addlist, delta))
196 cachedelta = (self.rev(p1), addlistdelta(addlist, delta))
190 arraytext = addlist
197 arraytext = addlist
191 text = buffer(arraytext)
198 text = buffer(arraytext)
192
199
193 n = self.addrevision(text, transaction, link, p1, p2, cachedelta)
200 n = self.addrevision(text, transaction, link, p1, p2, cachedelta)
194 self._mancache = (n, map, arraytext)
201 self._mancache = (n, map, arraytext)
195
202
196 return n
203 return n
General Comments 0
You need to be logged in to leave comments. Login now