From 68a2be3add29fc8fe0c4eaa0ec297dbf5764848b Mon Sep 17 00:00:00 2001 From: jomae Date: Thu, 18 Apr 2024 01:12:27 +0000 Subject: [PATCH] 1.6.1dev: fix `IndexError` raised from `GitRepository.normalize_rev('FFFF')` if the repository has 5,000+ revisions (closes #13747) git-svn-id: http://trac.edgewall.org/intertrac/log:/branches/1.6-stable@17769 af82e41b-90c4-0310-8c96-b1721e28e2e2 --- tracopt/versioncontrol/git/PyGIT.py | 2 +- tracopt/versioncontrol/git/tests/PyGIT.py | 60 +++++++++++++++++++++++ 2 files changed, 61 insertions(+), 1 deletion(-) diff --git a/tracopt/versioncontrol/git/PyGIT.py b/tracopt/versioncontrol/git/PyGIT.py index e1e660b19b..62f0a4bf01 100644 --- a/tracopt/versioncontrol/git/PyGIT.py +++ b/tracopt/versioncontrol/git/PyGIT.py @@ -845,7 +845,7 @@ def fullrev(self, rev): try: srevs = _rev_cache.srev_dict[self.__rev_key(rev)] - except KeyError: + except (KeyError, IndexError): return None resolved = None diff --git a/tracopt/versioncontrol/git/tests/PyGIT.py b/tracopt/versioncontrol/git/tests/PyGIT.py index 51dc5b3a8d..6aec05bf7b 100644 --- a/tracopt/versioncontrol/git/tests/PyGIT.py +++ b/tracopt/versioncontrol/git/tests/PyGIT.py @@ -12,6 +12,7 @@ # history and logs, available at https://trac.edgewall.org/log/. import os +import subprocess import tempfile import unittest from datetime import datetime @@ -474,6 +475,64 @@ def validate(path, quotepath): validate(paths[1], 'false') +class StorageTestCase(unittest.TestCase, GitCommandMixin): + + def setUp(self): + self.env = EnvironmentStub() + self.repos_path = mkdtemp() + self._git('init', '--bare') + + def tearDown(self): + self.env.reset_db() + if os.path.isdir(self.repos_path): + rmtree(self.repos_path) + + def _storage(self): + return Storage(self.repos_path, self.env.log, self.git_bin, 'utf-8') + + def _test_srev_dict(self, n_revs, type_): + with self._spawn_git('fast-import', stdin=subprocess.PIPE) as proc: + write = proc.stdin.write + write(b'blob\n') + write(b'mark :1\n') + write(b'data 0\n') + write(b'\n') + write(b'reset refs/heads/master\n') + for i in range(n_revs): + ts = 1000000000 + i + write(b'commit refs/heads/master\n') + write(b'mark :2\n') + write(b'author Joe %d +0000\n' % ts) + write(b'committer Joe %d +0000\n' % ts) + write(b'data 2\n') + write(b'.\n') + write(b'M 100644 :1 .gitignore\n') + write(b'\n') + stdout, stderr = proc.communicate() + self.assertEqual(0, proc.returncode, + 'git exits with %r, stdout %r, stderr %r' % + (proc.returncode, stdout, stderr)) + + storage = self._storage() + self.assertIsInstance(storage.rev_cache.srev_dict, type_) + for i in range(0x10000): + srev_b = b'%04x' % i + frev_b = storage.fullrev(srev_b) + if frev_b is None: + continue + self.assertEqual(frev_b[:4], srev_b) + frev_u = frev_b.decode('ascii') + srev_u = storage.shortrev(frev_u) + self.assertTrue(frev_u.startswith(srev_u), + 'frev_u %(frev_u)r, srev_u %(srev_u)r' % locals()) + + def test_srev_dict_a_dict(self): + self._test_srev_dict(4500, dict) + + def test_srev_dict_a_list(self): + self._test_srev_dict(5500, list) + + class SizedDictTestCase(unittest.TestCase): def test_setdefault_raises(self): @@ -636,6 +695,7 @@ def test_suite(): suite.addTest(makeSuite(TestParseCommit)) suite.addTest(makeSuite(NormalTestCase)) suite.addTest(makeSuite(UnicodeNameTestCase)) + suite.addTest(makeSuite(StorageTestCase)) else: print("SKIP: tracopt/versioncontrol/git/tests/PyGIT.py (git cli " "binary, 'git', not found)")