diff options
Diffstat (limited to 'subcmds')
-rw-r--r-- | subcmds/abandon.py | 2 | ||||
-rw-r--r-- | subcmds/branches.py | 2 | ||||
-rw-r--r-- | subcmds/checkout.py | 2 | ||||
-rw-r--r-- | subcmds/cherry_pick.py | 2 | ||||
-rw-r--r-- | subcmds/diff.py | 4 | ||||
-rw-r--r-- | subcmds/diffmanifests.py | 6 | ||||
-rw-r--r-- | subcmds/download.py | 2 | ||||
-rw-r--r-- | subcmds/forall.py | 18 | ||||
-rw-r--r-- | subcmds/gitc_delete.py | 4 | ||||
-rw-r--r-- | subcmds/gitc_init.py | 2 | ||||
-rw-r--r-- | subcmds/grep.py | 2 | ||||
-rw-r--r-- | subcmds/help.py | 19 | ||||
-rw-r--r-- | subcmds/info.py | 17 | ||||
-rw-r--r-- | subcmds/init.py | 99 | ||||
-rw-r--r-- | subcmds/list.py | 28 | ||||
-rw-r--r-- | subcmds/manifest.py | 27 | ||||
-rw-r--r-- | subcmds/overview.py | 17 | ||||
-rw-r--r-- | subcmds/prune.py | 2 | ||||
-rw-r--r-- | subcmds/rebase.py | 20 | ||||
-rw-r--r-- | subcmds/selfupdate.py | 2 | ||||
-rw-r--r-- | subcmds/smartsync.py | 2 | ||||
-rw-r--r-- | subcmds/stage.py | 2 | ||||
-rw-r--r-- | subcmds/start.py | 2 | ||||
-rw-r--r-- | subcmds/status.py | 2 | ||||
-rw-r--r-- | subcmds/sync.py | 306 | ||||
-rw-r--r-- | subcmds/upload.py | 112 | ||||
-rw-r--r-- | subcmds/version.py | 4 |
27 files changed, 486 insertions, 221 deletions
diff --git a/subcmds/abandon.py b/subcmds/abandon.py index c7c127d6..85d85f5a 100644 --- a/subcmds/abandon.py +++ b/subcmds/abandon.py | |||
@@ -23,7 +23,7 @@ from progress import Progress | |||
23 | 23 | ||
24 | 24 | ||
25 | class Abandon(Command): | 25 | class Abandon(Command): |
26 | common = True | 26 | COMMON = True |
27 | helpSummary = "Permanently abandon a development branch" | 27 | helpSummary = "Permanently abandon a development branch" |
28 | helpUsage = """ | 28 | helpUsage = """ |
29 | %prog [--all | <branchname>] [<project>...] | 29 | %prog [--all | <branchname>] [<project>...] |
diff --git a/subcmds/branches.py b/subcmds/branches.py index 2dc102bb..6d975ed4 100644 --- a/subcmds/branches.py +++ b/subcmds/branches.py | |||
@@ -62,7 +62,7 @@ class BranchInfo(object): | |||
62 | 62 | ||
63 | 63 | ||
64 | class Branches(Command): | 64 | class Branches(Command): |
65 | common = True | 65 | COMMON = True |
66 | helpSummary = "View current topic branches" | 66 | helpSummary = "View current topic branches" |
67 | helpUsage = """ | 67 | helpUsage = """ |
68 | %prog [<project>...] | 68 | %prog [<project>...] |
diff --git a/subcmds/checkout.py b/subcmds/checkout.py index 4d8009b1..9b429489 100644 --- a/subcmds/checkout.py +++ b/subcmds/checkout.py | |||
@@ -20,7 +20,7 @@ from progress import Progress | |||
20 | 20 | ||
21 | 21 | ||
22 | class Checkout(Command): | 22 | class Checkout(Command): |
23 | common = True | 23 | COMMON = True |
24 | helpSummary = "Checkout a branch for development" | 24 | helpSummary = "Checkout a branch for development" |
25 | helpUsage = """ | 25 | helpUsage = """ |
26 | %prog <branchname> [<project>...] | 26 | %prog <branchname> [<project>...] |
diff --git a/subcmds/cherry_pick.py b/subcmds/cherry_pick.py index fc4998c3..7bd858bf 100644 --- a/subcmds/cherry_pick.py +++ b/subcmds/cherry_pick.py | |||
@@ -21,7 +21,7 @@ CHANGE_ID_RE = re.compile(r'^\s*Change-Id: I([0-9a-f]{40})\s*$') | |||
21 | 21 | ||
22 | 22 | ||
23 | class CherryPick(Command): | 23 | class CherryPick(Command): |
24 | common = True | 24 | COMMON = True |
25 | helpSummary = "Cherry-pick a change." | 25 | helpSummary = "Cherry-pick a change." |
26 | helpUsage = """ | 26 | helpUsage = """ |
27 | %prog <sha1> | 27 | %prog <sha1> |
diff --git a/subcmds/diff.py b/subcmds/diff.py index 4966bb1a..00a7ec29 100644 --- a/subcmds/diff.py +++ b/subcmds/diff.py | |||
@@ -19,7 +19,7 @@ from command import DEFAULT_LOCAL_JOBS, PagedCommand | |||
19 | 19 | ||
20 | 20 | ||
21 | class Diff(PagedCommand): | 21 | class Diff(PagedCommand): |
22 | common = True | 22 | COMMON = True |
23 | helpSummary = "Show changes between commit and working tree" | 23 | helpSummary = "Show changes between commit and working tree" |
24 | helpUsage = """ | 24 | helpUsage = """ |
25 | %prog [<project>...] | 25 | %prog [<project>...] |
@@ -33,7 +33,7 @@ to the Unix 'patch' command. | |||
33 | def _Options(self, p): | 33 | def _Options(self, p): |
34 | p.add_option('-u', '--absolute', | 34 | p.add_option('-u', '--absolute', |
35 | dest='absolute', action='store_true', | 35 | dest='absolute', action='store_true', |
36 | help='Paths are relative to the repository root') | 36 | help='paths are relative to the repository root') |
37 | 37 | ||
38 | def _ExecuteOne(self, absolute, project): | 38 | def _ExecuteOne(self, absolute, project): |
39 | """Obtains the diff for a specific project. | 39 | """Obtains the diff for a specific project. |
diff --git a/subcmds/diffmanifests.py b/subcmds/diffmanifests.py index 392e5972..f6cc30a2 100644 --- a/subcmds/diffmanifests.py +++ b/subcmds/diffmanifests.py | |||
@@ -31,7 +31,7 @@ class Diffmanifests(PagedCommand): | |||
31 | deeper level. | 31 | deeper level. |
32 | """ | 32 | """ |
33 | 33 | ||
34 | common = True | 34 | COMMON = True |
35 | helpSummary = "Manifest diff utility" | 35 | helpSummary = "Manifest diff utility" |
36 | helpUsage = """%prog manifest1.xml [manifest2.xml] [options]""" | 36 | helpUsage = """%prog manifest1.xml [manifest2.xml] [options]""" |
37 | 37 | ||
@@ -68,10 +68,10 @@ synced and their revisions won't be found. | |||
68 | def _Options(self, p): | 68 | def _Options(self, p): |
69 | p.add_option('--raw', | 69 | p.add_option('--raw', |
70 | dest='raw', action='store_true', | 70 | dest='raw', action='store_true', |
71 | help='Display raw diff.') | 71 | help='display raw diff') |
72 | p.add_option('--no-color', | 72 | p.add_option('--no-color', |
73 | dest='color', action='store_false', default=True, | 73 | dest='color', action='store_false', default=True, |
74 | help='does not display the diff in color.') | 74 | help='does not display the diff in color') |
75 | p.add_option('--pretty-format', | 75 | p.add_option('--pretty-format', |
76 | dest='pretty_format', action='store', | 76 | dest='pretty_format', action='store', |
77 | metavar='<FORMAT>', | 77 | metavar='<FORMAT>', |
diff --git a/subcmds/download.py b/subcmds/download.py index 81d997e0..523f25e0 100644 --- a/subcmds/download.py +++ b/subcmds/download.py | |||
@@ -22,7 +22,7 @@ CHANGE_RE = re.compile(r'^([1-9][0-9]*)(?:[/\.-]([1-9][0-9]*))?$') | |||
22 | 22 | ||
23 | 23 | ||
24 | class Download(Command): | 24 | class Download(Command): |
25 | common = True | 25 | COMMON = True |
26 | helpSummary = "Download and checkout a change" | 26 | helpSummary = "Download and checkout a change" |
27 | helpUsage = """ | 27 | helpUsage = """ |
28 | %prog {[project] change[/patchset]}... | 28 | %prog {[project] change[/patchset]}... |
diff --git a/subcmds/forall.py b/subcmds/forall.py index 4a631fb7..7c1dea9e 100644 --- a/subcmds/forall.py +++ b/subcmds/forall.py | |||
@@ -41,7 +41,7 @@ class ForallColoring(Coloring): | |||
41 | 41 | ||
42 | 42 | ||
43 | class Forall(Command, MirrorSafeCommand): | 43 | class Forall(Command, MirrorSafeCommand): |
44 | common = False | 44 | COMMON = False |
45 | helpSummary = "Run a shell command in each project" | 45 | helpSummary = "Run a shell command in each project" |
46 | helpUsage = """ | 46 | helpUsage = """ |
47 | %prog [<project>...] -c <command> [<arg>...] | 47 | %prog [<project>...] -c <command> [<arg>...] |
@@ -131,30 +131,30 @@ without iterating through the remaining projects. | |||
131 | def _Options(self, p): | 131 | def _Options(self, p): |
132 | p.add_option('-r', '--regex', | 132 | p.add_option('-r', '--regex', |
133 | dest='regex', action='store_true', | 133 | dest='regex', action='store_true', |
134 | help="Execute the command only on projects matching regex or wildcard expression") | 134 | help='execute the command only on projects matching regex or wildcard expression') |
135 | p.add_option('-i', '--inverse-regex', | 135 | p.add_option('-i', '--inverse-regex', |
136 | dest='inverse_regex', action='store_true', | 136 | dest='inverse_regex', action='store_true', |
137 | help="Execute the command only on projects not matching regex or " | 137 | help='execute the command only on projects not matching regex or ' |
138 | "wildcard expression") | 138 | 'wildcard expression') |
139 | p.add_option('-g', '--groups', | 139 | p.add_option('-g', '--groups', |
140 | dest='groups', | 140 | dest='groups', |
141 | help="Execute the command only on projects matching the specified groups") | 141 | help='execute the command only on projects matching the specified groups') |
142 | p.add_option('-c', '--command', | 142 | p.add_option('-c', '--command', |
143 | help='Command (and arguments) to execute', | 143 | help='command (and arguments) to execute', |
144 | dest='command', | 144 | dest='command', |
145 | action='callback', | 145 | action='callback', |
146 | callback=self._cmd_option) | 146 | callback=self._cmd_option) |
147 | p.add_option('-e', '--abort-on-errors', | 147 | p.add_option('-e', '--abort-on-errors', |
148 | dest='abort_on_errors', action='store_true', | 148 | dest='abort_on_errors', action='store_true', |
149 | help='Abort if a command exits unsuccessfully') | 149 | help='abort if a command exits unsuccessfully') |
150 | p.add_option('--ignore-missing', action='store_true', | 150 | p.add_option('--ignore-missing', action='store_true', |
151 | help='Silently skip & do not exit non-zero due missing ' | 151 | help='silently skip & do not exit non-zero due missing ' |
152 | 'checkouts') | 152 | 'checkouts') |
153 | 153 | ||
154 | g = p.get_option_group('--quiet') | 154 | g = p.get_option_group('--quiet') |
155 | g.add_option('-p', | 155 | g.add_option('-p', |
156 | dest='project_header', action='store_true', | 156 | dest='project_header', action='store_true', |
157 | help='Show project headers before output') | 157 | help='show project headers before output') |
158 | p.add_option('--interactive', | 158 | p.add_option('--interactive', |
159 | action='store_true', | 159 | action='store_true', |
160 | help='force interactive usage') | 160 | help='force interactive usage') |
diff --git a/subcmds/gitc_delete.py b/subcmds/gitc_delete.py index 56e0eaba..df749469 100644 --- a/subcmds/gitc_delete.py +++ b/subcmds/gitc_delete.py | |||
@@ -19,7 +19,7 @@ import platform_utils | |||
19 | 19 | ||
20 | 20 | ||
21 | class GitcDelete(Command, GitcClientCommand): | 21 | class GitcDelete(Command, GitcClientCommand): |
22 | common = True | 22 | COMMON = True |
23 | visible_everywhere = False | 23 | visible_everywhere = False |
24 | helpSummary = "Delete a GITC Client." | 24 | helpSummary = "Delete a GITC Client." |
25 | helpUsage = """ | 25 | helpUsage = """ |
@@ -33,7 +33,7 @@ and all locally downloaded sources. | |||
33 | def _Options(self, p): | 33 | def _Options(self, p): |
34 | p.add_option('-f', '--force', | 34 | p.add_option('-f', '--force', |
35 | dest='force', action='store_true', | 35 | dest='force', action='store_true', |
36 | help='Force the deletion (no prompt).') | 36 | help='force the deletion (no prompt)') |
37 | 37 | ||
38 | def Execute(self, opt, args): | 38 | def Execute(self, opt, args): |
39 | if not opt.force: | 39 | if not opt.force: |
diff --git a/subcmds/gitc_init.py b/subcmds/gitc_init.py index 23a4ebb6..e705b613 100644 --- a/subcmds/gitc_init.py +++ b/subcmds/gitc_init.py | |||
@@ -23,7 +23,7 @@ import wrapper | |||
23 | 23 | ||
24 | 24 | ||
25 | class GitcInit(init.Init, GitcAvailableCommand): | 25 | class GitcInit(init.Init, GitcAvailableCommand): |
26 | common = True | 26 | COMMON = True |
27 | helpSummary = "Initialize a GITC Client." | 27 | helpSummary = "Initialize a GITC Client." |
28 | helpUsage = """ | 28 | helpUsage = """ |
29 | %prog [options] [client name] | 29 | %prog [options] [client name] |
diff --git a/subcmds/grep.py b/subcmds/grep.py index 6cb1445a..8ac4ba14 100644 --- a/subcmds/grep.py +++ b/subcmds/grep.py | |||
@@ -29,7 +29,7 @@ class GrepColoring(Coloring): | |||
29 | 29 | ||
30 | 30 | ||
31 | class Grep(PagedCommand): | 31 | class Grep(PagedCommand): |
32 | common = True | 32 | COMMON = True |
33 | helpSummary = "Print lines matching a pattern" | 33 | helpSummary = "Print lines matching a pattern" |
34 | helpUsage = """ | 34 | helpUsage = """ |
35 | %prog {pattern | -e pattern} [<project>...] | 35 | %prog {pattern | -e pattern} [<project>...] |
diff --git a/subcmds/help.py b/subcmds/help.py index 6a767e6f..1a60ef45 100644 --- a/subcmds/help.py +++ b/subcmds/help.py | |||
@@ -20,10 +20,11 @@ from subcmds import all_commands | |||
20 | from color import Coloring | 20 | from color import Coloring |
21 | from command import PagedCommand, MirrorSafeCommand, GitcAvailableCommand, GitcClientCommand | 21 | from command import PagedCommand, MirrorSafeCommand, GitcAvailableCommand, GitcClientCommand |
22 | import gitc_utils | 22 | import gitc_utils |
23 | from wrapper import Wrapper | ||
23 | 24 | ||
24 | 25 | ||
25 | class Help(PagedCommand, MirrorSafeCommand): | 26 | class Help(PagedCommand, MirrorSafeCommand): |
26 | common = False | 27 | COMMON = False |
27 | helpSummary = "Display detailed help on a command" | 28 | helpSummary = "Display detailed help on a command" |
28 | helpUsage = """ | 29 | helpUsage = """ |
29 | %prog [--all|command] | 30 | %prog [--all|command] |
@@ -49,14 +50,21 @@ Displays detailed usage information about a command. | |||
49 | 50 | ||
50 | def _PrintAllCommands(self): | 51 | def _PrintAllCommands(self): |
51 | print('usage: repo COMMAND [ARGS]') | 52 | print('usage: repo COMMAND [ARGS]') |
53 | self.PrintAllCommandsBody() | ||
54 | |||
55 | def PrintAllCommandsBody(self): | ||
52 | print('The complete list of recognized repo commands are:') | 56 | print('The complete list of recognized repo commands are:') |
53 | commandNames = list(sorted(all_commands)) | 57 | commandNames = list(sorted(all_commands)) |
54 | self._PrintCommands(commandNames) | 58 | self._PrintCommands(commandNames) |
55 | print("See 'repo help <command>' for more information on a " | 59 | print("See 'repo help <command>' for more information on a " |
56 | 'specific command.') | 60 | 'specific command.') |
61 | print('Bug reports:', Wrapper().BUG_URL) | ||
57 | 62 | ||
58 | def _PrintCommonCommands(self): | 63 | def _PrintCommonCommands(self): |
59 | print('usage: repo COMMAND [ARGS]') | 64 | print('usage: repo COMMAND [ARGS]') |
65 | self.PrintCommonCommandsBody() | ||
66 | |||
67 | def PrintCommonCommandsBody(self): | ||
60 | print('The most commonly used repo commands are:') | 68 | print('The most commonly used repo commands are:') |
61 | 69 | ||
62 | def gitc_supported(cmd): | 70 | def gitc_supported(cmd): |
@@ -72,12 +80,13 @@ Displays detailed usage information about a command. | |||
72 | 80 | ||
73 | commandNames = list(sorted([name | 81 | commandNames = list(sorted([name |
74 | for name, command in all_commands.items() | 82 | for name, command in all_commands.items() |
75 | if command.common and gitc_supported(command)])) | 83 | if command.COMMON and gitc_supported(command)])) |
76 | self._PrintCommands(commandNames) | 84 | self._PrintCommands(commandNames) |
77 | 85 | ||
78 | print( | 86 | print( |
79 | "See 'repo help <command>' for more information on a specific command.\n" | 87 | "See 'repo help <command>' for more information on a specific command.\n" |
80 | "See 'repo help --all' for a complete list of recognized commands.") | 88 | "See 'repo help --all' for a complete list of recognized commands.") |
89 | print('Bug reports:', Wrapper().BUG_URL) | ||
81 | 90 | ||
82 | def _PrintCommandHelp(self, cmd, header_prefix=''): | 91 | def _PrintCommandHelp(self, cmd, header_prefix=''): |
83 | class _Out(Coloring): | 92 | class _Out(Coloring): |
@@ -136,8 +145,7 @@ Displays detailed usage information about a command. | |||
136 | 145 | ||
137 | def _PrintAllCommandHelp(self): | 146 | def _PrintAllCommandHelp(self): |
138 | for name in sorted(all_commands): | 147 | for name in sorted(all_commands): |
139 | cmd = all_commands[name]() | 148 | cmd = all_commands[name](manifest=self.manifest) |
140 | cmd.manifest = self.manifest | ||
141 | self._PrintCommandHelp(cmd, header_prefix='[%s] ' % (name,)) | 149 | self._PrintCommandHelp(cmd, header_prefix='[%s] ' % (name,)) |
142 | 150 | ||
143 | def _Options(self, p): | 151 | def _Options(self, p): |
@@ -161,12 +169,11 @@ Displays detailed usage information about a command. | |||
161 | name = args[0] | 169 | name = args[0] |
162 | 170 | ||
163 | try: | 171 | try: |
164 | cmd = all_commands[name]() | 172 | cmd = all_commands[name](manifest=self.manifest) |
165 | except KeyError: | 173 | except KeyError: |
166 | print("repo: '%s' is not a repo command." % name, file=sys.stderr) | 174 | print("repo: '%s' is not a repo command." % name, file=sys.stderr) |
167 | sys.exit(1) | 175 | sys.exit(1) |
168 | 176 | ||
169 | cmd.manifest = self.manifest | ||
170 | self._PrintCommandHelp(cmd) | 177 | self._PrintCommandHelp(cmd) |
171 | 178 | ||
172 | else: | 179 | else: |
diff --git a/subcmds/info.py b/subcmds/info.py index 6381fa8e..6c1246ef 100644 --- a/subcmds/info.py +++ b/subcmds/info.py | |||
@@ -12,6 +12,8 @@ | |||
12 | # See the License for the specific language governing permissions and | 12 | # See the License for the specific language governing permissions and |
13 | # limitations under the License. | 13 | # limitations under the License. |
14 | 14 | ||
15 | import optparse | ||
16 | |||
15 | from command import PagedCommand | 17 | from command import PagedCommand |
16 | from color import Coloring | 18 | from color import Coloring |
17 | from git_refs import R_M, R_HEADS | 19 | from git_refs import R_M, R_HEADS |
@@ -23,9 +25,9 @@ class _Coloring(Coloring): | |||
23 | 25 | ||
24 | 26 | ||
25 | class Info(PagedCommand): | 27 | class Info(PagedCommand): |
26 | common = True | 28 | COMMON = True |
27 | helpSummary = "Get info on the manifest branch, current branch or unmerged branches" | 29 | helpSummary = "Get info on the manifest branch, current branch or unmerged branches" |
28 | helpUsage = "%prog [-dl] [-o [-b]] [<project>...]" | 30 | helpUsage = "%prog [-dl] [-o [-c]] [<project>...]" |
29 | 31 | ||
30 | def _Options(self, p): | 32 | def _Options(self, p): |
31 | p.add_option('-d', '--diff', | 33 | p.add_option('-d', '--diff', |
@@ -34,12 +36,19 @@ class Info(PagedCommand): | |||
34 | p.add_option('-o', '--overview', | 36 | p.add_option('-o', '--overview', |
35 | dest='overview', action='store_true', | 37 | dest='overview', action='store_true', |
36 | help='show overview of all local commits') | 38 | help='show overview of all local commits') |
37 | p.add_option('-b', '--current-branch', | 39 | p.add_option('-c', '--current-branch', |
38 | dest="current_branch", action="store_true", | 40 | dest="current_branch", action="store_true", |
39 | help="consider only checked out branches") | 41 | help="consider only checked out branches") |
42 | p.add_option('--no-current-branch', | ||
43 | dest='current_branch', action='store_false', | ||
44 | help='consider all local branches') | ||
45 | # Turn this into a warning & remove this someday. | ||
46 | p.add_option('-b', | ||
47 | dest='current_branch', action='store_true', | ||
48 | help=optparse.SUPPRESS_HELP) | ||
40 | p.add_option('-l', '--local-only', | 49 | p.add_option('-l', '--local-only', |
41 | dest="local", action="store_true", | 50 | dest="local", action="store_true", |
42 | help="Disable all remote operations") | 51 | help="disable all remote operations") |
43 | 52 | ||
44 | def Execute(self, opt, args): | 53 | def Execute(self, opt, args): |
45 | self.out = _Coloring(self.client.globalConfig) | 54 | self.out = _Coloring(self.client.globalConfig) |
diff --git a/subcmds/init.py b/subcmds/init.py index 4182262e..9c6b2ad9 100644 --- a/subcmds/init.py +++ b/subcmds/init.py | |||
@@ -12,10 +12,10 @@ | |||
12 | # See the License for the specific language governing permissions and | 12 | # See the License for the specific language governing permissions and |
13 | # limitations under the License. | 13 | # limitations under the License. |
14 | 14 | ||
15 | import optparse | ||
16 | import os | 15 | import os |
17 | import platform | 16 | import platform |
18 | import re | 17 | import re |
18 | import subprocess | ||
19 | import sys | 19 | import sys |
20 | import urllib.parse | 20 | import urllib.parse |
21 | 21 | ||
@@ -25,13 +25,14 @@ from error import ManifestParseError | |||
25 | from project import SyncBuffer | 25 | from project import SyncBuffer |
26 | from git_config import GitConfig | 26 | from git_config import GitConfig |
27 | from git_command import git_require, MIN_GIT_VERSION_SOFT, MIN_GIT_VERSION_HARD | 27 | from git_command import git_require, MIN_GIT_VERSION_SOFT, MIN_GIT_VERSION_HARD |
28 | import fetch | ||
28 | import git_superproject | 29 | import git_superproject |
29 | import platform_utils | 30 | import platform_utils |
30 | from wrapper import Wrapper | 31 | from wrapper import Wrapper |
31 | 32 | ||
32 | 33 | ||
33 | class Init(InteractiveCommand, MirrorSafeCommand): | 34 | class Init(InteractiveCommand, MirrorSafeCommand): |
34 | common = True | 35 | COMMON = True |
35 | helpSummary = "Initialize a repo client checkout in the current directory" | 36 | helpSummary = "Initialize a repo client checkout in the current directory" |
36 | helpUsage = """ | 37 | helpUsage = """ |
37 | %prog [options] [manifest url] | 38 | %prog [options] [manifest url] |
@@ -54,6 +55,12 @@ The optional -m argument can be used to specify an alternate manifest | |||
54 | to be used. If no manifest is specified, the manifest default.xml | 55 | to be used. If no manifest is specified, the manifest default.xml |
55 | will be used. | 56 | will be used. |
56 | 57 | ||
58 | If the --standalone-manifest argument is set, the manifest will be downloaded | ||
59 | directly from the specified --manifest-url as a static file (rather than | ||
60 | setting up a manifest git checkout). With --standalone-manifest, the manifest | ||
61 | will be fully static and will not be re-downloaded during subsesquent | ||
62 | `repo init` and `repo sync` calls. | ||
63 | |||
57 | The --reference option can be used to point to a directory that | 64 | The --reference option can be used to point to a directory that |
58 | has the content of a --mirror sync. This will make the working | 65 | has the content of a --mirror sync. This will make the working |
59 | directory use as much data as possible from the local reference | 66 | directory use as much data as possible from the local reference |
@@ -97,15 +104,38 @@ to update the working directory files. | |||
97 | """ | 104 | """ |
98 | superproject = git_superproject.Superproject(self.manifest, | 105 | superproject = git_superproject.Superproject(self.manifest, |
99 | self.repodir, | 106 | self.repodir, |
107 | self.git_event_log, | ||
100 | quiet=opt.quiet) | 108 | quiet=opt.quiet) |
101 | if not superproject.Sync(): | 109 | sync_result = superproject.Sync() |
102 | print('error: git update of superproject failed', file=sys.stderr) | 110 | if not sync_result.success: |
103 | sys.exit(1) | 111 | print('warning: git update of superproject failed, repo sync will not ' |
112 | 'use superproject to fetch source; while this error is not fatal, ' | ||
113 | 'and you can continue to run repo sync, please run repo init with ' | ||
114 | 'the --no-use-superproject option to stop seeing this warning', | ||
115 | file=sys.stderr) | ||
116 | if sync_result.fatal and opt.use_superproject is not None: | ||
117 | sys.exit(1) | ||
104 | 118 | ||
105 | def _SyncManifest(self, opt): | 119 | def _SyncManifest(self, opt): |
106 | m = self.manifest.manifestProject | 120 | m = self.manifest.manifestProject |
107 | is_new = not m.Exists | 121 | is_new = not m.Exists |
108 | 122 | ||
123 | # If repo has already been initialized, we take -u with the absence of | ||
124 | # --standalone-manifest to mean "transition to a standard repo set up", | ||
125 | # which necessitates starting fresh. | ||
126 | # If --standalone-manifest is set, we always tear everything down and start | ||
127 | # anew. | ||
128 | if not is_new: | ||
129 | was_standalone_manifest = m.config.GetString('manifest.standalone') | ||
130 | if opt.standalone_manifest or ( | ||
131 | was_standalone_manifest and opt.manifest_url): | ||
132 | m.config.ClearCache() | ||
133 | if m.gitdir and os.path.exists(m.gitdir): | ||
134 | platform_utils.rmtree(m.gitdir) | ||
135 | if m.worktree and os.path.exists(m.worktree): | ||
136 | platform_utils.rmtree(m.worktree) | ||
137 | |||
138 | is_new = not m.Exists | ||
109 | if is_new: | 139 | if is_new: |
110 | if not opt.manifest_url: | 140 | if not opt.manifest_url: |
111 | print('fatal: manifest url is required.', file=sys.stderr) | 141 | print('fatal: manifest url is required.', file=sys.stderr) |
@@ -130,6 +160,19 @@ to update the working directory files. | |||
130 | 160 | ||
131 | m._InitGitDir(mirror_git=mirrored_manifest_git) | 161 | m._InitGitDir(mirror_git=mirrored_manifest_git) |
132 | 162 | ||
163 | # If standalone_manifest is set, mark the project as "standalone" -- we'll | ||
164 | # still do much of the manifests.git set up, but will avoid actual syncs to | ||
165 | # a remote. | ||
166 | standalone_manifest = False | ||
167 | if opt.standalone_manifest: | ||
168 | standalone_manifest = True | ||
169 | elif not opt.manifest_url: | ||
170 | # If -u is set and --standalone-manifest is not, then we're not in | ||
171 | # standalone mode. Otherwise, use config to infer what we were in the last | ||
172 | # init. | ||
173 | standalone_manifest = bool(m.config.GetString('manifest.standalone')) | ||
174 | m.config.SetString('manifest.standalone', opt.manifest_url) | ||
175 | |||
133 | self._ConfigureDepth(opt) | 176 | self._ConfigureDepth(opt) |
134 | 177 | ||
135 | # Set the remote URL before the remote branch as we might need it below. | 178 | # Set the remote URL before the remote branch as we might need it below. |
@@ -139,22 +182,23 @@ to update the working directory files. | |||
139 | r.ResetFetch() | 182 | r.ResetFetch() |
140 | r.Save() | 183 | r.Save() |
141 | 184 | ||
142 | if opt.manifest_branch: | 185 | if not standalone_manifest: |
143 | if opt.manifest_branch == 'HEAD': | 186 | if opt.manifest_branch: |
144 | opt.manifest_branch = m.ResolveRemoteHead() | 187 | if opt.manifest_branch == 'HEAD': |
145 | if opt.manifest_branch is None: | 188 | opt.manifest_branch = m.ResolveRemoteHead() |
146 | print('fatal: unable to resolve HEAD', file=sys.stderr) | 189 | if opt.manifest_branch is None: |
147 | sys.exit(1) | 190 | print('fatal: unable to resolve HEAD', file=sys.stderr) |
148 | m.revisionExpr = opt.manifest_branch | 191 | sys.exit(1) |
149 | else: | 192 | m.revisionExpr = opt.manifest_branch |
150 | if is_new: | ||
151 | default_branch = m.ResolveRemoteHead() | ||
152 | if default_branch is None: | ||
153 | # If the remote doesn't have HEAD configured, default to master. | ||
154 | default_branch = 'refs/heads/master' | ||
155 | m.revisionExpr = default_branch | ||
156 | else: | 193 | else: |
157 | m.PreSync() | 194 | if is_new: |
195 | default_branch = m.ResolveRemoteHead() | ||
196 | if default_branch is None: | ||
197 | # If the remote doesn't have HEAD configured, default to master. | ||
198 | default_branch = 'refs/heads/master' | ||
199 | m.revisionExpr = default_branch | ||
200 | else: | ||
201 | m.PreSync() | ||
158 | 202 | ||
159 | groups = re.split(r'[,\s]+', opt.groups) | 203 | groups = re.split(r'[,\s]+', opt.groups) |
160 | all_platforms = ['linux', 'darwin', 'windows'] | 204 | all_platforms = ['linux', 'darwin', 'windows'] |
@@ -244,6 +288,16 @@ to update the working directory files. | |||
244 | if opt.use_superproject is not None: | 288 | if opt.use_superproject is not None: |
245 | m.config.SetBoolean('repo.superproject', opt.use_superproject) | 289 | m.config.SetBoolean('repo.superproject', opt.use_superproject) |
246 | 290 | ||
291 | if standalone_manifest: | ||
292 | if is_new: | ||
293 | manifest_name = 'default.xml' | ||
294 | manifest_data = fetch.fetch_file(opt.manifest_url) | ||
295 | dest = os.path.join(m.worktree, manifest_name) | ||
296 | os.makedirs(os.path.dirname(dest), exist_ok=True) | ||
297 | with open(dest, 'wb') as f: | ||
298 | f.write(manifest_data) | ||
299 | return | ||
300 | |||
247 | if not m.Sync_NetworkHalf(is_new=is_new, quiet=opt.quiet, verbose=opt.verbose, | 301 | if not m.Sync_NetworkHalf(is_new=is_new, quiet=opt.quiet, verbose=opt.verbose, |
248 | clone_bundle=opt.clone_bundle, | 302 | clone_bundle=opt.clone_bundle, |
249 | current_branch_only=opt.current_branch_only, | 303 | current_branch_only=opt.current_branch_only, |
@@ -420,6 +474,11 @@ to update the working directory files. | |||
420 | if opt.archive and opt.mirror: | 474 | if opt.archive and opt.mirror: |
421 | self.OptionParser.error('--mirror and --archive cannot be used together.') | 475 | self.OptionParser.error('--mirror and --archive cannot be used together.') |
422 | 476 | ||
477 | if opt.standalone_manifest and ( | ||
478 | opt.manifest_branch or opt.manifest_name != 'default.xml'): | ||
479 | self.OptionParser.error('--manifest-branch and --manifest-name cannot' | ||
480 | ' be used with --standalone-manifest.') | ||
481 | |||
423 | if args: | 482 | if args: |
424 | if opt.manifest_url: | 483 | if opt.manifest_url: |
425 | self.OptionParser.error( | 484 | self.OptionParser.error( |
diff --git a/subcmds/list.py b/subcmds/list.py index 5cbc0c22..6adf85b7 100644 --- a/subcmds/list.py +++ b/subcmds/list.py | |||
@@ -12,11 +12,13 @@ | |||
12 | # See the License for the specific language governing permissions and | 12 | # See the License for the specific language governing permissions and |
13 | # limitations under the License. | 13 | # limitations under the License. |
14 | 14 | ||
15 | import os | ||
16 | |||
15 | from command import Command, MirrorSafeCommand | 17 | from command import Command, MirrorSafeCommand |
16 | 18 | ||
17 | 19 | ||
18 | class List(Command, MirrorSafeCommand): | 20 | class List(Command, MirrorSafeCommand): |
19 | common = True | 21 | COMMON = True |
20 | helpSummary = "List projects and their associated directories" | 22 | helpSummary = "List projects and their associated directories" |
21 | helpUsage = """ | 23 | helpUsage = """ |
22 | %prog [-f] [<project>...] | 24 | %prog [-f] [<project>...] |
@@ -36,27 +38,33 @@ This is similar to running: repo forall -c 'echo "$REPO_PATH : $REPO_PROJECT"'. | |||
36 | def _Options(self, p): | 38 | def _Options(self, p): |
37 | p.add_option('-r', '--regex', | 39 | p.add_option('-r', '--regex', |
38 | dest='regex', action='store_true', | 40 | dest='regex', action='store_true', |
39 | help="Filter the project list based on regex or wildcard matching of strings") | 41 | help='filter the project list based on regex or wildcard matching of strings') |
40 | p.add_option('-g', '--groups', | 42 | p.add_option('-g', '--groups', |
41 | dest='groups', | 43 | dest='groups', |
42 | help="Filter the project list based on the groups the project is in") | 44 | help='filter the project list based on the groups the project is in') |
43 | p.add_option('-a', '--all', | 45 | p.add_option('-a', '--all', |
44 | action='store_true', | 46 | action='store_true', |
45 | help='Show projects regardless of checkout state') | 47 | help='show projects regardless of checkout state') |
46 | p.add_option('-f', '--fullpath', | ||
47 | dest='fullpath', action='store_true', | ||
48 | help="Display the full work tree path instead of the relative path") | ||
49 | p.add_option('-n', '--name-only', | 48 | p.add_option('-n', '--name-only', |
50 | dest='name_only', action='store_true', | 49 | dest='name_only', action='store_true', |
51 | help="Display only the name of the repository") | 50 | help='display only the name of the repository') |
52 | p.add_option('-p', '--path-only', | 51 | p.add_option('-p', '--path-only', |
53 | dest='path_only', action='store_true', | 52 | dest='path_only', action='store_true', |
54 | help="Display only the path of the repository") | 53 | help='display only the path of the repository') |
54 | p.add_option('-f', '--fullpath', | ||
55 | dest='fullpath', action='store_true', | ||
56 | help='display the full work tree path instead of the relative path') | ||
57 | p.add_option('--relative-to', metavar='PATH', | ||
58 | help='display paths relative to this one (default: top of repo client checkout)') | ||
55 | 59 | ||
56 | def ValidateOptions(self, opt, args): | 60 | def ValidateOptions(self, opt, args): |
57 | if opt.fullpath and opt.name_only: | 61 | if opt.fullpath and opt.name_only: |
58 | self.OptionParser.error('cannot combine -f and -n') | 62 | self.OptionParser.error('cannot combine -f and -n') |
59 | 63 | ||
64 | # Resolve any symlinks so the output is stable. | ||
65 | if opt.relative_to: | ||
66 | opt.relative_to = os.path.realpath(opt.relative_to) | ||
67 | |||
60 | def Execute(self, opt, args): | 68 | def Execute(self, opt, args): |
61 | """List all projects and the associated directories. | 69 | """List all projects and the associated directories. |
62 | 70 | ||
@@ -76,6 +84,8 @@ This is similar to running: repo forall -c 'echo "$REPO_PATH : $REPO_PROJECT"'. | |||
76 | def _getpath(x): | 84 | def _getpath(x): |
77 | if opt.fullpath: | 85 | if opt.fullpath: |
78 | return x.worktree | 86 | return x.worktree |
87 | if opt.relative_to: | ||
88 | return os.path.relpath(x.worktree, opt.relative_to) | ||
79 | return x.relpath | 89 | return x.relpath |
80 | 90 | ||
81 | lines = [] | 91 | lines = [] |
diff --git a/subcmds/manifest.py b/subcmds/manifest.py index e33e683c..0fbdeac0 100644 --- a/subcmds/manifest.py +++ b/subcmds/manifest.py | |||
@@ -20,7 +20,7 @@ from command import PagedCommand | |||
20 | 20 | ||
21 | 21 | ||
22 | class Manifest(PagedCommand): | 22 | class Manifest(PagedCommand): |
23 | common = False | 23 | COMMON = False |
24 | helpSummary = "Manifest inspection utility" | 24 | helpSummary = "Manifest inspection utility" |
25 | helpUsage = """ | 25 | helpUsage = """ |
26 | %prog [-o {-|NAME.xml}] [-m MANIFEST.xml] [-r] | 26 | %prog [-o {-|NAME.xml}] [-m MANIFEST.xml] [-r] |
@@ -53,27 +53,29 @@ to indicate the remote ref to push changes to via 'repo upload'. | |||
53 | def _Options(self, p): | 53 | def _Options(self, p): |
54 | p.add_option('-r', '--revision-as-HEAD', | 54 | p.add_option('-r', '--revision-as-HEAD', |
55 | dest='peg_rev', action='store_true', | 55 | dest='peg_rev', action='store_true', |
56 | help='Save revisions as current HEAD') | 56 | help='save revisions as current HEAD') |
57 | p.add_option('-m', '--manifest-name', | 57 | p.add_option('-m', '--manifest-name', |
58 | help='temporary manifest to use for this sync', metavar='NAME.xml') | 58 | help='temporary manifest to use for this sync', metavar='NAME.xml') |
59 | p.add_option('--suppress-upstream-revision', dest='peg_rev_upstream', | 59 | p.add_option('--suppress-upstream-revision', dest='peg_rev_upstream', |
60 | default=True, action='store_false', | 60 | default=True, action='store_false', |
61 | help='If in -r mode, do not write the upstream field. ' | 61 | help='if in -r mode, do not write the upstream field ' |
62 | 'Only of use if the branch names for a sha1 manifest are ' | 62 | '(only of use if the branch names for a sha1 manifest are ' |
63 | 'sensitive.') | 63 | 'sensitive)') |
64 | p.add_option('--suppress-dest-branch', dest='peg_rev_dest_branch', | 64 | p.add_option('--suppress-dest-branch', dest='peg_rev_dest_branch', |
65 | default=True, action='store_false', | 65 | default=True, action='store_false', |
66 | help='If in -r mode, do not write the dest-branch field. ' | 66 | help='if in -r mode, do not write the dest-branch field ' |
67 | 'Only of use if the branch names for a sha1 manifest are ' | 67 | '(only of use if the branch names for a sha1 manifest are ' |
68 | 'sensitive.') | 68 | 'sensitive)') |
69 | p.add_option('--json', default=False, action='store_true', | 69 | p.add_option('--json', default=False, action='store_true', |
70 | help='Output manifest in JSON format (experimental).') | 70 | help='output manifest in JSON format (experimental)') |
71 | p.add_option('--pretty', default=False, action='store_true', | 71 | p.add_option('--pretty', default=False, action='store_true', |
72 | help='Format output for humans to read.') | 72 | help='format output for humans to read') |
73 | p.add_option('--no-local-manifests', default=False, action='store_true', | ||
74 | dest='ignore_local_manifests', help='ignore local manifests') | ||
73 | p.add_option('-o', '--output-file', | 75 | p.add_option('-o', '--output-file', |
74 | dest='output_file', | 76 | dest='output_file', |
75 | default='-', | 77 | default='-', |
76 | help='File to save the manifest to', | 78 | help='file to save the manifest to', |
77 | metavar='-|NAME.xml') | 79 | metavar='-|NAME.xml') |
78 | 80 | ||
79 | def _Output(self, opt): | 81 | def _Output(self, opt): |
@@ -85,6 +87,9 @@ to indicate the remote ref to push changes to via 'repo upload'. | |||
85 | fd = sys.stdout | 87 | fd = sys.stdout |
86 | else: | 88 | else: |
87 | fd = open(opt.output_file, 'w') | 89 | fd = open(opt.output_file, 'w') |
90 | |||
91 | self.manifest.SetUseLocalManifests(not opt.ignore_local_manifests) | ||
92 | |||
88 | if opt.json: | 93 | if opt.json: |
89 | print('warning: --json is experimental!', file=sys.stderr) | 94 | print('warning: --json is experimental!', file=sys.stderr) |
90 | doc = self.manifest.ToDict(peg_rev=opt.peg_rev, | 95 | doc = self.manifest.ToDict(peg_rev=opt.peg_rev, |
diff --git a/subcmds/overview.py b/subcmds/overview.py index 004a847c..63f5a79e 100644 --- a/subcmds/overview.py +++ b/subcmds/overview.py | |||
@@ -12,12 +12,14 @@ | |||
12 | # See the License for the specific language governing permissions and | 12 | # See the License for the specific language governing permissions and |
13 | # limitations under the License. | 13 | # limitations under the License. |
14 | 14 | ||
15 | import optparse | ||
16 | |||
15 | from color import Coloring | 17 | from color import Coloring |
16 | from command import PagedCommand | 18 | from command import PagedCommand |
17 | 19 | ||
18 | 20 | ||
19 | class Overview(PagedCommand): | 21 | class Overview(PagedCommand): |
20 | common = True | 22 | COMMON = True |
21 | helpSummary = "Display overview of unmerged project branches" | 23 | helpSummary = "Display overview of unmerged project branches" |
22 | helpUsage = """ | 24 | helpUsage = """ |
23 | %prog [--current-branch] [<project>...] | 25 | %prog [--current-branch] [<project>...] |
@@ -26,15 +28,22 @@ class Overview(PagedCommand): | |||
26 | The '%prog' command is used to display an overview of the projects branches, | 28 | The '%prog' command is used to display an overview of the projects branches, |
27 | and list any local commits that have not yet been merged into the project. | 29 | and list any local commits that have not yet been merged into the project. |
28 | 30 | ||
29 | The -b/--current-branch option can be used to restrict the output to only | 31 | The -c/--current-branch option can be used to restrict the output to only |
30 | branches currently checked out in each project. By default, all branches | 32 | branches currently checked out in each project. By default, all branches |
31 | are displayed. | 33 | are displayed. |
32 | """ | 34 | """ |
33 | 35 | ||
34 | def _Options(self, p): | 36 | def _Options(self, p): |
35 | p.add_option('-b', '--current-branch', | 37 | p.add_option('-c', '--current-branch', |
36 | dest="current_branch", action="store_true", | 38 | dest="current_branch", action="store_true", |
37 | help="Consider only checked out branches") | 39 | help="consider only checked out branches") |
40 | p.add_option('--no-current-branch', | ||
41 | dest='current_branch', action='store_false', | ||
42 | help='consider all local branches') | ||
43 | # Turn this into a warning & remove this someday. | ||
44 | p.add_option('-b', | ||
45 | dest='current_branch', action='store_true', | ||
46 | help=optparse.SUPPRESS_HELP) | ||
38 | 47 | ||
39 | def Execute(self, opt, args): | 48 | def Execute(self, opt, args): |
40 | all_branches = [] | 49 | all_branches = [] |
diff --git a/subcmds/prune.py b/subcmds/prune.py index 236b647f..584ee7ed 100644 --- a/subcmds/prune.py +++ b/subcmds/prune.py | |||
@@ -19,7 +19,7 @@ from command import DEFAULT_LOCAL_JOBS, PagedCommand | |||
19 | 19 | ||
20 | 20 | ||
21 | class Prune(PagedCommand): | 21 | class Prune(PagedCommand): |
22 | common = True | 22 | COMMON = True |
23 | helpSummary = "Prune (delete) already merged topics" | 23 | helpSummary = "Prune (delete) already merged topics" |
24 | helpUsage = """ | 24 | helpUsage = """ |
25 | %prog [<project>...] | 25 | %prog [<project>...] |
diff --git a/subcmds/rebase.py b/subcmds/rebase.py index e0186d4d..7c53eb7a 100644 --- a/subcmds/rebase.py +++ b/subcmds/rebase.py | |||
@@ -27,7 +27,7 @@ class RebaseColoring(Coloring): | |||
27 | 27 | ||
28 | 28 | ||
29 | class Rebase(Command): | 29 | class Rebase(Command): |
30 | common = True | 30 | COMMON = True |
31 | helpSummary = "Rebase local branches on upstream branch" | 31 | helpSummary = "Rebase local branches on upstream branch" |
32 | helpUsage = """ | 32 | helpUsage = """ |
33 | %prog {[<project>...] | -i <project>...} | 33 | %prog {[<project>...] | -i <project>...} |
@@ -46,27 +46,27 @@ branch but need to incorporate new upstream changes "underneath" them. | |||
46 | 46 | ||
47 | p.add_option('--fail-fast', | 47 | p.add_option('--fail-fast', |
48 | dest='fail_fast', action='store_true', | 48 | dest='fail_fast', action='store_true', |
49 | help='Stop rebasing after first error is hit') | 49 | help='stop rebasing after first error is hit') |
50 | p.add_option('-f', '--force-rebase', | 50 | p.add_option('-f', '--force-rebase', |
51 | dest='force_rebase', action='store_true', | 51 | dest='force_rebase', action='store_true', |
52 | help='Pass --force-rebase to git rebase') | 52 | help='pass --force-rebase to git rebase') |
53 | p.add_option('--no-ff', | 53 | p.add_option('--no-ff', |
54 | dest='ff', default=True, action='store_false', | 54 | dest='ff', default=True, action='store_false', |
55 | help='Pass --no-ff to git rebase') | 55 | help='pass --no-ff to git rebase') |
56 | p.add_option('--autosquash', | 56 | p.add_option('--autosquash', |
57 | dest='autosquash', action='store_true', | 57 | dest='autosquash', action='store_true', |
58 | help='Pass --autosquash to git rebase') | 58 | help='pass --autosquash to git rebase') |
59 | p.add_option('--whitespace', | 59 | p.add_option('--whitespace', |
60 | dest='whitespace', action='store', metavar='WS', | 60 | dest='whitespace', action='store', metavar='WS', |
61 | help='Pass --whitespace to git rebase') | 61 | help='pass --whitespace to git rebase') |
62 | p.add_option('--auto-stash', | 62 | p.add_option('--auto-stash', |
63 | dest='auto_stash', action='store_true', | 63 | dest='auto_stash', action='store_true', |
64 | help='Stash local modifications before starting') | 64 | help='stash local modifications before starting') |
65 | p.add_option('-m', '--onto-manifest', | 65 | p.add_option('-m', '--onto-manifest', |
66 | dest='onto_manifest', action='store_true', | 66 | dest='onto_manifest', action='store_true', |
67 | help='Rebase onto the manifest version instead of upstream ' | 67 | help='rebase onto the manifest version instead of upstream ' |
68 | 'HEAD. This helps to make sure the local tree stays ' | 68 | 'HEAD (this helps to make sure the local tree stays ' |
69 | 'consistent if you previously synced to a manifest.') | 69 | 'consistent if you previously synced to a manifest)') |
70 | 70 | ||
71 | def Execute(self, opt, args): | 71 | def Execute(self, opt, args): |
72 | all_projects = self.GetProjects(args) | 72 | all_projects = self.GetProjects(args) |
diff --git a/subcmds/selfupdate.py b/subcmds/selfupdate.py index 388881d9..282f518e 100644 --- a/subcmds/selfupdate.py +++ b/subcmds/selfupdate.py | |||
@@ -21,7 +21,7 @@ from subcmds.sync import _PostRepoFetch | |||
21 | 21 | ||
22 | 22 | ||
23 | class Selfupdate(Command, MirrorSafeCommand): | 23 | class Selfupdate(Command, MirrorSafeCommand): |
24 | common = False | 24 | COMMON = False |
25 | helpSummary = "Update repo to the latest version" | 25 | helpSummary = "Update repo to the latest version" |
26 | helpUsage = """ | 26 | helpUsage = """ |
27 | %prog | 27 | %prog |
diff --git a/subcmds/smartsync.py b/subcmds/smartsync.py index c7d1d4d4..d91d59c6 100644 --- a/subcmds/smartsync.py +++ b/subcmds/smartsync.py | |||
@@ -16,7 +16,7 @@ from subcmds.sync import Sync | |||
16 | 16 | ||
17 | 17 | ||
18 | class Smartsync(Sync): | 18 | class Smartsync(Sync): |
19 | common = True | 19 | COMMON = True |
20 | helpSummary = "Update working tree to the latest known good revision" | 20 | helpSummary = "Update working tree to the latest known good revision" |
21 | helpUsage = """ | 21 | helpUsage = """ |
22 | %prog [<project>...] | 22 | %prog [<project>...] |
diff --git a/subcmds/stage.py b/subcmds/stage.py index ff0f1738..0389a4ff 100644 --- a/subcmds/stage.py +++ b/subcmds/stage.py | |||
@@ -28,7 +28,7 @@ class _ProjectList(Coloring): | |||
28 | 28 | ||
29 | 29 | ||
30 | class Stage(InteractiveCommand): | 30 | class Stage(InteractiveCommand): |
31 | common = True | 31 | COMMON = True |
32 | helpSummary = "Stage file(s) for commit" | 32 | helpSummary = "Stage file(s) for commit" |
33 | helpUsage = """ | 33 | helpUsage = """ |
34 | %prog -i [<project>...] | 34 | %prog -i [<project>...] |
diff --git a/subcmds/start.py b/subcmds/start.py index ff2bae56..2addaf2e 100644 --- a/subcmds/start.py +++ b/subcmds/start.py | |||
@@ -25,7 +25,7 @@ from project import SyncBuffer | |||
25 | 25 | ||
26 | 26 | ||
27 | class Start(Command): | 27 | class Start(Command): |
28 | common = True | 28 | COMMON = True |
29 | helpSummary = "Start a new branch for development" | 29 | helpSummary = "Start a new branch for development" |
30 | helpUsage = """ | 30 | helpUsage = """ |
31 | %prog <newbranchname> [--all | <project>...] | 31 | %prog <newbranchname> [--all | <project>...] |
diff --git a/subcmds/status.py b/subcmds/status.py index 1b48dcea..5b669547 100644 --- a/subcmds/status.py +++ b/subcmds/status.py | |||
@@ -24,7 +24,7 @@ import platform_utils | |||
24 | 24 | ||
25 | 25 | ||
26 | class Status(PagedCommand): | 26 | class Status(PagedCommand): |
27 | common = True | 27 | COMMON = True |
28 | helpSummary = "Show the working tree status" | 28 | helpSummary = "Show the working tree status" |
29 | helpUsage = """ | 29 | helpUsage = """ |
30 | %prog [<project>...] | 30 | %prog [<project>...] |
diff --git a/subcmds/sync.py b/subcmds/sync.py index d41052d7..3211cbb1 100644 --- a/subcmds/sync.py +++ b/subcmds/sync.py | |||
@@ -12,6 +12,7 @@ | |||
12 | # See the License for the specific language governing permissions and | 12 | # See the License for the specific language governing permissions and |
13 | # limitations under the License. | 13 | # limitations under the License. |
14 | 14 | ||
15 | import errno | ||
15 | import functools | 16 | import functools |
16 | import http.cookiejar as cookielib | 17 | import http.cookiejar as cookielib |
17 | import io | 18 | import io |
@@ -56,6 +57,7 @@ from error import RepoChangedException, GitError, ManifestParseError | |||
56 | import platform_utils | 57 | import platform_utils |
57 | from project import SyncBuffer | 58 | from project import SyncBuffer |
58 | from progress import Progress | 59 | from progress import Progress |
60 | import ssh | ||
59 | from wrapper import Wrapper | 61 | from wrapper import Wrapper |
60 | from manifest_xml import GitcManifest | 62 | from manifest_xml import GitcManifest |
61 | 63 | ||
@@ -64,7 +66,7 @@ _ONE_DAY_S = 24 * 60 * 60 | |||
64 | 66 | ||
65 | class Sync(Command, MirrorSafeCommand): | 67 | class Sync(Command, MirrorSafeCommand): |
66 | jobs = 1 | 68 | jobs = 1 |
67 | common = True | 69 | COMMON = True |
68 | helpSummary = "Update working tree to the latest revision" | 70 | helpSummary = "Update working tree to the latest revision" |
69 | helpUsage = """ | 71 | helpUsage = """ |
70 | %prog [<project>...] | 72 | %prog [<project>...] |
@@ -168,10 +170,11 @@ later is required to fix a server side protocol bug. | |||
168 | PARALLEL_JOBS = 1 | 170 | PARALLEL_JOBS = 1 |
169 | 171 | ||
170 | def _CommonOptions(self, p): | 172 | def _CommonOptions(self, p): |
171 | try: | 173 | if self.manifest: |
172 | self.PARALLEL_JOBS = self.manifest.default.sync_j | 174 | try: |
173 | except ManifestParseError: | 175 | self.PARALLEL_JOBS = self.manifest.default.sync_j |
174 | pass | 176 | except ManifestParseError: |
177 | pass | ||
175 | super()._CommonOptions(p) | 178 | super()._CommonOptions(p) |
176 | 179 | ||
177 | def _Options(self, p, show_smart=True): | 180 | def _Options(self, p, show_smart=True): |
@@ -212,6 +215,9 @@ later is required to fix a server side protocol bug. | |||
212 | p.add_option('-c', '--current-branch', | 215 | p.add_option('-c', '--current-branch', |
213 | dest='current_branch_only', action='store_true', | 216 | dest='current_branch_only', action='store_true', |
214 | help='fetch only current branch from server') | 217 | help='fetch only current branch from server') |
218 | p.add_option('--no-current-branch', | ||
219 | dest='current_branch_only', action='store_false', | ||
220 | help='fetch all branches from server') | ||
215 | p.add_option('-m', '--manifest-name', | 221 | p.add_option('-m', '--manifest-name', |
216 | dest='manifest_name', | 222 | dest='manifest_name', |
217 | help='temporary manifest to use for this sync', metavar='NAME.xml') | 223 | help='temporary manifest to use for this sync', metavar='NAME.xml') |
@@ -230,8 +236,14 @@ later is required to fix a server side protocol bug. | |||
230 | help='fetch submodules from server') | 236 | help='fetch submodules from server') |
231 | p.add_option('--use-superproject', action='store_true', | 237 | p.add_option('--use-superproject', action='store_true', |
232 | help='use the manifest superproject to sync projects') | 238 | help='use the manifest superproject to sync projects') |
239 | p.add_option('--no-use-superproject', action='store_false', | ||
240 | dest='use_superproject', | ||
241 | help='disable use of manifest superprojects') | ||
242 | p.add_option('--tags', | ||
243 | action='store_false', | ||
244 | help='fetch tags') | ||
233 | p.add_option('--no-tags', | 245 | p.add_option('--no-tags', |
234 | dest='tags', default=True, action='store_false', | 246 | dest='tags', action='store_false', |
235 | help="don't fetch tags") | 247 | help="don't fetch tags") |
236 | p.add_option('--optimized-fetch', | 248 | p.add_option('--optimized-fetch', |
237 | dest='optimized_fetch', action='store_true', | 249 | dest='optimized_fetch', action='store_true', |
@@ -266,17 +278,11 @@ later is required to fix a server side protocol bug. | |||
266 | branch = branch[len(R_HEADS):] | 278 | branch = branch[len(R_HEADS):] |
267 | return branch | 279 | return branch |
268 | 280 | ||
269 | def _UseSuperproject(self, opt): | ||
270 | """Returns True if use-superproject option is enabled""" | ||
271 | return (opt.use_superproject or | ||
272 | self.manifest.manifestProject.config.GetBoolean( | ||
273 | 'repo.superproject')) | ||
274 | |||
275 | def _GetCurrentBranchOnly(self, opt): | 281 | def _GetCurrentBranchOnly(self, opt): |
276 | """Returns True if current-branch or use-superproject options are enabled.""" | 282 | """Returns True if current-branch or use-superproject options are enabled.""" |
277 | return opt.current_branch_only or self._UseSuperproject(opt) | 283 | return opt.current_branch_only or git_superproject.UseSuperproject(opt, self.manifest) |
278 | 284 | ||
279 | def _UpdateProjectsRevisionId(self, opt, args): | 285 | def _UpdateProjectsRevisionId(self, opt, args, load_local_manifests, superproject_logging_data): |
280 | """Update revisionId of every project with the SHA from superproject. | 286 | """Update revisionId of every project with the SHA from superproject. |
281 | 287 | ||
282 | This function updates each project's revisionId with SHA from superproject. | 288 | This function updates each project's revisionId with SHA from superproject. |
@@ -286,22 +292,40 @@ later is required to fix a server side protocol bug. | |||
286 | opt: Program options returned from optparse. See _Options(). | 292 | opt: Program options returned from optparse. See _Options(). |
287 | args: Arguments to pass to GetProjects. See the GetProjects | 293 | args: Arguments to pass to GetProjects. See the GetProjects |
288 | docstring for details. | 294 | docstring for details. |
295 | load_local_manifests: Whether to load local manifests. | ||
296 | superproject_logging_data: A dictionary of superproject data that is to be logged. | ||
289 | 297 | ||
290 | Returns: | 298 | Returns: |
291 | Returns path to the overriding manifest file. | 299 | Returns path to the overriding manifest file instead of None. |
292 | """ | 300 | """ |
301 | print_messages = git_superproject.PrintMessages(opt, self.manifest) | ||
293 | superproject = git_superproject.Superproject(self.manifest, | 302 | superproject = git_superproject.Superproject(self.manifest, |
294 | self.repodir, | 303 | self.repodir, |
295 | quiet=opt.quiet) | 304 | self.git_event_log, |
305 | quiet=opt.quiet, | ||
306 | print_messages=print_messages) | ||
307 | if opt.local_only: | ||
308 | manifest_path = superproject.manifest_path | ||
309 | if manifest_path: | ||
310 | self._ReloadManifest(manifest_path, load_local_manifests) | ||
311 | return manifest_path | ||
312 | |||
296 | all_projects = self.GetProjects(args, | 313 | all_projects = self.GetProjects(args, |
297 | missing_ok=True, | 314 | missing_ok=True, |
298 | submodules_ok=opt.fetch_submodules) | 315 | submodules_ok=opt.fetch_submodules) |
299 | manifest_path = superproject.UpdateProjectsRevisionId(all_projects) | 316 | update_result = superproject.UpdateProjectsRevisionId(all_projects) |
300 | if not manifest_path: | 317 | manifest_path = update_result.manifest_path |
301 | print('error: Update of revsionId from superproject has failed', | 318 | superproject_logging_data['updatedrevisionid'] = bool(manifest_path) |
302 | file=sys.stderr) | 319 | if manifest_path: |
303 | sys.exit(1) | 320 | self._ReloadManifest(manifest_path, load_local_manifests) |
304 | self._ReloadManifest(manifest_path) | 321 | else: |
322 | if print_messages: | ||
323 | print('warning: Update of revisionId from superproject has failed, ' | ||
324 | 'repo sync will not use superproject to fetch the source. ', | ||
325 | 'Please resync with the --no-use-superproject option to avoid this repo warning.', | ||
326 | file=sys.stderr) | ||
327 | if update_result.fatal and opt.use_superproject is not None: | ||
328 | sys.exit(1) | ||
305 | return manifest_path | 329 | return manifest_path |
306 | 330 | ||
307 | def _FetchProjectList(self, opt, projects): | 331 | def _FetchProjectList(self, opt, projects): |
@@ -343,11 +367,12 @@ later is required to fix a server side protocol bug. | |||
343 | optimized_fetch=opt.optimized_fetch, | 367 | optimized_fetch=opt.optimized_fetch, |
344 | retry_fetches=opt.retry_fetches, | 368 | retry_fetches=opt.retry_fetches, |
345 | prune=opt.prune, | 369 | prune=opt.prune, |
370 | ssh_proxy=self.ssh_proxy, | ||
346 | clone_filter=self.manifest.CloneFilter, | 371 | clone_filter=self.manifest.CloneFilter, |
347 | partial_clone_exclude=self.manifest.PartialCloneExclude) | 372 | partial_clone_exclude=self.manifest.PartialCloneExclude) |
348 | 373 | ||
349 | output = buf.getvalue() | 374 | output = buf.getvalue() |
350 | if opt.verbose and output: | 375 | if (opt.verbose or not success) and output: |
351 | print('\n' + output.rstrip()) | 376 | print('\n' + output.rstrip()) |
352 | 377 | ||
353 | if not success: | 378 | if not success: |
@@ -364,7 +389,11 @@ later is required to fix a server side protocol bug. | |||
364 | finish = time.time() | 389 | finish = time.time() |
365 | return (success, project, start, finish) | 390 | return (success, project, start, finish) |
366 | 391 | ||
367 | def _Fetch(self, projects, opt, err_event): | 392 | @classmethod |
393 | def _FetchInitChild(cls, ssh_proxy): | ||
394 | cls.ssh_proxy = ssh_proxy | ||
395 | |||
396 | def _Fetch(self, projects, opt, err_event, ssh_proxy): | ||
368 | ret = True | 397 | ret = True |
369 | 398 | ||
370 | jobs = opt.jobs_network if opt.jobs_network else self.jobs | 399 | jobs = opt.jobs_network if opt.jobs_network else self.jobs |
@@ -394,8 +423,14 @@ later is required to fix a server side protocol bug. | |||
394 | break | 423 | break |
395 | return ret | 424 | return ret |
396 | 425 | ||
426 | # We pass the ssh proxy settings via the class. This allows multiprocessing | ||
427 | # to pickle it up when spawning children. We can't pass it as an argument | ||
428 | # to _FetchProjectList below as multiprocessing is unable to pickle those. | ||
429 | Sync.ssh_proxy = None | ||
430 | |||
397 | # NB: Multiprocessing is heavy, so don't spin it up for one job. | 431 | # NB: Multiprocessing is heavy, so don't spin it up for one job. |
398 | if len(projects_list) == 1 or jobs == 1: | 432 | if len(projects_list) == 1 or jobs == 1: |
433 | self._FetchInitChild(ssh_proxy) | ||
399 | if not _ProcessResults(self._FetchProjectList(opt, x) for x in projects_list): | 434 | if not _ProcessResults(self._FetchProjectList(opt, x) for x in projects_list): |
400 | ret = False | 435 | ret = False |
401 | else: | 436 | else: |
@@ -413,7 +448,8 @@ later is required to fix a server side protocol bug. | |||
413 | else: | 448 | else: |
414 | pm.update(inc=0, msg='warming up') | 449 | pm.update(inc=0, msg='warming up') |
415 | chunksize = 4 | 450 | chunksize = 4 |
416 | with multiprocessing.Pool(jobs) as pool: | 451 | with multiprocessing.Pool( |
452 | jobs, initializer=self._FetchInitChild, initargs=(ssh_proxy,)) as pool: | ||
417 | results = pool.imap_unordered( | 453 | results = pool.imap_unordered( |
418 | functools.partial(self._FetchProjectList, opt), | 454 | functools.partial(self._FetchProjectList, opt), |
419 | projects_list, | 455 | projects_list, |
@@ -422,6 +458,11 @@ later is required to fix a server side protocol bug. | |||
422 | ret = False | 458 | ret = False |
423 | pool.close() | 459 | pool.close() |
424 | 460 | ||
461 | # Cleanup the reference now that we're done with it, and we're going to | ||
462 | # release any resources it points to. If we don't, later multiprocessing | ||
463 | # usage (e.g. checkouts) will try to pickle and then crash. | ||
464 | del Sync.ssh_proxy | ||
465 | |||
425 | pm.end() | 466 | pm.end() |
426 | self._fetch_times.Save() | 467 | self._fetch_times.Save() |
427 | 468 | ||
@@ -430,6 +471,69 @@ later is required to fix a server side protocol bug. | |||
430 | 471 | ||
431 | return (ret, fetched) | 472 | return (ret, fetched) |
432 | 473 | ||
474 | def _FetchMain(self, opt, args, all_projects, err_event, manifest_name, | ||
475 | load_local_manifests, ssh_proxy): | ||
476 | """The main network fetch loop. | ||
477 | |||
478 | Args: | ||
479 | opt: Program options returned from optparse. See _Options(). | ||
480 | args: Command line args used to filter out projects. | ||
481 | all_projects: List of all projects that should be fetched. | ||
482 | err_event: Whether an error was hit while processing. | ||
483 | manifest_name: Manifest file to be reloaded. | ||
484 | load_local_manifests: Whether to load local manifests. | ||
485 | ssh_proxy: SSH manager for clients & masters. | ||
486 | |||
487 | Returns: | ||
488 | List of all projects that should be checked out. | ||
489 | """ | ||
490 | rp = self.manifest.repoProject | ||
491 | |||
492 | to_fetch = [] | ||
493 | now = time.time() | ||
494 | if _ONE_DAY_S <= (now - rp.LastFetch): | ||
495 | to_fetch.append(rp) | ||
496 | to_fetch.extend(all_projects) | ||
497 | to_fetch.sort(key=self._fetch_times.Get, reverse=True) | ||
498 | |||
499 | success, fetched = self._Fetch(to_fetch, opt, err_event, ssh_proxy) | ||
500 | if not success: | ||
501 | err_event.set() | ||
502 | |||
503 | _PostRepoFetch(rp, opt.repo_verify) | ||
504 | if opt.network_only: | ||
505 | # bail out now; the rest touches the working tree | ||
506 | if err_event.is_set(): | ||
507 | print('\nerror: Exited sync due to fetch errors.\n', file=sys.stderr) | ||
508 | sys.exit(1) | ||
509 | return | ||
510 | |||
511 | # Iteratively fetch missing and/or nested unregistered submodules | ||
512 | previously_missing_set = set() | ||
513 | while True: | ||
514 | self._ReloadManifest(manifest_name, load_local_manifests) | ||
515 | all_projects = self.GetProjects(args, | ||
516 | missing_ok=True, | ||
517 | submodules_ok=opt.fetch_submodules) | ||
518 | missing = [] | ||
519 | for project in all_projects: | ||
520 | if project.gitdir not in fetched: | ||
521 | missing.append(project) | ||
522 | if not missing: | ||
523 | break | ||
524 | # Stop us from non-stopped fetching actually-missing repos: If set of | ||
525 | # missing repos has not been changed from last fetch, we break. | ||
526 | missing_set = set(p.name for p in missing) | ||
527 | if previously_missing_set == missing_set: | ||
528 | break | ||
529 | previously_missing_set = missing_set | ||
530 | success, new_fetched = self._Fetch(missing, opt, err_event, ssh_proxy) | ||
531 | if not success: | ||
532 | err_event.set() | ||
533 | fetched.update(new_fetched) | ||
534 | |||
535 | return all_projects | ||
536 | |||
433 | def _CheckoutOne(self, detach_head, force_sync, project): | 537 | def _CheckoutOne(self, detach_head, force_sync, project): |
434 | """Checkout work tree for one project | 538 | """Checkout work tree for one project |
435 | 539 | ||
@@ -564,10 +668,18 @@ later is required to fix a server side protocol bug. | |||
564 | t.join() | 668 | t.join() |
565 | pm.end() | 669 | pm.end() |
566 | 670 | ||
567 | def _ReloadManifest(self, manifest_name=None): | 671 | def _ReloadManifest(self, manifest_name=None, load_local_manifests=True): |
672 | """Reload the manfiest from the file specified by the |manifest_name|. | ||
673 | |||
674 | It unloads the manifest if |manifest_name| is None. | ||
675 | |||
676 | Args: | ||
677 | manifest_name: Manifest file to be reloaded. | ||
678 | load_local_manifests: Whether to load local manifests. | ||
679 | """ | ||
568 | if manifest_name: | 680 | if manifest_name: |
569 | # Override calls _Unload already | 681 | # Override calls _Unload already |
570 | self.manifest.Override(manifest_name) | 682 | self.manifest.Override(manifest_name, load_local_manifests=load_local_manifests) |
571 | else: | 683 | else: |
572 | self.manifest._Unload() | 684 | self.manifest._Unload() |
573 | 685 | ||
@@ -614,6 +726,56 @@ later is required to fix a server side protocol bug. | |||
614 | fd.write('\n') | 726 | fd.write('\n') |
615 | return 0 | 727 | return 0 |
616 | 728 | ||
729 | def UpdateCopyLinkfileList(self): | ||
730 | """Save all dests of copyfile and linkfile, and update them if needed. | ||
731 | |||
732 | Returns: | ||
733 | Whether update was successful. | ||
734 | """ | ||
735 | new_paths = {} | ||
736 | new_linkfile_paths = [] | ||
737 | new_copyfile_paths = [] | ||
738 | for project in self.GetProjects(None, missing_ok=True): | ||
739 | new_linkfile_paths.extend(x.dest for x in project.linkfiles) | ||
740 | new_copyfile_paths.extend(x.dest for x in project.copyfiles) | ||
741 | |||
742 | new_paths = { | ||
743 | 'linkfile': new_linkfile_paths, | ||
744 | 'copyfile': new_copyfile_paths, | ||
745 | } | ||
746 | |||
747 | copylinkfile_name = 'copy-link-files.json' | ||
748 | copylinkfile_path = os.path.join(self.manifest.repodir, copylinkfile_name) | ||
749 | old_copylinkfile_paths = {} | ||
750 | |||
751 | if os.path.exists(copylinkfile_path): | ||
752 | with open(copylinkfile_path, 'rb') as fp: | ||
753 | try: | ||
754 | old_copylinkfile_paths = json.load(fp) | ||
755 | except: | ||
756 | print('error: %s is not a json formatted file.' % | ||
757 | copylinkfile_path, file=sys.stderr) | ||
758 | platform_utils.remove(copylinkfile_path) | ||
759 | return False | ||
760 | |||
761 | need_remove_files = [] | ||
762 | need_remove_files.extend( | ||
763 | set(old_copylinkfile_paths.get('linkfile', [])) - | ||
764 | set(new_linkfile_paths)) | ||
765 | need_remove_files.extend( | ||
766 | set(old_copylinkfile_paths.get('copyfile', [])) - | ||
767 | set(new_copyfile_paths)) | ||
768 | |||
769 | for need_remove_file in need_remove_files: | ||
770 | # Try to remove the updated copyfile or linkfile. | ||
771 | # So, if the file is not exist, nothing need to do. | ||
772 | platform_utils.remove(need_remove_file, missing_ok=True) | ||
773 | |||
774 | # Create copy-link-files.json, save dest path of "copyfile" and "linkfile". | ||
775 | with open(copylinkfile_path, 'w', encoding='utf-8') as fp: | ||
776 | json.dump(new_paths, fp) | ||
777 | return True | ||
778 | |||
617 | def _SmartSyncSetup(self, opt, smart_sync_manifest_path): | 779 | def _SmartSyncSetup(self, opt, smart_sync_manifest_path): |
618 | if not self.manifest.manifest_server: | 780 | if not self.manifest.manifest_server: |
619 | print('error: cannot smart sync: no manifest server defined in ' | 781 | print('error: cannot smart sync: no manifest server defined in ' |
@@ -730,7 +892,7 @@ later is required to fix a server side protocol bug. | |||
730 | start, time.time(), clean) | 892 | start, time.time(), clean) |
731 | if not clean: | 893 | if not clean: |
732 | sys.exit(1) | 894 | sys.exit(1) |
733 | self._ReloadManifest(opt.manifest_name) | 895 | self._ReloadManifest(manifest_name) |
734 | if opt.jobs is None: | 896 | if opt.jobs is None: |
735 | self.jobs = self.manifest.default.sync_j | 897 | self.jobs = self.manifest.default.sync_j |
736 | 898 | ||
@@ -779,7 +941,7 @@ later is required to fix a server side protocol bug. | |||
779 | print('error: failed to remove existing smart sync override manifest: %s' % | 941 | print('error: failed to remove existing smart sync override manifest: %s' % |
780 | e, file=sys.stderr) | 942 | e, file=sys.stderr) |
781 | 943 | ||
782 | err_event = _threading.Event() | 944 | err_event = multiprocessing.Event() |
783 | 945 | ||
784 | rp = self.manifest.repoProject | 946 | rp = self.manifest.repoProject |
785 | rp.PreSync() | 947 | rp.PreSync() |
@@ -802,8 +964,16 @@ later is required to fix a server side protocol bug. | |||
802 | else: | 964 | else: |
803 | self._UpdateManifestProject(opt, mp, manifest_name) | 965 | self._UpdateManifestProject(opt, mp, manifest_name) |
804 | 966 | ||
805 | if self._UseSuperproject(opt): | 967 | load_local_manifests = not self.manifest.HasLocalManifests |
806 | manifest_name = self._UpdateProjectsRevisionId(opt, args) | 968 | use_superproject = git_superproject.UseSuperproject(opt, self.manifest) |
969 | superproject_logging_data = { | ||
970 | 'superproject': use_superproject, | ||
971 | 'haslocalmanifests': bool(self.manifest.HasLocalManifests), | ||
972 | 'hassuperprojecttag': bool(self.manifest.superproject), | ||
973 | } | ||
974 | if use_superproject: | ||
975 | manifest_name = self._UpdateProjectsRevisionId( | ||
976 | opt, args, load_local_manifests, superproject_logging_data) or opt.manifest_name | ||
807 | 977 | ||
808 | if self.gitc_manifest: | 978 | if self.gitc_manifest: |
809 | gitc_manifest_projects = self.GetProjects(args, | 979 | gitc_manifest_projects = self.GetProjects(args, |
@@ -849,49 +1019,17 @@ later is required to fix a server side protocol bug. | |||
849 | 1019 | ||
850 | self._fetch_times = _FetchTimes(self.manifest) | 1020 | self._fetch_times = _FetchTimes(self.manifest) |
851 | if not opt.local_only: | 1021 | if not opt.local_only: |
852 | to_fetch = [] | 1022 | with multiprocessing.Manager() as manager: |
853 | now = time.time() | 1023 | with ssh.ProxyManager(manager) as ssh_proxy: |
854 | if _ONE_DAY_S <= (now - rp.LastFetch): | 1024 | # Initialize the socket dir once in the parent. |
855 | to_fetch.append(rp) | 1025 | ssh_proxy.sock() |
856 | to_fetch.extend(all_projects) | 1026 | all_projects = self._FetchMain(opt, args, all_projects, err_event, |
857 | to_fetch.sort(key=self._fetch_times.Get, reverse=True) | 1027 | manifest_name, load_local_manifests, |
858 | 1028 | ssh_proxy) | |
859 | success, fetched = self._Fetch(to_fetch, opt, err_event) | ||
860 | if not success: | ||
861 | err_event.set() | ||
862 | 1029 | ||
863 | _PostRepoFetch(rp, opt.repo_verify) | ||
864 | if opt.network_only: | 1030 | if opt.network_only: |
865 | # bail out now; the rest touches the working tree | ||
866 | if err_event.is_set(): | ||
867 | print('\nerror: Exited sync due to fetch errors.\n', file=sys.stderr) | ||
868 | sys.exit(1) | ||
869 | return | 1031 | return |
870 | 1032 | ||
871 | # Iteratively fetch missing and/or nested unregistered submodules | ||
872 | previously_missing_set = set() | ||
873 | while True: | ||
874 | self._ReloadManifest(manifest_name) | ||
875 | all_projects = self.GetProjects(args, | ||
876 | missing_ok=True, | ||
877 | submodules_ok=opt.fetch_submodules) | ||
878 | missing = [] | ||
879 | for project in all_projects: | ||
880 | if project.gitdir not in fetched: | ||
881 | missing.append(project) | ||
882 | if not missing: | ||
883 | break | ||
884 | # Stop us from non-stopped fetching actually-missing repos: If set of | ||
885 | # missing repos has not been changed from last fetch, we break. | ||
886 | missing_set = set(p.name for p in missing) | ||
887 | if previously_missing_set == missing_set: | ||
888 | break | ||
889 | previously_missing_set = missing_set | ||
890 | success, new_fetched = self._Fetch(missing, opt, err_event) | ||
891 | if not success: | ||
892 | err_event.set() | ||
893 | fetched.update(new_fetched) | ||
894 | |||
895 | # If we saw an error, exit with code 1 so that other scripts can check. | 1033 | # If we saw an error, exit with code 1 so that other scripts can check. |
896 | if err_event.is_set(): | 1034 | if err_event.is_set(): |
897 | err_network_sync = True | 1035 | err_network_sync = True |
@@ -914,6 +1052,13 @@ later is required to fix a server side protocol bug. | |||
914 | print('\nerror: Local checkouts *not* updated.', file=sys.stderr) | 1052 | print('\nerror: Local checkouts *not* updated.', file=sys.stderr) |
915 | sys.exit(1) | 1053 | sys.exit(1) |
916 | 1054 | ||
1055 | err_update_linkfiles = not self.UpdateCopyLinkfileList() | ||
1056 | if err_update_linkfiles: | ||
1057 | err_event.set() | ||
1058 | if opt.fail_fast: | ||
1059 | print('\nerror: Local update copyfile or linkfile failed.', file=sys.stderr) | ||
1060 | sys.exit(1) | ||
1061 | |||
917 | err_results = [] | 1062 | err_results = [] |
918 | # NB: We don't exit here because this is the last step. | 1063 | # NB: We don't exit here because this is the last step. |
919 | err_checkout = not self._Checkout(all_projects, opt, err_results) | 1064 | err_checkout = not self._Checkout(all_projects, opt, err_results) |
@@ -932,6 +1077,8 @@ later is required to fix a server side protocol bug. | |||
932 | print('error: Downloading network changes failed.', file=sys.stderr) | 1077 | print('error: Downloading network changes failed.', file=sys.stderr) |
933 | if err_update_projects: | 1078 | if err_update_projects: |
934 | print('error: Updating local project lists failed.', file=sys.stderr) | 1079 | print('error: Updating local project lists failed.', file=sys.stderr) |
1080 | if err_update_linkfiles: | ||
1081 | print('error: Updating copyfiles or linkfiles failed.', file=sys.stderr) | ||
935 | if err_checkout: | 1082 | if err_checkout: |
936 | print('error: Checking out local projects failed.', file=sys.stderr) | 1083 | print('error: Checking out local projects failed.', file=sys.stderr) |
937 | if err_results: | 1084 | if err_results: |
@@ -940,6 +1087,15 @@ later is required to fix a server side protocol bug. | |||
940 | file=sys.stderr) | 1087 | file=sys.stderr) |
941 | sys.exit(1) | 1088 | sys.exit(1) |
942 | 1089 | ||
1090 | # Log the previous sync analysis state from the config. | ||
1091 | self.git_event_log.LogDataConfigEvents(mp.config.GetSyncAnalysisStateData(), | ||
1092 | 'previous_sync_state') | ||
1093 | |||
1094 | # Update and log with the new sync analysis state. | ||
1095 | mp.config.UpdateSyncAnalysisState(opt, superproject_logging_data) | ||
1096 | self.git_event_log.LogDataConfigEvents(mp.config.GetSyncAnalysisStateData(), | ||
1097 | 'current_sync_state') | ||
1098 | |||
943 | if not opt.quiet: | 1099 | if not opt.quiet: |
944 | print('repo sync has finished successfully.') | 1100 | print('repo sync has finished successfully.') |
945 | 1101 | ||
@@ -1011,10 +1167,7 @@ class _FetchTimes(object): | |||
1011 | with open(self._path) as f: | 1167 | with open(self._path) as f: |
1012 | self._times = json.load(f) | 1168 | self._times = json.load(f) |
1013 | except (IOError, ValueError): | 1169 | except (IOError, ValueError): |
1014 | try: | 1170 | platform_utils.remove(self._path, missing_ok=True) |
1015 | platform_utils.remove(self._path) | ||
1016 | except OSError: | ||
1017 | pass | ||
1018 | self._times = {} | 1171 | self._times = {} |
1019 | 1172 | ||
1020 | def Save(self): | 1173 | def Save(self): |
@@ -1032,10 +1185,7 @@ class _FetchTimes(object): | |||
1032 | with open(self._path, 'w') as f: | 1185 | with open(self._path, 'w') as f: |
1033 | json.dump(self._times, f, indent=2) | 1186 | json.dump(self._times, f, indent=2) |
1034 | except (IOError, TypeError): | 1187 | except (IOError, TypeError): |
1035 | try: | 1188 | platform_utils.remove(self._path, missing_ok=True) |
1036 | platform_utils.remove(self._path) | ||
1037 | except OSError: | ||
1038 | pass | ||
1039 | 1189 | ||
1040 | # This is a replacement for xmlrpc.client.Transport using urllib2 | 1190 | # This is a replacement for xmlrpc.client.Transport using urllib2 |
1041 | # and supporting persistent-http[s]. It cannot change hosts from | 1191 | # and supporting persistent-http[s]. It cannot change hosts from |
diff --git a/subcmds/upload.py b/subcmds/upload.py index 50dccc52..c48deab6 100644 --- a/subcmds/upload.py +++ b/subcmds/upload.py | |||
@@ -13,10 +13,12 @@ | |||
13 | # limitations under the License. | 13 | # limitations under the License. |
14 | 14 | ||
15 | import copy | 15 | import copy |
16 | import functools | ||
17 | import optparse | ||
16 | import re | 18 | import re |
17 | import sys | 19 | import sys |
18 | 20 | ||
19 | from command import InteractiveCommand | 21 | from command import DEFAULT_LOCAL_JOBS, InteractiveCommand |
20 | from editor import Editor | 22 | from editor import Editor |
21 | from error import UploadError | 23 | from error import UploadError |
22 | from git_command import GitCommand | 24 | from git_command import GitCommand |
@@ -53,7 +55,7 @@ def _SplitEmails(values): | |||
53 | 55 | ||
54 | 56 | ||
55 | class Upload(InteractiveCommand): | 57 | class Upload(InteractiveCommand): |
56 | common = True | 58 | COMMON = True |
57 | helpSummary = "Upload changes for code review" | 59 | helpSummary = "Upload changes for code review" |
58 | helpUsage = """ | 60 | helpUsage = """ |
59 | %prog [--re --cc] [<project>]... | 61 | %prog [--re --cc] [<project>]... |
@@ -145,58 +147,66 @@ https://gerrit-review.googlesource.com/Documentation/user-upload.html#notify | |||
145 | Gerrit Code Review: https://www.gerritcodereview.com/ | 147 | Gerrit Code Review: https://www.gerritcodereview.com/ |
146 | 148 | ||
147 | """ | 149 | """ |
150 | PARALLEL_JOBS = DEFAULT_LOCAL_JOBS | ||
148 | 151 | ||
149 | def _Options(self, p): | 152 | def _Options(self, p): |
150 | p.add_option('-t', | 153 | p.add_option('-t', |
151 | dest='auto_topic', action='store_true', | 154 | dest='auto_topic', action='store_true', |
152 | help='Send local branch name to Gerrit Code Review') | 155 | help='send local branch name to Gerrit Code Review') |
153 | p.add_option('--hashtag', '--ht', | 156 | p.add_option('--hashtag', '--ht', |
154 | dest='hashtags', action='append', default=[], | 157 | dest='hashtags', action='append', default=[], |
155 | help='Add hashtags (comma delimited) to the review.') | 158 | help='add hashtags (comma delimited) to the review') |
156 | p.add_option('--hashtag-branch', '--htb', | 159 | p.add_option('--hashtag-branch', '--htb', |
157 | action='store_true', | 160 | action='store_true', |
158 | help='Add local branch name as a hashtag.') | 161 | help='add local branch name as a hashtag') |
159 | p.add_option('-l', '--label', | 162 | p.add_option('-l', '--label', |
160 | dest='labels', action='append', default=[], | 163 | dest='labels', action='append', default=[], |
161 | help='Add a label when uploading.') | 164 | help='add a label when uploading') |
162 | p.add_option('--re', '--reviewers', | 165 | p.add_option('--re', '--reviewers', |
163 | type='string', action='append', dest='reviewers', | 166 | type='string', action='append', dest='reviewers', |
164 | help='Request reviews from these people.') | 167 | help='request reviews from these people') |
165 | p.add_option('--cc', | 168 | p.add_option('--cc', |
166 | type='string', action='append', dest='cc', | 169 | type='string', action='append', dest='cc', |
167 | help='Also send email to these email addresses.') | 170 | help='also send email to these email addresses') |
168 | p.add_option('--br', | 171 | p.add_option('--br', '--branch', |
169 | type='string', action='store', dest='branch', | 172 | type='string', action='store', dest='branch', |
170 | help='Branch to upload.') | 173 | help='(local) branch to upload') |
171 | p.add_option('--cbr', '--current-branch', | 174 | p.add_option('-c', '--current-branch', |
172 | dest='current_branch', action='store_true', | 175 | dest='current_branch', action='store_true', |
173 | help='Upload current git branch.') | 176 | help='upload current git branch') |
177 | p.add_option('--no-current-branch', | ||
178 | dest='current_branch', action='store_false', | ||
179 | help='upload all git branches') | ||
180 | # Turn this into a warning & remove this someday. | ||
181 | p.add_option('--cbr', | ||
182 | dest='current_branch', action='store_true', | ||
183 | help=optparse.SUPPRESS_HELP) | ||
174 | p.add_option('--ne', '--no-emails', | 184 | p.add_option('--ne', '--no-emails', |
175 | action='store_false', dest='notify', default=True, | 185 | action='store_false', dest='notify', default=True, |
176 | help='If specified, do not send emails on upload.') | 186 | help='do not send e-mails on upload') |
177 | p.add_option('-p', '--private', | 187 | p.add_option('-p', '--private', |
178 | action='store_true', dest='private', default=False, | 188 | action='store_true', dest='private', default=False, |
179 | help='If specified, upload as a private change.') | 189 | help='upload as a private change (deprecated; use --wip)') |
180 | p.add_option('-w', '--wip', | 190 | p.add_option('-w', '--wip', |
181 | action='store_true', dest='wip', default=False, | 191 | action='store_true', dest='wip', default=False, |
182 | help='If specified, upload as a work-in-progress change.') | 192 | help='upload as a work-in-progress change') |
183 | p.add_option('-o', '--push-option', | 193 | p.add_option('-o', '--push-option', |
184 | type='string', action='append', dest='push_options', | 194 | type='string', action='append', dest='push_options', |
185 | default=[], | 195 | default=[], |
186 | help='Additional push options to transmit') | 196 | help='additional push options to transmit') |
187 | p.add_option('-D', '--destination', '--dest', | 197 | p.add_option('-D', '--destination', '--dest', |
188 | type='string', action='store', dest='dest_branch', | 198 | type='string', action='store', dest='dest_branch', |
189 | metavar='BRANCH', | 199 | metavar='BRANCH', |
190 | help='Submit for review on this target branch.') | 200 | help='submit for review on this target branch') |
191 | p.add_option('-n', '--dry-run', | 201 | p.add_option('-n', '--dry-run', |
192 | dest='dryrun', default=False, action='store_true', | 202 | dest='dryrun', default=False, action='store_true', |
193 | help='Do everything except actually upload the CL.') | 203 | help='do everything except actually upload the CL') |
194 | p.add_option('-y', '--yes', | 204 | p.add_option('-y', '--yes', |
195 | default=False, action='store_true', | 205 | default=False, action='store_true', |
196 | help='Answer yes to all safe prompts.') | 206 | help='answer yes to all safe prompts') |
197 | p.add_option('--no-cert-checks', | 207 | p.add_option('--no-cert-checks', |
198 | dest='validate_certs', action='store_false', default=True, | 208 | dest='validate_certs', action='store_false', default=True, |
199 | help='Disable verifying ssl certs (unsafe).') | 209 | help='disable verifying ssl certs (unsafe)') |
200 | RepoHook.AddOptionGroup(p, 'pre-upload') | 210 | RepoHook.AddOptionGroup(p, 'pre-upload') |
201 | 211 | ||
202 | def _SingleBranch(self, opt, branch, people): | 212 | def _SingleBranch(self, opt, branch, people): |
@@ -502,40 +512,46 @@ Gerrit Code Review: https://www.gerritcodereview.com/ | |||
502 | merge_branch = p.stdout.strip() | 512 | merge_branch = p.stdout.strip() |
503 | return merge_branch | 513 | return merge_branch |
504 | 514 | ||
515 | @staticmethod | ||
516 | def _GatherOne(opt, project): | ||
517 | """Figure out the upload status for |project|.""" | ||
518 | if opt.current_branch: | ||
519 | cbr = project.CurrentBranch | ||
520 | up_branch = project.GetUploadableBranch(cbr) | ||
521 | avail = [up_branch] if up_branch else None | ||
522 | else: | ||
523 | avail = project.GetUploadableBranches(opt.branch) | ||
524 | return (project, avail) | ||
525 | |||
505 | def Execute(self, opt, args): | 526 | def Execute(self, opt, args): |
506 | project_list = self.GetProjects(args) | 527 | projects = self.GetProjects(args) |
507 | pending = [] | 528 | |
508 | reviewers = [] | 529 | def _ProcessResults(_pool, _out, results): |
509 | cc = [] | 530 | pending = [] |
510 | branch = None | 531 | for result in results: |
511 | 532 | project, avail = result | |
512 | if opt.branch: | 533 | if avail is None: |
513 | branch = opt.branch | 534 | print('repo: error: %s: Unable to upload branch "%s". ' |
514 | |||
515 | for project in project_list: | ||
516 | if opt.current_branch: | ||
517 | cbr = project.CurrentBranch | ||
518 | up_branch = project.GetUploadableBranch(cbr) | ||
519 | if up_branch: | ||
520 | avail = [up_branch] | ||
521 | else: | ||
522 | avail = None | ||
523 | print('repo: error: Unable to upload branch "%s". ' | ||
524 | 'You might be able to fix the branch by running:\n' | 535 | 'You might be able to fix the branch by running:\n' |
525 | ' git branch --set-upstream-to m/%s' % | 536 | ' git branch --set-upstream-to m/%s' % |
526 | (str(cbr), self.manifest.branch), | 537 | (project.relpath, project.CurrentBranch, self.manifest.branch), |
527 | file=sys.stderr) | 538 | file=sys.stderr) |
528 | else: | 539 | elif avail: |
529 | avail = project.GetUploadableBranches(branch) | 540 | pending.append(result) |
530 | if avail: | 541 | return pending |
531 | pending.append((project, avail)) | 542 | |
543 | pending = self.ExecuteInParallel( | ||
544 | opt.jobs, | ||
545 | functools.partial(self._GatherOne, opt), | ||
546 | projects, | ||
547 | callback=_ProcessResults) | ||
532 | 548 | ||
533 | if not pending: | 549 | if not pending: |
534 | if branch is None: | 550 | if opt.branch is None: |
535 | print('repo: error: no branches ready for upload', file=sys.stderr) | 551 | print('repo: error: no branches ready for upload', file=sys.stderr) |
536 | else: | 552 | else: |
537 | print('repo: error: no branches named "%s" ready for upload' % | 553 | print('repo: error: no branches named "%s" ready for upload' % |
538 | (branch,), file=sys.stderr) | 554 | (opt.branch,), file=sys.stderr) |
539 | return 1 | 555 | return 1 |
540 | 556 | ||
541 | pending_proj_names = [project.name for (project, available) in pending] | 557 | pending_proj_names = [project.name for (project, available) in pending] |
@@ -548,10 +564,8 @@ Gerrit Code Review: https://www.gerritcodereview.com/ | |||
548 | worktree_list=pending_worktrees): | 564 | worktree_list=pending_worktrees): |
549 | return 1 | 565 | return 1 |
550 | 566 | ||
551 | if opt.reviewers: | 567 | reviewers = _SplitEmails(opt.reviewers) if opt.reviewers else [] |
552 | reviewers = _SplitEmails(opt.reviewers) | 568 | cc = _SplitEmails(opt.cc) if opt.cc else [] |
553 | if opt.cc: | ||
554 | cc = _SplitEmails(opt.cc) | ||
555 | people = (reviewers, cc) | 569 | people = (reviewers, cc) |
556 | 570 | ||
557 | if len(pending) == 1 and len(pending[0][1]) == 1: | 571 | if len(pending) == 1 and len(pending[0][1]) == 1: |
diff --git a/subcmds/version.py b/subcmds/version.py index e95a86dc..09b053ea 100644 --- a/subcmds/version.py +++ b/subcmds/version.py | |||
@@ -18,13 +18,14 @@ import sys | |||
18 | from command import Command, MirrorSafeCommand | 18 | from command import Command, MirrorSafeCommand |
19 | from git_command import git, RepoSourceVersion, user_agent | 19 | from git_command import git, RepoSourceVersion, user_agent |
20 | from git_refs import HEAD | 20 | from git_refs import HEAD |
21 | from wrapper import Wrapper | ||
21 | 22 | ||
22 | 23 | ||
23 | class Version(Command, MirrorSafeCommand): | 24 | class Version(Command, MirrorSafeCommand): |
24 | wrapper_version = None | 25 | wrapper_version = None |
25 | wrapper_path = None | 26 | wrapper_path = None |
26 | 27 | ||
27 | common = False | 28 | COMMON = False |
28 | helpSummary = "Display the version of repo" | 29 | helpSummary = "Display the version of repo" |
29 | helpUsage = """ | 30 | helpUsage = """ |
30 | %prog | 31 | %prog |
@@ -62,3 +63,4 @@ class Version(Command, MirrorSafeCommand): | |||
62 | print('OS %s %s (%s)' % (uname.system, uname.release, uname.version)) | 63 | print('OS %s %s (%s)' % (uname.system, uname.release, uname.version)) |
63 | print('CPU %s (%s)' % | 64 | print('CPU %s (%s)' % |
64 | (uname.machine, uname.processor if uname.processor else 'unknown')) | 65 | (uname.machine, uname.processor if uname.processor else 'unknown')) |
66 | print('Bug reports:', Wrapper().BUG_URL) | ||