7 |
7 |
8 from i18n import _ |
8 from i18n import _ |
9 import mdiff, parsers, error, revlog, util |
9 import mdiff, parsers, error, revlog, util |
10 import array, struct |
10 import array, struct |
11 |
11 |
12 # Pure Python fallback |
12 |
13 def _parsemanifest(mfdict, fdict, lines): |
13 class _lazymanifest(dict): |
14 bin = revlog.bin |
14 """This is the pure implementation of lazymanifest. |
15 for l in lines.splitlines(): |
15 |
16 f, n = l.split('\0') |
16 It has not been optimized *at all* and is not lazy. |
17 if len(n) > 40: |
17 """ |
18 fdict[f] = n[40:] |
18 |
19 mfdict[f] = bin(n[:40]) |
19 def __init__(self, data): |
20 else: |
20 # This init method does a little bit of excessive-looking |
21 mfdict[f] = bin(n) |
21 # precondition checking. This is so that the behavior of this |
22 |
22 # class exactly matches its C counterpart to try and help |
23 def _parse(lines, mfdict, flags): |
23 # prevent surprise breakage for anyone that develops against |
24 try: |
24 # the pure version. |
25 parsers.parse_manifest(mfdict, flags, lines) |
25 if data and data[-1] != '\n': |
26 except AttributeError: |
26 raise ValueError('Manifest did not end in a newline.') |
27 _parsemanifest(mfdict, flags, lines) |
27 dict.__init__(self) |
28 return mfdict |
28 prev = None |
29 |
29 for l in data.splitlines(): |
30 class manifestdict(dict): |
30 if prev is not None and prev > l: |
|
31 raise ValueError('Manifest lines not in sorted order.') |
|
32 prev = l |
|
33 f, n = l.split('\0') |
|
34 if len(n) > 40: |
|
35 self[f] = revlog.bin(n[:40]), n[40:] |
|
36 else: |
|
37 self[f] = revlog.bin(n), '' |
|
38 |
|
39 def __setitem__(self, k, v): |
|
40 node, flag = v |
|
41 assert node is not None |
|
42 if len(node) > 21: |
|
43 node = node[:21] # match c implementation behavior |
|
44 dict.__setitem__(self, k, (node, flag)) |
|
45 |
|
46 def __iter__(self): |
|
47 return ((f, e[0], e[1]) for f, e in sorted(self.iteritems())) |
|
48 |
|
49 def copy(self): |
|
50 c = _lazymanifest('') |
|
51 c.update(self) |
|
52 return c |
|
53 |
|
54 def diff(self, m2, clean=False): |
|
55 '''Finds changes between the current manifest and m2.''' |
|
56 diff = {} |
|
57 |
|
58 for fn, e1 in self.iteritems(): |
|
59 if fn not in m2: |
|
60 diff[fn] = e1, (None, '') |
|
61 else: |
|
62 e2 = m2[fn] |
|
63 if e1 != e2: |
|
64 diff[fn] = e1, e2 |
|
65 elif clean: |
|
66 diff[fn] = None |
|
67 |
|
68 for fn, e2 in m2.iteritems(): |
|
69 if fn not in self: |
|
70 diff[fn] = (None, ''), e2 |
|
71 |
|
72 return diff |
|
73 |
|
74 def filtercopy(self, filterfn): |
|
75 c = _lazymanifest('') |
|
76 for f, n, fl in self: |
|
77 if filterfn(f): |
|
78 c[f] = n, fl |
|
79 return c |
|
80 |
|
81 def text(self): |
|
82 """Get the full data of this manifest as a bytestring.""" |
|
83 fl = sorted(self) |
|
84 |
|
85 _hex = revlog.hex |
|
86 # if this is changed to support newlines in filenames, |
|
87 # be sure to check the templates/ dir again (especially *-raw.tmpl) |
|
88 return ''.join("%s\0%s%s\n" % ( |
|
89 f, _hex(n[:20]), flag) for f, n, flag in fl) |
|
90 |
|
91 class manifestdict(object): |
31 def __init__(self, data=''): |
92 def __init__(self, data=''): |
32 self._flags = {} |
93 self._lm = _lazymanifest(data) |
33 _parse(data, self, self._flags) |
94 |
34 |
95 def __getitem__(self, key): |
35 def __setitem__(self, k, v): |
96 return self._lm[key][0] |
36 assert v is not None |
97 |
37 dict.__setitem__(self, k, v) |
98 def __len__(self): |
38 def flags(self, f): |
99 return len(self._lm) |
39 return self._flags.get(f, "") |
100 |
40 def setflag(self, f, flags): |
101 def __setitem__(self, key, node): |
41 """Set the flags (symlink, executable) for path f.""" |
102 self._lm[key] = node, self.flags(key, '') |
42 self._flags[f] = flags |
103 |
43 def copy(self): |
104 def __contains__(self, key): |
44 copy = manifestdict() |
105 return key in self._lm |
45 dict.__init__(copy, self) |
106 |
46 copy._flags = dict.copy(self._flags) |
107 def __delitem__(self, key): |
47 return copy |
108 del self._lm[key] |
|
109 |
|
110 def keys(self): |
|
111 return [x[0] for x in self._lm] |
|
112 |
|
113 def iterkeys(self): |
|
114 return iter(self.keys()) |
|
115 |
48 def intersectfiles(self, files): |
116 def intersectfiles(self, files): |
49 '''make a new manifestdict with the intersection of self with files |
117 '''make a new lazymanifest with the intersection of self with files |
50 |
118 |
51 The algorithm assumes that files is much smaller than self.''' |
119 The algorithm assumes that files is much smaller than self.''' |
52 ret = manifestdict() |
120 ret = manifestdict() |
|
121 lm = self._lm |
53 for fn in files: |
122 for fn in files: |
54 if fn in self: |
123 if fn in lm: |
55 ret[fn] = self[fn] |
124 ret._lm[fn] = self._lm[fn] |
56 flags = self._flags.get(fn, None) |
|
57 if flags: |
|
58 ret._flags[fn] = flags |
|
59 return ret |
125 return ret |
60 |
126 |
61 def filesnotin(self, m2): |
127 def filesnotin(self, m2): |
62 '''Set of files in this manifest that are not in the other''' |
128 '''Set of files in this manifest that are not in the other''' |
63 files = set(self.iterkeys()) |
129 files = set(self.iterkeys()) |
93 nodeid in the current/other manifest and fl1/fl2 is the flag |
157 nodeid in the current/other manifest and fl1/fl2 is the flag |
94 in the current/other manifest. Where the file does not exist, |
158 in the current/other manifest. Where the file does not exist, |
95 the nodeid will be None and the flags will be the empty |
159 the nodeid will be None and the flags will be the empty |
96 string. |
160 string. |
97 ''' |
161 ''' |
98 diff = {} |
162 return self._lm.diff(m2._lm, clean) |
99 |
163 |
100 for fn, n1 in self.iteritems(): |
164 def setflag(self, key, flag): |
101 fl1 = self._flags.get(fn, '') |
165 self._lm[key] = self[key], flag |
102 n2 = m2.get(fn, None) |
166 |
103 fl2 = m2._flags.get(fn, '') |
167 def get(self, key, default=None): |
104 if n2 is None: |
168 try: |
105 fl2 = '' |
169 return self._lm[key][0] |
106 if n1 != n2 or fl1 != fl2: |
170 except KeyError: |
107 diff[fn] = ((n1, fl1), (n2, fl2)) |
171 return default |
108 elif clean: |
172 |
109 diff[fn] = None |
173 def flags(self, key, default=''): |
110 |
174 try: |
111 for fn, n2 in m2.iteritems(): |
175 return self._lm[key][1] |
112 if fn not in self: |
176 except KeyError: |
113 fl2 = m2._flags.get(fn, '') |
177 return default |
114 diff[fn] = ((None, ''), (n2, fl2)) |
178 |
115 |
179 def __iter__(self): |
116 return diff |
180 return (x[0] for x in self._lm) |
|
181 |
|
182 def copy(self): |
|
183 c = manifestdict('') |
|
184 c._lm = self._lm.copy() |
|
185 return c |
|
186 |
|
187 def iteritems(self): |
|
188 return (x[:2] for x in self._lm) |
117 |
189 |
118 def text(self): |
190 def text(self): |
119 """Get the full data of this manifest as a bytestring.""" |
191 return self._lm.text() |
120 fl = sorted(self) |
|
121 _checkforbidden(fl) |
|
122 |
|
123 hex, flags = revlog.hex, self.flags |
|
124 # if this is changed to support newlines in filenames, |
|
125 # be sure to check the templates/ dir again (especially *-raw.tmpl) |
|
126 return ''.join("%s\0%s%s\n" % (f, hex(self[f]), flags(f)) for f in fl) |
|
127 |
192 |
128 def fastdelta(self, base, changes): |
193 def fastdelta(self, base, changes): |
129 """Given a base manifest text as an array.array and a list of changes |
194 """Given a base manifest text as an array.array and a list of changes |
130 relative to that text, compute a delta that can be used by revlog. |
195 relative to that text, compute a delta that can be used by revlog. |
131 """ |
196 """ |
141 # each line and creates the deltas |
206 # each line and creates the deltas |
142 for f, todelete in changes: |
207 for f, todelete in changes: |
143 # bs will either be the index of the item or the insert point |
208 # bs will either be the index of the item or the insert point |
144 start, end = _msearch(addbuf, f, start) |
209 start, end = _msearch(addbuf, f, start) |
145 if not todelete: |
210 if not todelete: |
146 l = "%s\0%s%s\n" % (f, revlog.hex(self[f]), self.flags(f)) |
211 h, fl = self._lm[f] |
|
212 l = "%s\0%s%s\n" % (f, revlog.hex(h), fl) |
147 else: |
213 else: |
148 if start == end: |
214 if start == end: |
149 # item we want to delete was not found, error out |
215 # item we want to delete was not found, error out |
150 raise AssertionError( |
216 raise AssertionError( |
151 _("failed to remove %s from manifest") % f) |
217 _("failed to remove %s from manifest") % f) |
278 return (node, flags) pair if found, (None, None) if not.''' |
344 return (node, flags) pair if found, (None, None) if not.''' |
279 if node in self._mancache: |
345 if node in self._mancache: |
280 m = self._mancache[node][0] |
346 m = self._mancache[node][0] |
281 return m.get(f), m.flags(f) |
347 return m.get(f), m.flags(f) |
282 text = self.revision(node) |
348 text = self.revision(node) |
283 start, end = _msearch(text, f) |
349 try: |
284 if start == end: |
350 return manifestdict(text)._lm[f] |
|
351 except KeyError: |
285 return None, None |
352 return None, None |
286 l = text[start:end] |
|
287 f, n = l.split('\0') |
|
288 return revlog.bin(n[:40]), n[40:-1] |
|
289 |
353 |
290 def add(self, m, transaction, link, p1, p2, added, removed): |
354 def add(self, m, transaction, link, p1, p2, added, removed): |
291 if p1 in self._mancache: |
355 if p1 in self._mancache: |
292 # If our first parent is in the manifest cache, we can |
356 # If our first parent is in the manifest cache, we can |
293 # compute a delta here using properties we know about the |
357 # compute a delta here using properties we know about the |