Show More
@@ -9,13 +9,12 b' import sys, struct' | |||||
9 | from revlog import * |
|
9 | from revlog import * | |
10 | from i18n import gettext as _ |
|
10 | from i18n import gettext as _ | |
11 | from demandload import * |
|
11 | from demandload import * | |
12 | demandload(globals(), "bisect") |
|
12 | demandload(globals(), "bisect array") | |
13 |
|
13 | |||
14 | class manifest(revlog): |
|
14 | class manifest(revlog): | |
15 | def __init__(self, opener): |
|
15 | def __init__(self, opener): | |
16 | self.mapcache = None |
|
16 | self.mapcache = None | |
17 | self.listcache = None |
|
17 | self.listcache = None | |
18 | self.addlist = None |
|
|||
19 | revlog.__init__(self, opener, "00manifest.i", "00manifest.d") |
|
18 | revlog.__init__(self, opener, "00manifest.i", "00manifest.d") | |
20 |
|
19 | |||
21 | def read(self, node): |
|
20 | def read(self, node): | |
@@ -25,8 +24,9 b' class manifest(revlog):' | |||||
25 | text = self.revision(node) |
|
24 | text = self.revision(node) | |
26 | map = {} |
|
25 | map = {} | |
27 | flag = {} |
|
26 | flag = {} | |
28 |
self.listcache = ( |
|
27 | self.listcache = array.array('c', text) | |
29 | for l in self.listcache[1]: |
|
28 | lines = text.splitlines(1) | |
|
29 | for l in lines: | |||
30 | (f, n) = l.split('\0') |
|
30 | (f, n) = l.split('\0') | |
31 | map[f] = bin(n[:40]) |
|
31 | map[f] = bin(n[:40]) | |
32 | flag[f] = (n[40:-1] == "x") |
|
32 | flag[f] = (n[40:-1] == "x") | |
@@ -39,57 +39,67 b' class manifest(revlog):' | |||||
39 | self.read(node) |
|
39 | self.read(node) | |
40 | return self.mapcache[2] |
|
40 | return self.mapcache[2] | |
41 |
|
41 | |||
|
42 | def diff(self, a, b): | |||
|
43 | return mdiff.textdiff(str(a), str(b)) | |||
|
44 | ||||
42 | def add(self, map, flags, transaction, link, p1=None, p2=None, |
|
45 | def add(self, map, flags, transaction, link, p1=None, p2=None, | |
43 | changed=None): |
|
46 | changed=None): | |
44 | # directly generate the mdiff delta from the data collected during |
|
47 | ||
45 | # the bisect loop below |
|
48 | # returns a tuple (start, end). If the string is found | |
46 | def gendelta(delta): |
|
49 | # m[start:end] are the line containing that string. If start == end | |
47 | i = 0 |
|
50 | # the string was not found and they indicate the proper sorted | |
48 | result = [] |
|
51 | # insertion point. This was taken from bisect_left, and modified | |
49 | while i < len(delta): |
|
52 | # to find line start/end as it goes along. | |
50 | start = delta[i][2] |
|
53 | # | |
51 | end = delta[i][3] |
|
54 | # m should be a buffer or a string | |
52 | l = delta[i][4] |
|
55 | # s is a string | |
53 | if l == None: |
|
56 | # | |
54 | l = "" |
|
57 | def manifestsearch(m, s, lo=0, hi=None): | |
55 | while i < len(delta) - 1 and start <= delta[i+1][2] \ |
|
58 | def advance(i, c): | |
56 | and end >= delta[i+1][2]: |
|
59 | while i < lenm and m[i] != c: | |
57 | if delta[i+1][3] > end: |
|
|||
58 | end = delta[i+1][3] |
|
|||
59 | if delta[i+1][4]: |
|
|||
60 | l += delta[i+1][4] |
|
|||
61 | i += 1 |
|
60 | i += 1 | |
62 | result.append(struct.pack(">lll", start, end, len(l)) + l) |
|
61 | return i | |
63 |
|
|
62 | lenm = len(m) | |
64 | return result |
|
63 | if not hi: | |
|
64 | hi = lenm | |||
|
65 | while lo < hi: | |||
|
66 | mid = (lo + hi) // 2 | |||
|
67 | start = mid | |||
|
68 | while start > 0 and m[start-1] != '\n': | |||
|
69 | start -= 1 | |||
|
70 | end = advance(start, '\0') | |||
|
71 | if m[start:end] < s: | |||
|
72 | # we know that after the null there are 40 bytes of sha1 | |||
|
73 | # this translates to the bisect lo = mid + 1 | |||
|
74 | lo = advance(end + 40, '\n') + 1 | |||
|
75 | else: | |||
|
76 | # this translates to the bisect hi = mid | |||
|
77 | hi = start | |||
|
78 | end = advance(lo, '\0') | |||
|
79 | found = m[lo:end] | |||
|
80 | if cmp(s, found) == 0: | |||
|
81 | # we know that after the null there are 40 bytes of sha1 | |||
|
82 | end = advance(end + 40, '\n') | |||
|
83 | return (lo, end+1) | |||
|
84 | else: | |||
|
85 | return (lo, lo) | |||
65 |
|
86 | |||
66 | # apply the changes collected during the bisect loop to our addlist |
|
87 | # apply the changes collected during the bisect loop to our addlist | |
67 | def addlistdelta(addlist, delta): |
|
88 | # return a delta suitable for addrevision | |
68 | # apply the deltas to the addlist. start from the bottom up |
|
89 | def addlistdelta(addlist, x): | |
|
90 | # start from the bottom up | |||
69 | # so changes to the offsets don't mess things up. |
|
91 | # so changes to the offsets don't mess things up. | |
70 |
i = len( |
|
92 | i = len(x) | |
71 | while i > 0: |
|
93 | while i > 0: | |
72 | i -= 1 |
|
94 | i -= 1 | |
73 |
start = |
|
95 | start = x[i][0] | |
74 |
end = |
|
96 | end = x[i][1] | |
75 |
if |
|
97 | if x[i][2]: | |
76 |
addlist[start:end] = |
|
98 | addlist[start:end] = array.array('c', x[i][2]) | |
77 | else: |
|
99 | else: | |
78 | del addlist[start:end] |
|
100 | del addlist[start:end] | |
79 | return addlist |
|
101 | return "".join([struct.pack(">lll", d[0], d[1], len(d[2])) + d[2] \ | |
80 |
|
102 | for d in x ]) | ||
81 | # calculate the byte offset of the start of each line in the |
|
|||
82 | # manifest |
|
|||
83 | def calcoffsets(addlist): |
|
|||
84 | offsets = [0] * (len(addlist) + 1) |
|
|||
85 | offset = 0 |
|
|||
86 | i = 0 |
|
|||
87 | while i < len(addlist): |
|
|||
88 | offsets[i] = offset |
|
|||
89 | offset += len(addlist[i]) |
|
|||
90 | i += 1 |
|
|||
91 | offsets[i] = offset |
|
|||
92 | return offsets |
|
|||
93 |
|
103 | |||
94 | # if we're using the listcache, make sure it is valid and |
|
104 | # if we're using the listcache, make sure it is valid and | |
95 | # parented by the same node we're diffing against |
|
105 | # parented by the same node we're diffing against | |
@@ -98,15 +108,13 b' class manifest(revlog):' | |||||
98 | files = map.keys() |
|
108 | files = map.keys() | |
99 | files.sort() |
|
109 | files.sort() | |
100 |
|
110 | |||
101 |
|
|
111 | text = ["%s\000%s%s\n" % | |
102 | (f, hex(map[f]), flags[f] and "x" or '') |
|
112 | (f, hex(map[f]), flags[f] and "x" or '') | |
103 | for f in files] |
|
113 | for f in files] | |
|
114 | self.listcache = array.array('c', "".join(text)) | |||
104 | cachedelta = None |
|
115 | cachedelta = None | |
105 | else: |
|
116 | else: | |
106 |
addlist = self.listcache |
|
117 | addlist = self.listcache | |
107 |
|
||||
108 | # find the starting offset for each line in the add list |
|
|||
109 | offsets = calcoffsets(addlist) |
|
|||
110 |
|
118 | |||
111 | # combine the changed lists into one list for sorting |
|
119 | # combine the changed lists into one list for sorting | |
112 | work = [[x, 0] for x in changed[0]] |
|
120 | work = [[x, 0] for x in changed[0]] | |
@@ -114,45 +122,52 b' class manifest(revlog):' | |||||
114 | work.sort() |
|
122 | work.sort() | |
115 |
|
123 | |||
116 | delta = [] |
|
124 | delta = [] | |
117 |
|
|
125 | dstart = None | |
|
126 | dend = None | |||
|
127 | dline = [""] | |||
|
128 | start = 0 | |||
|
129 | # zero copy representation of addlist as a buffer | |||
|
130 | addbuf = buffer(addlist) | |||
118 |
|
131 | |||
|
132 | # start with a readonly loop that finds the offset of | |||
|
133 | # each line and creates the deltas | |||
119 | for w in work: |
|
134 | for w in work: | |
120 | f = w[0] |
|
135 | f = w[0] | |
121 | # bs will either be the index of the item or the insert point |
|
136 | # bs will either be the index of the item or the insert point | |
122 |
|
|
137 | start, end = manifestsearch(addbuf, f, start) | |
123 | if bs < len(addlist): |
|
|||
124 | fn = addlist[bs][:addlist[bs].index('\0')] |
|
|||
125 | else: |
|
|||
126 | fn = None |
|
|||
127 | if w[1] == 0: |
|
138 | if w[1] == 0: | |
128 | l = "%s\000%s%s\n" % (f, hex(map[f]), |
|
139 | l = "%s\000%s%s\n" % (f, hex(map[f]), | |
129 | flags[f] and "x" or '') |
|
140 | flags[f] and "x" or '') | |
130 | else: |
|
141 | else: | |
131 |
l = |
|
142 | l = "" | |
132 |
start = |
|
143 | if start == end and w[1] == 1: | |
133 | if fn != f: |
|
144 | # item we want to delete was not found, error out | |
134 | # item not found, insert a new one |
|
145 | raise AssertionError( | |
135 | end = bs |
|
|||
136 | if w[1] == 1: |
|
|||
137 | raise AssertionError( |
|
|||
138 | _("failed to remove %s from manifest\n") % f) |
|
146 | _("failed to remove %s from manifest\n") % f) | |
|
147 | if dstart != None and dstart <= start and dend >= start: | |||
|
148 | if dend < end: | |||
|
149 | dend = end | |||
|
150 | if l: | |||
|
151 | dline.append(l) | |||
139 | else: |
|
152 | else: | |
140 | # item is found, replace/delete the existing line |
|
153 | if dstart != None: | |
141 | end = bs + 1 |
|
154 | delta.append([dstart, dend, "".join(dline)]) | |
142 | delta.append([start, end, offsets[start], offsets[end], l]) |
|
155 | dstart = start | |
|
156 | dend = end | |||
|
157 | dline = [l] | |||
143 |
|
158 | |||
144 | self.addlist = addlistdelta(addlist, delta) |
|
159 | if dstart != None: | |
145 | if self.mapcache[0] == self.tip(): |
|
160 | delta.append([dstart, dend, "".join(dline)]) | |
146 | cachedelta = "".join(gendelta(delta)) |
|
161 | # apply the delta to the addlist, and get a delta for addrevision | |
147 | else: |
|
162 | cachedelta = addlistdelta(addlist, delta) | |
148 | cachedelta = None |
|
|||
149 |
|
163 | |||
150 | text = "".join(self.addlist) |
|
164 | # the delta is only valid if we've been processing the tip revision | |
151 | if cachedelta and mdiff.patch(self.listcache[0], cachedelta) != text: |
|
165 | if self.mapcache[0] != self.tip(): | |
152 | raise AssertionError(_("manifest delta failure\n")) |
|
166 | cachedelta = None | |
153 | n = self.addrevision(text, transaction, link, p1, p2, cachedelta) |
|
167 | self.listcache = addlist | |
|
168 | ||||
|
169 | n = self.addrevision(buffer(self.listcache), transaction, link, p1, \ | |||
|
170 | p2, cachedelta) | |||
154 | self.mapcache = (n, map, flags) |
|
171 | self.mapcache = (n, map, flags) | |
155 | self.listcache = (text, self.addlist) |
|
|||
156 | self.addlist = None |
|
|||
157 |
|
172 | |||
158 | return n |
|
173 | return n |
General Comments 0
You need to be logged in to leave comments.
Login now