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