442 return '', f |
442 return '', f |
443 |
443 |
444 class treemanifest(object): |
444 class treemanifest(object): |
445 def __init__(self, dir='', text=''): |
445 def __init__(self, dir='', text=''): |
446 self._dir = dir |
446 self._dir = dir |
|
447 self._node = revlog.nullid |
447 self._dirs = {} |
448 self._dirs = {} |
448 # Using _lazymanifest here is a little slower than plain old dicts |
449 # Using _lazymanifest here is a little slower than plain old dicts |
449 self._files = {} |
450 self._files = {} |
450 self._flags = {} |
451 self._flags = {} |
451 self.parse(text) |
452 def readsubtree(subdir, subm): |
|
453 raise AssertionError('treemanifest constructor only accepts ' |
|
454 'flat manifests') |
|
455 self.parse(text, readsubtree) |
452 |
456 |
453 def _subpath(self, path): |
457 def _subpath(self, path): |
454 return self._dir + path |
458 return self._dir + path |
455 |
459 |
456 def __len__(self): |
460 def __len__(self): |
462 def _isempty(self): |
466 def _isempty(self): |
463 return (not self._files and (not self._dirs or |
467 return (not self._files and (not self._dirs or |
464 util.all(m._isempty() for m in self._dirs.values()))) |
468 util.all(m._isempty() for m in self._dirs.values()))) |
465 |
469 |
466 def __str__(self): |
470 def __str__(self): |
467 return '<treemanifest dir=%s>' % self._dir |
471 return ('<treemanifest dir=%s, node=%s>' % |
|
472 (self._dir, revlog.hex(self._node))) |
|
473 |
|
474 def dir(self): |
|
475 '''The directory that this tree manifest represents, including a |
|
476 trailing '/'. Empty string for the repo root directory.''' |
|
477 return self._dir |
|
478 |
|
479 def node(self): |
|
480 '''This node of this instance. nullid for unsaved instances. Should |
|
481 be updated when the instance is read or written from a revlog. |
|
482 ''' |
|
483 return self._node |
|
484 |
|
485 def setnode(self, node): |
|
486 self._node = node |
468 |
487 |
469 def iteritems(self): |
488 def iteritems(self): |
470 for p, n in sorted(self._dirs.items() + self._files.items()): |
489 for p, n in sorted(self._dirs.items() + self._files.items()): |
471 if p in self._files: |
490 if p in self._files: |
472 yield self._subpath(p), n |
491 yield self._subpath(p), n |
555 else: |
574 else: |
556 self._files[f] = n[:21] # to match manifestdict's behavior |
575 self._files[f] = n[:21] # to match manifestdict's behavior |
557 |
576 |
558 def setflag(self, f, flags): |
577 def setflag(self, f, flags): |
559 """Set the flags (symlink, executable) for path f.""" |
578 """Set the flags (symlink, executable) for path f.""" |
|
579 assert 'd' not in flags |
560 dir, subpath = _splittopdir(f) |
580 dir, subpath = _splittopdir(f) |
561 if dir: |
581 if dir: |
562 if dir not in self._dirs: |
582 if dir not in self._dirs: |
563 self._dirs[dir] = treemanifest(self._subpath(dir)) |
583 self._dirs[dir] = treemanifest(self._subpath(dir)) |
564 self._dirs[dir].setflag(subpath, flags) |
584 self._dirs[dir].setflag(subpath, flags) |
565 else: |
585 else: |
566 self._flags[f] = flags |
586 self._flags[f] = flags |
567 |
587 |
568 def copy(self): |
588 def copy(self): |
569 copy = treemanifest(self._dir) |
589 copy = treemanifest(self._dir) |
|
590 copy._node = self._node |
570 for d in self._dirs: |
591 for d in self._dirs: |
571 copy._dirs[d] = self._dirs[d].copy() |
592 copy._dirs[d] = self._dirs[d].copy() |
572 copy._files = dict.copy(self._files) |
593 copy._files = dict.copy(self._files) |
573 copy._flags = dict.copy(self._flags) |
594 copy._flags = dict.copy(self._flags) |
574 return copy |
595 return copy |
735 result[t2._subpath(fn)] = ((None, ''), (n2, fl2)) |
756 result[t2._subpath(fn)] = ((None, ''), (n2, fl2)) |
736 |
757 |
737 _diff(self, m2) |
758 _diff(self, m2) |
738 return result |
759 return result |
739 |
760 |
740 def parse(self, text): |
761 def parse(self, text, readsubtree): |
741 for f, n, fl in _parse(text): |
762 for f, n, fl in _parse(text): |
742 self[f] = n |
763 if fl == 'd': |
743 if fl: |
764 f = f + '/' |
744 self.setflag(f, fl) |
765 self._dirs[f] = readsubtree(self._subpath(f), n) |
|
766 else: |
|
767 # Use __setitem__ and setflag rather than assigning directly |
|
768 # to _files and _flags, thereby letting us parse flat manifests |
|
769 # as well as tree manifests. |
|
770 self[f] = n |
|
771 if fl: |
|
772 self.setflag(f, fl) |
745 |
773 |
746 def text(self, usemanifestv2=False): |
774 def text(self, usemanifestv2=False): |
747 """Get the full data of this manifest as a bytestring.""" |
775 """Get the full data of this manifest as a bytestring.""" |
748 flags = self.flags |
776 flags = self.flags |
749 return _text(((f, self[f], flags(f)) for f in self.keys()), |
777 return _text(((f, self[f], flags(f)) for f in self.keys()), |
750 usemanifestv2) |
778 usemanifestv2) |
751 |
779 |
|
780 def dirtext(self, usemanifestv2=False): |
|
781 """Get the full data of this directory as a bytestring. Make sure that |
|
782 any submanifests have been written first, so their nodeids are correct. |
|
783 """ |
|
784 flags = self.flags |
|
785 dirs = [(d[:-1], self._dirs[d]._node, 'd') for d in self._dirs] |
|
786 files = [(f, self._files[f], flags(f)) for f in self._files] |
|
787 return _text(sorted(dirs + files), usemanifestv2) |
|
788 |
|
789 def writesubtrees(self, m1, m2, writesubtree): |
|
790 emptytree = treemanifest() |
|
791 for d, subm in self._dirs.iteritems(): |
|
792 subp1 = m1._dirs.get(d, emptytree)._node |
|
793 subp2 = m2._dirs.get(d, emptytree)._node |
|
794 if subp1 == revlog.nullid: |
|
795 subp1, subp2 = subp2, subp1 |
|
796 writesubtree(subm, subp1, subp2) |
|
797 |
752 class manifest(revlog.revlog): |
798 class manifest(revlog.revlog): |
753 def __init__(self, opener): |
799 def __init__(self, opener, dir=''): |
754 # During normal operations, we expect to deal with not more than four |
800 # During normal operations, we expect to deal with not more than four |
755 # revs at a time (such as during commit --amend). When rebasing large |
801 # revs at a time (such as during commit --amend). When rebasing large |
756 # stacks of commits, the number can go up, hence the config knob below. |
802 # stacks of commits, the number can go up, hence the config knob below. |
757 cachesize = 4 |
803 cachesize = 4 |
758 usetreemanifest = False |
804 usetreemanifest = False |
761 if opts is not None: |
807 if opts is not None: |
762 cachesize = opts.get('manifestcachesize', cachesize) |
808 cachesize = opts.get('manifestcachesize', cachesize) |
763 usetreemanifest = opts.get('treemanifest', usetreemanifest) |
809 usetreemanifest = opts.get('treemanifest', usetreemanifest) |
764 usemanifestv2 = opts.get('manifestv2', usemanifestv2) |
810 usemanifestv2 = opts.get('manifestv2', usemanifestv2) |
765 self._mancache = util.lrucachedict(cachesize) |
811 self._mancache = util.lrucachedict(cachesize) |
766 revlog.revlog.__init__(self, opener, "00manifest.i") |
|
767 self._treeinmem = usetreemanifest |
812 self._treeinmem = usetreemanifest |
768 self._treeondisk = usetreemanifest |
813 self._treeondisk = usetreemanifest |
769 self._usemanifestv2 = usemanifestv2 |
814 self._usemanifestv2 = usemanifestv2 |
|
815 indexfile = "00manifest.i" |
|
816 if dir: |
|
817 assert self._treeondisk |
|
818 indexfile = "meta/" + dir + "00manifest.i" |
|
819 revlog.revlog.__init__(self, opener, indexfile) |
|
820 self._dir = dir |
770 |
821 |
771 def _newmanifest(self, data=''): |
822 def _newmanifest(self, data=''): |
772 if self._treeinmem: |
823 if self._treeinmem: |
773 return treemanifest('', data) |
824 return treemanifest(self._dir, data) |
774 return manifestdict(data) |
825 return manifestdict(data) |
775 |
826 |
776 def _slowreaddelta(self, node): |
827 def _slowreaddelta(self, node): |
777 r0 = self.deltaparent(self.rev(node)) |
828 r0 = self.deltaparent(self.rev(node)) |
778 m0 = self.read(self.node(r0)) |
829 m0 = self.read(self.node(r0)) |
810 if node == revlog.nullid: |
861 if node == revlog.nullid: |
811 return self._newmanifest() # don't upset local cache |
862 return self._newmanifest() # don't upset local cache |
812 if node in self._mancache: |
863 if node in self._mancache: |
813 return self._mancache[node][0] |
864 return self._mancache[node][0] |
814 text = self.revision(node) |
865 text = self.revision(node) |
815 arraytext = array.array('c', text) |
866 if self._treeondisk: |
816 m = self._newmanifest(text) |
867 def readsubtree(dir, subm): |
|
868 sublog = manifest(self.opener, dir) |
|
869 return sublog.read(subm) |
|
870 m = self._newmanifest() |
|
871 m.parse(text, readsubtree) |
|
872 m.setnode(node) |
|
873 arraytext = None |
|
874 else: |
|
875 m = self._newmanifest(text) |
|
876 arraytext = array.array('c', text) |
817 self._mancache[node] = (m, arraytext) |
877 self._mancache[node] = (m, arraytext) |
818 return m |
878 return m |
819 |
879 |
820 def find(self, node, f): |
880 def find(self, node, f): |
821 '''look up entry for a single file efficiently. |
881 '''look up entry for a single file efficiently. |
849 else: |
909 else: |
850 # The first parent manifest isn't already loaded, so we'll |
910 # The first parent manifest isn't already loaded, so we'll |
851 # just encode a fulltext of the manifest and pass that |
911 # just encode a fulltext of the manifest and pass that |
852 # through to the revlog layer, and let it handle the delta |
912 # through to the revlog layer, and let it handle the delta |
853 # process. |
913 # process. |
854 text = m.text(self._usemanifestv2) |
914 if self._treeondisk: |
855 arraytext = array.array('c', text) |
915 m1 = self.read(p1) |
856 n = self.addrevision(text, transaction, link, p1, p2) |
916 m2 = self.read(p2) |
|
917 n = self._addtree(m, transaction, link, m1, m2) |
|
918 arraytext = None |
|
919 else: |
|
920 text = m.text(self._usemanifestv2) |
|
921 n = self.addrevision(text, transaction, link, p1, p2) |
|
922 arraytext = array.array('c', text) |
857 |
923 |
858 self._mancache[n] = (m, arraytext) |
924 self._mancache[n] = (m, arraytext) |
859 |
925 |
860 return n |
926 return n |
|
927 |
|
928 def _addtree(self, m, transaction, link, m1, m2): |
|
929 def writesubtree(subm, subp1, subp2): |
|
930 sublog = manifest(self.opener, subm.dir()) |
|
931 sublog.add(subm, transaction, link, subp1, subp2, None, None) |
|
932 m.writesubtrees(m1, m2, writesubtree) |
|
933 text = m.dirtext(self._usemanifestv2) |
|
934 # If the manifest is unchanged compared to one parent, |
|
935 # don't write a new revision |
|
936 if text == m1.dirtext(self._usemanifestv2): |
|
937 n = m1.node() |
|
938 elif text == m2.dirtext(self._usemanifestv2): |
|
939 n = m2.node() |
|
940 else: |
|
941 n = self.addrevision(text, transaction, link, m1.node(), m2.node()) |
|
942 # Save nodeid so parent manifest can calculate its nodeid |
|
943 m.setnode(n) |
|
944 return n |