diff --git a/llvm/utils/revert_checker.py b/llvm/utils/revert_checker.py index 3c3ff3e6b846..ced68bc8475d 100755 --- a/llvm/utils/revert_checker.py +++ b/llvm/utils/revert_checker.py @@ -41,6 +41,7 @@ origin/release/12.x) are deduplicated in output. import argparse import collections +import itertools import logging import re import subprocess @@ -246,7 +247,11 @@ def _load_pr_commit_mappings( # enough for the 99% case of reverts: rarely should someone land a cleanish # revert of a >6 month old change... def find_reverts( - git_dir: str, across_ref: str, root: str, max_pr_lookback: int = 20000 + git_dir: str, + across_ref: str, + root: str, + max_pr_lookback: int = 20000, + stop_at_sha: Optional[str] = None, ) -> List[Revert]: """Finds reverts across `across_ref` in `git_dir`, starting from `root`. @@ -260,6 +265,9 @@ def find_reverts( SHAs. These heuristics require that commit history from `root` to `some_parent_of_root` is loaded in memory. `max_pr_lookback` is how many commits behind `across_ref` should be loaded in memory. + stop_at_sha: If non-None and `stop_at_sha` is encountered while walking + to `across_ref` from `root`, stop checking for reverts. This allows for + faster incremental checking between `find_reverts` calls. """ across_sha = _rev_parse(git_dir, across_ref) root_sha = _rev_parse(git_dir, root) @@ -281,10 +289,16 @@ def find_reverts( root_sha, ) + commit_log_stream: Iterable[_LogEntry] = _log_stream(git_dir, root_sha, across_sha) + if stop_at_sha: + commit_log_stream = itertools.takewhile( + lambda x: x.sha != stop_at_sha, commit_log_stream + ) + all_reverts = [] # Lazily load PR <-> commit mappings, since it can be expensive. pr_commit_mappings = None - for sha, commit_message in _log_stream(git_dir, root_sha, across_sha): + for sha, commit_message in commit_log_stream: reverts, pr_reverts = _try_parse_reverts_from_commit_message( commit_message, ) diff --git a/llvm/utils/revert_checker_test.py b/llvm/utils/revert_checker_test.py index c149be8dc0dd..b99a920402a8 100755 --- a/llvm/utils/revert_checker_test.py +++ b/llvm/utils/revert_checker_test.py @@ -130,6 +130,45 @@ class Test(unittest.TestCase): ], ) + def test_stop_at_sha_stops_early(self) -> None: + reverts = revert_checker.find_reverts( + git_dir=get_llvm_project_path(), + # This SHA is a direct child of the reverted SHA expected below. + across_ref="2d5f3b0a61fb171617012a2c3ba05fd31fb3bb1d", + # This SHA is a direct child of the revert SHA listed below. + root="2c01b278580212914ec037bb5dd9b73702dfe7f1", + max_pr_lookback=50, + # This SHA is the first revert that would be returned, if not for + # `stop_at_sha`. + stop_at_sha="50866e84d1da8462aeb96607bf6d9e5bbd5869c5", + ) + self.assertEqual(reverts, []) + + def test_stop_at_sha_still_catches_reverts_in_range(self) -> None: + reverts = revert_checker.find_reverts( + git_dir=get_llvm_project_path(), + # This SHA is a direct child of the reverted SHA expected below. + across_ref="2d5f3b0a61fb171617012a2c3ba05fd31fb3bb1d", + # This SHA is the direct child of the revert mentioned in + # `assertEqual` below. + root="2c01b278580212914ec037bb5dd9b73702dfe7f1", + max_pr_lookback=50, + # This SHA is the direct parent of the revert mentioned in + # `assertEqual` below. + stop_at_sha="b96ebee1fab2b281c97deb54f3d61c469fe07d01", + ) + self.assertEqual( + reverts, + [ + revert_checker.Revert( + # This SHA is a `Reverts ${PR}` for #111004. + sha="50866e84d1da8462aeb96607bf6d9e5bbd5869c5", + # ...And this was the commit for #111004. + reverted_sha="67160c5ab5f5b7fd5fa7851abcfde367c8a9f91b", + ), + ], + ) + def test_pr_based_revert_works(self) -> None: reverts = revert_checker.find_reverts( git_dir=get_llvm_project_path(),