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._node = revlog.nullid |
|
448 self._dirty = False |
448 self._dirs = {} |
449 self._dirs = {} |
449 # Using _lazymanifest here is a little slower than plain old dicts |
450 # Using _lazymanifest here is a little slower than plain old dicts |
450 self._files = {} |
451 self._files = {} |
451 self._flags = {} |
452 self._flags = {} |
452 def readsubtree(subdir, subm): |
453 if text: |
453 raise AssertionError('treemanifest constructor only accepts ' |
454 def readsubtree(subdir, subm): |
454 'flat manifests') |
455 raise AssertionError('treemanifest constructor only accepts ' |
455 self.parse(text, readsubtree) |
456 'flat manifests') |
|
457 self.parse(text, readsubtree) |
|
458 self._dirty = True # Mark flat manifest dirty after parsing |
456 |
459 |
457 def _subpath(self, path): |
460 def _subpath(self, path): |
458 return self._dir + path |
461 return self._dir + path |
459 |
462 |
460 def __len__(self): |
463 def __len__(self): |
466 def _isempty(self): |
469 def _isempty(self): |
467 return (not self._files and (not self._dirs or |
470 return (not self._files and (not self._dirs or |
468 all(m._isempty() for m in self._dirs.values()))) |
471 all(m._isempty() for m in self._dirs.values()))) |
469 |
472 |
470 def __str__(self): |
473 def __str__(self): |
471 return ('<treemanifest dir=%s, node=%s>' % |
474 return ('<treemanifest dir=%s, node=%s, dirty=%s>' % |
472 (self._dir, revlog.hex(self._node))) |
475 (self._dir, revlog.hex(self._node), self._dirty)) |
473 |
476 |
474 def dir(self): |
477 def dir(self): |
475 '''The directory that this tree manifest represents, including a |
478 '''The directory that this tree manifest represents, including a |
476 trailing '/'. Empty string for the repo root directory.''' |
479 trailing '/'. Empty string for the repo root directory.''' |
477 return self._dir |
480 return self._dir |
478 |
481 |
479 def node(self): |
482 def node(self): |
480 '''This node of this instance. nullid for unsaved instances. Should |
483 '''This node of this instance. nullid for unsaved instances. Should |
481 be updated when the instance is read or written from a revlog. |
484 be updated when the instance is read or written from a revlog. |
482 ''' |
485 ''' |
|
486 assert not self._dirty |
483 return self._node |
487 return self._node |
484 |
488 |
485 def setnode(self, node): |
489 def setnode(self, node): |
486 self._node = node |
490 self._node = node |
|
491 self._dirty = False |
487 |
492 |
488 def iteritems(self): |
493 def iteritems(self): |
489 for p, n in sorted(self._dirs.items() + self._files.items()): |
494 for p, n in sorted(self._dirs.items() + self._files.items()): |
490 if p in self._files: |
495 if p in self._files: |
491 yield self._subpath(p), n |
496 yield self._subpath(p), n |
561 del self._dirs[dir] |
566 del self._dirs[dir] |
562 else: |
567 else: |
563 del self._files[f] |
568 del self._files[f] |
564 if f in self._flags: |
569 if f in self._flags: |
565 del self._flags[f] |
570 del self._flags[f] |
|
571 self._dirty = True |
566 |
572 |
567 def __setitem__(self, f, n): |
573 def __setitem__(self, f, n): |
568 assert n is not None |
574 assert n is not None |
569 dir, subpath = _splittopdir(f) |
575 dir, subpath = _splittopdir(f) |
570 if dir: |
576 if dir: |
571 if dir not in self._dirs: |
577 if dir not in self._dirs: |
572 self._dirs[dir] = treemanifest(self._subpath(dir)) |
578 self._dirs[dir] = treemanifest(self._subpath(dir)) |
573 self._dirs[dir].__setitem__(subpath, n) |
579 self._dirs[dir].__setitem__(subpath, n) |
574 else: |
580 else: |
575 self._files[f] = n[:21] # to match manifestdict's behavior |
581 self._files[f] = n[:21] # to match manifestdict's behavior |
|
582 self._dirty = True |
576 |
583 |
577 def setflag(self, f, flags): |
584 def setflag(self, f, flags): |
578 """Set the flags (symlink, executable) for path f.""" |
585 """Set the flags (symlink, executable) for path f.""" |
579 assert 'd' not in flags |
586 assert 'd' not in flags |
580 dir, subpath = _splittopdir(f) |
587 dir, subpath = _splittopdir(f) |
582 if dir not in self._dirs: |
589 if dir not in self._dirs: |
583 self._dirs[dir] = treemanifest(self._subpath(dir)) |
590 self._dirs[dir] = treemanifest(self._subpath(dir)) |
584 self._dirs[dir].setflag(subpath, flags) |
591 self._dirs[dir].setflag(subpath, flags) |
585 else: |
592 else: |
586 self._flags[f] = flags |
593 self._flags[f] = flags |
|
594 self._dirty = True |
587 |
595 |
588 def copy(self): |
596 def copy(self): |
589 copy = treemanifest(self._dir) |
597 copy = treemanifest(self._dir) |
590 copy._node = self._node |
598 copy._node = self._node |
|
599 copy._dirty = self._dirty |
591 for d in self._dirs: |
600 for d in self._dirs: |
592 copy._dirs[d] = self._dirs[d].copy() |
601 copy._dirs[d] = self._dirs[d].copy() |
593 copy._files = dict.copy(self._files) |
602 copy._files = dict.copy(self._files) |
594 copy._flags = dict.copy(self._flags) |
603 copy._flags = dict.copy(self._flags) |
595 return copy |
604 return copy |
596 |
605 |
597 def filesnotin(self, m2): |
606 def filesnotin(self, m2): |
598 '''Set of files in this manifest that are not in the other''' |
607 '''Set of files in this manifest that are not in the other''' |
599 files = set() |
608 files = set() |
600 def _filesnotin(t1, t2): |
609 def _filesnotin(t1, t2): |
|
610 if t1._node == t2._node and not t1._dirty and not t2._dirty: |
|
611 return |
601 for d, m1 in t1._dirs.iteritems(): |
612 for d, m1 in t1._dirs.iteritems(): |
602 if d in t2._dirs: |
613 if d in t2._dirs: |
603 m2 = t2._dirs[d] |
614 m2 = t2._dirs[d] |
604 _filesnotin(m1, m2) |
615 _filesnotin(m1, m2) |
605 else: |
616 else: |
697 for dir, subm in self._dirs.iteritems(): |
708 for dir, subm in self._dirs.iteritems(): |
698 m = subm._matches(match) |
709 m = subm._matches(match) |
699 if not m._isempty(): |
710 if not m._isempty(): |
700 ret._dirs[dir] = m |
711 ret._dirs[dir] = m |
701 |
712 |
|
713 if not ret._isempty(): |
|
714 ret._dirty = True |
702 return ret |
715 return ret |
703 |
716 |
704 def diff(self, m2, clean=False): |
717 def diff(self, m2, clean=False): |
705 '''Finds changes between the current manifest and m2. |
718 '''Finds changes between the current manifest and m2. |
706 |
719 |
717 string. |
730 string. |
718 ''' |
731 ''' |
719 result = {} |
732 result = {} |
720 emptytree = treemanifest() |
733 emptytree = treemanifest() |
721 def _diff(t1, t2): |
734 def _diff(t1, t2): |
|
735 if t1._node == t2._node and not t1._dirty and not t2._dirty: |
|
736 return |
722 for d, m1 in t1._dirs.iteritems(): |
737 for d, m1 in t1._dirs.iteritems(): |
723 m2 = t2._dirs.get(d, emptytree) |
738 m2 = t2._dirs.get(d, emptytree) |
724 _diff(m1, m2) |
739 _diff(m1, m2) |
725 |
740 |
726 for d, m2 in t2._dirs.iteritems(): |
741 for d, m2 in t2._dirs.iteritems(): |
747 def parse(self, text, readsubtree): |
762 def parse(self, text, readsubtree): |
748 for f, n, fl in _parse(text): |
763 for f, n, fl in _parse(text): |
749 if fl == 'd': |
764 if fl == 'd': |
750 f = f + '/' |
765 f = f + '/' |
751 self._dirs[f] = readsubtree(self._subpath(f), n) |
766 self._dirs[f] = readsubtree(self._subpath(f), n) |
752 else: |
767 elif '/' in f: |
753 # Use __setitem__ and setflag rather than assigning directly |
768 # This is a flat manifest, so use __setitem__ and setflag rather |
754 # to _files and _flags, thereby letting us parse flat manifests |
769 # than assigning directly to _files and _flags, so we can |
755 # as well as tree manifests. |
770 # assign a path in a subdirectory, and to mark dirty (compared |
|
771 # to nullid). |
756 self[f] = n |
772 self[f] = n |
757 if fl: |
773 if fl: |
758 self.setflag(f, fl) |
774 self.setflag(f, fl) |
|
775 else: |
|
776 # Assigning to _files and _flags avoids marking as dirty, |
|
777 # and should be a little faster. |
|
778 self._files[f] = n |
|
779 if fl: |
|
780 self._flags[f] = fl |
759 |
781 |
760 def text(self, usemanifestv2=False): |
782 def text(self, usemanifestv2=False): |
761 """Get the full data of this manifest as a bytestring.""" |
783 """Get the full data of this manifest as a bytestring.""" |
762 flags = self.flags |
784 flags = self.flags |
763 return _text(((f, self[f], flags(f)) for f in self.keys()), |
785 return _text(((f, self[f], flags(f)) for f in self.keys()), |