blob: 0ad3ce26225d3c738338dd293cf69fb4d54981ae [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):
Mike Frysinger4f210542021-06-14 16:05:19 -040058 COMMON = True
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070059 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 Frysinger7fa8eed2022-08-04 04:27:17 -040081While most normal Gerrit options have dedicated command line options,
82direct access to the Gerit options is available via --push-options.
83This is useful when Gerrit has newer functionality that %prog doesn't
84yet support, or doesn't have plans to support. See the Push Options
85documentation for more details:
86https://gerrit-review.googlesource.com/Documentation/user-upload.html#push_options
87
Mike Frysingerb8f7bb02018-10-10 01:05:11 -040088# Configuration
Shawn O. Pearcea608fb02009-04-17 12:11:24 -070089
90review.URL.autoupload:
91
Mike Frysingere9311272011-08-11 15:46:43 -040092To disable the "Upload ... (y/N)?" prompt, you can set a per-project
Shawn O. Pearcea608fb02009-04-17 12:11:24 -070093or global Git configuration option. If review.URL.autoupload is set
94to "true" then repo will assume you always answer "y" at the prompt,
95and will not prompt you further. If it is set to "false" then repo
96will assume you always answer "n", and will abort.
97
bijia093fdb62013-11-28 09:19:22 +080098review.URL.autoreviewer:
99
100To automatically append a user or mailing list to reviews, you can set
101a per-project or global Git option to do so.
102
Ben Komalo08a3f682010-07-15 16:03:02 -0700103review.URL.autocopy:
104
105To automatically copy a user or mailing list to all uploaded reviews,
106you can set a per-project or global Git option to do so. Specifically,
107review.URL.autocopy can be set to a comma separated list of reviewers
108who you always want copied on all uploads with a non-empty --re
109argument.
110
Shawn O. Pearce3575b8f2010-07-15 17:00:14 -0700111review.URL.username:
112
113Override the username used to connect to Gerrit Code Review.
114By default the local part of the email address is used.
115
Shawn O. Pearcea608fb02009-04-17 12:11:24 -0700116The URL must match the review URL listed in the manifest XML file,
117or in the .git/config within the project. For example:
118
119 [remote "origin"]
120 url = git://git.example.com/project.git
121 review = http://review.example.com/
122
123 [review "http://review.example.com/"]
124 autoupload = true
Ben Komalo08a3f682010-07-15 16:03:02 -0700125 autocopy = johndoe@company.com,my-team-alias@company.com
Shawn O. Pearcea608fb02009-04-17 12:11:24 -0700126
Anthony Russellod666e932012-06-01 00:48:22 -0400127review.URL.uploadtopic:
128
129To add a topic branch whenever uploading a commit, you can set a
130per-project or global Git option to do so. If review.URL.uploadtopic
131is set to "true" then repo will assume you always want the equivalent
132of the -t option to the repo command. If unset or set to "false" then
133repo will make use of only the command line option.
134
Mike Frysinger84685ba2020-02-19 02:22:22 -0500135review.URL.uploadhashtags:
136
137To add hashtags whenever uploading a commit, you can set a per-project
138or global Git option to do so. The value of review.URL.uploadhashtags
Mike Frysingerfc1b18a2020-02-24 15:38:07 -0500139will be used as comma delimited hashtags like the --hashtag option.
140
141review.URL.uploadlabels:
142
143To add labels whenever uploading a commit, you can set a per-project
144or global Git option to do so. The value of review.URL.uploadlabels
145will be used as comma delimited labels like the --label option.
Mike Frysinger84685ba2020-02-19 02:22:22 -0500146
Mike Frysingerf725e542020-03-14 17:39:03 -0400147review.URL.uploadnotify:
148
149Control e-mail notifications when uploading.
150https://gerrit-review.googlesource.com/Documentation/user-upload.html#notify
151
Mike Frysingerb8f7bb02018-10-10 01:05:11 -0400152# References
Shawn O. Pearce337fb9c2009-04-18 10:59:33 -0700153
Mike Frysinger3b24e7b2018-10-10 00:57:44 -0400154Gerrit Code Review: https://www.gerritcodereview.com/
Shawn O. Pearce337fb9c2009-04-18 10:59:33 -0700155
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700156"""
Mike Frysingerd1f3e142021-05-01 12:02:01 -0400157 PARALLEL_JOBS = DEFAULT_LOCAL_JOBS
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700158
Shawn O. Pearcec99883f2008-11-11 17:12:43 -0800159 def _Options(self, p):
Shawn O. Pearcea5ece0e2010-07-15 16:52:42 -0700160 p.add_option('-t',
161 dest='auto_topic', action='store_true',
Mike Frysingerc177f942021-05-04 08:06:36 -0400162 help='send local branch name to Gerrit Code Review')
Mike Frysinger84685ba2020-02-19 02:22:22 -0500163 p.add_option('--hashtag', '--ht',
164 dest='hashtags', action='append', default=[],
Mike Frysingerc177f942021-05-04 08:06:36 -0400165 help='add hashtags (comma delimited) to the review')
Mike Frysinger7ff80af2020-02-19 03:00:26 -0500166 p.add_option('--hashtag-branch', '--htb',
167 action='store_true',
Mike Frysingerc177f942021-05-04 08:06:36 -0400168 help='add local branch name as a hashtag')
Mike Frysingerfc1b18a2020-02-24 15:38:07 -0500169 p.add_option('-l', '--label',
170 dest='labels', action='append', default=[],
Mike Frysingerc177f942021-05-04 08:06:36 -0400171 help='add a label when uploading')
Joe Onorato2896a792008-11-17 16:56:36 -0500172 p.add_option('--re', '--reviewers',
David Pursehouse08671042020-02-12 13:52:31 +0900173 type='string', action='append', dest='reviewers',
Mike Frysingerc177f942021-05-04 08:06:36 -0400174 help='request reviews from these people')
Joe Onorato2896a792008-11-17 16:56:36 -0500175 p.add_option('--cc',
David Pursehouse08671042020-02-12 13:52:31 +0900176 type='string', action='append', dest='cc',
Mike Frysingerc177f942021-05-04 08:06:36 -0400177 help='also send email to these email addresses')
Mike Frysingerd1f3e142021-05-01 12:02:01 -0400178 p.add_option('--br', '--branch',
David Pursehouse08671042020-02-12 13:52:31 +0900179 type='string', action='store', dest='branch',
Mike Frysingerc177f942021-05-04 08:06:36 -0400180 help='(local) branch to upload')
Mike Frysingerb8e09ea2021-05-03 00:51:52 -0400181 p.add_option('-c', '--current-branch',
Daniel Sandlere9d6b612012-04-06 10:39:32 -0400182 dest='current_branch', action='store_true',
Mike Frysingerc177f942021-05-04 08:06:36 -0400183 help='upload current git branch')
Mike Frysinger73561142021-05-03 01:10:09 -0400184 p.add_option('--no-current-branch',
185 dest='current_branch', action='store_false',
Mike Frysingerc177f942021-05-04 08:06:36 -0400186 help='upload all git branches')
Mike Frysingerb8e09ea2021-05-03 00:51:52 -0400187 # Turn this into a warning & remove this someday.
188 p.add_option('--cbr',
189 dest='current_branch', action='store_true',
190 help=optparse.SUPPRESS_HELP)
Vadim Bendeburybd8f6582018-10-31 13:48:01 -0700191 p.add_option('--ne', '--no-emails',
192 action='store_false', dest='notify', default=True,
Mike Frysingerc177f942021-05-04 08:06:36 -0400193 help='do not send e-mails on upload')
Changcheng Xiao87984c62017-08-02 16:55:03 +0200194 p.add_option('-p', '--private',
195 action='store_true', dest='private', default=False,
Mike Frysingerc177f942021-05-04 08:06:36 -0400196 help='upload as a private change (deprecated; use --wip)')
Changcheng Xiao87984c62017-08-02 16:55:03 +0200197 p.add_option('-w', '--wip',
198 action='store_true', dest='wip', default=False,
Mike Frysingerc177f942021-05-04 08:06:36 -0400199 help='upload as a work-in-progress change')
William Escandeac76fd32022-08-02 16:05:37 -0700200 p.add_option('-r', '--ready',
201 action='store_true', default=False,
202 help='mark change as ready (clears work-in-progress setting)')
Masaya Suzuki305a2d02017-11-13 10:48:34 -0800203 p.add_option('-o', '--push-option',
204 type='string', action='append', dest='push_options',
205 default=[],
Mike Frysingerc177f942021-05-04 08:06:36 -0400206 help='additional push options to transmit')
Bryan Jacobsf609f912013-05-06 13:36:24 -0400207 p.add_option('-D', '--destination', '--dest',
208 type='string', action='store', dest='dest_branch',
209 metavar='BRANCH',
Mike Frysingerc177f942021-05-04 08:06:36 -0400210 help='submit for review on this target branch')
Mike Frysinger819cc812020-02-19 02:27:22 -0500211 p.add_option('-n', '--dry-run',
212 dest='dryrun', default=False, action='store_true',
Mike Frysingerc177f942021-05-04 08:06:36 -0400213 help='do everything except actually upload the CL')
Mike Frysinger02aa8892020-02-19 02:32:52 -0500214 p.add_option('-y', '--yes',
215 default=False, action='store_true',
Mike Frysingerc177f942021-05-04 08:06:36 -0400216 help='answer yes to all safe prompts')
Martin Geisler9fb64ae2022-07-08 10:50:10 +0200217 p.add_option('--ignore-untracked-files',
218 action='store_true', default=False,
219 help='ignore untracked files in the working copy')
220 p.add_option('--no-ignore-untracked-files',
221 dest='ignore_untracked_files', action='store_false',
222 help='always ask about untracked files in the working copy')
Mike Frysinger21c15752020-02-11 05:17:16 -0500223 p.add_option('--no-cert-checks',
224 dest='validate_certs', action='store_false', default=True,
Mike Frysingerc177f942021-05-04 08:06:36 -0400225 help='disable verifying ssl certs (unsafe)')
Remy Bohmer7f7acfe2020-08-01 18:36:44 +0200226 RepoHook.AddOptionGroup(p, 'pre-upload')
Doug Anderson37282b42011-03-04 11:54:18 -0800227
Shawn O. Pearcea5ece0e2010-07-15 16:52:42 -0700228 def _SingleBranch(self, opt, branch, people):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700229 project = branch.project
230 name = branch.name
Shawn O. Pearcea608fb02009-04-17 12:11:24 -0700231 remote = project.GetBranch(name).remote
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700232
Shawn O. Pearcea608fb02009-04-17 12:11:24 -0700233 key = 'review.%s.autoupload' % remote.review
234 answer = project.config.GetBoolean(key)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700235
Shawn O. Pearcea608fb02009-04-17 12:11:24 -0700236 if answer is False:
237 _die("upload blocked by %s = false" % key)
238
239 if answer is None:
Shawn O. Pearce66bdd462009-04-17 18:47:22 -0700240 date = branch.date
David Pursehouse8a68ff92012-09-24 12:15:13 +0900241 commit_list = branch.commits
Shawn O. Pearce66bdd462009-04-17 18:47:22 -0700242
Chirayu Desai610d3c42013-06-24 14:02:12 +0530243 destination = opt.dest_branch or project.dest_branch or project.revisionExpr
Nicolas Cornub54343d2017-07-10 10:31:24 +0200244 print('Upload project %s/ to remote branch %s%s:' %
LaMont Jonescc879a92021-11-18 22:40:18 +0000245 (project.RelPath(local=opt.this_manifest_only), destination,
246 ' (private)' if opt.private else ''))
Sarah Owenscecd1d82012-11-01 22:59:27 -0700247 print(' branch %s (%2d commit%s, %s):' % (
David Pursehouseabdf7502020-02-12 14:58:39 +0900248 name,
249 len(commit_list),
250 len(commit_list) != 1 and 's' or '',
251 date))
David Pursehouse8a68ff92012-09-24 12:15:13 +0900252 for commit in commit_list:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700253 print(' %s' % commit)
Shawn O. Pearcea608fb02009-04-17 12:11:24 -0700254
Mike Frysingerab85fe72019-07-04 17:35:11 -0400255 print('to %s (y/N)? ' % remote.review, end='')
256 # TODO: When we require Python 3, use flush=True w/print above.
257 sys.stdout.flush()
Mike Frysinger02aa8892020-02-19 02:32:52 -0500258 if opt.yes:
259 print('<--yes>')
260 answer = True
261 else:
262 answer = sys.stdin.readline().strip().lower()
263 answer = answer in ('y', 'yes', '1', 'true', 't')
Shawn O. Pearcea608fb02009-04-17 12:11:24 -0700264
Mike Frysingere6d4b842022-08-19 07:26:38 -0400265 if not opt.yes and answer:
Dan Morrill879a9a52010-05-04 16:56:07 -0700266 if len(branch.commits) > UNUSUAL_COMMIT_THRESHOLD:
267 answer = _ConfirmManyUploads()
268
269 if answer:
Shawn O. Pearcea5ece0e2010-07-15 16:52:42 -0700270 self._UploadAndReport(opt, [branch], people)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700271 else:
272 _die("upload aborted by user")
273
Shawn O. Pearcea5ece0e2010-07-15 16:52:42 -0700274 def _MultipleBranches(self, opt, pending, people):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700275 projects = {}
276 branches = {}
277
278 script = []
279 script.append('# Uncomment the branches to upload:')
280 for project, avail in pending:
LaMont Jonesb240d282022-11-08 23:10:27 +0000281 project_path = project.RelPath(local=opt.this_manifest_only)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700282 script.append('#')
LaMont Jonesb240d282022-11-08 23:10:27 +0000283 script.append(f'# project {project_path}/:')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700284
285 b = {}
286 for branch in avail:
Bryan Jacobs710d4b02013-05-31 15:28:05 -0400287 if branch is None:
288 continue
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700289 name = branch.name
290 date = branch.date
David Pursehouse8a68ff92012-09-24 12:15:13 +0900291 commit_list = branch.commits
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700292
293 if b:
294 script.append('#')
Bryan Jacobs691a7592013-05-31 15:45:28 -0400295 destination = opt.dest_branch or project.dest_branch or project.revisionExpr
Christer Fletcher6a1f7372011-04-28 14:13:14 +0200296 script.append('# branch %s (%2d commit%s, %s) to remote branch %s:' % (
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700297 name,
David Pursehouse8a68ff92012-09-24 12:15:13 +0900298 len(commit_list),
299 len(commit_list) != 1 and 's' or '',
Christer Fletcher6a1f7372011-04-28 14:13:14 +0200300 date,
Bryan Jacobs691a7592013-05-31 15:45:28 -0400301 destination))
David Pursehouse8a68ff92012-09-24 12:15:13 +0900302 for commit in commit_list:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700303 script.append('# %s' % commit)
304 b[name] = branch
305
LaMont Jonesb240d282022-11-08 23:10:27 +0000306 projects[project_path] = project
307 branches[project_path] = b
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700308 script.append('')
309
310 script = Editor.EditString("\n".join(script)).split("\n")
311
312 project_re = re.compile(r'^#?\s*project\s*([^\s]+)/:$')
313 branch_re = re.compile(r'^\s*branch\s*([^\s(]+)\s*\(.*')
314
315 project = None
316 todo = []
317
318 for line in script:
319 m = project_re.match(line)
320 if m:
321 name = m.group(1)
322 project = projects.get(name)
323 if not project:
324 _die('project %s not available for upload', name)
325 continue
326
327 m = branch_re.match(line)
328 if m:
329 name = m.group(1)
330 if not project:
331 _die('project for branch %s not in script', name)
LaMont Jonesb240d282022-11-08 23:10:27 +0000332 project_path = project.RelPath(local=opt.this_manifest_only)
333 branch = branches[project_path].get(name)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700334 if not branch:
LaMont Jonesb240d282022-11-08 23:10:27 +0000335 _die('branch %s not in %s', name, project_path)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700336 todo.append(branch)
337 if not todo:
338 _die("nothing uncommented for upload")
Dan Morrill879a9a52010-05-04 16:56:07 -0700339
Mike Frysingere6d4b842022-08-19 07:26:38 -0400340 if not opt.yes:
341 many_commits = False
342 for branch in todo:
343 if len(branch.commits) > UNUSUAL_COMMIT_THRESHOLD:
344 many_commits = True
345 break
346 if many_commits:
347 if not _ConfirmManyUploads(multiple_branches=True):
348 _die("upload aborted by user")
Dan Morrill879a9a52010-05-04 16:56:07 -0700349
Shawn O. Pearcea5ece0e2010-07-15 16:52:42 -0700350 self._UploadAndReport(opt, todo, people)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700351
bijia093fdb62013-11-28 09:19:22 +0800352 def _AppendAutoList(self, branch, people):
Ben Komalo08a3f682010-07-15 16:03:02 -0700353 """
bijia093fdb62013-11-28 09:19:22 +0800354 Appends the list of reviewers in the git project's config.
Ben Komalo08a3f682010-07-15 16:03:02 -0700355 Appends the list of users in the CC list in the git project's config if a
356 non-empty reviewer list was found.
357 """
Ben Komalo08a3f682010-07-15 16:03:02 -0700358 name = branch.name
359 project = branch.project
bijia093fdb62013-11-28 09:19:22 +0800360
361 key = 'review.%s.autoreviewer' % project.GetBranch(name).remote.review
362 raw_list = project.config.GetString(key)
David Pursehouse8f78a832020-02-12 11:20:36 +0900363 if raw_list is not None:
bijia093fdb62013-11-28 09:19:22 +0800364 people[0].extend([entry.strip() for entry in raw_list.split(',')])
365
Ben Komalo08a3f682010-07-15 16:03:02 -0700366 key = 'review.%s.autocopy' % project.GetBranch(name).remote.review
367 raw_list = project.config.GetString(key)
David Pursehouse8f78a832020-02-12 11:20:36 +0900368 if raw_list is not None and len(people[0]) > 0:
Ben Komalo08a3f682010-07-15 16:03:02 -0700369 people[1].extend([entry.strip() for entry in raw_list.split(',')])
370
Ficus Kirkpatrickbc7ef672009-05-04 12:45:11 -0700371 def _FindGerritChange(self, branch):
372 last_pub = branch.project.WasPublished(branch.name)
373 if last_pub is None:
374 return ""
375
376 refs = branch.GetPublishedRefs()
377 try:
378 # refs/changes/XYZ/N --> XYZ
379 return refs.get(last_pub).split('/')[-2]
David Pursehouse1d947b32012-10-25 12:23:11 +0900380 except (AttributeError, IndexError):
Ficus Kirkpatrickbc7ef672009-05-04 12:45:11 -0700381 return ""
382
Shawn O. Pearcea5ece0e2010-07-15 16:52:42 -0700383 def _UploadAndReport(self, opt, todo, original_people):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700384 have_errors = False
385 for branch in todo:
386 try:
Ben Komalo08a3f682010-07-15 16:03:02 -0700387 people = copy.deepcopy(original_people)
bijia093fdb62013-11-28 09:19:22 +0800388 self._AppendAutoList(branch, people)
Ben Komalo08a3f682010-07-15 16:03:02 -0700389
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500390 # Check if there are local changes that may have been forgotten
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700391 changes = branch.project.UncommitedFiles()
Martin Geisler9fb64ae2022-07-08 10:50:10 +0200392 if opt.ignore_untracked_files:
393 untracked = set(branch.project.UntrackedFiles())
394 changes = [x for x in changes if x not in untracked]
395
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700396 if changes:
David Pursehousec1b86a22012-11-14 11:36:51 +0900397 key = 'review.%s.autoupload' % branch.project.remote.review
398 answer = branch.project.config.GetBoolean(key)
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500399
David Pursehousec1b86a22012-11-14 11:36:51 +0900400 # if they want to auto upload, let's not ask because it could be automated
401 if answer is None:
Mike Frysingerab85fe72019-07-04 17:35:11 -0400402 print()
403 print('Uncommitted changes in %s (did you forget to amend?):'
404 % branch.project.name)
405 print('\n'.join(changes))
406 print('Continue uploading? (y/N) ', end='')
407 # TODO: When we require Python 3, use flush=True w/print above.
408 sys.stdout.flush()
Mike Frysinger02aa8892020-02-19 02:32:52 -0500409 if opt.yes:
410 print('<--yes>')
411 a = 'yes'
412 else:
413 a = sys.stdin.readline().strip().lower()
David Pursehousec1b86a22012-11-14 11:36:51 +0900414 if a not in ('y', 'yes', 't', 'true', 'on'):
415 print("skipping upload", file=sys.stderr)
416 branch.uploaded = False
417 branch.error = 'User aborted'
418 continue
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500419
Anthony Russellod666e932012-06-01 00:48:22 -0400420 # Check if topic branches should be sent to the server during upload
421 if opt.auto_topic is not True:
David Pursehousec1b86a22012-11-14 11:36:51 +0900422 key = 'review.%s.uploadtopic' % branch.project.remote.review
423 opt.auto_topic = branch.project.config.GetBoolean(key)
Anthony Russellod666e932012-06-01 00:48:22 -0400424
Mike Frysingerfc1b18a2020-02-24 15:38:07 -0500425 def _ExpandCommaList(value):
426 """Split |value| up into comma delimited entries."""
Mike Frysinger84685ba2020-02-19 02:22:22 -0500427 if not value:
428 return
Mike Frysingerfc1b18a2020-02-24 15:38:07 -0500429 for ret in value.split(','):
430 ret = ret.strip()
431 if ret:
432 yield ret
433
434 # Check if hashtags should be included.
Mike Frysinger84685ba2020-02-19 02:22:22 -0500435 key = 'review.%s.uploadhashtags' % branch.project.remote.review
Mike Frysingerfc1b18a2020-02-24 15:38:07 -0500436 hashtags = set(_ExpandCommaList(branch.project.config.GetString(key)))
Mike Frysinger84685ba2020-02-19 02:22:22 -0500437 for tag in opt.hashtags:
Mike Frysingerfc1b18a2020-02-24 15:38:07 -0500438 hashtags.update(_ExpandCommaList(tag))
Mike Frysinger7ff80af2020-02-19 03:00:26 -0500439 if opt.hashtag_branch:
440 hashtags.add(branch.name)
Mike Frysinger84685ba2020-02-19 02:22:22 -0500441
Mike Frysingerfc1b18a2020-02-24 15:38:07 -0500442 # Check if labels should be included.
443 key = 'review.%s.uploadlabels' % branch.project.remote.review
444 labels = set(_ExpandCommaList(branch.project.config.GetString(key)))
445 for label in opt.labels:
446 labels.update(_ExpandCommaList(label))
Mike Frysingerfc1b18a2020-02-24 15:38:07 -0500447
Mike Frysingerf725e542020-03-14 17:39:03 -0400448 # Handle e-mail notifications.
449 if opt.notify is False:
450 notify = 'NONE'
451 else:
452 key = 'review.%s.uploadnotify' % branch.project.remote.review
453 notify = branch.project.config.GetString(key)
454
Colin Cross59b31cb2013-10-08 23:10:52 -0700455 destination = opt.dest_branch or branch.project.dest_branch
Conley Owens3bfd7212013-09-30 15:54:38 -0700456
457 # Make sure our local branch is not setup to track a different remote branch
458 merge_branch = self._GetMergeBranch(branch.project)
Conley Owensfbd3f2a2013-10-15 12:59:00 -0700459 if destination:
Sean McAllister682f0b62020-05-18 09:15:51 -0600460 full_dest = destination
461 if not full_dest.startswith(R_HEADS):
462 full_dest = R_HEADS + full_dest
463
Conley Owensfbd3f2a2013-10-15 12:59:00 -0700464 if not opt.dest_branch and merge_branch and merge_branch != full_dest:
465 print('merge branch %s does not match destination branch %s'
466 % (merge_branch, full_dest))
467 print('skipping upload.')
468 print('Please use `--destination %s` if this is intentional'
469 % destination)
470 branch.uploaded = False
471 continue
Conley Owens3bfd7212013-09-30 15:54:38 -0700472
Changcheng Xiao87984c62017-08-02 16:55:03 +0200473 branch.UploadForReview(people,
Mike Frysinger819cc812020-02-19 02:27:22 -0500474 dryrun=opt.dryrun,
Changcheng Xiao87984c62017-08-02 16:55:03 +0200475 auto_topic=opt.auto_topic,
Mike Frysinger84685ba2020-02-19 02:22:22 -0500476 hashtags=hashtags,
Mike Frysingerfc1b18a2020-02-24 15:38:07 -0500477 labels=labels,
Changcheng Xiao87984c62017-08-02 16:55:03 +0200478 private=opt.private,
Mike Frysingerf725e542020-03-14 17:39:03 -0400479 notify=notify,
Changcheng Xiao87984c62017-08-02 16:55:03 +0200480 wip=opt.wip,
William Escandeac76fd32022-08-02 16:05:37 -0700481 ready=opt.ready,
Łukasz Gardońbed59ce2017-08-08 10:18:11 +0200482 dest_branch=destination,
Masaya Suzuki305a2d02017-11-13 10:48:34 -0800483 validate_certs=opt.validate_certs,
484 push_options=opt.push_options)
Łukasz Gardońbed59ce2017-08-08 10:18:11 +0200485
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700486 branch.uploaded = True
Sarah Owensa5be53f2012-09-09 15:37:57 -0700487 except UploadError as e:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700488 branch.error = e
489 branch.uploaded = False
490 have_errors = True
491
Sarah Owenscecd1d82012-11-01 22:59:27 -0700492 print(file=sys.stderr)
493 print('----------------------------------------------------------------------', file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700494
495 if have_errors:
496 for branch in todo:
497 if not branch.uploaded:
Shawn O. Pearcef00e0ce2009-08-22 18:39:49 -0700498 if len(str(branch.error)) <= 30:
499 fmt = ' (%s)'
500 else:
501 fmt = '\n (%s)'
Sarah Owenscecd1d82012-11-01 22:59:27 -0700502 print(('[FAILED] %-15s %-15s' + fmt) % (
LaMont Jonescc879a92021-11-18 22:40:18 +0000503 branch.project.RelPath(local=opt.this_manifest_only) + '/',
David Pursehouseabdf7502020-02-12 14:58:39 +0900504 branch.name,
505 str(branch.error)),
506 file=sys.stderr)
Sarah Owenscecd1d82012-11-01 22:59:27 -0700507 print()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700508
509 for branch in todo:
David Pursehousec1b86a22012-11-14 11:36:51 +0900510 if branch.uploaded:
511 print('[OK ] %-15s %s' % (
LaMont Jonescc879a92021-11-18 22:40:18 +0000512 branch.project.RelPath(local=opt.this_manifest_only) + '/',
David Pursehouseabdf7502020-02-12 14:58:39 +0900513 branch.name),
514 file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700515
516 if have_errors:
517 sys.exit(1)
518
Conley Owens3bfd7212013-09-30 15:54:38 -0700519 def _GetMergeBranch(self, project):
520 p = GitCommand(project,
521 ['rev-parse', '--abbrev-ref', 'HEAD'],
David Pursehousee5913ae2020-02-12 13:56:59 +0900522 capture_stdout=True,
523 capture_stderr=True)
Conley Owens3bfd7212013-09-30 15:54:38 -0700524 p.Wait()
525 local_branch = p.stdout.strip()
526 p = GitCommand(project,
527 ['config', '--get', 'branch.%s.merge' % local_branch],
David Pursehousee5913ae2020-02-12 13:56:59 +0900528 capture_stdout=True,
529 capture_stderr=True)
Conley Owens3bfd7212013-09-30 15:54:38 -0700530 p.Wait()
531 merge_branch = p.stdout.strip()
532 return merge_branch
533
Mike Frysingerd1f3e142021-05-01 12:02:01 -0400534 @staticmethod
535 def _GatherOne(opt, project):
536 """Figure out the upload status for |project|."""
537 if opt.current_branch:
538 cbr = project.CurrentBranch
539 up_branch = project.GetUploadableBranch(cbr)
540 avail = [up_branch] if up_branch else None
541 else:
542 avail = project.GetUploadableBranches(opt.branch)
543 return (project, avail)
544
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700545 def Execute(self, opt, args):
LaMont Jonescc879a92021-11-18 22:40:18 +0000546 projects = self.GetProjects(args, all_manifests=not opt.this_manifest_only)
Mandeep Singh Bainesd6c93a22011-05-26 10:34:11 -0700547
Mike Frysingerd1f3e142021-05-01 12:02:01 -0400548 def _ProcessResults(_pool, _out, results):
549 pending = []
550 for result in results:
551 project, avail = result
552 if avail is None:
Mike Frysinger3b038ce2021-05-01 00:45:19 -0400553 print('repo: error: %s: Unable to upload branch "%s". '
Mike Frysinger160748f2020-11-10 01:09:51 -0500554 'You might be able to fix the branch by running:\n'
555 ' git branch --set-upstream-to m/%s' %
LaMont Jonescc879a92021-11-18 22:40:18 +0000556 (project.RelPath(local=opt.this_manifest_only), project.CurrentBranch,
557 project.manifest.branch),
Warren Turkal011d4f42013-11-27 16:20:57 -0800558 file=sys.stderr)
Mike Frysingerd1f3e142021-05-01 12:02:01 -0400559 elif avail:
560 pending.append(result)
561 return pending
562
563 pending = self.ExecuteInParallel(
564 opt.jobs,
565 functools.partial(self._GatherOne, opt),
566 projects,
567 callback=_ProcessResults)
Doug Anderson37282b42011-03-04 11:54:18 -0800568
Mike Frysinger163a3be2016-04-04 17:31:32 -0400569 if not pending:
Mike Frysingerd1f3e142021-05-01 12:02:01 -0400570 if opt.branch is None:
Mike Frysinger7a753b82020-02-19 00:14:32 -0500571 print('repo: error: no branches ready for upload', file=sys.stderr)
572 else:
573 print('repo: error: no branches named "%s" ready for upload' %
Mike Frysingerd1f3e142021-05-01 12:02:01 -0400574 (opt.branch,), file=sys.stderr)
Mike Frysinger7a753b82020-02-19 00:14:32 -0500575 return 1
Mike Frysinger163a3be2016-04-04 17:31:32 -0400576
LaMont Jonescc879a92021-11-18 22:40:18 +0000577 manifests = {project.manifest.topdir: project.manifest
578 for (project, available) in pending}
579 ret = 0
580 for manifest in manifests.values():
581 pending_proj_names = [project.name for (project, available) in pending
582 if project.manifest.topdir == manifest.topdir]
583 pending_worktrees = [project.worktree for (project, available) in pending
584 if project.manifest.topdir == manifest.topdir]
585 hook = RepoHook.FromSubcmd(
586 hook_type='pre-upload', manifest=manifest,
587 opt=opt, abort_if_user_denies=True)
588 if not hook.Run(
589 project_list=pending_proj_names,
590 worktree_list=pending_worktrees):
591 ret = 1
592 if ret:
593 return ret
Doug Anderson37282b42011-03-04 11:54:18 -0800594
Mike Frysingerd1f3e142021-05-01 12:02:01 -0400595 reviewers = _SplitEmails(opt.reviewers) if opt.reviewers else []
596 cc = _SplitEmails(opt.cc) if opt.cc else []
David Pursehouse8f62fb72012-11-14 12:09:38 +0900597 people = (reviewers, cc)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700598
Mike Frysinger163a3be2016-04-04 17:31:32 -0400599 if len(pending) == 1 and len(pending[0][1]) == 1:
Shawn O. Pearcea5ece0e2010-07-15 16:52:42 -0700600 self._SingleBranch(opt, pending[0][1][0], people)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700601 else:
Shawn O. Pearcea5ece0e2010-07-15 16:52:42 -0700602 self._MultipleBranches(opt, pending, people)