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