707 missings.sort() |
707 missings.sort() |
708 if missings: |
708 if missings: |
709 raise error.RequirementError(_("unknown repository format: " |
709 raise error.RequirementError(_("unknown repository format: " |
710 "requires features '%s' (upgrade Mercurial)") % "', '".join(missings)) |
710 "requires features '%s' (upgrade Mercurial)") % "', '".join(missings)) |
711 return requirements |
711 return requirements |
|
712 |
|
713 class filecacheentry(object): |
|
714 def __init__(self, path): |
|
715 self.path = path |
|
716 self.cachestat = filecacheentry.stat(self.path) |
|
717 |
|
718 if self.cachestat: |
|
719 self._cacheable = self.cachestat.cacheable() |
|
720 else: |
|
721 # None means we don't know yet |
|
722 self._cacheable = None |
|
723 |
|
724 def refresh(self): |
|
725 if self.cacheable(): |
|
726 self.cachestat = filecacheentry.stat(self.path) |
|
727 |
|
728 def cacheable(self): |
|
729 if self._cacheable is not None: |
|
730 return self._cacheable |
|
731 |
|
732 # we don't know yet, assume it is for now |
|
733 return True |
|
734 |
|
735 def changed(self): |
|
736 # no point in going further if we can't cache it |
|
737 if not self.cacheable(): |
|
738 return True |
|
739 |
|
740 newstat = filecacheentry.stat(self.path) |
|
741 |
|
742 # we may not know if it's cacheable yet, check again now |
|
743 if newstat and self._cacheable is None: |
|
744 self._cacheable = newstat.cacheable() |
|
745 |
|
746 # check again |
|
747 if not self._cacheable: |
|
748 return True |
|
749 |
|
750 if self.cachestat != newstat: |
|
751 self.cachestat = newstat |
|
752 return True |
|
753 else: |
|
754 return False |
|
755 |
|
756 @staticmethod |
|
757 def stat(path): |
|
758 try: |
|
759 return util.cachestat(path) |
|
760 except OSError, e: |
|
761 if e.errno != errno.ENOENT: |
|
762 raise |
|
763 |
|
764 class filecache(object): |
|
765 '''A property like decorator that tracks a file under .hg/ for updates. |
|
766 |
|
767 Records stat info when called in _filecache. |
|
768 |
|
769 On subsequent calls, compares old stat info with new info, and recreates |
|
770 the object when needed, updating the new stat info in _filecache. |
|
771 |
|
772 Mercurial either atomic renames or appends for files under .hg, |
|
773 so to ensure the cache is reliable we need the filesystem to be able |
|
774 to tell us if a file has been replaced. If it can't, we fallback to |
|
775 recreating the object on every call (essentially the same behaviour as |
|
776 propertycache).''' |
|
777 def __init__(self, path, instore=False): |
|
778 self.path = path |
|
779 self.instore = instore |
|
780 |
|
781 def __call__(self, func): |
|
782 self.func = func |
|
783 self.name = func.__name__ |
|
784 return self |
|
785 |
|
786 def __get__(self, obj, type=None): |
|
787 entry = obj._filecache.get(self.name) |
|
788 |
|
789 if entry: |
|
790 if entry.changed(): |
|
791 entry.obj = self.func(obj) |
|
792 else: |
|
793 path = self.instore and obj.sjoin(self.path) or obj.join(self.path) |
|
794 |
|
795 # We stat -before- creating the object so our cache doesn't lie if |
|
796 # a writer modified between the time we read and stat |
|
797 entry = filecacheentry(path) |
|
798 entry.obj = self.func(obj) |
|
799 |
|
800 obj._filecache[self.name] = entry |
|
801 |
|
802 setattr(obj, self.name, entry.obj) |
|
803 return entry.obj |