diff options
-rw-r--r-- | error.py | 8 | ||||
-rwxr-xr-x | main.py | 23 | ||||
-rw-r--r-- | project.py | 54 | ||||
-rw-r--r-- | subcmds/checkout.py | 50 | ||||
-rw-r--r-- | subcmds/cherry_pick.py | 76 | ||||
-rw-r--r-- | subcmds/download.py | 23 | ||||
-rw-r--r-- | subcmds/grep.py | 84 | ||||
-rw-r--r-- | subcmds/help.py | 7 | ||||
-rw-r--r-- | subcmds/selfupdate.py | 10 | ||||
-rw-r--r-- | subcmds/start.py | 38 |
10 files changed, 251 insertions, 122 deletions
@@ -56,6 +56,10 @@ class RepoUnhandledExceptionError(RepoExitError): | |||
56 | self.error = error | 56 | self.error = error |
57 | 57 | ||
58 | 58 | ||
59 | class SilentRepoExitError(RepoExitError): | ||
60 | """RepoExitError that should no include CLI logging of issue/issues.""" | ||
61 | |||
62 | |||
59 | class ManifestParseError(RepoExitError): | 63 | class ManifestParseError(RepoExitError): |
60 | """Failed to parse the manifest file.""" | 64 | """Failed to parse the manifest file.""" |
61 | 65 | ||
@@ -125,6 +129,10 @@ class DownloadError(RepoExitError): | |||
125 | return self.reason | 129 | return self.reason |
126 | 130 | ||
127 | 131 | ||
132 | class InvalidArgumentsError(RepoExitError): | ||
133 | """Invalid command Arguments.""" | ||
134 | |||
135 | |||
128 | class SyncError(RepoExitError): | 136 | class SyncError(RepoExitError): |
129 | """Cannot sync repo.""" | 137 | """Cannot sync repo.""" |
130 | 138 | ||
@@ -57,6 +57,7 @@ from error import RepoChangedException | |||
57 | from error import RepoExitError | 57 | from error import RepoExitError |
58 | from error import RepoUnhandledExceptionError | 58 | from error import RepoUnhandledExceptionError |
59 | from error import RepoError | 59 | from error import RepoError |
60 | from error import SilentRepoExitError | ||
60 | import gitc_utils | 61 | import gitc_utils |
61 | from manifest_xml import GitcClient, RepoClient | 62 | from manifest_xml import GitcClient, RepoClient |
62 | from pager import RunPager, TerminatePager | 63 | from pager import RunPager, TerminatePager |
@@ -872,16 +873,20 @@ def _Main(argv): | |||
872 | 873 | ||
873 | result = repo._Run(name, gopts, argv) or 0 | 874 | result = repo._Run(name, gopts, argv) or 0 |
874 | except RepoExitError as e: | 875 | except RepoExitError as e: |
875 | exception_name = type(e).__name__ | 876 | if not isinstance(e, SilentRepoExitError): |
877 | exception_name = type(e).__name__ | ||
878 | print("fatal: %s" % e, file=sys.stderr) | ||
879 | if e.aggregate_errors: | ||
880 | print(f"{exception_name} Aggregate Errors") | ||
881 | for err in e.aggregate_errors[:MAX_PRINT_ERRORS]: | ||
882 | print(err) | ||
883 | if ( | ||
884 | e.aggregate_errors | ||
885 | and len(e.aggregate_errors) > MAX_PRINT_ERRORS | ||
886 | ): | ||
887 | diff = len(e.aggregate_errors) - MAX_PRINT_ERRORS | ||
888 | print(f"+{diff} additional errors ...") | ||
876 | result = e.exit_code | 889 | result = e.exit_code |
877 | print("fatal: %s" % e, file=sys.stderr) | ||
878 | if e.aggregate_errors: | ||
879 | print(f"{exception_name} Aggregate Errors") | ||
880 | for err in e.aggregate_errors[:MAX_PRINT_ERRORS]: | ||
881 | print(err) | ||
882 | if len(e.aggregate_errors) > MAX_PRINT_ERRORS: | ||
883 | diff = len(e.aggregate_errors) - MAX_PRINT_ERRORS | ||
884 | print(f"+{diff} additional errors ...") | ||
885 | except KeyboardInterrupt: | 890 | except KeyboardInterrupt: |
886 | print("aborted by user", file=sys.stderr) | 891 | print("aborted by user", file=sys.stderr) |
887 | result = KEYBOARD_INTERRUPT_EXIT | 892 | result = KEYBOARD_INTERRUPT_EXIT |
@@ -1733,8 +1733,7 @@ class Project(object): | |||
1733 | cmd.append( | 1733 | cmd.append( |
1734 | "refs/changes/%2.2d/%d/%d" % (change_id % 100, change_id, patch_id) | 1734 | "refs/changes/%2.2d/%d/%d" % (change_id % 100, change_id, patch_id) |
1735 | ) | 1735 | ) |
1736 | if GitCommand(self, cmd, bare=True).Wait() != 0: | 1736 | GitCommand(self, cmd, bare=True, verify_command=True).Wait() |
1737 | return None | ||
1738 | return DownloadedChange( | 1737 | return DownloadedChange( |
1739 | self, | 1738 | self, |
1740 | self.GetRevisionId(), | 1739 | self.GetRevisionId(), |
@@ -1911,7 +1910,10 @@ class Project(object): | |||
1911 | 1910 | ||
1912 | all_refs = self.bare_ref.all | 1911 | all_refs = self.bare_ref.all |
1913 | if R_HEADS + name in all_refs: | 1912 | if R_HEADS + name in all_refs: |
1914 | return GitCommand(self, ["checkout", "-q", name, "--"]).Wait() == 0 | 1913 | GitCommand( |
1914 | self, ["checkout", "-q", name, "--"], verify_command=True | ||
1915 | ).Wait() | ||
1916 | return True | ||
1915 | 1917 | ||
1916 | branch = self.GetBranch(name) | 1918 | branch = self.GetBranch(name) |
1917 | branch.remote = self.GetRemote() | 1919 | branch.remote = self.GetRemote() |
@@ -1938,15 +1940,13 @@ class Project(object): | |||
1938 | branch.Save() | 1940 | branch.Save() |
1939 | return True | 1941 | return True |
1940 | 1942 | ||
1941 | if ( | 1943 | GitCommand( |
1942 | GitCommand( | 1944 | self, |
1943 | self, ["checkout", "-q", "-b", branch.name, revid] | 1945 | ["checkout", "-q", "-b", branch.name, revid], |
1944 | ).Wait() | 1946 | verify_command=True, |
1945 | == 0 | 1947 | ).Wait() |
1946 | ): | 1948 | branch.Save() |
1947 | branch.Save() | 1949 | return True |
1948 | return True | ||
1949 | return False | ||
1950 | 1950 | ||
1951 | def CheckoutBranch(self, name): | 1951 | def CheckoutBranch(self, name): |
1952 | """Checkout a local topic branch. | 1952 | """Checkout a local topic branch. |
@@ -1955,8 +1955,8 @@ class Project(object): | |||
1955 | name: The name of the branch to checkout. | 1955 | name: The name of the branch to checkout. |
1956 | 1956 | ||
1957 | Returns: | 1957 | Returns: |
1958 | True if the checkout succeeded; False if it didn't; None if the | 1958 | True if the checkout succeeded; False if the |
1959 | branch didn't exist. | 1959 | branch doesn't exist. |
1960 | """ | 1960 | """ |
1961 | rev = R_HEADS + name | 1961 | rev = R_HEADS + name |
1962 | head = self.work_git.GetHead() | 1962 | head = self.work_git.GetHead() |
@@ -1969,7 +1969,7 @@ class Project(object): | |||
1969 | revid = all_refs[rev] | 1969 | revid = all_refs[rev] |
1970 | except KeyError: | 1970 | except KeyError: |
1971 | # Branch does not exist in this project. | 1971 | # Branch does not exist in this project. |
1972 | return None | 1972 | return False |
1973 | 1973 | ||
1974 | if head.startswith(R_HEADS): | 1974 | if head.startswith(R_HEADS): |
1975 | try: | 1975 | try: |
@@ -1986,15 +1986,14 @@ class Project(object): | |||
1986 | ) | 1986 | ) |
1987 | return True | 1987 | return True |
1988 | 1988 | ||
1989 | return ( | 1989 | GitCommand( |
1990 | GitCommand( | 1990 | self, |
1991 | self, | 1991 | ["checkout", name, "--"], |
1992 | ["checkout", name, "--"], | 1992 | capture_stdout=True, |
1993 | capture_stdout=True, | 1993 | capture_stderr=True, |
1994 | capture_stderr=True, | 1994 | verify_command=True, |
1995 | ).Wait() | 1995 | ).Wait() |
1996 | == 0 | 1996 | return True |
1997 | ) | ||
1998 | 1997 | ||
1999 | def AbandonBranch(self, name): | 1998 | def AbandonBranch(self, name): |
2000 | """Destroy a local topic branch. | 1999 | """Destroy a local topic branch. |
@@ -4458,9 +4457,12 @@ class ManifestProject(MetaProject): | |||
4458 | syncbuf.Finish() | 4457 | syncbuf.Finish() |
4459 | 4458 | ||
4460 | if is_new or self.CurrentBranch is None: | 4459 | if is_new or self.CurrentBranch is None: |
4461 | if not self.StartBranch("default"): | 4460 | try: |
4461 | self.StartBranch("default") | ||
4462 | except GitError as e: | ||
4463 | msg = str(e) | ||
4462 | print( | 4464 | print( |
4463 | "fatal: cannot create default in manifest", | 4465 | f"fatal: cannot create default in manifest {msg}", |
4464 | file=sys.stderr, | 4466 | file=sys.stderr, |
4465 | ) | 4467 | ) |
4466 | return False | 4468 | return False |
diff --git a/subcmds/checkout.py b/subcmds/checkout.py index 6448518f..033fd349 100644 --- a/subcmds/checkout.py +++ b/subcmds/checkout.py | |||
@@ -15,8 +15,26 @@ | |||
15 | import functools | 15 | import functools |
16 | import sys | 16 | import sys |
17 | 17 | ||
18 | from typing import NamedTuple | ||
18 | from command import Command, DEFAULT_LOCAL_JOBS | 19 | from command import Command, DEFAULT_LOCAL_JOBS |
19 | from progress import Progress | 20 | from progress import Progress |
21 | from project import Project | ||
22 | from error import GitError, RepoExitError | ||
23 | |||
24 | |||
25 | class CheckoutBranchResult(NamedTuple): | ||
26 | # Whether the Project is on the branch (i.e. branch exists and no errors) | ||
27 | result: bool | ||
28 | project: Project | ||
29 | error: Exception | ||
30 | |||
31 | |||
32 | class CheckoutCommandError(RepoExitError): | ||
33 | """Exception thrown when checkout command fails.""" | ||
34 | |||
35 | |||
36 | class MissingBranchError(RepoExitError): | ||
37 | """Exception thrown when no project has specified branch.""" | ||
20 | 38 | ||
21 | 39 | ||
22 | class Checkout(Command): | 40 | class Checkout(Command): |
@@ -41,23 +59,30 @@ The command is equivalent to: | |||
41 | 59 | ||
42 | def _ExecuteOne(self, nb, project): | 60 | def _ExecuteOne(self, nb, project): |
43 | """Checkout one project.""" | 61 | """Checkout one project.""" |
44 | return (project.CheckoutBranch(nb), project) | 62 | error = None |
63 | result = None | ||
64 | try: | ||
65 | result = project.CheckoutBranch(nb) | ||
66 | except GitError as e: | ||
67 | error = e | ||
68 | return CheckoutBranchResult(result, project, error) | ||
45 | 69 | ||
46 | def Execute(self, opt, args): | 70 | def Execute(self, opt, args): |
47 | nb = args[0] | 71 | nb = args[0] |
48 | err = [] | 72 | err = [] |
73 | err_projects = [] | ||
49 | success = [] | 74 | success = [] |
50 | all_projects = self.GetProjects( | 75 | all_projects = self.GetProjects( |
51 | args[1:], all_manifests=not opt.this_manifest_only | 76 | args[1:], all_manifests=not opt.this_manifest_only |
52 | ) | 77 | ) |
53 | 78 | ||
54 | def _ProcessResults(_pool, pm, results): | 79 | def _ProcessResults(_pool, pm, results): |
55 | for status, project in results: | 80 | for result in results: |
56 | if status is not None: | 81 | if result.error is not None: |
57 | if status: | 82 | err.append(result.error) |
58 | success.append(project) | 83 | err_projects.append(result.project) |
59 | else: | 84 | elif result.result: |
60 | err.append(project) | 85 | success.append(result.project) |
61 | pm.update(msg="") | 86 | pm.update(msg="") |
62 | 87 | ||
63 | self.ExecuteInParallel( | 88 | self.ExecuteInParallel( |
@@ -70,13 +95,14 @@ The command is equivalent to: | |||
70 | ), | 95 | ), |
71 | ) | 96 | ) |
72 | 97 | ||
73 | if err: | 98 | if err_projects: |
74 | for p in err: | 99 | for p in err_projects: |
75 | print( | 100 | print( |
76 | "error: %s/: cannot checkout %s" % (p.relpath, nb), | 101 | "error: %s/: cannot checkout %s" % (p.relpath, nb), |
77 | file=sys.stderr, | 102 | file=sys.stderr, |
78 | ) | 103 | ) |
79 | sys.exit(1) | 104 | raise CheckoutCommandError(aggregate_errors=err) |
80 | elif not success: | 105 | elif not success: |
81 | print("error: no project has branch %s" % nb, file=sys.stderr) | 106 | msg = f"error: no project has branch {nb}" |
82 | sys.exit(1) | 107 | print(msg, file=sys.stderr) |
108 | raise MissingBranchError(msg) | ||
diff --git a/subcmds/cherry_pick.py b/subcmds/cherry_pick.py index 4cfb8c88..7a4dd09e 100644 --- a/subcmds/cherry_pick.py +++ b/subcmds/cherry_pick.py | |||
@@ -16,6 +16,7 @@ import re | |||
16 | import sys | 16 | import sys |
17 | from command import Command | 17 | from command import Command |
18 | from git_command import GitCommand | 18 | from git_command import GitCommand |
19 | from error import GitError | ||
19 | 20 | ||
20 | CHANGE_ID_RE = re.compile(r"^\s*Change-Id: I([0-9a-f]{40})\s*$") | 21 | CHANGE_ID_RE = re.compile(r"^\s*Change-Id: I([0-9a-f]{40})\s*$") |
21 | 22 | ||
@@ -44,18 +45,31 @@ change id will be added. | |||
44 | ["rev-parse", "--verify", reference], | 45 | ["rev-parse", "--verify", reference], |
45 | capture_stdout=True, | 46 | capture_stdout=True, |
46 | capture_stderr=True, | 47 | capture_stderr=True, |
48 | verify_command=True, | ||
47 | ) | 49 | ) |
48 | if p.Wait() != 0: | 50 | try: |
51 | p.Wait() | ||
52 | except GitError: | ||
49 | print(p.stderr, file=sys.stderr) | 53 | print(p.stderr, file=sys.stderr) |
50 | sys.exit(1) | 54 | raise |
55 | |||
51 | sha1 = p.stdout.strip() | 56 | sha1 = p.stdout.strip() |
52 | 57 | ||
53 | p = GitCommand(None, ["cat-file", "commit", sha1], capture_stdout=True) | 58 | p = GitCommand( |
54 | if p.Wait() != 0: | 59 | None, |
60 | ["cat-file", "commit", sha1], | ||
61 | capture_stdout=True, | ||
62 | verify_command=True, | ||
63 | ) | ||
64 | |||
65 | try: | ||
66 | p.Wait() | ||
67 | except GitError: | ||
55 | print( | 68 | print( |
56 | "error: Failed to retrieve old commit message", file=sys.stderr | 69 | "error: Failed to retrieve old commit message", file=sys.stderr |
57 | ) | 70 | ) |
58 | sys.exit(1) | 71 | raise |
72 | |||
59 | old_msg = self._StripHeader(p.stdout) | 73 | old_msg = self._StripHeader(p.stdout) |
60 | 74 | ||
61 | p = GitCommand( | 75 | p = GitCommand( |
@@ -63,37 +77,47 @@ change id will be added. | |||
63 | ["cherry-pick", sha1], | 77 | ["cherry-pick", sha1], |
64 | capture_stdout=True, | 78 | capture_stdout=True, |
65 | capture_stderr=True, | 79 | capture_stderr=True, |
80 | verify_command=True, | ||
66 | ) | 81 | ) |
67 | status = p.Wait() | 82 | |
83 | try: | ||
84 | p.Wait() | ||
85 | except GitError as e: | ||
86 | print(str(e)) | ||
87 | print( | ||
88 | "NOTE: When committing (please see above) and editing the " | ||
89 | "commit message, please remove the old Change-Id-line and " | ||
90 | "add:" | ||
91 | ) | ||
92 | print(self._GetReference(sha1), file=sys.stderr) | ||
93 | print(file=sys.stderr) | ||
94 | raise | ||
68 | 95 | ||
69 | if p.stdout: | 96 | if p.stdout: |
70 | print(p.stdout.strip(), file=sys.stdout) | 97 | print(p.stdout.strip(), file=sys.stdout) |
71 | if p.stderr: | 98 | if p.stderr: |
72 | print(p.stderr.strip(), file=sys.stderr) | 99 | print(p.stderr.strip(), file=sys.stderr) |
73 | 100 | ||
74 | if status == 0: | 101 | # The cherry-pick was applied correctly. We just need to edit |
75 | # The cherry-pick was applied correctly. We just need to edit the | 102 | # the commit message. |
76 | # commit message. | 103 | new_msg = self._Reformat(old_msg, sha1) |
77 | new_msg = self._Reformat(old_msg, sha1) | ||
78 | |||
79 | p = GitCommand( | ||
80 | None, | ||
81 | ["commit", "--amend", "-F", "-"], | ||
82 | input=new_msg, | ||
83 | capture_stdout=True, | ||
84 | capture_stderr=True, | ||
85 | ) | ||
86 | if p.Wait() != 0: | ||
87 | print("error: Failed to update commit message", file=sys.stderr) | ||
88 | sys.exit(1) | ||
89 | 104 | ||
90 | else: | 105 | p = GitCommand( |
106 | None, | ||
107 | ["commit", "--amend", "-F", "-"], | ||
108 | input=new_msg, | ||
109 | capture_stdout=True, | ||
110 | capture_stderr=True, | ||
111 | verify_command=True, | ||
112 | ) | ||
113 | try: | ||
114 | p.Wait() | ||
115 | except GitError: | ||
91 | print( | 116 | print( |
92 | "NOTE: When committing (please see above) and editing the " | 117 | "error: Failed to update commit message", |
93 | "commit message, please remove the old Change-Id-line and add:" | 118 | file=sys.stderr, |
94 | ) | 119 | ) |
95 | print(self._GetReference(sha1), file=sys.stderr) | 120 | raise |
96 | print(file=sys.stderr) | ||
97 | 121 | ||
98 | def _IsChangeId(self, line): | 122 | def _IsChangeId(self, line): |
99 | return CHANGE_ID_RE.match(line) | 123 | return CHANGE_ID_RE.match(line) |
diff --git a/subcmds/download.py b/subcmds/download.py index 475c0bc2..18e555be 100644 --- a/subcmds/download.py +++ b/subcmds/download.py | |||
@@ -16,11 +16,15 @@ import re | |||
16 | import sys | 16 | import sys |
17 | 17 | ||
18 | from command import Command | 18 | from command import Command |
19 | from error import GitError, NoSuchProjectError | 19 | from error import GitError, NoSuchProjectError, RepoExitError |
20 | 20 | ||
21 | CHANGE_RE = re.compile(r"^([1-9][0-9]*)(?:[/\.-]([1-9][0-9]*))?$") | 21 | CHANGE_RE = re.compile(r"^([1-9][0-9]*)(?:[/\.-]([1-9][0-9]*))?$") |
22 | 22 | ||
23 | 23 | ||
24 | class DownloadCommandError(RepoExitError): | ||
25 | """Error raised when download command fails.""" | ||
26 | |||
27 | |||
24 | class Download(Command): | 28 | class Download(Command): |
25 | COMMON = True | 29 | COMMON = True |
26 | helpSummary = "Download and checkout a change" | 30 | helpSummary = "Download and checkout a change" |
@@ -137,15 +141,16 @@ If no project is specified try to use current directory as a project. | |||
137 | ) | 141 | ) |
138 | 142 | ||
139 | def Execute(self, opt, args): | 143 | def Execute(self, opt, args): |
144 | try: | ||
145 | self._ExecuteHelper(opt, args) | ||
146 | except Exception as e: | ||
147 | if isinstance(e, RepoExitError): | ||
148 | raise e | ||
149 | raise DownloadCommandError(aggregate_errors=[e]) | ||
150 | |||
151 | def _ExecuteHelper(self, opt, args): | ||
140 | for project, change_id, ps_id in self._ParseChangeIds(opt, args): | 152 | for project, change_id, ps_id in self._ParseChangeIds(opt, args): |
141 | dl = project.DownloadPatchSet(change_id, ps_id) | 153 | dl = project.DownloadPatchSet(change_id, ps_id) |
142 | if not dl: | ||
143 | print( | ||
144 | "[%s] change %d/%d not found" | ||
145 | % (project.name, change_id, ps_id), | ||
146 | file=sys.stderr, | ||
147 | ) | ||
148 | sys.exit(1) | ||
149 | 154 | ||
150 | if not opt.revert and not dl.commits: | 155 | if not opt.revert and not dl.commits: |
151 | print( | 156 | print( |
@@ -201,4 +206,4 @@ If no project is specified try to use current directory as a project. | |||
201 | % (project.name, mode, dl.commit), | 206 | % (project.name, mode, dl.commit), |
202 | file=sys.stderr, | 207 | file=sys.stderr, |
203 | ) | 208 | ) |
204 | sys.exit(1) | 209 | raise |
diff --git a/subcmds/grep.py b/subcmds/grep.py index 5cd33763..9ebd776c 100644 --- a/subcmds/grep.py +++ b/subcmds/grep.py | |||
@@ -17,8 +17,10 @@ import sys | |||
17 | 17 | ||
18 | from color import Coloring | 18 | from color import Coloring |
19 | from command import DEFAULT_LOCAL_JOBS, PagedCommand | 19 | from command import DEFAULT_LOCAL_JOBS, PagedCommand |
20 | from error import GitError | 20 | from error import GitError, InvalidArgumentsError, SilentRepoExitError |
21 | from git_command import GitCommand | 21 | from git_command import GitCommand |
22 | from typing import NamedTuple | ||
23 | from project import Project | ||
22 | 24 | ||
23 | 25 | ||
24 | class GrepColoring(Coloring): | 26 | class GrepColoring(Coloring): |
@@ -28,6 +30,22 @@ class GrepColoring(Coloring): | |||
28 | self.fail = self.printer("fail", fg="red") | 30 | self.fail = self.printer("fail", fg="red") |
29 | 31 | ||
30 | 32 | ||
33 | class ExecuteOneResult(NamedTuple): | ||
34 | """Result from an execute instance.""" | ||
35 | |||
36 | project: Project | ||
37 | rc: int | ||
38 | stdout: str | ||
39 | stderr: str | ||
40 | error: GitError | ||
41 | |||
42 | |||
43 | class GrepCommandError(SilentRepoExitError): | ||
44 | """Grep command failure. Since Grep command | ||
45 | output already outputs errors ensure that | ||
46 | aggregate errors exit silently.""" | ||
47 | |||
48 | |||
31 | class Grep(PagedCommand): | 49 | class Grep(PagedCommand): |
32 | COMMON = True | 50 | COMMON = True |
33 | helpSummary = "Print lines matching a pattern" | 51 | helpSummary = "Print lines matching a pattern" |
@@ -246,11 +264,18 @@ contain a line that matches both expressions: | |||
246 | bare=False, | 264 | bare=False, |
247 | capture_stdout=True, | 265 | capture_stdout=True, |
248 | capture_stderr=True, | 266 | capture_stderr=True, |
267 | verify_command=True, | ||
249 | ) | 268 | ) |
250 | except GitError as e: | 269 | except GitError as e: |
251 | return (project, -1, None, str(e)) | 270 | return ExecuteOneResult(project, -1, None, str(e), e) |
252 | 271 | ||
253 | return (project, p.Wait(), p.stdout, p.stderr) | 272 | try: |
273 | error = None | ||
274 | rc = p.Wait() | ||
275 | except GitError as e: | ||
276 | rc = 1 | ||
277 | error = e | ||
278 | return ExecuteOneResult(project, rc, p.stdout, p.stderr, error) | ||
254 | 279 | ||
255 | @staticmethod | 280 | @staticmethod |
256 | def _ProcessResults(full_name, have_rev, opt, _pool, out, results): | 281 | def _ProcessResults(full_name, have_rev, opt, _pool, out, results): |
@@ -258,31 +283,40 @@ contain a line that matches both expressions: | |||
258 | bad_rev = False | 283 | bad_rev = False |
259 | have_match = False | 284 | have_match = False |
260 | _RelPath = lambda p: p.RelPath(local=opt.this_manifest_only) | 285 | _RelPath = lambda p: p.RelPath(local=opt.this_manifest_only) |
286 | errors = [] | ||
261 | 287 | ||
262 | for project, rc, stdout, stderr in results: | 288 | for result in results: |
263 | if rc < 0: | 289 | if result.rc < 0: |
264 | git_failed = True | 290 | git_failed = True |
265 | out.project("--- project %s ---" % _RelPath(project)) | 291 | out.project("--- project %s ---" % _RelPath(result.project)) |
266 | out.nl() | 292 | out.nl() |
267 | out.fail("%s", stderr) | 293 | out.fail("%s", result.stderr) |
268 | out.nl() | 294 | out.nl() |
295 | errors.append(result.error) | ||
269 | continue | 296 | continue |
270 | 297 | ||
271 | if rc: | 298 | if result.rc: |
272 | # no results | 299 | # no results |
273 | if stderr: | 300 | if result.stderr: |
274 | if have_rev and "fatal: ambiguous argument" in stderr: | 301 | if ( |
302 | have_rev | ||
303 | and "fatal: ambiguous argument" in result.stderr | ||
304 | ): | ||
275 | bad_rev = True | 305 | bad_rev = True |
276 | else: | 306 | else: |
277 | out.project("--- project %s ---" % _RelPath(project)) | 307 | out.project( |
308 | "--- project %s ---" % _RelPath(result.project) | ||
309 | ) | ||
278 | out.nl() | 310 | out.nl() |
279 | out.fail("%s", stderr.strip()) | 311 | out.fail("%s", result.stderr.strip()) |
280 | out.nl() | 312 | out.nl() |
313 | if result.error is not None: | ||
314 | errors.append(result.error) | ||
281 | continue | 315 | continue |
282 | have_match = True | 316 | have_match = True |
283 | 317 | ||
284 | # We cut the last element, to avoid a blank line. | 318 | # We cut the last element, to avoid a blank line. |
285 | r = stdout.split("\n") | 319 | r = result.stdout.split("\n") |
286 | r = r[0:-1] | 320 | r = r[0:-1] |
287 | 321 | ||
288 | if have_rev and full_name: | 322 | if have_rev and full_name: |
@@ -290,13 +324,13 @@ contain a line that matches both expressions: | |||
290 | rev, line = line.split(":", 1) | 324 | rev, line = line.split(":", 1) |
291 | out.write("%s", rev) | 325 | out.write("%s", rev) |
292 | out.write(":") | 326 | out.write(":") |
293 | out.project(_RelPath(project)) | 327 | out.project(_RelPath(result.project)) |
294 | out.write("/") | 328 | out.write("/") |
295 | out.write("%s", line) | 329 | out.write("%s", line) |
296 | out.nl() | 330 | out.nl() |
297 | elif full_name: | 331 | elif full_name: |
298 | for line in r: | 332 | for line in r: |
299 | out.project(_RelPath(project)) | 333 | out.project(_RelPath(result.project)) |
300 | out.write("/") | 334 | out.write("/") |
301 | out.write("%s", line) | 335 | out.write("%s", line) |
302 | out.nl() | 336 | out.nl() |
@@ -304,7 +338,7 @@ contain a line that matches both expressions: | |||
304 | for line in r: | 338 | for line in r: |
305 | print(line) | 339 | print(line) |
306 | 340 | ||
307 | return (git_failed, bad_rev, have_match) | 341 | return (git_failed, bad_rev, have_match, errors) |
308 | 342 | ||
309 | def Execute(self, opt, args): | 343 | def Execute(self, opt, args): |
310 | out = GrepColoring(self.manifest.manifestProject.config) | 344 | out = GrepColoring(self.manifest.manifestProject.config) |
@@ -333,16 +367,14 @@ contain a line that matches both expressions: | |||
333 | have_rev = False | 367 | have_rev = False |
334 | if opt.revision: | 368 | if opt.revision: |
335 | if "--cached" in cmd_argv: | 369 | if "--cached" in cmd_argv: |
336 | print( | 370 | msg = "fatal: cannot combine --cached and --revision" |
337 | "fatal: cannot combine --cached and --revision", | 371 | print(msg, file=sys.stderr) |
338 | file=sys.stderr, | 372 | raise InvalidArgumentsError(msg) |
339 | ) | ||
340 | sys.exit(1) | ||
341 | have_rev = True | 373 | have_rev = True |
342 | cmd_argv.extend(opt.revision) | 374 | cmd_argv.extend(opt.revision) |
343 | cmd_argv.append("--") | 375 | cmd_argv.append("--") |
344 | 376 | ||
345 | git_failed, bad_rev, have_match = self.ExecuteInParallel( | 377 | git_failed, bad_rev, have_match, errors = self.ExecuteInParallel( |
346 | opt.jobs, | 378 | opt.jobs, |
347 | functools.partial(self._ExecuteOne, cmd_argv), | 379 | functools.partial(self._ExecuteOne, cmd_argv), |
348 | projects, | 380 | projects, |
@@ -354,12 +386,12 @@ contain a line that matches both expressions: | |||
354 | ) | 386 | ) |
355 | 387 | ||
356 | if git_failed: | 388 | if git_failed: |
357 | sys.exit(1) | 389 | raise GrepCommandError( |
390 | "error: git failures", aggregate_errors=errors | ||
391 | ) | ||
358 | elif have_match: | 392 | elif have_match: |
359 | sys.exit(0) | 393 | sys.exit(0) |
360 | elif have_rev and bad_rev: | 394 | elif have_rev and bad_rev: |
361 | for r in opt.revision: | 395 | for r in opt.revision: |
362 | print("error: can't search revision %s" % r, file=sys.stderr) | 396 | print("error: can't search revision %s" % r, file=sys.stderr) |
363 | sys.exit(1) | 397 | raise GrepCommandError(aggregate_errors=errors) |
364 | else: | ||
365 | sys.exit(1) | ||
diff --git a/subcmds/help.py b/subcmds/help.py index 50a48047..593bf676 100644 --- a/subcmds/help.py +++ b/subcmds/help.py | |||
@@ -26,6 +26,11 @@ from command import ( | |||
26 | ) | 26 | ) |
27 | import gitc_utils | 27 | import gitc_utils |
28 | from wrapper import Wrapper | 28 | from wrapper import Wrapper |
29 | from error import RepoExitError | ||
30 | |||
31 | |||
32 | class InvalidHelpCommand(RepoExitError): | ||
33 | """Invalid command passed into help.""" | ||
29 | 34 | ||
30 | 35 | ||
31 | class Help(PagedCommand, MirrorSafeCommand): | 36 | class Help(PagedCommand, MirrorSafeCommand): |
@@ -202,7 +207,7 @@ Displays detailed usage information about a command. | |||
202 | print( | 207 | print( |
203 | "repo: '%s' is not a repo command." % name, file=sys.stderr | 208 | "repo: '%s' is not a repo command." % name, file=sys.stderr |
204 | ) | 209 | ) |
205 | sys.exit(1) | 210 | raise InvalidHelpCommand(name) |
206 | 211 | ||
207 | self._PrintCommandHelp(cmd) | 212 | self._PrintCommandHelp(cmd) |
208 | 213 | ||
diff --git a/subcmds/selfupdate.py b/subcmds/selfupdate.py index d5d0a838..00376b66 100644 --- a/subcmds/selfupdate.py +++ b/subcmds/selfupdate.py | |||
@@ -18,6 +18,11 @@ import sys | |||
18 | from command import Command, MirrorSafeCommand | 18 | from command import Command, MirrorSafeCommand |
19 | from subcmds.sync import _PostRepoUpgrade | 19 | from subcmds.sync import _PostRepoUpgrade |
20 | from subcmds.sync import _PostRepoFetch | 20 | from subcmds.sync import _PostRepoFetch |
21 | from error import RepoExitError | ||
22 | |||
23 | |||
24 | class SelfupdateError(RepoExitError): | ||
25 | """Exit error for failed selfupdate command.""" | ||
21 | 26 | ||
22 | 27 | ||
23 | class Selfupdate(Command, MirrorSafeCommand): | 28 | class Selfupdate(Command, MirrorSafeCommand): |
@@ -58,9 +63,10 @@ need to be performed by an end-user. | |||
58 | _PostRepoUpgrade(self.manifest) | 63 | _PostRepoUpgrade(self.manifest) |
59 | 64 | ||
60 | else: | 65 | else: |
61 | if not rp.Sync_NetworkHalf().success: | 66 | result = rp.Sync_NetworkHalf() |
67 | if result.error: | ||
62 | print("error: can't update repo", file=sys.stderr) | 68 | print("error: can't update repo", file=sys.stderr) |
63 | sys.exit(1) | 69 | raise SelfupdateError(aggregate_errors=[result.error]) |
64 | 70 | ||
65 | rp.bare_git.gc("--auto") | 71 | rp.bare_git.gc("--auto") |
66 | _PostRepoFetch(rp, repo_verify=opt.repo_verify, verbose=True) | 72 | _PostRepoFetch(rp, repo_verify=opt.repo_verify, verbose=True) |
diff --git a/subcmds/start.py b/subcmds/start.py index f6355126..67ac7df9 100644 --- a/subcmds/start.py +++ b/subcmds/start.py | |||
@@ -21,7 +21,18 @@ from git_config import IsImmutable | |||
21 | from git_command import git | 21 | from git_command import git |
22 | import gitc_utils | 22 | import gitc_utils |
23 | from progress import Progress | 23 | from progress import Progress |
24 | from project import SyncBuffer | 24 | from project import SyncBuffer, Project |
25 | from typing import NamedTuple | ||
26 | from error import RepoExitError | ||
27 | |||
28 | |||
29 | class ExecuteOneResult(NamedTuple): | ||
30 | project: Project | ||
31 | error: Exception | ||
32 | |||
33 | |||
34 | class StartError(RepoExitError): | ||
35 | """Exit error for failed start command.""" | ||
25 | 36 | ||
26 | 37 | ||
27 | class Start(Command): | 38 | class Start(Command): |
@@ -73,6 +84,7 @@ revision specified in the manifest. | |||
73 | # a change, then we can't push back to it. Substitute with | 84 | # a change, then we can't push back to it. Substitute with |
74 | # dest_branch, if defined; or with manifest default revision instead. | 85 | # dest_branch, if defined; or with manifest default revision instead. |
75 | branch_merge = "" | 86 | branch_merge = "" |
87 | error = None | ||
76 | if IsImmutable(project.revisionExpr): | 88 | if IsImmutable(project.revisionExpr): |
77 | if project.dest_branch: | 89 | if project.dest_branch: |
78 | branch_merge = project.dest_branch | 90 | branch_merge = project.dest_branch |
@@ -80,7 +92,7 @@ revision specified in the manifest. | |||
80 | branch_merge = self.manifest.default.revisionExpr | 92 | branch_merge = self.manifest.default.revisionExpr |
81 | 93 | ||
82 | try: | 94 | try: |
83 | ret = project.StartBranch( | 95 | project.StartBranch( |
84 | nb, branch_merge=branch_merge, revision=revision | 96 | nb, branch_merge=branch_merge, revision=revision |
85 | ) | 97 | ) |
86 | except Exception as e: | 98 | except Exception as e: |
@@ -88,11 +100,12 @@ revision specified in the manifest. | |||
88 | "error: unable to checkout %s: %s" % (project.name, e), | 100 | "error: unable to checkout %s: %s" % (project.name, e), |
89 | file=sys.stderr, | 101 | file=sys.stderr, |
90 | ) | 102 | ) |
91 | ret = False | 103 | error = e |
92 | return (ret, project) | 104 | return ExecuteOneResult(project, error) |
93 | 105 | ||
94 | def Execute(self, opt, args): | 106 | def Execute(self, opt, args): |
95 | nb = args[0] | 107 | nb = args[0] |
108 | err_projects = [] | ||
96 | err = [] | 109 | err = [] |
97 | projects = [] | 110 | projects = [] |
98 | if not opt.all: | 111 | if not opt.all: |
@@ -146,9 +159,10 @@ revision specified in the manifest. | |||
146 | pm.end() | 159 | pm.end() |
147 | 160 | ||
148 | def _ProcessResults(_pool, pm, results): | 161 | def _ProcessResults(_pool, pm, results): |
149 | for result, project in results: | 162 | for result in results: |
150 | if not result: | 163 | if result.error: |
151 | err.append(project) | 164 | err_projects.append(result.project) |
165 | err.append(result.error) | ||
152 | pm.update(msg="") | 166 | pm.update(msg="") |
153 | 167 | ||
154 | self.ExecuteInParallel( | 168 | self.ExecuteInParallel( |
@@ -161,13 +175,15 @@ revision specified in the manifest. | |||
161 | ), | 175 | ), |
162 | ) | 176 | ) |
163 | 177 | ||
164 | if err: | 178 | if err_projects: |
165 | for p in err: | 179 | for p in err_projects: |
166 | print( | 180 | print( |
167 | "error: %s/: cannot start %s" | 181 | "error: %s/: cannot start %s" |
168 | % (p.RelPath(local=opt.this_manifest_only), nb), | 182 | % (p.RelPath(local=opt.this_manifest_only), nb), |
169 | file=sys.stderr, | 183 | file=sys.stderr, |
170 | ) | 184 | ) |
171 | msg_fmt = "cannot start %d project(s)" | 185 | msg_fmt = "cannot start %d project(s)" |
172 | self.git_event_log.ErrorEvent(msg_fmt % (len(err)), msg_fmt) | 186 | self.git_event_log.ErrorEvent( |
173 | sys.exit(1) | 187 | msg_fmt % (len(err_projects)), msg_fmt |
188 | ) | ||
189 | raise StartError(aggregate_errors=err) | ||