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