comparison mercurial/manifest.py @ 22931:48c0b101a9de

manifest: add fastdelta method to manifestdict This is another step closer to alternate manifest implementations that can offer different hashing algorithms.
author Augie Fackler <raf@durin42.com>
date Wed, 08 Oct 2014 15:20:14 -0400
parents 1acb81d10eaf
children 03602f76deee
comparison
equal deleted inserted replaced
22930:1acb81d10eaf 22931:48c0b101a9de
46 46
47 hex, flags = revlog.hex, self.flags 47 hex, flags = revlog.hex, self.flags
48 # if this is changed to support newlines in filenames, 48 # if this is changed to support newlines in filenames,
49 # be sure to check the templates/ dir again (especially *-raw.tmpl) 49 # be sure to check the templates/ dir again (especially *-raw.tmpl)
50 return ''.join("%s\0%s%s\n" % (f, hex(self[f]), flags(f)) for f in fl) 50 return ''.join("%s\0%s%s\n" % (f, hex(self[f]), flags(f)) for f in fl)
51
52 def fastdelta(self, base, changes):
53 """Given a base manifest text as an array.array and a list of changes
54 relative to that text, compute a delta that can be used by revlog.
55 """
56 delta = []
57 dstart = None
58 dend = None
59 dline = [""]
60 start = 0
61 # zero copy representation of base as a buffer
62 addbuf = util.buffer(base)
63
64 # start with a readonly loop that finds the offset of
65 # each line and creates the deltas
66 for f, todelete in changes:
67 # bs will either be the index of the item or the insert point
68 start, end = _msearch(addbuf, f, start)
69 if not todelete:
70 l = "%s\0%s%s\n" % (f, revlog.hex(self[f]), self.flags(f))
71 else:
72 if start == end:
73 # item we want to delete was not found, error out
74 raise AssertionError(
75 _("failed to remove %s from manifest") % f)
76 l = ""
77 if dstart is not None and dstart <= start and dend >= start:
78 if dend < end:
79 dend = end
80 if l:
81 dline.append(l)
82 else:
83 if dstart is not None:
84 delta.append([dstart, dend, "".join(dline)])
85 dstart = start
86 dend = end
87 dline = [l]
88
89 if dstart is not None:
90 delta.append([dstart, dend, "".join(dline)])
91 # apply the delta to the base, and get a delta for addrevision
92 deltatext, arraytext = _addlistdelta(base, delta)
93 return arraytext, deltatext
51 94
52 def _msearch(m, s, lo=0, hi=None): 95 def _msearch(m, s, lo=0, hi=None):
53 '''return a tuple (start, end) that says where to find s within m. 96 '''return a tuple (start, end) that says where to find s within m.
54 97
55 If the string is found m[start:end] are the line containing 98 If the string is found m[start:end] are the line containing
171 if p1 in self._mancache: 214 if p1 in self._mancache:
172 # If our first parent is in the manifest cache, we can 215 # If our first parent is in the manifest cache, we can
173 # compute a delta here using properties we know about the 216 # compute a delta here using properties we know about the
174 # manifest up-front, which may save time later for the 217 # manifest up-front, which may save time later for the
175 # revlog layer. 218 # revlog layer.
176 addlist = self._mancache[p1][1]
177 219
178 _checkforbidden(added) 220 _checkforbidden(added)
179 # combine the changed lists into one list for sorting 221 # combine the changed lists into one list for sorting
180 work = [(x, False) for x in added] 222 work = [(x, False) for x in added]
181 work.extend((x, True) for x in removed) 223 work.extend((x, True) for x in removed)
182 # this could use heapq.merge() (from Python 2.6+) or equivalent 224 # this could use heapq.merge() (from Python 2.6+) or equivalent
183 # since the lists are already sorted 225 # since the lists are already sorted
184 work.sort() 226 work.sort()
185 227
186 delta = [] 228 arraytext, deltatext = map.fastdelta(self._mancache[p1][1], work)
187 dstart = None 229 cachedelta = self.rev(p1), deltatext
188 dend = None
189 dline = [""]
190 start = 0
191 # zero copy representation of addlist as a buffer
192 addbuf = util.buffer(addlist)
193
194 # start with a readonly loop that finds the offset of
195 # each line and creates the deltas
196 for f, todelete in work:
197 # bs will either be the index of the item or the insert point
198 start, end = _msearch(addbuf, f, start)
199 if not todelete:
200 l = "%s\0%s%s\n" % (f, revlog.hex(map[f]), map.flags(f))
201 else:
202 if start == end:
203 # item we want to delete was not found, error out
204 raise AssertionError(
205 _("failed to remove %s from manifest") % f)
206 l = ""
207 if dstart is not None and dstart <= start and dend >= start:
208 if dend < end:
209 dend = end
210 if l:
211 dline.append(l)
212 else:
213 if dstart is not None:
214 delta.append([dstart, dend, "".join(dline)])
215 dstart = start
216 dend = end
217 dline = [l]
218
219 if dstart is not None:
220 delta.append([dstart, dend, "".join(dline)])
221 # apply the delta to the addlist, and get a delta for addrevision
222 deltatext, addlist = _addlistdelta(addlist, delta)
223 cachedelta = (self.rev(p1), deltatext)
224 arraytext = addlist
225 text = util.buffer(arraytext) 230 text = util.buffer(arraytext)
226 else: 231 else:
227 # The first parent manifest isn't already loaded, so we'll 232 # The first parent manifest isn't already loaded, so we'll
228 # just encode a fulltext of the manifest and pass that 233 # just encode a fulltext of the manifest and pass that
229 # through to the revlog layer, and let it handle the delta 234 # through to the revlog layer, and let it handle the delta