##// END OF EJS Templates
treemanifest: make node reuse match flat manifest behavior...
Durham Goode -
r31294:c134a33b default
parent child Browse files
Show More
@@ -1,1599 +1,1603 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 __future__ import absolute_import
8 from __future__ import absolute_import
9
9
10 import array
10 import array
11 import heapq
11 import heapq
12 import os
12 import os
13 import struct
13 import struct
14
14
15 from .i18n import _
15 from .i18n import _
16 from . import (
16 from . import (
17 error,
17 error,
18 mdiff,
18 mdiff,
19 parsers,
19 parsers,
20 revlog,
20 revlog,
21 util,
21 util,
22 )
22 )
23
23
24 propertycache = util.propertycache
24 propertycache = util.propertycache
25
25
26 def _parsev1(data):
26 def _parsev1(data):
27 # This method does a little bit of excessive-looking
27 # This method does a little bit of excessive-looking
28 # precondition checking. This is so that the behavior of this
28 # precondition checking. This is so that the behavior of this
29 # class exactly matches its C counterpart to try and help
29 # class exactly matches its C counterpart to try and help
30 # prevent surprise breakage for anyone that develops against
30 # prevent surprise breakage for anyone that develops against
31 # the pure version.
31 # the pure version.
32 if data and data[-1] != '\n':
32 if data and data[-1] != '\n':
33 raise ValueError('Manifest did not end in a newline.')
33 raise ValueError('Manifest did not end in a newline.')
34 prev = None
34 prev = None
35 for l in data.splitlines():
35 for l in data.splitlines():
36 if prev is not None and prev > l:
36 if prev is not None and prev > l:
37 raise ValueError('Manifest lines not in sorted order.')
37 raise ValueError('Manifest lines not in sorted order.')
38 prev = l
38 prev = l
39 f, n = l.split('\0')
39 f, n = l.split('\0')
40 if len(n) > 40:
40 if len(n) > 40:
41 yield f, revlog.bin(n[:40]), n[40:]
41 yield f, revlog.bin(n[:40]), n[40:]
42 else:
42 else:
43 yield f, revlog.bin(n), ''
43 yield f, revlog.bin(n), ''
44
44
45 def _parsev2(data):
45 def _parsev2(data):
46 metadataend = data.find('\n')
46 metadataend = data.find('\n')
47 # Just ignore metadata for now
47 # Just ignore metadata for now
48 pos = metadataend + 1
48 pos = metadataend + 1
49 prevf = ''
49 prevf = ''
50 while pos < len(data):
50 while pos < len(data):
51 end = data.find('\n', pos + 1) # +1 to skip stem length byte
51 end = data.find('\n', pos + 1) # +1 to skip stem length byte
52 if end == -1:
52 if end == -1:
53 raise ValueError('Manifest ended with incomplete file entry.')
53 raise ValueError('Manifest ended with incomplete file entry.')
54 stemlen = ord(data[pos])
54 stemlen = ord(data[pos])
55 items = data[pos + 1:end].split('\0')
55 items = data[pos + 1:end].split('\0')
56 f = prevf[:stemlen] + items[0]
56 f = prevf[:stemlen] + items[0]
57 if prevf > f:
57 if prevf > f:
58 raise ValueError('Manifest entries not in sorted order.')
58 raise ValueError('Manifest entries not in sorted order.')
59 fl = items[1]
59 fl = items[1]
60 # Just ignore metadata (items[2:] for now)
60 # Just ignore metadata (items[2:] for now)
61 n = data[end + 1:end + 21]
61 n = data[end + 1:end + 21]
62 yield f, n, fl
62 yield f, n, fl
63 pos = end + 22
63 pos = end + 22
64 prevf = f
64 prevf = f
65
65
66 def _parse(data):
66 def _parse(data):
67 """Generates (path, node, flags) tuples from a manifest text"""
67 """Generates (path, node, flags) tuples from a manifest text"""
68 if data.startswith('\0'):
68 if data.startswith('\0'):
69 return iter(_parsev2(data))
69 return iter(_parsev2(data))
70 else:
70 else:
71 return iter(_parsev1(data))
71 return iter(_parsev1(data))
72
72
73 def _text(it, usemanifestv2):
73 def _text(it, usemanifestv2):
74 """Given an iterator over (path, node, flags) tuples, returns a manifest
74 """Given an iterator over (path, node, flags) tuples, returns a manifest
75 text"""
75 text"""
76 if usemanifestv2:
76 if usemanifestv2:
77 return _textv2(it)
77 return _textv2(it)
78 else:
78 else:
79 return _textv1(it)
79 return _textv1(it)
80
80
81 def _textv1(it):
81 def _textv1(it):
82 files = []
82 files = []
83 lines = []
83 lines = []
84 _hex = revlog.hex
84 _hex = revlog.hex
85 for f, n, fl in it:
85 for f, n, fl in it:
86 files.append(f)
86 files.append(f)
87 # if this is changed to support newlines in filenames,
87 # if this is changed to support newlines in filenames,
88 # be sure to check the templates/ dir again (especially *-raw.tmpl)
88 # be sure to check the templates/ dir again (especially *-raw.tmpl)
89 lines.append("%s\0%s%s\n" % (f, _hex(n), fl))
89 lines.append("%s\0%s%s\n" % (f, _hex(n), fl))
90
90
91 _checkforbidden(files)
91 _checkforbidden(files)
92 return ''.join(lines)
92 return ''.join(lines)
93
93
94 def _textv2(it):
94 def _textv2(it):
95 files = []
95 files = []
96 lines = ['\0\n']
96 lines = ['\0\n']
97 prevf = ''
97 prevf = ''
98 for f, n, fl in it:
98 for f, n, fl in it:
99 files.append(f)
99 files.append(f)
100 stem = os.path.commonprefix([prevf, f])
100 stem = os.path.commonprefix([prevf, f])
101 stemlen = min(len(stem), 255)
101 stemlen = min(len(stem), 255)
102 lines.append("%c%s\0%s\n%s\n" % (stemlen, f[stemlen:], fl, n))
102 lines.append("%c%s\0%s\n%s\n" % (stemlen, f[stemlen:], fl, n))
103 prevf = f
103 prevf = f
104 _checkforbidden(files)
104 _checkforbidden(files)
105 return ''.join(lines)
105 return ''.join(lines)
106
106
107 class lazymanifestiter(object):
107 class lazymanifestiter(object):
108 def __init__(self, lm):
108 def __init__(self, lm):
109 self.pos = 0
109 self.pos = 0
110 self.lm = lm
110 self.lm = lm
111
111
112 def __iter__(self):
112 def __iter__(self):
113 return self
113 return self
114
114
115 def next(self):
115 def next(self):
116 try:
116 try:
117 data, pos = self.lm._get(self.pos)
117 data, pos = self.lm._get(self.pos)
118 except IndexError:
118 except IndexError:
119 raise StopIteration
119 raise StopIteration
120 if pos == -1:
120 if pos == -1:
121 self.pos += 1
121 self.pos += 1
122 return data[0]
122 return data[0]
123 self.pos += 1
123 self.pos += 1
124 zeropos = data.find('\x00', pos)
124 zeropos = data.find('\x00', pos)
125 return data[pos:zeropos]
125 return data[pos:zeropos]
126
126
127 class lazymanifestiterentries(object):
127 class lazymanifestiterentries(object):
128 def __init__(self, lm):
128 def __init__(self, lm):
129 self.lm = lm
129 self.lm = lm
130 self.pos = 0
130 self.pos = 0
131
131
132 def __iter__(self):
132 def __iter__(self):
133 return self
133 return self
134
134
135 def next(self):
135 def next(self):
136 try:
136 try:
137 data, pos = self.lm._get(self.pos)
137 data, pos = self.lm._get(self.pos)
138 except IndexError:
138 except IndexError:
139 raise StopIteration
139 raise StopIteration
140 if pos == -1:
140 if pos == -1:
141 self.pos += 1
141 self.pos += 1
142 return data
142 return data
143 zeropos = data.find('\x00', pos)
143 zeropos = data.find('\x00', pos)
144 hashval = unhexlify(data, self.lm.extrainfo[self.pos],
144 hashval = unhexlify(data, self.lm.extrainfo[self.pos],
145 zeropos + 1, 40)
145 zeropos + 1, 40)
146 flags = self.lm._getflags(data, self.pos, zeropos)
146 flags = self.lm._getflags(data, self.pos, zeropos)
147 self.pos += 1
147 self.pos += 1
148 return (data[pos:zeropos], hashval, flags)
148 return (data[pos:zeropos], hashval, flags)
149
149
150 def unhexlify(data, extra, pos, length):
150 def unhexlify(data, extra, pos, length):
151 s = data[pos:pos + length].decode('hex')
151 s = data[pos:pos + length].decode('hex')
152 if extra:
152 if extra:
153 s += chr(extra & 0xff)
153 s += chr(extra & 0xff)
154 return s
154 return s
155
155
156 def _cmp(a, b):
156 def _cmp(a, b):
157 return (a > b) - (a < b)
157 return (a > b) - (a < b)
158
158
159 class _lazymanifest(object):
159 class _lazymanifest(object):
160 def __init__(self, data, positions=None, extrainfo=None, extradata=None):
160 def __init__(self, data, positions=None, extrainfo=None, extradata=None):
161 if positions is None:
161 if positions is None:
162 self.positions = self.findlines(data)
162 self.positions = self.findlines(data)
163 self.extrainfo = [0] * len(self.positions)
163 self.extrainfo = [0] * len(self.positions)
164 self.data = data
164 self.data = data
165 self.extradata = []
165 self.extradata = []
166 else:
166 else:
167 self.positions = positions[:]
167 self.positions = positions[:]
168 self.extrainfo = extrainfo[:]
168 self.extrainfo = extrainfo[:]
169 self.extradata = extradata[:]
169 self.extradata = extradata[:]
170 self.data = data
170 self.data = data
171
171
172 def findlines(self, data):
172 def findlines(self, data):
173 if not data:
173 if not data:
174 return []
174 return []
175 pos = data.find("\n")
175 pos = data.find("\n")
176 if pos == -1 or data[-1] != '\n':
176 if pos == -1 or data[-1] != '\n':
177 raise ValueError("Manifest did not end in a newline.")
177 raise ValueError("Manifest did not end in a newline.")
178 positions = [0]
178 positions = [0]
179 prev = data[:data.find('\x00')]
179 prev = data[:data.find('\x00')]
180 while pos < len(data) - 1 and pos != -1:
180 while pos < len(data) - 1 and pos != -1:
181 positions.append(pos + 1)
181 positions.append(pos + 1)
182 nexts = data[pos + 1:data.find('\x00', pos + 1)]
182 nexts = data[pos + 1:data.find('\x00', pos + 1)]
183 if nexts < prev:
183 if nexts < prev:
184 raise ValueError("Manifest lines not in sorted order.")
184 raise ValueError("Manifest lines not in sorted order.")
185 prev = nexts
185 prev = nexts
186 pos = data.find("\n", pos + 1)
186 pos = data.find("\n", pos + 1)
187 return positions
187 return positions
188
188
189 def _get(self, index):
189 def _get(self, index):
190 # get the position encoded in pos:
190 # get the position encoded in pos:
191 # positive number is an index in 'data'
191 # positive number is an index in 'data'
192 # negative number is in extrapieces
192 # negative number is in extrapieces
193 pos = self.positions[index]
193 pos = self.positions[index]
194 if pos >= 0:
194 if pos >= 0:
195 return self.data, pos
195 return self.data, pos
196 return self.extradata[-pos - 1], -1
196 return self.extradata[-pos - 1], -1
197
197
198 def _getkey(self, pos):
198 def _getkey(self, pos):
199 if pos >= 0:
199 if pos >= 0:
200 return self.data[pos:self.data.find('\x00', pos + 1)]
200 return self.data[pos:self.data.find('\x00', pos + 1)]
201 return self.extradata[-pos - 1][0]
201 return self.extradata[-pos - 1][0]
202
202
203 def bsearch(self, key):
203 def bsearch(self, key):
204 first = 0
204 first = 0
205 last = len(self.positions) - 1
205 last = len(self.positions) - 1
206
206
207 while first <= last:
207 while first <= last:
208 midpoint = (first + last)//2
208 midpoint = (first + last)//2
209 nextpos = self.positions[midpoint]
209 nextpos = self.positions[midpoint]
210 candidate = self._getkey(nextpos)
210 candidate = self._getkey(nextpos)
211 r = _cmp(key, candidate)
211 r = _cmp(key, candidate)
212 if r == 0:
212 if r == 0:
213 return midpoint
213 return midpoint
214 else:
214 else:
215 if r < 0:
215 if r < 0:
216 last = midpoint - 1
216 last = midpoint - 1
217 else:
217 else:
218 first = midpoint + 1
218 first = midpoint + 1
219 return -1
219 return -1
220
220
221 def bsearch2(self, key):
221 def bsearch2(self, key):
222 # same as the above, but will always return the position
222 # same as the above, but will always return the position
223 # done for performance reasons
223 # done for performance reasons
224 first = 0
224 first = 0
225 last = len(self.positions) - 1
225 last = len(self.positions) - 1
226
226
227 while first <= last:
227 while first <= last:
228 midpoint = (first + last)//2
228 midpoint = (first + last)//2
229 nextpos = self.positions[midpoint]
229 nextpos = self.positions[midpoint]
230 candidate = self._getkey(nextpos)
230 candidate = self._getkey(nextpos)
231 r = _cmp(key, candidate)
231 r = _cmp(key, candidate)
232 if r == 0:
232 if r == 0:
233 return (midpoint, True)
233 return (midpoint, True)
234 else:
234 else:
235 if r < 0:
235 if r < 0:
236 last = midpoint - 1
236 last = midpoint - 1
237 else:
237 else:
238 first = midpoint + 1
238 first = midpoint + 1
239 return (first, False)
239 return (first, False)
240
240
241 def __contains__(self, key):
241 def __contains__(self, key):
242 return self.bsearch(key) != -1
242 return self.bsearch(key) != -1
243
243
244 def _getflags(self, data, needle, pos):
244 def _getflags(self, data, needle, pos):
245 start = pos + 41
245 start = pos + 41
246 end = data.find("\n", start)
246 end = data.find("\n", start)
247 if end == -1:
247 if end == -1:
248 end = len(data) - 1
248 end = len(data) - 1
249 if start == end:
249 if start == end:
250 return ''
250 return ''
251 return self.data[start:end]
251 return self.data[start:end]
252
252
253 def __getitem__(self, key):
253 def __getitem__(self, key):
254 if not isinstance(key, str):
254 if not isinstance(key, str):
255 raise TypeError("getitem: manifest keys must be a string.")
255 raise TypeError("getitem: manifest keys must be a string.")
256 needle = self.bsearch(key)
256 needle = self.bsearch(key)
257 if needle == -1:
257 if needle == -1:
258 raise KeyError
258 raise KeyError
259 data, pos = self._get(needle)
259 data, pos = self._get(needle)
260 if pos == -1:
260 if pos == -1:
261 return (data[1], data[2])
261 return (data[1], data[2])
262 zeropos = data.find('\x00', pos)
262 zeropos = data.find('\x00', pos)
263 assert 0 <= needle <= len(self.positions)
263 assert 0 <= needle <= len(self.positions)
264 assert len(self.extrainfo) == len(self.positions)
264 assert len(self.extrainfo) == len(self.positions)
265 hashval = unhexlify(data, self.extrainfo[needle], zeropos + 1, 40)
265 hashval = unhexlify(data, self.extrainfo[needle], zeropos + 1, 40)
266 flags = self._getflags(data, needle, zeropos)
266 flags = self._getflags(data, needle, zeropos)
267 return (hashval, flags)
267 return (hashval, flags)
268
268
269 def __delitem__(self, key):
269 def __delitem__(self, key):
270 needle, found = self.bsearch2(key)
270 needle, found = self.bsearch2(key)
271 if not found:
271 if not found:
272 raise KeyError
272 raise KeyError
273 cur = self.positions[needle]
273 cur = self.positions[needle]
274 self.positions = self.positions[:needle] + self.positions[needle + 1:]
274 self.positions = self.positions[:needle] + self.positions[needle + 1:]
275 self.extrainfo = self.extrainfo[:needle] + self.extrainfo[needle + 1:]
275 self.extrainfo = self.extrainfo[:needle] + self.extrainfo[needle + 1:]
276 if cur >= 0:
276 if cur >= 0:
277 self.data = self.data[:cur] + '\x00' + self.data[cur + 1:]
277 self.data = self.data[:cur] + '\x00' + self.data[cur + 1:]
278
278
279 def __setitem__(self, key, value):
279 def __setitem__(self, key, value):
280 if not isinstance(key, str):
280 if not isinstance(key, str):
281 raise TypeError("setitem: manifest keys must be a string.")
281 raise TypeError("setitem: manifest keys must be a string.")
282 if not isinstance(value, tuple) or len(value) != 2:
282 if not isinstance(value, tuple) or len(value) != 2:
283 raise TypeError("Manifest values must be a tuple of (node, flags).")
283 raise TypeError("Manifest values must be a tuple of (node, flags).")
284 hashval = value[0]
284 hashval = value[0]
285 if not isinstance(hashval, str) or not 20 <= len(hashval) <= 22:
285 if not isinstance(hashval, str) or not 20 <= len(hashval) <= 22:
286 raise TypeError("node must be a 20-byte string")
286 raise TypeError("node must be a 20-byte string")
287 flags = value[1]
287 flags = value[1]
288 if len(hashval) == 22:
288 if len(hashval) == 22:
289 hashval = hashval[:-1]
289 hashval = hashval[:-1]
290 if not isinstance(flags, str) or len(flags) > 1:
290 if not isinstance(flags, str) or len(flags) > 1:
291 raise TypeError("flags must a 0 or 1 byte string, got %r", flags)
291 raise TypeError("flags must a 0 or 1 byte string, got %r", flags)
292 needle, found = self.bsearch2(key)
292 needle, found = self.bsearch2(key)
293 if found:
293 if found:
294 # put the item
294 # put the item
295 pos = self.positions[needle]
295 pos = self.positions[needle]
296 if pos < 0:
296 if pos < 0:
297 self.extradata[-pos - 1] = (key, hashval, value[1])
297 self.extradata[-pos - 1] = (key, hashval, value[1])
298 else:
298 else:
299 # just don't bother
299 # just don't bother
300 self.extradata.append((key, hashval, value[1]))
300 self.extradata.append((key, hashval, value[1]))
301 self.positions[needle] = -len(self.extradata)
301 self.positions[needle] = -len(self.extradata)
302 else:
302 else:
303 # not found, put it in with extra positions
303 # not found, put it in with extra positions
304 self.extradata.append((key, hashval, value[1]))
304 self.extradata.append((key, hashval, value[1]))
305 self.positions = (self.positions[:needle] + [-len(self.extradata)]
305 self.positions = (self.positions[:needle] + [-len(self.extradata)]
306 + self.positions[needle:])
306 + self.positions[needle:])
307 self.extrainfo = (self.extrainfo[:needle] + [0] +
307 self.extrainfo = (self.extrainfo[:needle] + [0] +
308 self.extrainfo[needle:])
308 self.extrainfo[needle:])
309
309
310 def copy(self):
310 def copy(self):
311 # XXX call _compact like in C?
311 # XXX call _compact like in C?
312 return _lazymanifest(self.data, self.positions, self.extrainfo,
312 return _lazymanifest(self.data, self.positions, self.extrainfo,
313 self.extradata)
313 self.extradata)
314
314
315 def _compact(self):
315 def _compact(self):
316 # hopefully not called TOO often
316 # hopefully not called TOO often
317 if len(self.extradata) == 0:
317 if len(self.extradata) == 0:
318 return
318 return
319 l = []
319 l = []
320 last_cut = 0
320 last_cut = 0
321 i = 0
321 i = 0
322 offset = 0
322 offset = 0
323 self.extrainfo = [0] * len(self.positions)
323 self.extrainfo = [0] * len(self.positions)
324 while i < len(self.positions):
324 while i < len(self.positions):
325 if self.positions[i] >= 0:
325 if self.positions[i] >= 0:
326 cur = self.positions[i]
326 cur = self.positions[i]
327 last_cut = cur
327 last_cut = cur
328 while True:
328 while True:
329 self.positions[i] = offset
329 self.positions[i] = offset
330 i += 1
330 i += 1
331 if i == len(self.positions) or self.positions[i] < 0:
331 if i == len(self.positions) or self.positions[i] < 0:
332 break
332 break
333 offset += self.positions[i] - cur
333 offset += self.positions[i] - cur
334 cur = self.positions[i]
334 cur = self.positions[i]
335 end_cut = self.data.find('\n', cur)
335 end_cut = self.data.find('\n', cur)
336 if end_cut != -1:
336 if end_cut != -1:
337 end_cut += 1
337 end_cut += 1
338 offset += end_cut - cur
338 offset += end_cut - cur
339 l.append(self.data[last_cut:end_cut])
339 l.append(self.data[last_cut:end_cut])
340 else:
340 else:
341 while i < len(self.positions) and self.positions[i] < 0:
341 while i < len(self.positions) and self.positions[i] < 0:
342 cur = self.positions[i]
342 cur = self.positions[i]
343 t = self.extradata[-cur - 1]
343 t = self.extradata[-cur - 1]
344 l.append(self._pack(t))
344 l.append(self._pack(t))
345 self.positions[i] = offset
345 self.positions[i] = offset
346 if len(t[1]) > 20:
346 if len(t[1]) > 20:
347 self.extrainfo[i] = ord(t[1][21])
347 self.extrainfo[i] = ord(t[1][21])
348 offset += len(l[-1])
348 offset += len(l[-1])
349 i += 1
349 i += 1
350 self.data = ''.join(l)
350 self.data = ''.join(l)
351 self.extradata = []
351 self.extradata = []
352
352
353 def _pack(self, d):
353 def _pack(self, d):
354 return d[0] + '\x00' + d[1][:20].encode('hex') + d[2] + '\n'
354 return d[0] + '\x00' + d[1][:20].encode('hex') + d[2] + '\n'
355
355
356 def text(self):
356 def text(self):
357 self._compact()
357 self._compact()
358 return self.data
358 return self.data
359
359
360 def diff(self, m2, clean=False):
360 def diff(self, m2, clean=False):
361 '''Finds changes between the current manifest and m2.'''
361 '''Finds changes between the current manifest and m2.'''
362 # XXX think whether efficiency matters here
362 # XXX think whether efficiency matters here
363 diff = {}
363 diff = {}
364
364
365 for fn, e1, flags in self.iterentries():
365 for fn, e1, flags in self.iterentries():
366 if fn not in m2:
366 if fn not in m2:
367 diff[fn] = (e1, flags), (None, '')
367 diff[fn] = (e1, flags), (None, '')
368 else:
368 else:
369 e2 = m2[fn]
369 e2 = m2[fn]
370 if (e1, flags) != e2:
370 if (e1, flags) != e2:
371 diff[fn] = (e1, flags), e2
371 diff[fn] = (e1, flags), e2
372 elif clean:
372 elif clean:
373 diff[fn] = None
373 diff[fn] = None
374
374
375 for fn, e2, flags in m2.iterentries():
375 for fn, e2, flags in m2.iterentries():
376 if fn not in self:
376 if fn not in self:
377 diff[fn] = (None, ''), (e2, flags)
377 diff[fn] = (None, ''), (e2, flags)
378
378
379 return diff
379 return diff
380
380
381 def iterentries(self):
381 def iterentries(self):
382 return lazymanifestiterentries(self)
382 return lazymanifestiterentries(self)
383
383
384 def iterkeys(self):
384 def iterkeys(self):
385 return lazymanifestiter(self)
385 return lazymanifestiter(self)
386
386
387 def __iter__(self):
387 def __iter__(self):
388 return lazymanifestiter(self)
388 return lazymanifestiter(self)
389
389
390 def __len__(self):
390 def __len__(self):
391 return len(self.positions)
391 return len(self.positions)
392
392
393 def filtercopy(self, filterfn):
393 def filtercopy(self, filterfn):
394 # XXX should be optimized
394 # XXX should be optimized
395 c = _lazymanifest('')
395 c = _lazymanifest('')
396 for f, n, fl in self.iterentries():
396 for f, n, fl in self.iterentries():
397 if filterfn(f):
397 if filterfn(f):
398 c[f] = n, fl
398 c[f] = n, fl
399 return c
399 return c
400
400
401 try:
401 try:
402 _lazymanifest = parsers.lazymanifest
402 _lazymanifest = parsers.lazymanifest
403 except AttributeError:
403 except AttributeError:
404 pass
404 pass
405
405
406 class manifestdict(object):
406 class manifestdict(object):
407 def __init__(self, data=''):
407 def __init__(self, data=''):
408 if data.startswith('\0'):
408 if data.startswith('\0'):
409 #_lazymanifest can not parse v2
409 #_lazymanifest can not parse v2
410 self._lm = _lazymanifest('')
410 self._lm = _lazymanifest('')
411 for f, n, fl in _parsev2(data):
411 for f, n, fl in _parsev2(data):
412 self._lm[f] = n, fl
412 self._lm[f] = n, fl
413 else:
413 else:
414 self._lm = _lazymanifest(data)
414 self._lm = _lazymanifest(data)
415
415
416 def __getitem__(self, key):
416 def __getitem__(self, key):
417 return self._lm[key][0]
417 return self._lm[key][0]
418
418
419 def find(self, key):
419 def find(self, key):
420 return self._lm[key]
420 return self._lm[key]
421
421
422 def __len__(self):
422 def __len__(self):
423 return len(self._lm)
423 return len(self._lm)
424
424
425 def __nonzero__(self):
425 def __nonzero__(self):
426 # nonzero is covered by the __len__ function, but implementing it here
426 # nonzero is covered by the __len__ function, but implementing it here
427 # makes it easier for extensions to override.
427 # makes it easier for extensions to override.
428 return len(self._lm) != 0
428 return len(self._lm) != 0
429
429
430 def __setitem__(self, key, node):
430 def __setitem__(self, key, node):
431 self._lm[key] = node, self.flags(key, '')
431 self._lm[key] = node, self.flags(key, '')
432
432
433 def __contains__(self, key):
433 def __contains__(self, key):
434 return key in self._lm
434 return key in self._lm
435
435
436 def __delitem__(self, key):
436 def __delitem__(self, key):
437 del self._lm[key]
437 del self._lm[key]
438
438
439 def __iter__(self):
439 def __iter__(self):
440 return self._lm.__iter__()
440 return self._lm.__iter__()
441
441
442 def iterkeys(self):
442 def iterkeys(self):
443 return self._lm.iterkeys()
443 return self._lm.iterkeys()
444
444
445 def keys(self):
445 def keys(self):
446 return list(self.iterkeys())
446 return list(self.iterkeys())
447
447
448 def filesnotin(self, m2, match=None):
448 def filesnotin(self, m2, match=None):
449 '''Set of files in this manifest that are not in the other'''
449 '''Set of files in this manifest that are not in the other'''
450 if match:
450 if match:
451 m1 = self.matches(match)
451 m1 = self.matches(match)
452 m2 = m2.matches(match)
452 m2 = m2.matches(match)
453 return m1.filesnotin(m2)
453 return m1.filesnotin(m2)
454 diff = self.diff(m2)
454 diff = self.diff(m2)
455 files = set(filepath
455 files = set(filepath
456 for filepath, hashflags in diff.iteritems()
456 for filepath, hashflags in diff.iteritems()
457 if hashflags[1][0] is None)
457 if hashflags[1][0] is None)
458 return files
458 return files
459
459
460 @propertycache
460 @propertycache
461 def _dirs(self):
461 def _dirs(self):
462 return util.dirs(self)
462 return util.dirs(self)
463
463
464 def dirs(self):
464 def dirs(self):
465 return self._dirs
465 return self._dirs
466
466
467 def hasdir(self, dir):
467 def hasdir(self, dir):
468 return dir in self._dirs
468 return dir in self._dirs
469
469
470 def _filesfastpath(self, match):
470 def _filesfastpath(self, match):
471 '''Checks whether we can correctly and quickly iterate over matcher
471 '''Checks whether we can correctly and quickly iterate over matcher
472 files instead of over manifest files.'''
472 files instead of over manifest files.'''
473 files = match.files()
473 files = match.files()
474 return (len(files) < 100 and (match.isexact() or
474 return (len(files) < 100 and (match.isexact() or
475 (match.prefix() and all(fn in self for fn in files))))
475 (match.prefix() and all(fn in self for fn in files))))
476
476
477 def walk(self, match):
477 def walk(self, match):
478 '''Generates matching file names.
478 '''Generates matching file names.
479
479
480 Equivalent to manifest.matches(match).iterkeys(), but without creating
480 Equivalent to manifest.matches(match).iterkeys(), but without creating
481 an entirely new manifest.
481 an entirely new manifest.
482
482
483 It also reports nonexistent files by marking them bad with match.bad().
483 It also reports nonexistent files by marking them bad with match.bad().
484 '''
484 '''
485 if match.always():
485 if match.always():
486 for f in iter(self):
486 for f in iter(self):
487 yield f
487 yield f
488 return
488 return
489
489
490 fset = set(match.files())
490 fset = set(match.files())
491
491
492 # avoid the entire walk if we're only looking for specific files
492 # avoid the entire walk if we're only looking for specific files
493 if self._filesfastpath(match):
493 if self._filesfastpath(match):
494 for fn in sorted(fset):
494 for fn in sorted(fset):
495 yield fn
495 yield fn
496 return
496 return
497
497
498 for fn in self:
498 for fn in self:
499 if fn in fset:
499 if fn in fset:
500 # specified pattern is the exact name
500 # specified pattern is the exact name
501 fset.remove(fn)
501 fset.remove(fn)
502 if match(fn):
502 if match(fn):
503 yield fn
503 yield fn
504
504
505 # for dirstate.walk, files=['.'] means "walk the whole tree".
505 # for dirstate.walk, files=['.'] means "walk the whole tree".
506 # follow that here, too
506 # follow that here, too
507 fset.discard('.')
507 fset.discard('.')
508
508
509 for fn in sorted(fset):
509 for fn in sorted(fset):
510 if not self.hasdir(fn):
510 if not self.hasdir(fn):
511 match.bad(fn, None)
511 match.bad(fn, None)
512
512
513 def matches(self, match):
513 def matches(self, match):
514 '''generate a new manifest filtered by the match argument'''
514 '''generate a new manifest filtered by the match argument'''
515 if match.always():
515 if match.always():
516 return self.copy()
516 return self.copy()
517
517
518 if self._filesfastpath(match):
518 if self._filesfastpath(match):
519 m = manifestdict()
519 m = manifestdict()
520 lm = self._lm
520 lm = self._lm
521 for fn in match.files():
521 for fn in match.files():
522 if fn in lm:
522 if fn in lm:
523 m._lm[fn] = lm[fn]
523 m._lm[fn] = lm[fn]
524 return m
524 return m
525
525
526 m = manifestdict()
526 m = manifestdict()
527 m._lm = self._lm.filtercopy(match)
527 m._lm = self._lm.filtercopy(match)
528 return m
528 return m
529
529
530 def diff(self, m2, match=None, clean=False):
530 def diff(self, m2, match=None, clean=False):
531 '''Finds changes between the current manifest and m2.
531 '''Finds changes between the current manifest and m2.
532
532
533 Args:
533 Args:
534 m2: the manifest to which this manifest should be compared.
534 m2: the manifest to which this manifest should be compared.
535 clean: if true, include files unchanged between these manifests
535 clean: if true, include files unchanged between these manifests
536 with a None value in the returned dictionary.
536 with a None value in the returned dictionary.
537
537
538 The result is returned as a dict with filename as key and
538 The result is returned as a dict with filename as key and
539 values of the form ((n1,fl1),(n2,fl2)), where n1/n2 is the
539 values of the form ((n1,fl1),(n2,fl2)), where n1/n2 is the
540 nodeid in the current/other manifest and fl1/fl2 is the flag
540 nodeid in the current/other manifest and fl1/fl2 is the flag
541 in the current/other manifest. Where the file does not exist,
541 in the current/other manifest. Where the file does not exist,
542 the nodeid will be None and the flags will be the empty
542 the nodeid will be None and the flags will be the empty
543 string.
543 string.
544 '''
544 '''
545 if match:
545 if match:
546 m1 = self.matches(match)
546 m1 = self.matches(match)
547 m2 = m2.matches(match)
547 m2 = m2.matches(match)
548 return m1.diff(m2, clean=clean)
548 return m1.diff(m2, clean=clean)
549 return self._lm.diff(m2._lm, clean)
549 return self._lm.diff(m2._lm, clean)
550
550
551 def setflag(self, key, flag):
551 def setflag(self, key, flag):
552 self._lm[key] = self[key], flag
552 self._lm[key] = self[key], flag
553
553
554 def get(self, key, default=None):
554 def get(self, key, default=None):
555 try:
555 try:
556 return self._lm[key][0]
556 return self._lm[key][0]
557 except KeyError:
557 except KeyError:
558 return default
558 return default
559
559
560 def flags(self, key, default=''):
560 def flags(self, key, default=''):
561 try:
561 try:
562 return self._lm[key][1]
562 return self._lm[key][1]
563 except KeyError:
563 except KeyError:
564 return default
564 return default
565
565
566 def copy(self):
566 def copy(self):
567 c = manifestdict()
567 c = manifestdict()
568 c._lm = self._lm.copy()
568 c._lm = self._lm.copy()
569 return c
569 return c
570
570
571 def iteritems(self):
571 def iteritems(self):
572 return (x[:2] for x in self._lm.iterentries())
572 return (x[:2] for x in self._lm.iterentries())
573
573
574 def iterentries(self):
574 def iterentries(self):
575 return self._lm.iterentries()
575 return self._lm.iterentries()
576
576
577 def text(self, usemanifestv2=False):
577 def text(self, usemanifestv2=False):
578 if usemanifestv2:
578 if usemanifestv2:
579 return _textv2(self._lm.iterentries())
579 return _textv2(self._lm.iterentries())
580 else:
580 else:
581 # use (probably) native version for v1
581 # use (probably) native version for v1
582 return self._lm.text()
582 return self._lm.text()
583
583
584 def fastdelta(self, base, changes):
584 def fastdelta(self, base, changes):
585 """Given a base manifest text as an array.array and a list of changes
585 """Given a base manifest text as an array.array and a list of changes
586 relative to that text, compute a delta that can be used by revlog.
586 relative to that text, compute a delta that can be used by revlog.
587 """
587 """
588 delta = []
588 delta = []
589 dstart = None
589 dstart = None
590 dend = None
590 dend = None
591 dline = [""]
591 dline = [""]
592 start = 0
592 start = 0
593 # zero copy representation of base as a buffer
593 # zero copy representation of base as a buffer
594 addbuf = util.buffer(base)
594 addbuf = util.buffer(base)
595
595
596 changes = list(changes)
596 changes = list(changes)
597 if len(changes) < 1000:
597 if len(changes) < 1000:
598 # start with a readonly loop that finds the offset of
598 # start with a readonly loop that finds the offset of
599 # each line and creates the deltas
599 # each line and creates the deltas
600 for f, todelete in changes:
600 for f, todelete in changes:
601 # bs will either be the index of the item or the insert point
601 # bs will either be the index of the item or the insert point
602 start, end = _msearch(addbuf, f, start)
602 start, end = _msearch(addbuf, f, start)
603 if not todelete:
603 if not todelete:
604 h, fl = self._lm[f]
604 h, fl = self._lm[f]
605 l = "%s\0%s%s\n" % (f, revlog.hex(h), fl)
605 l = "%s\0%s%s\n" % (f, revlog.hex(h), fl)
606 else:
606 else:
607 if start == end:
607 if start == end:
608 # item we want to delete was not found, error out
608 # item we want to delete was not found, error out
609 raise AssertionError(
609 raise AssertionError(
610 _("failed to remove %s from manifest") % f)
610 _("failed to remove %s from manifest") % f)
611 l = ""
611 l = ""
612 if dstart is not None and dstart <= start and dend >= start:
612 if dstart is not None and dstart <= start and dend >= start:
613 if dend < end:
613 if dend < end:
614 dend = end
614 dend = end
615 if l:
615 if l:
616 dline.append(l)
616 dline.append(l)
617 else:
617 else:
618 if dstart is not None:
618 if dstart is not None:
619 delta.append([dstart, dend, "".join(dline)])
619 delta.append([dstart, dend, "".join(dline)])
620 dstart = start
620 dstart = start
621 dend = end
621 dend = end
622 dline = [l]
622 dline = [l]
623
623
624 if dstart is not None:
624 if dstart is not None:
625 delta.append([dstart, dend, "".join(dline)])
625 delta.append([dstart, dend, "".join(dline)])
626 # apply the delta to the base, and get a delta for addrevision
626 # apply the delta to the base, and get a delta for addrevision
627 deltatext, arraytext = _addlistdelta(base, delta)
627 deltatext, arraytext = _addlistdelta(base, delta)
628 else:
628 else:
629 # For large changes, it's much cheaper to just build the text and
629 # For large changes, it's much cheaper to just build the text and
630 # diff it.
630 # diff it.
631 arraytext = array.array('c', self.text())
631 arraytext = array.array('c', self.text())
632 deltatext = mdiff.textdiff(base, arraytext)
632 deltatext = mdiff.textdiff(base, arraytext)
633
633
634 return arraytext, deltatext
634 return arraytext, deltatext
635
635
636 def _msearch(m, s, lo=0, hi=None):
636 def _msearch(m, s, lo=0, hi=None):
637 '''return a tuple (start, end) that says where to find s within m.
637 '''return a tuple (start, end) that says where to find s within m.
638
638
639 If the string is found m[start:end] are the line containing
639 If the string is found m[start:end] are the line containing
640 that string. If start == end the string was not found and
640 that string. If start == end the string was not found and
641 they indicate the proper sorted insertion point.
641 they indicate the proper sorted insertion point.
642
642
643 m should be a buffer or a string
643 m should be a buffer or a string
644 s is a string'''
644 s is a string'''
645 def advance(i, c):
645 def advance(i, c):
646 while i < lenm and m[i] != c:
646 while i < lenm and m[i] != c:
647 i += 1
647 i += 1
648 return i
648 return i
649 if not s:
649 if not s:
650 return (lo, lo)
650 return (lo, lo)
651 lenm = len(m)
651 lenm = len(m)
652 if not hi:
652 if not hi:
653 hi = lenm
653 hi = lenm
654 while lo < hi:
654 while lo < hi:
655 mid = (lo + hi) // 2
655 mid = (lo + hi) // 2
656 start = mid
656 start = mid
657 while start > 0 and m[start - 1] != '\n':
657 while start > 0 and m[start - 1] != '\n':
658 start -= 1
658 start -= 1
659 end = advance(start, '\0')
659 end = advance(start, '\0')
660 if m[start:end] < s:
660 if m[start:end] < s:
661 # we know that after the null there are 40 bytes of sha1
661 # we know that after the null there are 40 bytes of sha1
662 # this translates to the bisect lo = mid + 1
662 # this translates to the bisect lo = mid + 1
663 lo = advance(end + 40, '\n') + 1
663 lo = advance(end + 40, '\n') + 1
664 else:
664 else:
665 # this translates to the bisect hi = mid
665 # this translates to the bisect hi = mid
666 hi = start
666 hi = start
667 end = advance(lo, '\0')
667 end = advance(lo, '\0')
668 found = m[lo:end]
668 found = m[lo:end]
669 if s == found:
669 if s == found:
670 # we know that after the null there are 40 bytes of sha1
670 # we know that after the null there are 40 bytes of sha1
671 end = advance(end + 40, '\n')
671 end = advance(end + 40, '\n')
672 return (lo, end + 1)
672 return (lo, end + 1)
673 else:
673 else:
674 return (lo, lo)
674 return (lo, lo)
675
675
676 def _checkforbidden(l):
676 def _checkforbidden(l):
677 """Check filenames for illegal characters."""
677 """Check filenames for illegal characters."""
678 for f in l:
678 for f in l:
679 if '\n' in f or '\r' in f:
679 if '\n' in f or '\r' in f:
680 raise error.RevlogError(
680 raise error.RevlogError(
681 _("'\\n' and '\\r' disallowed in filenames: %r") % f)
681 _("'\\n' and '\\r' disallowed in filenames: %r") % f)
682
682
683
683
684 # apply the changes collected during the bisect loop to our addlist
684 # apply the changes collected during the bisect loop to our addlist
685 # return a delta suitable for addrevision
685 # return a delta suitable for addrevision
686 def _addlistdelta(addlist, x):
686 def _addlistdelta(addlist, x):
687 # for large addlist arrays, building a new array is cheaper
687 # for large addlist arrays, building a new array is cheaper
688 # than repeatedly modifying the existing one
688 # than repeatedly modifying the existing one
689 currentposition = 0
689 currentposition = 0
690 newaddlist = array.array('c')
690 newaddlist = array.array('c')
691
691
692 for start, end, content in x:
692 for start, end, content in x:
693 newaddlist += addlist[currentposition:start]
693 newaddlist += addlist[currentposition:start]
694 if content:
694 if content:
695 newaddlist += array.array('c', content)
695 newaddlist += array.array('c', content)
696
696
697 currentposition = end
697 currentposition = end
698
698
699 newaddlist += addlist[currentposition:]
699 newaddlist += addlist[currentposition:]
700
700
701 deltatext = "".join(struct.pack(">lll", start, end, len(content))
701 deltatext = "".join(struct.pack(">lll", start, end, len(content))
702 + content for start, end, content in x)
702 + content for start, end, content in x)
703 return deltatext, newaddlist
703 return deltatext, newaddlist
704
704
705 def _splittopdir(f):
705 def _splittopdir(f):
706 if '/' in f:
706 if '/' in f:
707 dir, subpath = f.split('/', 1)
707 dir, subpath = f.split('/', 1)
708 return dir + '/', subpath
708 return dir + '/', subpath
709 else:
709 else:
710 return '', f
710 return '', f
711
711
712 _noop = lambda s: None
712 _noop = lambda s: None
713
713
714 class treemanifest(object):
714 class treemanifest(object):
715 def __init__(self, dir='', text=''):
715 def __init__(self, dir='', text=''):
716 self._dir = dir
716 self._dir = dir
717 self._node = revlog.nullid
717 self._node = revlog.nullid
718 self._loadfunc = _noop
718 self._loadfunc = _noop
719 self._copyfunc = _noop
719 self._copyfunc = _noop
720 self._dirty = False
720 self._dirty = False
721 self._dirs = {}
721 self._dirs = {}
722 # Using _lazymanifest here is a little slower than plain old dicts
722 # Using _lazymanifest here is a little slower than plain old dicts
723 self._files = {}
723 self._files = {}
724 self._flags = {}
724 self._flags = {}
725 if text:
725 if text:
726 def readsubtree(subdir, subm):
726 def readsubtree(subdir, subm):
727 raise AssertionError('treemanifest constructor only accepts '
727 raise AssertionError('treemanifest constructor only accepts '
728 'flat manifests')
728 'flat manifests')
729 self.parse(text, readsubtree)
729 self.parse(text, readsubtree)
730 self._dirty = True # Mark flat manifest dirty after parsing
730 self._dirty = True # Mark flat manifest dirty after parsing
731
731
732 def _subpath(self, path):
732 def _subpath(self, path):
733 return self._dir + path
733 return self._dir + path
734
734
735 def __len__(self):
735 def __len__(self):
736 self._load()
736 self._load()
737 size = len(self._files)
737 size = len(self._files)
738 for m in self._dirs.values():
738 for m in self._dirs.values():
739 size += m.__len__()
739 size += m.__len__()
740 return size
740 return size
741
741
742 def _isempty(self):
742 def _isempty(self):
743 self._load() # for consistency; already loaded by all callers
743 self._load() # for consistency; already loaded by all callers
744 return (not self._files and (not self._dirs or
744 return (not self._files and (not self._dirs or
745 all(m._isempty() for m in self._dirs.values())))
745 all(m._isempty() for m in self._dirs.values())))
746
746
747 def __repr__(self):
747 def __repr__(self):
748 return ('<treemanifest dir=%s, node=%s, loaded=%s, dirty=%s at 0x%x>' %
748 return ('<treemanifest dir=%s, node=%s, loaded=%s, dirty=%s at 0x%x>' %
749 (self._dir, revlog.hex(self._node),
749 (self._dir, revlog.hex(self._node),
750 bool(self._loadfunc is _noop),
750 bool(self._loadfunc is _noop),
751 self._dirty, id(self)))
751 self._dirty, id(self)))
752
752
753 def dir(self):
753 def dir(self):
754 '''The directory that this tree manifest represents, including a
754 '''The directory that this tree manifest represents, including a
755 trailing '/'. Empty string for the repo root directory.'''
755 trailing '/'. Empty string for the repo root directory.'''
756 return self._dir
756 return self._dir
757
757
758 def node(self):
758 def node(self):
759 '''This node of this instance. nullid for unsaved instances. Should
759 '''This node of this instance. nullid for unsaved instances. Should
760 be updated when the instance is read or written from a revlog.
760 be updated when the instance is read or written from a revlog.
761 '''
761 '''
762 assert not self._dirty
762 assert not self._dirty
763 return self._node
763 return self._node
764
764
765 def setnode(self, node):
765 def setnode(self, node):
766 self._node = node
766 self._node = node
767 self._dirty = False
767 self._dirty = False
768
768
769 def iterentries(self):
769 def iterentries(self):
770 self._load()
770 self._load()
771 for p, n in sorted(self._dirs.items() + self._files.items()):
771 for p, n in sorted(self._dirs.items() + self._files.items()):
772 if p in self._files:
772 if p in self._files:
773 yield self._subpath(p), n, self._flags.get(p, '')
773 yield self._subpath(p), n, self._flags.get(p, '')
774 else:
774 else:
775 for x in n.iterentries():
775 for x in n.iterentries():
776 yield x
776 yield x
777
777
778 def iteritems(self):
778 def iteritems(self):
779 self._load()
779 self._load()
780 for p, n in sorted(self._dirs.items() + self._files.items()):
780 for p, n in sorted(self._dirs.items() + self._files.items()):
781 if p in self._files:
781 if p in self._files:
782 yield self._subpath(p), n
782 yield self._subpath(p), n
783 else:
783 else:
784 for f, sn in n.iteritems():
784 for f, sn in n.iteritems():
785 yield f, sn
785 yield f, sn
786
786
787 def iterkeys(self):
787 def iterkeys(self):
788 self._load()
788 self._load()
789 for p in sorted(self._dirs.keys() + self._files.keys()):
789 for p in sorted(self._dirs.keys() + self._files.keys()):
790 if p in self._files:
790 if p in self._files:
791 yield self._subpath(p)
791 yield self._subpath(p)
792 else:
792 else:
793 for f in self._dirs[p].iterkeys():
793 for f in self._dirs[p].iterkeys():
794 yield f
794 yield f
795
795
796 def keys(self):
796 def keys(self):
797 return list(self.iterkeys())
797 return list(self.iterkeys())
798
798
799 def __iter__(self):
799 def __iter__(self):
800 return self.iterkeys()
800 return self.iterkeys()
801
801
802 def __contains__(self, f):
802 def __contains__(self, f):
803 if f is None:
803 if f is None:
804 return False
804 return False
805 self._load()
805 self._load()
806 dir, subpath = _splittopdir(f)
806 dir, subpath = _splittopdir(f)
807 if dir:
807 if dir:
808 if dir not in self._dirs:
808 if dir not in self._dirs:
809 return False
809 return False
810 return self._dirs[dir].__contains__(subpath)
810 return self._dirs[dir].__contains__(subpath)
811 else:
811 else:
812 return f in self._files
812 return f in self._files
813
813
814 def get(self, f, default=None):
814 def get(self, f, default=None):
815 self._load()
815 self._load()
816 dir, subpath = _splittopdir(f)
816 dir, subpath = _splittopdir(f)
817 if dir:
817 if dir:
818 if dir not in self._dirs:
818 if dir not in self._dirs:
819 return default
819 return default
820 return self._dirs[dir].get(subpath, default)
820 return self._dirs[dir].get(subpath, default)
821 else:
821 else:
822 return self._files.get(f, default)
822 return self._files.get(f, default)
823
823
824 def __getitem__(self, f):
824 def __getitem__(self, f):
825 self._load()
825 self._load()
826 dir, subpath = _splittopdir(f)
826 dir, subpath = _splittopdir(f)
827 if dir:
827 if dir:
828 return self._dirs[dir].__getitem__(subpath)
828 return self._dirs[dir].__getitem__(subpath)
829 else:
829 else:
830 return self._files[f]
830 return self._files[f]
831
831
832 def flags(self, f):
832 def flags(self, f):
833 self._load()
833 self._load()
834 dir, subpath = _splittopdir(f)
834 dir, subpath = _splittopdir(f)
835 if dir:
835 if dir:
836 if dir not in self._dirs:
836 if dir not in self._dirs:
837 return ''
837 return ''
838 return self._dirs[dir].flags(subpath)
838 return self._dirs[dir].flags(subpath)
839 else:
839 else:
840 if f in self._dirs:
840 if f in self._dirs:
841 return ''
841 return ''
842 return self._flags.get(f, '')
842 return self._flags.get(f, '')
843
843
844 def find(self, f):
844 def find(self, f):
845 self._load()
845 self._load()
846 dir, subpath = _splittopdir(f)
846 dir, subpath = _splittopdir(f)
847 if dir:
847 if dir:
848 return self._dirs[dir].find(subpath)
848 return self._dirs[dir].find(subpath)
849 else:
849 else:
850 return self._files[f], self._flags.get(f, '')
850 return self._files[f], self._flags.get(f, '')
851
851
852 def __delitem__(self, f):
852 def __delitem__(self, f):
853 self._load()
853 self._load()
854 dir, subpath = _splittopdir(f)
854 dir, subpath = _splittopdir(f)
855 if dir:
855 if dir:
856 self._dirs[dir].__delitem__(subpath)
856 self._dirs[dir].__delitem__(subpath)
857 # If the directory is now empty, remove it
857 # If the directory is now empty, remove it
858 if self._dirs[dir]._isempty():
858 if self._dirs[dir]._isempty():
859 del self._dirs[dir]
859 del self._dirs[dir]
860 else:
860 else:
861 del self._files[f]
861 del self._files[f]
862 if f in self._flags:
862 if f in self._flags:
863 del self._flags[f]
863 del self._flags[f]
864 self._dirty = True
864 self._dirty = True
865
865
866 def __setitem__(self, f, n):
866 def __setitem__(self, f, n):
867 assert n is not None
867 assert n is not None
868 self._load()
868 self._load()
869 dir, subpath = _splittopdir(f)
869 dir, subpath = _splittopdir(f)
870 if dir:
870 if dir:
871 if dir not in self._dirs:
871 if dir not in self._dirs:
872 self._dirs[dir] = treemanifest(self._subpath(dir))
872 self._dirs[dir] = treemanifest(self._subpath(dir))
873 self._dirs[dir].__setitem__(subpath, n)
873 self._dirs[dir].__setitem__(subpath, n)
874 else:
874 else:
875 self._files[f] = n[:21] # to match manifestdict's behavior
875 self._files[f] = n[:21] # to match manifestdict's behavior
876 self._dirty = True
876 self._dirty = True
877
877
878 def _load(self):
878 def _load(self):
879 if self._loadfunc is not _noop:
879 if self._loadfunc is not _noop:
880 lf, self._loadfunc = self._loadfunc, _noop
880 lf, self._loadfunc = self._loadfunc, _noop
881 lf(self)
881 lf(self)
882 elif self._copyfunc is not _noop:
882 elif self._copyfunc is not _noop:
883 cf, self._copyfunc = self._copyfunc, _noop
883 cf, self._copyfunc = self._copyfunc, _noop
884 cf(self)
884 cf(self)
885
885
886 def setflag(self, f, flags):
886 def setflag(self, f, flags):
887 """Set the flags (symlink, executable) for path f."""
887 """Set the flags (symlink, executable) for path f."""
888 self._load()
888 self._load()
889 dir, subpath = _splittopdir(f)
889 dir, subpath = _splittopdir(f)
890 if dir:
890 if dir:
891 if dir not in self._dirs:
891 if dir not in self._dirs:
892 self._dirs[dir] = treemanifest(self._subpath(dir))
892 self._dirs[dir] = treemanifest(self._subpath(dir))
893 self._dirs[dir].setflag(subpath, flags)
893 self._dirs[dir].setflag(subpath, flags)
894 else:
894 else:
895 self._flags[f] = flags
895 self._flags[f] = flags
896 self._dirty = True
896 self._dirty = True
897
897
898 def copy(self):
898 def copy(self):
899 copy = treemanifest(self._dir)
899 copy = treemanifest(self._dir)
900 copy._node = self._node
900 copy._node = self._node
901 copy._dirty = self._dirty
901 copy._dirty = self._dirty
902 if self._copyfunc is _noop:
902 if self._copyfunc is _noop:
903 def _copyfunc(s):
903 def _copyfunc(s):
904 self._load()
904 self._load()
905 for d in self._dirs:
905 for d in self._dirs:
906 s._dirs[d] = self._dirs[d].copy()
906 s._dirs[d] = self._dirs[d].copy()
907 s._files = dict.copy(self._files)
907 s._files = dict.copy(self._files)
908 s._flags = dict.copy(self._flags)
908 s._flags = dict.copy(self._flags)
909 if self._loadfunc is _noop:
909 if self._loadfunc is _noop:
910 _copyfunc(copy)
910 _copyfunc(copy)
911 else:
911 else:
912 copy._copyfunc = _copyfunc
912 copy._copyfunc = _copyfunc
913 else:
913 else:
914 copy._copyfunc = self._copyfunc
914 copy._copyfunc = self._copyfunc
915 return copy
915 return copy
916
916
917 def filesnotin(self, m2, match=None):
917 def filesnotin(self, m2, match=None):
918 '''Set of files in this manifest that are not in the other'''
918 '''Set of files in this manifest that are not in the other'''
919 if match:
919 if match:
920 m1 = self.matches(match)
920 m1 = self.matches(match)
921 m2 = m2.matches(match)
921 m2 = m2.matches(match)
922 return m1.filesnotin(m2)
922 return m1.filesnotin(m2)
923
923
924 files = set()
924 files = set()
925 def _filesnotin(t1, t2):
925 def _filesnotin(t1, t2):
926 if t1._node == t2._node and not t1._dirty and not t2._dirty:
926 if t1._node == t2._node and not t1._dirty and not t2._dirty:
927 return
927 return
928 t1._load()
928 t1._load()
929 t2._load()
929 t2._load()
930 for d, m1 in t1._dirs.iteritems():
930 for d, m1 in t1._dirs.iteritems():
931 if d in t2._dirs:
931 if d in t2._dirs:
932 m2 = t2._dirs[d]
932 m2 = t2._dirs[d]
933 _filesnotin(m1, m2)
933 _filesnotin(m1, m2)
934 else:
934 else:
935 files.update(m1.iterkeys())
935 files.update(m1.iterkeys())
936
936
937 for fn in t1._files.iterkeys():
937 for fn in t1._files.iterkeys():
938 if fn not in t2._files:
938 if fn not in t2._files:
939 files.add(t1._subpath(fn))
939 files.add(t1._subpath(fn))
940
940
941 _filesnotin(self, m2)
941 _filesnotin(self, m2)
942 return files
942 return files
943
943
944 @propertycache
944 @propertycache
945 def _alldirs(self):
945 def _alldirs(self):
946 return util.dirs(self)
946 return util.dirs(self)
947
947
948 def dirs(self):
948 def dirs(self):
949 return self._alldirs
949 return self._alldirs
950
950
951 def hasdir(self, dir):
951 def hasdir(self, dir):
952 self._load()
952 self._load()
953 topdir, subdir = _splittopdir(dir)
953 topdir, subdir = _splittopdir(dir)
954 if topdir:
954 if topdir:
955 if topdir in self._dirs:
955 if topdir in self._dirs:
956 return self._dirs[topdir].hasdir(subdir)
956 return self._dirs[topdir].hasdir(subdir)
957 return False
957 return False
958 return (dir + '/') in self._dirs
958 return (dir + '/') in self._dirs
959
959
960 def walk(self, match):
960 def walk(self, match):
961 '''Generates matching file names.
961 '''Generates matching file names.
962
962
963 Equivalent to manifest.matches(match).iterkeys(), but without creating
963 Equivalent to manifest.matches(match).iterkeys(), but without creating
964 an entirely new manifest.
964 an entirely new manifest.
965
965
966 It also reports nonexistent files by marking them bad with match.bad().
966 It also reports nonexistent files by marking them bad with match.bad().
967 '''
967 '''
968 if match.always():
968 if match.always():
969 for f in iter(self):
969 for f in iter(self):
970 yield f
970 yield f
971 return
971 return
972
972
973 fset = set(match.files())
973 fset = set(match.files())
974
974
975 for fn in self._walk(match):
975 for fn in self._walk(match):
976 if fn in fset:
976 if fn in fset:
977 # specified pattern is the exact name
977 # specified pattern is the exact name
978 fset.remove(fn)
978 fset.remove(fn)
979 yield fn
979 yield fn
980
980
981 # for dirstate.walk, files=['.'] means "walk the whole tree".
981 # for dirstate.walk, files=['.'] means "walk the whole tree".
982 # follow that here, too
982 # follow that here, too
983 fset.discard('.')
983 fset.discard('.')
984
984
985 for fn in sorted(fset):
985 for fn in sorted(fset):
986 if not self.hasdir(fn):
986 if not self.hasdir(fn):
987 match.bad(fn, None)
987 match.bad(fn, None)
988
988
989 def _walk(self, match):
989 def _walk(self, match):
990 '''Recursively generates matching file names for walk().'''
990 '''Recursively generates matching file names for walk().'''
991 if not match.visitdir(self._dir[:-1] or '.'):
991 if not match.visitdir(self._dir[:-1] or '.'):
992 return
992 return
993
993
994 # yield this dir's files and walk its submanifests
994 # yield this dir's files and walk its submanifests
995 self._load()
995 self._load()
996 for p in sorted(self._dirs.keys() + self._files.keys()):
996 for p in sorted(self._dirs.keys() + self._files.keys()):
997 if p in self._files:
997 if p in self._files:
998 fullp = self._subpath(p)
998 fullp = self._subpath(p)
999 if match(fullp):
999 if match(fullp):
1000 yield fullp
1000 yield fullp
1001 else:
1001 else:
1002 for f in self._dirs[p]._walk(match):
1002 for f in self._dirs[p]._walk(match):
1003 yield f
1003 yield f
1004
1004
1005 def matches(self, match):
1005 def matches(self, match):
1006 '''generate a new manifest filtered by the match argument'''
1006 '''generate a new manifest filtered by the match argument'''
1007 if match.always():
1007 if match.always():
1008 return self.copy()
1008 return self.copy()
1009
1009
1010 return self._matches(match)
1010 return self._matches(match)
1011
1011
1012 def _matches(self, match):
1012 def _matches(self, match):
1013 '''recursively generate a new manifest filtered by the match argument.
1013 '''recursively generate a new manifest filtered by the match argument.
1014 '''
1014 '''
1015
1015
1016 visit = match.visitdir(self._dir[:-1] or '.')
1016 visit = match.visitdir(self._dir[:-1] or '.')
1017 if visit == 'all':
1017 if visit == 'all':
1018 return self.copy()
1018 return self.copy()
1019 ret = treemanifest(self._dir)
1019 ret = treemanifest(self._dir)
1020 if not visit:
1020 if not visit:
1021 return ret
1021 return ret
1022
1022
1023 self._load()
1023 self._load()
1024 for fn in self._files:
1024 for fn in self._files:
1025 fullp = self._subpath(fn)
1025 fullp = self._subpath(fn)
1026 if not match(fullp):
1026 if not match(fullp):
1027 continue
1027 continue
1028 ret._files[fn] = self._files[fn]
1028 ret._files[fn] = self._files[fn]
1029 if fn in self._flags:
1029 if fn in self._flags:
1030 ret._flags[fn] = self._flags[fn]
1030 ret._flags[fn] = self._flags[fn]
1031
1031
1032 for dir, subm in self._dirs.iteritems():
1032 for dir, subm in self._dirs.iteritems():
1033 m = subm._matches(match)
1033 m = subm._matches(match)
1034 if not m._isempty():
1034 if not m._isempty():
1035 ret._dirs[dir] = m
1035 ret._dirs[dir] = m
1036
1036
1037 if not ret._isempty():
1037 if not ret._isempty():
1038 ret._dirty = True
1038 ret._dirty = True
1039 return ret
1039 return ret
1040
1040
1041 def diff(self, m2, match=None, clean=False):
1041 def diff(self, m2, match=None, clean=False):
1042 '''Finds changes between the current manifest and m2.
1042 '''Finds changes between the current manifest and m2.
1043
1043
1044 Args:
1044 Args:
1045 m2: the manifest to which this manifest should be compared.
1045 m2: the manifest to which this manifest should be compared.
1046 clean: if true, include files unchanged between these manifests
1046 clean: if true, include files unchanged between these manifests
1047 with a None value in the returned dictionary.
1047 with a None value in the returned dictionary.
1048
1048
1049 The result is returned as a dict with filename as key and
1049 The result is returned as a dict with filename as key and
1050 values of the form ((n1,fl1),(n2,fl2)), where n1/n2 is the
1050 values of the form ((n1,fl1),(n2,fl2)), where n1/n2 is the
1051 nodeid in the current/other manifest and fl1/fl2 is the flag
1051 nodeid in the current/other manifest and fl1/fl2 is the flag
1052 in the current/other manifest. Where the file does not exist,
1052 in the current/other manifest. Where the file does not exist,
1053 the nodeid will be None and the flags will be the empty
1053 the nodeid will be None and the flags will be the empty
1054 string.
1054 string.
1055 '''
1055 '''
1056 if match:
1056 if match:
1057 m1 = self.matches(match)
1057 m1 = self.matches(match)
1058 m2 = m2.matches(match)
1058 m2 = m2.matches(match)
1059 return m1.diff(m2, clean=clean)
1059 return m1.diff(m2, clean=clean)
1060 result = {}
1060 result = {}
1061 emptytree = treemanifest()
1061 emptytree = treemanifest()
1062 def _diff(t1, t2):
1062 def _diff(t1, t2):
1063 if t1._node == t2._node and not t1._dirty and not t2._dirty:
1063 if t1._node == t2._node and not t1._dirty and not t2._dirty:
1064 return
1064 return
1065 t1._load()
1065 t1._load()
1066 t2._load()
1066 t2._load()
1067 for d, m1 in t1._dirs.iteritems():
1067 for d, m1 in t1._dirs.iteritems():
1068 m2 = t2._dirs.get(d, emptytree)
1068 m2 = t2._dirs.get(d, emptytree)
1069 _diff(m1, m2)
1069 _diff(m1, m2)
1070
1070
1071 for d, m2 in t2._dirs.iteritems():
1071 for d, m2 in t2._dirs.iteritems():
1072 if d not in t1._dirs:
1072 if d not in t1._dirs:
1073 _diff(emptytree, m2)
1073 _diff(emptytree, m2)
1074
1074
1075 for fn, n1 in t1._files.iteritems():
1075 for fn, n1 in t1._files.iteritems():
1076 fl1 = t1._flags.get(fn, '')
1076 fl1 = t1._flags.get(fn, '')
1077 n2 = t2._files.get(fn, None)
1077 n2 = t2._files.get(fn, None)
1078 fl2 = t2._flags.get(fn, '')
1078 fl2 = t2._flags.get(fn, '')
1079 if n1 != n2 or fl1 != fl2:
1079 if n1 != n2 or fl1 != fl2:
1080 result[t1._subpath(fn)] = ((n1, fl1), (n2, fl2))
1080 result[t1._subpath(fn)] = ((n1, fl1), (n2, fl2))
1081 elif clean:
1081 elif clean:
1082 result[t1._subpath(fn)] = None
1082 result[t1._subpath(fn)] = None
1083
1083
1084 for fn, n2 in t2._files.iteritems():
1084 for fn, n2 in t2._files.iteritems():
1085 if fn not in t1._files:
1085 if fn not in t1._files:
1086 fl2 = t2._flags.get(fn, '')
1086 fl2 = t2._flags.get(fn, '')
1087 result[t2._subpath(fn)] = ((None, ''), (n2, fl2))
1087 result[t2._subpath(fn)] = ((None, ''), (n2, fl2))
1088
1088
1089 _diff(self, m2)
1089 _diff(self, m2)
1090 return result
1090 return result
1091
1091
1092 def unmodifiedsince(self, m2):
1092 def unmodifiedsince(self, m2):
1093 return not self._dirty and not m2._dirty and self._node == m2._node
1093 return not self._dirty and not m2._dirty and self._node == m2._node
1094
1094
1095 def parse(self, text, readsubtree):
1095 def parse(self, text, readsubtree):
1096 for f, n, fl in _parse(text):
1096 for f, n, fl in _parse(text):
1097 if fl == 't':
1097 if fl == 't':
1098 f = f + '/'
1098 f = f + '/'
1099 self._dirs[f] = readsubtree(self._subpath(f), n)
1099 self._dirs[f] = readsubtree(self._subpath(f), n)
1100 elif '/' in f:
1100 elif '/' in f:
1101 # This is a flat manifest, so use __setitem__ and setflag rather
1101 # This is a flat manifest, so use __setitem__ and setflag rather
1102 # than assigning directly to _files and _flags, so we can
1102 # than assigning directly to _files and _flags, so we can
1103 # assign a path in a subdirectory, and to mark dirty (compared
1103 # assign a path in a subdirectory, and to mark dirty (compared
1104 # to nullid).
1104 # to nullid).
1105 self[f] = n
1105 self[f] = n
1106 if fl:
1106 if fl:
1107 self.setflag(f, fl)
1107 self.setflag(f, fl)
1108 else:
1108 else:
1109 # Assigning to _files and _flags avoids marking as dirty,
1109 # Assigning to _files and _flags avoids marking as dirty,
1110 # and should be a little faster.
1110 # and should be a little faster.
1111 self._files[f] = n
1111 self._files[f] = n
1112 if fl:
1112 if fl:
1113 self._flags[f] = fl
1113 self._flags[f] = fl
1114
1114
1115 def text(self, usemanifestv2=False):
1115 def text(self, usemanifestv2=False):
1116 """Get the full data of this manifest as a bytestring."""
1116 """Get the full data of this manifest as a bytestring."""
1117 self._load()
1117 self._load()
1118 return _text(self.iterentries(), usemanifestv2)
1118 return _text(self.iterentries(), usemanifestv2)
1119
1119
1120 def dirtext(self, usemanifestv2=False):
1120 def dirtext(self, usemanifestv2=False):
1121 """Get the full data of this directory as a bytestring. Make sure that
1121 """Get the full data of this directory as a bytestring. Make sure that
1122 any submanifests have been written first, so their nodeids are correct.
1122 any submanifests have been written first, so their nodeids are correct.
1123 """
1123 """
1124 self._load()
1124 self._load()
1125 flags = self.flags
1125 flags = self.flags
1126 dirs = [(d[:-1], self._dirs[d]._node, 't') for d in self._dirs]
1126 dirs = [(d[:-1], self._dirs[d]._node, 't') for d in self._dirs]
1127 files = [(f, self._files[f], flags(f)) for f in self._files]
1127 files = [(f, self._files[f], flags(f)) for f in self._files]
1128 return _text(sorted(dirs + files), usemanifestv2)
1128 return _text(sorted(dirs + files), usemanifestv2)
1129
1129
1130 def read(self, gettext, readsubtree):
1130 def read(self, gettext, readsubtree):
1131 def _load_for_read(s):
1131 def _load_for_read(s):
1132 s.parse(gettext(), readsubtree)
1132 s.parse(gettext(), readsubtree)
1133 s._dirty = False
1133 s._dirty = False
1134 self._loadfunc = _load_for_read
1134 self._loadfunc = _load_for_read
1135
1135
1136 def writesubtrees(self, m1, m2, writesubtree):
1136 def writesubtrees(self, m1, m2, writesubtree):
1137 self._load() # for consistency; should never have any effect here
1137 self._load() # for consistency; should never have any effect here
1138 m1._load()
1138 m1._load()
1139 m2._load()
1139 m2._load()
1140 emptytree = treemanifest()
1140 emptytree = treemanifest()
1141 for d, subm in self._dirs.iteritems():
1141 for d, subm in self._dirs.iteritems():
1142 subp1 = m1._dirs.get(d, emptytree)._node
1142 subp1 = m1._dirs.get(d, emptytree)._node
1143 subp2 = m2._dirs.get(d, emptytree)._node
1143 subp2 = m2._dirs.get(d, emptytree)._node
1144 if subp1 == revlog.nullid:
1144 if subp1 == revlog.nullid:
1145 subp1, subp2 = subp2, subp1
1145 subp1, subp2 = subp2, subp1
1146 writesubtree(subm, subp1, subp2)
1146 writesubtree(subm, subp1, subp2)
1147
1147
1148 class manifestrevlog(revlog.revlog):
1148 class manifestrevlog(revlog.revlog):
1149 '''A revlog that stores manifest texts. This is responsible for caching the
1149 '''A revlog that stores manifest texts. This is responsible for caching the
1150 full-text manifest contents.
1150 full-text manifest contents.
1151 '''
1151 '''
1152 def __init__(self, opener, dir='', dirlogcache=None, indexfile=None):
1152 def __init__(self, opener, dir='', dirlogcache=None, indexfile=None):
1153 """Constructs a new manifest revlog
1153 """Constructs a new manifest revlog
1154
1154
1155 `indexfile` - used by extensions to have two manifests at once, like
1155 `indexfile` - used by extensions to have two manifests at once, like
1156 when transitioning between flatmanifeset and treemanifests.
1156 when transitioning between flatmanifeset and treemanifests.
1157 """
1157 """
1158 # During normal operations, we expect to deal with not more than four
1158 # During normal operations, we expect to deal with not more than four
1159 # revs at a time (such as during commit --amend). When rebasing large
1159 # revs at a time (such as during commit --amend). When rebasing large
1160 # stacks of commits, the number can go up, hence the config knob below.
1160 # stacks of commits, the number can go up, hence the config knob below.
1161 cachesize = 4
1161 cachesize = 4
1162 usetreemanifest = False
1162 usetreemanifest = False
1163 usemanifestv2 = False
1163 usemanifestv2 = False
1164 opts = getattr(opener, 'options', None)
1164 opts = getattr(opener, 'options', None)
1165 if opts is not None:
1165 if opts is not None:
1166 cachesize = opts.get('manifestcachesize', cachesize)
1166 cachesize = opts.get('manifestcachesize', cachesize)
1167 usetreemanifest = opts.get('treemanifest', usetreemanifest)
1167 usetreemanifest = opts.get('treemanifest', usetreemanifest)
1168 usemanifestv2 = opts.get('manifestv2', usemanifestv2)
1168 usemanifestv2 = opts.get('manifestv2', usemanifestv2)
1169
1169
1170 self._treeondisk = usetreemanifest
1170 self._treeondisk = usetreemanifest
1171 self._usemanifestv2 = usemanifestv2
1171 self._usemanifestv2 = usemanifestv2
1172
1172
1173 self._fulltextcache = util.lrucachedict(cachesize)
1173 self._fulltextcache = util.lrucachedict(cachesize)
1174
1174
1175 if dir:
1175 if dir:
1176 assert self._treeondisk, 'opts is %r' % opts
1176 assert self._treeondisk, 'opts is %r' % opts
1177 if not dir.endswith('/'):
1177 if not dir.endswith('/'):
1178 dir = dir + '/'
1178 dir = dir + '/'
1179
1179
1180 if indexfile is None:
1180 if indexfile is None:
1181 indexfile = '00manifest.i'
1181 indexfile = '00manifest.i'
1182 if dir:
1182 if dir:
1183 indexfile = "meta/" + dir + indexfile
1183 indexfile = "meta/" + dir + indexfile
1184
1184
1185 self._dir = dir
1185 self._dir = dir
1186 # The dirlogcache is kept on the root manifest log
1186 # The dirlogcache is kept on the root manifest log
1187 if dir:
1187 if dir:
1188 self._dirlogcache = dirlogcache
1188 self._dirlogcache = dirlogcache
1189 else:
1189 else:
1190 self._dirlogcache = {'': self}
1190 self._dirlogcache = {'': self}
1191
1191
1192 super(manifestrevlog, self).__init__(opener, indexfile,
1192 super(manifestrevlog, self).__init__(opener, indexfile,
1193 checkambig=bool(dir))
1193 checkambig=bool(dir))
1194
1194
1195 @property
1195 @property
1196 def fulltextcache(self):
1196 def fulltextcache(self):
1197 return self._fulltextcache
1197 return self._fulltextcache
1198
1198
1199 def clearcaches(self):
1199 def clearcaches(self):
1200 super(manifestrevlog, self).clearcaches()
1200 super(manifestrevlog, self).clearcaches()
1201 self._fulltextcache.clear()
1201 self._fulltextcache.clear()
1202 self._dirlogcache = {'': self}
1202 self._dirlogcache = {'': self}
1203
1203
1204 def dirlog(self, dir):
1204 def dirlog(self, dir):
1205 if dir:
1205 if dir:
1206 assert self._treeondisk
1206 assert self._treeondisk
1207 if dir not in self._dirlogcache:
1207 if dir not in self._dirlogcache:
1208 self._dirlogcache[dir] = manifestrevlog(self.opener, dir,
1208 self._dirlogcache[dir] = manifestrevlog(self.opener, dir,
1209 self._dirlogcache)
1209 self._dirlogcache)
1210 return self._dirlogcache[dir]
1210 return self._dirlogcache[dir]
1211
1211
1212 def add(self, m, transaction, link, p1, p2, added, removed, readtree=None):
1212 def add(self, m, transaction, link, p1, p2, added, removed, readtree=None):
1213 if (p1 in self.fulltextcache and util.safehasattr(m, 'fastdelta')
1213 if (p1 in self.fulltextcache and util.safehasattr(m, 'fastdelta')
1214 and not self._usemanifestv2):
1214 and not self._usemanifestv2):
1215 # If our first parent is in the manifest cache, we can
1215 # If our first parent is in the manifest cache, we can
1216 # compute a delta here using properties we know about the
1216 # compute a delta here using properties we know about the
1217 # manifest up-front, which may save time later for the
1217 # manifest up-front, which may save time later for the
1218 # revlog layer.
1218 # revlog layer.
1219
1219
1220 _checkforbidden(added)
1220 _checkforbidden(added)
1221 # combine the changed lists into one sorted iterator
1221 # combine the changed lists into one sorted iterator
1222 work = heapq.merge([(x, False) for x in added],
1222 work = heapq.merge([(x, False) for x in added],
1223 [(x, True) for x in removed])
1223 [(x, True) for x in removed])
1224
1224
1225 arraytext, deltatext = m.fastdelta(self.fulltextcache[p1], work)
1225 arraytext, deltatext = m.fastdelta(self.fulltextcache[p1], work)
1226 cachedelta = self.rev(p1), deltatext
1226 cachedelta = self.rev(p1), deltatext
1227 text = util.buffer(arraytext)
1227 text = util.buffer(arraytext)
1228 n = self.addrevision(text, transaction, link, p1, p2, cachedelta)
1228 n = self.addrevision(text, transaction, link, p1, p2, cachedelta)
1229 else:
1229 else:
1230 # The first parent manifest isn't already loaded, so we'll
1230 # The first parent manifest isn't already loaded, so we'll
1231 # just encode a fulltext of the manifest and pass that
1231 # just encode a fulltext of the manifest and pass that
1232 # through to the revlog layer, and let it handle the delta
1232 # through to the revlog layer, and let it handle the delta
1233 # process.
1233 # process.
1234 if self._treeondisk:
1234 if self._treeondisk:
1235 assert readtree, "readtree must be set for treemanifest writes"
1235 assert readtree, "readtree must be set for treemanifest writes"
1236 m1 = readtree(self._dir, p1)
1236 m1 = readtree(self._dir, p1)
1237 m2 = readtree(self._dir, p2)
1237 m2 = readtree(self._dir, p2)
1238 n = self._addtree(m, transaction, link, m1, m2, readtree)
1238 n = self._addtree(m, transaction, link, m1, m2, readtree)
1239 arraytext = None
1239 arraytext = None
1240 else:
1240 else:
1241 text = m.text(self._usemanifestv2)
1241 text = m.text(self._usemanifestv2)
1242 n = self.addrevision(text, transaction, link, p1, p2)
1242 n = self.addrevision(text, transaction, link, p1, p2)
1243 arraytext = array.array('c', text)
1243 arraytext = array.array('c', text)
1244
1244
1245 if arraytext is not None:
1245 if arraytext is not None:
1246 self.fulltextcache[n] = arraytext
1246 self.fulltextcache[n] = arraytext
1247
1247
1248 return n
1248 return n
1249
1249
1250 def _addtree(self, m, transaction, link, m1, m2, readtree):
1250 def _addtree(self, m, transaction, link, m1, m2, readtree):
1251 # If the manifest is unchanged compared to one parent,
1251 # If the manifest is unchanged compared to one parent,
1252 # don't write a new revision
1252 # don't write a new revision
1253 if m.unmodifiedsince(m1) or m.unmodifiedsince(m2):
1253 if self._dir != '' and (m.unmodifiedsince(m1) or m.unmodifiedsince(m2)):
1254 return m.node()
1254 return m.node()
1255 def writesubtree(subm, subp1, subp2):
1255 def writesubtree(subm, subp1, subp2):
1256 sublog = self.dirlog(subm.dir())
1256 sublog = self.dirlog(subm.dir())
1257 sublog.add(subm, transaction, link, subp1, subp2, None, None,
1257 sublog.add(subm, transaction, link, subp1, subp2, None, None,
1258 readtree=readtree)
1258 readtree=readtree)
1259 m.writesubtrees(m1, m2, writesubtree)
1259 m.writesubtrees(m1, m2, writesubtree)
1260 text = m.dirtext(self._usemanifestv2)
1260 text = m.dirtext(self._usemanifestv2)
1261 n = None
1262 if self._dir != '':
1261 # Double-check whether contents are unchanged to one parent
1263 # Double-check whether contents are unchanged to one parent
1262 if text == m1.dirtext(self._usemanifestv2):
1264 if text == m1.dirtext(self._usemanifestv2):
1263 n = m1.node()
1265 n = m1.node()
1264 elif text == m2.dirtext(self._usemanifestv2):
1266 elif text == m2.dirtext(self._usemanifestv2):
1265 n = m2.node()
1267 n = m2.node()
1266 else:
1268
1269 if not n:
1267 n = self.addrevision(text, transaction, link, m1.node(), m2.node())
1270 n = self.addrevision(text, transaction, link, m1.node(), m2.node())
1271
1268 # Save nodeid so parent manifest can calculate its nodeid
1272 # Save nodeid so parent manifest can calculate its nodeid
1269 m.setnode(n)
1273 m.setnode(n)
1270 return n
1274 return n
1271
1275
1272 class manifestlog(object):
1276 class manifestlog(object):
1273 """A collection class representing the collection of manifest snapshots
1277 """A collection class representing the collection of manifest snapshots
1274 referenced by commits in the repository.
1278 referenced by commits in the repository.
1275
1279
1276 In this situation, 'manifest' refers to the abstract concept of a snapshot
1280 In this situation, 'manifest' refers to the abstract concept of a snapshot
1277 of the list of files in the given commit. Consumers of the output of this
1281 of the list of files in the given commit. Consumers of the output of this
1278 class do not care about the implementation details of the actual manifests
1282 class do not care about the implementation details of the actual manifests
1279 they receive (i.e. tree or flat or lazily loaded, etc)."""
1283 they receive (i.e. tree or flat or lazily loaded, etc)."""
1280 def __init__(self, opener, repo):
1284 def __init__(self, opener, repo):
1281 usetreemanifest = False
1285 usetreemanifest = False
1282 cachesize = 4
1286 cachesize = 4
1283
1287
1284 opts = getattr(opener, 'options', None)
1288 opts = getattr(opener, 'options', None)
1285 if opts is not None:
1289 if opts is not None:
1286 usetreemanifest = opts.get('treemanifest', usetreemanifest)
1290 usetreemanifest = opts.get('treemanifest', usetreemanifest)
1287 cachesize = opts.get('manifestcachesize', cachesize)
1291 cachesize = opts.get('manifestcachesize', cachesize)
1288 self._treeinmem = usetreemanifest
1292 self._treeinmem = usetreemanifest
1289
1293
1290 self._oldmanifest = repo._constructmanifest()
1294 self._oldmanifest = repo._constructmanifest()
1291 self._revlog = self._oldmanifest
1295 self._revlog = self._oldmanifest
1292
1296
1293 # A cache of the manifestctx or treemanifestctx for each directory
1297 # A cache of the manifestctx or treemanifestctx for each directory
1294 self._dirmancache = {}
1298 self._dirmancache = {}
1295 self._dirmancache[''] = util.lrucachedict(cachesize)
1299 self._dirmancache[''] = util.lrucachedict(cachesize)
1296
1300
1297 self.cachesize = cachesize
1301 self.cachesize = cachesize
1298
1302
1299 def __getitem__(self, node):
1303 def __getitem__(self, node):
1300 """Retrieves the manifest instance for the given node. Throws a
1304 """Retrieves the manifest instance for the given node. Throws a
1301 LookupError if not found.
1305 LookupError if not found.
1302 """
1306 """
1303 return self.get('', node)
1307 return self.get('', node)
1304
1308
1305 def get(self, dir, node, verify=True):
1309 def get(self, dir, node, verify=True):
1306 """Retrieves the manifest instance for the given node. Throws a
1310 """Retrieves the manifest instance for the given node. Throws a
1307 LookupError if not found.
1311 LookupError if not found.
1308
1312
1309 `verify` - if True an exception will be thrown if the node is not in
1313 `verify` - if True an exception will be thrown if the node is not in
1310 the revlog
1314 the revlog
1311 """
1315 """
1312 if node in self._dirmancache.get(dir, ()):
1316 if node in self._dirmancache.get(dir, ()):
1313 cachemf = self._dirmancache[dir][node]
1317 cachemf = self._dirmancache[dir][node]
1314 # The old manifest may put non-ctx manifests in the cache, so
1318 # The old manifest may put non-ctx manifests in the cache, so
1315 # skip those since they don't implement the full api.
1319 # skip those since they don't implement the full api.
1316 if (isinstance(cachemf, manifestctx) or
1320 if (isinstance(cachemf, manifestctx) or
1317 isinstance(cachemf, treemanifestctx)):
1321 isinstance(cachemf, treemanifestctx)):
1318 return cachemf
1322 return cachemf
1319
1323
1320 if dir:
1324 if dir:
1321 if self._revlog._treeondisk:
1325 if self._revlog._treeondisk:
1322 if verify:
1326 if verify:
1323 dirlog = self._revlog.dirlog(dir)
1327 dirlog = self._revlog.dirlog(dir)
1324 if node not in dirlog.nodemap:
1328 if node not in dirlog.nodemap:
1325 raise LookupError(node, dirlog.indexfile,
1329 raise LookupError(node, dirlog.indexfile,
1326 _('no node'))
1330 _('no node'))
1327 m = treemanifestctx(self, dir, node)
1331 m = treemanifestctx(self, dir, node)
1328 else:
1332 else:
1329 raise error.Abort(
1333 raise error.Abort(
1330 _("cannot ask for manifest directory '%s' in a flat "
1334 _("cannot ask for manifest directory '%s' in a flat "
1331 "manifest") % dir)
1335 "manifest") % dir)
1332 else:
1336 else:
1333 if verify:
1337 if verify:
1334 if node not in self._revlog.nodemap:
1338 if node not in self._revlog.nodemap:
1335 raise LookupError(node, self._revlog.indexfile,
1339 raise LookupError(node, self._revlog.indexfile,
1336 _('no node'))
1340 _('no node'))
1337 if self._treeinmem:
1341 if self._treeinmem:
1338 m = treemanifestctx(self, '', node)
1342 m = treemanifestctx(self, '', node)
1339 else:
1343 else:
1340 m = manifestctx(self, node)
1344 m = manifestctx(self, node)
1341
1345
1342 if node != revlog.nullid:
1346 if node != revlog.nullid:
1343 mancache = self._dirmancache.get(dir)
1347 mancache = self._dirmancache.get(dir)
1344 if not mancache:
1348 if not mancache:
1345 mancache = util.lrucachedict(self.cachesize)
1349 mancache = util.lrucachedict(self.cachesize)
1346 self._dirmancache[dir] = mancache
1350 self._dirmancache[dir] = mancache
1347 mancache[node] = m
1351 mancache[node] = m
1348 return m
1352 return m
1349
1353
1350 def clearcaches(self):
1354 def clearcaches(self):
1351 self._dirmancache.clear()
1355 self._dirmancache.clear()
1352 self._revlog.clearcaches()
1356 self._revlog.clearcaches()
1353
1357
1354 class memmanifestctx(object):
1358 class memmanifestctx(object):
1355 def __init__(self, manifestlog):
1359 def __init__(self, manifestlog):
1356 self._manifestlog = manifestlog
1360 self._manifestlog = manifestlog
1357 self._manifestdict = manifestdict()
1361 self._manifestdict = manifestdict()
1358
1362
1359 def _revlog(self):
1363 def _revlog(self):
1360 return self._manifestlog._revlog
1364 return self._manifestlog._revlog
1361
1365
1362 def new(self):
1366 def new(self):
1363 return memmanifestctx(self._manifestlog)
1367 return memmanifestctx(self._manifestlog)
1364
1368
1365 def copy(self):
1369 def copy(self):
1366 memmf = memmanifestctx(self._manifestlog)
1370 memmf = memmanifestctx(self._manifestlog)
1367 memmf._manifestdict = self.read().copy()
1371 memmf._manifestdict = self.read().copy()
1368 return memmf
1372 return memmf
1369
1373
1370 def read(self):
1374 def read(self):
1371 return self._manifestdict
1375 return self._manifestdict
1372
1376
1373 def write(self, transaction, link, p1, p2, added, removed):
1377 def write(self, transaction, link, p1, p2, added, removed):
1374 return self._revlog().add(self._manifestdict, transaction, link, p1, p2,
1378 return self._revlog().add(self._manifestdict, transaction, link, p1, p2,
1375 added, removed)
1379 added, removed)
1376
1380
1377 class manifestctx(object):
1381 class manifestctx(object):
1378 """A class representing a single revision of a manifest, including its
1382 """A class representing a single revision of a manifest, including its
1379 contents, its parent revs, and its linkrev.
1383 contents, its parent revs, and its linkrev.
1380 """
1384 """
1381 def __init__(self, manifestlog, node):
1385 def __init__(self, manifestlog, node):
1382 self._manifestlog = manifestlog
1386 self._manifestlog = manifestlog
1383 self._data = None
1387 self._data = None
1384
1388
1385 self._node = node
1389 self._node = node
1386
1390
1387 # TODO: We eventually want p1, p2, and linkrev exposed on this class,
1391 # TODO: We eventually want p1, p2, and linkrev exposed on this class,
1388 # but let's add it later when something needs it and we can load it
1392 # but let's add it later when something needs it and we can load it
1389 # lazily.
1393 # lazily.
1390 #self.p1, self.p2 = revlog.parents(node)
1394 #self.p1, self.p2 = revlog.parents(node)
1391 #rev = revlog.rev(node)
1395 #rev = revlog.rev(node)
1392 #self.linkrev = revlog.linkrev(rev)
1396 #self.linkrev = revlog.linkrev(rev)
1393
1397
1394 def _revlog(self):
1398 def _revlog(self):
1395 return self._manifestlog._revlog
1399 return self._manifestlog._revlog
1396
1400
1397 def node(self):
1401 def node(self):
1398 return self._node
1402 return self._node
1399
1403
1400 def new(self):
1404 def new(self):
1401 return memmanifestctx(self._manifestlog)
1405 return memmanifestctx(self._manifestlog)
1402
1406
1403 def copy(self):
1407 def copy(self):
1404 memmf = memmanifestctx(self._manifestlog)
1408 memmf = memmanifestctx(self._manifestlog)
1405 memmf._manifestdict = self.read().copy()
1409 memmf._manifestdict = self.read().copy()
1406 return memmf
1410 return memmf
1407
1411
1408 @propertycache
1412 @propertycache
1409 def parents(self):
1413 def parents(self):
1410 return self._revlog().parents(self._node)
1414 return self._revlog().parents(self._node)
1411
1415
1412 def read(self):
1416 def read(self):
1413 if self._data is None:
1417 if self._data is None:
1414 if self._node == revlog.nullid:
1418 if self._node == revlog.nullid:
1415 self._data = manifestdict()
1419 self._data = manifestdict()
1416 else:
1420 else:
1417 rl = self._revlog()
1421 rl = self._revlog()
1418 text = rl.revision(self._node)
1422 text = rl.revision(self._node)
1419 arraytext = array.array('c', text)
1423 arraytext = array.array('c', text)
1420 rl._fulltextcache[self._node] = arraytext
1424 rl._fulltextcache[self._node] = arraytext
1421 self._data = manifestdict(text)
1425 self._data = manifestdict(text)
1422 return self._data
1426 return self._data
1423
1427
1424 def readfast(self, shallow=False):
1428 def readfast(self, shallow=False):
1425 '''Calls either readdelta or read, based on which would be less work.
1429 '''Calls either readdelta or read, based on which would be less work.
1426 readdelta is called if the delta is against the p1, and therefore can be
1430 readdelta is called if the delta is against the p1, and therefore can be
1427 read quickly.
1431 read quickly.
1428
1432
1429 If `shallow` is True, nothing changes since this is a flat manifest.
1433 If `shallow` is True, nothing changes since this is a flat manifest.
1430 '''
1434 '''
1431 rl = self._revlog()
1435 rl = self._revlog()
1432 r = rl.rev(self._node)
1436 r = rl.rev(self._node)
1433 deltaparent = rl.deltaparent(r)
1437 deltaparent = rl.deltaparent(r)
1434 if deltaparent != revlog.nullrev and deltaparent in rl.parentrevs(r):
1438 if deltaparent != revlog.nullrev and deltaparent in rl.parentrevs(r):
1435 return self.readdelta()
1439 return self.readdelta()
1436 return self.read()
1440 return self.read()
1437
1441
1438 def readdelta(self, shallow=False):
1442 def readdelta(self, shallow=False):
1439 '''Returns a manifest containing just the entries that are present
1443 '''Returns a manifest containing just the entries that are present
1440 in this manifest, but not in its p1 manifest. This is efficient to read
1444 in this manifest, but not in its p1 manifest. This is efficient to read
1441 if the revlog delta is already p1.
1445 if the revlog delta is already p1.
1442
1446
1443 Changing the value of `shallow` has no effect on flat manifests.
1447 Changing the value of `shallow` has no effect on flat manifests.
1444 '''
1448 '''
1445 revlog = self._revlog()
1449 revlog = self._revlog()
1446 if revlog._usemanifestv2:
1450 if revlog._usemanifestv2:
1447 # Need to perform a slow delta
1451 # Need to perform a slow delta
1448 r0 = revlog.deltaparent(revlog.rev(self._node))
1452 r0 = revlog.deltaparent(revlog.rev(self._node))
1449 m0 = self._manifestlog[revlog.node(r0)].read()
1453 m0 = self._manifestlog[revlog.node(r0)].read()
1450 m1 = self.read()
1454 m1 = self.read()
1451 md = manifestdict()
1455 md = manifestdict()
1452 for f, ((n0, fl0), (n1, fl1)) in m0.diff(m1).iteritems():
1456 for f, ((n0, fl0), (n1, fl1)) in m0.diff(m1).iteritems():
1453 if n1:
1457 if n1:
1454 md[f] = n1
1458 md[f] = n1
1455 if fl1:
1459 if fl1:
1456 md.setflag(f, fl1)
1460 md.setflag(f, fl1)
1457 return md
1461 return md
1458
1462
1459 r = revlog.rev(self._node)
1463 r = revlog.rev(self._node)
1460 d = mdiff.patchtext(revlog.revdiff(revlog.deltaparent(r), r))
1464 d = mdiff.patchtext(revlog.revdiff(revlog.deltaparent(r), r))
1461 return manifestdict(d)
1465 return manifestdict(d)
1462
1466
1463 def find(self, key):
1467 def find(self, key):
1464 return self.read().find(key)
1468 return self.read().find(key)
1465
1469
1466 class memtreemanifestctx(object):
1470 class memtreemanifestctx(object):
1467 def __init__(self, manifestlog, dir=''):
1471 def __init__(self, manifestlog, dir=''):
1468 self._manifestlog = manifestlog
1472 self._manifestlog = manifestlog
1469 self._dir = dir
1473 self._dir = dir
1470 self._treemanifest = treemanifest()
1474 self._treemanifest = treemanifest()
1471
1475
1472 def _revlog(self):
1476 def _revlog(self):
1473 return self._manifestlog._revlog
1477 return self._manifestlog._revlog
1474
1478
1475 def new(self, dir=''):
1479 def new(self, dir=''):
1476 return memtreemanifestctx(self._manifestlog, dir=dir)
1480 return memtreemanifestctx(self._manifestlog, dir=dir)
1477
1481
1478 def copy(self):
1482 def copy(self):
1479 memmf = memtreemanifestctx(self._manifestlog, dir=self._dir)
1483 memmf = memtreemanifestctx(self._manifestlog, dir=self._dir)
1480 memmf._treemanifest = self._treemanifest.copy()
1484 memmf._treemanifest = self._treemanifest.copy()
1481 return memmf
1485 return memmf
1482
1486
1483 def read(self):
1487 def read(self):
1484 return self._treemanifest
1488 return self._treemanifest
1485
1489
1486 def write(self, transaction, link, p1, p2, added, removed):
1490 def write(self, transaction, link, p1, p2, added, removed):
1487 def readtree(dir, node):
1491 def readtree(dir, node):
1488 return self._manifestlog.get(dir, node).read()
1492 return self._manifestlog.get(dir, node).read()
1489 return self._revlog().add(self._treemanifest, transaction, link, p1, p2,
1493 return self._revlog().add(self._treemanifest, transaction, link, p1, p2,
1490 added, removed, readtree=readtree)
1494 added, removed, readtree=readtree)
1491
1495
1492 class treemanifestctx(object):
1496 class treemanifestctx(object):
1493 def __init__(self, manifestlog, dir, node):
1497 def __init__(self, manifestlog, dir, node):
1494 self._manifestlog = manifestlog
1498 self._manifestlog = manifestlog
1495 self._dir = dir
1499 self._dir = dir
1496 self._data = None
1500 self._data = None
1497
1501
1498 self._node = node
1502 self._node = node
1499
1503
1500 # TODO: Load p1/p2/linkrev lazily. They need to be lazily loaded so that
1504 # TODO: Load p1/p2/linkrev lazily. They need to be lazily loaded so that
1501 # we can instantiate treemanifestctx objects for directories we don't
1505 # we can instantiate treemanifestctx objects for directories we don't
1502 # have on disk.
1506 # have on disk.
1503 #self.p1, self.p2 = revlog.parents(node)
1507 #self.p1, self.p2 = revlog.parents(node)
1504 #rev = revlog.rev(node)
1508 #rev = revlog.rev(node)
1505 #self.linkrev = revlog.linkrev(rev)
1509 #self.linkrev = revlog.linkrev(rev)
1506
1510
1507 def _revlog(self):
1511 def _revlog(self):
1508 return self._manifestlog._revlog.dirlog(self._dir)
1512 return self._manifestlog._revlog.dirlog(self._dir)
1509
1513
1510 def read(self):
1514 def read(self):
1511 if self._data is None:
1515 if self._data is None:
1512 rl = self._revlog()
1516 rl = self._revlog()
1513 if self._node == revlog.nullid:
1517 if self._node == revlog.nullid:
1514 self._data = treemanifest()
1518 self._data = treemanifest()
1515 elif rl._treeondisk:
1519 elif rl._treeondisk:
1516 m = treemanifest(dir=self._dir)
1520 m = treemanifest(dir=self._dir)
1517 def gettext():
1521 def gettext():
1518 return rl.revision(self._node)
1522 return rl.revision(self._node)
1519 def readsubtree(dir, subm):
1523 def readsubtree(dir, subm):
1520 # Set verify to False since we need to be able to create
1524 # Set verify to False since we need to be able to create
1521 # subtrees for trees that don't exist on disk.
1525 # subtrees for trees that don't exist on disk.
1522 return self._manifestlog.get(dir, subm, verify=False).read()
1526 return self._manifestlog.get(dir, subm, verify=False).read()
1523 m.read(gettext, readsubtree)
1527 m.read(gettext, readsubtree)
1524 m.setnode(self._node)
1528 m.setnode(self._node)
1525 self._data = m
1529 self._data = m
1526 else:
1530 else:
1527 text = rl.revision(self._node)
1531 text = rl.revision(self._node)
1528 arraytext = array.array('c', text)
1532 arraytext = array.array('c', text)
1529 rl.fulltextcache[self._node] = arraytext
1533 rl.fulltextcache[self._node] = arraytext
1530 self._data = treemanifest(dir=self._dir, text=text)
1534 self._data = treemanifest(dir=self._dir, text=text)
1531
1535
1532 return self._data
1536 return self._data
1533
1537
1534 def node(self):
1538 def node(self):
1535 return self._node
1539 return self._node
1536
1540
1537 def new(self, dir=''):
1541 def new(self, dir=''):
1538 return memtreemanifestctx(self._manifestlog, dir=dir)
1542 return memtreemanifestctx(self._manifestlog, dir=dir)
1539
1543
1540 def copy(self):
1544 def copy(self):
1541 memmf = memtreemanifestctx(self._manifestlog, dir=self._dir)
1545 memmf = memtreemanifestctx(self._manifestlog, dir=self._dir)
1542 memmf._treemanifest = self.read().copy()
1546 memmf._treemanifest = self.read().copy()
1543 return memmf
1547 return memmf
1544
1548
1545 @propertycache
1549 @propertycache
1546 def parents(self):
1550 def parents(self):
1547 return self._revlog().parents(self._node)
1551 return self._revlog().parents(self._node)
1548
1552
1549 def readdelta(self, shallow=False):
1553 def readdelta(self, shallow=False):
1550 '''Returns a manifest containing just the entries that are present
1554 '''Returns a manifest containing just the entries that are present
1551 in this manifest, but not in its p1 manifest. This is efficient to read
1555 in this manifest, but not in its p1 manifest. This is efficient to read
1552 if the revlog delta is already p1.
1556 if the revlog delta is already p1.
1553
1557
1554 If `shallow` is True, this will read the delta for this directory,
1558 If `shallow` is True, this will read the delta for this directory,
1555 without recursively reading subdirectory manifests. Instead, any
1559 without recursively reading subdirectory manifests. Instead, any
1556 subdirectory entry will be reported as it appears in the manifest, i.e.
1560 subdirectory entry will be reported as it appears in the manifest, i.e.
1557 the subdirectory will be reported among files and distinguished only by
1561 the subdirectory will be reported among files and distinguished only by
1558 its 't' flag.
1562 its 't' flag.
1559 '''
1563 '''
1560 revlog = self._revlog()
1564 revlog = self._revlog()
1561 if shallow and not revlog._usemanifestv2:
1565 if shallow and not revlog._usemanifestv2:
1562 r = revlog.rev(self._node)
1566 r = revlog.rev(self._node)
1563 d = mdiff.patchtext(revlog.revdiff(revlog.deltaparent(r), r))
1567 d = mdiff.patchtext(revlog.revdiff(revlog.deltaparent(r), r))
1564 return manifestdict(d)
1568 return manifestdict(d)
1565 else:
1569 else:
1566 # Need to perform a slow delta
1570 # Need to perform a slow delta
1567 r0 = revlog.deltaparent(revlog.rev(self._node))
1571 r0 = revlog.deltaparent(revlog.rev(self._node))
1568 m0 = self._manifestlog.get(self._dir, revlog.node(r0)).read()
1572 m0 = self._manifestlog.get(self._dir, revlog.node(r0)).read()
1569 m1 = self.read()
1573 m1 = self.read()
1570 md = treemanifest(dir=self._dir)
1574 md = treemanifest(dir=self._dir)
1571 for f, ((n0, fl0), (n1, fl1)) in m0.diff(m1).iteritems():
1575 for f, ((n0, fl0), (n1, fl1)) in m0.diff(m1).iteritems():
1572 if n1:
1576 if n1:
1573 md[f] = n1
1577 md[f] = n1
1574 if fl1:
1578 if fl1:
1575 md.setflag(f, fl1)
1579 md.setflag(f, fl1)
1576 return md
1580 return md
1577
1581
1578 def readfast(self, shallow=False):
1582 def readfast(self, shallow=False):
1579 '''Calls either readdelta or read, based on which would be less work.
1583 '''Calls either readdelta or read, based on which would be less work.
1580 readdelta is called if the delta is against the p1, and therefore can be
1584 readdelta is called if the delta is against the p1, and therefore can be
1581 read quickly.
1585 read quickly.
1582
1586
1583 If `shallow` is True, it only returns the entries from this manifest,
1587 If `shallow` is True, it only returns the entries from this manifest,
1584 and not any submanifests.
1588 and not any submanifests.
1585 '''
1589 '''
1586 rl = self._revlog()
1590 rl = self._revlog()
1587 r = rl.rev(self._node)
1591 r = rl.rev(self._node)
1588 deltaparent = rl.deltaparent(r)
1592 deltaparent = rl.deltaparent(r)
1589 if (deltaparent != revlog.nullrev and
1593 if (deltaparent != revlog.nullrev and
1590 deltaparent in rl.parentrevs(r)):
1594 deltaparent in rl.parentrevs(r)):
1591 return self.readdelta(shallow=shallow)
1595 return self.readdelta(shallow=shallow)
1592
1596
1593 if shallow:
1597 if shallow:
1594 return manifestdict(rl.revision(self._node))
1598 return manifestdict(rl.revision(self._node))
1595 else:
1599 else:
1596 return self.read()
1600 return self.read()
1597
1601
1598 def find(self, key):
1602 def find(self, key):
1599 return self.read().find(key)
1603 return self.read().find(key)
@@ -1,859 +1,869 b''
1 #require killdaemons
1 #require killdaemons
2
2
3 $ cat << EOF >> $HGRCPATH
3 $ cat << EOF >> $HGRCPATH
4 > [format]
4 > [format]
5 > usegeneraldelta=yes
5 > usegeneraldelta=yes
6 > [ui]
6 > [ui]
7 > ssh=python "$TESTDIR/dummyssh"
7 > ssh=python "$TESTDIR/dummyssh"
8 > EOF
8 > EOF
9
9
10 Set up repo
10 Set up repo
11
11
12 $ hg --config experimental.treemanifest=True init repo
12 $ hg --config experimental.treemanifest=True init repo
13 $ cd repo
13 $ cd repo
14
14
15 Requirements get set on init
15 Requirements get set on init
16
16
17 $ grep treemanifest .hg/requires
17 $ grep treemanifest .hg/requires
18 treemanifest
18 treemanifest
19
19
20 Without directories, looks like any other repo
20 Without directories, looks like any other repo
21
21
22 $ echo 0 > a
22 $ echo 0 > a
23 $ echo 0 > b
23 $ echo 0 > b
24 $ hg ci -Aqm initial
24 $ hg ci -Aqm initial
25 $ hg debugdata -m 0
25 $ hg debugdata -m 0
26 a\x00362fef284ce2ca02aecc8de6d5e8a1c3af0556fe (esc)
26 a\x00362fef284ce2ca02aecc8de6d5e8a1c3af0556fe (esc)
27 b\x00362fef284ce2ca02aecc8de6d5e8a1c3af0556fe (esc)
27 b\x00362fef284ce2ca02aecc8de6d5e8a1c3af0556fe (esc)
28
28
29 Submanifest is stored in separate revlog
29 Submanifest is stored in separate revlog
30
30
31 $ mkdir dir1
31 $ mkdir dir1
32 $ echo 1 > dir1/a
32 $ echo 1 > dir1/a
33 $ echo 1 > dir1/b
33 $ echo 1 > dir1/b
34 $ echo 1 > e
34 $ echo 1 > e
35 $ hg ci -Aqm 'add dir1'
35 $ hg ci -Aqm 'add dir1'
36 $ hg debugdata -m 1
36 $ hg debugdata -m 1
37 a\x00362fef284ce2ca02aecc8de6d5e8a1c3af0556fe (esc)
37 a\x00362fef284ce2ca02aecc8de6d5e8a1c3af0556fe (esc)
38 b\x00362fef284ce2ca02aecc8de6d5e8a1c3af0556fe (esc)
38 b\x00362fef284ce2ca02aecc8de6d5e8a1c3af0556fe (esc)
39 dir1\x008b3ffd73f901e83304c83d33132c8e774ceac44et (esc)
39 dir1\x008b3ffd73f901e83304c83d33132c8e774ceac44et (esc)
40 e\x00b8e02f6433738021a065f94175c7cd23db5f05be (esc)
40 e\x00b8e02f6433738021a065f94175c7cd23db5f05be (esc)
41 $ hg debugdata --dir dir1 0
41 $ hg debugdata --dir dir1 0
42 a\x00b8e02f6433738021a065f94175c7cd23db5f05be (esc)
42 a\x00b8e02f6433738021a065f94175c7cd23db5f05be (esc)
43 b\x00b8e02f6433738021a065f94175c7cd23db5f05be (esc)
43 b\x00b8e02f6433738021a065f94175c7cd23db5f05be (esc)
44
44
45 Can add nested directories
45 Can add nested directories
46
46
47 $ mkdir dir1/dir1
47 $ mkdir dir1/dir1
48 $ echo 2 > dir1/dir1/a
48 $ echo 2 > dir1/dir1/a
49 $ echo 2 > dir1/dir1/b
49 $ echo 2 > dir1/dir1/b
50 $ mkdir dir1/dir2
50 $ mkdir dir1/dir2
51 $ echo 2 > dir1/dir2/a
51 $ echo 2 > dir1/dir2/a
52 $ echo 2 > dir1/dir2/b
52 $ echo 2 > dir1/dir2/b
53 $ hg ci -Aqm 'add dir1/dir1'
53 $ hg ci -Aqm 'add dir1/dir1'
54 $ hg files -r .
54 $ hg files -r .
55 a
55 a
56 b
56 b
57 dir1/a (glob)
57 dir1/a (glob)
58 dir1/b (glob)
58 dir1/b (glob)
59 dir1/dir1/a (glob)
59 dir1/dir1/a (glob)
60 dir1/dir1/b (glob)
60 dir1/dir1/b (glob)
61 dir1/dir2/a (glob)
61 dir1/dir2/a (glob)
62 dir1/dir2/b (glob)
62 dir1/dir2/b (glob)
63 e
63 e
64
64
65 The manifest command works
65 The manifest command works
66
66
67 $ hg manifest
67 $ hg manifest
68 a
68 a
69 b
69 b
70 dir1/a
70 dir1/a
71 dir1/b
71 dir1/b
72 dir1/dir1/a
72 dir1/dir1/a
73 dir1/dir1/b
73 dir1/dir1/b
74 dir1/dir2/a
74 dir1/dir2/a
75 dir1/dir2/b
75 dir1/dir2/b
76 e
76 e
77
77
78 Revision is not created for unchanged directory
78 Revision is not created for unchanged directory
79
79
80 $ mkdir dir2
80 $ mkdir dir2
81 $ echo 3 > dir2/a
81 $ echo 3 > dir2/a
82 $ hg add dir2
82 $ hg add dir2
83 adding dir2/a (glob)
83 adding dir2/a (glob)
84 $ hg debugindex --dir dir1 > before
84 $ hg debugindex --dir dir1 > before
85 $ hg ci -qm 'add dir2'
85 $ hg ci -qm 'add dir2'
86 $ hg debugindex --dir dir1 > after
86 $ hg debugindex --dir dir1 > after
87 $ diff before after
87 $ diff before after
88 $ rm before after
88 $ rm before after
89
89
90 Removing directory does not create an revlog entry
90 Removing directory does not create an revlog entry
91
91
92 $ hg rm dir1/dir1
92 $ hg rm dir1/dir1
93 removing dir1/dir1/a (glob)
93 removing dir1/dir1/a (glob)
94 removing dir1/dir1/b (glob)
94 removing dir1/dir1/b (glob)
95 $ hg debugindex --dir dir1/dir1 > before
95 $ hg debugindex --dir dir1/dir1 > before
96 $ hg ci -qm 'remove dir1/dir1'
96 $ hg ci -qm 'remove dir1/dir1'
97 $ hg debugindex --dir dir1/dir1 > after
97 $ hg debugindex --dir dir1/dir1 > after
98 $ diff before after
98 $ diff before after
99 $ rm before after
99 $ rm before after
100
100
101 Check that hg files (calls treemanifest.walk()) works
101 Check that hg files (calls treemanifest.walk()) works
102 without loading all directory revlogs
102 without loading all directory revlogs
103
103
104 $ hg co 'desc("add dir2")'
104 $ hg co 'desc("add dir2")'
105 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
105 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
106 $ mv .hg/store/meta/dir2 .hg/store/meta/dir2-backup
106 $ mv .hg/store/meta/dir2 .hg/store/meta/dir2-backup
107 $ hg files -r . dir1
107 $ hg files -r . dir1
108 dir1/a (glob)
108 dir1/a (glob)
109 dir1/b (glob)
109 dir1/b (glob)
110 dir1/dir1/a (glob)
110 dir1/dir1/a (glob)
111 dir1/dir1/b (glob)
111 dir1/dir1/b (glob)
112 dir1/dir2/a (glob)
112 dir1/dir2/a (glob)
113 dir1/dir2/b (glob)
113 dir1/dir2/b (glob)
114
114
115 Check that status between revisions works (calls treemanifest.matches())
115 Check that status between revisions works (calls treemanifest.matches())
116 without loading all directory revlogs
116 without loading all directory revlogs
117
117
118 $ hg status --rev 'desc("add dir1")' --rev . dir1
118 $ hg status --rev 'desc("add dir1")' --rev . dir1
119 A dir1/dir1/a
119 A dir1/dir1/a
120 A dir1/dir1/b
120 A dir1/dir1/b
121 A dir1/dir2/a
121 A dir1/dir2/a
122 A dir1/dir2/b
122 A dir1/dir2/b
123 $ mv .hg/store/meta/dir2-backup .hg/store/meta/dir2
123 $ mv .hg/store/meta/dir2-backup .hg/store/meta/dir2
124
124
125 Merge creates 2-parent revision of directory revlog
125 Merge creates 2-parent revision of directory revlog
126
126
127 $ echo 5 > dir1/a
127 $ echo 5 > dir1/a
128 $ hg ci -Aqm 'modify dir1/a'
128 $ hg ci -Aqm 'modify dir1/a'
129 $ hg co '.^'
129 $ hg co '.^'
130 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
130 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
131 $ echo 6 > dir1/b
131 $ echo 6 > dir1/b
132 $ hg ci -Aqm 'modify dir1/b'
132 $ hg ci -Aqm 'modify dir1/b'
133 $ hg merge 'desc("modify dir1/a")'
133 $ hg merge 'desc("modify dir1/a")'
134 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
134 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
135 (branch merge, don't forget to commit)
135 (branch merge, don't forget to commit)
136 $ hg ci -m 'conflict-free merge involving dir1/'
136 $ hg ci -m 'conflict-free merge involving dir1/'
137 $ cat dir1/a
137 $ cat dir1/a
138 5
138 5
139 $ cat dir1/b
139 $ cat dir1/b
140 6
140 6
141 $ hg debugindex --dir dir1
141 $ hg debugindex --dir dir1
142 rev offset length delta linkrev nodeid p1 p2
142 rev offset length delta linkrev nodeid p1 p2
143 0 0 54 -1 1 8b3ffd73f901 000000000000 000000000000
143 0 0 54 -1 1 8b3ffd73f901 000000000000 000000000000
144 1 54 68 0 2 68e9d057c5a8 8b3ffd73f901 000000000000
144 1 54 68 0 2 68e9d057c5a8 8b3ffd73f901 000000000000
145 2 122 12 1 4 4698198d2624 68e9d057c5a8 000000000000
145 2 122 12 1 4 4698198d2624 68e9d057c5a8 000000000000
146 3 134 55 1 5 44844058ccce 68e9d057c5a8 000000000000
146 3 134 55 1 5 44844058ccce 68e9d057c5a8 000000000000
147 4 189 55 1 6 bf3d9b744927 68e9d057c5a8 000000000000
147 4 189 55 1 6 bf3d9b744927 68e9d057c5a8 000000000000
148 5 244 55 4 7 dde7c0af2a03 bf3d9b744927 44844058ccce
148 5 244 55 4 7 dde7c0af2a03 bf3d9b744927 44844058ccce
149
149
150 Merge keeping directory from parent 1 does not create revlog entry. (Note that
150 Merge keeping directory from parent 1 does not create revlog entry. (Note that
151 dir1's manifest does change, but only because dir1/a's filelog changes.)
151 dir1's manifest does change, but only because dir1/a's filelog changes.)
152
152
153 $ hg co 'desc("add dir2")'
153 $ hg co 'desc("add dir2")'
154 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
154 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
155 $ echo 8 > dir2/a
155 $ echo 8 > dir2/a
156 $ hg ci -m 'modify dir2/a'
156 $ hg ci -m 'modify dir2/a'
157 created new head
157 created new head
158
158
159 $ hg debugindex --dir dir2 > before
159 $ hg debugindex --dir dir2 > before
160 $ hg merge 'desc("modify dir1/a")'
160 $ hg merge 'desc("modify dir1/a")'
161 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
161 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
162 (branch merge, don't forget to commit)
162 (branch merge, don't forget to commit)
163 $ hg revert -r 'desc("modify dir2/a")' .
163 $ hg revert -r 'desc("modify dir2/a")' .
164 reverting dir1/a (glob)
164 reverting dir1/a (glob)
165 $ hg ci -m 'merge, keeping parent 1'
165 $ hg ci -m 'merge, keeping parent 1'
166 $ hg debugindex --dir dir2 > after
166 $ hg debugindex --dir dir2 > after
167 $ diff before after
167 $ diff before after
168 $ rm before after
168 $ rm before after
169
169
170 Merge keeping directory from parent 2 does not create revlog entry. (Note that
170 Merge keeping directory from parent 2 does not create revlog entry. (Note that
171 dir2's manifest does change, but only because dir2/a's filelog changes.)
171 dir2's manifest does change, but only because dir2/a's filelog changes.)
172
172
173 $ hg co 'desc("modify dir2/a")'
173 $ hg co 'desc("modify dir2/a")'
174 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
174 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
175 $ hg debugindex --dir dir1 > before
175 $ hg debugindex --dir dir1 > before
176 $ hg merge 'desc("modify dir1/a")'
176 $ hg merge 'desc("modify dir1/a")'
177 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
177 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
178 (branch merge, don't forget to commit)
178 (branch merge, don't forget to commit)
179 $ hg revert -r 'desc("modify dir1/a")' .
179 $ hg revert -r 'desc("modify dir1/a")' .
180 reverting dir2/a (glob)
180 reverting dir2/a (glob)
181 $ hg ci -m 'merge, keeping parent 2'
181 $ hg ci -m 'merge, keeping parent 2'
182 created new head
182 created new head
183 $ hg debugindex --dir dir1 > after
183 $ hg debugindex --dir dir1 > after
184 $ diff before after
184 $ diff before after
185 $ rm before after
185 $ rm before after
186
186
187 Create flat source repo for tests with mixed flat/tree manifests
187 Create flat source repo for tests with mixed flat/tree manifests
188
188
189 $ cd ..
189 $ cd ..
190 $ hg init repo-flat
190 $ hg init repo-flat
191 $ cd repo-flat
191 $ cd repo-flat
192
192
193 Create a few commits with flat manifest
193 Create a few commits with flat manifest
194
194
195 $ echo 0 > a
195 $ echo 0 > a
196 $ echo 0 > b
196 $ echo 0 > b
197 $ echo 0 > e
197 $ echo 0 > e
198 $ for d in dir1 dir1/dir1 dir1/dir2 dir2
198 $ for d in dir1 dir1/dir1 dir1/dir2 dir2
199 > do
199 > do
200 > mkdir $d
200 > mkdir $d
201 > echo 0 > $d/a
201 > echo 0 > $d/a
202 > echo 0 > $d/b
202 > echo 0 > $d/b
203 > done
203 > done
204 $ hg ci -Aqm initial
204 $ hg ci -Aqm initial
205
205
206 $ echo 1 > a
206 $ echo 1 > a
207 $ echo 1 > dir1/a
207 $ echo 1 > dir1/a
208 $ echo 1 > dir1/dir1/a
208 $ echo 1 > dir1/dir1/a
209 $ hg ci -Aqm 'modify on branch 1'
209 $ hg ci -Aqm 'modify on branch 1'
210
210
211 $ hg co 0
211 $ hg co 0
212 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
212 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
213 $ echo 2 > b
213 $ echo 2 > b
214 $ echo 2 > dir1/b
214 $ echo 2 > dir1/b
215 $ echo 2 > dir1/dir1/b
215 $ echo 2 > dir1/dir1/b
216 $ hg ci -Aqm 'modify on branch 2'
216 $ hg ci -Aqm 'modify on branch 2'
217
217
218 $ hg merge 1
218 $ hg merge 1
219 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
219 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
220 (branch merge, don't forget to commit)
220 (branch merge, don't forget to commit)
221 $ hg ci -m 'merge of flat manifests to new flat manifest'
221 $ hg ci -m 'merge of flat manifests to new flat manifest'
222
222
223 $ hg serve -p $HGPORT -d --pid-file=hg.pid --errorlog=errors.log
223 $ hg serve -p $HGPORT -d --pid-file=hg.pid --errorlog=errors.log
224 $ cat hg.pid >> $DAEMON_PIDS
224 $ cat hg.pid >> $DAEMON_PIDS
225
225
226 Create clone with tree manifests enabled
226 Create clone with tree manifests enabled
227
227
228 $ cd ..
228 $ cd ..
229 $ hg clone --config experimental.treemanifest=1 \
229 $ hg clone --config experimental.treemanifest=1 \
230 > http://localhost:$HGPORT repo-mixed -r 1
230 > http://localhost:$HGPORT repo-mixed -r 1
231 adding changesets
231 adding changesets
232 adding manifests
232 adding manifests
233 adding file changes
233 adding file changes
234 added 2 changesets with 14 changes to 11 files
234 added 2 changesets with 14 changes to 11 files
235 updating to branch default
235 updating to branch default
236 11 files updated, 0 files merged, 0 files removed, 0 files unresolved
236 11 files updated, 0 files merged, 0 files removed, 0 files unresolved
237 $ cd repo-mixed
237 $ cd repo-mixed
238 $ test -d .hg/store/meta
238 $ test -d .hg/store/meta
239 [1]
239 [1]
240 $ grep treemanifest .hg/requires
240 $ grep treemanifest .hg/requires
241 treemanifest
241 treemanifest
242
242
243 Should be possible to push updates from flat to tree manifest repo
243 Should be possible to push updates from flat to tree manifest repo
244
244
245 $ hg -R ../repo-flat push ssh://user@dummy/repo-mixed
245 $ hg -R ../repo-flat push ssh://user@dummy/repo-mixed
246 pushing to ssh://user@dummy/repo-mixed
246 pushing to ssh://user@dummy/repo-mixed
247 searching for changes
247 searching for changes
248 remote: adding changesets
248 remote: adding changesets
249 remote: adding manifests
249 remote: adding manifests
250 remote: adding file changes
250 remote: adding file changes
251 remote: added 2 changesets with 3 changes to 3 files
251 remote: added 2 changesets with 3 changes to 3 files
252
252
253 Commit should store revlog per directory
253 Commit should store revlog per directory
254
254
255 $ hg co 1
255 $ hg co 1
256 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
256 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
257 $ echo 3 > a
257 $ echo 3 > a
258 $ echo 3 > dir1/a
258 $ echo 3 > dir1/a
259 $ echo 3 > dir1/dir1/a
259 $ echo 3 > dir1/dir1/a
260 $ hg ci -m 'first tree'
260 $ hg ci -m 'first tree'
261 created new head
261 created new head
262 $ find .hg/store/meta | sort
262 $ find .hg/store/meta | sort
263 .hg/store/meta
263 .hg/store/meta
264 .hg/store/meta/dir1
264 .hg/store/meta/dir1
265 .hg/store/meta/dir1/00manifest.i
265 .hg/store/meta/dir1/00manifest.i
266 .hg/store/meta/dir1/dir1
266 .hg/store/meta/dir1/dir1
267 .hg/store/meta/dir1/dir1/00manifest.i
267 .hg/store/meta/dir1/dir1/00manifest.i
268 .hg/store/meta/dir1/dir2
268 .hg/store/meta/dir1/dir2
269 .hg/store/meta/dir1/dir2/00manifest.i
269 .hg/store/meta/dir1/dir2/00manifest.i
270 .hg/store/meta/dir2
270 .hg/store/meta/dir2
271 .hg/store/meta/dir2/00manifest.i
271 .hg/store/meta/dir2/00manifest.i
272
272
273 Merge of two trees
273 Merge of two trees
274
274
275 $ hg co 2
275 $ hg co 2
276 6 files updated, 0 files merged, 0 files removed, 0 files unresolved
276 6 files updated, 0 files merged, 0 files removed, 0 files unresolved
277 $ hg merge 1
277 $ hg merge 1
278 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
278 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
279 (branch merge, don't forget to commit)
279 (branch merge, don't forget to commit)
280 $ hg ci -m 'merge of flat manifests to new tree manifest'
280 $ hg ci -m 'merge of flat manifests to new tree manifest'
281 created new head
281 created new head
282 $ hg diff -r 3
282 $ hg diff -r 3
283
283
284 Parent of tree root manifest should be flat manifest, and two for merge
284 Parent of tree root manifest should be flat manifest, and two for merge
285
285
286 $ hg debugindex -m
286 $ hg debugindex -m
287 rev offset length delta linkrev nodeid p1 p2
287 rev offset length delta linkrev nodeid p1 p2
288 0 0 80 -1 0 40536115ed9e 000000000000 000000000000
288 0 0 80 -1 0 40536115ed9e 000000000000 000000000000
289 1 80 83 0 1 f3376063c255 40536115ed9e 000000000000
289 1 80 83 0 1 f3376063c255 40536115ed9e 000000000000
290 2 163 89 0 2 5d9b9da231a2 40536115ed9e 000000000000
290 2 163 89 0 2 5d9b9da231a2 40536115ed9e 000000000000
291 3 252 83 2 3 d17d663cbd8a 5d9b9da231a2 f3376063c255
291 3 252 83 2 3 d17d663cbd8a 5d9b9da231a2 f3376063c255
292 4 335 124 1 4 51e32a8c60ee f3376063c255 000000000000
292 4 335 124 1 4 51e32a8c60ee f3376063c255 000000000000
293 5 459 126 2 5 cc5baa78b230 5d9b9da231a2 f3376063c255
293 5 459 126 2 5 cc5baa78b230 5d9b9da231a2 f3376063c255
294
294
295
295
296 Status across flat/tree boundary should work
296 Status across flat/tree boundary should work
297
297
298 $ hg status --rev '.^' --rev .
298 $ hg status --rev '.^' --rev .
299 M a
299 M a
300 M dir1/a
300 M dir1/a
301 M dir1/dir1/a
301 M dir1/dir1/a
302
302
303
303
304 Turning off treemanifest config has no effect
304 Turning off treemanifest config has no effect
305
305
306 $ hg debugindex --dir dir1
306 $ hg debugindex --dir dir1
307 rev offset length delta linkrev nodeid p1 p2
307 rev offset length delta linkrev nodeid p1 p2
308 0 0 127 -1 4 064927a0648a 000000000000 000000000000
308 0 0 127 -1 4 064927a0648a 000000000000 000000000000
309 1 127 111 0 5 25ecb8cb8618 000000000000 000000000000
309 1 127 111 0 5 25ecb8cb8618 000000000000 000000000000
310 $ echo 2 > dir1/a
310 $ echo 2 > dir1/a
311 $ hg --config experimental.treemanifest=False ci -qm 'modify dir1/a'
311 $ hg --config experimental.treemanifest=False ci -qm 'modify dir1/a'
312 $ hg debugindex --dir dir1
312 $ hg debugindex --dir dir1
313 rev offset length delta linkrev nodeid p1 p2
313 rev offset length delta linkrev nodeid p1 p2
314 0 0 127 -1 4 064927a0648a 000000000000 000000000000
314 0 0 127 -1 4 064927a0648a 000000000000 000000000000
315 1 127 111 0 5 25ecb8cb8618 000000000000 000000000000
315 1 127 111 0 5 25ecb8cb8618 000000000000 000000000000
316 2 238 55 1 6 5b16163a30c6 25ecb8cb8618 000000000000
316 2 238 55 1 6 5b16163a30c6 25ecb8cb8618 000000000000
317
317
318 Stripping and recovering changes should work
318 Stripping and recovering changes should work
319
319
320 $ hg st --change tip
320 $ hg st --change tip
321 M dir1/a
321 M dir1/a
322 $ hg --config extensions.strip= strip tip
322 $ hg --config extensions.strip= strip tip
323 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
323 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
324 saved backup bundle to $TESTTMP/repo-mixed/.hg/strip-backup/51cfd7b1e13b-78a2f3ed-backup.hg (glob)
324 saved backup bundle to $TESTTMP/repo-mixed/.hg/strip-backup/51cfd7b1e13b-78a2f3ed-backup.hg (glob)
325 $ hg debugindex --dir dir1
325 $ hg debugindex --dir dir1
326 rev offset length delta linkrev nodeid p1 p2
326 rev offset length delta linkrev nodeid p1 p2
327 0 0 127 -1 4 064927a0648a 000000000000 000000000000
327 0 0 127 -1 4 064927a0648a 000000000000 000000000000
328 1 127 111 0 5 25ecb8cb8618 000000000000 000000000000
328 1 127 111 0 5 25ecb8cb8618 000000000000 000000000000
329 $ hg incoming .hg/strip-backup/*
329 $ hg incoming .hg/strip-backup/*
330 comparing with .hg/strip-backup/*-backup.hg (glob)
330 comparing with .hg/strip-backup/*-backup.hg (glob)
331 searching for changes
331 searching for changes
332 changeset: 6:51cfd7b1e13b
332 changeset: 6:51cfd7b1e13b
333 tag: tip
333 tag: tip
334 user: test
334 user: test
335 date: Thu Jan 01 00:00:00 1970 +0000
335 date: Thu Jan 01 00:00:00 1970 +0000
336 summary: modify dir1/a
336 summary: modify dir1/a
337
337
338 $ hg pull .hg/strip-backup/*
338 $ hg pull .hg/strip-backup/*
339 pulling from .hg/strip-backup/51cfd7b1e13b-78a2f3ed-backup.hg
339 pulling from .hg/strip-backup/51cfd7b1e13b-78a2f3ed-backup.hg
340 searching for changes
340 searching for changes
341 adding changesets
341 adding changesets
342 adding manifests
342 adding manifests
343 adding file changes
343 adding file changes
344 added 1 changesets with 1 changes to 1 files
344 added 1 changesets with 1 changes to 1 files
345 (run 'hg update' to get a working copy)
345 (run 'hg update' to get a working copy)
346 $ hg --config extensions.strip= strip tip
346 $ hg --config extensions.strip= strip tip
347 saved backup bundle to $TESTTMP/repo-mixed/.hg/strip-backup/*-backup.hg (glob)
347 saved backup bundle to $TESTTMP/repo-mixed/.hg/strip-backup/*-backup.hg (glob)
348 $ hg unbundle -q .hg/strip-backup/*
348 $ hg unbundle -q .hg/strip-backup/*
349 $ hg debugindex --dir dir1
349 $ hg debugindex --dir dir1
350 rev offset length delta linkrev nodeid p1 p2
350 rev offset length delta linkrev nodeid p1 p2
351 0 0 127 -1 4 064927a0648a 000000000000 000000000000
351 0 0 127 -1 4 064927a0648a 000000000000 000000000000
352 1 127 111 0 5 25ecb8cb8618 000000000000 000000000000
352 1 127 111 0 5 25ecb8cb8618 000000000000 000000000000
353 2 238 55 1 6 5b16163a30c6 25ecb8cb8618 000000000000
353 2 238 55 1 6 5b16163a30c6 25ecb8cb8618 000000000000
354 $ hg st --change tip
354 $ hg st --change tip
355 M dir1/a
355 M dir1/a
356
356
357 Shelving and unshelving should work
357 Shelving and unshelving should work
358
358
359 $ echo foo >> dir1/a
359 $ echo foo >> dir1/a
360 $ hg --config extensions.shelve= shelve
360 $ hg --config extensions.shelve= shelve
361 shelved as default
361 shelved as default
362 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
362 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
363 $ hg --config extensions.shelve= unshelve
363 $ hg --config extensions.shelve= unshelve
364 unshelving change 'default'
364 unshelving change 'default'
365 $ hg diff --nodates
365 $ hg diff --nodates
366 diff -r 708a273da119 dir1/a
366 diff -r 708a273da119 dir1/a
367 --- a/dir1/a
367 --- a/dir1/a
368 +++ b/dir1/a
368 +++ b/dir1/a
369 @@ -1,1 +1,2 @@
369 @@ -1,1 +1,2 @@
370 1
370 1
371 +foo
371 +foo
372
372
373 Pushing from treemanifest repo to an empty repo makes that a treemanifest repo
373 Pushing from treemanifest repo to an empty repo makes that a treemanifest repo
374
374
375 $ cd ..
375 $ cd ..
376 $ hg init empty-repo
376 $ hg init empty-repo
377 $ cat << EOF >> empty-repo/.hg/hgrc
377 $ cat << EOF >> empty-repo/.hg/hgrc
378 > [experimental]
378 > [experimental]
379 > changegroup3=yes
379 > changegroup3=yes
380 > EOF
380 > EOF
381 $ grep treemanifest empty-repo/.hg/requires
381 $ grep treemanifest empty-repo/.hg/requires
382 [1]
382 [1]
383 $ hg push -R repo -r 0 empty-repo
383 $ hg push -R repo -r 0 empty-repo
384 pushing to empty-repo
384 pushing to empty-repo
385 searching for changes
385 searching for changes
386 adding changesets
386 adding changesets
387 adding manifests
387 adding manifests
388 adding file changes
388 adding file changes
389 added 1 changesets with 2 changes to 2 files
389 added 1 changesets with 2 changes to 2 files
390 $ grep treemanifest empty-repo/.hg/requires
390 $ grep treemanifest empty-repo/.hg/requires
391 treemanifest
391 treemanifest
392
392
393 Pushing to an empty repo works
393 Pushing to an empty repo works
394
394
395 $ hg --config experimental.treemanifest=1 init clone
395 $ hg --config experimental.treemanifest=1 init clone
396 $ grep treemanifest clone/.hg/requires
396 $ grep treemanifest clone/.hg/requires
397 treemanifest
397 treemanifest
398 $ hg push -R repo clone
398 $ hg push -R repo clone
399 pushing to clone
399 pushing to clone
400 searching for changes
400 searching for changes
401 adding changesets
401 adding changesets
402 adding manifests
402 adding manifests
403 adding file changes
403 adding file changes
404 added 11 changesets with 15 changes to 10 files (+3 heads)
404 added 11 changesets with 15 changes to 10 files (+3 heads)
405 $ grep treemanifest clone/.hg/requires
405 $ grep treemanifest clone/.hg/requires
406 treemanifest
406 treemanifest
407 $ hg -R clone verify
407 $ hg -R clone verify
408 checking changesets
408 checking changesets
409 checking manifests
409 checking manifests
410 checking directory manifests
410 checking directory manifests
411 crosschecking files in changesets and manifests
411 crosschecking files in changesets and manifests
412 checking files
412 checking files
413 10 files, 11 changesets, 15 total revisions
413 10 files, 11 changesets, 15 total revisions
414
414
415 Create deeper repo with tree manifests.
415 Create deeper repo with tree manifests.
416
416
417 $ hg --config experimental.treemanifest=True init deeprepo
417 $ hg --config experimental.treemanifest=True init deeprepo
418 $ cd deeprepo
418 $ cd deeprepo
419
419
420 $ mkdir .A
420 $ mkdir .A
421 $ mkdir b
421 $ mkdir b
422 $ mkdir b/bar
422 $ mkdir b/bar
423 $ mkdir b/bar/orange
423 $ mkdir b/bar/orange
424 $ mkdir b/bar/orange/fly
424 $ mkdir b/bar/orange/fly
425 $ mkdir b/foo
425 $ mkdir b/foo
426 $ mkdir b/foo/apple
426 $ mkdir b/foo/apple
427 $ mkdir b/foo/apple/bees
427 $ mkdir b/foo/apple/bees
428
428
429 $ touch .A/one.txt
429 $ touch .A/one.txt
430 $ touch .A/two.txt
430 $ touch .A/two.txt
431 $ touch b/bar/fruits.txt
431 $ touch b/bar/fruits.txt
432 $ touch b/bar/orange/fly/gnat.py
432 $ touch b/bar/orange/fly/gnat.py
433 $ touch b/bar/orange/fly/housefly.txt
433 $ touch b/bar/orange/fly/housefly.txt
434 $ touch b/foo/apple/bees/flower.py
434 $ touch b/foo/apple/bees/flower.py
435 $ touch c.txt
435 $ touch c.txt
436 $ touch d.py
436 $ touch d.py
437
437
438 $ hg ci -Aqm 'initial'
438 $ hg ci -Aqm 'initial'
439
439
440 $ echo >> .A/one.txt
440 $ echo >> .A/one.txt
441 $ echo >> .A/two.txt
441 $ echo >> .A/two.txt
442 $ echo >> b/bar/fruits.txt
442 $ echo >> b/bar/fruits.txt
443 $ echo >> b/bar/orange/fly/gnat.py
443 $ echo >> b/bar/orange/fly/gnat.py
444 $ echo >> b/bar/orange/fly/housefly.txt
444 $ echo >> b/bar/orange/fly/housefly.txt
445 $ echo >> b/foo/apple/bees/flower.py
445 $ echo >> b/foo/apple/bees/flower.py
446 $ echo >> c.txt
446 $ echo >> c.txt
447 $ echo >> d.py
447 $ echo >> d.py
448 $ hg ci -Aqm 'second'
448 $ hg ci -Aqm 'second'
449
449
450 We'll see that visitdir works by removing some treemanifest revlogs and running
450 We'll see that visitdir works by removing some treemanifest revlogs and running
451 the files command with various parameters.
451 the files command with various parameters.
452
452
453 Test files from the root.
453 Test files from the root.
454
454
455 $ hg files -r .
455 $ hg files -r .
456 .A/one.txt (glob)
456 .A/one.txt (glob)
457 .A/two.txt (glob)
457 .A/two.txt (glob)
458 b/bar/fruits.txt (glob)
458 b/bar/fruits.txt (glob)
459 b/bar/orange/fly/gnat.py (glob)
459 b/bar/orange/fly/gnat.py (glob)
460 b/bar/orange/fly/housefly.txt (glob)
460 b/bar/orange/fly/housefly.txt (glob)
461 b/foo/apple/bees/flower.py (glob)
461 b/foo/apple/bees/flower.py (glob)
462 c.txt
462 c.txt
463 d.py
463 d.py
464
464
465 Excludes with a glob should not exclude everything from the glob's root
465 Excludes with a glob should not exclude everything from the glob's root
466
466
467 $ hg files -r . -X 'b/fo?' b
467 $ hg files -r . -X 'b/fo?' b
468 b/bar/fruits.txt (glob)
468 b/bar/fruits.txt (glob)
469 b/bar/orange/fly/gnat.py (glob)
469 b/bar/orange/fly/gnat.py (glob)
470 b/bar/orange/fly/housefly.txt (glob)
470 b/bar/orange/fly/housefly.txt (glob)
471 $ cp -R .hg/store .hg/store-copy
471 $ cp -R .hg/store .hg/store-copy
472
472
473 Test files for a subdirectory.
473 Test files for a subdirectory.
474
474
475 $ rm -r .hg/store/meta/~2e_a
475 $ rm -r .hg/store/meta/~2e_a
476 $ hg files -r . b
476 $ hg files -r . b
477 b/bar/fruits.txt (glob)
477 b/bar/fruits.txt (glob)
478 b/bar/orange/fly/gnat.py (glob)
478 b/bar/orange/fly/gnat.py (glob)
479 b/bar/orange/fly/housefly.txt (glob)
479 b/bar/orange/fly/housefly.txt (glob)
480 b/foo/apple/bees/flower.py (glob)
480 b/foo/apple/bees/flower.py (glob)
481 $ hg diff -r '.^' -r . --stat b
481 $ hg diff -r '.^' -r . --stat b
482 b/bar/fruits.txt | 1 +
482 b/bar/fruits.txt | 1 +
483 b/bar/orange/fly/gnat.py | 1 +
483 b/bar/orange/fly/gnat.py | 1 +
484 b/bar/orange/fly/housefly.txt | 1 +
484 b/bar/orange/fly/housefly.txt | 1 +
485 b/foo/apple/bees/flower.py | 1 +
485 b/foo/apple/bees/flower.py | 1 +
486 4 files changed, 4 insertions(+), 0 deletions(-)
486 4 files changed, 4 insertions(+), 0 deletions(-)
487 $ cp -R .hg/store-copy/. .hg/store
487 $ cp -R .hg/store-copy/. .hg/store
488
488
489 Test files with just includes and excludes.
489 Test files with just includes and excludes.
490
490
491 $ rm -r .hg/store/meta/~2e_a
491 $ rm -r .hg/store/meta/~2e_a
492 $ rm -r .hg/store/meta/b/bar/orange/fly
492 $ rm -r .hg/store/meta/b/bar/orange/fly
493 $ rm -r .hg/store/meta/b/foo/apple/bees
493 $ rm -r .hg/store/meta/b/foo/apple/bees
494 $ hg files -r . -I path:b/bar -X path:b/bar/orange/fly -I path:b/foo -X path:b/foo/apple/bees
494 $ hg files -r . -I path:b/bar -X path:b/bar/orange/fly -I path:b/foo -X path:b/foo/apple/bees
495 b/bar/fruits.txt (glob)
495 b/bar/fruits.txt (glob)
496 $ hg diff -r '.^' -r . --stat -I path:b/bar -X path:b/bar/orange/fly -I path:b/foo -X path:b/foo/apple/bees
496 $ hg diff -r '.^' -r . --stat -I path:b/bar -X path:b/bar/orange/fly -I path:b/foo -X path:b/foo/apple/bees
497 b/bar/fruits.txt | 1 +
497 b/bar/fruits.txt | 1 +
498 1 files changed, 1 insertions(+), 0 deletions(-)
498 1 files changed, 1 insertions(+), 0 deletions(-)
499 $ cp -R .hg/store-copy/. .hg/store
499 $ cp -R .hg/store-copy/. .hg/store
500
500
501 Test files for a subdirectory, excluding a directory within it.
501 Test files for a subdirectory, excluding a directory within it.
502
502
503 $ rm -r .hg/store/meta/~2e_a
503 $ rm -r .hg/store/meta/~2e_a
504 $ rm -r .hg/store/meta/b/foo
504 $ rm -r .hg/store/meta/b/foo
505 $ hg files -r . -X path:b/foo b
505 $ hg files -r . -X path:b/foo b
506 b/bar/fruits.txt (glob)
506 b/bar/fruits.txt (glob)
507 b/bar/orange/fly/gnat.py (glob)
507 b/bar/orange/fly/gnat.py (glob)
508 b/bar/orange/fly/housefly.txt (glob)
508 b/bar/orange/fly/housefly.txt (glob)
509 $ hg diff -r '.^' -r . --stat -X path:b/foo b
509 $ hg diff -r '.^' -r . --stat -X path:b/foo b
510 b/bar/fruits.txt | 1 +
510 b/bar/fruits.txt | 1 +
511 b/bar/orange/fly/gnat.py | 1 +
511 b/bar/orange/fly/gnat.py | 1 +
512 b/bar/orange/fly/housefly.txt | 1 +
512 b/bar/orange/fly/housefly.txt | 1 +
513 3 files changed, 3 insertions(+), 0 deletions(-)
513 3 files changed, 3 insertions(+), 0 deletions(-)
514 $ cp -R .hg/store-copy/. .hg/store
514 $ cp -R .hg/store-copy/. .hg/store
515
515
516 Test files for a sub directory, including only a directory within it, and
516 Test files for a sub directory, including only a directory within it, and
517 including an unrelated directory.
517 including an unrelated directory.
518
518
519 $ rm -r .hg/store/meta/~2e_a
519 $ rm -r .hg/store/meta/~2e_a
520 $ rm -r .hg/store/meta/b/foo
520 $ rm -r .hg/store/meta/b/foo
521 $ hg files -r . -I path:b/bar/orange -I path:a b
521 $ hg files -r . -I path:b/bar/orange -I path:a b
522 b/bar/orange/fly/gnat.py (glob)
522 b/bar/orange/fly/gnat.py (glob)
523 b/bar/orange/fly/housefly.txt (glob)
523 b/bar/orange/fly/housefly.txt (glob)
524 $ hg diff -r '.^' -r . --stat -I path:b/bar/orange -I path:a b
524 $ hg diff -r '.^' -r . --stat -I path:b/bar/orange -I path:a b
525 b/bar/orange/fly/gnat.py | 1 +
525 b/bar/orange/fly/gnat.py | 1 +
526 b/bar/orange/fly/housefly.txt | 1 +
526 b/bar/orange/fly/housefly.txt | 1 +
527 2 files changed, 2 insertions(+), 0 deletions(-)
527 2 files changed, 2 insertions(+), 0 deletions(-)
528 $ cp -R .hg/store-copy/. .hg/store
528 $ cp -R .hg/store-copy/. .hg/store
529
529
530 Test files for a pattern, including a directory, and excluding a directory
530 Test files for a pattern, including a directory, and excluding a directory
531 within that.
531 within that.
532
532
533 $ rm -r .hg/store/meta/~2e_a
533 $ rm -r .hg/store/meta/~2e_a
534 $ rm -r .hg/store/meta/b/foo
534 $ rm -r .hg/store/meta/b/foo
535 $ rm -r .hg/store/meta/b/bar/orange
535 $ rm -r .hg/store/meta/b/bar/orange
536 $ hg files -r . glob:**.txt -I path:b/bar -X path:b/bar/orange
536 $ hg files -r . glob:**.txt -I path:b/bar -X path:b/bar/orange
537 b/bar/fruits.txt (glob)
537 b/bar/fruits.txt (glob)
538 $ hg diff -r '.^' -r . --stat glob:**.txt -I path:b/bar -X path:b/bar/orange
538 $ hg diff -r '.^' -r . --stat glob:**.txt -I path:b/bar -X path:b/bar/orange
539 b/bar/fruits.txt | 1 +
539 b/bar/fruits.txt | 1 +
540 1 files changed, 1 insertions(+), 0 deletions(-)
540 1 files changed, 1 insertions(+), 0 deletions(-)
541 $ cp -R .hg/store-copy/. .hg/store
541 $ cp -R .hg/store-copy/. .hg/store
542
542
543 Add some more changes to the deep repo
543 Add some more changes to the deep repo
544 $ echo narf >> b/bar/fruits.txt
544 $ echo narf >> b/bar/fruits.txt
545 $ hg ci -m narf
545 $ hg ci -m narf
546 $ echo troz >> b/bar/orange/fly/gnat.py
546 $ echo troz >> b/bar/orange/fly/gnat.py
547 $ hg ci -m troz
547 $ hg ci -m troz
548
548
549 Verify works
549 Verify works
550 $ hg verify
550 $ hg verify
551 checking changesets
551 checking changesets
552 checking manifests
552 checking manifests
553 checking directory manifests
553 checking directory manifests
554 crosschecking files in changesets and manifests
554 crosschecking files in changesets and manifests
555 checking files
555 checking files
556 8 files, 4 changesets, 18 total revisions
556 8 files, 4 changesets, 18 total revisions
557
557
558 Dirlogs are included in fncache
558 Dirlogs are included in fncache
559 $ grep meta/.A/00manifest.i .hg/store/fncache
559 $ grep meta/.A/00manifest.i .hg/store/fncache
560 meta/.A/00manifest.i
560 meta/.A/00manifest.i
561
561
562 Rebuilt fncache includes dirlogs
562 Rebuilt fncache includes dirlogs
563 $ rm .hg/store/fncache
563 $ rm .hg/store/fncache
564 $ hg debugrebuildfncache
564 $ hg debugrebuildfncache
565 adding data/.A/one.txt.i
565 adding data/.A/one.txt.i
566 adding data/.A/two.txt.i
566 adding data/.A/two.txt.i
567 adding data/b/bar/fruits.txt.i
567 adding data/b/bar/fruits.txt.i
568 adding data/b/bar/orange/fly/gnat.py.i
568 adding data/b/bar/orange/fly/gnat.py.i
569 adding data/b/bar/orange/fly/housefly.txt.i
569 adding data/b/bar/orange/fly/housefly.txt.i
570 adding data/b/foo/apple/bees/flower.py.i
570 adding data/b/foo/apple/bees/flower.py.i
571 adding data/c.txt.i
571 adding data/c.txt.i
572 adding data/d.py.i
572 adding data/d.py.i
573 adding meta/.A/00manifest.i
573 adding meta/.A/00manifest.i
574 adding meta/b/00manifest.i
574 adding meta/b/00manifest.i
575 adding meta/b/bar/00manifest.i
575 adding meta/b/bar/00manifest.i
576 adding meta/b/bar/orange/00manifest.i
576 adding meta/b/bar/orange/00manifest.i
577 adding meta/b/bar/orange/fly/00manifest.i
577 adding meta/b/bar/orange/fly/00manifest.i
578 adding meta/b/foo/00manifest.i
578 adding meta/b/foo/00manifest.i
579 adding meta/b/foo/apple/00manifest.i
579 adding meta/b/foo/apple/00manifest.i
580 adding meta/b/foo/apple/bees/00manifest.i
580 adding meta/b/foo/apple/bees/00manifest.i
581 16 items added, 0 removed from fncache
581 16 items added, 0 removed from fncache
582
582
583 Finish first server
583 Finish first server
584 $ killdaemons.py
584 $ killdaemons.py
585
585
586 Back up the recently added revlogs
586 Back up the recently added revlogs
587 $ cp -R .hg/store .hg/store-newcopy
587 $ cp -R .hg/store .hg/store-newcopy
588
588
589 Verify reports missing dirlog
589 Verify reports missing dirlog
590 $ rm .hg/store/meta/b/00manifest.*
590 $ rm .hg/store/meta/b/00manifest.*
591 $ hg verify
591 $ hg verify
592 checking changesets
592 checking changesets
593 checking manifests
593 checking manifests
594 checking directory manifests
594 checking directory manifests
595 0: empty or missing b/
595 0: empty or missing b/
596 b/@0: parent-directory manifest refers to unknown revision 67688a370455
596 b/@0: parent-directory manifest refers to unknown revision 67688a370455
597 b/@1: parent-directory manifest refers to unknown revision f065da70369e
597 b/@1: parent-directory manifest refers to unknown revision f065da70369e
598 b/@2: parent-directory manifest refers to unknown revision ac0d30948e0b
598 b/@2: parent-directory manifest refers to unknown revision ac0d30948e0b
599 b/@3: parent-directory manifest refers to unknown revision 367152e6af28
599 b/@3: parent-directory manifest refers to unknown revision 367152e6af28
600 warning: orphan revlog 'meta/b/bar/00manifest.i'
600 warning: orphan revlog 'meta/b/bar/00manifest.i'
601 warning: orphan revlog 'meta/b/bar/orange/00manifest.i'
601 warning: orphan revlog 'meta/b/bar/orange/00manifest.i'
602 warning: orphan revlog 'meta/b/bar/orange/fly/00manifest.i'
602 warning: orphan revlog 'meta/b/bar/orange/fly/00manifest.i'
603 warning: orphan revlog 'meta/b/foo/00manifest.i'
603 warning: orphan revlog 'meta/b/foo/00manifest.i'
604 warning: orphan revlog 'meta/b/foo/apple/00manifest.i'
604 warning: orphan revlog 'meta/b/foo/apple/00manifest.i'
605 warning: orphan revlog 'meta/b/foo/apple/bees/00manifest.i'
605 warning: orphan revlog 'meta/b/foo/apple/bees/00manifest.i'
606 crosschecking files in changesets and manifests
606 crosschecking files in changesets and manifests
607 b/bar/fruits.txt@0: in changeset but not in manifest
607 b/bar/fruits.txt@0: in changeset but not in manifest
608 b/bar/orange/fly/gnat.py@0: in changeset but not in manifest
608 b/bar/orange/fly/gnat.py@0: in changeset but not in manifest
609 b/bar/orange/fly/housefly.txt@0: in changeset but not in manifest
609 b/bar/orange/fly/housefly.txt@0: in changeset but not in manifest
610 b/foo/apple/bees/flower.py@0: in changeset but not in manifest
610 b/foo/apple/bees/flower.py@0: in changeset but not in manifest
611 checking files
611 checking files
612 8 files, 4 changesets, 18 total revisions
612 8 files, 4 changesets, 18 total revisions
613 6 warnings encountered!
613 6 warnings encountered!
614 9 integrity errors encountered!
614 9 integrity errors encountered!
615 (first damaged changeset appears to be 0)
615 (first damaged changeset appears to be 0)
616 [1]
616 [1]
617 $ cp -R .hg/store-newcopy/. .hg/store
617 $ cp -R .hg/store-newcopy/. .hg/store
618
618
619 Verify reports missing dirlog entry
619 Verify reports missing dirlog entry
620 $ mv -f .hg/store-copy/meta/b/00manifest.* .hg/store/meta/b/
620 $ mv -f .hg/store-copy/meta/b/00manifest.* .hg/store/meta/b/
621 $ hg verify
621 $ hg verify
622 checking changesets
622 checking changesets
623 checking manifests
623 checking manifests
624 checking directory manifests
624 checking directory manifests
625 b/@2: parent-directory manifest refers to unknown revision ac0d30948e0b
625 b/@2: parent-directory manifest refers to unknown revision ac0d30948e0b
626 b/@3: parent-directory manifest refers to unknown revision 367152e6af28
626 b/@3: parent-directory manifest refers to unknown revision 367152e6af28
627 b/bar/@?: rev 2 points to unexpected changeset 2
627 b/bar/@?: rev 2 points to unexpected changeset 2
628 b/bar/@?: 44d7e1146e0d not in parent-directory manifest
628 b/bar/@?: 44d7e1146e0d not in parent-directory manifest
629 b/bar/@?: rev 3 points to unexpected changeset 3
629 b/bar/@?: rev 3 points to unexpected changeset 3
630 b/bar/@?: 70b10c6b17b7 not in parent-directory manifest
630 b/bar/@?: 70b10c6b17b7 not in parent-directory manifest
631 b/bar/orange/@?: rev 2 points to unexpected changeset 3
631 b/bar/orange/@?: rev 2 points to unexpected changeset 3
632 (expected None)
632 (expected None)
633 b/bar/orange/fly/@?: rev 2 points to unexpected changeset 3
633 b/bar/orange/fly/@?: rev 2 points to unexpected changeset 3
634 (expected None)
634 (expected None)
635 crosschecking files in changesets and manifests
635 crosschecking files in changesets and manifests
636 checking files
636 checking files
637 8 files, 4 changesets, 18 total revisions
637 8 files, 4 changesets, 18 total revisions
638 2 warnings encountered!
638 2 warnings encountered!
639 8 integrity errors encountered!
639 8 integrity errors encountered!
640 (first damaged changeset appears to be 2)
640 (first damaged changeset appears to be 2)
641 [1]
641 [1]
642 $ cp -R .hg/store-newcopy/. .hg/store
642 $ cp -R .hg/store-newcopy/. .hg/store
643
643
644 Test cloning a treemanifest repo over http.
644 Test cloning a treemanifest repo over http.
645 $ hg serve -p $HGPORT -d --pid-file=hg.pid --errorlog=errors.log
645 $ hg serve -p $HGPORT -d --pid-file=hg.pid --errorlog=errors.log
646 $ cat hg.pid >> $DAEMON_PIDS
646 $ cat hg.pid >> $DAEMON_PIDS
647 $ cd ..
647 $ cd ..
648 We can clone even with the knob turned off and we'll get a treemanifest repo.
648 We can clone even with the knob turned off and we'll get a treemanifest repo.
649 $ hg clone --config experimental.treemanifest=False \
649 $ hg clone --config experimental.treemanifest=False \
650 > --config experimental.changegroup3=True \
650 > --config experimental.changegroup3=True \
651 > http://localhost:$HGPORT deepclone
651 > http://localhost:$HGPORT deepclone
652 requesting all changes
652 requesting all changes
653 adding changesets
653 adding changesets
654 adding manifests
654 adding manifests
655 adding file changes
655 adding file changes
656 added 4 changesets with 18 changes to 8 files
656 added 4 changesets with 18 changes to 8 files
657 updating to branch default
657 updating to branch default
658 8 files updated, 0 files merged, 0 files removed, 0 files unresolved
658 8 files updated, 0 files merged, 0 files removed, 0 files unresolved
659 No server errors.
659 No server errors.
660 $ cat deeprepo/errors.log
660 $ cat deeprepo/errors.log
661 requires got updated to include treemanifest
661 requires got updated to include treemanifest
662 $ cat deepclone/.hg/requires | grep treemanifest
662 $ cat deepclone/.hg/requires | grep treemanifest
663 treemanifest
663 treemanifest
664 Tree manifest revlogs exist.
664 Tree manifest revlogs exist.
665 $ find deepclone/.hg/store/meta | sort
665 $ find deepclone/.hg/store/meta | sort
666 deepclone/.hg/store/meta
666 deepclone/.hg/store/meta
667 deepclone/.hg/store/meta/b
667 deepclone/.hg/store/meta/b
668 deepclone/.hg/store/meta/b/00manifest.i
668 deepclone/.hg/store/meta/b/00manifest.i
669 deepclone/.hg/store/meta/b/bar
669 deepclone/.hg/store/meta/b/bar
670 deepclone/.hg/store/meta/b/bar/00manifest.i
670 deepclone/.hg/store/meta/b/bar/00manifest.i
671 deepclone/.hg/store/meta/b/bar/orange
671 deepclone/.hg/store/meta/b/bar/orange
672 deepclone/.hg/store/meta/b/bar/orange/00manifest.i
672 deepclone/.hg/store/meta/b/bar/orange/00manifest.i
673 deepclone/.hg/store/meta/b/bar/orange/fly
673 deepclone/.hg/store/meta/b/bar/orange/fly
674 deepclone/.hg/store/meta/b/bar/orange/fly/00manifest.i
674 deepclone/.hg/store/meta/b/bar/orange/fly/00manifest.i
675 deepclone/.hg/store/meta/b/foo
675 deepclone/.hg/store/meta/b/foo
676 deepclone/.hg/store/meta/b/foo/00manifest.i
676 deepclone/.hg/store/meta/b/foo/00manifest.i
677 deepclone/.hg/store/meta/b/foo/apple
677 deepclone/.hg/store/meta/b/foo/apple
678 deepclone/.hg/store/meta/b/foo/apple/00manifest.i
678 deepclone/.hg/store/meta/b/foo/apple/00manifest.i
679 deepclone/.hg/store/meta/b/foo/apple/bees
679 deepclone/.hg/store/meta/b/foo/apple/bees
680 deepclone/.hg/store/meta/b/foo/apple/bees/00manifest.i
680 deepclone/.hg/store/meta/b/foo/apple/bees/00manifest.i
681 deepclone/.hg/store/meta/~2e_a
681 deepclone/.hg/store/meta/~2e_a
682 deepclone/.hg/store/meta/~2e_a/00manifest.i
682 deepclone/.hg/store/meta/~2e_a/00manifest.i
683 Verify passes.
683 Verify passes.
684 $ cd deepclone
684 $ cd deepclone
685 $ hg verify
685 $ hg verify
686 checking changesets
686 checking changesets
687 checking manifests
687 checking manifests
688 checking directory manifests
688 checking directory manifests
689 crosschecking files in changesets and manifests
689 crosschecking files in changesets and manifests
690 checking files
690 checking files
691 8 files, 4 changesets, 18 total revisions
691 8 files, 4 changesets, 18 total revisions
692 $ cd ..
692 $ cd ..
693
693
694 Create clones using old repo formats to use in later tests
694 Create clones using old repo formats to use in later tests
695 $ hg clone --config format.usestore=False \
695 $ hg clone --config format.usestore=False \
696 > --config experimental.changegroup3=True \
696 > --config experimental.changegroup3=True \
697 > http://localhost:$HGPORT deeprepo-basicstore
697 > http://localhost:$HGPORT deeprepo-basicstore
698 requesting all changes
698 requesting all changes
699 adding changesets
699 adding changesets
700 adding manifests
700 adding manifests
701 adding file changes
701 adding file changes
702 added 4 changesets with 18 changes to 8 files
702 added 4 changesets with 18 changes to 8 files
703 updating to branch default
703 updating to branch default
704 8 files updated, 0 files merged, 0 files removed, 0 files unresolved
704 8 files updated, 0 files merged, 0 files removed, 0 files unresolved
705 $ cd deeprepo-basicstore
705 $ cd deeprepo-basicstore
706 $ grep store .hg/requires
706 $ grep store .hg/requires
707 [1]
707 [1]
708 $ hg serve -p $HGPORT1 -d --pid-file=hg.pid --errorlog=errors.log
708 $ hg serve -p $HGPORT1 -d --pid-file=hg.pid --errorlog=errors.log
709 $ cat hg.pid >> $DAEMON_PIDS
709 $ cat hg.pid >> $DAEMON_PIDS
710 $ cd ..
710 $ cd ..
711 $ hg clone --config format.usefncache=False \
711 $ hg clone --config format.usefncache=False \
712 > --config experimental.changegroup3=True \
712 > --config experimental.changegroup3=True \
713 > http://localhost:$HGPORT deeprepo-encodedstore
713 > http://localhost:$HGPORT deeprepo-encodedstore
714 requesting all changes
714 requesting all changes
715 adding changesets
715 adding changesets
716 adding manifests
716 adding manifests
717 adding file changes
717 adding file changes
718 added 4 changesets with 18 changes to 8 files
718 added 4 changesets with 18 changes to 8 files
719 updating to branch default
719 updating to branch default
720 8 files updated, 0 files merged, 0 files removed, 0 files unresolved
720 8 files updated, 0 files merged, 0 files removed, 0 files unresolved
721 $ cd deeprepo-encodedstore
721 $ cd deeprepo-encodedstore
722 $ grep fncache .hg/requires
722 $ grep fncache .hg/requires
723 [1]
723 [1]
724 $ hg serve -p $HGPORT2 -d --pid-file=hg.pid --errorlog=errors.log
724 $ hg serve -p $HGPORT2 -d --pid-file=hg.pid --errorlog=errors.log
725 $ cat hg.pid >> $DAEMON_PIDS
725 $ cat hg.pid >> $DAEMON_PIDS
726 $ cd ..
726 $ cd ..
727
727
728 Local clone with basicstore
728 Local clone with basicstore
729 $ hg clone -U deeprepo-basicstore local-clone-basicstore
729 $ hg clone -U deeprepo-basicstore local-clone-basicstore
730 $ hg -R local-clone-basicstore verify
730 $ hg -R local-clone-basicstore verify
731 checking changesets
731 checking changesets
732 checking manifests
732 checking manifests
733 checking directory manifests
733 checking directory manifests
734 crosschecking files in changesets and manifests
734 crosschecking files in changesets and manifests
735 checking files
735 checking files
736 8 files, 4 changesets, 18 total revisions
736 8 files, 4 changesets, 18 total revisions
737
737
738 Local clone with encodedstore
738 Local clone with encodedstore
739 $ hg clone -U deeprepo-encodedstore local-clone-encodedstore
739 $ hg clone -U deeprepo-encodedstore local-clone-encodedstore
740 $ hg -R local-clone-encodedstore verify
740 $ hg -R local-clone-encodedstore verify
741 checking changesets
741 checking changesets
742 checking manifests
742 checking manifests
743 checking directory manifests
743 checking directory manifests
744 crosschecking files in changesets and manifests
744 crosschecking files in changesets and manifests
745 checking files
745 checking files
746 8 files, 4 changesets, 18 total revisions
746 8 files, 4 changesets, 18 total revisions
747
747
748 Local clone with fncachestore
748 Local clone with fncachestore
749 $ hg clone -U deeprepo local-clone-fncachestore
749 $ hg clone -U deeprepo local-clone-fncachestore
750 $ hg -R local-clone-fncachestore verify
750 $ hg -R local-clone-fncachestore verify
751 checking changesets
751 checking changesets
752 checking manifests
752 checking manifests
753 checking directory manifests
753 checking directory manifests
754 crosschecking files in changesets and manifests
754 crosschecking files in changesets and manifests
755 checking files
755 checking files
756 8 files, 4 changesets, 18 total revisions
756 8 files, 4 changesets, 18 total revisions
757
757
758 Stream clone with basicstore
758 Stream clone with basicstore
759 $ hg clone --config experimental.changegroup3=True --uncompressed -U \
759 $ hg clone --config experimental.changegroup3=True --uncompressed -U \
760 > http://localhost:$HGPORT1 stream-clone-basicstore
760 > http://localhost:$HGPORT1 stream-clone-basicstore
761 streaming all changes
761 streaming all changes
762 18 files to transfer, * of data (glob)
762 18 files to transfer, * of data (glob)
763 transferred * in * seconds (*) (glob)
763 transferred * in * seconds (*) (glob)
764 searching for changes
764 searching for changes
765 no changes found
765 no changes found
766 $ hg -R stream-clone-basicstore verify
766 $ hg -R stream-clone-basicstore verify
767 checking changesets
767 checking changesets
768 checking manifests
768 checking manifests
769 checking directory manifests
769 checking directory manifests
770 crosschecking files in changesets and manifests
770 crosschecking files in changesets and manifests
771 checking files
771 checking files
772 8 files, 4 changesets, 18 total revisions
772 8 files, 4 changesets, 18 total revisions
773
773
774 Stream clone with encodedstore
774 Stream clone with encodedstore
775 $ hg clone --config experimental.changegroup3=True --uncompressed -U \
775 $ hg clone --config experimental.changegroup3=True --uncompressed -U \
776 > http://localhost:$HGPORT2 stream-clone-encodedstore
776 > http://localhost:$HGPORT2 stream-clone-encodedstore
777 streaming all changes
777 streaming all changes
778 18 files to transfer, * of data (glob)
778 18 files to transfer, * of data (glob)
779 transferred * in * seconds (*) (glob)
779 transferred * in * seconds (*) (glob)
780 searching for changes
780 searching for changes
781 no changes found
781 no changes found
782 $ hg -R stream-clone-encodedstore verify
782 $ hg -R stream-clone-encodedstore verify
783 checking changesets
783 checking changesets
784 checking manifests
784 checking manifests
785 checking directory manifests
785 checking directory manifests
786 crosschecking files in changesets and manifests
786 crosschecking files in changesets and manifests
787 checking files
787 checking files
788 8 files, 4 changesets, 18 total revisions
788 8 files, 4 changesets, 18 total revisions
789
789
790 Stream clone with fncachestore
790 Stream clone with fncachestore
791 $ hg clone --config experimental.changegroup3=True --uncompressed -U \
791 $ hg clone --config experimental.changegroup3=True --uncompressed -U \
792 > http://localhost:$HGPORT stream-clone-fncachestore
792 > http://localhost:$HGPORT stream-clone-fncachestore
793 streaming all changes
793 streaming all changes
794 18 files to transfer, * of data (glob)
794 18 files to transfer, * of data (glob)
795 transferred * in * seconds (*) (glob)
795 transferred * in * seconds (*) (glob)
796 searching for changes
796 searching for changes
797 no changes found
797 no changes found
798 $ hg -R stream-clone-fncachestore verify
798 $ hg -R stream-clone-fncachestore verify
799 checking changesets
799 checking changesets
800 checking manifests
800 checking manifests
801 checking directory manifests
801 checking directory manifests
802 crosschecking files in changesets and manifests
802 crosschecking files in changesets and manifests
803 checking files
803 checking files
804 8 files, 4 changesets, 18 total revisions
804 8 files, 4 changesets, 18 total revisions
805
805
806 Packed bundle
806 Packed bundle
807 $ hg -R deeprepo debugcreatestreamclonebundle repo-packed.hg
807 $ hg -R deeprepo debugcreatestreamclonebundle repo-packed.hg
808 writing 5330 bytes for 18 files
808 writing 5330 bytes for 18 files
809 bundle requirements: generaldelta, revlogv1, treemanifest
809 bundle requirements: generaldelta, revlogv1, treemanifest
810 $ hg debugbundle --spec repo-packed.hg
810 $ hg debugbundle --spec repo-packed.hg
811 none-packed1;requirements%3Dgeneraldelta%2Crevlogv1%2Ctreemanifest
811 none-packed1;requirements%3Dgeneraldelta%2Crevlogv1%2Ctreemanifest
812
812
813 Bundle with changegroup2 is not supported
813 Bundle with changegroup2 is not supported
814
814
815 $ hg -R deeprepo bundle --all -t v2 deeprepo.bundle
815 $ hg -R deeprepo bundle --all -t v2 deeprepo.bundle
816 abort: repository does not support bundle version 02
816 abort: repository does not support bundle version 02
817 [255]
817 [255]
818
818
819 Pull does not include changegroup for manifest the client already has from
819 Pull does not include changegroup for manifest the client already has from
820 other branch
820 other branch
821
821
822 $ mkdir grafted-dir-repo
822 $ mkdir grafted-dir-repo
823 $ cd grafted-dir-repo
823 $ cd grafted-dir-repo
824 $ hg --config experimental.treemanifest=1 init
824 $ hg --config experimental.treemanifest=1 init
825 $ mkdir dir
825 $ mkdir dir
826 $ echo a > dir/file
826 $ echo a > dir/file
827 $ echo a > file
827 $ echo a > file
828 $ hg ci -Am initial
828 $ hg ci -Am initial
829 adding dir/file
829 adding dir/file
830 adding file
830 adding file
831 $ echo b > dir/file
831 $ echo b > dir/file
832 $ hg ci -m updated
832 $ hg ci -m updated
833 $ hg co '.^'
833 $ hg co '.^'
834 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
834 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
835 $ hg revert -r tip dir/
835 $ hg revert -r tip dir/
836 reverting dir/file (glob)
836 reverting dir/file (glob)
837 $ echo b > file # to make sure root manifest is sent
837 $ echo b > file # to make sure root manifest is sent
838 $ hg ci -m grafted
838 $ hg ci -m grafted
839 created new head
839 created new head
840 $ cd ..
840 $ cd ..
841
841
842 $ hg --config experimental.treemanifest=1 clone --pull -r 1 \
842 $ hg --config experimental.treemanifest=1 clone --pull -r 1 \
843 > grafted-dir-repo grafted-dir-repo-clone
843 > grafted-dir-repo grafted-dir-repo-clone
844 adding changesets
844 adding changesets
845 adding manifests
845 adding manifests
846 adding file changes
846 adding file changes
847 added 2 changesets with 3 changes to 2 files
847 added 2 changesets with 3 changes to 2 files
848 updating to branch default
848 updating to branch default
849 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
849 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
850 $ cd grafted-dir-repo-clone
850 $ cd grafted-dir-repo-clone
851 $ hg pull -r 2
851 $ hg pull -r 2
852 pulling from $TESTTMP/grafted-dir-repo (glob)
852 pulling from $TESTTMP/grafted-dir-repo (glob)
853 searching for changes
853 searching for changes
854 adding changesets
854 adding changesets
855 adding manifests
855 adding manifests
856 adding file changes
856 adding file changes
857 added 1 changesets with 1 changes to 1 files (+1 heads)
857 added 1 changesets with 1 changes to 1 files (+1 heads)
858 (run 'hg heads' to see heads, 'hg merge' to merge)
858 (run 'hg heads' to see heads, 'hg merge' to merge)
859
859
860 Committing a empty commit does not duplicate root treemanifest
861 $ echo z >> z
862 $ hg commit -Aqm 'pre-empty commit'
863 $ hg rm z
864 $ hg commit --amend -m 'empty commit'
865 saved backup bundle to $TESTTMP/grafted-dir-repo-clone/.hg/strip-backup/cb99d5717cea-de37743b-amend-backup.hg (glob)
866 $ hg log -r 'tip + tip^' -T '{manifest}\n'
867 1:678d3574b88c
868 1:678d3574b88c
869 $ hg --config extensions.strip= strip -r . -q
General Comments 0
You need to be logged in to leave comments. Login now