diff options
Diffstat (limited to 'subcmds/upload.py')
| -rw-r--r-- | subcmds/upload.py | 315 |
1 files changed, 191 insertions, 124 deletions
diff --git a/subcmds/upload.py b/subcmds/upload.py index 5c12aaee..c48deab6 100644 --- a/subcmds/upload.py +++ b/subcmds/upload.py | |||
| @@ -1,5 +1,3 @@ | |||
| 1 | # -*- coding:utf-8 -*- | ||
| 2 | # | ||
| 3 | # Copyright (C) 2008 The Android Open Source Project | 1 | # Copyright (C) 2008 The Android Open Source Project |
| 4 | # | 2 | # |
| 5 | # Licensed under the Apache License, Version 2.0 (the "License"); | 3 | # Licensed under the Apache License, Version 2.0 (the "License"); |
| @@ -14,25 +12,23 @@ | |||
| 14 | # See the License for the specific language governing permissions and | 12 | # See the License for the specific language governing permissions and |
| 15 | # limitations under the License. | 13 | # limitations under the License. |
| 16 | 14 | ||
| 17 | from __future__ import print_function | ||
| 18 | import copy | 15 | import copy |
| 16 | import functools | ||
| 17 | import optparse | ||
| 19 | import re | 18 | import re |
| 20 | import sys | 19 | import sys |
| 21 | 20 | ||
| 22 | from command import InteractiveCommand | 21 | from command import DEFAULT_LOCAL_JOBS, InteractiveCommand |
| 23 | from editor import Editor | 22 | from editor import Editor |
| 24 | from error import HookError, UploadError | 23 | from error import UploadError |
| 25 | from git_command import GitCommand | 24 | from git_command import GitCommand |
| 26 | from project import RepoHook | 25 | from git_refs import R_HEADS |
| 26 | from hooks import RepoHook | ||
| 27 | 27 | ||
| 28 | from pyversion import is_python3 | ||
| 29 | if not is_python3(): | ||
| 30 | input = raw_input | ||
| 31 | else: | ||
| 32 | unicode = str | ||
| 33 | 28 | ||
| 34 | UNUSUAL_COMMIT_THRESHOLD = 5 | 29 | UNUSUAL_COMMIT_THRESHOLD = 5 |
| 35 | 30 | ||
| 31 | |||
| 36 | def _ConfirmManyUploads(multiple_branches=False): | 32 | def _ConfirmManyUploads(multiple_branches=False): |
| 37 | if multiple_branches: | 33 | if multiple_branches: |
| 38 | print('ATTENTION: One or more branches has an unusually high number ' | 34 | print('ATTENTION: One or more branches has an unusually high number ' |
| @@ -44,19 +40,22 @@ def _ConfirmManyUploads(multiple_branches=False): | |||
| 44 | answer = input("If you are sure you intend to do this, type 'yes': ").strip() | 40 | answer = input("If you are sure you intend to do this, type 'yes': ").strip() |
| 45 | return answer == "yes" | 41 | return answer == "yes" |
| 46 | 42 | ||
| 43 | |||
| 47 | def _die(fmt, *args): | 44 | def _die(fmt, *args): |
| 48 | msg = fmt % args | 45 | msg = fmt % args |
| 49 | print('error: %s' % msg, file=sys.stderr) | 46 | print('error: %s' % msg, file=sys.stderr) |
| 50 | sys.exit(1) | 47 | sys.exit(1) |
| 51 | 48 | ||
| 49 | |||
| 52 | def _SplitEmails(values): | 50 | def _SplitEmails(values): |
| 53 | result = [] | 51 | result = [] |
| 54 | for value in values: | 52 | for value in values: |
| 55 | result.extend([s.strip() for s in value.split(',')]) | 53 | result.extend([s.strip() for s in value.split(',')]) |
| 56 | return result | 54 | return result |
| 57 | 55 | ||
| 56 | |||
| 58 | class Upload(InteractiveCommand): | 57 | class Upload(InteractiveCommand): |
| 59 | common = True | 58 | COMMON = True |
| 60 | helpSummary = "Upload changes for code review" | 59 | helpSummary = "Upload changes for code review" |
| 61 | helpUsage = """ | 60 | helpUsage = """ |
| 62 | %prog [--re --cc] [<project>]... | 61 | %prog [--re --cc] [<project>]... |
| @@ -126,74 +125,89 @@ is set to "true" then repo will assume you always want the equivalent | |||
| 126 | of the -t option to the repo command. If unset or set to "false" then | 125 | of the -t option to the repo command. If unset or set to "false" then |
| 127 | repo will make use of only the command line option. | 126 | repo will make use of only the command line option. |
| 128 | 127 | ||
| 128 | review.URL.uploadhashtags: | ||
| 129 | |||
| 130 | To add hashtags whenever uploading a commit, you can set a per-project | ||
| 131 | or global Git option to do so. The value of review.URL.uploadhashtags | ||
| 132 | will be used as comma delimited hashtags like the --hashtag option. | ||
| 133 | |||
| 134 | review.URL.uploadlabels: | ||
| 135 | |||
| 136 | To add labels whenever uploading a commit, you can set a per-project | ||
| 137 | or global Git option to do so. The value of review.URL.uploadlabels | ||
| 138 | will be used as comma delimited labels like the --label option. | ||
| 139 | |||
| 140 | review.URL.uploadnotify: | ||
| 141 | |||
| 142 | Control e-mail notifications when uploading. | ||
| 143 | https://gerrit-review.googlesource.com/Documentation/user-upload.html#notify | ||
| 144 | |||
| 129 | # References | 145 | # References |
| 130 | 146 | ||
| 131 | Gerrit Code Review: https://www.gerritcodereview.com/ | 147 | Gerrit Code Review: https://www.gerritcodereview.com/ |
| 132 | 148 | ||
| 133 | """ | 149 | """ |
| 150 | PARALLEL_JOBS = DEFAULT_LOCAL_JOBS | ||
| 134 | 151 | ||
| 135 | def _Options(self, p): | 152 | def _Options(self, p): |
| 136 | p.add_option('-t', | 153 | p.add_option('-t', |
| 137 | dest='auto_topic', action='store_true', | 154 | dest='auto_topic', action='store_true', |
| 138 | help='Send local branch name to Gerrit Code Review') | 155 | help='send local branch name to Gerrit Code Review') |
| 156 | p.add_option('--hashtag', '--ht', | ||
| 157 | dest='hashtags', action='append', default=[], | ||
| 158 | help='add hashtags (comma delimited) to the review') | ||
| 159 | p.add_option('--hashtag-branch', '--htb', | ||
| 160 | action='store_true', | ||
| 161 | help='add local branch name as a hashtag') | ||
| 162 | p.add_option('-l', '--label', | ||
| 163 | dest='labels', action='append', default=[], | ||
| 164 | help='add a label when uploading') | ||
| 139 | p.add_option('--re', '--reviewers', | 165 | p.add_option('--re', '--reviewers', |
| 140 | type='string', action='append', dest='reviewers', | 166 | type='string', action='append', dest='reviewers', |
| 141 | help='Request reviews from these people.') | 167 | help='request reviews from these people') |
| 142 | p.add_option('--cc', | 168 | p.add_option('--cc', |
| 143 | type='string', action='append', dest='cc', | 169 | type='string', action='append', dest='cc', |
| 144 | help='Also send email to these email addresses.') | 170 | help='also send email to these email addresses') |
| 145 | p.add_option('--br', | 171 | p.add_option('--br', '--branch', |
| 146 | type='string', action='store', dest='branch', | 172 | type='string', action='store', dest='branch', |
| 147 | help='Branch to upload.') | 173 | help='(local) branch to upload') |
| 148 | p.add_option('--cbr', '--current-branch', | 174 | p.add_option('-c', '--current-branch', |
| 175 | dest='current_branch', action='store_true', | ||
| 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', | ||
| 149 | dest='current_branch', action='store_true', | 182 | dest='current_branch', action='store_true', |
| 150 | help='Upload current git branch.') | 183 | help=optparse.SUPPRESS_HELP) |
| 151 | p.add_option('-d', '--draft', | ||
| 152 | action='store_true', dest='draft', default=False, | ||
| 153 | help='If specified, upload as a draft.') | ||
| 154 | p.add_option('--ne', '--no-emails', | 184 | p.add_option('--ne', '--no-emails', |
| 155 | action='store_false', dest='notify', default=True, | 185 | action='store_false', dest='notify', default=True, |
| 156 | help='If specified, do not send emails on upload.') | 186 | help='do not send e-mails on upload') |
| 157 | p.add_option('-p', '--private', | 187 | p.add_option('-p', '--private', |
| 158 | action='store_true', dest='private', default=False, | 188 | action='store_true', dest='private', default=False, |
| 159 | help='If specified, upload as a private change.') | 189 | help='upload as a private change (deprecated; use --wip)') |
| 160 | p.add_option('-w', '--wip', | 190 | p.add_option('-w', '--wip', |
| 161 | action='store_true', dest='wip', default=False, | 191 | action='store_true', dest='wip', default=False, |
| 162 | help='If specified, upload as a work-in-progress change.') | 192 | help='upload as a work-in-progress change') |
| 163 | p.add_option('-o', '--push-option', | 193 | p.add_option('-o', '--push-option', |
| 164 | type='string', action='append', dest='push_options', | 194 | type='string', action='append', dest='push_options', |
| 165 | default=[], | 195 | default=[], |
| 166 | help='Additional push options to transmit') | 196 | help='additional push options to transmit') |
| 167 | p.add_option('-D', '--destination', '--dest', | 197 | p.add_option('-D', '--destination', '--dest', |
| 168 | type='string', action='store', dest='dest_branch', | 198 | type='string', action='store', dest='dest_branch', |
| 169 | metavar='BRANCH', | 199 | metavar='BRANCH', |
| 170 | help='Submit for review on this target branch.') | 200 | help='submit for review on this target branch') |
| 171 | 201 | p.add_option('-n', '--dry-run', | |
| 172 | # Options relating to upload hook. Note that verify and no-verify are NOT | 202 | dest='dryrun', default=False, action='store_true', |
| 173 | # opposites of each other, which is why they store to different locations. | 203 | help='do everything except actually upload the CL') |
| 174 | # We are using them to match 'git commit' syntax. | 204 | p.add_option('-y', '--yes', |
| 175 | # | 205 | default=False, action='store_true', |
| 176 | # Combinations: | 206 | help='answer yes to all safe prompts') |
| 177 | # - no-verify=False, verify=False (DEFAULT): | ||
| 178 | # If stdout is a tty, can prompt about running upload hooks if needed. | ||
| 179 | # If user denies running hooks, the upload is cancelled. If stdout is | ||
| 180 | # not a tty and we would need to prompt about upload hooks, upload is | ||
| 181 | # cancelled. | ||
| 182 | # - no-verify=False, verify=True: | ||
| 183 | # Always run upload hooks with no prompt. | ||
| 184 | # - no-verify=True, verify=False: | ||
| 185 | # Never run upload hooks, but upload anyway (AKA bypass hooks). | ||
| 186 | # - no-verify=True, verify=True: | ||
| 187 | # Invalid | ||
| 188 | p.add_option('--no-cert-checks', | 207 | p.add_option('--no-cert-checks', |
| 189 | dest='validate_certs', action='store_false', default=True, | 208 | dest='validate_certs', action='store_false', default=True, |
| 190 | help='Disable verifying ssl certs (unsafe).') | 209 | help='disable verifying ssl certs (unsafe)') |
| 191 | p.add_option('--no-verify', | 210 | RepoHook.AddOptionGroup(p, 'pre-upload') |
| 192 | dest='bypass_hooks', action='store_true', | ||
| 193 | help='Do not run the upload hook.') | ||
| 194 | p.add_option('--verify', | ||
| 195 | dest='allow_all_hooks', action='store_true', | ||
| 196 | help='Run the upload hook without prompting.') | ||
| 197 | 211 | ||
| 198 | def _SingleBranch(self, opt, branch, people): | 212 | def _SingleBranch(self, opt, branch, people): |
| 199 | project = branch.project | 213 | project = branch.project |
| @@ -212,20 +226,24 @@ Gerrit Code Review: https://www.gerritcodereview.com/ | |||
| 212 | 226 | ||
| 213 | destination = opt.dest_branch or project.dest_branch or project.revisionExpr | 227 | destination = opt.dest_branch or project.dest_branch or project.revisionExpr |
| 214 | print('Upload project %s/ to remote branch %s%s:' % | 228 | print('Upload project %s/ to remote branch %s%s:' % |
| 215 | (project.relpath, destination, ' (draft)' if opt.draft else '')) | 229 | (project.relpath, destination, ' (private)' if opt.private else '')) |
| 216 | print(' branch %s (%2d commit%s, %s):' % ( | 230 | print(' branch %s (%2d commit%s, %s):' % ( |
| 217 | name, | 231 | name, |
| 218 | len(commit_list), | 232 | len(commit_list), |
| 219 | len(commit_list) != 1 and 's' or '', | 233 | len(commit_list) != 1 and 's' or '', |
| 220 | date)) | 234 | date)) |
| 221 | for commit in commit_list: | 235 | for commit in commit_list: |
| 222 | print(' %s' % commit) | 236 | print(' %s' % commit) |
| 223 | 237 | ||
| 224 | print('to %s (y/N)? ' % remote.review, end='') | 238 | print('to %s (y/N)? ' % remote.review, end='') |
| 225 | # TODO: When we require Python 3, use flush=True w/print above. | 239 | # TODO: When we require Python 3, use flush=True w/print above. |
| 226 | sys.stdout.flush() | 240 | sys.stdout.flush() |
| 227 | answer = sys.stdin.readline().strip().lower() | 241 | if opt.yes: |
| 228 | answer = answer in ('y', 'yes', '1', 'true', 't') | 242 | print('<--yes>') |
| 243 | answer = True | ||
| 244 | else: | ||
| 245 | answer = sys.stdin.readline().strip().lower() | ||
| 246 | answer = answer in ('y', 'yes', '1', 'true', 't') | ||
| 229 | 247 | ||
| 230 | if answer: | 248 | if answer: |
| 231 | if len(branch.commits) > UNUSUAL_COMMIT_THRESHOLD: | 249 | if len(branch.commits) > UNUSUAL_COMMIT_THRESHOLD: |
| @@ -322,12 +340,12 @@ Gerrit Code Review: https://www.gerritcodereview.com/ | |||
| 322 | 340 | ||
| 323 | key = 'review.%s.autoreviewer' % project.GetBranch(name).remote.review | 341 | key = 'review.%s.autoreviewer' % project.GetBranch(name).remote.review |
| 324 | raw_list = project.config.GetString(key) | 342 | raw_list = project.config.GetString(key) |
| 325 | if not raw_list is None: | 343 | if raw_list is not None: |
| 326 | people[0].extend([entry.strip() for entry in raw_list.split(',')]) | 344 | people[0].extend([entry.strip() for entry in raw_list.split(',')]) |
| 327 | 345 | ||
| 328 | key = 'review.%s.autocopy' % project.GetBranch(name).remote.review | 346 | key = 'review.%s.autocopy' % project.GetBranch(name).remote.review |
| 329 | raw_list = project.config.GetString(key) | 347 | raw_list = project.config.GetString(key) |
| 330 | if not raw_list is None and len(people[0]) > 0: | 348 | if raw_list is not None and len(people[0]) > 0: |
| 331 | people[1].extend([entry.strip() for entry in raw_list.split(',')]) | 349 | people[1].extend([entry.strip() for entry in raw_list.split(',')]) |
| 332 | 350 | ||
| 333 | def _FindGerritChange(self, branch): | 351 | def _FindGerritChange(self, branch): |
| @@ -364,7 +382,11 @@ Gerrit Code Review: https://www.gerritcodereview.com/ | |||
| 364 | print('Continue uploading? (y/N) ', end='') | 382 | print('Continue uploading? (y/N) ', end='') |
| 365 | # TODO: When we require Python 3, use flush=True w/print above. | 383 | # TODO: When we require Python 3, use flush=True w/print above. |
| 366 | sys.stdout.flush() | 384 | sys.stdout.flush() |
| 367 | a = sys.stdin.readline().strip().lower() | 385 | if opt.yes: |
| 386 | print('<--yes>') | ||
| 387 | a = 'yes' | ||
| 388 | else: | ||
| 389 | a = sys.stdin.readline().strip().lower() | ||
| 368 | if a not in ('y', 'yes', 't', 'true', 'on'): | 390 | if a not in ('y', 'yes', 't', 'true', 'on'): |
| 369 | print("skipping upload", file=sys.stderr) | 391 | print("skipping upload", file=sys.stderr) |
| 370 | branch.uploaded = False | 392 | branch.uploaded = False |
| @@ -376,12 +398,51 @@ Gerrit Code Review: https://www.gerritcodereview.com/ | |||
| 376 | key = 'review.%s.uploadtopic' % branch.project.remote.review | 398 | key = 'review.%s.uploadtopic' % branch.project.remote.review |
| 377 | opt.auto_topic = branch.project.config.GetBoolean(key) | 399 | opt.auto_topic = branch.project.config.GetBoolean(key) |
| 378 | 400 | ||
| 401 | def _ExpandCommaList(value): | ||
| 402 | """Split |value| up into comma delimited entries.""" | ||
| 403 | if not value: | ||
| 404 | return | ||
| 405 | for ret in value.split(','): | ||
| 406 | ret = ret.strip() | ||
| 407 | if ret: | ||
| 408 | yield ret | ||
| 409 | |||
| 410 | # Check if hashtags should be included. | ||
| 411 | key = 'review.%s.uploadhashtags' % branch.project.remote.review | ||
| 412 | hashtags = set(_ExpandCommaList(branch.project.config.GetString(key))) | ||
| 413 | for tag in opt.hashtags: | ||
| 414 | hashtags.update(_ExpandCommaList(tag)) | ||
| 415 | if opt.hashtag_branch: | ||
| 416 | hashtags.add(branch.name) | ||
| 417 | |||
| 418 | # Check if labels should be included. | ||
| 419 | key = 'review.%s.uploadlabels' % branch.project.remote.review | ||
| 420 | labels = set(_ExpandCommaList(branch.project.config.GetString(key))) | ||
| 421 | for label in opt.labels: | ||
| 422 | labels.update(_ExpandCommaList(label)) | ||
| 423 | # Basic sanity check on label syntax. | ||
| 424 | for label in labels: | ||
| 425 | if not re.match(r'^.+[+-][0-9]+$', label): | ||
| 426 | print('repo: error: invalid label syntax "%s": labels use forms ' | ||
| 427 | 'like CodeReview+1 or Verified-1' % (label,), file=sys.stderr) | ||
| 428 | sys.exit(1) | ||
| 429 | |||
| 430 | # Handle e-mail notifications. | ||
| 431 | if opt.notify is False: | ||
| 432 | notify = 'NONE' | ||
| 433 | else: | ||
| 434 | key = 'review.%s.uploadnotify' % branch.project.remote.review | ||
| 435 | notify = branch.project.config.GetString(key) | ||
| 436 | |||
| 379 | destination = opt.dest_branch or branch.project.dest_branch | 437 | destination = opt.dest_branch or branch.project.dest_branch |
| 380 | 438 | ||
| 381 | # Make sure our local branch is not setup to track a different remote branch | 439 | # Make sure our local branch is not setup to track a different remote branch |
| 382 | merge_branch = self._GetMergeBranch(branch.project) | 440 | merge_branch = self._GetMergeBranch(branch.project) |
| 383 | if destination: | 441 | if destination: |
| 384 | full_dest = 'refs/heads/%s' % destination | 442 | full_dest = destination |
| 443 | if not full_dest.startswith(R_HEADS): | ||
| 444 | full_dest = R_HEADS + full_dest | ||
| 445 | |||
| 385 | if not opt.dest_branch and merge_branch and merge_branch != full_dest: | 446 | if not opt.dest_branch and merge_branch and merge_branch != full_dest: |
| 386 | print('merge branch %s does not match destination branch %s' | 447 | print('merge branch %s does not match destination branch %s' |
| 387 | % (merge_branch, full_dest)) | 448 | % (merge_branch, full_dest)) |
| @@ -392,10 +453,12 @@ Gerrit Code Review: https://www.gerritcodereview.com/ | |||
| 392 | continue | 453 | continue |
| 393 | 454 | ||
| 394 | branch.UploadForReview(people, | 455 | branch.UploadForReview(people, |
| 456 | dryrun=opt.dryrun, | ||
| 395 | auto_topic=opt.auto_topic, | 457 | auto_topic=opt.auto_topic, |
| 396 | draft=opt.draft, | 458 | hashtags=hashtags, |
| 459 | labels=labels, | ||
| 397 | private=opt.private, | 460 | private=opt.private, |
| 398 | notify=None if opt.notify else 'NONE', | 461 | notify=notify, |
| 399 | wip=opt.wip, | 462 | wip=opt.wip, |
| 400 | dest_branch=destination, | 463 | dest_branch=destination, |
| 401 | validate_certs=opt.validate_certs, | 464 | validate_certs=opt.validate_certs, |
| @@ -418,18 +481,18 @@ Gerrit Code Review: https://www.gerritcodereview.com/ | |||
| 418 | else: | 481 | else: |
| 419 | fmt = '\n (%s)' | 482 | fmt = '\n (%s)' |
| 420 | print(('[FAILED] %-15s %-15s' + fmt) % ( | 483 | print(('[FAILED] %-15s %-15s' + fmt) % ( |
| 421 | branch.project.relpath + '/', \ | 484 | branch.project.relpath + '/', |
| 422 | branch.name, \ | 485 | branch.name, |
| 423 | str(branch.error)), | 486 | str(branch.error)), |
| 424 | file=sys.stderr) | 487 | file=sys.stderr) |
| 425 | print() | 488 | print() |
| 426 | 489 | ||
| 427 | for branch in todo: | 490 | for branch in todo: |
| 428 | if branch.uploaded: | 491 | if branch.uploaded: |
| 429 | print('[OK ] %-15s %s' % ( | 492 | print('[OK ] %-15s %s' % ( |
| 430 | branch.project.relpath + '/', | 493 | branch.project.relpath + '/', |
| 431 | branch.name), | 494 | branch.name), |
| 432 | file=sys.stderr) | 495 | file=sys.stderr) |
| 433 | 496 | ||
| 434 | if have_errors: | 497 | if have_errors: |
| 435 | sys.exit(1) | 498 | sys.exit(1) |
| @@ -437,68 +500,72 @@ Gerrit Code Review: https://www.gerritcodereview.com/ | |||
| 437 | def _GetMergeBranch(self, project): | 500 | def _GetMergeBranch(self, project): |
| 438 | p = GitCommand(project, | 501 | p = GitCommand(project, |
| 439 | ['rev-parse', '--abbrev-ref', 'HEAD'], | 502 | ['rev-parse', '--abbrev-ref', 'HEAD'], |
| 440 | capture_stdout = True, | 503 | capture_stdout=True, |
| 441 | capture_stderr = True) | 504 | capture_stderr=True) |
| 442 | p.Wait() | 505 | p.Wait() |
| 443 | local_branch = p.stdout.strip() | 506 | local_branch = p.stdout.strip() |
| 444 | p = GitCommand(project, | 507 | p = GitCommand(project, |
| 445 | ['config', '--get', 'branch.%s.merge' % local_branch], | 508 | ['config', '--get', 'branch.%s.merge' % local_branch], |
| 446 | capture_stdout = True, | 509 | capture_stdout=True, |
| 447 | capture_stderr = True) | 510 | capture_stderr=True) |
| 448 | p.Wait() | 511 | p.Wait() |
| 449 | merge_branch = p.stdout.strip() | 512 | merge_branch = p.stdout.strip() |
| 450 | return merge_branch | 513 | return merge_branch |
| 451 | 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 | |||
| 452 | def Execute(self, opt, args): | 526 | def Execute(self, opt, args): |
| 453 | project_list = self.GetProjects(args) | 527 | projects = self.GetProjects(args) |
| 454 | pending = [] | 528 | |
| 455 | reviewers = [] | 529 | def _ProcessResults(_pool, _out, results): |
| 456 | cc = [] | 530 | pending = [] |
| 457 | branch = None | 531 | for result in results: |
| 458 | 532 | project, avail = result | |
| 459 | if opt.branch: | 533 | if avail is None: |
| 460 | branch = opt.branch | 534 | print('repo: error: %s: Unable to upload branch "%s". ' |
| 461 | 535 | 'You might be able to fix the branch by running:\n' | |
| 462 | for project in project_list: | 536 | ' git branch --set-upstream-to m/%s' % |
| 463 | if opt.current_branch: | 537 | (project.relpath, project.CurrentBranch, self.manifest.branch), |
| 464 | cbr = project.CurrentBranch | ||
| 465 | up_branch = project.GetUploadableBranch(cbr) | ||
| 466 | if up_branch: | ||
| 467 | avail = [up_branch] | ||
| 468 | else: | ||
| 469 | avail = None | ||
| 470 | print('ERROR: Current branch (%s) not uploadable. ' | ||
| 471 | 'You may be able to type ' | ||
| 472 | '"git branch --set-upstream-to m/master" to fix ' | ||
| 473 | 'your branch.' % str(cbr), | ||
| 474 | file=sys.stderr) | 538 | file=sys.stderr) |
| 475 | else: | 539 | elif avail: |
| 476 | avail = project.GetUploadableBranches(branch) | 540 | pending.append(result) |
| 477 | if avail: | 541 | return pending |
| 478 | pending.append((project, avail)) | 542 | |
| 543 | pending = self.ExecuteInParallel( | ||
| 544 | opt.jobs, | ||
| 545 | functools.partial(self._GatherOne, opt), | ||
| 546 | projects, | ||
| 547 | callback=_ProcessResults) | ||
| 479 | 548 | ||
| 480 | if not pending: | 549 | if not pending: |
| 481 | print("no branches ready for upload", file=sys.stderr) | 550 | if opt.branch is None: |
| 482 | return | 551 | print('repo: error: no branches ready for upload', file=sys.stderr) |
| 483 | 552 | else: | |
| 484 | if not opt.bypass_hooks: | 553 | print('repo: error: no branches named "%s" ready for upload' % |
| 485 | hook = RepoHook('pre-upload', self.manifest.repo_hooks_project, | 554 | (opt.branch,), file=sys.stderr) |
| 486 | self.manifest.topdir, | 555 | return 1 |
| 487 | self.manifest.manifestProject.GetRemote('origin').url, | 556 | |
| 488 | abort_if_user_denies=True) | 557 | pending_proj_names = [project.name for (project, available) in pending] |
| 489 | pending_proj_names = [project.name for (project, available) in pending] | 558 | pending_worktrees = [project.worktree for (project, available) in pending] |
| 490 | pending_worktrees = [project.worktree for (project, available) in pending] | 559 | hook = RepoHook.FromSubcmd( |
| 491 | try: | 560 | hook_type='pre-upload', manifest=self.manifest, |
| 492 | hook.Run(opt.allow_all_hooks, project_list=pending_proj_names, | 561 | opt=opt, abort_if_user_denies=True) |
| 493 | worktree_list=pending_worktrees) | 562 | if not hook.Run( |
| 494 | except HookError as e: | 563 | project_list=pending_proj_names, |
| 495 | print("ERROR: %s" % str(e), file=sys.stderr) | 564 | worktree_list=pending_worktrees): |
| 496 | return | 565 | return 1 |
| 497 | 566 | ||
| 498 | if opt.reviewers: | 567 | reviewers = _SplitEmails(opt.reviewers) if opt.reviewers else [] |
| 499 | reviewers = _SplitEmails(opt.reviewers) | 568 | cc = _SplitEmails(opt.cc) if opt.cc else [] |
| 500 | if opt.cc: | ||
| 501 | cc = _SplitEmails(opt.cc) | ||
| 502 | people = (reviewers, cc) | 569 | people = (reviewers, cc) |
| 503 | 570 | ||
| 504 | if len(pending) == 1 and len(pending[0][1]) == 1: | 571 | if len(pending) == 1 and len(pending[0][1]) == 1: |
