blob: 57434b18335b45e88b33ddc6180d4045adc84e77 [file] [log] [blame]
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001# Copyright (C) 2008 The Android Open Source Project
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
Ben Komalo08a3f682010-07-15 16:03:02 -070015import copy
Mike Frysingerd1f3e142021-05-01 12:02:01 -040016import functools
Mike Frysingerb8e09ea2021-05-03 00:51:52 -040017import optparse
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070018import re
19import sys
20
Mike Frysingerd1f3e142021-05-01 12:02:01 -040021from command import DEFAULT_LOCAL_JOBS, InteractiveCommand
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070022from editor import Editor
Remy Bohmer7f7acfe2020-08-01 18:36:44 +020023from error import UploadError
Conley Owens3bfd7212013-09-30 15:54:38 -070024from git_command import GitCommand
Sean McAllister682f0b62020-05-18 09:15:51 -060025from git_refs import R_HEADS
Remy Bohmer16c13282020-09-10 10:38:04 +020026from hooks import RepoHook
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070027
Chirayu Desai217ea7d2013-03-01 19:14:38 +053028
Dan Morrillf0a9a1a2010-05-05 08:18:35 -070029UNUSUAL_COMMIT_THRESHOLD = 5
Dan Morrill879a9a52010-05-04 16:56:07 -070030
David Pursehouse819827a2020-02-12 15:20:19 +090031
Dan Morrill879a9a52010-05-04 16:56:07 -070032def _ConfirmManyUploads(multiple_branches=False):
33 if multiple_branches:
David Pursehouse2f9e7e42013-03-05 17:26:46 +090034 print('ATTENTION: One or more branches has an unusually high number '
Sarah Owenscecd1d82012-11-01 22:59:27 -070035 'of commits.')
Dan Morrill879a9a52010-05-04 16:56:07 -070036 else:
Sarah Owenscecd1d82012-11-01 22:59:27 -070037 print('ATTENTION: You are uploading an unusually high number of commits.')
David Pursehouse2f9e7e42013-03-05 17:26:46 +090038 print('YOU PROBABLY DO NOT MEAN TO DO THIS. (Did you rebase across '
Sarah Owenscecd1d82012-11-01 22:59:27 -070039 'branches?)')
Chirayu Desai217ea7d2013-03-01 19:14:38 +053040 answer = input("If you are sure you intend to do this, type 'yes': ").strip()
Dan Morrill879a9a52010-05-04 16:56:07 -070041 return answer == "yes"
42
David Pursehouse819827a2020-02-12 15:20:19 +090043
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070044def _die(fmt, *args):
45 msg = fmt % args
Sarah Owenscecd1d82012-11-01 22:59:27 -070046 print('error: %s' % msg, file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070047 sys.exit(1)
48
David Pursehouse819827a2020-02-12 15:20:19 +090049
Joe Onorato2896a792008-11-17 16:56:36 -050050def _SplitEmails(values):
51 result = []
David Pursehouse8a68ff92012-09-24 12:15:13 +090052 for value in values:
53 result.extend([s.strip() for s in value.split(',')])
Joe Onorato2896a792008-11-17 16:56:36 -050054 return result
55
David Pursehouse819827a2020-02-12 15:20:19 +090056
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070057class Upload(InteractiveCommand):
58 common = True
59 helpSummary = "Upload changes for code review"
David Pursehouse8f62fb72012-11-14 12:09:38 +090060 helpUsage = """
Ficus Kirkpatricka0de6e82010-10-22 13:06:47 -070061%prog [--re --cc] [<project>]...
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070062"""
63 helpDescription = """
Shawn O. Pearce337fb9c2009-04-18 10:59:33 -070064The '%prog' command is used to send changes to the Gerrit Code
65Review system. It searches for topic branches in local projects
66that have not yet been published for review. If multiple topic
67branches are found, '%prog' opens an editor to allow the user to
68select which branches to upload.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070069
Shawn O. Pearce337fb9c2009-04-18 10:59:33 -070070'%prog' searches for uploadable changes in all projects listed at
71the command line. Projects can be specified either by name, or by
72a relative or absolute path to the project's local directory. If no
73projects are specified, '%prog' will search for uploadable changes
74in all projects listed in the manifest.
Joe Onorato2896a792008-11-17 16:56:36 -050075
76If the --reviewers or --cc options are passed, those emails are
77added to the respective list of users, and emails are sent to any
Shawn O. Pearce337fb9c2009-04-18 10:59:33 -070078new users. Users passed as --reviewers must already be registered
Joe Onorato2896a792008-11-17 16:56:36 -050079with the code review system, or the upload will fail.
Shawn O. Pearcea6df7d22008-12-12 08:04:07 -080080
Mike Frysingerb8f7bb02018-10-10 01:05:11 -040081# Configuration
Shawn O. Pearcea608fb02009-04-17 12:11:24 -070082
83review.URL.autoupload:
84
Mike Frysingere9311272011-08-11 15:46:43 -040085To disable the "Upload ... (y/N)?" prompt, you can set a per-project
Shawn O. Pearcea608fb02009-04-17 12:11:24 -070086or global Git configuration option. If review.URL.autoupload is set
87to "true" then repo will assume you always answer "y" at the prompt,
88and will not prompt you further. If it is set to "false" then repo
89will assume you always answer "n", and will abort.
90
bijia093fdb62013-11-28 09:19:22 +080091review.URL.autoreviewer:
92
93To automatically append a user or mailing list to reviews, you can set
94a per-project or global Git option to do so.
95
Ben Komalo08a3f682010-07-15 16:03:02 -070096review.URL.autocopy:
97
98To automatically copy a user or mailing list to all uploaded reviews,
99you can set a per-project or global Git option to do so. Specifically,
100review.URL.autocopy can be set to a comma separated list of reviewers
101who you always want copied on all uploads with a non-empty --re
102argument.
103
Shawn O. Pearce3575b8f2010-07-15 17:00:14 -0700104review.URL.username:
105
106Override the username used to connect to Gerrit Code Review.
107By default the local part of the email address is used.
108
Shawn O. Pearcea608fb02009-04-17 12:11:24 -0700109The URL must match the review URL listed in the manifest XML file,
110or in the .git/config within the project. For example:
111
112 [remote "origin"]
113 url = git://git.example.com/project.git
114 review = http://review.example.com/
115
116 [review "http://review.example.com/"]
117 autoupload = true
Ben Komalo08a3f682010-07-15 16:03:02 -0700118 autocopy = johndoe@company.com,my-team-alias@company.com
Shawn O. Pearcea608fb02009-04-17 12:11:24 -0700119
Anthony Russellod666e932012-06-01 00:48:22 -0400120review.URL.uploadtopic:
121
122To add a topic branch whenever uploading a commit, you can set a
123per-project or global Git option to do so. If review.URL.uploadtopic
124is set to "true" then repo will assume you always want the equivalent
125of the -t option to the repo command. If unset or set to "false" then
126repo will make use of only the command line option.
127
Mike Frysinger84685ba2020-02-19 02:22:22 -0500128review.URL.uploadhashtags:
129
130To add hashtags whenever uploading a commit, you can set a per-project
131or global Git option to do so. The value of review.URL.uploadhashtags
Mike Frysingerfc1b18a2020-02-24 15:38:07 -0500132will be used as comma delimited hashtags like the --hashtag option.
133
134review.URL.uploadlabels:
135
136To add labels whenever uploading a commit, you can set a per-project
137or global Git option to do so. The value of review.URL.uploadlabels
138will be used as comma delimited labels like the --label option.
Mike Frysinger84685ba2020-02-19 02:22:22 -0500139
Mike Frysingerf725e542020-03-14 17:39:03 -0400140review.URL.uploadnotify:
141
142Control e-mail notifications when uploading.
143https://gerrit-review.googlesource.com/Documentation/user-upload.html#notify
144
Mike Frysingerb8f7bb02018-10-10 01:05:11 -0400145# References
Shawn O. Pearce337fb9c2009-04-18 10:59:33 -0700146
Mike Frysinger3b24e7b2018-10-10 00:57:44 -0400147Gerrit Code Review: https://www.gerritcodereview.com/
Shawn O. Pearce337fb9c2009-04-18 10:59:33 -0700148
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700149"""
Mike Frysingerd1f3e142021-05-01 12:02:01 -0400150 PARALLEL_JOBS = DEFAULT_LOCAL_JOBS
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700151
Shawn O. Pearcec99883f2008-11-11 17:12:43 -0800152 def _Options(self, p):
Shawn O. Pearcea5ece0e2010-07-15 16:52:42 -0700153 p.add_option('-t',
154 dest='auto_topic', action='store_true',
155 help='Send local branch name to Gerrit Code Review')
Mike Frysinger84685ba2020-02-19 02:22:22 -0500156 p.add_option('--hashtag', '--ht',
157 dest='hashtags', action='append', default=[],
158 help='Add hashtags (comma delimited) to the review.')
Mike Frysinger7ff80af2020-02-19 03:00:26 -0500159 p.add_option('--hashtag-branch', '--htb',
160 action='store_true',
161 help='Add local branch name as a hashtag.')
Mike Frysingerfc1b18a2020-02-24 15:38:07 -0500162 p.add_option('-l', '--label',
163 dest='labels', action='append', default=[],
164 help='Add a label when uploading.')
Joe Onorato2896a792008-11-17 16:56:36 -0500165 p.add_option('--re', '--reviewers',
David Pursehouse08671042020-02-12 13:52:31 +0900166 type='string', action='append', dest='reviewers',
Joe Onorato2896a792008-11-17 16:56:36 -0500167 help='Request reviews from these people.')
168 p.add_option('--cc',
David Pursehouse08671042020-02-12 13:52:31 +0900169 type='string', action='append', dest='cc',
Joe Onorato2896a792008-11-17 16:56:36 -0500170 help='Also send email to these email addresses.')
Mike Frysingerd1f3e142021-05-01 12:02:01 -0400171 p.add_option('--br', '--branch',
David Pursehouse08671042020-02-12 13:52:31 +0900172 type='string', action='store', dest='branch',
Mike Frysingerd1f3e142021-05-01 12:02:01 -0400173 help='(Local) branch to upload.')
Mike Frysingerb8e09ea2021-05-03 00:51:52 -0400174 p.add_option('-c', '--current-branch',
Daniel Sandlere9d6b612012-04-06 10:39:32 -0400175 dest='current_branch', action='store_true',
176 help='Upload current git branch.')
Mike Frysingerb8e09ea2021-05-03 00:51:52 -0400177 # Turn this into a warning & remove this someday.
178 p.add_option('--cbr',
179 dest='current_branch', action='store_true',
180 help=optparse.SUPPRESS_HELP)
Vadim Bendeburybd8f6582018-10-31 13:48:01 -0700181 p.add_option('--ne', '--no-emails',
182 action='store_false', dest='notify', default=True,
183 help='If specified, do not send emails on upload.')
Changcheng Xiao87984c62017-08-02 16:55:03 +0200184 p.add_option('-p', '--private',
185 action='store_true', dest='private', default=False,
186 help='If specified, upload as a private change.')
187 p.add_option('-w', '--wip',
188 action='store_true', dest='wip', default=False,
189 help='If specified, upload as a work-in-progress change.')
Masaya Suzuki305a2d02017-11-13 10:48:34 -0800190 p.add_option('-o', '--push-option',
191 type='string', action='append', dest='push_options',
192 default=[],
193 help='Additional push options to transmit')
Bryan Jacobsf609f912013-05-06 13:36:24 -0400194 p.add_option('-D', '--destination', '--dest',
195 type='string', action='store', dest='dest_branch',
196 metavar='BRANCH',
197 help='Submit for review on this target branch.')
Mike Frysinger819cc812020-02-19 02:27:22 -0500198 p.add_option('-n', '--dry-run',
199 dest='dryrun', default=False, action='store_true',
200 help='Do everything except actually upload the CL.')
Mike Frysinger02aa8892020-02-19 02:32:52 -0500201 p.add_option('-y', '--yes',
202 default=False, action='store_true',
203 help='Answer yes to all safe prompts.')
Mike Frysinger21c15752020-02-11 05:17:16 -0500204 p.add_option('--no-cert-checks',
205 dest='validate_certs', action='store_false', default=True,
206 help='Disable verifying ssl certs (unsafe).')
Remy Bohmer7f7acfe2020-08-01 18:36:44 +0200207 RepoHook.AddOptionGroup(p, 'pre-upload')
Doug Anderson37282b42011-03-04 11:54:18 -0800208
Shawn O. Pearcea5ece0e2010-07-15 16:52:42 -0700209 def _SingleBranch(self, opt, branch, people):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700210 project = branch.project
211 name = branch.name
Shawn O. Pearcea608fb02009-04-17 12:11:24 -0700212 remote = project.GetBranch(name).remote
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700213
Shawn O. Pearcea608fb02009-04-17 12:11:24 -0700214 key = 'review.%s.autoupload' % remote.review
215 answer = project.config.GetBoolean(key)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700216
Shawn O. Pearcea608fb02009-04-17 12:11:24 -0700217 if answer is False:
218 _die("upload blocked by %s = false" % key)
219
220 if answer is None:
Shawn O. Pearce66bdd462009-04-17 18:47:22 -0700221 date = branch.date
David Pursehouse8a68ff92012-09-24 12:15:13 +0900222 commit_list = branch.commits
Shawn O. Pearce66bdd462009-04-17 18:47:22 -0700223
Chirayu Desai610d3c42013-06-24 14:02:12 +0530224 destination = opt.dest_branch or project.dest_branch or project.revisionExpr
Nicolas Cornub54343d2017-07-10 10:31:24 +0200225 print('Upload project %s/ to remote branch %s%s:' %
Mike Frysingerb0fbc7f2020-02-25 02:58:04 -0500226 (project.relpath, destination, ' (private)' if opt.private else ''))
Sarah Owenscecd1d82012-11-01 22:59:27 -0700227 print(' branch %s (%2d commit%s, %s):' % (
David Pursehouseabdf7502020-02-12 14:58:39 +0900228 name,
229 len(commit_list),
230 len(commit_list) != 1 and 's' or '',
231 date))
David Pursehouse8a68ff92012-09-24 12:15:13 +0900232 for commit in commit_list:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700233 print(' %s' % commit)
Shawn O. Pearcea608fb02009-04-17 12:11:24 -0700234
Mike Frysingerab85fe72019-07-04 17:35:11 -0400235 print('to %s (y/N)? ' % remote.review, end='')
236 # TODO: When we require Python 3, use flush=True w/print above.
237 sys.stdout.flush()
Mike Frysinger02aa8892020-02-19 02:32:52 -0500238 if opt.yes:
239 print('<--yes>')
240 answer = True
241 else:
242 answer = sys.stdin.readline().strip().lower()
243 answer = answer in ('y', 'yes', '1', 'true', 't')
Shawn O. Pearcea608fb02009-04-17 12:11:24 -0700244
245 if answer:
Dan Morrill879a9a52010-05-04 16:56:07 -0700246 if len(branch.commits) > UNUSUAL_COMMIT_THRESHOLD:
247 answer = _ConfirmManyUploads()
248
249 if answer:
Shawn O. Pearcea5ece0e2010-07-15 16:52:42 -0700250 self._UploadAndReport(opt, [branch], people)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700251 else:
252 _die("upload aborted by user")
253
Shawn O. Pearcea5ece0e2010-07-15 16:52:42 -0700254 def _MultipleBranches(self, opt, pending, people):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700255 projects = {}
256 branches = {}
257
258 script = []
259 script.append('# Uncomment the branches to upload:')
260 for project, avail in pending:
261 script.append('#')
262 script.append('# project %s/:' % project.relpath)
263
264 b = {}
265 for branch in avail:
Bryan Jacobs710d4b02013-05-31 15:28:05 -0400266 if branch is None:
267 continue
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700268 name = branch.name
269 date = branch.date
David Pursehouse8a68ff92012-09-24 12:15:13 +0900270 commit_list = branch.commits
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700271
272 if b:
273 script.append('#')
Bryan Jacobs691a7592013-05-31 15:45:28 -0400274 destination = opt.dest_branch or project.dest_branch or project.revisionExpr
Christer Fletcher6a1f7372011-04-28 14:13:14 +0200275 script.append('# branch %s (%2d commit%s, %s) to remote branch %s:' % (
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700276 name,
David Pursehouse8a68ff92012-09-24 12:15:13 +0900277 len(commit_list),
278 len(commit_list) != 1 and 's' or '',
Christer Fletcher6a1f7372011-04-28 14:13:14 +0200279 date,
Bryan Jacobs691a7592013-05-31 15:45:28 -0400280 destination))
David Pursehouse8a68ff92012-09-24 12:15:13 +0900281 for commit in commit_list:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700282 script.append('# %s' % commit)
283 b[name] = branch
284
285 projects[project.relpath] = project
286 branches[project.name] = b
287 script.append('')
288
289 script = Editor.EditString("\n".join(script)).split("\n")
290
291 project_re = re.compile(r'^#?\s*project\s*([^\s]+)/:$')
292 branch_re = re.compile(r'^\s*branch\s*([^\s(]+)\s*\(.*')
293
294 project = None
295 todo = []
296
297 for line in script:
298 m = project_re.match(line)
299 if m:
300 name = m.group(1)
301 project = projects.get(name)
302 if not project:
303 _die('project %s not available for upload', name)
304 continue
305
306 m = branch_re.match(line)
307 if m:
308 name = m.group(1)
309 if not project:
310 _die('project for branch %s not in script', name)
311 branch = branches[project.name].get(name)
312 if not branch:
313 _die('branch %s not in %s', name, project.relpath)
314 todo.append(branch)
315 if not todo:
316 _die("nothing uncommented for upload")
Dan Morrill879a9a52010-05-04 16:56:07 -0700317
318 many_commits = False
319 for branch in todo:
320 if len(branch.commits) > UNUSUAL_COMMIT_THRESHOLD:
321 many_commits = True
322 break
323 if many_commits:
324 if not _ConfirmManyUploads(multiple_branches=True):
325 _die("upload aborted by user")
326
Shawn O. Pearcea5ece0e2010-07-15 16:52:42 -0700327 self._UploadAndReport(opt, todo, people)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700328
bijia093fdb62013-11-28 09:19:22 +0800329 def _AppendAutoList(self, branch, people):
Ben Komalo08a3f682010-07-15 16:03:02 -0700330 """
bijia093fdb62013-11-28 09:19:22 +0800331 Appends the list of reviewers in the git project's config.
Ben Komalo08a3f682010-07-15 16:03:02 -0700332 Appends the list of users in the CC list in the git project's config if a
333 non-empty reviewer list was found.
334 """
Ben Komalo08a3f682010-07-15 16:03:02 -0700335 name = branch.name
336 project = branch.project
bijia093fdb62013-11-28 09:19:22 +0800337
338 key = 'review.%s.autoreviewer' % project.GetBranch(name).remote.review
339 raw_list = project.config.GetString(key)
David Pursehouse8f78a832020-02-12 11:20:36 +0900340 if raw_list is not None:
bijia093fdb62013-11-28 09:19:22 +0800341 people[0].extend([entry.strip() for entry in raw_list.split(',')])
342
Ben Komalo08a3f682010-07-15 16:03:02 -0700343 key = 'review.%s.autocopy' % project.GetBranch(name).remote.review
344 raw_list = project.config.GetString(key)
David Pursehouse8f78a832020-02-12 11:20:36 +0900345 if raw_list is not None and len(people[0]) > 0:
Ben Komalo08a3f682010-07-15 16:03:02 -0700346 people[1].extend([entry.strip() for entry in raw_list.split(',')])
347
Ficus Kirkpatrickbc7ef672009-05-04 12:45:11 -0700348 def _FindGerritChange(self, branch):
349 last_pub = branch.project.WasPublished(branch.name)
350 if last_pub is None:
351 return ""
352
353 refs = branch.GetPublishedRefs()
354 try:
355 # refs/changes/XYZ/N --> XYZ
356 return refs.get(last_pub).split('/')[-2]
David Pursehouse1d947b32012-10-25 12:23:11 +0900357 except (AttributeError, IndexError):
Ficus Kirkpatrickbc7ef672009-05-04 12:45:11 -0700358 return ""
359
Shawn O. Pearcea5ece0e2010-07-15 16:52:42 -0700360 def _UploadAndReport(self, opt, todo, original_people):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700361 have_errors = False
362 for branch in todo:
363 try:
Ben Komalo08a3f682010-07-15 16:03:02 -0700364 people = copy.deepcopy(original_people)
bijia093fdb62013-11-28 09:19:22 +0800365 self._AppendAutoList(branch, people)
Ben Komalo08a3f682010-07-15 16:03:02 -0700366
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500367 # Check if there are local changes that may have been forgotten
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700368 changes = branch.project.UncommitedFiles()
369 if changes:
David Pursehousec1b86a22012-11-14 11:36:51 +0900370 key = 'review.%s.autoupload' % branch.project.remote.review
371 answer = branch.project.config.GetBoolean(key)
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500372
David Pursehousec1b86a22012-11-14 11:36:51 +0900373 # if they want to auto upload, let's not ask because it could be automated
374 if answer is None:
Mike Frysingerab85fe72019-07-04 17:35:11 -0400375 print()
376 print('Uncommitted changes in %s (did you forget to amend?):'
377 % branch.project.name)
378 print('\n'.join(changes))
379 print('Continue uploading? (y/N) ', end='')
380 # TODO: When we require Python 3, use flush=True w/print above.
381 sys.stdout.flush()
Mike Frysinger02aa8892020-02-19 02:32:52 -0500382 if opt.yes:
383 print('<--yes>')
384 a = 'yes'
385 else:
386 a = sys.stdin.readline().strip().lower()
David Pursehousec1b86a22012-11-14 11:36:51 +0900387 if a not in ('y', 'yes', 't', 'true', 'on'):
388 print("skipping upload", file=sys.stderr)
389 branch.uploaded = False
390 branch.error = 'User aborted'
391 continue
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500392
Anthony Russellod666e932012-06-01 00:48:22 -0400393 # Check if topic branches should be sent to the server during upload
394 if opt.auto_topic is not True:
David Pursehousec1b86a22012-11-14 11:36:51 +0900395 key = 'review.%s.uploadtopic' % branch.project.remote.review
396 opt.auto_topic = branch.project.config.GetBoolean(key)
Anthony Russellod666e932012-06-01 00:48:22 -0400397
Mike Frysingerfc1b18a2020-02-24 15:38:07 -0500398 def _ExpandCommaList(value):
399 """Split |value| up into comma delimited entries."""
Mike Frysinger84685ba2020-02-19 02:22:22 -0500400 if not value:
401 return
Mike Frysingerfc1b18a2020-02-24 15:38:07 -0500402 for ret in value.split(','):
403 ret = ret.strip()
404 if ret:
405 yield ret
406
407 # Check if hashtags should be included.
Mike Frysinger84685ba2020-02-19 02:22:22 -0500408 key = 'review.%s.uploadhashtags' % branch.project.remote.review
Mike Frysingerfc1b18a2020-02-24 15:38:07 -0500409 hashtags = set(_ExpandCommaList(branch.project.config.GetString(key)))
Mike Frysinger84685ba2020-02-19 02:22:22 -0500410 for tag in opt.hashtags:
Mike Frysingerfc1b18a2020-02-24 15:38:07 -0500411 hashtags.update(_ExpandCommaList(tag))
Mike Frysinger7ff80af2020-02-19 03:00:26 -0500412 if opt.hashtag_branch:
413 hashtags.add(branch.name)
Mike Frysinger84685ba2020-02-19 02:22:22 -0500414
Mike Frysingerfc1b18a2020-02-24 15:38:07 -0500415 # Check if labels should be included.
416 key = 'review.%s.uploadlabels' % branch.project.remote.review
417 labels = set(_ExpandCommaList(branch.project.config.GetString(key)))
418 for label in opt.labels:
419 labels.update(_ExpandCommaList(label))
420 # Basic sanity check on label syntax.
421 for label in labels:
422 if not re.match(r'^.+[+-][0-9]+$', label):
423 print('repo: error: invalid label syntax "%s": labels use forms '
424 'like CodeReview+1 or Verified-1' % (label,), file=sys.stderr)
425 sys.exit(1)
426
Mike Frysingerf725e542020-03-14 17:39:03 -0400427 # Handle e-mail notifications.
428 if opt.notify is False:
429 notify = 'NONE'
430 else:
431 key = 'review.%s.uploadnotify' % branch.project.remote.review
432 notify = branch.project.config.GetString(key)
433
Colin Cross59b31cb2013-10-08 23:10:52 -0700434 destination = opt.dest_branch or branch.project.dest_branch
Conley Owens3bfd7212013-09-30 15:54:38 -0700435
436 # Make sure our local branch is not setup to track a different remote branch
437 merge_branch = self._GetMergeBranch(branch.project)
Conley Owensfbd3f2a2013-10-15 12:59:00 -0700438 if destination:
Sean McAllister682f0b62020-05-18 09:15:51 -0600439 full_dest = destination
440 if not full_dest.startswith(R_HEADS):
441 full_dest = R_HEADS + full_dest
442
Conley Owensfbd3f2a2013-10-15 12:59:00 -0700443 if not opt.dest_branch and merge_branch and merge_branch != full_dest:
444 print('merge branch %s does not match destination branch %s'
445 % (merge_branch, full_dest))
446 print('skipping upload.')
447 print('Please use `--destination %s` if this is intentional'
448 % destination)
449 branch.uploaded = False
450 continue
Conley Owens3bfd7212013-09-30 15:54:38 -0700451
Changcheng Xiao87984c62017-08-02 16:55:03 +0200452 branch.UploadForReview(people,
Mike Frysinger819cc812020-02-19 02:27:22 -0500453 dryrun=opt.dryrun,
Changcheng Xiao87984c62017-08-02 16:55:03 +0200454 auto_topic=opt.auto_topic,
Mike Frysinger84685ba2020-02-19 02:22:22 -0500455 hashtags=hashtags,
Mike Frysingerfc1b18a2020-02-24 15:38:07 -0500456 labels=labels,
Changcheng Xiao87984c62017-08-02 16:55:03 +0200457 private=opt.private,
Mike Frysingerf725e542020-03-14 17:39:03 -0400458 notify=notify,
Changcheng Xiao87984c62017-08-02 16:55:03 +0200459 wip=opt.wip,
Łukasz Gardońbed59ce2017-08-08 10:18:11 +0200460 dest_branch=destination,
Masaya Suzuki305a2d02017-11-13 10:48:34 -0800461 validate_certs=opt.validate_certs,
462 push_options=opt.push_options)
Łukasz Gardońbed59ce2017-08-08 10:18:11 +0200463
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700464 branch.uploaded = True
Sarah Owensa5be53f2012-09-09 15:37:57 -0700465 except UploadError as e:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700466 branch.error = e
467 branch.uploaded = False
468 have_errors = True
469
Sarah Owenscecd1d82012-11-01 22:59:27 -0700470 print(file=sys.stderr)
471 print('----------------------------------------------------------------------', file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700472
473 if have_errors:
474 for branch in todo:
475 if not branch.uploaded:
Shawn O. Pearcef00e0ce2009-08-22 18:39:49 -0700476 if len(str(branch.error)) <= 30:
477 fmt = ' (%s)'
478 else:
479 fmt = '\n (%s)'
Sarah Owenscecd1d82012-11-01 22:59:27 -0700480 print(('[FAILED] %-15s %-15s' + fmt) % (
David Pursehouseabdf7502020-02-12 14:58:39 +0900481 branch.project.relpath + '/',
482 branch.name,
483 str(branch.error)),
484 file=sys.stderr)
Sarah Owenscecd1d82012-11-01 22:59:27 -0700485 print()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700486
487 for branch in todo:
David Pursehousec1b86a22012-11-14 11:36:51 +0900488 if branch.uploaded:
489 print('[OK ] %-15s %s' % (
David Pursehouseabdf7502020-02-12 14:58:39 +0900490 branch.project.relpath + '/',
491 branch.name),
492 file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700493
494 if have_errors:
495 sys.exit(1)
496
Conley Owens3bfd7212013-09-30 15:54:38 -0700497 def _GetMergeBranch(self, project):
498 p = GitCommand(project,
499 ['rev-parse', '--abbrev-ref', 'HEAD'],
David Pursehousee5913ae2020-02-12 13:56:59 +0900500 capture_stdout=True,
501 capture_stderr=True)
Conley Owens3bfd7212013-09-30 15:54:38 -0700502 p.Wait()
503 local_branch = p.stdout.strip()
504 p = GitCommand(project,
505 ['config', '--get', 'branch.%s.merge' % local_branch],
David Pursehousee5913ae2020-02-12 13:56:59 +0900506 capture_stdout=True,
507 capture_stderr=True)
Conley Owens3bfd7212013-09-30 15:54:38 -0700508 p.Wait()
509 merge_branch = p.stdout.strip()
510 return merge_branch
511
Mike Frysingerd1f3e142021-05-01 12:02:01 -0400512 @staticmethod
513 def _GatherOne(opt, project):
514 """Figure out the upload status for |project|."""
515 if opt.current_branch:
516 cbr = project.CurrentBranch
517 up_branch = project.GetUploadableBranch(cbr)
518 avail = [up_branch] if up_branch else None
519 else:
520 avail = project.GetUploadableBranches(opt.branch)
521 return (project, avail)
522
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700523 def Execute(self, opt, args):
Mike Frysingerd1f3e142021-05-01 12:02:01 -0400524 projects = self.GetProjects(args)
Mandeep Singh Bainesd6c93a22011-05-26 10:34:11 -0700525
Mike Frysingerd1f3e142021-05-01 12:02:01 -0400526 def _ProcessResults(_pool, _out, results):
527 pending = []
528 for result in results:
529 project, avail = result
530 if avail is None:
Mike Frysinger3b038ce2021-05-01 00:45:19 -0400531 print('repo: error: %s: Unable to upload branch "%s". '
Mike Frysinger160748f2020-11-10 01:09:51 -0500532 'You might be able to fix the branch by running:\n'
533 ' git branch --set-upstream-to m/%s' %
Mike Frysingerd1f3e142021-05-01 12:02:01 -0400534 (project.relpath, project.CurrentBranch, self.manifest.branch),
Warren Turkal011d4f42013-11-27 16:20:57 -0800535 file=sys.stderr)
Mike Frysingerd1f3e142021-05-01 12:02:01 -0400536 elif avail:
537 pending.append(result)
538 return pending
539
540 pending = self.ExecuteInParallel(
541 opt.jobs,
542 functools.partial(self._GatherOne, opt),
543 projects,
544 callback=_ProcessResults)
Doug Anderson37282b42011-03-04 11:54:18 -0800545
Mike Frysinger163a3be2016-04-04 17:31:32 -0400546 if not pending:
Mike Frysingerd1f3e142021-05-01 12:02:01 -0400547 if opt.branch is None:
Mike Frysinger7a753b82020-02-19 00:14:32 -0500548 print('repo: error: no branches ready for upload', file=sys.stderr)
549 else:
550 print('repo: error: no branches named "%s" ready for upload' %
Mike Frysingerd1f3e142021-05-01 12:02:01 -0400551 (opt.branch,), file=sys.stderr)
Mike Frysinger7a753b82020-02-19 00:14:32 -0500552 return 1
Mike Frysinger163a3be2016-04-04 17:31:32 -0400553
Remy Bohmer7f7acfe2020-08-01 18:36:44 +0200554 pending_proj_names = [project.name for (project, available) in pending]
555 pending_worktrees = [project.worktree for (project, available) in pending]
556 hook = RepoHook.FromSubcmd(
557 hook_type='pre-upload', manifest=self.manifest,
558 opt=opt, abort_if_user_denies=True)
559 if not hook.Run(
560 project_list=pending_proj_names,
561 worktree_list=pending_worktrees):
562 return 1
Doug Anderson37282b42011-03-04 11:54:18 -0800563
Mike Frysingerd1f3e142021-05-01 12:02:01 -0400564 reviewers = _SplitEmails(opt.reviewers) if opt.reviewers else []
565 cc = _SplitEmails(opt.cc) if opt.cc else []
David Pursehouse8f62fb72012-11-14 12:09:38 +0900566 people = (reviewers, cc)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700567
Mike Frysinger163a3be2016-04-04 17:31:32 -0400568 if len(pending) == 1 and len(pending[0][1]) == 1:
Shawn O. Pearcea5ece0e2010-07-15 16:52:42 -0700569 self._SingleBranch(opt, pending[0][1][0], people)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700570 else:
Shawn O. Pearcea5ece0e2010-07-15 16:52:42 -0700571 self._MultipleBranches(opt, pending, people)