summaryrefslogtreecommitdiffstats
path: root/subcmds/sync.py
diff options
context:
space:
mode:
authorJason Chang <jasonnc@google.com>2023-07-14 16:45:35 -0700
committerLUCI <gerrit-scoped@luci-project-accounts.iam.gserviceaccount.com>2023-08-02 18:29:05 +0000
commit32b59565b7bd41ec1a121869823557f0b2b022d7 (patch)
tree0cd0fe644ecc6e319df96861f26b77a55c9969eb /subcmds/sync.py
parenta6413f5d88f12466b3daa833668d0f59fc65ece4 (diff)
downloadgit-repo-32b59565b7bd41ec1a121869823557f0b2b022d7.tar.gz
Refactor errors for sync command
Per discussion in go/repo-error-update updated aggregated and exit errors for sync command. Aggregated errors are errors that result in eventual command failure. Exit errors are errors that result in immediate command failure. Also updated main.py to log aggregated and exit errors to git sessions log Bug: b/293344017 Change-Id: I77a21f14da32fe2e68c16841feb22de72e86a251 Reviewed-on: https://gerrit-review.googlesource.com/c/git-repo/+/379614 Reviewed-by: Aravind Vasudevan <aravindvasudev@google.com> Tested-by: Jason Chang <jasonnc@google.com> Commit-Queue: Jason Chang <jasonnc@google.com>
Diffstat (limited to 'subcmds/sync.py')
-rw-r--r--subcmds/sync.py159
1 files changed, 114 insertions, 45 deletions
diff --git a/subcmds/sync.py b/subcmds/sync.py
index 5f8bc2f0..eaca50c9 100644
--- a/subcmds/sync.py
+++ b/subcmds/sync.py
@@ -63,9 +63,16 @@ from command import (
63 MirrorSafeCommand, 63 MirrorSafeCommand,
64 WORKER_BATCH_SIZE, 64 WORKER_BATCH_SIZE,
65) 65)
66from error import RepoChangedException, GitError 66from error import (
67 RepoChangedException,
68 GitError,
69 RepoExitError,
70 SyncError,
71 UpdateManifestError,
72 RepoUnhandledExceptionError,
73)
67import platform_utils 74import platform_utils
68from project import SyncBuffer 75from project import SyncBuffer, DeleteWorktreeError
69from progress import Progress, elapsed_str, jobs_str 76from progress import Progress, elapsed_str, jobs_str
70from repo_trace import Trace 77from repo_trace import Trace
71import ssh 78import ssh
@@ -94,6 +101,7 @@ class _FetchOneResult(NamedTuple):
94 """ 101 """
95 102
96 success: bool 103 success: bool
104 errors: List[Exception]
97 project: Project 105 project: Project
98 start: float 106 start: float
99 finish: float 107 finish: float
@@ -110,6 +118,7 @@ class _FetchResult(NamedTuple):
110 118
111 success: bool 119 success: bool
112 projects: Set[str] 120 projects: Set[str]
121 errors: List[Exception]
113 122
114 123
115class _FetchMainResult(NamedTuple): 124class _FetchMainResult(NamedTuple):
@@ -120,6 +129,7 @@ class _FetchMainResult(NamedTuple):
120 """ 129 """
121 130
122 all_projects: List[Project] 131 all_projects: List[Project]
132 errors: List[Exception]
123 133
124 134
125class _CheckoutOneResult(NamedTuple): 135class _CheckoutOneResult(NamedTuple):
@@ -133,11 +143,24 @@ class _CheckoutOneResult(NamedTuple):
133 """ 143 """
134 144
135 success: bool 145 success: bool
146 errors: List[Exception]
136 project: Project 147 project: Project
137 start: float 148 start: float
138 finish: float 149 finish: float
139 150
140 151
152class SuperprojectError(SyncError):
153 """Superproject sync repo."""
154
155
156class SyncFailFastError(SyncError):
157 """Sync exit error when --fail-fast set."""
158
159
160class SmartSyncError(SyncError):
161 """Smart sync exit error."""
162
163
141class Sync(Command, MirrorSafeCommand): 164class Sync(Command, MirrorSafeCommand):
142 COMMON = True 165 COMMON = True
143 MULTI_MANIFEST_SUPPORT = True 166 MULTI_MANIFEST_SUPPORT = True
@@ -588,7 +611,7 @@ later is required to fix a server side protocol bug.
588 file=sys.stderr, 611 file=sys.stderr,
589 ) 612 )
590 if update_result.fatal and opt.use_superproject is not None: 613 if update_result.fatal and opt.use_superproject is not None:
591 sys.exit(1) 614 raise SuperprojectError()
592 if need_unload: 615 if need_unload:
593 m.outer_client.manifest.Unload() 616 m.outer_client.manifest.Unload()
594 617
@@ -621,6 +644,7 @@ later is required to fix a server side protocol bug.
621 self._sync_dict[k] = start 644 self._sync_dict[k] = start
622 success = False 645 success = False
623 remote_fetched = False 646 remote_fetched = False
647 errors = []
624 buf = io.StringIO() 648 buf = io.StringIO()
625 try: 649 try:
626 sync_result = project.Sync_NetworkHalf( 650 sync_result = project.Sync_NetworkHalf(
@@ -644,6 +668,8 @@ later is required to fix a server side protocol bug.
644 ) 668 )
645 success = sync_result.success 669 success = sync_result.success
646 remote_fetched = sync_result.remote_fetched 670 remote_fetched = sync_result.remote_fetched
671 if sync_result.error:
672 errors.append(sync_result.error)
647 673
648 output = buf.getvalue() 674 output = buf.getvalue()
649 if (opt.verbose or not success) and output: 675 if (opt.verbose or not success) and output:
@@ -659,6 +685,7 @@ later is required to fix a server side protocol bug.
659 print(f"Keyboard interrupt while processing {project.name}") 685 print(f"Keyboard interrupt while processing {project.name}")
660 except GitError as e: 686 except GitError as e:
661 print("error.GitError: Cannot fetch %s" % str(e), file=sys.stderr) 687 print("error.GitError: Cannot fetch %s" % str(e), file=sys.stderr)
688 errors.append(e)
662 except Exception as e: 689 except Exception as e:
663 print( 690 print(
664 "error: Cannot fetch %s (%s: %s)" 691 "error: Cannot fetch %s (%s: %s)"
@@ -666,11 +693,14 @@ later is required to fix a server side protocol bug.
666 file=sys.stderr, 693 file=sys.stderr,
667 ) 694 )
668 del self._sync_dict[k] 695 del self._sync_dict[k]
696 errors.append(e)
669 raise 697 raise
670 698
671 finish = time.time() 699 finish = time.time()
672 del self._sync_dict[k] 700 del self._sync_dict[k]
673 return _FetchOneResult(success, project, start, finish, remote_fetched) 701 return _FetchOneResult(
702 success, errors, project, start, finish, remote_fetched
703 )
674 704
675 @classmethod 705 @classmethod
676 def _FetchInitChild(cls, ssh_proxy): 706 def _FetchInitChild(cls, ssh_proxy):
@@ -701,6 +731,7 @@ later is required to fix a server side protocol bug.
701 jobs = opt.jobs_network 731 jobs = opt.jobs_network
702 fetched = set() 732 fetched = set()
703 remote_fetched = set() 733 remote_fetched = set()
734 errors = []
704 pm = Progress( 735 pm = Progress(
705 "Fetching", 736 "Fetching",
706 len(projects), 737 len(projects),
@@ -745,6 +776,8 @@ later is required to fix a server side protocol bug.
745 finish, 776 finish,
746 success, 777 success,
747 ) 778 )
779 if result.errors:
780 errors.extend(result.errors)
748 if result.remote_fetched: 781 if result.remote_fetched:
749 remote_fetched.add(project) 782 remote_fetched.add(project)
750 # Check for any errors before running any more tasks. 783 # Check for any errors before running any more tasks.
@@ -813,7 +846,7 @@ later is required to fix a server side protocol bug.
813 if not self.outer_client.manifest.IsArchive: 846 if not self.outer_client.manifest.IsArchive:
814 self._GCProjects(projects, opt, err_event) 847 self._GCProjects(projects, opt, err_event)
815 848
816 return _FetchResult(ret, fetched) 849 return _FetchResult(ret, fetched, errors)
817 850
818 def _FetchMain( 851 def _FetchMain(
819 self, opt, args, all_projects, err_event, ssh_proxy, manifest 852 self, opt, args, all_projects, err_event, ssh_proxy, manifest
@@ -832,6 +865,7 @@ later is required to fix a server side protocol bug.
832 List of all projects that should be checked out. 865 List of all projects that should be checked out.
833 """ 866 """
834 rp = manifest.repoProject 867 rp = manifest.repoProject
868 errors = []
835 869
836 to_fetch = [] 870 to_fetch = []
837 now = time.time() 871 now = time.time()
@@ -843,6 +877,9 @@ later is required to fix a server side protocol bug.
843 result = self._Fetch(to_fetch, opt, err_event, ssh_proxy) 877 result = self._Fetch(to_fetch, opt, err_event, ssh_proxy)
844 success = result.success 878 success = result.success
845 fetched = result.projects 879 fetched = result.projects
880 if result.errors:
881 errors.extend(result.errors)
882
846 if not success: 883 if not success:
847 err_event.set() 884 err_event.set()
848 885
@@ -854,8 +891,11 @@ later is required to fix a server side protocol bug.
854 "\nerror: Exited sync due to fetch errors.\n", 891 "\nerror: Exited sync due to fetch errors.\n",
855 file=sys.stderr, 892 file=sys.stderr,
856 ) 893 )
857 sys.exit(1) 894 raise SyncError(
858 return _FetchMainResult([]) 895 "error: Exited sync due to fetch errors.",
896 aggregate_errors=errors,
897 )
898 return _FetchMainResult([], errors)
859 899
860 # Iteratively fetch missing and/or nested unregistered submodules. 900 # Iteratively fetch missing and/or nested unregistered submodules.
861 previously_missing_set = set() 901 previously_missing_set = set()
@@ -883,11 +923,13 @@ later is required to fix a server side protocol bug.
883 result = self._Fetch(missing, opt, err_event, ssh_proxy) 923 result = self._Fetch(missing, opt, err_event, ssh_proxy)
884 success = result.success 924 success = result.success
885 new_fetched = result.projects 925 new_fetched = result.projects
926 if result.errors:
927 errors.extend(result.errors)
886 if not success: 928 if not success:
887 err_event.set() 929 err_event.set()
888 fetched.update(new_fetched) 930 fetched.update(new_fetched)
889 931
890 return _FetchMainResult(all_projects) 932 return _FetchMainResult(all_projects, errors)
891 933
892 def _CheckoutOne(self, detach_head, force_sync, project): 934 def _CheckoutOne(self, detach_head, force_sync, project):
893 """Checkout work tree for one project 935 """Checkout work tree for one project
@@ -905,8 +947,11 @@ later is required to fix a server side protocol bug.
905 project.manifest.manifestProject.config, detach_head=detach_head 947 project.manifest.manifestProject.config, detach_head=detach_head
906 ) 948 )
907 success = False 949 success = False
950 errors = []
908 try: 951 try:
909 project.Sync_LocalHalf(syncbuf, force_sync=force_sync) 952 project.Sync_LocalHalf(
953 syncbuf, force_sync=force_sync, errors=errors
954 )
910 success = syncbuf.Finish() 955 success = syncbuf.Finish()
911 except GitError as e: 956 except GitError as e:
912 print( 957 print(
@@ -914,6 +959,7 @@ later is required to fix a server side protocol bug.
914 % (project.name, str(e)), 959 % (project.name, str(e)),
915 file=sys.stderr, 960 file=sys.stderr,
916 ) 961 )
962 errors.append(e)
917 except Exception as e: 963 except Exception as e:
918 print( 964 print(
919 "error: Cannot checkout %s: %s: %s" 965 "error: Cannot checkout %s: %s: %s"
@@ -925,9 +971,9 @@ later is required to fix a server side protocol bug.
925 if not success: 971 if not success:
926 print("error: Cannot checkout %s" % (project.name), file=sys.stderr) 972 print("error: Cannot checkout %s" % (project.name), file=sys.stderr)
927 finish = time.time() 973 finish = time.time()
928 return _CheckoutOneResult(success, project, start, finish) 974 return _CheckoutOneResult(success, errors, project, start, finish)
929 975
930 def _Checkout(self, all_projects, opt, err_results): 976 def _Checkout(self, all_projects, opt, err_results, checkout_errors):
931 """Checkout projects listed in all_projects 977 """Checkout projects listed in all_projects
932 978
933 Args: 979 Args:
@@ -949,6 +995,10 @@ later is required to fix a server side protocol bug.
949 self.event_log.AddSync( 995 self.event_log.AddSync(
950 project, event_log.TASK_SYNC_LOCAL, start, finish, success 996 project, event_log.TASK_SYNC_LOCAL, start, finish, success
951 ) 997 )
998
999 if result.errors:
1000 checkout_errors.extend(result.errors)
1001
952 # Check for any errors before running any more tasks. 1002 # Check for any errors before running any more tasks.
953 # ...we'll let existing jobs finish, though. 1003 # ...we'll let existing jobs finish, though.
954 if success: 1004 if success:
@@ -1214,10 +1264,9 @@ later is required to fix a server side protocol bug.
1214 revisionId=None, 1264 revisionId=None,
1215 groups=None, 1265 groups=None,
1216 ) 1266 )
1217 if not project.DeleteWorktree( 1267 project.DeleteWorktree(
1218 quiet=opt.quiet, force=opt.force_remove_dirty 1268 quiet=opt.quiet, force=opt.force_remove_dirty
1219 ): 1269 )
1220 return 1
1221 1270
1222 new_project_paths.sort() 1271 new_project_paths.sort()
1223 with open(file_path, "w") as fd: 1272 with open(file_path, "w") as fd:
@@ -1260,7 +1309,7 @@ later is required to fix a server side protocol bug.
1260 file=sys.stderr, 1309 file=sys.stderr,
1261 ) 1310 )
1262 platform_utils.remove(copylinkfile_path) 1311 platform_utils.remove(copylinkfile_path)
1263 return False 1312 raise
1264 1313
1265 need_remove_files = [] 1314 need_remove_files = []
1266 need_remove_files.extend( 1315 need_remove_files.extend(
@@ -1285,12 +1334,10 @@ later is required to fix a server side protocol bug.
1285 1334
1286 def _SmartSyncSetup(self, opt, smart_sync_manifest_path, manifest): 1335 def _SmartSyncSetup(self, opt, smart_sync_manifest_path, manifest):
1287 if not manifest.manifest_server: 1336 if not manifest.manifest_server:
1288 print( 1337 raise SmartSyncError(
1289 "error: cannot smart sync: no manifest server defined in " 1338 "error: cannot smart sync: no manifest server defined in "
1290 "manifest", 1339 "manifest"
1291 file=sys.stderr,
1292 ) 1340 )
1293 sys.exit(1)
1294 1341
1295 manifest_server = manifest.manifest_server 1342 manifest_server = manifest.manifest_server
1296 if not opt.quiet: 1343 if not opt.quiet:
@@ -1368,33 +1415,28 @@ later is required to fix a server side protocol bug.
1368 with open(smart_sync_manifest_path, "w") as f: 1415 with open(smart_sync_manifest_path, "w") as f:
1369 f.write(manifest_str) 1416 f.write(manifest_str)
1370 except IOError as e: 1417 except IOError as e:
1371 print( 1418 raise SmartSyncError(
1372 "error: cannot write manifest to %s:\n%s" 1419 "error: cannot write manifest to %s:\n%s"
1373 % (smart_sync_manifest_path, e), 1420 % (smart_sync_manifest_path, e),
1374 file=sys.stderr, 1421 aggregate_errors=[e],
1375 ) 1422 )
1376 sys.exit(1)
1377 self._ReloadManifest(manifest_name, manifest) 1423 self._ReloadManifest(manifest_name, manifest)
1378 else: 1424 else:
1379 print( 1425 raise SmartSyncError(
1380 "error: manifest server RPC call failed: %s" % manifest_str, 1426 "error: manifest server RPC call failed: %s" % manifest_str
1381 file=sys.stderr,
1382 ) 1427 )
1383 sys.exit(1)
1384 except (socket.error, IOError, xmlrpc.client.Fault) as e: 1428 except (socket.error, IOError, xmlrpc.client.Fault) as e:
1385 print( 1429 raise SmartSyncError(
1386 "error: cannot connect to manifest server %s:\n%s" 1430 "error: cannot connect to manifest server %s:\n%s"
1387 % (manifest.manifest_server, e), 1431 % (manifest.manifest_server, e),
1388 file=sys.stderr, 1432 aggregate_errors=[e],
1389 ) 1433 )
1390 sys.exit(1)
1391 except xmlrpc.client.ProtocolError as e: 1434 except xmlrpc.client.ProtocolError as e:
1392 print( 1435 raise SmartSyncError(
1393 "error: cannot connect to manifest server %s:\n%d %s" 1436 "error: cannot connect to manifest server %s:\n%d %s"
1394 % (manifest.manifest_server, e.errcode, e.errmsg), 1437 % (manifest.manifest_server, e.errcode, e.errmsg),
1395 file=sys.stderr, 1438 aggregate_errors=[e],
1396 ) 1439 )
1397 sys.exit(1)
1398 1440
1399 return manifest_name 1441 return manifest_name
1400 1442
@@ -1436,7 +1478,7 @@ later is required to fix a server side protocol bug.
1436 """ 1478 """
1437 if not opt.local_only: 1479 if not opt.local_only:
1438 start = time.time() 1480 start = time.time()
1439 success = mp.Sync_NetworkHalf( 1481 result = mp.Sync_NetworkHalf(
1440 quiet=opt.quiet, 1482 quiet=opt.quiet,
1441 verbose=opt.verbose, 1483 verbose=opt.verbose,
1442 current_branch_only=self._GetCurrentBranchOnly( 1484 current_branch_only=self._GetCurrentBranchOnly(
@@ -1453,19 +1495,24 @@ later is required to fix a server side protocol bug.
1453 ) 1495 )
1454 finish = time.time() 1496 finish = time.time()
1455 self.event_log.AddSync( 1497 self.event_log.AddSync(
1456 mp, event_log.TASK_SYNC_NETWORK, start, finish, success 1498 mp, event_log.TASK_SYNC_NETWORK, start, finish, result.success
1457 ) 1499 )
1458 1500
1459 if mp.HasChanges: 1501 if mp.HasChanges:
1502 errors = []
1460 syncbuf = SyncBuffer(mp.config) 1503 syncbuf = SyncBuffer(mp.config)
1461 start = time.time() 1504 start = time.time()
1462 mp.Sync_LocalHalf(syncbuf, submodules=mp.manifest.HasSubmodules) 1505 mp.Sync_LocalHalf(
1506 syncbuf, submodules=mp.manifest.HasSubmodules, errors=errors
1507 )
1463 clean = syncbuf.Finish() 1508 clean = syncbuf.Finish()
1464 self.event_log.AddSync( 1509 self.event_log.AddSync(
1465 mp, event_log.TASK_SYNC_LOCAL, start, time.time(), clean 1510 mp, event_log.TASK_SYNC_LOCAL, start, time.time(), clean
1466 ) 1511 )
1467 if not clean: 1512 if not clean:
1468 sys.exit(1) 1513 raise UpdateManifestError(
1514 aggregate_errors=errors, project=mp.name
1515 )
1469 self._ReloadManifest(manifest_name, mp.manifest) 1516 self._ReloadManifest(manifest_name, mp.manifest)
1470 1517
1471 def ValidateOptions(self, opt, args): 1518 def ValidateOptions(self, opt, args):
@@ -1546,6 +1593,15 @@ later is required to fix a server side protocol bug.
1546 opt.jobs_checkout = min(opt.jobs_checkout, jobs_soft_limit) 1593 opt.jobs_checkout = min(opt.jobs_checkout, jobs_soft_limit)
1547 1594
1548 def Execute(self, opt, args): 1595 def Execute(self, opt, args):
1596 errors = []
1597 try:
1598 self._ExecuteHelper(opt, args, errors)
1599 except RepoExitError:
1600 raise
1601 except (KeyboardInterrupt, Exception) as e:
1602 raise RepoUnhandledExceptionError(e, aggregate_errors=errors)
1603
1604 def _ExecuteHelper(self, opt, args, errors):
1549 manifest = self.outer_manifest 1605 manifest = self.outer_manifest
1550 if not opt.outer_manifest: 1606 if not opt.outer_manifest:
1551 manifest = self.manifest 1607 manifest = self.manifest
@@ -1695,6 +1751,8 @@ later is required to fix a server side protocol bug.
1695 result = self._FetchMain( 1751 result = self._FetchMain(
1696 opt, args, all_projects, err_event, ssh_proxy, manifest 1752 opt, args, all_projects, err_event, ssh_proxy, manifest
1697 ) 1753 )
1754 if result.errors:
1755 errors.extend(result.errors)
1698 all_projects = result.all_projects 1756 all_projects = result.all_projects
1699 1757
1700 if opt.network_only: 1758 if opt.network_only:
@@ -1712,36 +1770,47 @@ later is required to fix a server side protocol bug.
1712 "`repo sync -l` will update some local checkouts.", 1770 "`repo sync -l` will update some local checkouts.",
1713 file=sys.stderr, 1771 file=sys.stderr,
1714 ) 1772 )
1715 sys.exit(1) 1773 raise SyncFailFastError(aggregate_errors=errors)
1716 1774
1717 for m in self.ManifestList(opt): 1775 for m in self.ManifestList(opt):
1718 if m.IsMirror or m.IsArchive: 1776 if m.IsMirror or m.IsArchive:
1719 # Bail out now, we have no working tree. 1777 # Bail out now, we have no working tree.
1720 continue 1778 continue
1721 1779
1722 if self.UpdateProjectList(opt, m): 1780 try:
1781 self.UpdateProjectList(opt, m)
1782 except Exception as e:
1723 err_event.set() 1783 err_event.set()
1724 err_update_projects = True 1784 err_update_projects = True
1785 errors.append(e)
1786 if isinstance(e, DeleteWorktreeError):
1787 errors.extend(e.aggregate_errors)
1725 if opt.fail_fast: 1788 if opt.fail_fast:
1726 print( 1789 print(
1727 "\nerror: Local checkouts *not* updated.", 1790 "\nerror: Local checkouts *not* updated.",
1728 file=sys.stderr, 1791 file=sys.stderr,
1729 ) 1792 )
1730 sys.exit(1) 1793 raise SyncFailFastError(aggregate_errors=errors)
1731 1794
1732 err_update_linkfiles = not self.UpdateCopyLinkfileList(m) 1795 err_update_linkfiles = False
1733 if err_update_linkfiles: 1796 try:
1797 self.UpdateCopyLinkfileList(m)
1798 except Exception as e:
1799 err_update_linkfiles = True
1800 errors.append(e)
1734 err_event.set() 1801 err_event.set()
1735 if opt.fail_fast: 1802 if opt.fail_fast:
1736 print( 1803 print(
1737 "\nerror: Local update copyfile or linkfile failed.", 1804 "\nerror: Local update copyfile or linkfile failed.",
1738 file=sys.stderr, 1805 file=sys.stderr,
1739 ) 1806 )
1740 sys.exit(1) 1807 raise SyncFailFastError(aggregate_errors=errors)
1741 1808
1742 err_results = [] 1809 err_results = []
1743 # NB: We don't exit here because this is the last step. 1810 # NB: We don't exit here because this is the last step.
1744 err_checkout = not self._Checkout(all_projects, opt, err_results) 1811 err_checkout = not self._Checkout(
1812 all_projects, opt, err_results, errors
1813 )
1745 if err_checkout: 1814 if err_checkout:
1746 err_event.set() 1815 err_event.set()
1747 1816
@@ -1784,7 +1853,7 @@ later is required to fix a server side protocol bug.
1784 "error.", 1853 "error.",
1785 file=sys.stderr, 1854 file=sys.stderr,
1786 ) 1855 )
1787 sys.exit(1) 1856 raise SyncError(aggregate_errors=errors)
1788 1857
1789 # Log the previous sync analysis state from the config. 1858 # Log the previous sync analysis state from the config.
1790 self.git_event_log.LogDataConfigEvents( 1859 self.git_event_log.LogDataConfigEvents(
@@ -1842,7 +1911,7 @@ def _PostRepoFetch(rp, repo_verify=True, verbose=False):
1842 try: 1911 try:
1843 rp.work_git.reset("--keep", new_rev) 1912 rp.work_git.reset("--keep", new_rev)
1844 except GitError as e: 1913 except GitError as e:
1845 sys.exit(str(e)) 1914 raise RepoUnhandledExceptionError(e)
1846 print("info: Restarting repo with latest version", file=sys.stderr) 1915 print("info: Restarting repo with latest version", file=sys.stderr)
1847 raise RepoChangedException(["--repo-upgraded"]) 1916 raise RepoChangedException(["--repo-upgraded"])
1848 else: 1917 else: