Mercurial > public > mercurial-scm > hg
comparison mercurial/scmutil.py @ 14928:dca59d5be12d
scmutil: introduce filecache
The idea is being able to associate a file with a property, and watch
that file stat info for modifications when we decide it's important for it to
be up-to-date. Once it changes, we recreate the object.
On filesystems that can't uniquely identify a file, we always recreate.
As a consequence, localrepo.invalidate() will become much less expensive in the
case where nothing changed on-disk.
author | Idan Kamara <idankk86@gmail.com> |
---|---|
date | Sat, 09 Jul 2011 19:06:59 +0300 |
parents | 6ed2a449cb5b |
children | 5523529bd1af |
comparison
equal
deleted
inserted
replaced
14927:2aa3e07b2f07 | 14928:dca59d5be12d |
---|---|
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 |