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 |