##// END OF EJS Templates
manifest: convert a recursive function to iterative one using stacks...
Pulkit Goyal -
r41188:2c3f6985 default
parent child Browse files
Show More
@@ -1,2044 +1,2052 b''
1 1 # manifest.py - manifest revision class for mercurial
2 2 #
3 3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 from __future__ import absolute_import
9 9
10 10 import heapq
11 11 import itertools
12 12 import struct
13 13 import weakref
14 14
15 15 from .i18n import _
16 16 from .node import (
17 17 bin,
18 18 hex,
19 19 nullid,
20 20 nullrev,
21 21 )
22 22 from . import (
23 23 error,
24 24 mdiff,
25 25 policy,
26 26 pycompat,
27 27 repository,
28 28 revlog,
29 29 util,
30 30 )
31 31 from .utils import (
32 32 interfaceutil,
33 33 )
34 34
35 35 parsers = policy.importmod(r'parsers')
36 36 propertycache = util.propertycache
37 37
38 38 def _parse(data):
39 39 # This method does a little bit of excessive-looking
40 40 # precondition checking. This is so that the behavior of this
41 41 # class exactly matches its C counterpart to try and help
42 42 # prevent surprise breakage for anyone that develops against
43 43 # the pure version.
44 44 if data and data[-1:] != '\n':
45 45 raise ValueError('Manifest did not end in a newline.')
46 46 prev = None
47 47 for l in data.splitlines():
48 48 if prev is not None and prev > l:
49 49 raise ValueError('Manifest lines not in sorted order.')
50 50 prev = l
51 51 f, n = l.split('\0')
52 52 if len(n) > 40:
53 53 yield f, bin(n[:40]), n[40:]
54 54 else:
55 55 yield f, bin(n), ''
56 56
57 57 def _text(it):
58 58 files = []
59 59 lines = []
60 60 for f, n, fl in it:
61 61 files.append(f)
62 62 # if this is changed to support newlines in filenames,
63 63 # be sure to check the templates/ dir again (especially *-raw.tmpl)
64 64 lines.append("%s\0%s%s\n" % (f, hex(n), fl))
65 65
66 66 _checkforbidden(files)
67 67 return ''.join(lines)
68 68
69 69 class lazymanifestiter(object):
70 70 def __init__(self, lm):
71 71 self.pos = 0
72 72 self.lm = lm
73 73
74 74 def __iter__(self):
75 75 return self
76 76
77 77 def next(self):
78 78 try:
79 79 data, pos = self.lm._get(self.pos)
80 80 except IndexError:
81 81 raise StopIteration
82 82 if pos == -1:
83 83 self.pos += 1
84 84 return data[0]
85 85 self.pos += 1
86 86 zeropos = data.find('\x00', pos)
87 87 return data[pos:zeropos]
88 88
89 89 __next__ = next
90 90
91 91 class lazymanifestiterentries(object):
92 92 def __init__(self, lm):
93 93 self.lm = lm
94 94 self.pos = 0
95 95
96 96 def __iter__(self):
97 97 return self
98 98
99 99 def next(self):
100 100 try:
101 101 data, pos = self.lm._get(self.pos)
102 102 except IndexError:
103 103 raise StopIteration
104 104 if pos == -1:
105 105 self.pos += 1
106 106 return data
107 107 zeropos = data.find('\x00', pos)
108 108 hashval = unhexlify(data, self.lm.extrainfo[self.pos],
109 109 zeropos + 1, 40)
110 110 flags = self.lm._getflags(data, self.pos, zeropos)
111 111 self.pos += 1
112 112 return (data[pos:zeropos], hashval, flags)
113 113
114 114 __next__ = next
115 115
116 116 def unhexlify(data, extra, pos, length):
117 117 s = bin(data[pos:pos + length])
118 118 if extra:
119 119 s += chr(extra & 0xff)
120 120 return s
121 121
122 122 def _cmp(a, b):
123 123 return (a > b) - (a < b)
124 124
125 125 class _lazymanifest(object):
126 126 def __init__(self, data, positions=None, extrainfo=None, extradata=None):
127 127 if positions is None:
128 128 self.positions = self.findlines(data)
129 129 self.extrainfo = [0] * len(self.positions)
130 130 self.data = data
131 131 self.extradata = []
132 132 else:
133 133 self.positions = positions[:]
134 134 self.extrainfo = extrainfo[:]
135 135 self.extradata = extradata[:]
136 136 self.data = data
137 137
138 138 def findlines(self, data):
139 139 if not data:
140 140 return []
141 141 pos = data.find("\n")
142 142 if pos == -1 or data[-1:] != '\n':
143 143 raise ValueError("Manifest did not end in a newline.")
144 144 positions = [0]
145 145 prev = data[:data.find('\x00')]
146 146 while pos < len(data) - 1 and pos != -1:
147 147 positions.append(pos + 1)
148 148 nexts = data[pos + 1:data.find('\x00', pos + 1)]
149 149 if nexts < prev:
150 150 raise ValueError("Manifest lines not in sorted order.")
151 151 prev = nexts
152 152 pos = data.find("\n", pos + 1)
153 153 return positions
154 154
155 155 def _get(self, index):
156 156 # get the position encoded in pos:
157 157 # positive number is an index in 'data'
158 158 # negative number is in extrapieces
159 159 pos = self.positions[index]
160 160 if pos >= 0:
161 161 return self.data, pos
162 162 return self.extradata[-pos - 1], -1
163 163
164 164 def _getkey(self, pos):
165 165 if pos >= 0:
166 166 return self.data[pos:self.data.find('\x00', pos + 1)]
167 167 return self.extradata[-pos - 1][0]
168 168
169 169 def bsearch(self, key):
170 170 first = 0
171 171 last = len(self.positions) - 1
172 172
173 173 while first <= last:
174 174 midpoint = (first + last)//2
175 175 nextpos = self.positions[midpoint]
176 176 candidate = self._getkey(nextpos)
177 177 r = _cmp(key, candidate)
178 178 if r == 0:
179 179 return midpoint
180 180 else:
181 181 if r < 0:
182 182 last = midpoint - 1
183 183 else:
184 184 first = midpoint + 1
185 185 return -1
186 186
187 187 def bsearch2(self, key):
188 188 # same as the above, but will always return the position
189 189 # done for performance reasons
190 190 first = 0
191 191 last = len(self.positions) - 1
192 192
193 193 while first <= last:
194 194 midpoint = (first + last)//2
195 195 nextpos = self.positions[midpoint]
196 196 candidate = self._getkey(nextpos)
197 197 r = _cmp(key, candidate)
198 198 if r == 0:
199 199 return (midpoint, True)
200 200 else:
201 201 if r < 0:
202 202 last = midpoint - 1
203 203 else:
204 204 first = midpoint + 1
205 205 return (first, False)
206 206
207 207 def __contains__(self, key):
208 208 return self.bsearch(key) != -1
209 209
210 210 def _getflags(self, data, needle, pos):
211 211 start = pos + 41
212 212 end = data.find("\n", start)
213 213 if end == -1:
214 214 end = len(data) - 1
215 215 if start == end:
216 216 return ''
217 217 return self.data[start:end]
218 218
219 219 def __getitem__(self, key):
220 220 if not isinstance(key, bytes):
221 221 raise TypeError("getitem: manifest keys must be a bytes.")
222 222 needle = self.bsearch(key)
223 223 if needle == -1:
224 224 raise KeyError
225 225 data, pos = self._get(needle)
226 226 if pos == -1:
227 227 return (data[1], data[2])
228 228 zeropos = data.find('\x00', pos)
229 229 assert 0 <= needle <= len(self.positions)
230 230 assert len(self.extrainfo) == len(self.positions)
231 231 hashval = unhexlify(data, self.extrainfo[needle], zeropos + 1, 40)
232 232 flags = self._getflags(data, needle, zeropos)
233 233 return (hashval, flags)
234 234
235 235 def __delitem__(self, key):
236 236 needle, found = self.bsearch2(key)
237 237 if not found:
238 238 raise KeyError
239 239 cur = self.positions[needle]
240 240 self.positions = self.positions[:needle] + self.positions[needle + 1:]
241 241 self.extrainfo = self.extrainfo[:needle] + self.extrainfo[needle + 1:]
242 242 if cur >= 0:
243 243 self.data = self.data[:cur] + '\x00' + self.data[cur + 1:]
244 244
245 245 def __setitem__(self, key, value):
246 246 if not isinstance(key, bytes):
247 247 raise TypeError("setitem: manifest keys must be a byte string.")
248 248 if not isinstance(value, tuple) or len(value) != 2:
249 249 raise TypeError("Manifest values must be a tuple of (node, flags).")
250 250 hashval = value[0]
251 251 if not isinstance(hashval, bytes) or not 20 <= len(hashval) <= 22:
252 252 raise TypeError("node must be a 20-byte byte string")
253 253 flags = value[1]
254 254 if len(hashval) == 22:
255 255 hashval = hashval[:-1]
256 256 if not isinstance(flags, bytes) or len(flags) > 1:
257 257 raise TypeError("flags must a 0 or 1 byte string, got %r", flags)
258 258 needle, found = self.bsearch2(key)
259 259 if found:
260 260 # put the item
261 261 pos = self.positions[needle]
262 262 if pos < 0:
263 263 self.extradata[-pos - 1] = (key, hashval, value[1])
264 264 else:
265 265 # just don't bother
266 266 self.extradata.append((key, hashval, value[1]))
267 267 self.positions[needle] = -len(self.extradata)
268 268 else:
269 269 # not found, put it in with extra positions
270 270 self.extradata.append((key, hashval, value[1]))
271 271 self.positions = (self.positions[:needle] + [-len(self.extradata)]
272 272 + self.positions[needle:])
273 273 self.extrainfo = (self.extrainfo[:needle] + [0] +
274 274 self.extrainfo[needle:])
275 275
276 276 def copy(self):
277 277 # XXX call _compact like in C?
278 278 return _lazymanifest(self.data, self.positions, self.extrainfo,
279 279 self.extradata)
280 280
281 281 def _compact(self):
282 282 # hopefully not called TOO often
283 283 if len(self.extradata) == 0:
284 284 return
285 285 l = []
286 286 last_cut = 0
287 287 i = 0
288 288 offset = 0
289 289 self.extrainfo = [0] * len(self.positions)
290 290 while i < len(self.positions):
291 291 if self.positions[i] >= 0:
292 292 cur = self.positions[i]
293 293 last_cut = cur
294 294 while True:
295 295 self.positions[i] = offset
296 296 i += 1
297 297 if i == len(self.positions) or self.positions[i] < 0:
298 298 break
299 299 offset += self.positions[i] - cur
300 300 cur = self.positions[i]
301 301 end_cut = self.data.find('\n', cur)
302 302 if end_cut != -1:
303 303 end_cut += 1
304 304 offset += end_cut - cur
305 305 l.append(self.data[last_cut:end_cut])
306 306 else:
307 307 while i < len(self.positions) and self.positions[i] < 0:
308 308 cur = self.positions[i]
309 309 t = self.extradata[-cur - 1]
310 310 l.append(self._pack(t))
311 311 self.positions[i] = offset
312 312 if len(t[1]) > 20:
313 313 self.extrainfo[i] = ord(t[1][21])
314 314 offset += len(l[-1])
315 315 i += 1
316 316 self.data = ''.join(l)
317 317 self.extradata = []
318 318
319 319 def _pack(self, d):
320 320 return d[0] + '\x00' + hex(d[1][:20]) + d[2] + '\n'
321 321
322 322 def text(self):
323 323 self._compact()
324 324 return self.data
325 325
326 326 def diff(self, m2, clean=False):
327 327 '''Finds changes between the current manifest and m2.'''
328 328 # XXX think whether efficiency matters here
329 329 diff = {}
330 330
331 331 for fn, e1, flags in self.iterentries():
332 332 if fn not in m2:
333 333 diff[fn] = (e1, flags), (None, '')
334 334 else:
335 335 e2 = m2[fn]
336 336 if (e1, flags) != e2:
337 337 diff[fn] = (e1, flags), e2
338 338 elif clean:
339 339 diff[fn] = None
340 340
341 341 for fn, e2, flags in m2.iterentries():
342 342 if fn not in self:
343 343 diff[fn] = (None, ''), (e2, flags)
344 344
345 345 return diff
346 346
347 347 def iterentries(self):
348 348 return lazymanifestiterentries(self)
349 349
350 350 def iterkeys(self):
351 351 return lazymanifestiter(self)
352 352
353 353 def __iter__(self):
354 354 return lazymanifestiter(self)
355 355
356 356 def __len__(self):
357 357 return len(self.positions)
358 358
359 359 def filtercopy(self, filterfn):
360 360 # XXX should be optimized
361 361 c = _lazymanifest('')
362 362 for f, n, fl in self.iterentries():
363 363 if filterfn(f):
364 364 c[f] = n, fl
365 365 return c
366 366
367 367 try:
368 368 _lazymanifest = parsers.lazymanifest
369 369 except AttributeError:
370 370 pass
371 371
372 372 @interfaceutil.implementer(repository.imanifestdict)
373 373 class manifestdict(object):
374 374 def __init__(self, data=''):
375 375 self._lm = _lazymanifest(data)
376 376
377 377 def __getitem__(self, key):
378 378 return self._lm[key][0]
379 379
380 380 def find(self, key):
381 381 return self._lm[key]
382 382
383 383 def __len__(self):
384 384 return len(self._lm)
385 385
386 386 def __nonzero__(self):
387 387 # nonzero is covered by the __len__ function, but implementing it here
388 388 # makes it easier for extensions to override.
389 389 return len(self._lm) != 0
390 390
391 391 __bool__ = __nonzero__
392 392
393 393 def __setitem__(self, key, node):
394 394 self._lm[key] = node, self.flags(key, '')
395 395
396 396 def __contains__(self, key):
397 397 if key is None:
398 398 return False
399 399 return key in self._lm
400 400
401 401 def __delitem__(self, key):
402 402 del self._lm[key]
403 403
404 404 def __iter__(self):
405 405 return self._lm.__iter__()
406 406
407 407 def iterkeys(self):
408 408 return self._lm.iterkeys()
409 409
410 410 def keys(self):
411 411 return list(self.iterkeys())
412 412
413 413 def filesnotin(self, m2, match=None):
414 414 '''Set of files in this manifest that are not in the other'''
415 415 if match:
416 416 m1 = self.matches(match)
417 417 m2 = m2.matches(match)
418 418 return m1.filesnotin(m2)
419 419 diff = self.diff(m2)
420 420 files = set(filepath
421 421 for filepath, hashflags in diff.iteritems()
422 422 if hashflags[1][0] is None)
423 423 return files
424 424
425 425 @propertycache
426 426 def _dirs(self):
427 427 return util.dirs(self)
428 428
429 429 def dirs(self):
430 430 return self._dirs
431 431
432 432 def hasdir(self, dir):
433 433 return dir in self._dirs
434 434
435 435 def _filesfastpath(self, match):
436 436 '''Checks whether we can correctly and quickly iterate over matcher
437 437 files instead of over manifest files.'''
438 438 files = match.files()
439 439 return (len(files) < 100 and (match.isexact() or
440 440 (match.prefix() and all(fn in self for fn in files))))
441 441
442 442 def walk(self, match):
443 443 '''Generates matching file names.
444 444
445 445 Equivalent to manifest.matches(match).iterkeys(), but without creating
446 446 an entirely new manifest.
447 447
448 448 It also reports nonexistent files by marking them bad with match.bad().
449 449 '''
450 450 if match.always():
451 451 for f in iter(self):
452 452 yield f
453 453 return
454 454
455 455 fset = set(match.files())
456 456
457 457 # avoid the entire walk if we're only looking for specific files
458 458 if self._filesfastpath(match):
459 459 for fn in sorted(fset):
460 460 yield fn
461 461 return
462 462
463 463 for fn in self:
464 464 if fn in fset:
465 465 # specified pattern is the exact name
466 466 fset.remove(fn)
467 467 if match(fn):
468 468 yield fn
469 469
470 470 # for dirstate.walk, files=['.'] means "walk the whole tree".
471 471 # follow that here, too
472 472 fset.discard('.')
473 473
474 474 for fn in sorted(fset):
475 475 if not self.hasdir(fn):
476 476 match.bad(fn, None)
477 477
478 478 def matches(self, match):
479 479 '''generate a new manifest filtered by the match argument'''
480 480 if match.always():
481 481 return self.copy()
482 482
483 483 if self._filesfastpath(match):
484 484 m = manifestdict()
485 485 lm = self._lm
486 486 for fn in match.files():
487 487 if fn in lm:
488 488 m._lm[fn] = lm[fn]
489 489 return m
490 490
491 491 m = manifestdict()
492 492 m._lm = self._lm.filtercopy(match)
493 493 return m
494 494
495 495 def diff(self, m2, match=None, clean=False):
496 496 '''Finds changes between the current manifest and m2.
497 497
498 498 Args:
499 499 m2: the manifest to which this manifest should be compared.
500 500 clean: if true, include files unchanged between these manifests
501 501 with a None value in the returned dictionary.
502 502
503 503 The result is returned as a dict with filename as key and
504 504 values of the form ((n1,fl1),(n2,fl2)), where n1/n2 is the
505 505 nodeid in the current/other manifest and fl1/fl2 is the flag
506 506 in the current/other manifest. Where the file does not exist,
507 507 the nodeid will be None and the flags will be the empty
508 508 string.
509 509 '''
510 510 if match:
511 511 m1 = self.matches(match)
512 512 m2 = m2.matches(match)
513 513 return m1.diff(m2, clean=clean)
514 514 return self._lm.diff(m2._lm, clean)
515 515
516 516 def setflag(self, key, flag):
517 517 self._lm[key] = self[key], flag
518 518
519 519 def get(self, key, default=None):
520 520 try:
521 521 return self._lm[key][0]
522 522 except KeyError:
523 523 return default
524 524
525 525 def flags(self, key, default=''):
526 526 try:
527 527 return self._lm[key][1]
528 528 except KeyError:
529 529 return default
530 530
531 531 def copy(self):
532 532 c = manifestdict()
533 533 c._lm = self._lm.copy()
534 534 return c
535 535
536 536 def items(self):
537 537 return (x[:2] for x in self._lm.iterentries())
538 538
539 539 def iteritems(self):
540 540 return (x[:2] for x in self._lm.iterentries())
541 541
542 542 def iterentries(self):
543 543 return self._lm.iterentries()
544 544
545 545 def text(self):
546 546 # most likely uses native version
547 547 return self._lm.text()
548 548
549 549 def fastdelta(self, base, changes):
550 550 """Given a base manifest text as a bytearray and a list of changes
551 551 relative to that text, compute a delta that can be used by revlog.
552 552 """
553 553 delta = []
554 554 dstart = None
555 555 dend = None
556 556 dline = [""]
557 557 start = 0
558 558 # zero copy representation of base as a buffer
559 559 addbuf = util.buffer(base)
560 560
561 561 changes = list(changes)
562 562 if len(changes) < 1000:
563 563 # start with a readonly loop that finds the offset of
564 564 # each line and creates the deltas
565 565 for f, todelete in changes:
566 566 # bs will either be the index of the item or the insert point
567 567 start, end = _msearch(addbuf, f, start)
568 568 if not todelete:
569 569 h, fl = self._lm[f]
570 570 l = "%s\0%s%s\n" % (f, hex(h), fl)
571 571 else:
572 572 if start == end:
573 573 # item we want to delete was not found, error out
574 574 raise AssertionError(
575 575 _("failed to remove %s from manifest") % f)
576 576 l = ""
577 577 if dstart is not None and dstart <= start and dend >= start:
578 578 if dend < end:
579 579 dend = end
580 580 if l:
581 581 dline.append(l)
582 582 else:
583 583 if dstart is not None:
584 584 delta.append([dstart, dend, "".join(dline)])
585 585 dstart = start
586 586 dend = end
587 587 dline = [l]
588 588
589 589 if dstart is not None:
590 590 delta.append([dstart, dend, "".join(dline)])
591 591 # apply the delta to the base, and get a delta for addrevision
592 592 deltatext, arraytext = _addlistdelta(base, delta)
593 593 else:
594 594 # For large changes, it's much cheaper to just build the text and
595 595 # diff it.
596 596 arraytext = bytearray(self.text())
597 597 deltatext = mdiff.textdiff(
598 598 util.buffer(base), util.buffer(arraytext))
599 599
600 600 return arraytext, deltatext
601 601
602 602 def _msearch(m, s, lo=0, hi=None):
603 603 '''return a tuple (start, end) that says where to find s within m.
604 604
605 605 If the string is found m[start:end] are the line containing
606 606 that string. If start == end the string was not found and
607 607 they indicate the proper sorted insertion point.
608 608
609 609 m should be a buffer, a memoryview or a byte string.
610 610 s is a byte string'''
611 611 def advance(i, c):
612 612 while i < lenm and m[i:i + 1] != c:
613 613 i += 1
614 614 return i
615 615 if not s:
616 616 return (lo, lo)
617 617 lenm = len(m)
618 618 if not hi:
619 619 hi = lenm
620 620 while lo < hi:
621 621 mid = (lo + hi) // 2
622 622 start = mid
623 623 while start > 0 and m[start - 1:start] != '\n':
624 624 start -= 1
625 625 end = advance(start, '\0')
626 626 if bytes(m[start:end]) < s:
627 627 # we know that after the null there are 40 bytes of sha1
628 628 # this translates to the bisect lo = mid + 1
629 629 lo = advance(end + 40, '\n') + 1
630 630 else:
631 631 # this translates to the bisect hi = mid
632 632 hi = start
633 633 end = advance(lo, '\0')
634 634 found = m[lo:end]
635 635 if s == found:
636 636 # we know that after the null there are 40 bytes of sha1
637 637 end = advance(end + 40, '\n')
638 638 return (lo, end + 1)
639 639 else:
640 640 return (lo, lo)
641 641
642 642 def _checkforbidden(l):
643 643 """Check filenames for illegal characters."""
644 644 for f in l:
645 645 if '\n' in f or '\r' in f:
646 646 raise error.StorageError(
647 647 _("'\\n' and '\\r' disallowed in filenames: %r")
648 648 % pycompat.bytestr(f))
649 649
650 650
651 651 # apply the changes collected during the bisect loop to our addlist
652 652 # return a delta suitable for addrevision
653 653 def _addlistdelta(addlist, x):
654 654 # for large addlist arrays, building a new array is cheaper
655 655 # than repeatedly modifying the existing one
656 656 currentposition = 0
657 657 newaddlist = bytearray()
658 658
659 659 for start, end, content in x:
660 660 newaddlist += addlist[currentposition:start]
661 661 if content:
662 662 newaddlist += bytearray(content)
663 663
664 664 currentposition = end
665 665
666 666 newaddlist += addlist[currentposition:]
667 667
668 668 deltatext = "".join(struct.pack(">lll", start, end, len(content))
669 669 + content for start, end, content in x)
670 670 return deltatext, newaddlist
671 671
672 672 def _splittopdir(f):
673 673 if '/' in f:
674 674 dir, subpath = f.split('/', 1)
675 675 return dir + '/', subpath
676 676 else:
677 677 return '', f
678 678
679 679 _noop = lambda s: None
680 680
681 681 class treemanifest(object):
682 682 def __init__(self, dir='', text=''):
683 683 self._dir = dir
684 684 self._node = nullid
685 685 self._loadfunc = _noop
686 686 self._copyfunc = _noop
687 687 self._dirty = False
688 688 self._dirs = {}
689 689 self._lazydirs = {}
690 690 # Using _lazymanifest here is a little slower than plain old dicts
691 691 self._files = {}
692 692 self._flags = {}
693 693 if text:
694 694 def readsubtree(subdir, subm):
695 695 raise AssertionError('treemanifest constructor only accepts '
696 696 'flat manifests')
697 697 self.parse(text, readsubtree)
698 698 self._dirty = True # Mark flat manifest dirty after parsing
699 699
700 700 def _subpath(self, path):
701 701 return self._dir + path
702 702
703 703 def _loadalllazy(self):
704 704 selfdirs = self._dirs
705 705 for d, (path, node, readsubtree, docopy) in self._lazydirs.iteritems():
706 706 if docopy:
707 707 selfdirs[d] = readsubtree(path, node).copy()
708 708 else:
709 709 selfdirs[d] = readsubtree(path, node)
710 710 self._lazydirs = {}
711 711
712 712 def _loadlazy(self, d):
713 713 v = self._lazydirs.get(d)
714 714 if v:
715 715 path, node, readsubtree, docopy = v
716 716 if docopy:
717 717 self._dirs[d] = readsubtree(path, node).copy()
718 718 else:
719 719 self._dirs[d] = readsubtree(path, node)
720 720 del self._lazydirs[d]
721 721
722 722 def _loadchildrensetlazy(self, visit):
723 723 if not visit:
724 724 return None
725 725 if visit == 'all' or visit == 'this':
726 726 self._loadalllazy()
727 727 return None
728 728
729 729 loadlazy = self._loadlazy
730 730 for k in visit:
731 731 loadlazy(k + '/')
732 732 return visit
733 733
734 734 def _loaddifflazy(self, t1, t2):
735 735 """load items in t1 and t2 if they're needed for diffing.
736 736
737 737 The criteria currently is:
738 738 - if it's not present in _lazydirs in either t1 or t2, load it in the
739 739 other (it may already be loaded or it may not exist, doesn't matter)
740 740 - if it's present in _lazydirs in both, compare the nodeid; if it
741 741 differs, load it in both
742 742 """
743 743 toloadlazy = []
744 744 for d, v1 in t1._lazydirs.iteritems():
745 745 v2 = t2._lazydirs.get(d)
746 746 if not v2 or v2[1] != v1[1]:
747 747 toloadlazy.append(d)
748 748 for d, v1 in t2._lazydirs.iteritems():
749 749 if d not in t1._lazydirs:
750 750 toloadlazy.append(d)
751 751
752 752 for d in toloadlazy:
753 753 t1._loadlazy(d)
754 754 t2._loadlazy(d)
755 755
756 756 def __len__(self):
757 757 self._load()
758 758 size = len(self._files)
759 759 self._loadalllazy()
760 760 for m in self._dirs.values():
761 761 size += m.__len__()
762 762 return size
763 763
764 764 def __nonzero__(self):
765 765 # Faster than "__len() != 0" since it avoids loading sub-manifests
766 766 return not self._isempty()
767 767
768 768 __bool__ = __nonzero__
769 769
770 770 def _isempty(self):
771 771 self._load() # for consistency; already loaded by all callers
772 772 # See if we can skip loading everything.
773 773 if self._files or (self._dirs and
774 774 any(not m._isempty() for m in self._dirs.values())):
775 775 return False
776 776 self._loadalllazy()
777 777 return (not self._dirs or
778 778 all(m._isempty() for m in self._dirs.values()))
779 779
780 780 def __repr__(self):
781 781 return ('<treemanifest dir=%s, node=%s, loaded=%s, dirty=%s at 0x%x>' %
782 782 (self._dir, hex(self._node),
783 783 bool(self._loadfunc is _noop),
784 784 self._dirty, id(self)))
785 785
786 786 def dir(self):
787 787 '''The directory that this tree manifest represents, including a
788 788 trailing '/'. Empty string for the repo root directory.'''
789 789 return self._dir
790 790
791 791 def node(self):
792 792 '''This node of this instance. nullid for unsaved instances. Should
793 793 be updated when the instance is read or written from a revlog.
794 794 '''
795 795 assert not self._dirty
796 796 return self._node
797 797
798 798 def setnode(self, node):
799 799 self._node = node
800 800 self._dirty = False
801 801
802 802 def iterentries(self):
803 803 self._load()
804 804 self._loadalllazy()
805 805 for p, n in sorted(itertools.chain(self._dirs.items(),
806 806 self._files.items())):
807 807 if p in self._files:
808 808 yield self._subpath(p), n, self._flags.get(p, '')
809 809 else:
810 810 for x in n.iterentries():
811 811 yield x
812 812
813 813 def items(self):
814 814 self._load()
815 815 self._loadalllazy()
816 816 for p, n in sorted(itertools.chain(self._dirs.items(),
817 817 self._files.items())):
818 818 if p in self._files:
819 819 yield self._subpath(p), n
820 820 else:
821 821 for f, sn in n.iteritems():
822 822 yield f, sn
823 823
824 824 iteritems = items
825 825
826 826 def iterkeys(self):
827 827 self._load()
828 828 self._loadalllazy()
829 829 for p in sorted(itertools.chain(self._dirs, self._files)):
830 830 if p in self._files:
831 831 yield self._subpath(p)
832 832 else:
833 833 for f in self._dirs[p]:
834 834 yield f
835 835
836 836 def keys(self):
837 837 return list(self.iterkeys())
838 838
839 839 def __iter__(self):
840 840 return self.iterkeys()
841 841
842 842 def __contains__(self, f):
843 843 if f is None:
844 844 return False
845 845 self._load()
846 846 dir, subpath = _splittopdir(f)
847 847 if dir:
848 848 self._loadlazy(dir)
849 849
850 850 if dir not in self._dirs:
851 851 return False
852 852
853 853 return self._dirs[dir].__contains__(subpath)
854 854 else:
855 855 return f in self._files
856 856
857 857 def get(self, f, default=None):
858 858 self._load()
859 859 dir, subpath = _splittopdir(f)
860 860 if dir:
861 861 self._loadlazy(dir)
862 862
863 863 if dir not in self._dirs:
864 864 return default
865 865 return self._dirs[dir].get(subpath, default)
866 866 else:
867 867 return self._files.get(f, default)
868 868
869 869 def __getitem__(self, f):
870 870 self._load()
871 871 dir, subpath = _splittopdir(f)
872 872 if dir:
873 873 self._loadlazy(dir)
874 874
875 875 return self._dirs[dir].__getitem__(subpath)
876 876 else:
877 877 return self._files[f]
878 878
879 879 def flags(self, f):
880 880 self._load()
881 881 dir, subpath = _splittopdir(f)
882 882 if dir:
883 883 self._loadlazy(dir)
884 884
885 885 if dir not in self._dirs:
886 886 return ''
887 887 return self._dirs[dir].flags(subpath)
888 888 else:
889 889 if f in self._lazydirs or f in self._dirs:
890 890 return ''
891 891 return self._flags.get(f, '')
892 892
893 893 def find(self, f):
894 894 self._load()
895 895 dir, subpath = _splittopdir(f)
896 896 if dir:
897 897 self._loadlazy(dir)
898 898
899 899 return self._dirs[dir].find(subpath)
900 900 else:
901 901 return self._files[f], self._flags.get(f, '')
902 902
903 903 def __delitem__(self, f):
904 904 self._load()
905 905 dir, subpath = _splittopdir(f)
906 906 if dir:
907 907 self._loadlazy(dir)
908 908
909 909 self._dirs[dir].__delitem__(subpath)
910 910 # If the directory is now empty, remove it
911 911 if self._dirs[dir]._isempty():
912 912 del self._dirs[dir]
913 913 else:
914 914 del self._files[f]
915 915 if f in self._flags:
916 916 del self._flags[f]
917 917 self._dirty = True
918 918
919 919 def __setitem__(self, f, n):
920 920 assert n is not None
921 921 self._load()
922 922 dir, subpath = _splittopdir(f)
923 923 if dir:
924 924 self._loadlazy(dir)
925 925 if dir not in self._dirs:
926 926 self._dirs[dir] = treemanifest(self._subpath(dir))
927 927 self._dirs[dir].__setitem__(subpath, n)
928 928 else:
929 929 self._files[f] = n[:21] # to match manifestdict's behavior
930 930 self._dirty = True
931 931
932 932 def _load(self):
933 933 if self._loadfunc is not _noop:
934 934 lf, self._loadfunc = self._loadfunc, _noop
935 935 lf(self)
936 936 elif self._copyfunc is not _noop:
937 937 cf, self._copyfunc = self._copyfunc, _noop
938 938 cf(self)
939 939
940 940 def setflag(self, f, flags):
941 941 """Set the flags (symlink, executable) for path f."""
942 942 self._load()
943 943 dir, subpath = _splittopdir(f)
944 944 if dir:
945 945 self._loadlazy(dir)
946 946 if dir not in self._dirs:
947 947 self._dirs[dir] = treemanifest(self._subpath(dir))
948 948 self._dirs[dir].setflag(subpath, flags)
949 949 else:
950 950 self._flags[f] = flags
951 951 self._dirty = True
952 952
953 953 def copy(self):
954 954 copy = treemanifest(self._dir)
955 955 copy._node = self._node
956 956 copy._dirty = self._dirty
957 957 if self._copyfunc is _noop:
958 958 def _copyfunc(s):
959 959 self._load()
960 960 s._lazydirs = {d: (p, n, r, True) for
961 961 d, (p, n, r, c) in self._lazydirs.iteritems()}
962 962 sdirs = s._dirs
963 963 for d, v in self._dirs.iteritems():
964 964 sdirs[d] = v.copy()
965 965 s._files = dict.copy(self._files)
966 966 s._flags = dict.copy(self._flags)
967 967 if self._loadfunc is _noop:
968 968 _copyfunc(copy)
969 969 else:
970 970 copy._copyfunc = _copyfunc
971 971 else:
972 972 copy._copyfunc = self._copyfunc
973 973 return copy
974 974
975 975 def filesnotin(self, m2, match=None):
976 976 '''Set of files in this manifest that are not in the other'''
977 977 if match and not match.always():
978 978 m1 = self.matches(match)
979 979 m2 = m2.matches(match)
980 980 return m1.filesnotin(m2)
981 981
982 982 files = set()
983 983 def _filesnotin(t1, t2):
984 984 if t1._node == t2._node and not t1._dirty and not t2._dirty:
985 985 return
986 986 t1._load()
987 987 t2._load()
988 988 self._loaddifflazy(t1, t2)
989 989 for d, m1 in t1._dirs.iteritems():
990 990 if d in t2._dirs:
991 991 m2 = t2._dirs[d]
992 992 _filesnotin(m1, m2)
993 993 else:
994 994 files.update(m1.iterkeys())
995 995
996 996 for fn in t1._files:
997 997 if fn not in t2._files:
998 998 files.add(t1._subpath(fn))
999 999
1000 1000 _filesnotin(self, m2)
1001 1001 return files
1002 1002
1003 1003 @propertycache
1004 1004 def _alldirs(self):
1005 1005 return util.dirs(self)
1006 1006
1007 1007 def dirs(self):
1008 1008 return self._alldirs
1009 1009
1010 1010 def hasdir(self, dir):
1011 1011 self._load()
1012 1012 topdir, subdir = _splittopdir(dir)
1013 1013 if topdir:
1014 1014 self._loadlazy(topdir)
1015 1015 if topdir in self._dirs:
1016 1016 return self._dirs[topdir].hasdir(subdir)
1017 1017 return False
1018 1018 dirslash = dir + '/'
1019 1019 return dirslash in self._dirs or dirslash in self._lazydirs
1020 1020
1021 1021 def walk(self, match):
1022 1022 '''Generates matching file names.
1023 1023
1024 1024 Equivalent to manifest.matches(match).iterkeys(), but without creating
1025 1025 an entirely new manifest.
1026 1026
1027 1027 It also reports nonexistent files by marking them bad with match.bad().
1028 1028 '''
1029 1029 if match.always():
1030 1030 for f in iter(self):
1031 1031 yield f
1032 1032 return
1033 1033
1034 1034 fset = set(match.files())
1035 1035
1036 1036 for fn in self._walk(match):
1037 1037 if fn in fset:
1038 1038 # specified pattern is the exact name
1039 1039 fset.remove(fn)
1040 1040 yield fn
1041 1041
1042 1042 # for dirstate.walk, files=['.'] means "walk the whole tree".
1043 1043 # follow that here, too
1044 1044 fset.discard('.')
1045 1045
1046 1046 for fn in sorted(fset):
1047 1047 if not self.hasdir(fn):
1048 1048 match.bad(fn, None)
1049 1049
1050 1050 def _walk(self, match):
1051 1051 '''Recursively generates matching file names for walk().'''
1052 1052 visit = match.visitchildrenset(self._dir[:-1] or '.')
1053 1053 if not visit:
1054 1054 return
1055 1055
1056 1056 # yield this dir's files and walk its submanifests
1057 1057 self._load()
1058 1058 visit = self._loadchildrensetlazy(visit)
1059 1059 for p in sorted(list(self._dirs) + list(self._files)):
1060 1060 if p in self._files:
1061 1061 fullp = self._subpath(p)
1062 1062 if match(fullp):
1063 1063 yield fullp
1064 1064 else:
1065 1065 if not visit or p[:-1] in visit:
1066 1066 for f in self._dirs[p]._walk(match):
1067 1067 yield f
1068 1068
1069 1069 def matches(self, match):
1070 1070 '''generate a new manifest filtered by the match argument'''
1071 1071 if match.always():
1072 1072 return self.copy()
1073 1073
1074 1074 return self._matches(match)
1075 1075
1076 1076 def _matches(self, match):
1077 1077 '''recursively generate a new manifest filtered by the match argument.
1078 1078 '''
1079 1079
1080 1080 visit = match.visitchildrenset(self._dir[:-1] or '.')
1081 1081 if visit == 'all':
1082 1082 return self.copy()
1083 1083 ret = treemanifest(self._dir)
1084 1084 if not visit:
1085 1085 return ret
1086 1086
1087 1087 self._load()
1088 1088 for fn in self._files:
1089 1089 # While visitchildrenset *usually* lists only subdirs, this is
1090 1090 # actually up to the matcher and may have some files in the set().
1091 1091 # If visit == 'this', we should obviously look at the files in this
1092 1092 # directory; if visit is a set, and fn is in it, we should inspect
1093 1093 # fn (but no need to inspect things not in the set).
1094 1094 if visit != 'this' and fn not in visit:
1095 1095 continue
1096 1096 fullp = self._subpath(fn)
1097 1097 # visitchildrenset isn't perfect, we still need to call the regular
1098 1098 # matcher code to further filter results.
1099 1099 if not match(fullp):
1100 1100 continue
1101 1101 ret._files[fn] = self._files[fn]
1102 1102 if fn in self._flags:
1103 1103 ret._flags[fn] = self._flags[fn]
1104 1104
1105 1105 visit = self._loadchildrensetlazy(visit)
1106 1106 for dir, subm in self._dirs.iteritems():
1107 1107 if visit and dir[:-1] not in visit:
1108 1108 continue
1109 1109 m = subm._matches(match)
1110 1110 if not m._isempty():
1111 1111 ret._dirs[dir] = m
1112 1112
1113 1113 if not ret._isempty():
1114 1114 ret._dirty = True
1115 1115 return ret
1116 1116
1117 1117 def diff(self, m2, match=None, clean=False):
1118 1118 '''Finds changes between the current manifest and m2.
1119 1119
1120 1120 Args:
1121 1121 m2: the manifest to which this manifest should be compared.
1122 1122 clean: if true, include files unchanged between these manifests
1123 1123 with a None value in the returned dictionary.
1124 1124
1125 1125 The result is returned as a dict with filename as key and
1126 1126 values of the form ((n1,fl1),(n2,fl2)), where n1/n2 is the
1127 1127 nodeid in the current/other manifest and fl1/fl2 is the flag
1128 1128 in the current/other manifest. Where the file does not exist,
1129 1129 the nodeid will be None and the flags will be the empty
1130 1130 string.
1131 1131 '''
1132 1132 if match and not match.always():
1133 1133 m1 = self.matches(match)
1134 1134 m2 = m2.matches(match)
1135 1135 return m1.diff(m2, clean=clean)
1136 1136 result = {}
1137 1137 emptytree = treemanifest()
1138 def _diff(t1, t2):
1138
1139 def _iterativediff(t1, t2, stack):
1140 """compares two tree manifests and append new tree-manifests which
1141 needs to be compared to stack"""
1139 1142 if t1._node == t2._node and not t1._dirty and not t2._dirty:
1140 1143 return
1141 1144 t1._load()
1142 1145 t2._load()
1143 1146 self._loaddifflazy(t1, t2)
1144 1147
1145 1148 for d, m1 in t1._dirs.iteritems():
1146 1149 m2 = t2._dirs.get(d, emptytree)
1147 _diff(m1, m2)
1150 stack.append((m1, m2))
1148 1151
1149 1152 for d, m2 in t2._dirs.iteritems():
1150 1153 if d not in t1._dirs:
1151 _diff(emptytree, m2)
1154 stack.append((emptytree, m2))
1152 1155
1153 1156 for fn, n1 in t1._files.iteritems():
1154 1157 fl1 = t1._flags.get(fn, '')
1155 1158 n2 = t2._files.get(fn, None)
1156 1159 fl2 = t2._flags.get(fn, '')
1157 1160 if n1 != n2 or fl1 != fl2:
1158 1161 result[t1._subpath(fn)] = ((n1, fl1), (n2, fl2))
1159 1162 elif clean:
1160 1163 result[t1._subpath(fn)] = None
1161 1164
1162 1165 for fn, n2 in t2._files.iteritems():
1163 1166 if fn not in t1._files:
1164 1167 fl2 = t2._flags.get(fn, '')
1165 1168 result[t2._subpath(fn)] = ((None, ''), (n2, fl2))
1166 1169
1167 _diff(self, m2)
1170 stackls = []
1171 _iterativediff(self, m2, stackls)
1172 while stackls:
1173 t1, t2 = stackls.pop()
1174 # stackls is populated in the function call
1175 _iterativediff(t1, t2, stackls)
1168 1176 return result
1169 1177
1170 1178 def unmodifiedsince(self, m2):
1171 1179 return not self._dirty and not m2._dirty and self._node == m2._node
1172 1180
1173 1181 def parse(self, text, readsubtree):
1174 1182 selflazy = self._lazydirs
1175 1183 subpath = self._subpath
1176 1184 for f, n, fl in _parse(text):
1177 1185 if fl == 't':
1178 1186 f = f + '/'
1179 1187 # False below means "doesn't need to be copied" and can use the
1180 1188 # cached value from readsubtree directly.
1181 1189 selflazy[f] = (subpath(f), n, readsubtree, False)
1182 1190 elif '/' in f:
1183 1191 # This is a flat manifest, so use __setitem__ and setflag rather
1184 1192 # than assigning directly to _files and _flags, so we can
1185 1193 # assign a path in a subdirectory, and to mark dirty (compared
1186 1194 # to nullid).
1187 1195 self[f] = n
1188 1196 if fl:
1189 1197 self.setflag(f, fl)
1190 1198 else:
1191 1199 # Assigning to _files and _flags avoids marking as dirty,
1192 1200 # and should be a little faster.
1193 1201 self._files[f] = n
1194 1202 if fl:
1195 1203 self._flags[f] = fl
1196 1204
1197 1205 def text(self):
1198 1206 """Get the full data of this manifest as a bytestring."""
1199 1207 self._load()
1200 1208 return _text(self.iterentries())
1201 1209
1202 1210 def dirtext(self):
1203 1211 """Get the full data of this directory as a bytestring. Make sure that
1204 1212 any submanifests have been written first, so their nodeids are correct.
1205 1213 """
1206 1214 self._load()
1207 1215 flags = self.flags
1208 1216 lazydirs = [(d[:-1], v[1], 't') for d, v in self._lazydirs.iteritems()]
1209 1217 dirs = [(d[:-1], self._dirs[d]._node, 't') for d in self._dirs]
1210 1218 files = [(f, self._files[f], flags(f)) for f in self._files]
1211 1219 return _text(sorted(dirs + files + lazydirs))
1212 1220
1213 1221 def read(self, gettext, readsubtree):
1214 1222 def _load_for_read(s):
1215 1223 s.parse(gettext(), readsubtree)
1216 1224 s._dirty = False
1217 1225 self._loadfunc = _load_for_read
1218 1226
1219 1227 def writesubtrees(self, m1, m2, writesubtree, match):
1220 1228 self._load() # for consistency; should never have any effect here
1221 1229 m1._load()
1222 1230 m2._load()
1223 1231 emptytree = treemanifest()
1224 1232 def getnode(m, d):
1225 1233 ld = m._lazydirs.get(d)
1226 1234 if ld:
1227 1235 return ld[1]
1228 1236 return m._dirs.get(d, emptytree)._node
1229 1237
1230 1238 # let's skip investigating things that `match` says we do not need.
1231 1239 visit = match.visitchildrenset(self._dir[:-1] or '.')
1232 1240 visit = self._loadchildrensetlazy(visit)
1233 1241 if visit == 'this' or visit == 'all':
1234 1242 visit = None
1235 1243 for d, subm in self._dirs.iteritems():
1236 1244 if visit and d[:-1] not in visit:
1237 1245 continue
1238 1246 subp1 = getnode(m1, d)
1239 1247 subp2 = getnode(m2, d)
1240 1248 if subp1 == nullid:
1241 1249 subp1, subp2 = subp2, subp1
1242 1250 writesubtree(subm, subp1, subp2, match)
1243 1251
1244 1252 def walksubtrees(self, matcher=None):
1245 1253 """Returns an iterator of the subtrees of this manifest, including this
1246 1254 manifest itself.
1247 1255
1248 1256 If `matcher` is provided, it only returns subtrees that match.
1249 1257 """
1250 1258 if matcher and not matcher.visitdir(self._dir[:-1] or '.'):
1251 1259 return
1252 1260 if not matcher or matcher(self._dir[:-1]):
1253 1261 yield self
1254 1262
1255 1263 self._load()
1256 1264 # OPT: use visitchildrenset to avoid loading everything.
1257 1265 self._loadalllazy()
1258 1266 for d, subm in self._dirs.iteritems():
1259 1267 for subtree in subm.walksubtrees(matcher=matcher):
1260 1268 yield subtree
1261 1269
1262 1270 class manifestfulltextcache(util.lrucachedict):
1263 1271 """File-backed LRU cache for the manifest cache
1264 1272
1265 1273 File consists of entries, up to EOF:
1266 1274
1267 1275 - 20 bytes node, 4 bytes length, <length> manifest data
1268 1276
1269 1277 These are written in reverse cache order (oldest to newest).
1270 1278
1271 1279 """
1272 1280 def __init__(self, max):
1273 1281 super(manifestfulltextcache, self).__init__(max)
1274 1282 self._dirty = False
1275 1283 self._read = False
1276 1284 self._opener = None
1277 1285
1278 1286 def read(self):
1279 1287 if self._read or self._opener is None:
1280 1288 return
1281 1289
1282 1290 try:
1283 1291 with self._opener('manifestfulltextcache') as fp:
1284 1292 set = super(manifestfulltextcache, self).__setitem__
1285 1293 # ignore trailing data, this is a cache, corruption is skipped
1286 1294 while True:
1287 1295 node = fp.read(20)
1288 1296 if len(node) < 20:
1289 1297 break
1290 1298 try:
1291 1299 size = struct.unpack('>L', fp.read(4))[0]
1292 1300 except struct.error:
1293 1301 break
1294 1302 value = bytearray(fp.read(size))
1295 1303 if len(value) != size:
1296 1304 break
1297 1305 set(node, value)
1298 1306 except IOError:
1299 1307 # the file is allowed to be missing
1300 1308 pass
1301 1309
1302 1310 self._read = True
1303 1311 self._dirty = False
1304 1312
1305 1313 def write(self):
1306 1314 if not self._dirty or self._opener is None:
1307 1315 return
1308 1316 # rotate backwards to the first used node
1309 1317 with self._opener(
1310 1318 'manifestfulltextcache', 'w', atomictemp=True, checkambig=True
1311 1319 ) as fp:
1312 1320 node = self._head.prev
1313 1321 while True:
1314 1322 if node.key in self._cache:
1315 1323 fp.write(node.key)
1316 1324 fp.write(struct.pack('>L', len(node.value)))
1317 1325 fp.write(node.value)
1318 1326 if node is self._head:
1319 1327 break
1320 1328 node = node.prev
1321 1329
1322 1330 def __len__(self):
1323 1331 if not self._read:
1324 1332 self.read()
1325 1333 return super(manifestfulltextcache, self).__len__()
1326 1334
1327 1335 def __contains__(self, k):
1328 1336 if not self._read:
1329 1337 self.read()
1330 1338 return super(manifestfulltextcache, self).__contains__(k)
1331 1339
1332 1340 def __iter__(self):
1333 1341 if not self._read:
1334 1342 self.read()
1335 1343 return super(manifestfulltextcache, self).__iter__()
1336 1344
1337 1345 def __getitem__(self, k):
1338 1346 if not self._read:
1339 1347 self.read()
1340 1348 # the cache lru order can change on read
1341 1349 setdirty = self._cache.get(k) is not self._head
1342 1350 value = super(manifestfulltextcache, self).__getitem__(k)
1343 1351 if setdirty:
1344 1352 self._dirty = True
1345 1353 return value
1346 1354
1347 1355 def __setitem__(self, k, v):
1348 1356 if not self._read:
1349 1357 self.read()
1350 1358 super(manifestfulltextcache, self).__setitem__(k, v)
1351 1359 self._dirty = True
1352 1360
1353 1361 def __delitem__(self, k):
1354 1362 if not self._read:
1355 1363 self.read()
1356 1364 super(manifestfulltextcache, self).__delitem__(k)
1357 1365 self._dirty = True
1358 1366
1359 1367 def get(self, k, default=None):
1360 1368 if not self._read:
1361 1369 self.read()
1362 1370 return super(manifestfulltextcache, self).get(k, default=default)
1363 1371
1364 1372 def clear(self, clear_persisted_data=False):
1365 1373 super(manifestfulltextcache, self).clear()
1366 1374 if clear_persisted_data:
1367 1375 self._dirty = True
1368 1376 self.write()
1369 1377 self._read = False
1370 1378
1371 1379 @interfaceutil.implementer(repository.imanifeststorage)
1372 1380 class manifestrevlog(object):
1373 1381 '''A revlog that stores manifest texts. This is responsible for caching the
1374 1382 full-text manifest contents.
1375 1383 '''
1376 1384 def __init__(self, opener, tree='', dirlogcache=None, indexfile=None,
1377 1385 treemanifest=False):
1378 1386 """Constructs a new manifest revlog
1379 1387
1380 1388 `indexfile` - used by extensions to have two manifests at once, like
1381 1389 when transitioning between flatmanifeset and treemanifests.
1382 1390
1383 1391 `treemanifest` - used to indicate this is a tree manifest revlog. Opener
1384 1392 options can also be used to make this a tree manifest revlog. The opener
1385 1393 option takes precedence, so if it is set to True, we ignore whatever
1386 1394 value is passed in to the constructor.
1387 1395 """
1388 1396 # During normal operations, we expect to deal with not more than four
1389 1397 # revs at a time (such as during commit --amend). When rebasing large
1390 1398 # stacks of commits, the number can go up, hence the config knob below.
1391 1399 cachesize = 4
1392 1400 optiontreemanifest = False
1393 1401 opts = getattr(opener, 'options', None)
1394 1402 if opts is not None:
1395 1403 cachesize = opts.get('manifestcachesize', cachesize)
1396 1404 optiontreemanifest = opts.get('treemanifest', False)
1397 1405
1398 1406 self._treeondisk = optiontreemanifest or treemanifest
1399 1407
1400 1408 self._fulltextcache = manifestfulltextcache(cachesize)
1401 1409
1402 1410 if tree:
1403 1411 assert self._treeondisk, 'opts is %r' % opts
1404 1412
1405 1413 if indexfile is None:
1406 1414 indexfile = '00manifest.i'
1407 1415 if tree:
1408 1416 indexfile = "meta/" + tree + indexfile
1409 1417
1410 1418 self.tree = tree
1411 1419
1412 1420 # The dirlogcache is kept on the root manifest log
1413 1421 if tree:
1414 1422 self._dirlogcache = dirlogcache
1415 1423 else:
1416 1424 self._dirlogcache = {'': self}
1417 1425
1418 1426 self._revlog = revlog.revlog(opener, indexfile,
1419 1427 # only root indexfile is cached
1420 1428 checkambig=not bool(tree),
1421 1429 mmaplargeindex=True)
1422 1430
1423 1431 self.index = self._revlog.index
1424 1432 self.version = self._revlog.version
1425 1433 self._generaldelta = self._revlog._generaldelta
1426 1434
1427 1435 def _setupmanifestcachehooks(self, repo):
1428 1436 """Persist the manifestfulltextcache on lock release"""
1429 1437 if not util.safehasattr(repo, '_lockref'):
1430 1438 return
1431 1439
1432 1440 self._fulltextcache._opener = repo.cachevfs
1433 1441 reporef = weakref.ref(repo)
1434 1442 manifestrevlogref = weakref.ref(self)
1435 1443
1436 1444 def persistmanifestcache():
1437 1445 repo = reporef()
1438 1446 self = manifestrevlogref()
1439 1447 if repo is None or self is None:
1440 1448 return
1441 1449 if repo.manifestlog.getstorage(b'') is not self:
1442 1450 # there's a different manifest in play now, abort
1443 1451 return
1444 1452 self._fulltextcache.write()
1445 1453
1446 1454 if repo._currentlock(repo._lockref) is not None:
1447 1455 repo._afterlock(persistmanifestcache)
1448 1456
1449 1457 @property
1450 1458 def fulltextcache(self):
1451 1459 return self._fulltextcache
1452 1460
1453 1461 def clearcaches(self, clear_persisted_data=False):
1454 1462 self._revlog.clearcaches()
1455 1463 self._fulltextcache.clear(clear_persisted_data=clear_persisted_data)
1456 1464 self._dirlogcache = {self.tree: self}
1457 1465
1458 1466 def dirlog(self, d):
1459 1467 if d:
1460 1468 assert self._treeondisk
1461 1469 if d not in self._dirlogcache:
1462 1470 mfrevlog = manifestrevlog(self.opener, d,
1463 1471 self._dirlogcache,
1464 1472 treemanifest=self._treeondisk)
1465 1473 self._dirlogcache[d] = mfrevlog
1466 1474 return self._dirlogcache[d]
1467 1475
1468 1476 def add(self, m, transaction, link, p1, p2, added, removed, readtree=None,
1469 1477 match=None):
1470 1478 if p1 in self.fulltextcache and util.safehasattr(m, 'fastdelta'):
1471 1479 # If our first parent is in the manifest cache, we can
1472 1480 # compute a delta here using properties we know about the
1473 1481 # manifest up-front, which may save time later for the
1474 1482 # revlog layer.
1475 1483
1476 1484 _checkforbidden(added)
1477 1485 # combine the changed lists into one sorted iterator
1478 1486 work = heapq.merge([(x, False) for x in added],
1479 1487 [(x, True) for x in removed])
1480 1488
1481 1489 arraytext, deltatext = m.fastdelta(self.fulltextcache[p1], work)
1482 1490 cachedelta = self._revlog.rev(p1), deltatext
1483 1491 text = util.buffer(arraytext)
1484 1492 n = self._revlog.addrevision(text, transaction, link, p1, p2,
1485 1493 cachedelta)
1486 1494 else:
1487 1495 # The first parent manifest isn't already loaded, so we'll
1488 1496 # just encode a fulltext of the manifest and pass that
1489 1497 # through to the revlog layer, and let it handle the delta
1490 1498 # process.
1491 1499 if self._treeondisk:
1492 1500 assert readtree, "readtree must be set for treemanifest writes"
1493 1501 assert match, "match must be specified for treemanifest writes"
1494 1502 m1 = readtree(self.tree, p1)
1495 1503 m2 = readtree(self.tree, p2)
1496 1504 n = self._addtree(m, transaction, link, m1, m2, readtree,
1497 1505 match=match)
1498 1506 arraytext = None
1499 1507 else:
1500 1508 text = m.text()
1501 1509 n = self._revlog.addrevision(text, transaction, link, p1, p2)
1502 1510 arraytext = bytearray(text)
1503 1511
1504 1512 if arraytext is not None:
1505 1513 self.fulltextcache[n] = arraytext
1506 1514
1507 1515 return n
1508 1516
1509 1517 def _addtree(self, m, transaction, link, m1, m2, readtree, match):
1510 1518 # If the manifest is unchanged compared to one parent,
1511 1519 # don't write a new revision
1512 1520 if self.tree != '' and (m.unmodifiedsince(m1) or m.unmodifiedsince(
1513 1521 m2)):
1514 1522 return m.node()
1515 1523 def writesubtree(subm, subp1, subp2, match):
1516 1524 sublog = self.dirlog(subm.dir())
1517 1525 sublog.add(subm, transaction, link, subp1, subp2, None, None,
1518 1526 readtree=readtree, match=match)
1519 1527 m.writesubtrees(m1, m2, writesubtree, match)
1520 1528 text = m.dirtext()
1521 1529 n = None
1522 1530 if self.tree != '':
1523 1531 # Double-check whether contents are unchanged to one parent
1524 1532 if text == m1.dirtext():
1525 1533 n = m1.node()
1526 1534 elif text == m2.dirtext():
1527 1535 n = m2.node()
1528 1536
1529 1537 if not n:
1530 1538 n = self._revlog.addrevision(text, transaction, link, m1.node(),
1531 1539 m2.node())
1532 1540
1533 1541 # Save nodeid so parent manifest can calculate its nodeid
1534 1542 m.setnode(n)
1535 1543 return n
1536 1544
1537 1545 def __len__(self):
1538 1546 return len(self._revlog)
1539 1547
1540 1548 def __iter__(self):
1541 1549 return self._revlog.__iter__()
1542 1550
1543 1551 def rev(self, node):
1544 1552 return self._revlog.rev(node)
1545 1553
1546 1554 def node(self, rev):
1547 1555 return self._revlog.node(rev)
1548 1556
1549 1557 def lookup(self, value):
1550 1558 return self._revlog.lookup(value)
1551 1559
1552 1560 def parentrevs(self, rev):
1553 1561 return self._revlog.parentrevs(rev)
1554 1562
1555 1563 def parents(self, node):
1556 1564 return self._revlog.parents(node)
1557 1565
1558 1566 def linkrev(self, rev):
1559 1567 return self._revlog.linkrev(rev)
1560 1568
1561 1569 def checksize(self):
1562 1570 return self._revlog.checksize()
1563 1571
1564 1572 def revision(self, node, _df=None, raw=False):
1565 1573 return self._revlog.revision(node, _df=_df, raw=raw)
1566 1574
1567 1575 def revdiff(self, rev1, rev2):
1568 1576 return self._revlog.revdiff(rev1, rev2)
1569 1577
1570 1578 def cmp(self, node, text):
1571 1579 return self._revlog.cmp(node, text)
1572 1580
1573 1581 def deltaparent(self, rev):
1574 1582 return self._revlog.deltaparent(rev)
1575 1583
1576 1584 def emitrevisions(self, nodes, nodesorder=None,
1577 1585 revisiondata=False, assumehaveparentrevisions=False,
1578 1586 deltamode=repository.CG_DELTAMODE_STD):
1579 1587 return self._revlog.emitrevisions(
1580 1588 nodes, nodesorder=nodesorder, revisiondata=revisiondata,
1581 1589 assumehaveparentrevisions=assumehaveparentrevisions,
1582 1590 deltamode=deltamode)
1583 1591
1584 1592 def addgroup(self, deltas, linkmapper, transaction, addrevisioncb=None):
1585 1593 return self._revlog.addgroup(deltas, linkmapper, transaction,
1586 1594 addrevisioncb=addrevisioncb)
1587 1595
1588 1596 def rawsize(self, rev):
1589 1597 return self._revlog.rawsize(rev)
1590 1598
1591 1599 def getstrippoint(self, minlink):
1592 1600 return self._revlog.getstrippoint(minlink)
1593 1601
1594 1602 def strip(self, minlink, transaction):
1595 1603 return self._revlog.strip(minlink, transaction)
1596 1604
1597 1605 def files(self):
1598 1606 return self._revlog.files()
1599 1607
1600 1608 def clone(self, tr, destrevlog, **kwargs):
1601 1609 if not isinstance(destrevlog, manifestrevlog):
1602 1610 raise error.ProgrammingError('expected manifestrevlog to clone()')
1603 1611
1604 1612 return self._revlog.clone(tr, destrevlog._revlog, **kwargs)
1605 1613
1606 1614 def storageinfo(self, exclusivefiles=False, sharedfiles=False,
1607 1615 revisionscount=False, trackedsize=False,
1608 1616 storedsize=False):
1609 1617 return self._revlog.storageinfo(
1610 1618 exclusivefiles=exclusivefiles, sharedfiles=sharedfiles,
1611 1619 revisionscount=revisionscount, trackedsize=trackedsize,
1612 1620 storedsize=storedsize)
1613 1621
1614 1622 @property
1615 1623 def indexfile(self):
1616 1624 return self._revlog.indexfile
1617 1625
1618 1626 @indexfile.setter
1619 1627 def indexfile(self, value):
1620 1628 self._revlog.indexfile = value
1621 1629
1622 1630 @property
1623 1631 def opener(self):
1624 1632 return self._revlog.opener
1625 1633
1626 1634 @opener.setter
1627 1635 def opener(self, value):
1628 1636 self._revlog.opener = value
1629 1637
1630 1638 @interfaceutil.implementer(repository.imanifestlog)
1631 1639 class manifestlog(object):
1632 1640 """A collection class representing the collection of manifest snapshots
1633 1641 referenced by commits in the repository.
1634 1642
1635 1643 In this situation, 'manifest' refers to the abstract concept of a snapshot
1636 1644 of the list of files in the given commit. Consumers of the output of this
1637 1645 class do not care about the implementation details of the actual manifests
1638 1646 they receive (i.e. tree or flat or lazily loaded, etc)."""
1639 1647 def __init__(self, opener, repo, rootstore, narrowmatch):
1640 1648 usetreemanifest = False
1641 1649 cachesize = 4
1642 1650
1643 1651 opts = getattr(opener, 'options', None)
1644 1652 if opts is not None:
1645 1653 usetreemanifest = opts.get('treemanifest', usetreemanifest)
1646 1654 cachesize = opts.get('manifestcachesize', cachesize)
1647 1655
1648 1656 self._treemanifests = usetreemanifest
1649 1657
1650 1658 self._rootstore = rootstore
1651 1659 self._rootstore._setupmanifestcachehooks(repo)
1652 1660 self._narrowmatch = narrowmatch
1653 1661
1654 1662 # A cache of the manifestctx or treemanifestctx for each directory
1655 1663 self._dirmancache = {}
1656 1664 self._dirmancache[''] = util.lrucachedict(cachesize)
1657 1665
1658 1666 self._cachesize = cachesize
1659 1667
1660 1668 def __getitem__(self, node):
1661 1669 """Retrieves the manifest instance for the given node. Throws a
1662 1670 LookupError if not found.
1663 1671 """
1664 1672 return self.get('', node)
1665 1673
1666 1674 def get(self, tree, node, verify=True):
1667 1675 """Retrieves the manifest instance for the given node. Throws a
1668 1676 LookupError if not found.
1669 1677
1670 1678 `verify` - if True an exception will be thrown if the node is not in
1671 1679 the revlog
1672 1680 """
1673 1681 if node in self._dirmancache.get(tree, ()):
1674 1682 return self._dirmancache[tree][node]
1675 1683
1676 1684 if not self._narrowmatch.always():
1677 1685 if not self._narrowmatch.visitdir(tree[:-1] or '.'):
1678 1686 return excludeddirmanifestctx(tree, node)
1679 1687 if tree:
1680 1688 if self._rootstore._treeondisk:
1681 1689 if verify:
1682 1690 # Side-effect is LookupError is raised if node doesn't
1683 1691 # exist.
1684 1692 self.getstorage(tree).rev(node)
1685 1693
1686 1694 m = treemanifestctx(self, tree, node)
1687 1695 else:
1688 1696 raise error.Abort(
1689 1697 _("cannot ask for manifest directory '%s' in a flat "
1690 1698 "manifest") % tree)
1691 1699 else:
1692 1700 if verify:
1693 1701 # Side-effect is LookupError is raised if node doesn't exist.
1694 1702 self._rootstore.rev(node)
1695 1703
1696 1704 if self._treemanifests:
1697 1705 m = treemanifestctx(self, '', node)
1698 1706 else:
1699 1707 m = manifestctx(self, node)
1700 1708
1701 1709 if node != nullid:
1702 1710 mancache = self._dirmancache.get(tree)
1703 1711 if not mancache:
1704 1712 mancache = util.lrucachedict(self._cachesize)
1705 1713 self._dirmancache[tree] = mancache
1706 1714 mancache[node] = m
1707 1715 return m
1708 1716
1709 1717 def getstorage(self, tree):
1710 1718 return self._rootstore.dirlog(tree)
1711 1719
1712 1720 def clearcaches(self, clear_persisted_data=False):
1713 1721 self._dirmancache.clear()
1714 1722 self._rootstore.clearcaches(clear_persisted_data=clear_persisted_data)
1715 1723
1716 1724 def rev(self, node):
1717 1725 return self._rootstore.rev(node)
1718 1726
1719 1727 @interfaceutil.implementer(repository.imanifestrevisionwritable)
1720 1728 class memmanifestctx(object):
1721 1729 def __init__(self, manifestlog):
1722 1730 self._manifestlog = manifestlog
1723 1731 self._manifestdict = manifestdict()
1724 1732
1725 1733 def _storage(self):
1726 1734 return self._manifestlog.getstorage(b'')
1727 1735
1728 1736 def new(self):
1729 1737 return memmanifestctx(self._manifestlog)
1730 1738
1731 1739 def copy(self):
1732 1740 memmf = memmanifestctx(self._manifestlog)
1733 1741 memmf._manifestdict = self.read().copy()
1734 1742 return memmf
1735 1743
1736 1744 def read(self):
1737 1745 return self._manifestdict
1738 1746
1739 1747 def write(self, transaction, link, p1, p2, added, removed, match=None):
1740 1748 return self._storage().add(self._manifestdict, transaction, link,
1741 1749 p1, p2, added, removed, match=match)
1742 1750
1743 1751 @interfaceutil.implementer(repository.imanifestrevisionstored)
1744 1752 class manifestctx(object):
1745 1753 """A class representing a single revision of a manifest, including its
1746 1754 contents, its parent revs, and its linkrev.
1747 1755 """
1748 1756 def __init__(self, manifestlog, node):
1749 1757 self._manifestlog = manifestlog
1750 1758 self._data = None
1751 1759
1752 1760 self._node = node
1753 1761
1754 1762 # TODO: We eventually want p1, p2, and linkrev exposed on this class,
1755 1763 # but let's add it later when something needs it and we can load it
1756 1764 # lazily.
1757 1765 #self.p1, self.p2 = store.parents(node)
1758 1766 #rev = store.rev(node)
1759 1767 #self.linkrev = store.linkrev(rev)
1760 1768
1761 1769 def _storage(self):
1762 1770 return self._manifestlog.getstorage(b'')
1763 1771
1764 1772 def node(self):
1765 1773 return self._node
1766 1774
1767 1775 def new(self):
1768 1776 return memmanifestctx(self._manifestlog)
1769 1777
1770 1778 def copy(self):
1771 1779 memmf = memmanifestctx(self._manifestlog)
1772 1780 memmf._manifestdict = self.read().copy()
1773 1781 return memmf
1774 1782
1775 1783 @propertycache
1776 1784 def parents(self):
1777 1785 return self._storage().parents(self._node)
1778 1786
1779 1787 def read(self):
1780 1788 if self._data is None:
1781 1789 if self._node == nullid:
1782 1790 self._data = manifestdict()
1783 1791 else:
1784 1792 store = self._storage()
1785 1793 if self._node in store.fulltextcache:
1786 1794 text = pycompat.bytestr(store.fulltextcache[self._node])
1787 1795 else:
1788 1796 text = store.revision(self._node)
1789 1797 arraytext = bytearray(text)
1790 1798 store.fulltextcache[self._node] = arraytext
1791 1799 self._data = manifestdict(text)
1792 1800 return self._data
1793 1801
1794 1802 def readfast(self, shallow=False):
1795 1803 '''Calls either readdelta or read, based on which would be less work.
1796 1804 readdelta is called if the delta is against the p1, and therefore can be
1797 1805 read quickly.
1798 1806
1799 1807 If `shallow` is True, nothing changes since this is a flat manifest.
1800 1808 '''
1801 1809 store = self._storage()
1802 1810 r = store.rev(self._node)
1803 1811 deltaparent = store.deltaparent(r)
1804 1812 if deltaparent != nullrev and deltaparent in store.parentrevs(r):
1805 1813 return self.readdelta()
1806 1814 return self.read()
1807 1815
1808 1816 def readdelta(self, shallow=False):
1809 1817 '''Returns a manifest containing just the entries that are present
1810 1818 in this manifest, but not in its p1 manifest. This is efficient to read
1811 1819 if the revlog delta is already p1.
1812 1820
1813 1821 Changing the value of `shallow` has no effect on flat manifests.
1814 1822 '''
1815 1823 store = self._storage()
1816 1824 r = store.rev(self._node)
1817 1825 d = mdiff.patchtext(store.revdiff(store.deltaparent(r), r))
1818 1826 return manifestdict(d)
1819 1827
1820 1828 def find(self, key):
1821 1829 return self.read().find(key)
1822 1830
1823 1831 @interfaceutil.implementer(repository.imanifestrevisionwritable)
1824 1832 class memtreemanifestctx(object):
1825 1833 def __init__(self, manifestlog, dir=''):
1826 1834 self._manifestlog = manifestlog
1827 1835 self._dir = dir
1828 1836 self._treemanifest = treemanifest()
1829 1837
1830 1838 def _storage(self):
1831 1839 return self._manifestlog.getstorage(b'')
1832 1840
1833 1841 def new(self, dir=''):
1834 1842 return memtreemanifestctx(self._manifestlog, dir=dir)
1835 1843
1836 1844 def copy(self):
1837 1845 memmf = memtreemanifestctx(self._manifestlog, dir=self._dir)
1838 1846 memmf._treemanifest = self._treemanifest.copy()
1839 1847 return memmf
1840 1848
1841 1849 def read(self):
1842 1850 return self._treemanifest
1843 1851
1844 1852 def write(self, transaction, link, p1, p2, added, removed, match=None):
1845 1853 def readtree(dir, node):
1846 1854 return self._manifestlog.get(dir, node).read()
1847 1855 return self._storage().add(self._treemanifest, transaction, link,
1848 1856 p1, p2, added, removed, readtree=readtree,
1849 1857 match=match)
1850 1858
1851 1859 @interfaceutil.implementer(repository.imanifestrevisionstored)
1852 1860 class treemanifestctx(object):
1853 1861 def __init__(self, manifestlog, dir, node):
1854 1862 self._manifestlog = manifestlog
1855 1863 self._dir = dir
1856 1864 self._data = None
1857 1865
1858 1866 self._node = node
1859 1867
1860 1868 # TODO: Load p1/p2/linkrev lazily. They need to be lazily loaded so that
1861 1869 # we can instantiate treemanifestctx objects for directories we don't
1862 1870 # have on disk.
1863 1871 #self.p1, self.p2 = store.parents(node)
1864 1872 #rev = store.rev(node)
1865 1873 #self.linkrev = store.linkrev(rev)
1866 1874
1867 1875 def _storage(self):
1868 1876 narrowmatch = self._manifestlog._narrowmatch
1869 1877 if not narrowmatch.always():
1870 1878 if not narrowmatch.visitdir(self._dir[:-1] or '.'):
1871 1879 return excludedmanifestrevlog(self._dir)
1872 1880 return self._manifestlog.getstorage(self._dir)
1873 1881
1874 1882 def read(self):
1875 1883 if self._data is None:
1876 1884 store = self._storage()
1877 1885 if self._node == nullid:
1878 1886 self._data = treemanifest()
1879 1887 # TODO accessing non-public API
1880 1888 elif store._treeondisk:
1881 1889 m = treemanifest(dir=self._dir)
1882 1890 def gettext():
1883 1891 return store.revision(self._node)
1884 1892 def readsubtree(dir, subm):
1885 1893 # Set verify to False since we need to be able to create
1886 1894 # subtrees for trees that don't exist on disk.
1887 1895 return self._manifestlog.get(dir, subm, verify=False).read()
1888 1896 m.read(gettext, readsubtree)
1889 1897 m.setnode(self._node)
1890 1898 self._data = m
1891 1899 else:
1892 1900 if self._node in store.fulltextcache:
1893 1901 text = pycompat.bytestr(store.fulltextcache[self._node])
1894 1902 else:
1895 1903 text = store.revision(self._node)
1896 1904 arraytext = bytearray(text)
1897 1905 store.fulltextcache[self._node] = arraytext
1898 1906 self._data = treemanifest(dir=self._dir, text=text)
1899 1907
1900 1908 return self._data
1901 1909
1902 1910 def node(self):
1903 1911 return self._node
1904 1912
1905 1913 def new(self, dir=''):
1906 1914 return memtreemanifestctx(self._manifestlog, dir=dir)
1907 1915
1908 1916 def copy(self):
1909 1917 memmf = memtreemanifestctx(self._manifestlog, dir=self._dir)
1910 1918 memmf._treemanifest = self.read().copy()
1911 1919 return memmf
1912 1920
1913 1921 @propertycache
1914 1922 def parents(self):
1915 1923 return self._storage().parents(self._node)
1916 1924
1917 1925 def readdelta(self, shallow=False):
1918 1926 '''Returns a manifest containing just the entries that are present
1919 1927 in this manifest, but not in its p1 manifest. This is efficient to read
1920 1928 if the revlog delta is already p1.
1921 1929
1922 1930 If `shallow` is True, this will read the delta for this directory,
1923 1931 without recursively reading subdirectory manifests. Instead, any
1924 1932 subdirectory entry will be reported as it appears in the manifest, i.e.
1925 1933 the subdirectory will be reported among files and distinguished only by
1926 1934 its 't' flag.
1927 1935 '''
1928 1936 store = self._storage()
1929 1937 if shallow:
1930 1938 r = store.rev(self._node)
1931 1939 d = mdiff.patchtext(store.revdiff(store.deltaparent(r), r))
1932 1940 return manifestdict(d)
1933 1941 else:
1934 1942 # Need to perform a slow delta
1935 1943 r0 = store.deltaparent(store.rev(self._node))
1936 1944 m0 = self._manifestlog.get(self._dir, store.node(r0)).read()
1937 1945 m1 = self.read()
1938 1946 md = treemanifest(dir=self._dir)
1939 1947 for f, ((n0, fl0), (n1, fl1)) in m0.diff(m1).iteritems():
1940 1948 if n1:
1941 1949 md[f] = n1
1942 1950 if fl1:
1943 1951 md.setflag(f, fl1)
1944 1952 return md
1945 1953
1946 1954 def readfast(self, shallow=False):
1947 1955 '''Calls either readdelta or read, based on which would be less work.
1948 1956 readdelta is called if the delta is against the p1, and therefore can be
1949 1957 read quickly.
1950 1958
1951 1959 If `shallow` is True, it only returns the entries from this manifest,
1952 1960 and not any submanifests.
1953 1961 '''
1954 1962 store = self._storage()
1955 1963 r = store.rev(self._node)
1956 1964 deltaparent = store.deltaparent(r)
1957 1965 if (deltaparent != nullrev and
1958 1966 deltaparent in store.parentrevs(r)):
1959 1967 return self.readdelta(shallow=shallow)
1960 1968
1961 1969 if shallow:
1962 1970 return manifestdict(store.revision(self._node))
1963 1971 else:
1964 1972 return self.read()
1965 1973
1966 1974 def find(self, key):
1967 1975 return self.read().find(key)
1968 1976
1969 1977 class excludeddir(treemanifest):
1970 1978 """Stand-in for a directory that is excluded from the repository.
1971 1979
1972 1980 With narrowing active on a repository that uses treemanifests,
1973 1981 some of the directory revlogs will be excluded from the resulting
1974 1982 clone. This is a huge storage win for clients, but means we need
1975 1983 some sort of pseudo-manifest to surface to internals so we can
1976 1984 detect a merge conflict outside the narrowspec. That's what this
1977 1985 class is: it stands in for a directory whose node is known, but
1978 1986 whose contents are unknown.
1979 1987 """
1980 1988 def __init__(self, dir, node):
1981 1989 super(excludeddir, self).__init__(dir)
1982 1990 self._node = node
1983 1991 # Add an empty file, which will be included by iterators and such,
1984 1992 # appearing as the directory itself (i.e. something like "dir/")
1985 1993 self._files[''] = node
1986 1994 self._flags[''] = 't'
1987 1995
1988 1996 # Manifests outside the narrowspec should never be modified, so avoid
1989 1997 # copying. This makes a noticeable difference when there are very many
1990 1998 # directories outside the narrowspec. Also, it makes sense for the copy to
1991 1999 # be of the same type as the original, which would not happen with the
1992 2000 # super type's copy().
1993 2001 def copy(self):
1994 2002 return self
1995 2003
1996 2004 class excludeddirmanifestctx(treemanifestctx):
1997 2005 """context wrapper for excludeddir - see that docstring for rationale"""
1998 2006 def __init__(self, dir, node):
1999 2007 self._dir = dir
2000 2008 self._node = node
2001 2009
2002 2010 def read(self):
2003 2011 return excludeddir(self._dir, self._node)
2004 2012
2005 2013 def write(self, *args):
2006 2014 raise error.ProgrammingError(
2007 2015 'attempt to write manifest from excluded dir %s' % self._dir)
2008 2016
2009 2017 class excludedmanifestrevlog(manifestrevlog):
2010 2018 """Stand-in for excluded treemanifest revlogs.
2011 2019
2012 2020 When narrowing is active on a treemanifest repository, we'll have
2013 2021 references to directories we can't see due to the revlog being
2014 2022 skipped. This class exists to conform to the manifestrevlog
2015 2023 interface for those directories and proactively prevent writes to
2016 2024 outside the narrowspec.
2017 2025 """
2018 2026
2019 2027 def __init__(self, dir):
2020 2028 self._dir = dir
2021 2029
2022 2030 def __len__(self):
2023 2031 raise error.ProgrammingError(
2024 2032 'attempt to get length of excluded dir %s' % self._dir)
2025 2033
2026 2034 def rev(self, node):
2027 2035 raise error.ProgrammingError(
2028 2036 'attempt to get rev from excluded dir %s' % self._dir)
2029 2037
2030 2038 def linkrev(self, node):
2031 2039 raise error.ProgrammingError(
2032 2040 'attempt to get linkrev from excluded dir %s' % self._dir)
2033 2041
2034 2042 def node(self, rev):
2035 2043 raise error.ProgrammingError(
2036 2044 'attempt to get node from excluded dir %s' % self._dir)
2037 2045
2038 2046 def add(self, *args, **kwargs):
2039 2047 # We should never write entries in dirlogs outside the narrow clone.
2040 2048 # However, the method still gets called from writesubtree() in
2041 2049 # _addtree(), so we need to handle it. We should possibly make that
2042 2050 # avoid calling add() with a clean manifest (_dirty is always False
2043 2051 # in excludeddir instances).
2044 2052 pass
General Comments 0
You need to be logged in to leave comments. Login now