Mercurial > public > mercurial-scm > hg-stable
comparison mercurial/context.py @ 40736:d98fb3f42f33
context: floor adjustlinkrev graph walk during copy tracing
The `_adjustlinkrev` method gains an optional "stoprev" argument. The linkrev
adjustment will give up once this floor is reached. The relevant functions
using `_adjustlinkrev` are updated to pass an appropriate value in the copy
tracing code.
In some private repository, about 10% of the status call triggered the
pathological case addressed by this change. The speedup varies from one call
to another, the best-observed win is moving from 170s to 11s.
The effect of this change can be seen in the public pypy repository, running the
following command:
hg perftracecopies --source 83c9ff0c0206 --destination 59c79103d5b0
before: 3.401753 seconds
after: 2.634897 seconds (-23%)
author | Boris Feld <boris.feld@octobus.net> |
---|---|
date | Wed, 10 Oct 2018 00:50:37 +0200 |
parents | 69206452a2ac |
children | cb372d09d30a |
comparison
equal
deleted
inserted
replaced
40735:69206452a2ac | 40736:d98fb3f42f33 |
---|---|
710 or self.size() == fctx.size()): | 710 or self.size() == fctx.size()): |
711 return self._filelog.cmp(self._filenode, fctx.data()) | 711 return self._filelog.cmp(self._filenode, fctx.data()) |
712 | 712 |
713 return True | 713 return True |
714 | 714 |
715 def _adjustlinkrev(self, srcrev, inclusive=False): | 715 def _adjustlinkrev(self, srcrev, inclusive=False, stoprev=None): |
716 """return the first ancestor of <srcrev> introducing <fnode> | 716 """return the first ancestor of <srcrev> introducing <fnode> |
717 | 717 |
718 If the linkrev of the file revision does not point to an ancestor of | 718 If the linkrev of the file revision does not point to an ancestor of |
719 srcrev, we'll walk down the ancestors until we find one introducing | 719 srcrev, we'll walk down the ancestors until we find one introducing |
720 this file revision. | 720 this file revision. |
721 | 721 |
722 :srcrev: the changeset revision we search ancestors from | 722 :srcrev: the changeset revision we search ancestors from |
723 :inclusive: if true, the src revision will also be checked | 723 :inclusive: if true, the src revision will also be checked |
724 :stoprev: an optional revision to stop the walk at. If no introduction | |
725 of this file content could be found before this floor | |
726 revision, the function will returns "None" and stops its | |
727 iteration. | |
724 """ | 728 """ |
725 repo = self._repo | 729 repo = self._repo |
726 cl = repo.unfiltered().changelog | 730 cl = repo.unfiltered().changelog |
727 mfl = repo.manifestlog | 731 mfl = repo.manifestlog |
728 # fetch the linkrev | 732 # fetch the linkrev |
746 if iteranc is None: | 750 if iteranc is None: |
747 iteranc = cl.ancestors(revs, lkr, inclusive=inclusive) | 751 iteranc = cl.ancestors(revs, lkr, inclusive=inclusive) |
748 fnode = self._filenode | 752 fnode = self._filenode |
749 path = self._path | 753 path = self._path |
750 for a in iteranc: | 754 for a in iteranc: |
755 if stoprev is not None and a < stoprev: | |
756 return None | |
751 ac = cl.read(a) # get changeset data (we avoid object creation) | 757 ac = cl.read(a) # get changeset data (we avoid object creation) |
752 if path in ac[3]: # checking the 'files' field. | 758 if path in ac[3]: # checking the 'files' field. |
753 # The file has been touched, check if the content is | 759 # The file has been touched, check if the content is |
754 # similar to the one we search for. | 760 # similar to the one we search for. |
755 if fnode == mfl[ac[0]].readfast().get(path): | 761 if fnode == mfl[ac[0]].readfast().get(path): |
763 def isintroducedafter(self, changelogrev): | 769 def isintroducedafter(self, changelogrev): |
764 """True if a filectx has been introduced after a given floor revision | 770 """True if a filectx has been introduced after a given floor revision |
765 """ | 771 """ |
766 if self.linkrev() >= changelogrev: | 772 if self.linkrev() >= changelogrev: |
767 return True | 773 return True |
768 introrev = self._introrev() | 774 introrev = self._introrev(stoprev=changelogrev) |
775 if introrev is None: | |
776 return False | |
769 return introrev >= changelogrev | 777 return introrev >= changelogrev |
770 | 778 |
771 def introrev(self): | 779 def introrev(self): |
772 """return the rev of the changeset which introduced this file revision | 780 """return the rev of the changeset which introduced this file revision |
773 | 781 |
777 'linkrev-shadowing' when a file revision is used by multiple | 785 'linkrev-shadowing' when a file revision is used by multiple |
778 changesets. | 786 changesets. |
779 """ | 787 """ |
780 return self._introrev() | 788 return self._introrev() |
781 | 789 |
782 def _introrev(self): | 790 def _introrev(self, stoprev=None): |
791 """ | |
792 Same as `introrev` but, with an extra argument to limit changelog | |
793 iteration range in some internal usecase. | |
794 | |
795 If `stoprev` is set, the `introrev` will not be searched past that | |
796 `stoprev` revision and "None" might be returned. This is useful to | |
797 limit the iteration range. | |
798 """ | |
783 toprev = None | 799 toprev = None |
784 attrs = vars(self) | 800 attrs = vars(self) |
785 if r'_changeid' in attrs: | 801 if r'_changeid' in attrs: |
786 # We have a cached value already | 802 # We have a cached value already |
787 toprev = self._changeid | 803 toprev = self._changeid |
788 elif r'_changectx' in attrs: | 804 elif r'_changectx' in attrs: |
789 # We know which changelog entry we are coming from | 805 # We know which changelog entry we are coming from |
790 toprev = self._changectx.rev() | 806 toprev = self._changectx.rev() |
791 | 807 |
792 if toprev is not None: | 808 if toprev is not None: |
793 return self._adjustlinkrev(toprev, inclusive=True) | 809 return self._adjustlinkrev(toprev, inclusive=True, stoprev=stoprev) |
794 elif r'_descendantrev' in attrs: | 810 elif r'_descendantrev' in attrs: |
795 introrev = self._adjustlinkrev(self._descendantrev) | 811 introrev = self._adjustlinkrev(self._descendantrev, stoprev=stoprev) |
796 # be nice and cache the result of the computation | 812 # be nice and cache the result of the computation |
797 self._changeid = introrev | 813 if introrev is not None: |
814 self._changeid = introrev | |
798 return introrev | 815 return introrev |
799 else: | 816 else: |
800 return self.linkrev() | 817 return self.linkrev() |
801 | 818 |
802 def introfilectx(self): | 819 def introfilectx(self): |