##// END OF EJS Templates
treemanifest: lazily load manifests...
Martin von Zweigbergk -
r25222:0de132d5 default
parent child Browse files
Show More
@@ -441,10 +441,13 b' def _splittopdir(f):'
441 else:
441 else:
442 return '', f
442 return '', f
443
443
444 _noop = lambda: None
445
444 class treemanifest(object):
446 class treemanifest(object):
445 def __init__(self, dir='', text=''):
447 def __init__(self, dir='', text=''):
446 self._dir = dir
448 self._dir = dir
447 self._node = revlog.nullid
449 self._node = revlog.nullid
450 self._load = _noop
448 self._dirty = False
451 self._dirty = False
449 self._dirs = {}
452 self._dirs = {}
450 # Using _lazymanifest here is a little slower than plain old dicts
453 # Using _lazymanifest here is a little slower than plain old dicts
@@ -461,18 +464,22 b' class treemanifest(object):'
461 return self._dir + path
464 return self._dir + path
462
465
463 def __len__(self):
466 def __len__(self):
467 self._load()
464 size = len(self._files)
468 size = len(self._files)
465 for m in self._dirs.values():
469 for m in self._dirs.values():
466 size += m.__len__()
470 size += m.__len__()
467 return size
471 return size
468
472
469 def _isempty(self):
473 def _isempty(self):
474 self._load() # for consistency; already loaded by all callers
470 return (not self._files and (not self._dirs or
475 return (not self._files and (not self._dirs or
471 all(m._isempty() for m in self._dirs.values())))
476 all(m._isempty() for m in self._dirs.values())))
472
477
473 def __str__(self):
478 def __str__(self):
474 return ('<treemanifest dir=%s, node=%s, dirty=%s>' %
479 return ('<treemanifest dir=%s, node=%s, loaded=%s, dirty=%s>' %
475 (self._dir, revlog.hex(self._node), self._dirty))
480 (self._dir, revlog.hex(self._node),
481 bool(self._load is _noop),
482 self._dirty))
476
483
477 def dir(self):
484 def dir(self):
478 '''The directory that this tree manifest represents, including a
485 '''The directory that this tree manifest represents, including a
@@ -491,6 +498,7 b' class treemanifest(object):'
491 self._dirty = False
498 self._dirty = False
492
499
493 def iteritems(self):
500 def iteritems(self):
501 self._load()
494 for p, n in sorted(self._dirs.items() + self._files.items()):
502 for p, n in sorted(self._dirs.items() + self._files.items()):
495 if p in self._files:
503 if p in self._files:
496 yield self._subpath(p), n
504 yield self._subpath(p), n
@@ -499,6 +507,7 b' class treemanifest(object):'
499 yield f, sn
507 yield f, sn
500
508
501 def iterkeys(self):
509 def iterkeys(self):
510 self._load()
502 for p in sorted(self._dirs.keys() + self._files.keys()):
511 for p in sorted(self._dirs.keys() + self._files.keys()):
503 if p in self._files:
512 if p in self._files:
504 yield self._subpath(p)
513 yield self._subpath(p)
@@ -515,6 +524,7 b' class treemanifest(object):'
515 def __contains__(self, f):
524 def __contains__(self, f):
516 if f is None:
525 if f is None:
517 return False
526 return False
527 self._load()
518 dir, subpath = _splittopdir(f)
528 dir, subpath = _splittopdir(f)
519 if dir:
529 if dir:
520 if dir not in self._dirs:
530 if dir not in self._dirs:
@@ -524,6 +534,7 b' class treemanifest(object):'
524 return f in self._files
534 return f in self._files
525
535
526 def get(self, f, default=None):
536 def get(self, f, default=None):
537 self._load()
527 dir, subpath = _splittopdir(f)
538 dir, subpath = _splittopdir(f)
528 if dir:
539 if dir:
529 if dir not in self._dirs:
540 if dir not in self._dirs:
@@ -533,6 +544,7 b' class treemanifest(object):'
533 return self._files.get(f, default)
544 return self._files.get(f, default)
534
545
535 def __getitem__(self, f):
546 def __getitem__(self, f):
547 self._load()
536 dir, subpath = _splittopdir(f)
548 dir, subpath = _splittopdir(f)
537 if dir:
549 if dir:
538 return self._dirs[dir].__getitem__(subpath)
550 return self._dirs[dir].__getitem__(subpath)
@@ -540,6 +552,7 b' class treemanifest(object):'
540 return self._files[f]
552 return self._files[f]
541
553
542 def flags(self, f):
554 def flags(self, f):
555 self._load()
543 dir, subpath = _splittopdir(f)
556 dir, subpath = _splittopdir(f)
544 if dir:
557 if dir:
545 if dir not in self._dirs:
558 if dir not in self._dirs:
@@ -551,6 +564,7 b' class treemanifest(object):'
551 return self._flags.get(f, '')
564 return self._flags.get(f, '')
552
565
553 def find(self, f):
566 def find(self, f):
567 self._load()
554 dir, subpath = _splittopdir(f)
568 dir, subpath = _splittopdir(f)
555 if dir:
569 if dir:
556 return self._dirs[dir].find(subpath)
570 return self._dirs[dir].find(subpath)
@@ -558,6 +572,7 b' class treemanifest(object):'
558 return self._files[f], self._flags.get(f, '')
572 return self._files[f], self._flags.get(f, '')
559
573
560 def __delitem__(self, f):
574 def __delitem__(self, f):
575 self._load()
561 dir, subpath = _splittopdir(f)
576 dir, subpath = _splittopdir(f)
562 if dir:
577 if dir:
563 self._dirs[dir].__delitem__(subpath)
578 self._dirs[dir].__delitem__(subpath)
@@ -572,6 +587,7 b' class treemanifest(object):'
572
587
573 def __setitem__(self, f, n):
588 def __setitem__(self, f, n):
574 assert n is not None
589 assert n is not None
590 self._load()
575 dir, subpath = _splittopdir(f)
591 dir, subpath = _splittopdir(f)
576 if dir:
592 if dir:
577 if dir not in self._dirs:
593 if dir not in self._dirs:
@@ -584,6 +600,7 b' class treemanifest(object):'
584 def setflag(self, f, flags):
600 def setflag(self, f, flags):
585 """Set the flags (symlink, executable) for path f."""
601 """Set the flags (symlink, executable) for path f."""
586 assert 'd' not in flags
602 assert 'd' not in flags
603 self._load()
587 dir, subpath = _splittopdir(f)
604 dir, subpath = _splittopdir(f)
588 if dir:
605 if dir:
589 if dir not in self._dirs:
606 if dir not in self._dirs:
@@ -597,10 +614,19 b' class treemanifest(object):'
597 copy = treemanifest(self._dir)
614 copy = treemanifest(self._dir)
598 copy._node = self._node
615 copy._node = self._node
599 copy._dirty = self._dirty
616 copy._dirty = self._dirty
600 for d in self._dirs:
617 def _load():
601 copy._dirs[d] = self._dirs[d].copy()
618 self._load()
602 copy._files = dict.copy(self._files)
619 for d in self._dirs:
603 copy._flags = dict.copy(self._flags)
620 copy._dirs[d] = self._dirs[d].copy()
621 copy._files = dict.copy(self._files)
622 copy._flags = dict.copy(self._flags)
623 copy._load = _noop
624 copy._load = _load
625 if self._load == _noop:
626 # Chaining _load if it's _noop is functionally correct, but the
627 # chain may end up excessively long (stack overflow), and
628 # will prevent garbage collection of 'self'.
629 copy._load()
604 return copy
630 return copy
605
631
606 def filesnotin(self, m2):
632 def filesnotin(self, m2):
@@ -609,6 +635,8 b' class treemanifest(object):'
609 def _filesnotin(t1, t2):
635 def _filesnotin(t1, t2):
610 if t1._node == t2._node and not t1._dirty and not t2._dirty:
636 if t1._node == t2._node and not t1._dirty and not t2._dirty:
611 return
637 return
638 t1._load()
639 t2._load()
612 for d, m1 in t1._dirs.iteritems():
640 for d, m1 in t1._dirs.iteritems():
613 if d in t2._dirs:
641 if d in t2._dirs:
614 m2 = t2._dirs[d]
642 m2 = t2._dirs[d]
@@ -631,6 +659,7 b' class treemanifest(object):'
631 return self._alldirs
659 return self._alldirs
632
660
633 def hasdir(self, dir):
661 def hasdir(self, dir):
662 self._load()
634 topdir, subdir = _splittopdir(dir)
663 topdir, subdir = _splittopdir(dir)
635 if topdir:
664 if topdir:
636 if topdir in self._dirs:
665 if topdir in self._dirs:
@@ -673,6 +702,7 b' class treemanifest(object):'
673 return
702 return
674
703
675 # yield this dir's files and walk its submanifests
704 # yield this dir's files and walk its submanifests
705 self._load()
676 for p in sorted(self._dirs.keys() + self._files.keys()):
706 for p in sorted(self._dirs.keys() + self._files.keys()):
677 if p in self._files:
707 if p in self._files:
678 fullp = self._subpath(p)
708 fullp = self._subpath(p)
@@ -697,6 +727,7 b' class treemanifest(object):'
697 if not match.visitdir(self._dir[:-1] or '.'):
727 if not match.visitdir(self._dir[:-1] or '.'):
698 return ret
728 return ret
699
729
730 self._load()
700 for fn in self._files:
731 for fn in self._files:
701 fullp = self._subpath(fn)
732 fullp = self._subpath(fn)
702 if not match(fullp):
733 if not match(fullp):
@@ -734,6 +765,8 b' class treemanifest(object):'
734 def _diff(t1, t2):
765 def _diff(t1, t2):
735 if t1._node == t2._node and not t1._dirty and not t2._dirty:
766 if t1._node == t2._node and not t1._dirty and not t2._dirty:
736 return
767 return
768 t1._load()
769 t2._load()
737 for d, m1 in t1._dirs.iteritems():
770 for d, m1 in t1._dirs.iteritems():
738 m2 = t2._dirs.get(d, emptytree)
771 m2 = t2._dirs.get(d, emptytree)
739 _diff(m1, m2)
772 _diff(m1, m2)
@@ -784,6 +817,7 b' class treemanifest(object):'
784
817
785 def text(self, usemanifestv2=False):
818 def text(self, usemanifestv2=False):
786 """Get the full data of this manifest as a bytestring."""
819 """Get the full data of this manifest as a bytestring."""
820 self._load()
787 flags = self.flags
821 flags = self.flags
788 return _text(((f, self[f], flags(f)) for f in self.keys()),
822 return _text(((f, self[f], flags(f)) for f in self.keys()),
789 usemanifestv2)
823 usemanifestv2)
@@ -792,12 +826,23 b' class treemanifest(object):'
792 """Get the full data of this directory as a bytestring. Make sure that
826 """Get the full data of this directory as a bytestring. Make sure that
793 any submanifests have been written first, so their nodeids are correct.
827 any submanifests have been written first, so their nodeids are correct.
794 """
828 """
829 self._load()
795 flags = self.flags
830 flags = self.flags
796 dirs = [(d[:-1], self._dirs[d]._node, 'd') for d in self._dirs]
831 dirs = [(d[:-1], self._dirs[d]._node, 'd') for d in self._dirs]
797 files = [(f, self._files[f], flags(f)) for f in self._files]
832 files = [(f, self._files[f], flags(f)) for f in self._files]
798 return _text(sorted(dirs + files), usemanifestv2)
833 return _text(sorted(dirs + files), usemanifestv2)
799
834
835 def read(self, gettext, readsubtree):
836 def _load():
837 # Mark as loaded already here, so __setitem__ and setflag() don't
838 # cause infinite loops when they try to load.
839 self._load = _noop
840 self.parse(gettext(), readsubtree)
841 self._dirty = False
842 self._load = _load
843
800 def writesubtrees(self, m1, m2, writesubtree):
844 def writesubtrees(self, m1, m2, writesubtree):
845 self._load() # for consistency; should never have any effect here
801 emptytree = treemanifest()
846 emptytree = treemanifest()
802 for d, subm in self._dirs.iteritems():
847 for d, subm in self._dirs.iteritems():
803 subp1 = m1._dirs.get(d, emptytree)._node
848 subp1 = m1._dirs.get(d, emptytree)._node
@@ -891,15 +936,17 b' class manifest(revlog.revlog):'
891 return self._newmanifest() # don't upset local cache
936 return self._newmanifest() # don't upset local cache
892 if node in self._mancache:
937 if node in self._mancache:
893 return self._mancache[node][0]
938 return self._mancache[node][0]
894 text = self.revision(node)
895 if self._treeondisk:
939 if self._treeondisk:
940 def gettext():
941 return self.revision(node)
896 def readsubtree(dir, subm):
942 def readsubtree(dir, subm):
897 return self.dirlog(dir).read(subm)
943 return self.dirlog(dir).read(subm)
898 m = self._newmanifest()
944 m = self._newmanifest()
899 m.parse(text, readsubtree)
945 m.read(gettext, readsubtree)
900 m.setnode(node)
946 m.setnode(node)
901 arraytext = None
947 arraytext = None
902 else:
948 else:
949 text = self.revision(node)
903 m = self._newmanifest(text)
950 m = self._newmanifest(text)
904 arraytext = array.array('c', text)
951 arraytext = array.array('c', text)
905 self._mancache[node] = (m, arraytext)
952 self._mancache[node] = (m, arraytext)
@@ -78,9 +78,11 b' Removing directory does not create an re'
78 $ rm before after
78 $ rm before after
79
79
80 Check that hg files (calls treemanifest.walk()) works
80 Check that hg files (calls treemanifest.walk()) works
81 without loading all directory revlogs
81
82
82 $ hg co 'desc("add dir2")'
83 $ hg co 'desc("add dir2")'
83 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
84 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
85 $ mv .hg/store/meta/dir2 .hg/store/meta/dir2-backup
84 $ hg files -r . dir1
86 $ hg files -r . dir1
85 dir1/a (glob)
87 dir1/a (glob)
86 dir1/b (glob)
88 dir1/b (glob)
@@ -90,12 +92,14 b' Check that hg files (calls treemanifest.'
90 dir1/dir2/b (glob)
92 dir1/dir2/b (glob)
91
93
92 Check that status between revisions works (calls treemanifest.matches())
94 Check that status between revisions works (calls treemanifest.matches())
95 without loading all directory revlogs
93
96
94 $ hg status --rev 'desc("add dir1")' --rev . dir1
97 $ hg status --rev 'desc("add dir1")' --rev . dir1
95 A dir1/dir1/a
98 A dir1/dir1/a
96 A dir1/dir1/b
99 A dir1/dir1/b
97 A dir1/dir2/a
100 A dir1/dir2/a
98 A dir1/dir2/b
101 A dir1/dir2/b
102 $ mv .hg/store/meta/dir2-backup .hg/store/meta/dir2
99
103
100 Merge creates 2-parent revision of directory revlog
104 Merge creates 2-parent revision of directory revlog
101
105
General Comments 0
You need to be logged in to leave comments. Login now