diff options
Diffstat (limited to 'project.py')
-rw-r--r-- | project.py | 283 |
1 files changed, 196 insertions, 87 deletions
@@ -26,7 +26,7 @@ import sys | |||
26 | import tarfile | 26 | import tarfile |
27 | import tempfile | 27 | import tempfile |
28 | import time | 28 | import time |
29 | from typing import NamedTuple | 29 | from typing import NamedTuple, List |
30 | import urllib.parse | 30 | import urllib.parse |
31 | 31 | ||
32 | from color import Coloring | 32 | from color import Coloring |
@@ -41,7 +41,12 @@ from git_config import ( | |||
41 | ) | 41 | ) |
42 | import git_superproject | 42 | import git_superproject |
43 | from git_trace2_event_log import EventLog | 43 | from git_trace2_event_log import EventLog |
44 | from error import GitError, UploadError, DownloadError | 44 | from error import ( |
45 | GitError, | ||
46 | UploadError, | ||
47 | DownloadError, | ||
48 | RepoError, | ||
49 | ) | ||
45 | from error import ManifestInvalidRevisionError, ManifestInvalidPathError | 50 | from error import ManifestInvalidRevisionError, ManifestInvalidPathError |
46 | from error import NoManifestException, ManifestParseError | 51 | from error import NoManifestException, ManifestParseError |
47 | import platform_utils | 52 | import platform_utils |
@@ -54,11 +59,33 @@ from git_refs import GitRefs, HEAD, R_HEADS, R_TAGS, R_PUB, R_M, R_WORKTREE_M | |||
54 | class SyncNetworkHalfResult(NamedTuple): | 59 | class SyncNetworkHalfResult(NamedTuple): |
55 | """Sync_NetworkHalf return value.""" | 60 | """Sync_NetworkHalf return value.""" |
56 | 61 | ||
57 | # True if successful. | ||
58 | success: bool | ||
59 | # Did we query the remote? False when optimized_fetch is True and we have | 62 | # Did we query the remote? False when optimized_fetch is True and we have |
60 | # the commit already present. | 63 | # the commit already present. |
61 | remote_fetched: bool | 64 | remote_fetched: bool |
65 | # Error from SyncNetworkHalf | ||
66 | error: Exception = None | ||
67 | |||
68 | @property | ||
69 | def success(self) -> bool: | ||
70 | return not self.error | ||
71 | |||
72 | |||
73 | class SyncNetworkHalfError(RepoError): | ||
74 | """Failure trying to sync.""" | ||
75 | |||
76 | |||
77 | class DeleteWorktreeError(RepoError): | ||
78 | """Failure to delete worktree.""" | ||
79 | |||
80 | def __init__( | ||
81 | self, *args, aggregate_errors: List[Exception] = None, **kwargs | ||
82 | ) -> None: | ||
83 | super().__init__(*args, **kwargs) | ||
84 | self.aggregate_errors = aggregate_errors or [] | ||
85 | |||
86 | |||
87 | class DeleteDirtyWorktreeError(DeleteWorktreeError): | ||
88 | """Failure to delete worktree due to uncommitted changes.""" | ||
62 | 89 | ||
63 | 90 | ||
64 | # Maximum sleep time allowed during retries. | 91 | # Maximum sleep time allowed during retries. |
@@ -1070,13 +1097,19 @@ class Project(object): | |||
1070 | if branch is None: | 1097 | if branch is None: |
1071 | branch = self.CurrentBranch | 1098 | branch = self.CurrentBranch |
1072 | if branch is None: | 1099 | if branch is None: |
1073 | raise GitError("not currently on a branch") | 1100 | raise GitError("not currently on a branch", project=self.name) |
1074 | 1101 | ||
1075 | branch = self.GetBranch(branch) | 1102 | branch = self.GetBranch(branch) |
1076 | if not branch.LocalMerge: | 1103 | if not branch.LocalMerge: |
1077 | raise GitError("branch %s does not track a remote" % branch.name) | 1104 | raise GitError( |
1105 | "branch %s does not track a remote" % branch.name, | ||
1106 | project=self.name, | ||
1107 | ) | ||
1078 | if not branch.remote.review: | 1108 | if not branch.remote.review: |
1079 | raise GitError("remote %s has no review url" % branch.remote.name) | 1109 | raise GitError( |
1110 | "remote %s has no review url" % branch.remote.name, | ||
1111 | project=self.name, | ||
1112 | ) | ||
1080 | 1113 | ||
1081 | # Basic validity check on label syntax. | 1114 | # Basic validity check on label syntax. |
1082 | for label in labels: | 1115 | for label in labels: |
@@ -1193,11 +1226,18 @@ class Project(object): | |||
1193 | """ | 1226 | """ |
1194 | if archive and not isinstance(self, MetaProject): | 1227 | if archive and not isinstance(self, MetaProject): |
1195 | if self.remote.url.startswith(("http://", "https://")): | 1228 | if self.remote.url.startswith(("http://", "https://")): |
1229 | msg_template = ( | ||
1230 | "%s: Cannot fetch archives from http/https remotes." | ||
1231 | ) | ||
1232 | msg_args = self.name | ||
1233 | msg = msg_template % msg_args | ||
1196 | _error( | 1234 | _error( |
1197 | "%s: Cannot fetch archives from http/https remotes.", | 1235 | msg_template, |
1198 | self.name, | 1236 | msg_args, |
1237 | ) | ||
1238 | return SyncNetworkHalfResult( | ||
1239 | False, SyncNetworkHalfError(msg, project=self.name) | ||
1199 | ) | 1240 | ) |
1200 | return SyncNetworkHalfResult(False, False) | ||
1201 | 1241 | ||
1202 | name = self.relpath.replace("\\", "/") | 1242 | name = self.relpath.replace("\\", "/") |
1203 | name = name.replace("/", "_") | 1243 | name = name.replace("/", "_") |
@@ -1208,19 +1248,25 @@ class Project(object): | |||
1208 | self._FetchArchive(tarpath, cwd=topdir) | 1248 | self._FetchArchive(tarpath, cwd=topdir) |
1209 | except GitError as e: | 1249 | except GitError as e: |
1210 | _error("%s", e) | 1250 | _error("%s", e) |
1211 | return SyncNetworkHalfResult(False, False) | 1251 | return SyncNetworkHalfResult(False, e) |
1212 | 1252 | ||
1213 | # From now on, we only need absolute tarpath. | 1253 | # From now on, we only need absolute tarpath. |
1214 | tarpath = os.path.join(topdir, tarpath) | 1254 | tarpath = os.path.join(topdir, tarpath) |
1215 | 1255 | ||
1216 | if not self._ExtractArchive(tarpath, path=topdir): | 1256 | if not self._ExtractArchive(tarpath, path=topdir): |
1217 | return SyncNetworkHalfResult(False, True) | 1257 | return SyncNetworkHalfResult( |
1258 | True, | ||
1259 | SyncNetworkHalfError( | ||
1260 | f"Unable to Extract Archive {tarpath}", | ||
1261 | project=self.name, | ||
1262 | ), | ||
1263 | ) | ||
1218 | try: | 1264 | try: |
1219 | platform_utils.remove(tarpath) | 1265 | platform_utils.remove(tarpath) |
1220 | except OSError as e: | 1266 | except OSError as e: |
1221 | _warn("Cannot remove archive %s: %s", tarpath, str(e)) | 1267 | _warn("Cannot remove archive %s: %s", tarpath, str(e)) |
1222 | self._CopyAndLinkFiles() | 1268 | self._CopyAndLinkFiles() |
1223 | return SyncNetworkHalfResult(True, True) | 1269 | return SyncNetworkHalfResult(True) |
1224 | 1270 | ||
1225 | # If the shared object dir already exists, don't try to rebootstrap with | 1271 | # If the shared object dir already exists, don't try to rebootstrap with |
1226 | # a clone bundle download. We should have the majority of objects | 1272 | # a clone bundle download. We should have the majority of objects |
@@ -1310,23 +1356,35 @@ class Project(object): | |||
1310 | ) | 1356 | ) |
1311 | ): | 1357 | ): |
1312 | remote_fetched = True | 1358 | remote_fetched = True |
1313 | if not self._RemoteFetch( | 1359 | try: |
1314 | initial=is_new, | 1360 | if not self._RemoteFetch( |
1315 | quiet=quiet, | 1361 | initial=is_new, |
1316 | verbose=verbose, | 1362 | quiet=quiet, |
1317 | output_redir=output_redir, | 1363 | verbose=verbose, |
1318 | alt_dir=alt_dir, | 1364 | output_redir=output_redir, |
1319 | current_branch_only=current_branch_only, | 1365 | alt_dir=alt_dir, |
1320 | tags=tags, | 1366 | current_branch_only=current_branch_only, |
1321 | prune=prune, | 1367 | tags=tags, |
1322 | depth=depth, | 1368 | prune=prune, |
1323 | submodules=submodules, | 1369 | depth=depth, |
1324 | force_sync=force_sync, | 1370 | submodules=submodules, |
1325 | ssh_proxy=ssh_proxy, | 1371 | force_sync=force_sync, |
1326 | clone_filter=clone_filter, | 1372 | ssh_proxy=ssh_proxy, |
1327 | retry_fetches=retry_fetches, | 1373 | clone_filter=clone_filter, |
1328 | ): | 1374 | retry_fetches=retry_fetches, |
1329 | return SyncNetworkHalfResult(False, remote_fetched) | 1375 | ): |
1376 | return SyncNetworkHalfResult( | ||
1377 | remote_fetched, | ||
1378 | SyncNetworkHalfError( | ||
1379 | f"Unable to remote fetch project {self.name}", | ||
1380 | project=self.name, | ||
1381 | ), | ||
1382 | ) | ||
1383 | except RepoError as e: | ||
1384 | return SyncNetworkHalfResult( | ||
1385 | remote_fetched, | ||
1386 | e, | ||
1387 | ) | ||
1330 | 1388 | ||
1331 | mp = self.manifest.manifestProject | 1389 | mp = self.manifest.manifestProject |
1332 | dissociate = mp.dissociate | 1390 | dissociate = mp.dissociate |
@@ -1346,7 +1404,12 @@ class Project(object): | |||
1346 | if p.stdout and output_redir: | 1404 | if p.stdout and output_redir: |
1347 | output_redir.write(p.stdout) | 1405 | output_redir.write(p.stdout) |
1348 | if p.Wait() != 0: | 1406 | if p.Wait() != 0: |
1349 | return SyncNetworkHalfResult(False, remote_fetched) | 1407 | return SyncNetworkHalfResult( |
1408 | remote_fetched, | ||
1409 | GitError( | ||
1410 | "Unable to repack alternates", project=self.name | ||
1411 | ), | ||
1412 | ) | ||
1350 | platform_utils.remove(alternates_file) | 1413 | platform_utils.remove(alternates_file) |
1351 | 1414 | ||
1352 | if self.worktree: | 1415 | if self.worktree: |
@@ -1356,7 +1419,7 @@ class Project(object): | |||
1356 | platform_utils.remove( | 1419 | platform_utils.remove( |
1357 | os.path.join(self.gitdir, "FETCH_HEAD"), missing_ok=True | 1420 | os.path.join(self.gitdir, "FETCH_HEAD"), missing_ok=True |
1358 | ) | 1421 | ) |
1359 | return SyncNetworkHalfResult(True, remote_fetched) | 1422 | return SyncNetworkHalfResult(remote_fetched) |
1360 | 1423 | ||
1361 | def PostRepoUpgrade(self): | 1424 | def PostRepoUpgrade(self): |
1362 | self._InitHooks() | 1425 | self._InitHooks() |
@@ -1409,16 +1472,27 @@ class Project(object): | |||
1409 | 1472 | ||
1410 | self.revisionId = revisionId | 1473 | self.revisionId = revisionId |
1411 | 1474 | ||
1412 | def Sync_LocalHalf(self, syncbuf, force_sync=False, submodules=False): | 1475 | def Sync_LocalHalf( |
1476 | self, syncbuf, force_sync=False, submodules=False, errors=None | ||
1477 | ): | ||
1413 | """Perform only the local IO portion of the sync process. | 1478 | """Perform only the local IO portion of the sync process. |
1414 | 1479 | ||
1415 | Network access is not required. | 1480 | Network access is not required. |
1416 | """ | 1481 | """ |
1482 | if errors is None: | ||
1483 | errors = [] | ||
1484 | |||
1485 | def fail(error: Exception): | ||
1486 | errors.append(error) | ||
1487 | syncbuf.fail(self, error) | ||
1488 | |||
1417 | if not os.path.exists(self.gitdir): | 1489 | if not os.path.exists(self.gitdir): |
1418 | syncbuf.fail( | 1490 | fail( |
1419 | self, | 1491 | LocalSyncFail( |
1420 | "Cannot checkout %s due to missing network sync; Run " | 1492 | "Cannot checkout %s due to missing network sync; Run " |
1421 | "`repo sync -n %s` first." % (self.name, self.name), | 1493 | "`repo sync -n %s` first." % (self.name, self.name), |
1494 | project=self.name, | ||
1495 | ) | ||
1422 | ) | 1496 | ) |
1423 | return | 1497 | return |
1424 | 1498 | ||
@@ -1438,10 +1512,12 @@ class Project(object): | |||
1438 | ) | 1512 | ) |
1439 | bad_paths = paths & PROTECTED_PATHS | 1513 | bad_paths = paths & PROTECTED_PATHS |
1440 | if bad_paths: | 1514 | if bad_paths: |
1441 | syncbuf.fail( | 1515 | fail( |
1442 | self, | 1516 | LocalSyncFail( |
1443 | "Refusing to checkout project that writes to protected " | 1517 | "Refusing to checkout project that writes to protected " |
1444 | "paths: %s" % (", ".join(bad_paths),), | 1518 | "paths: %s" % (", ".join(bad_paths),), |
1519 | project=self.name, | ||
1520 | ) | ||
1445 | ) | 1521 | ) |
1446 | return | 1522 | return |
1447 | 1523 | ||
@@ -1466,7 +1542,7 @@ class Project(object): | |||
1466 | # Currently on a detached HEAD. The user is assumed to | 1542 | # Currently on a detached HEAD. The user is assumed to |
1467 | # not have any local modifications worth worrying about. | 1543 | # not have any local modifications worth worrying about. |
1468 | if self.IsRebaseInProgress(): | 1544 | if self.IsRebaseInProgress(): |
1469 | syncbuf.fail(self, _PriorSyncFailedError()) | 1545 | fail(_PriorSyncFailedError(project=self.name)) |
1470 | return | 1546 | return |
1471 | 1547 | ||
1472 | if head == revid: | 1548 | if head == revid: |
@@ -1486,7 +1562,7 @@ class Project(object): | |||
1486 | if submodules: | 1562 | if submodules: |
1487 | self._SyncSubmodules(quiet=True) | 1563 | self._SyncSubmodules(quiet=True) |
1488 | except GitError as e: | 1564 | except GitError as e: |
1489 | syncbuf.fail(self, e) | 1565 | fail(e) |
1490 | return | 1566 | return |
1491 | self._CopyAndLinkFiles() | 1567 | self._CopyAndLinkFiles() |
1492 | return | 1568 | return |
@@ -1511,7 +1587,7 @@ class Project(object): | |||
1511 | if submodules: | 1587 | if submodules: |
1512 | self._SyncSubmodules(quiet=True) | 1588 | self._SyncSubmodules(quiet=True) |
1513 | except GitError as e: | 1589 | except GitError as e: |
1514 | syncbuf.fail(self, e) | 1590 | fail(e) |
1515 | return | 1591 | return |
1516 | self._CopyAndLinkFiles() | 1592 | self._CopyAndLinkFiles() |
1517 | return | 1593 | return |
@@ -1534,10 +1610,13 @@ class Project(object): | |||
1534 | # The user has published this branch and some of those | 1610 | # The user has published this branch and some of those |
1535 | # commits are not yet merged upstream. We do not want | 1611 | # commits are not yet merged upstream. We do not want |
1536 | # to rewrite the published commits so we punt. | 1612 | # to rewrite the published commits so we punt. |
1537 | syncbuf.fail( | 1613 | fail( |
1538 | self, | 1614 | LocalSyncFail( |
1539 | "branch %s is published (but not merged) and is now " | 1615 | "branch %s is published (but not merged) and is " |
1540 | "%d commits behind" % (branch.name, len(upstream_gain)), | 1616 | "now %d commits behind" |
1617 | % (branch.name, len(upstream_gain)), | ||
1618 | project=self.name, | ||
1619 | ) | ||
1541 | ) | 1620 | ) |
1542 | return | 1621 | return |
1543 | elif pub == head: | 1622 | elif pub == head: |
@@ -1565,7 +1644,7 @@ class Project(object): | |||
1565 | return | 1644 | return |
1566 | 1645 | ||
1567 | if self.IsDirty(consider_untracked=False): | 1646 | if self.IsDirty(consider_untracked=False): |
1568 | syncbuf.fail(self, _DirtyError()) | 1647 | fail(_DirtyError(project=self.name)) |
1569 | return | 1648 | return |
1570 | 1649 | ||
1571 | # If the upstream switched on us, warn the user. | 1650 | # If the upstream switched on us, warn the user. |
@@ -1615,7 +1694,7 @@ class Project(object): | |||
1615 | self._SyncSubmodules(quiet=True) | 1694 | self._SyncSubmodules(quiet=True) |
1616 | self._CopyAndLinkFiles() | 1695 | self._CopyAndLinkFiles() |
1617 | except GitError as e: | 1696 | except GitError as e: |
1618 | syncbuf.fail(self, e) | 1697 | fail(e) |
1619 | return | 1698 | return |
1620 | else: | 1699 | else: |
1621 | syncbuf.later1(self, _doff) | 1700 | syncbuf.later1(self, _doff) |
@@ -1687,12 +1766,12 @@ class Project(object): | |||
1687 | file=sys.stderr, | 1766 | file=sys.stderr, |
1688 | ) | 1767 | ) |
1689 | else: | 1768 | else: |
1690 | print( | 1769 | msg = ( |
1691 | "error: %s: Cannot remove project: uncommitted changes are " | 1770 | "error: %s: Cannot remove project: uncommitted" |
1692 | "present.\n" % (self.RelPath(local=False),), | 1771 | "changes are present.\n" % self.RelPath(local=False) |
1693 | file=sys.stderr, | ||
1694 | ) | 1772 | ) |
1695 | return False | 1773 | print(msg, file=sys.stderr) |
1774 | raise DeleteDirtyWorktreeError(msg, project=self) | ||
1696 | 1775 | ||
1697 | if not quiet: | 1776 | if not quiet: |
1698 | print( | 1777 | print( |
@@ -1745,12 +1824,13 @@ class Project(object): | |||
1745 | % (self.RelPath(local=False),), | 1824 | % (self.RelPath(local=False),), |
1746 | file=sys.stderr, | 1825 | file=sys.stderr, |
1747 | ) | 1826 | ) |
1748 | return False | 1827 | raise DeleteWorktreeError(aggregate_errors=[e]) |
1749 | 1828 | ||
1750 | # Delete everything under the worktree, except for directories that | 1829 | # Delete everything under the worktree, except for directories that |
1751 | # contain another git project. | 1830 | # contain another git project. |
1752 | dirs_to_remove = [] | 1831 | dirs_to_remove = [] |
1753 | failed = False | 1832 | failed = False |
1833 | errors = [] | ||
1754 | for root, dirs, files in platform_utils.walk(self.worktree): | 1834 | for root, dirs, files in platform_utils.walk(self.worktree): |
1755 | for f in files: | 1835 | for f in files: |
1756 | path = os.path.join(root, f) | 1836 | path = os.path.join(root, f) |
@@ -1763,6 +1843,7 @@ class Project(object): | |||
1763 | file=sys.stderr, | 1843 | file=sys.stderr, |
1764 | ) | 1844 | ) |
1765 | failed = True | 1845 | failed = True |
1846 | errors.append(e) | ||
1766 | dirs[:] = [ | 1847 | dirs[:] = [ |
1767 | d | 1848 | d |
1768 | for d in dirs | 1849 | for d in dirs |
@@ -1784,6 +1865,7 @@ class Project(object): | |||
1784 | file=sys.stderr, | 1865 | file=sys.stderr, |
1785 | ) | 1866 | ) |
1786 | failed = True | 1867 | failed = True |
1868 | errors.append(e) | ||
1787 | elif not platform_utils.listdir(d): | 1869 | elif not platform_utils.listdir(d): |
1788 | try: | 1870 | try: |
1789 | platform_utils.rmdir(d) | 1871 | platform_utils.rmdir(d) |
@@ -1794,6 +1876,7 @@ class Project(object): | |||
1794 | file=sys.stderr, | 1876 | file=sys.stderr, |
1795 | ) | 1877 | ) |
1796 | failed = True | 1878 | failed = True |
1879 | errors.append(e) | ||
1797 | if failed: | 1880 | if failed: |
1798 | print( | 1881 | print( |
1799 | "error: %s: Failed to delete obsolete checkout." | 1882 | "error: %s: Failed to delete obsolete checkout." |
@@ -1804,7 +1887,7 @@ class Project(object): | |||
1804 | " Remove manually, then run `repo sync -l`.", | 1887 | " Remove manually, then run `repo sync -l`.", |
1805 | file=sys.stderr, | 1888 | file=sys.stderr, |
1806 | ) | 1889 | ) |
1807 | return False | 1890 | raise DeleteWorktreeError(aggregate_errors=errors) |
1808 | 1891 | ||
1809 | # Try deleting parent dirs if they are empty. | 1892 | # Try deleting parent dirs if they are empty. |
1810 | path = self.worktree | 1893 | path = self.worktree |
@@ -2264,11 +2347,14 @@ class Project(object): | |||
2264 | cmd.append(self.revisionExpr) | 2347 | cmd.append(self.revisionExpr) |
2265 | 2348 | ||
2266 | command = GitCommand( | 2349 | command = GitCommand( |
2267 | self, cmd, cwd=cwd, capture_stdout=True, capture_stderr=True | 2350 | self, |
2351 | cmd, | ||
2352 | cwd=cwd, | ||
2353 | capture_stdout=True, | ||
2354 | capture_stderr=True, | ||
2355 | verify_command=True, | ||
2268 | ) | 2356 | ) |
2269 | 2357 | command.Wait() | |
2270 | if command.Wait() != 0: | ||
2271 | raise GitError("git archive %s: %s" % (self.name, command.stderr)) | ||
2272 | 2358 | ||
2273 | def _RemoteFetch( | 2359 | def _RemoteFetch( |
2274 | self, | 2360 | self, |
@@ -2289,7 +2375,7 @@ class Project(object): | |||
2289 | retry_fetches=2, | 2375 | retry_fetches=2, |
2290 | retry_sleep_initial_sec=4.0, | 2376 | retry_sleep_initial_sec=4.0, |
2291 | retry_exp_factor=2.0, | 2377 | retry_exp_factor=2.0, |
2292 | ): | 2378 | ) -> bool: |
2293 | is_sha1 = False | 2379 | is_sha1 = False |
2294 | tag_name = None | 2380 | tag_name = None |
2295 | # The depth should not be used when fetching to a mirror because | 2381 | # The depth should not be used when fetching to a mirror because |
@@ -2473,6 +2559,7 @@ class Project(object): | |||
2473 | retry_cur_sleep = retry_sleep_initial_sec | 2559 | retry_cur_sleep = retry_sleep_initial_sec |
2474 | ok = prune_tried = False | 2560 | ok = prune_tried = False |
2475 | for try_n in range(retry_fetches): | 2561 | for try_n in range(retry_fetches): |
2562 | verify_command = try_n == retry_fetches - 1 | ||
2476 | gitcmd = GitCommand( | 2563 | gitcmd = GitCommand( |
2477 | self, | 2564 | self, |
2478 | cmd, | 2565 | cmd, |
@@ -2481,6 +2568,7 @@ class Project(object): | |||
2481 | ssh_proxy=ssh_proxy, | 2568 | ssh_proxy=ssh_proxy, |
2482 | merge_output=True, | 2569 | merge_output=True, |
2483 | capture_stdout=quiet or bool(output_redir), | 2570 | capture_stdout=quiet or bool(output_redir), |
2571 | verify_command=verify_command, | ||
2484 | ) | 2572 | ) |
2485 | if gitcmd.stdout and not quiet and output_redir: | 2573 | if gitcmd.stdout and not quiet and output_redir: |
2486 | output_redir.write(gitcmd.stdout) | 2574 | output_redir.write(gitcmd.stdout) |
@@ -2732,7 +2820,9 @@ class Project(object): | |||
2732 | cmd.append("--") | 2820 | cmd.append("--") |
2733 | if GitCommand(self, cmd).Wait() != 0: | 2821 | if GitCommand(self, cmd).Wait() != 0: |
2734 | if self._allrefs: | 2822 | if self._allrefs: |
2735 | raise GitError("%s checkout %s " % (self.name, rev)) | 2823 | raise GitError( |
2824 | "%s checkout %s " % (self.name, rev), project=self.name | ||
2825 | ) | ||
2736 | 2826 | ||
2737 | def _CherryPick(self, rev, ffonly=False, record_origin=False): | 2827 | def _CherryPick(self, rev, ffonly=False, record_origin=False): |
2738 | cmd = ["cherry-pick"] | 2828 | cmd = ["cherry-pick"] |
@@ -2744,7 +2834,9 @@ class Project(object): | |||
2744 | cmd.append("--") | 2834 | cmd.append("--") |
2745 | if GitCommand(self, cmd).Wait() != 0: | 2835 | if GitCommand(self, cmd).Wait() != 0: |
2746 | if self._allrefs: | 2836 | if self._allrefs: |
2747 | raise GitError("%s cherry-pick %s " % (self.name, rev)) | 2837 | raise GitError( |
2838 | "%s cherry-pick %s " % (self.name, rev), project=self.name | ||
2839 | ) | ||
2748 | 2840 | ||
2749 | def _LsRemote(self, refs): | 2841 | def _LsRemote(self, refs): |
2750 | cmd = ["ls-remote", self.remote.name, refs] | 2842 | cmd = ["ls-remote", self.remote.name, refs] |
@@ -2760,7 +2852,9 @@ class Project(object): | |||
2760 | cmd.append("--") | 2852 | cmd.append("--") |
2761 | if GitCommand(self, cmd).Wait() != 0: | 2853 | if GitCommand(self, cmd).Wait() != 0: |
2762 | if self._allrefs: | 2854 | if self._allrefs: |
2763 | raise GitError("%s revert %s " % (self.name, rev)) | 2855 | raise GitError( |
2856 | "%s revert %s " % (self.name, rev), project=self.name | ||
2857 | ) | ||
2764 | 2858 | ||
2765 | def _ResetHard(self, rev, quiet=True): | 2859 | def _ResetHard(self, rev, quiet=True): |
2766 | cmd = ["reset", "--hard"] | 2860 | cmd = ["reset", "--hard"] |
@@ -2768,7 +2862,9 @@ class Project(object): | |||
2768 | cmd.append("-q") | 2862 | cmd.append("-q") |
2769 | cmd.append(rev) | 2863 | cmd.append(rev) |
2770 | if GitCommand(self, cmd).Wait() != 0: | 2864 | if GitCommand(self, cmd).Wait() != 0: |
2771 | raise GitError("%s reset --hard %s " % (self.name, rev)) | 2865 | raise GitError( |
2866 | "%s reset --hard %s " % (self.name, rev), project=self.name | ||
2867 | ) | ||
2772 | 2868 | ||
2773 | def _SyncSubmodules(self, quiet=True): | 2869 | def _SyncSubmodules(self, quiet=True): |
2774 | cmd = ["submodule", "update", "--init", "--recursive"] | 2870 | cmd = ["submodule", "update", "--init", "--recursive"] |
@@ -2776,7 +2872,8 @@ class Project(object): | |||
2776 | cmd.append("-q") | 2872 | cmd.append("-q") |
2777 | if GitCommand(self, cmd).Wait() != 0: | 2873 | if GitCommand(self, cmd).Wait() != 0: |
2778 | raise GitError( | 2874 | raise GitError( |
2779 | "%s submodule update --init --recursive " % self.name | 2875 | "%s submodule update --init --recursive " % self.name, |
2876 | project=self.name, | ||
2780 | ) | 2877 | ) |
2781 | 2878 | ||
2782 | def _Rebase(self, upstream, onto=None): | 2879 | def _Rebase(self, upstream, onto=None): |
@@ -2785,14 +2882,18 @@ class Project(object): | |||
2785 | cmd.extend(["--onto", onto]) | 2882 | cmd.extend(["--onto", onto]) |
2786 | cmd.append(upstream) | 2883 | cmd.append(upstream) |
2787 | if GitCommand(self, cmd).Wait() != 0: | 2884 | if GitCommand(self, cmd).Wait() != 0: |
2788 | raise GitError("%s rebase %s " % (self.name, upstream)) | 2885 | raise GitError( |
2886 | "%s rebase %s " % (self.name, upstream), project=self.name | ||
2887 | ) | ||
2789 | 2888 | ||
2790 | def _FastForward(self, head, ffonly=False): | 2889 | def _FastForward(self, head, ffonly=False): |
2791 | cmd = ["merge", "--no-stat", head] | 2890 | cmd = ["merge", "--no-stat", head] |
2792 | if ffonly: | 2891 | if ffonly: |
2793 | cmd.append("--ff-only") | 2892 | cmd.append("--ff-only") |
2794 | if GitCommand(self, cmd).Wait() != 0: | 2893 | if GitCommand(self, cmd).Wait() != 0: |
2795 | raise GitError("%s merge %s " % (self.name, head)) | 2894 | raise GitError( |
2895 | "%s merge %s " % (self.name, head), project=self.name | ||
2896 | ) | ||
2796 | 2897 | ||
2797 | def _InitGitDir(self, mirror_git=None, force_sync=False, quiet=False): | 2898 | def _InitGitDir(self, mirror_git=None, force_sync=False, quiet=False): |
2798 | init_git_dir = not os.path.exists(self.gitdir) | 2899 | init_git_dir = not os.path.exists(self.gitdir) |
@@ -2964,7 +3065,9 @@ class Project(object): | |||
2964 | try: | 3065 | try: |
2965 | os.link(stock_hook, dst) | 3066 | os.link(stock_hook, dst) |
2966 | except OSError: | 3067 | except OSError: |
2967 | raise GitError(self._get_symlink_error_message()) | 3068 | raise GitError( |
3069 | self._get_symlink_error_message(), project=self.name | ||
3070 | ) | ||
2968 | else: | 3071 | else: |
2969 | raise | 3072 | raise |
2970 | 3073 | ||
@@ -3065,7 +3168,8 @@ class Project(object): | |||
3065 | "work tree. If you're comfortable with the " | 3168 | "work tree. If you're comfortable with the " |
3066 | "possibility of losing the work tree's git metadata," | 3169 | "possibility of losing the work tree's git metadata," |
3067 | " use `repo sync --force-sync {0}` to " | 3170 | " use `repo sync --force-sync {0}` to " |
3068 | "proceed.".format(self.RelPath(local=False)) | 3171 | "proceed.".format(self.RelPath(local=False)), |
3172 | project=self.name, | ||
3069 | ) | 3173 | ) |
3070 | 3174 | ||
3071 | def _ReferenceGitDir(self, gitdir, dotgit, copy_all): | 3175 | def _ReferenceGitDir(self, gitdir, dotgit, copy_all): |
@@ -3175,7 +3279,7 @@ class Project(object): | |||
3175 | 3279 | ||
3176 | # If using an old layout style (a directory), migrate it. | 3280 | # If using an old layout style (a directory), migrate it. |
3177 | if not platform_utils.islink(dotgit) and platform_utils.isdir(dotgit): | 3281 | if not platform_utils.islink(dotgit) and platform_utils.isdir(dotgit): |
3178 | self._MigrateOldWorkTreeGitDir(dotgit) | 3282 | self._MigrateOldWorkTreeGitDir(dotgit, project=self.name) |
3179 | 3283 | ||
3180 | init_dotgit = not os.path.exists(dotgit) | 3284 | init_dotgit = not os.path.exists(dotgit) |
3181 | if self.use_git_worktrees: | 3285 | if self.use_git_worktrees: |
@@ -3205,7 +3309,8 @@ class Project(object): | |||
3205 | cmd = ["read-tree", "--reset", "-u", "-v", HEAD] | 3309 | cmd = ["read-tree", "--reset", "-u", "-v", HEAD] |
3206 | if GitCommand(self, cmd).Wait() != 0: | 3310 | if GitCommand(self, cmd).Wait() != 0: |
3207 | raise GitError( | 3311 | raise GitError( |
3208 | "Cannot initialize work tree for " + self.name | 3312 | "Cannot initialize work tree for " + self.name, |
3313 | project=self.name, | ||
3209 | ) | 3314 | ) |
3210 | 3315 | ||
3211 | if submodules: | 3316 | if submodules: |
@@ -3213,7 +3318,7 @@ class Project(object): | |||
3213 | self._CopyAndLinkFiles() | 3318 | self._CopyAndLinkFiles() |
3214 | 3319 | ||
3215 | @classmethod | 3320 | @classmethod |
3216 | def _MigrateOldWorkTreeGitDir(cls, dotgit): | 3321 | def _MigrateOldWorkTreeGitDir(cls, dotgit, project=None): |
3217 | """Migrate the old worktree .git/ dir style to a symlink. | 3322 | """Migrate the old worktree .git/ dir style to a symlink. |
3218 | 3323 | ||
3219 | This logic specifically only uses state from |dotgit| to figure out | 3324 | This logic specifically only uses state from |dotgit| to figure out |
@@ -3223,7 +3328,9 @@ class Project(object): | |||
3223 | """ | 3328 | """ |
3224 | # Figure out where in .repo/projects/ it's pointing to. | 3329 | # Figure out where in .repo/projects/ it's pointing to. |
3225 | if not os.path.islink(os.path.join(dotgit, "refs")): | 3330 | if not os.path.islink(os.path.join(dotgit, "refs")): |
3226 | raise GitError(f"{dotgit}: unsupported checkout state") | 3331 | raise GitError( |
3332 | f"{dotgit}: unsupported checkout state", project=project | ||
3333 | ) | ||
3227 | gitdir = os.path.dirname(os.path.realpath(os.path.join(dotgit, "refs"))) | 3334 | gitdir = os.path.dirname(os.path.realpath(os.path.join(dotgit, "refs"))) |
3228 | 3335 | ||
3229 | # Remove known symlink paths that exist in .repo/projects/. | 3336 | # Remove known symlink paths that exist in .repo/projects/. |
@@ -3271,7 +3378,10 @@ class Project(object): | |||
3271 | f"{dotgit_path}: unknown file; please file a bug" | 3378 | f"{dotgit_path}: unknown file; please file a bug" |
3272 | ) | 3379 | ) |
3273 | if unknown_paths: | 3380 | if unknown_paths: |
3274 | raise GitError("Aborting migration: " + "\n".join(unknown_paths)) | 3381 | raise GitError( |
3382 | "Aborting migration: " + "\n".join(unknown_paths), | ||
3383 | project=project, | ||
3384 | ) | ||
3275 | 3385 | ||
3276 | # Now walk the paths and sync the .git/ to .repo/projects/. | 3386 | # Now walk the paths and sync the .git/ to .repo/projects/. |
3277 | for name in platform_utils.listdir(dotgit): | 3387 | for name in platform_utils.listdir(dotgit): |
@@ -3537,12 +3647,9 @@ class Project(object): | |||
3537 | gitdir=self._gitdir, | 3647 | gitdir=self._gitdir, |
3538 | capture_stdout=True, | 3648 | capture_stdout=True, |
3539 | capture_stderr=True, | 3649 | capture_stderr=True, |
3650 | verify_command=True, | ||
3540 | ) | 3651 | ) |
3541 | if p.Wait() != 0: | 3652 | p.Wait() |
3542 | raise GitError( | ||
3543 | "%s rev-list %s: %s" | ||
3544 | % (self._project.name, str(args), p.stderr) | ||
3545 | ) | ||
3546 | return p.stdout.splitlines() | 3653 | return p.stdout.splitlines() |
3547 | 3654 | ||
3548 | def __getattr__(self, name): | 3655 | def __getattr__(self, name): |
@@ -3588,11 +3695,9 @@ class Project(object): | |||
3588 | gitdir=self._gitdir, | 3695 | gitdir=self._gitdir, |
3589 | capture_stdout=True, | 3696 | capture_stdout=True, |
3590 | capture_stderr=True, | 3697 | capture_stderr=True, |
3698 | verify_command=True, | ||
3591 | ) | 3699 | ) |
3592 | if p.Wait() != 0: | 3700 | p.Wait() |
3593 | raise GitError( | ||
3594 | "%s %s: %s" % (self._project.name, name, p.stderr) | ||
3595 | ) | ||
3596 | r = p.stdout | 3701 | r = p.stdout |
3597 | if r.endswith("\n") and r.index("\n") == len(r) - 1: | 3702 | if r.endswith("\n") and r.index("\n") == len(r) - 1: |
3598 | return r[:-1] | 3703 | return r[:-1] |
@@ -3601,12 +3706,16 @@ class Project(object): | |||
3601 | return runner | 3706 | return runner |
3602 | 3707 | ||
3603 | 3708 | ||
3604 | class _PriorSyncFailedError(Exception): | 3709 | class LocalSyncFail(RepoError): |
3710 | """Default error when there is an Sync_LocalHalf error.""" | ||
3711 | |||
3712 | |||
3713 | class _PriorSyncFailedError(LocalSyncFail): | ||
3605 | def __str__(self): | 3714 | def __str__(self): |
3606 | return "prior sync failed; rebase still in progress" | 3715 | return "prior sync failed; rebase still in progress" |
3607 | 3716 | ||
3608 | 3717 | ||
3609 | class _DirtyError(Exception): | 3718 | class _DirtyError(LocalSyncFail): |
3610 | def __str__(self): | 3719 | def __str__(self): |
3611 | return "contains uncommitted changes" | 3720 | return "contains uncommitted changes" |
3612 | 3721 | ||