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