summaryrefslogtreecommitdiffstats
path: root/subcmds
diff options
context:
space:
mode:
Diffstat (limited to 'subcmds')
-rw-r--r--subcmds/abandon.py2
-rw-r--r--subcmds/branches.py2
-rw-r--r--subcmds/checkout.py2
-rw-r--r--subcmds/cherry_pick.py2
-rw-r--r--subcmds/diff.py4
-rw-r--r--subcmds/diffmanifests.py6
-rw-r--r--subcmds/download.py2
-rw-r--r--subcmds/forall.py18
-rw-r--r--subcmds/gitc_delete.py4
-rw-r--r--subcmds/gitc_init.py2
-rw-r--r--subcmds/grep.py2
-rw-r--r--subcmds/help.py19
-rw-r--r--subcmds/info.py17
-rw-r--r--subcmds/init.py99
-rw-r--r--subcmds/list.py28
-rw-r--r--subcmds/manifest.py27
-rw-r--r--subcmds/overview.py17
-rw-r--r--subcmds/prune.py2
-rw-r--r--subcmds/rebase.py20
-rw-r--r--subcmds/selfupdate.py2
-rw-r--r--subcmds/smartsync.py2
-rw-r--r--subcmds/stage.py2
-rw-r--r--subcmds/start.py2
-rw-r--r--subcmds/status.py2
-rw-r--r--subcmds/sync.py306
-rw-r--r--subcmds/upload.py112
-rw-r--r--subcmds/version.py4
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
25class Abandon(Command): 25class 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
64class Branches(Command): 64class 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
22class Checkout(Command): 22class 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
23class CherryPick(Command): 23class 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
21class Diff(PagedCommand): 21class 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
24class Download(Command): 24class 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
43class Forall(Command, MirrorSafeCommand): 43class 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
21class GitcDelete(Command, GitcClientCommand): 21class 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
25class GitcInit(init.Init, GitcAvailableCommand): 25class 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
31class Grep(PagedCommand): 31class 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
20from color import Coloring 20from color import Coloring
21from command import PagedCommand, MirrorSafeCommand, GitcAvailableCommand, GitcClientCommand 21from command import PagedCommand, MirrorSafeCommand, GitcAvailableCommand, GitcClientCommand
22import gitc_utils 22import gitc_utils
23from wrapper import Wrapper
23 24
24 25
25class Help(PagedCommand, MirrorSafeCommand): 26class 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
15import optparse
16
15from command import PagedCommand 17from command import PagedCommand
16from color import Coloring 18from color import Coloring
17from git_refs import R_M, R_HEADS 19from git_refs import R_M, R_HEADS
@@ -23,9 +25,9 @@ class _Coloring(Coloring):
23 25
24 26
25class Info(PagedCommand): 27class 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
15import optparse
16import os 15import os
17import platform 16import platform
18import re 17import re
18import subprocess
19import sys 19import sys
20import urllib.parse 20import urllib.parse
21 21
@@ -25,13 +25,14 @@ from error import ManifestParseError
25from project import SyncBuffer 25from project import SyncBuffer
26from git_config import GitConfig 26from git_config import GitConfig
27from git_command import git_require, MIN_GIT_VERSION_SOFT, MIN_GIT_VERSION_HARD 27from git_command import git_require, MIN_GIT_VERSION_SOFT, MIN_GIT_VERSION_HARD
28import fetch
28import git_superproject 29import git_superproject
29import platform_utils 30import platform_utils
30from wrapper import Wrapper 31from wrapper import Wrapper
31 32
32 33
33class Init(InteractiveCommand, MirrorSafeCommand): 34class 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
54to be used. If no manifest is specified, the manifest default.xml 55to be used. If no manifest is specified, the manifest default.xml
55will be used. 56will be used.
56 57
58If the --standalone-manifest argument is set, the manifest will be downloaded
59directly from the specified --manifest-url as a static file (rather than
60setting up a manifest git checkout). With --standalone-manifest, the manifest
61will be fully static and will not be re-downloaded during subsesquent
62`repo init` and `repo sync` calls.
63
57The --reference option can be used to point to a directory that 64The --reference option can be used to point to a directory that
58has the content of a --mirror sync. This will make the working 65has the content of a --mirror sync. This will make the working
59directory use as much data as possible from the local reference 66directory 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
15import os
16
15from command import Command, MirrorSafeCommand 17from command import Command, MirrorSafeCommand
16 18
17 19
18class List(Command, MirrorSafeCommand): 20class 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
22class Manifest(PagedCommand): 22class 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
15import optparse
16
15from color import Coloring 17from color import Coloring
16from command import PagedCommand 18from command import PagedCommand
17 19
18 20
19class Overview(PagedCommand): 21class 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):
26The '%prog' command is used to display an overview of the projects branches, 28The '%prog' command is used to display an overview of the projects branches,
27and list any local commits that have not yet been merged into the project. 29and list any local commits that have not yet been merged into the project.
28 30
29The -b/--current-branch option can be used to restrict the output to only 31The -c/--current-branch option can be used to restrict the output to only
30branches currently checked out in each project. By default, all branches 32branches currently checked out in each project. By default, all branches
31are displayed. 33are 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
21class Prune(PagedCommand): 21class 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
29class Rebase(Command): 29class 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
23class Selfupdate(Command, MirrorSafeCommand): 23class 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
18class Smartsync(Sync): 18class 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
30class Stage(InteractiveCommand): 30class 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
27class Start(Command): 27class 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
26class Status(PagedCommand): 26class 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
15import errno
15import functools 16import functools
16import http.cookiejar as cookielib 17import http.cookiejar as cookielib
17import io 18import io
@@ -56,6 +57,7 @@ from error import RepoChangedException, GitError, ManifestParseError
56import platform_utils 57import platform_utils
57from project import SyncBuffer 58from project import SyncBuffer
58from progress import Progress 59from progress import Progress
60import ssh
59from wrapper import Wrapper 61from wrapper import Wrapper
60from manifest_xml import GitcManifest 62from manifest_xml import GitcManifest
61 63
@@ -64,7 +66,7 @@ _ONE_DAY_S = 24 * 60 * 60
64 66
65class Sync(Command, MirrorSafeCommand): 67class 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
15import copy 15import copy
16import functools
17import optparse
16import re 18import re
17import sys 19import sys
18 20
19from command import InteractiveCommand 21from command import DEFAULT_LOCAL_JOBS, InteractiveCommand
20from editor import Editor 22from editor import Editor
21from error import UploadError 23from error import UploadError
22from git_command import GitCommand 24from git_command import GitCommand
@@ -53,7 +55,7 @@ def _SplitEmails(values):
53 55
54 56
55class Upload(InteractiveCommand): 57class 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
145Gerrit Code Review: https://www.gerritcodereview.com/ 147Gerrit 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
18from command import Command, MirrorSafeCommand 18from command import Command, MirrorSafeCommand
19from git_command import git, RepoSourceVersion, user_agent 19from git_command import git, RepoSourceVersion, user_agent
20from git_refs import HEAD 20from git_refs import HEAD
21from wrapper import Wrapper
21 22
22 23
23class Version(Command, MirrorSafeCommand): 24class 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)