| OLD | NEW |
| 1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
| 2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. | 2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 3 # Use of this source code is governed by a BSD-style license that can be | 3 # Use of this source code is governed by a BSD-style license that can be |
| 4 # found in the LICENSE file. | 4 # found in the LICENSE file. |
| 5 | 5 |
| 6 # Copyright (C) 2008 Evan Martin <martine@danga.com> | 6 # Copyright (C) 2008 Evan Martin <martine@danga.com> |
| 7 | 7 |
| 8 """A git-command for integrating reviews on Rietveld and Gerrit.""" | 8 """A git-command for integrating reviews on Rietveld and Gerrit.""" |
| 9 | 9 |
| 10 from __future__ import print_function |
| 11 |
| 10 from distutils.version import LooseVersion | 12 from distutils.version import LooseVersion |
| 11 from multiprocessing.pool import ThreadPool | 13 from multiprocessing.pool import ThreadPool |
| 12 import base64 | 14 import base64 |
| 13 import collections | 15 import collections |
| 14 import glob | 16 import glob |
| 15 import httplib | 17 import httplib |
| 16 import json | 18 import json |
| 17 import logging | 19 import logging |
| 18 import multiprocessing | 20 import multiprocessing |
| 19 import optparse | 21 import optparse |
| (...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 76 DEFAULT_LINT_IGNORE_REGEX = r"$^" | 78 DEFAULT_LINT_IGNORE_REGEX = r"$^" |
| 77 | 79 |
| 78 # Shortcut since it quickly becomes redundant. | 80 # Shortcut since it quickly becomes redundant. |
| 79 Fore = colorama.Fore | 81 Fore = colorama.Fore |
| 80 | 82 |
| 81 # Initialized in main() | 83 # Initialized in main() |
| 82 settings = None | 84 settings = None |
| 83 | 85 |
| 84 | 86 |
| 85 def DieWithError(message): | 87 def DieWithError(message): |
| 86 print >> sys.stderr, message | 88 print(message, file=sys.stderr) |
| 87 sys.exit(1) | 89 sys.exit(1) |
| 88 | 90 |
| 89 | 91 |
| 90 def GetNoGitPagerEnv(): | 92 def GetNoGitPagerEnv(): |
| 91 env = os.environ.copy() | 93 env = os.environ.copy() |
| 92 # 'cat' is a magical git string that disables pagers on all platforms. | 94 # 'cat' is a magical git string that disables pagers on all platforms. |
| 93 env['GIT_PAGER'] = 'cat' | 95 env['GIT_PAGER'] = 'cat' |
| 94 return env | 96 return env |
| 95 | 97 |
| 96 | 98 |
| (...skipping 262 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 359 _buildbucket_retry( | 361 _buildbucket_retry( |
| 360 'triggering tryjobs', | 362 'triggering tryjobs', |
| 361 http, | 363 http, |
| 362 buildbucket_put_url, | 364 buildbucket_put_url, |
| 363 'PUT', | 365 'PUT', |
| 364 body=json.dumps(batch_req_body), | 366 body=json.dumps(batch_req_body), |
| 365 headers={'Content-Type': 'application/json'} | 367 headers={'Content-Type': 'application/json'} |
| 366 ) | 368 ) |
| 367 print_text.append('To see results here, run: git cl try-results') | 369 print_text.append('To see results here, run: git cl try-results') |
| 368 print_text.append('To see results in browser, run: git cl web') | 370 print_text.append('To see results in browser, run: git cl web') |
| 369 print '\n'.join(print_text) | 371 print('\n'.join(print_text)) |
| 370 | 372 |
| 371 | 373 |
| 372 def fetch_try_jobs(auth_config, changelist, options): | 374 def fetch_try_jobs(auth_config, changelist, options): |
| 373 """Fetches tryjobs from buildbucket. | 375 """Fetches tryjobs from buildbucket. |
| 374 | 376 |
| 375 Returns a map from build id to build info as json dictionary. | 377 Returns a map from build id to build info as json dictionary. |
| 376 """ | 378 """ |
| 377 rietveld_url = settings.GetDefaultServerUrl() | 379 rietveld_url = settings.GetDefaultServerUrl() |
| 378 rietveld_host = urlparse.urlparse(rietveld_url).hostname | 380 rietveld_host = urlparse.urlparse(rietveld_url).hostname |
| 379 authenticator = auth.get_authenticator_for_host(rietveld_host, auth_config) | 381 authenticator = auth.get_authenticator_for_host(rietveld_host, auth_config) |
| 380 if authenticator.has_cached_credentials(): | 382 if authenticator.has_cached_credentials(): |
| 381 http = authenticator.authorize(httplib2.Http()) | 383 http = authenticator.authorize(httplib2.Http()) |
| 382 else: | 384 else: |
| 383 print ('Warning: Some results might be missing because %s' % | 385 print('Warning: Some results might be missing because %s' % |
| 384 # Get the message on how to login. | 386 # Get the message on how to login. |
| 385 auth.LoginRequiredError(rietveld_host).message) | 387 (auth.LoginRequiredError(rietveld_host).message,)) |
| 386 http = httplib2.Http() | 388 http = httplib2.Http() |
| 387 | 389 |
| 388 http.force_exception_to_status_code = True | 390 http.force_exception_to_status_code = True |
| 389 | 391 |
| 390 buildset = 'patch/rietveld/{hostname}/{issue}/{patch}'.format( | 392 buildset = 'patch/rietveld/{hostname}/{issue}/{patch}'.format( |
| 391 hostname=rietveld_host, | 393 hostname=rietveld_host, |
| 392 issue=changelist.GetIssue(), | 394 issue=changelist.GetIssue(), |
| 393 patch=options.patchset) | 395 patch=options.patchset) |
| 394 params = {'tag': 'buildset:%s' % buildset} | 396 params = {'tag': 'buildset:%s' % buildset} |
| 395 | 397 |
| 396 builds = {} | 398 builds = {} |
| 397 while True: | 399 while True: |
| 398 url = 'https://{hostname}/_ah/api/buildbucket/v1/search?{params}'.format( | 400 url = 'https://{hostname}/_ah/api/buildbucket/v1/search?{params}'.format( |
| 399 hostname=options.buildbucket_host, | 401 hostname=options.buildbucket_host, |
| 400 params=urllib.urlencode(params)) | 402 params=urllib.urlencode(params)) |
| 401 content = _buildbucket_retry('fetching tryjobs', http, url, 'GET') | 403 content = _buildbucket_retry('fetching tryjobs', http, url, 'GET') |
| 402 for build in content.get('builds', []): | 404 for build in content.get('builds', []): |
| 403 builds[build['id']] = build | 405 builds[build['id']] = build |
| 404 if 'next_cursor' in content: | 406 if 'next_cursor' in content: |
| 405 params['start_cursor'] = content['next_cursor'] | 407 params['start_cursor'] = content['next_cursor'] |
| 406 else: | 408 else: |
| 407 break | 409 break |
| 408 return builds | 410 return builds |
| 409 | 411 |
| 410 | 412 |
| 411 def print_tryjobs(options, builds): | 413 def print_tryjobs(options, builds): |
| 412 """Prints nicely result of fetch_try_jobs.""" | 414 """Prints nicely result of fetch_try_jobs.""" |
| 413 if not builds: | 415 if not builds: |
| 414 print 'No tryjobs scheduled' | 416 print('No tryjobs scheduled') |
| 415 return | 417 return |
| 416 | 418 |
| 417 # Make a copy, because we'll be modifying builds dictionary. | 419 # Make a copy, because we'll be modifying builds dictionary. |
| 418 builds = builds.copy() | 420 builds = builds.copy() |
| 419 builder_names_cache = {} | 421 builder_names_cache = {} |
| 420 | 422 |
| 421 def get_builder(b): | 423 def get_builder(b): |
| 422 try: | 424 try: |
| 423 return builder_names_cache[b['id']] | 425 return builder_names_cache[b['id']] |
| 424 except KeyError: | 426 except KeyError: |
| 425 try: | 427 try: |
| 426 parameters = json.loads(b['parameters_json']) | 428 parameters = json.loads(b['parameters_json']) |
| 427 name = parameters['builder_name'] | 429 name = parameters['builder_name'] |
| 428 except (ValueError, KeyError) as error: | 430 except (ValueError, KeyError) as error: |
| 429 print 'WARNING: failed to get builder name for build %s: %s' % ( | 431 print('WARNING: failed to get builder name for build %s: %s' % ( |
| 430 b['id'], error) | 432 b['id'], error)) |
| 431 name = None | 433 name = None |
| 432 builder_names_cache[b['id']] = name | 434 builder_names_cache[b['id']] = name |
| 433 return name | 435 return name |
| 434 | 436 |
| 435 def get_bucket(b): | 437 def get_bucket(b): |
| 436 bucket = b['bucket'] | 438 bucket = b['bucket'] |
| 437 if bucket.startswith('master.'): | 439 if bucket.startswith('master.'): |
| 438 return bucket[len('master.'):] | 440 return bucket[len('master.'):] |
| 439 return bucket | 441 return bucket |
| 440 | 442 |
| (...skipping 19 matching lines...) Expand all Loading... |
| 460 colorize = str | 462 colorize = str |
| 461 else: | 463 else: |
| 462 colorize = lambda x: '%s%s%s' % (color, x, Fore.RESET) | 464 colorize = lambda x: '%s%s%s' % (color, x, Fore.RESET) |
| 463 | 465 |
| 464 result = [] | 466 result = [] |
| 465 for b in builds.values(): | 467 for b in builds.values(): |
| 466 if all(b.get(k) == v for k, v in kwargs.iteritems()): | 468 if all(b.get(k) == v for k, v in kwargs.iteritems()): |
| 467 builds.pop(b['id']) | 469 builds.pop(b['id']) |
| 468 result.append(b) | 470 result.append(b) |
| 469 if result: | 471 if result: |
| 470 print colorize(title) | 472 print(colorize(title)) |
| 471 for b in sorted(result, key=sort_key): | 473 for b in sorted(result, key=sort_key): |
| 472 print ' ', colorize('\t'.join(map(str, f(b)))) | 474 print(' ', colorize('\t'.join(map(str, f(b))))) |
| 473 | 475 |
| 474 total = len(builds) | 476 total = len(builds) |
| 475 pop(status='COMPLETED', result='SUCCESS', | 477 pop(status='COMPLETED', result='SUCCESS', |
| 476 title='Successes:', color=Fore.GREEN, | 478 title='Successes:', color=Fore.GREEN, |
| 477 f=lambda b: (get_name(b), b.get('url'))) | 479 f=lambda b: (get_name(b), b.get('url'))) |
| 478 pop(status='COMPLETED', result='FAILURE', failure_reason='INFRA_FAILURE', | 480 pop(status='COMPLETED', result='FAILURE', failure_reason='INFRA_FAILURE', |
| 479 title='Infra Failures:', color=Fore.MAGENTA, | 481 title='Infra Failures:', color=Fore.MAGENTA, |
| 480 f=lambda b: (get_name(b), b.get('url'))) | 482 f=lambda b: (get_name(b), b.get('url'))) |
| 481 pop(status='COMPLETED', result='FAILURE', failure_reason='BUILD_FAILURE', | 483 pop(status='COMPLETED', result='FAILURE', failure_reason='BUILD_FAILURE', |
| 482 title='Failures:', color=Fore.RED, | 484 title='Failures:', color=Fore.RED, |
| (...skipping 14 matching lines...) Expand all Loading... |
| 497 pop(status='STARTED', | 499 pop(status='STARTED', |
| 498 title='Started:', color=Fore.YELLOW, | 500 title='Started:', color=Fore.YELLOW, |
| 499 f=lambda b: (get_name(b), b.get('url'))) | 501 f=lambda b: (get_name(b), b.get('url'))) |
| 500 pop(status='SCHEDULED', | 502 pop(status='SCHEDULED', |
| 501 title='Scheduled:', | 503 title='Scheduled:', |
| 502 f=lambda b: (get_name(b), 'id=%s' % b['id'])) | 504 f=lambda b: (get_name(b), 'id=%s' % b['id'])) |
| 503 # The last section is just in case buildbucket API changes OR there is a bug. | 505 # The last section is just in case buildbucket API changes OR there is a bug. |
| 504 pop(title='Other:', | 506 pop(title='Other:', |
| 505 f=lambda b: (get_name(b), 'id=%s' % b['id'])) | 507 f=lambda b: (get_name(b), 'id=%s' % b['id'])) |
| 506 assert len(builds) == 0 | 508 assert len(builds) == 0 |
| 507 print 'Total: %d tryjobs' % total | 509 print('Total: %d tryjobs' % total) |
| 508 | 510 |
| 509 | 511 |
| 510 def MatchSvnGlob(url, base_url, glob_spec, allow_wildcards): | 512 def MatchSvnGlob(url, base_url, glob_spec, allow_wildcards): |
| 511 """Return the corresponding git ref if |base_url| together with |glob_spec| | 513 """Return the corresponding git ref if |base_url| together with |glob_spec| |
| 512 matches the full |url|. | 514 matches the full |url|. |
| 513 | 515 |
| 514 If |allow_wildcards| is true, |glob_spec| can contain wildcards (see below). | 516 If |allow_wildcards| is true, |glob_spec| can contain wildcards (see below). |
| 515 """ | 517 """ |
| 516 fetch_suburl, as_ref = glob_spec.split(':') | 518 fetch_suburl, as_ref = glob_spec.split(':') |
| 517 if allow_wildcards: | 519 if allow_wildcards: |
| (...skipping 596 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1114 self._remote = (remote, branch.replace('refs/', 'refs/remotes/')) | 1116 self._remote = (remote, branch.replace('refs/', 'refs/remotes/')) |
| 1115 else: | 1117 else: |
| 1116 self._remote = (remote, 'refs/remotes/%s/%s' % (remote, branch)) | 1118 self._remote = (remote, 'refs/remotes/%s/%s' % (remote, branch)) |
| 1117 return self._remote | 1119 return self._remote |
| 1118 | 1120 |
| 1119 def GitSanityChecks(self, upstream_git_obj): | 1121 def GitSanityChecks(self, upstream_git_obj): |
| 1120 """Checks git repo status and ensures diff is from local commits.""" | 1122 """Checks git repo status and ensures diff is from local commits.""" |
| 1121 | 1123 |
| 1122 if upstream_git_obj is None: | 1124 if upstream_git_obj is None: |
| 1123 if self.GetBranch() is None: | 1125 if self.GetBranch() is None: |
| 1124 print >> sys.stderr, ( | 1126 print('ERROR: unable to determine current branch (detached HEAD?)', |
| 1125 'ERROR: unable to determine current branch (detached HEAD?)') | 1127 file=sys.stderr) |
| 1126 else: | 1128 else: |
| 1127 print >> sys.stderr, ( | 1129 print('ERROR: no upstream branch', file=sys.stderr) |
| 1128 'ERROR: no upstream branch') | |
| 1129 return False | 1130 return False |
| 1130 | 1131 |
| 1131 # Verify the commit we're diffing against is in our current branch. | 1132 # Verify the commit we're diffing against is in our current branch. |
| 1132 upstream_sha = RunGit(['rev-parse', '--verify', upstream_git_obj]).strip() | 1133 upstream_sha = RunGit(['rev-parse', '--verify', upstream_git_obj]).strip() |
| 1133 common_ancestor = RunGit(['merge-base', upstream_sha, 'HEAD']).strip() | 1134 common_ancestor = RunGit(['merge-base', upstream_sha, 'HEAD']).strip() |
| 1134 if upstream_sha != common_ancestor: | 1135 if upstream_sha != common_ancestor: |
| 1135 print >> sys.stderr, ( | 1136 print('ERROR: %s is not in the current branch. You may need to rebase ' |
| 1136 'ERROR: %s is not in the current branch. You may need to rebase ' | 1137 'your tracking branch' % upstream_sha, file=sys.stderr) |
| 1137 'your tracking branch' % upstream_sha) | |
| 1138 return False | 1138 return False |
| 1139 | 1139 |
| 1140 # List the commits inside the diff, and verify they are all local. | 1140 # List the commits inside the diff, and verify they are all local. |
| 1141 commits_in_diff = RunGit( | 1141 commits_in_diff = RunGit( |
| 1142 ['rev-list', '^%s' % upstream_sha, 'HEAD']).splitlines() | 1142 ['rev-list', '^%s' % upstream_sha, 'HEAD']).splitlines() |
| 1143 code, remote_branch = RunGitWithCode(['config', 'gitcl.remotebranch']) | 1143 code, remote_branch = RunGitWithCode(['config', 'gitcl.remotebranch']) |
| 1144 remote_branch = remote_branch.strip() | 1144 remote_branch = remote_branch.strip() |
| 1145 if code != 0: | 1145 if code != 0: |
| 1146 _, remote_branch = self.GetRemoteBranch() | 1146 _, remote_branch = self.GetRemoteBranch() |
| 1147 | 1147 |
| 1148 commits_in_remote = RunGit( | 1148 commits_in_remote = RunGit( |
| 1149 ['rev-list', '^%s' % upstream_sha, remote_branch]).splitlines() | 1149 ['rev-list', '^%s' % upstream_sha, remote_branch]).splitlines() |
| 1150 | 1150 |
| 1151 common_commits = set(commits_in_diff) & set(commits_in_remote) | 1151 common_commits = set(commits_in_diff) & set(commits_in_remote) |
| 1152 if common_commits: | 1152 if common_commits: |
| 1153 print >> sys.stderr, ( | 1153 print('ERROR: Your diff contains %d commits already in %s.\n' |
| 1154 'ERROR: Your diff contains %d commits already in %s.\n' | 1154 'Run "git log --oneline %s..HEAD" to get a list of commits in ' |
| 1155 'Run "git log --oneline %s..HEAD" to get a list of commits in ' | 1155 'the diff. If you are using a custom git flow, you can override' |
| 1156 'the diff. If you are using a custom git flow, you can override' | 1156 ' the reference used for this check with "git config ' |
| 1157 ' the reference used for this check with "git config ' | 1157 'gitcl.remotebranch <git-ref>".' % ( |
| 1158 'gitcl.remotebranch <git-ref>".' % ( | 1158 len(common_commits), remote_branch, upstream_git_obj), |
| 1159 len(common_commits), remote_branch, upstream_git_obj)) | 1159 file=sys.stderr) |
| 1160 return False | 1160 return False |
| 1161 return True | 1161 return True |
| 1162 | 1162 |
| 1163 def GetGitBaseUrlFromConfig(self): | 1163 def GetGitBaseUrlFromConfig(self): |
| 1164 """Return the configured base URL from branch.<branchname>.baseurl. | 1164 """Return the configured base URL from branch.<branchname>.baseurl. |
| 1165 | 1165 |
| 1166 Returns None if it is not set. | 1166 Returns None if it is not set. |
| 1167 """ | 1167 """ |
| 1168 return RunGit(['config', 'branch.%s.base-url' % self.GetBranch()], | 1168 return RunGit(['config', 'branch.%s.base-url' % self.GetBranch()], |
| 1169 error_ok=True).strip() | 1169 error_ok=True).strip() |
| (...skipping 212 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1382 if not hook_results.should_continue(): | 1382 if not hook_results.should_continue(): |
| 1383 return 1 | 1383 return 1 |
| 1384 if not options.reviewers and hook_results.reviewers: | 1384 if not options.reviewers and hook_results.reviewers: |
| 1385 options.reviewers = hook_results.reviewers.split(',') | 1385 options.reviewers = hook_results.reviewers.split(',') |
| 1386 | 1386 |
| 1387 if self.GetIssue(): | 1387 if self.GetIssue(): |
| 1388 latest_patchset = self.GetMostRecentPatchset() | 1388 latest_patchset = self.GetMostRecentPatchset() |
| 1389 local_patchset = self.GetPatchset() | 1389 local_patchset = self.GetPatchset() |
| 1390 if (latest_patchset and local_patchset and | 1390 if (latest_patchset and local_patchset and |
| 1391 local_patchset != latest_patchset): | 1391 local_patchset != latest_patchset): |
| 1392 print ('The last upload made from this repository was patchset #%d but ' | 1392 print('The last upload made from this repository was patchset #%d but ' |
| 1393 'the most recent patchset on the server is #%d.' | 1393 'the most recent patchset on the server is #%d.' |
| 1394 % (local_patchset, latest_patchset)) | 1394 % (local_patchset, latest_patchset)) |
| 1395 print ('Uploading will still work, but if you\'ve uploaded to this ' | 1395 print('Uploading will still work, but if you\'ve uploaded to this ' |
| 1396 'issue from another machine or branch the patch you\'re ' | 1396 'issue from another machine or branch the patch you\'re ' |
| 1397 'uploading now might not include those changes.') | 1397 'uploading now might not include those changes.') |
| 1398 ask_for_data('About to upload; enter to confirm.') | 1398 ask_for_data('About to upload; enter to confirm.') |
| 1399 | 1399 |
| 1400 print_stats(options.similarity, options.find_copies, git_diff_args) | 1400 print_stats(options.similarity, options.find_copies, git_diff_args) |
| 1401 ret = self.CMDUploadChange(options, git_diff_args, change) | 1401 ret = self.CMDUploadChange(options, git_diff_args, change) |
| 1402 if not ret: | 1402 if not ret: |
| 1403 git_set_branch_value('last-upload-hash', | 1403 git_set_branch_value('last-upload-hash', |
| 1404 RunGit(['rev-parse', 'HEAD']).strip()) | 1404 RunGit(['rev-parse', 'HEAD']).strip()) |
| 1405 # Run post upload hooks, if specified. | 1405 # Run post upload hooks, if specified. |
| 1406 if settings.GetRunPostUploadHook(): | 1406 if settings.GetRunPostUploadHook(): |
| 1407 presubmit_support.DoPostUploadExecuter( | 1407 presubmit_support.DoPostUploadExecuter( |
| 1408 change, | 1408 change, |
| 1409 self, | 1409 self, |
| 1410 settings.GetRoot(), | 1410 settings.GetRoot(), |
| 1411 options.verbose, | 1411 options.verbose, |
| 1412 sys.stdout) | 1412 sys.stdout) |
| 1413 | 1413 |
| 1414 # Upload all dependencies if specified. | 1414 # Upload all dependencies if specified. |
| 1415 if options.dependencies: | 1415 if options.dependencies: |
| 1416 print | 1416 print() |
| 1417 print '--dependencies has been specified.' | 1417 print('--dependencies has been specified.') |
| 1418 print 'All dependent local branches will be re-uploaded.' | 1418 print('All dependent local branches will be re-uploaded.') |
| 1419 print | 1419 print() |
| 1420 # Remove the dependencies flag from args so that we do not end up in a | 1420 # Remove the dependencies flag from args so that we do not end up in a |
| 1421 # loop. | 1421 # loop. |
| 1422 orig_args.remove('--dependencies') | 1422 orig_args.remove('--dependencies') |
| 1423 ret = upload_branch_deps(self, orig_args) | 1423 ret = upload_branch_deps(self, orig_args) |
| 1424 return ret | 1424 return ret |
| 1425 | 1425 |
| 1426 def SetCQState(self, new_state): | 1426 def SetCQState(self, new_state): |
| 1427 """Update the CQ state for latest patchset. | 1427 """Update the CQ state for latest patchset. |
| 1428 | 1428 |
| 1429 Issue must have been already uploaded and known. | 1429 Issue must have been already uploaded and known. |
| (...skipping 188 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1618 'error. It is likely that you deleted this ' | 1618 'error. It is likely that you deleted this ' |
| 1619 'issue on the server. If this is the\n' | 1619 'issue on the server. If this is the\n' |
| 1620 'case, please run\n\n' | 1620 'case, please run\n\n' |
| 1621 ' git cl issue 0\n\n' | 1621 ' git cl issue 0\n\n' |
| 1622 'to clear the association with the deleted issue. Then run ' | 1622 'to clear the association with the deleted issue. Then run ' |
| 1623 'this command again.') % issue) | 1623 'this command again.') % issue) |
| 1624 else: | 1624 else: |
| 1625 DieWithError( | 1625 DieWithError( |
| 1626 '\nFailed to fetch issue description. HTTP error %d' % e.code) | 1626 '\nFailed to fetch issue description. HTTP error %d' % e.code) |
| 1627 except urllib2.URLError as e: | 1627 except urllib2.URLError as e: |
| 1628 print >> sys.stderr, ( | 1628 print('Warning: Failed to retrieve CL description due to network ' |
| 1629 'Warning: Failed to retrieve CL description due to network ' | 1629 'failure.', file=sys.stderr) |
| 1630 'failure.') | |
| 1631 return '' | 1630 return '' |
| 1632 | 1631 |
| 1633 def GetMostRecentPatchset(self): | 1632 def GetMostRecentPatchset(self): |
| 1634 return self.GetIssueProperties()['patchsets'][-1] | 1633 return self.GetIssueProperties()['patchsets'][-1] |
| 1635 | 1634 |
| 1636 def GetPatchSetDiff(self, issue, patchset): | 1635 def GetPatchSetDiff(self, issue, patchset): |
| 1637 return self.RpcServer().get( | 1636 return self.RpcServer().get( |
| 1638 '/download/issue%s_%s.diff' % (issue, patchset)) | 1637 '/download/issue%s_%s.diff' % (issue, patchset)) |
| 1639 | 1638 |
| 1640 def GetIssueProperties(self): | 1639 def GetIssueProperties(self): |
| (...skipping 171 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1812 if directory: | 1811 if directory: |
| 1813 cmd.extend(('--directory', directory)) | 1812 cmd.extend(('--directory', directory)) |
| 1814 if reject: | 1813 if reject: |
| 1815 cmd.append('--reject') | 1814 cmd.append('--reject') |
| 1816 elif IsGitVersionAtLeast('1.7.12'): | 1815 elif IsGitVersionAtLeast('1.7.12'): |
| 1817 cmd.append('--3way') | 1816 cmd.append('--3way') |
| 1818 try: | 1817 try: |
| 1819 subprocess2.check_call(cmd, env=GetNoGitPagerEnv(), | 1818 subprocess2.check_call(cmd, env=GetNoGitPagerEnv(), |
| 1820 stdin=patch_data, stdout=subprocess2.VOID) | 1819 stdin=patch_data, stdout=subprocess2.VOID) |
| 1821 except subprocess2.CalledProcessError: | 1820 except subprocess2.CalledProcessError: |
| 1822 print 'Failed to apply the patch' | 1821 print('Failed to apply the patch') |
| 1823 return 1 | 1822 return 1 |
| 1824 | 1823 |
| 1825 # If we had an issue, commit the current state and register the issue. | 1824 # If we had an issue, commit the current state and register the issue. |
| 1826 if not nocommit: | 1825 if not nocommit: |
| 1827 RunGit(['commit', '-m', (self.GetDescription() + '\n\n' + | 1826 RunGit(['commit', '-m', (self.GetDescription() + '\n\n' + |
| 1828 'patch from issue %(i)s at patchset ' | 1827 'patch from issue %(i)s at patchset ' |
| 1829 '%(p)s (http://crrev.com/%(i)s#ps%(p)s)' | 1828 '%(p)s (http://crrev.com/%(i)s#ps%(p)s)' |
| 1830 % {'i': self.GetIssue(), 'p': patchset})]) | 1829 % {'i': self.GetIssue(), 'p': patchset})]) |
| 1831 self.SetIssue(self.GetIssue()) | 1830 self.SetIssue(self.GetIssue()) |
| 1832 self.SetPatchset(patchset) | 1831 self.SetPatchset(patchset) |
| 1833 print "Committed patch locally." | 1832 print('Committed patch locally.') |
| 1834 else: | 1833 else: |
| 1835 print "Patch applied to index." | 1834 print('Patch applied to index.') |
| 1836 return 0 | 1835 return 0 |
| 1837 | 1836 |
| 1838 @staticmethod | 1837 @staticmethod |
| 1839 def ParseIssueURL(parsed_url): | 1838 def ParseIssueURL(parsed_url): |
| 1840 if not parsed_url.scheme or not parsed_url.scheme.startswith('http'): | 1839 if not parsed_url.scheme or not parsed_url.scheme.startswith('http'): |
| 1841 return None | 1840 return None |
| 1842 # Typical url: https://domain/<issue_number>[/[other]] | 1841 # Typical url: https://domain/<issue_number>[/[other]] |
| 1843 match = re.match('/(\d+)(/.*)?$', parsed_url.path) | 1842 match = re.match('/(\d+)(/.*)?$', parsed_url.path) |
| 1844 if match: | 1843 if match: |
| 1845 return _RietveldParsedIssueNumberArgument( | 1844 return _RietveldParsedIssueNumberArgument( |
| (...skipping 21 matching lines...) Expand all Loading... |
| 1867 | 1866 |
| 1868 if options.email is not None: | 1867 if options.email is not None: |
| 1869 upload_args.extend(['--email', options.email]) | 1868 upload_args.extend(['--email', options.email]) |
| 1870 | 1869 |
| 1871 if self.GetIssue(): | 1870 if self.GetIssue(): |
| 1872 if options.title: | 1871 if options.title: |
| 1873 upload_args.extend(['--title', options.title]) | 1872 upload_args.extend(['--title', options.title]) |
| 1874 if options.message: | 1873 if options.message: |
| 1875 upload_args.extend(['--message', options.message]) | 1874 upload_args.extend(['--message', options.message]) |
| 1876 upload_args.extend(['--issue', str(self.GetIssue())]) | 1875 upload_args.extend(['--issue', str(self.GetIssue())]) |
| 1877 print ('This branch is associated with issue %s. ' | 1876 print('This branch is associated with issue %s. ' |
| 1878 'Adding patch to that issue.' % self.GetIssue()) | 1877 'Adding patch to that issue.' % self.GetIssue()) |
| 1879 else: | 1878 else: |
| 1880 if options.title: | 1879 if options.title: |
| 1881 upload_args.extend(['--title', options.title]) | 1880 upload_args.extend(['--title', options.title]) |
| 1882 message = (options.title or options.message or | 1881 message = (options.title or options.message or |
| 1883 CreateDescriptionFromLog(args)) | 1882 CreateDescriptionFromLog(args)) |
| 1884 change_desc = ChangeDescription(message) | 1883 change_desc = ChangeDescription(message) |
| 1885 if options.reviewers or options.tbr_owners: | 1884 if options.reviewers or options.tbr_owners: |
| 1886 change_desc.update_reviewers(options.reviewers, | 1885 change_desc.update_reviewers(options.reviewers, |
| 1887 options.tbr_owners, | 1886 options.tbr_owners, |
| 1888 change) | 1887 change) |
| 1889 if not options.force: | 1888 if not options.force: |
| 1890 change_desc.prompt() | 1889 change_desc.prompt() |
| 1891 | 1890 |
| 1892 if not change_desc.description: | 1891 if not change_desc.description: |
| 1893 print "Description is empty; aborting." | 1892 print('Description is empty; aborting.') |
| 1894 return 1 | 1893 return 1 |
| 1895 | 1894 |
| 1896 upload_args.extend(['--message', change_desc.description]) | 1895 upload_args.extend(['--message', change_desc.description]) |
| 1897 if change_desc.get_reviewers(): | 1896 if change_desc.get_reviewers(): |
| 1898 upload_args.append('--reviewers=%s' % ','.join( | 1897 upload_args.append('--reviewers=%s' % ','.join( |
| 1899 change_desc.get_reviewers())) | 1898 change_desc.get_reviewers())) |
| 1900 if options.send_mail: | 1899 if options.send_mail: |
| 1901 if not change_desc.get_reviewers(): | 1900 if not change_desc.get_reviewers(): |
| 1902 DieWithError("Must specify reviewers to send email.") | 1901 DieWithError("Must specify reviewers to send email.") |
| 1903 upload_args.append('--send_mail') | 1902 upload_args.append('--send_mail') |
| (...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1941 if target_ref: | 1940 if target_ref: |
| 1942 upload_args.extend(['--target_ref', target_ref]) | 1941 upload_args.extend(['--target_ref', target_ref]) |
| 1943 | 1942 |
| 1944 # Look for dependent patchsets. See crbug.com/480453 for more details. | 1943 # Look for dependent patchsets. See crbug.com/480453 for more details. |
| 1945 remote, upstream_branch = self.FetchUpstreamTuple(self.GetBranch()) | 1944 remote, upstream_branch = self.FetchUpstreamTuple(self.GetBranch()) |
| 1946 upstream_branch = ShortBranchName(upstream_branch) | 1945 upstream_branch = ShortBranchName(upstream_branch) |
| 1947 if remote is '.': | 1946 if remote is '.': |
| 1948 # A local branch is being tracked. | 1947 # A local branch is being tracked. |
| 1949 local_branch = upstream_branch | 1948 local_branch = upstream_branch |
| 1950 if settings.GetIsSkipDependencyUpload(local_branch): | 1949 if settings.GetIsSkipDependencyUpload(local_branch): |
| 1951 print | 1950 print() |
| 1952 print ('Skipping dependency patchset upload because git config ' | 1951 print('Skipping dependency patchset upload because git config ' |
| 1953 'branch.%s.skip-deps-uploads is set to True.' % local_branch) | 1952 'branch.%s.skip-deps-uploads is set to True.' % local_branch) |
| 1954 print | 1953 print() |
| 1955 else: | 1954 else: |
| 1956 auth_config = auth.extract_auth_config_from_options(options) | 1955 auth_config = auth.extract_auth_config_from_options(options) |
| 1957 branch_cl = Changelist(branchref='refs/heads/'+local_branch, | 1956 branch_cl = Changelist(branchref='refs/heads/'+local_branch, |
| 1958 auth_config=auth_config) | 1957 auth_config=auth_config) |
| 1959 branch_cl_issue_url = branch_cl.GetIssueURL() | 1958 branch_cl_issue_url = branch_cl.GetIssueURL() |
| 1960 branch_cl_issue = branch_cl.GetIssue() | 1959 branch_cl_issue = branch_cl.GetIssue() |
| 1961 branch_cl_patchset = branch_cl.GetPatchset() | 1960 branch_cl_patchset = branch_cl.GetPatchset() |
| 1962 if branch_cl_issue_url and branch_cl_issue and branch_cl_patchset: | 1961 if branch_cl_issue_url and branch_cl_issue and branch_cl_patchset: |
| 1963 upload_args.extend( | 1962 upload_args.extend( |
| 1964 ['--depends_on_patchset', '%s:%s' % ( | 1963 ['--depends_on_patchset', '%s:%s' % ( |
| (...skipping 604 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2569 | 2568 |
| 2570 def _AddChangeIdToCommitMessage(self, options, args): | 2569 def _AddChangeIdToCommitMessage(self, options, args): |
| 2571 """Re-commits using the current message, assumes the commit hook is in | 2570 """Re-commits using the current message, assumes the commit hook is in |
| 2572 place. | 2571 place. |
| 2573 """ | 2572 """ |
| 2574 log_desc = options.message or CreateDescriptionFromLog(args) | 2573 log_desc = options.message or CreateDescriptionFromLog(args) |
| 2575 git_command = ['commit', '--amend', '-m', log_desc] | 2574 git_command = ['commit', '--amend', '-m', log_desc] |
| 2576 RunGit(git_command) | 2575 RunGit(git_command) |
| 2577 new_log_desc = CreateDescriptionFromLog(args) | 2576 new_log_desc = CreateDescriptionFromLog(args) |
| 2578 if git_footers.get_footer_change_id(new_log_desc): | 2577 if git_footers.get_footer_change_id(new_log_desc): |
| 2579 print 'git-cl: Added Change-Id to commit message.' | 2578 print('git-cl: Added Change-Id to commit message.') |
| 2580 return new_log_desc | 2579 return new_log_desc |
| 2581 else: | 2580 else: |
| 2582 DieWithError('ERROR: Gerrit commit-msg hook not installed.') | 2581 DieWithError('ERROR: Gerrit commit-msg hook not installed.') |
| 2583 | 2582 |
| 2584 def SetCQState(self, new_state): | 2583 def SetCQState(self, new_state): |
| 2585 """Sets the Commit-Queue label assuming canonical CQ config for Gerrit.""" | 2584 """Sets the Commit-Queue label assuming canonical CQ config for Gerrit.""" |
| 2586 # TODO(tandrii): maybe allow configurability in codereview.settings or by | 2585 # TODO(tandrii): maybe allow configurability in codereview.settings or by |
| 2587 # self-discovery of label config for this CL using REST API. | 2586 # self-discovery of label config for this CL using REST API. |
| 2588 vote_map = { | 2587 vote_map = { |
| 2589 _CQState.NONE: 0, | 2588 _CQState.NONE: 0, |
| (...skipping 383 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2973 LoadCodereviewSettingsFromFile(urllib2.urlopen(url)) | 2972 LoadCodereviewSettingsFromFile(urllib2.urlopen(url)) |
| 2974 return 0 | 2973 return 0 |
| 2975 | 2974 |
| 2976 | 2975 |
| 2977 def CMDbaseurl(parser, args): | 2976 def CMDbaseurl(parser, args): |
| 2978 """Gets or sets base-url for this branch.""" | 2977 """Gets or sets base-url for this branch.""" |
| 2979 branchref = RunGit(['symbolic-ref', 'HEAD']).strip() | 2978 branchref = RunGit(['symbolic-ref', 'HEAD']).strip() |
| 2980 branch = ShortBranchName(branchref) | 2979 branch = ShortBranchName(branchref) |
| 2981 _, args = parser.parse_args(args) | 2980 _, args = parser.parse_args(args) |
| 2982 if not args: | 2981 if not args: |
| 2983 print("Current base-url:") | 2982 print('Current base-url:') |
| 2984 return RunGit(['config', 'branch.%s.base-url' % branch], | 2983 return RunGit(['config', 'branch.%s.base-url' % branch], |
| 2985 error_ok=False).strip() | 2984 error_ok=False).strip() |
| 2986 else: | 2985 else: |
| 2987 print("Setting base-url to %s" % args[0]) | 2986 print('Setting base-url to %s' % args[0]) |
| 2988 return RunGit(['config', 'branch.%s.base-url' % branch, args[0]], | 2987 return RunGit(['config', 'branch.%s.base-url' % branch, args[0]], |
| 2989 error_ok=False).strip() | 2988 error_ok=False).strip() |
| 2990 | 2989 |
| 2991 | 2990 |
| 2992 def color_for_status(status): | 2991 def color_for_status(status): |
| 2993 """Maps a Changelist status to color, for CMDstatus and other tools.""" | 2992 """Maps a Changelist status to color, for CMDstatus and other tools.""" |
| 2994 return { | 2993 return { |
| 2995 'unsent': Fore.RED, | 2994 'unsent': Fore.RED, |
| 2996 'waiting': Fore.BLUE, | 2995 'waiting': Fore.BLUE, |
| 2997 'reply': Fore.YELLOW, | 2996 'reply': Fore.YELLOW, |
| (...skipping 94 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 3092 | 3091 |
| 3093 # Create a dictionary of all local branches to the branches that are dependent | 3092 # Create a dictionary of all local branches to the branches that are dependent |
| 3094 # on it. | 3093 # on it. |
| 3095 tracked_to_dependents = collections.defaultdict(list) | 3094 tracked_to_dependents = collections.defaultdict(list) |
| 3096 for b in branches.splitlines(): | 3095 for b in branches.splitlines(): |
| 3097 tokens = b.split() | 3096 tokens = b.split() |
| 3098 if len(tokens) == 2: | 3097 if len(tokens) == 2: |
| 3099 branch_name, tracked = tokens | 3098 branch_name, tracked = tokens |
| 3100 tracked_to_dependents[tracked].append(branch_name) | 3099 tracked_to_dependents[tracked].append(branch_name) |
| 3101 | 3100 |
| 3102 print | 3101 print() |
| 3103 print 'The dependent local branches of %s are:' % root_branch | 3102 print('The dependent local branches of %s are:' % root_branch) |
| 3104 dependents = [] | 3103 dependents = [] |
| 3105 def traverse_dependents_preorder(branch, padding=''): | 3104 def traverse_dependents_preorder(branch, padding=''): |
| 3106 dependents_to_process = tracked_to_dependents.get(branch, []) | 3105 dependents_to_process = tracked_to_dependents.get(branch, []) |
| 3107 padding += ' ' | 3106 padding += ' ' |
| 3108 for dependent in dependents_to_process: | 3107 for dependent in dependents_to_process: |
| 3109 print '%s%s' % (padding, dependent) | 3108 print('%s%s' % (padding, dependent)) |
| 3110 dependents.append(dependent) | 3109 dependents.append(dependent) |
| 3111 traverse_dependents_preorder(dependent, padding) | 3110 traverse_dependents_preorder(dependent, padding) |
| 3112 traverse_dependents_preorder(root_branch) | 3111 traverse_dependents_preorder(root_branch) |
| 3113 print | 3112 print() |
| 3114 | 3113 |
| 3115 if not dependents: | 3114 if not dependents: |
| 3116 print 'There are no dependent local branches for %s' % root_branch | 3115 print('There are no dependent local branches for %s' % root_branch) |
| 3117 return 0 | 3116 return 0 |
| 3118 | 3117 |
| 3119 print ('This command will checkout all dependent branches and run ' | 3118 print('This command will checkout all dependent branches and run ' |
| 3120 '"git cl upload".') | 3119 '"git cl upload".') |
| 3121 ask_for_data('[Press enter to continue or ctrl-C to quit]') | 3120 ask_for_data('[Press enter to continue or ctrl-C to quit]') |
| 3122 | 3121 |
| 3123 # Add a default patchset title to all upload calls in Rietveld. | 3122 # Add a default patchset title to all upload calls in Rietveld. |
| 3124 if not cl.IsGerrit(): | 3123 if not cl.IsGerrit(): |
| 3125 args.extend(['-t', 'Updated patchset dependency']) | 3124 args.extend(['-t', 'Updated patchset dependency']) |
| 3126 | 3125 |
| 3127 # Record all dependents that failed to upload. | 3126 # Record all dependents that failed to upload. |
| 3128 failures = {} | 3127 failures = {} |
| 3129 # Go through all dependents, checkout the branch and upload. | 3128 # Go through all dependents, checkout the branch and upload. |
| 3130 try: | 3129 try: |
| 3131 for dependent_branch in dependents: | 3130 for dependent_branch in dependents: |
| 3132 print | 3131 print() |
| 3133 print '--------------------------------------' | 3132 print('--------------------------------------') |
| 3134 print 'Running "git cl upload" from %s:' % dependent_branch | 3133 print('Running "git cl upload" from %s:' % dependent_branch) |
| 3135 RunGit(['checkout', '-q', dependent_branch]) | 3134 RunGit(['checkout', '-q', dependent_branch]) |
| 3136 print | 3135 print() |
| 3137 try: | 3136 try: |
| 3138 if CMDupload(OptionParser(), args) != 0: | 3137 if CMDupload(OptionParser(), args) != 0: |
| 3139 print 'Upload failed for %s!' % dependent_branch | 3138 print('Upload failed for %s!' % dependent_branch) |
| 3140 failures[dependent_branch] = 1 | 3139 failures[dependent_branch] = 1 |
| 3141 except: # pylint: disable=W0702 | 3140 except: # pylint: disable=W0702 |
| 3142 failures[dependent_branch] = 1 | 3141 failures[dependent_branch] = 1 |
| 3143 print | 3142 print() |
| 3144 finally: | 3143 finally: |
| 3145 # Swap back to the original root branch. | 3144 # Swap back to the original root branch. |
| 3146 RunGit(['checkout', '-q', root_branch]) | 3145 RunGit(['checkout', '-q', root_branch]) |
| 3147 | 3146 |
| 3148 print | 3147 print() |
| 3149 print 'Upload complete for dependent branches!' | 3148 print('Upload complete for dependent branches!') |
| 3150 for dependent_branch in dependents: | 3149 for dependent_branch in dependents: |
| 3151 upload_status = 'failed' if failures.get(dependent_branch) else 'succeeded' | 3150 upload_status = 'failed' if failures.get(dependent_branch) else 'succeeded' |
| 3152 print ' %s : %s' % (dependent_branch, upload_status) | 3151 print(' %s : %s' % (dependent_branch, upload_status)) |
| 3153 print | 3152 print() |
| 3154 | 3153 |
| 3155 return 0 | 3154 return 0 |
| 3156 | 3155 |
| 3157 | 3156 |
| 3158 def CMDarchive(parser, args): | 3157 def CMDarchive(parser, args): |
| 3159 """Archives and deletes branches associated with closed changelists.""" | 3158 """Archives and deletes branches associated with closed changelists.""" |
| 3160 parser.add_option( | 3159 parser.add_option( |
| 3161 '-j', '--maxjobs', action='store', type=int, | 3160 '-j', '--maxjobs', action='store', type=int, |
| 3162 help='The maximum number of jobs to use when retrieving review status') | 3161 help='The maximum number of jobs to use when retrieving review status') |
| 3163 parser.add_option( | 3162 parser.add_option( |
| 3164 '-f', '--force', action='store_true', | 3163 '-f', '--force', action='store_true', |
| 3165 help='Bypasses the confirmation prompt.') | 3164 help='Bypasses the confirmation prompt.') |
| 3166 | 3165 |
| 3167 auth.add_auth_options(parser) | 3166 auth.add_auth_options(parser) |
| 3168 options, args = parser.parse_args(args) | 3167 options, args = parser.parse_args(args) |
| 3169 if args: | 3168 if args: |
| 3170 parser.error('Unsupported args: %s' % ' '.join(args)) | 3169 parser.error('Unsupported args: %s' % ' '.join(args)) |
| 3171 auth_config = auth.extract_auth_config_from_options(options) | 3170 auth_config = auth.extract_auth_config_from_options(options) |
| 3172 | 3171 |
| 3173 branches = RunGit(['for-each-ref', '--format=%(refname)', 'refs/heads']) | 3172 branches = RunGit(['for-each-ref', '--format=%(refname)', 'refs/heads']) |
| 3174 if not branches: | 3173 if not branches: |
| 3175 return 0 | 3174 return 0 |
| 3176 | 3175 |
| 3177 print 'Finding all branches associated with closed issues...' | 3176 print('Finding all branches associated with closed issues...') |
| 3178 changes = [Changelist(branchref=b, auth_config=auth_config) | 3177 changes = [Changelist(branchref=b, auth_config=auth_config) |
| 3179 for b in branches.splitlines()] | 3178 for b in branches.splitlines()] |
| 3180 alignment = max(5, max(len(c.GetBranch()) for c in changes)) | 3179 alignment = max(5, max(len(c.GetBranch()) for c in changes)) |
| 3181 statuses = get_cl_statuses(changes, | 3180 statuses = get_cl_statuses(changes, |
| 3182 fine_grained=True, | 3181 fine_grained=True, |
| 3183 max_processes=options.maxjobs) | 3182 max_processes=options.maxjobs) |
| 3184 proposal = [(cl.GetBranch(), | 3183 proposal = [(cl.GetBranch(), |
| 3185 'git-cl-archived-%s-%s' % (cl.GetIssue(), cl.GetBranch())) | 3184 'git-cl-archived-%s-%s' % (cl.GetIssue(), cl.GetBranch())) |
| 3186 for cl, status in statuses | 3185 for cl, status in statuses |
| 3187 if status == 'closed'] | 3186 if status == 'closed'] |
| 3188 proposal.sort() | 3187 proposal.sort() |
| 3189 | 3188 |
| 3190 if not proposal: | 3189 if not proposal: |
| 3191 print 'No branches with closed codereview issues found.' | 3190 print('No branches with closed codereview issues found.') |
| 3192 return 0 | 3191 return 0 |
| 3193 | 3192 |
| 3194 current_branch = GetCurrentBranch() | 3193 current_branch = GetCurrentBranch() |
| 3195 | 3194 |
| 3196 print '\nBranches with closed issues that will be archived:\n' | 3195 print('\nBranches with closed issues that will be archived:\n') |
| 3197 print '%*s | %s' % (alignment, 'Branch name', 'Archival tag name') | 3196 print('%*s | %s' % (alignment, 'Branch name', 'Archival tag name')) |
| 3198 for next_item in proposal: | 3197 for next_item in proposal: |
| 3199 print '%*s %s' % (alignment, next_item[0], next_item[1]) | 3198 print('%*s %s' % (alignment, next_item[0], next_item[1])) |
| 3200 | 3199 |
| 3201 if any(branch == current_branch for branch, _ in proposal): | 3200 if any(branch == current_branch for branch, _ in proposal): |
| 3202 print('You are currently on a branch \'%s\' which is associated with a ' | 3201 print('You are currently on a branch \'%s\' which is associated with a ' |
| 3203 'closed codereview issue, so archive cannot proceed. Please ' | 3202 'closed codereview issue, so archive cannot proceed. Please ' |
| 3204 'checkout another branch and run this command again.' % | 3203 'checkout another branch and run this command again.' % |
| 3205 current_branch) | 3204 current_branch) |
| 3206 return 1 | 3205 return 1 |
| 3207 | 3206 |
| 3208 if not options.force: | 3207 if not options.force: |
| 3209 if ask_for_data('\nProceed with deletion (Y/N)? ').lower() != 'y': | 3208 if ask_for_data('\nProceed with deletion (Y/N)? ').lower() != 'y': |
| 3210 print 'Aborted.' | 3209 print('Aborted.') |
| 3211 return 1 | 3210 return 1 |
| 3212 | 3211 |
| 3213 for branch, tagname in proposal: | 3212 for branch, tagname in proposal: |
| 3214 RunGit(['tag', tagname, branch]) | 3213 RunGit(['tag', tagname, branch]) |
| 3215 RunGit(['branch', '-D', branch]) | 3214 RunGit(['branch', '-D', branch]) |
| 3216 print '\nJob\'s done!' | 3215 print('\nJob\'s done!') |
| 3217 | 3216 |
| 3218 return 0 | 3217 return 0 |
| 3219 | 3218 |
| 3220 | 3219 |
| 3221 def CMDstatus(parser, args): | 3220 def CMDstatus(parser, args): |
| 3222 """Show status of changelists. | 3221 """Show status of changelists. |
| 3223 | 3222 |
| 3224 Colors are used to tell the state of the CL unless --fast is used: | 3223 Colors are used to tell the state of the CL unless --fast is used: |
| 3225 - Red not sent for review or broken | 3224 - Red not sent for review or broken |
| 3226 - Blue waiting for review | 3225 - Blue waiting for review |
| (...skipping 14 matching lines...) Expand all Loading... |
| 3241 | 3240 |
| 3242 auth.add_auth_options(parser) | 3241 auth.add_auth_options(parser) |
| 3243 options, args = parser.parse_args(args) | 3242 options, args = parser.parse_args(args) |
| 3244 if args: | 3243 if args: |
| 3245 parser.error('Unsupported args: %s' % args) | 3244 parser.error('Unsupported args: %s' % args) |
| 3246 auth_config = auth.extract_auth_config_from_options(options) | 3245 auth_config = auth.extract_auth_config_from_options(options) |
| 3247 | 3246 |
| 3248 if options.field: | 3247 if options.field: |
| 3249 cl = Changelist(auth_config=auth_config) | 3248 cl = Changelist(auth_config=auth_config) |
| 3250 if options.field.startswith('desc'): | 3249 if options.field.startswith('desc'): |
| 3251 print cl.GetDescription() | 3250 print(cl.GetDescription()) |
| 3252 elif options.field == 'id': | 3251 elif options.field == 'id': |
| 3253 issueid = cl.GetIssue() | 3252 issueid = cl.GetIssue() |
| 3254 if issueid: | 3253 if issueid: |
| 3255 print issueid | 3254 print(issueid) |
| 3256 elif options.field == 'patch': | 3255 elif options.field == 'patch': |
| 3257 patchset = cl.GetPatchset() | 3256 patchset = cl.GetPatchset() |
| 3258 if patchset: | 3257 if patchset: |
| 3259 print patchset | 3258 print(patchset) |
| 3260 elif options.field == 'url': | 3259 elif options.field == 'url': |
| 3261 url = cl.GetIssueURL() | 3260 url = cl.GetIssueURL() |
| 3262 if url: | 3261 if url: |
| 3263 print url | 3262 print(url) |
| 3264 return 0 | 3263 return 0 |
| 3265 | 3264 |
| 3266 branches = RunGit(['for-each-ref', '--format=%(refname)', 'refs/heads']) | 3265 branches = RunGit(['for-each-ref', '--format=%(refname)', 'refs/heads']) |
| 3267 if not branches: | 3266 if not branches: |
| 3268 print('No local branch found.') | 3267 print('No local branch found.') |
| 3269 return 0 | 3268 return 0 |
| 3270 | 3269 |
| 3271 changes = [ | 3270 changes = [ |
| 3272 Changelist(branchref=b, auth_config=auth_config) | 3271 Changelist(branchref=b, auth_config=auth_config) |
| 3273 for b in branches.splitlines()] | 3272 for b in branches.splitlines()] |
| 3274 print 'Branches associated with reviews:' | 3273 print('Branches associated with reviews:') |
| 3275 output = get_cl_statuses(changes, | 3274 output = get_cl_statuses(changes, |
| 3276 fine_grained=not options.fast, | 3275 fine_grained=not options.fast, |
| 3277 max_processes=options.maxjobs) | 3276 max_processes=options.maxjobs) |
| 3278 | 3277 |
| 3279 branch_statuses = {} | 3278 branch_statuses = {} |
| 3280 alignment = max(5, max(len(ShortBranchName(c.GetBranch())) for c in changes)) | 3279 alignment = max(5, max(len(ShortBranchName(c.GetBranch())) for c in changes)) |
| 3281 for cl in sorted(changes, key=lambda c: c.GetBranch()): | 3280 for cl in sorted(changes, key=lambda c: c.GetBranch()): |
| 3282 branch = cl.GetBranch() | 3281 branch = cl.GetBranch() |
| 3283 while branch not in branch_statuses: | 3282 while branch not in branch_statuses: |
| 3284 c, status = output.next() | 3283 c, status = output.next() |
| 3285 branch_statuses[c.GetBranch()] = status | 3284 branch_statuses[c.GetBranch()] = status |
| 3286 status = branch_statuses.pop(branch) | 3285 status = branch_statuses.pop(branch) |
| 3287 url = cl.GetIssueURL() | 3286 url = cl.GetIssueURL() |
| 3288 if url and (not status or status == 'error'): | 3287 if url and (not status or status == 'error'): |
| 3289 # The issue probably doesn't exist anymore. | 3288 # The issue probably doesn't exist anymore. |
| 3290 url += ' (broken)' | 3289 url += ' (broken)' |
| 3291 | 3290 |
| 3292 color = color_for_status(status) | 3291 color = color_for_status(status) |
| 3293 reset = Fore.RESET | 3292 reset = Fore.RESET |
| 3294 if not setup_color.IS_TTY: | 3293 if not setup_color.IS_TTY: |
| 3295 color = '' | 3294 color = '' |
| 3296 reset = '' | 3295 reset = '' |
| 3297 status_str = '(%s)' % status if status else '' | 3296 status_str = '(%s)' % status if status else '' |
| 3298 print ' %*s : %s%s %s%s' % ( | 3297 print(' %*s : %s%s %s%s' % ( |
| 3299 alignment, ShortBranchName(branch), color, url, | 3298 alignment, ShortBranchName(branch), color, url, |
| 3300 status_str, reset) | 3299 status_str, reset)) |
| 3301 | 3300 |
| 3302 cl = Changelist(auth_config=auth_config) | 3301 cl = Changelist(auth_config=auth_config) |
| 3303 print | 3302 print() |
| 3304 print 'Current branch:', | 3303 print('Current branch:',) |
| 3305 print cl.GetBranch() | 3304 print(cl.GetBranch()) |
| 3306 if not cl.GetIssue(): | 3305 if not cl.GetIssue(): |
| 3307 print 'No issue assigned.' | 3306 print('No issue assigned.') |
| 3308 return 0 | 3307 return 0 |
| 3309 print 'Issue number: %s (%s)' % (cl.GetIssue(), cl.GetIssueURL()) | 3308 print('Issue number: %s (%s)' % (cl.GetIssue(), cl.GetIssueURL())) |
| 3310 if not options.fast: | 3309 if not options.fast: |
| 3311 print 'Issue description:' | 3310 print('Issue description:') |
| 3312 print cl.GetDescription(pretty=True) | 3311 print(cl.GetDescription(pretty=True)) |
| 3313 return 0 | 3312 return 0 |
| 3314 | 3313 |
| 3315 | 3314 |
| 3316 def colorize_CMDstatus_doc(): | 3315 def colorize_CMDstatus_doc(): |
| 3317 """To be called once in main() to add colors to git cl status help.""" | 3316 """To be called once in main() to add colors to git cl status help.""" |
| 3318 colors = [i for i in dir(Fore) if i[0].isupper()] | 3317 colors = [i for i in dir(Fore) if i[0].isupper()] |
| 3319 | 3318 |
| 3320 def colorize_line(line): | 3319 def colorize_line(line): |
| 3321 for color in colors: | 3320 for color in colors: |
| 3322 if color in line.upper(): | 3321 if color in line.upper(): |
| (...skipping 27 matching lines...) Expand all Loading... |
| 3350 # Reverse issue lookup. | 3349 # Reverse issue lookup. |
| 3351 issue_branch_map = {} | 3350 issue_branch_map = {} |
| 3352 for branch in branches: | 3351 for branch in branches: |
| 3353 cl = Changelist(branchref=branch) | 3352 cl = Changelist(branchref=branch) |
| 3354 issue_branch_map.setdefault(cl.GetIssue(), []).append(branch) | 3353 issue_branch_map.setdefault(cl.GetIssue(), []).append(branch) |
| 3355 if not args: | 3354 if not args: |
| 3356 args = sorted(issue_branch_map.iterkeys()) | 3355 args = sorted(issue_branch_map.iterkeys()) |
| 3357 for issue in args: | 3356 for issue in args: |
| 3358 if not issue: | 3357 if not issue: |
| 3359 continue | 3358 continue |
| 3360 print 'Branch for issue number %s: %s' % ( | 3359 print('Branch for issue number %s: %s' % ( |
| 3361 issue, ', '.join(issue_branch_map.get(int(issue)) or ('None',))) | 3360 issue, ', '.join(issue_branch_map.get(int(issue)) or ('None',)))) |
| 3362 else: | 3361 else: |
| 3363 cl = Changelist(codereview=options.forced_codereview) | 3362 cl = Changelist(codereview=options.forced_codereview) |
| 3364 if len(args) > 0: | 3363 if len(args) > 0: |
| 3365 try: | 3364 try: |
| 3366 issue = int(args[0]) | 3365 issue = int(args[0]) |
| 3367 except ValueError: | 3366 except ValueError: |
| 3368 DieWithError('Pass a number to set the issue or none to list it.\n' | 3367 DieWithError('Pass a number to set the issue or none to list it.\n' |
| 3369 'Maybe you want to run git cl status?') | 3368 'Maybe you want to run git cl status?') |
| 3370 cl.SetIssue(issue) | 3369 cl.SetIssue(issue) |
| 3371 print 'Issue number: %s (%s)' % (cl.GetIssue(), cl.GetIssueURL()) | 3370 print('Issue number: %s (%s)' % (cl.GetIssue(), cl.GetIssueURL())) |
| 3372 return 0 | 3371 return 0 |
| 3373 | 3372 |
| 3374 | 3373 |
| 3375 def CMDcomments(parser, args): | 3374 def CMDcomments(parser, args): |
| 3376 """Shows or posts review comments for any changelist.""" | 3375 """Shows or posts review comments for any changelist.""" |
| 3377 parser.add_option('-a', '--add-comment', dest='comment', | 3376 parser.add_option('-a', '--add-comment', dest='comment', |
| 3378 help='comment to add to an issue') | 3377 help='comment to add to an issue') |
| 3379 parser.add_option('-i', dest='issue', | 3378 parser.add_option('-i', dest='issue', |
| 3380 help="review issue id (defaults to current issue)") | 3379 help="review issue id (defaults to current issue)") |
| 3381 parser.add_option('-j', '--json-file', | 3380 parser.add_option('-j', '--json-file', |
| (...skipping 28 matching lines...) Expand all Loading... |
| 3410 if message['disapproval']: | 3409 if message['disapproval']: |
| 3411 color = Fore.RED | 3410 color = Fore.RED |
| 3412 summary[-1]['not lgtm'] = True | 3411 summary[-1]['not lgtm'] = True |
| 3413 elif message['approval']: | 3412 elif message['approval']: |
| 3414 color = Fore.GREEN | 3413 color = Fore.GREEN |
| 3415 summary[-1]['lgtm'] = True | 3414 summary[-1]['lgtm'] = True |
| 3416 elif message['sender'] == data['owner_email']: | 3415 elif message['sender'] == data['owner_email']: |
| 3417 color = Fore.MAGENTA | 3416 color = Fore.MAGENTA |
| 3418 else: | 3417 else: |
| 3419 color = Fore.BLUE | 3418 color = Fore.BLUE |
| 3420 print '\n%s%s %s%s' % ( | 3419 print('\n%s%s %s%s' % ( |
| 3421 color, message['date'].split('.', 1)[0], message['sender'], | 3420 color, message['date'].split('.', 1)[0], message['sender'], |
| 3422 Fore.RESET) | 3421 Fore.RESET)) |
| 3423 if message['text'].strip(): | 3422 if message['text'].strip(): |
| 3424 print '\n'.join(' ' + l for l in message['text'].splitlines()) | 3423 print('\n'.join(' ' + l for l in message['text'].splitlines())) |
| 3425 if options.json_file: | 3424 if options.json_file: |
| 3426 with open(options.json_file, 'wb') as f: | 3425 with open(options.json_file, 'wb') as f: |
| 3427 json.dump(summary, f) | 3426 json.dump(summary, f) |
| 3428 return 0 | 3427 return 0 |
| 3429 | 3428 |
| 3430 | 3429 |
| 3431 @subcommand.usage('[codereview url or issue id]') | 3430 @subcommand.usage('[codereview url or issue id]') |
| 3432 def CMDdescription(parser, args): | 3431 def CMDdescription(parser, args): |
| 3433 """Brings up the editor for the current CL's description.""" | 3432 """Brings up the editor for the current CL's description.""" |
| 3434 parser.add_option('-d', '--display', action='store_true', | 3433 parser.add_option('-d', '--display', action='store_true', |
| (...skipping 18 matching lines...) Expand all Loading... |
| 3453 | 3452 |
| 3454 cl = Changelist( | 3453 cl = Changelist( |
| 3455 auth_config=auth_config, issue=target_issue, | 3454 auth_config=auth_config, issue=target_issue, |
| 3456 codereview=options.forced_codereview) | 3455 codereview=options.forced_codereview) |
| 3457 | 3456 |
| 3458 if not cl.GetIssue(): | 3457 if not cl.GetIssue(): |
| 3459 DieWithError('This branch has no associated changelist.') | 3458 DieWithError('This branch has no associated changelist.') |
| 3460 description = ChangeDescription(cl.GetDescription()) | 3459 description = ChangeDescription(cl.GetDescription()) |
| 3461 | 3460 |
| 3462 if options.display: | 3461 if options.display: |
| 3463 print description.description | 3462 print(description.description) |
| 3464 return 0 | 3463 return 0 |
| 3465 | 3464 |
| 3466 if options.new_description: | 3465 if options.new_description: |
| 3467 text = options.new_description | 3466 text = options.new_description |
| 3468 if text == '-': | 3467 if text == '-': |
| 3469 text = '\n'.join(l.rstrip() for l in sys.stdin) | 3468 text = '\n'.join(l.rstrip() for l in sys.stdin) |
| 3470 | 3469 |
| 3471 description.set_description(text) | 3470 description.set_description(text) |
| 3472 else: | 3471 else: |
| 3473 description.prompt() | 3472 description.prompt() |
| (...skipping 24 matching lines...) Expand all Loading... |
| 3498 auth.add_auth_options(parser) | 3497 auth.add_auth_options(parser) |
| 3499 options, args = parser.parse_args(args) | 3498 options, args = parser.parse_args(args) |
| 3500 auth_config = auth.extract_auth_config_from_options(options) | 3499 auth_config = auth.extract_auth_config_from_options(options) |
| 3501 | 3500 |
| 3502 # Access to a protected member _XX of a client class | 3501 # Access to a protected member _XX of a client class |
| 3503 # pylint: disable=W0212 | 3502 # pylint: disable=W0212 |
| 3504 try: | 3503 try: |
| 3505 import cpplint | 3504 import cpplint |
| 3506 import cpplint_chromium | 3505 import cpplint_chromium |
| 3507 except ImportError: | 3506 except ImportError: |
| 3508 print "Your depot_tools is missing cpplint.py and/or cpplint_chromium.py." | 3507 print('Your depot_tools is missing cpplint.py and/or cpplint_chromium.py.') |
| 3509 return 1 | 3508 return 1 |
| 3510 | 3509 |
| 3511 # Change the current working directory before calling lint so that it | 3510 # Change the current working directory before calling lint so that it |
| 3512 # shows the correct base. | 3511 # shows the correct base. |
| 3513 previous_cwd = os.getcwd() | 3512 previous_cwd = os.getcwd() |
| 3514 os.chdir(settings.GetRoot()) | 3513 os.chdir(settings.GetRoot()) |
| 3515 try: | 3514 try: |
| 3516 cl = Changelist(auth_config=auth_config) | 3515 cl = Changelist(auth_config=auth_config) |
| 3517 change = cl.GetChange(cl.GetCommonAncestorWithUpstream(), None) | 3516 change = cl.GetChange(cl.GetCommonAncestorWithUpstream(), None) |
| 3518 files = [f.LocalPath() for f in change.AffectedFiles()] | 3517 files = [f.LocalPath() for f in change.AffectedFiles()] |
| 3519 if not files: | 3518 if not files: |
| 3520 print "Cannot lint an empty CL" | 3519 print('Cannot lint an empty CL') |
| 3521 return 1 | 3520 return 1 |
| 3522 | 3521 |
| 3523 # Process cpplints arguments if any. | 3522 # Process cpplints arguments if any. |
| 3524 command = args + files | 3523 command = args + files |
| 3525 if options.filter: | 3524 if options.filter: |
| 3526 command = ['--filter=' + ','.join(options.filter)] + command | 3525 command = ['--filter=' + ','.join(options.filter)] + command |
| 3527 filenames = cpplint.ParseArguments(command) | 3526 filenames = cpplint.ParseArguments(command) |
| 3528 | 3527 |
| 3529 white_regex = re.compile(settings.GetLintRegex()) | 3528 white_regex = re.compile(settings.GetLintRegex()) |
| 3530 black_regex = re.compile(settings.GetLintIgnoreRegex()) | 3529 black_regex = re.compile(settings.GetLintIgnoreRegex()) |
| 3531 extra_check_functions = [cpplint_chromium.CheckPointerDeclarationWhitespace] | 3530 extra_check_functions = [cpplint_chromium.CheckPointerDeclarationWhitespace] |
| 3532 for filename in filenames: | 3531 for filename in filenames: |
| 3533 if white_regex.match(filename): | 3532 if white_regex.match(filename): |
| 3534 if black_regex.match(filename): | 3533 if black_regex.match(filename): |
| 3535 print "Ignoring file %s" % filename | 3534 print('Ignoring file %s' % filename) |
| 3536 else: | 3535 else: |
| 3537 cpplint.ProcessFile(filename, cpplint._cpplint_state.verbose_level, | 3536 cpplint.ProcessFile(filename, cpplint._cpplint_state.verbose_level, |
| 3538 extra_check_functions) | 3537 extra_check_functions) |
| 3539 else: | 3538 else: |
| 3540 print "Skipping file %s" % filename | 3539 print('Skipping file %s' % filename) |
| 3541 finally: | 3540 finally: |
| 3542 os.chdir(previous_cwd) | 3541 os.chdir(previous_cwd) |
| 3543 print "Total errors found: %d\n" % cpplint._cpplint_state.error_count | 3542 print('Total errors found: %d\n' % cpplint._cpplint_state.error_count) |
| 3544 if cpplint._cpplint_state.error_count != 0: | 3543 if cpplint._cpplint_state.error_count != 0: |
| 3545 return 1 | 3544 return 1 |
| 3546 return 0 | 3545 return 0 |
| 3547 | 3546 |
| 3548 | 3547 |
| 3549 def CMDpresubmit(parser, args): | 3548 def CMDpresubmit(parser, args): |
| 3550 """Runs presubmit tests on the current changelist.""" | 3549 """Runs presubmit tests on the current changelist.""" |
| 3551 parser.add_option('-u', '--upload', action='store_true', | 3550 parser.add_option('-u', '--upload', action='store_true', |
| 3552 help='Run upload hook instead of the push/dcommit hook') | 3551 help='Run upload hook instead of the push/dcommit hook') |
| 3553 parser.add_option('-f', '--force', action='store_true', | 3552 parser.add_option('-f', '--force', action='store_true', |
| 3554 help='Run checks even if tree is dirty') | 3553 help='Run checks even if tree is dirty') |
| 3555 auth.add_auth_options(parser) | 3554 auth.add_auth_options(parser) |
| 3556 options, args = parser.parse_args(args) | 3555 options, args = parser.parse_args(args) |
| 3557 auth_config = auth.extract_auth_config_from_options(options) | 3556 auth_config = auth.extract_auth_config_from_options(options) |
| 3558 | 3557 |
| 3559 if not options.force and git_common.is_dirty_git_tree('presubmit'): | 3558 if not options.force and git_common.is_dirty_git_tree('presubmit'): |
| 3560 print 'use --force to check even if tree is dirty.' | 3559 print('use --force to check even if tree is dirty.') |
| 3561 return 1 | 3560 return 1 |
| 3562 | 3561 |
| 3563 cl = Changelist(auth_config=auth_config) | 3562 cl = Changelist(auth_config=auth_config) |
| 3564 if args: | 3563 if args: |
| 3565 base_branch = args[0] | 3564 base_branch = args[0] |
| 3566 else: | 3565 else: |
| 3567 # Default to diffing against the common ancestor of the upstream branch. | 3566 # Default to diffing against the common ancestor of the upstream branch. |
| 3568 base_branch = cl.GetCommonAncestorWithUpstream() | 3567 base_branch = cl.GetCommonAncestorWithUpstream() |
| 3569 | 3568 |
| 3570 cl.RunHook( | 3569 cl.RunHook( |
| (...skipping 231 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 3802 'Before uploading a commit to Gerrit, ensure it\'s author field is ' | 3801 'Before uploading a commit to Gerrit, ensure it\'s author field is ' |
| 3803 'the contributor\'s "name <email>". If you can\'t upload such a ' | 3802 'the contributor\'s "name <email>". If you can\'t upload such a ' |
| 3804 'commit for review, contact your repository admin and request' | 3803 'commit for review, contact your repository admin and request' |
| 3805 '"Forge-Author" permission.') | 3804 '"Forge-Author" permission.') |
| 3806 return cl._codereview_impl.CMDLand(options.force, options.bypass_hooks, | 3805 return cl._codereview_impl.CMDLand(options.force, options.bypass_hooks, |
| 3807 options.verbose) | 3806 options.verbose) |
| 3808 | 3807 |
| 3809 current = cl.GetBranch() | 3808 current = cl.GetBranch() |
| 3810 remote, upstream_branch = cl.FetchUpstreamTuple(cl.GetBranch()) | 3809 remote, upstream_branch = cl.FetchUpstreamTuple(cl.GetBranch()) |
| 3811 if not settings.GetIsGitSvn() and remote == '.': | 3810 if not settings.GetIsGitSvn() and remote == '.': |
| 3812 print | 3811 print() |
| 3813 print 'Attempting to push branch %r into another local branch!' % current | 3812 print('Attempting to push branch %r into another local branch!' % current) |
| 3814 print | 3813 print() |
| 3815 print 'Either reparent this branch on top of origin/master:' | 3814 print('Either reparent this branch on top of origin/master:') |
| 3816 print ' git reparent-branch --root' | 3815 print(' git reparent-branch --root') |
| 3817 print | 3816 print() |
| 3818 print 'OR run `git rebase-update` if you think the parent branch is already' | 3817 print('OR run `git rebase-update` if you think the parent branch is ') |
| 3819 print 'committed.' | 3818 print('already committed.') |
| 3820 print | 3819 print() |
| 3821 print ' Current parent: %r' % upstream_branch | 3820 print(' Current parent: %r' % upstream_branch) |
| 3822 return 1 | 3821 return 1 |
| 3823 | 3822 |
| 3824 if not args or cmd == 'land': | 3823 if not args or cmd == 'land': |
| 3825 # Default to merging against our best guess of the upstream branch. | 3824 # Default to merging against our best guess of the upstream branch. |
| 3826 args = [cl.GetUpstreamBranch()] | 3825 args = [cl.GetUpstreamBranch()] |
| 3827 | 3826 |
| 3828 if options.contributor: | 3827 if options.contributor: |
| 3829 if not re.match('^.*\s<\S+@\S+>$', options.contributor): | 3828 if not re.match('^.*\s<\S+@\S+>$', options.contributor): |
| 3830 print "Please provide contibutor as 'First Last <email@example.com>'" | 3829 print("Please provide contibutor as 'First Last <email@example.com>'") |
| 3831 return 1 | 3830 return 1 |
| 3832 | 3831 |
| 3833 base_branch = args[0] | 3832 base_branch = args[0] |
| 3834 base_has_submodules = IsSubmoduleMergeCommit(base_branch) | 3833 base_has_submodules = IsSubmoduleMergeCommit(base_branch) |
| 3835 | 3834 |
| 3836 if git_common.is_dirty_git_tree(cmd): | 3835 if git_common.is_dirty_git_tree(cmd): |
| 3837 return 1 | 3836 return 1 |
| 3838 | 3837 |
| 3839 # This rev-list syntax means "show all commits not in my branch that | 3838 # This rev-list syntax means "show all commits not in my branch that |
| 3840 # are in base_branch". | 3839 # are in base_branch". |
| 3841 upstream_commits = RunGit(['rev-list', '^' + cl.GetBranchRef(), | 3840 upstream_commits = RunGit(['rev-list', '^' + cl.GetBranchRef(), |
| 3842 base_branch]).splitlines() | 3841 base_branch]).splitlines() |
| 3843 if upstream_commits: | 3842 if upstream_commits: |
| 3844 print ('Base branch "%s" has %d commits ' | 3843 print('Base branch "%s" has %d commits ' |
| 3845 'not in this branch.' % (base_branch, len(upstream_commits))) | 3844 'not in this branch.' % (base_branch, len(upstream_commits))) |
| 3846 print 'Run "git merge %s" before attempting to %s.' % (base_branch, cmd) | 3845 print('Run "git merge %s" before attempting to %s.' % (base_branch, cmd)) |
| 3847 return 1 | 3846 return 1 |
| 3848 | 3847 |
| 3849 # This is the revision `svn dcommit` will commit on top of. | 3848 # This is the revision `svn dcommit` will commit on top of. |
| 3850 svn_head = None | 3849 svn_head = None |
| 3851 if cmd == 'dcommit' or base_has_submodules: | 3850 if cmd == 'dcommit' or base_has_submodules: |
| 3852 svn_head = RunGit(['log', '--grep=^git-svn-id:', '-1', | 3851 svn_head = RunGit(['log', '--grep=^git-svn-id:', '-1', |
| 3853 '--pretty=format:%H']) | 3852 '--pretty=format:%H']) |
| 3854 | 3853 |
| 3855 if cmd == 'dcommit': | 3854 if cmd == 'dcommit': |
| 3856 # If the base_head is a submodule merge commit, the first parent of the | 3855 # If the base_head is a submodule merge commit, the first parent of the |
| 3857 # base_head should be a git-svn commit, which is what we're interested in. | 3856 # base_head should be a git-svn commit, which is what we're interested in. |
| 3858 base_svn_head = base_branch | 3857 base_svn_head = base_branch |
| 3859 if base_has_submodules: | 3858 if base_has_submodules: |
| 3860 base_svn_head += '^1' | 3859 base_svn_head += '^1' |
| 3861 | 3860 |
| 3862 extra_commits = RunGit(['rev-list', '^' + svn_head, base_svn_head]) | 3861 extra_commits = RunGit(['rev-list', '^' + svn_head, base_svn_head]) |
| 3863 if extra_commits: | 3862 if extra_commits: |
| 3864 print ('This branch has %d additional commits not upstreamed yet.' | 3863 print('This branch has %d additional commits not upstreamed yet.' |
| 3865 % len(extra_commits.splitlines())) | 3864 % len(extra_commits.splitlines())) |
| 3866 print ('Upstream "%s" or rebase this branch on top of the upstream trunk ' | 3865 print('Upstream "%s" or rebase this branch on top of the upstream trunk ' |
| 3867 'before attempting to %s.' % (base_branch, cmd)) | 3866 'before attempting to %s.' % (base_branch, cmd)) |
| 3868 return 1 | 3867 return 1 |
| 3869 | 3868 |
| 3870 merge_base = RunGit(['merge-base', base_branch, 'HEAD']).strip() | 3869 merge_base = RunGit(['merge-base', base_branch, 'HEAD']).strip() |
| 3871 if not options.bypass_hooks: | 3870 if not options.bypass_hooks: |
| 3872 author = None | 3871 author = None |
| 3873 if options.contributor: | 3872 if options.contributor: |
| 3874 author = re.search(r'\<(.*)\>', options.contributor).group(1) | 3873 author = re.search(r'\<(.*)\>', options.contributor).group(1) |
| 3875 hook_results = cl.RunHook( | 3874 hook_results = cl.RunHook( |
| 3876 committing=True, | 3875 committing=True, |
| 3877 may_prompt=not options.force, | 3876 may_prompt=not options.force, |
| (...skipping 14 matching lines...) Expand all Loading... |
| 3892 return 1 | 3891 return 1 |
| 3893 | 3892 |
| 3894 change_desc = ChangeDescription(options.message) | 3893 change_desc = ChangeDescription(options.message) |
| 3895 if not change_desc.description and cl.GetIssue(): | 3894 if not change_desc.description and cl.GetIssue(): |
| 3896 change_desc = ChangeDescription(cl.GetDescription()) | 3895 change_desc = ChangeDescription(cl.GetDescription()) |
| 3897 | 3896 |
| 3898 if not change_desc.description: | 3897 if not change_desc.description: |
| 3899 if not cl.GetIssue() and options.bypass_hooks: | 3898 if not cl.GetIssue() and options.bypass_hooks: |
| 3900 change_desc = ChangeDescription(CreateDescriptionFromLog([merge_base])) | 3899 change_desc = ChangeDescription(CreateDescriptionFromLog([merge_base])) |
| 3901 else: | 3900 else: |
| 3902 print 'No description set.' | 3901 print('No description set.') |
| 3903 print 'Visit %s/edit to set it.' % (cl.GetIssueURL()) | 3902 print('Visit %s/edit to set it.' % (cl.GetIssueURL())) |
| 3904 return 1 | 3903 return 1 |
| 3905 | 3904 |
| 3906 # Keep a separate copy for the commit message, because the commit message | 3905 # Keep a separate copy for the commit message, because the commit message |
| 3907 # contains the link to the Rietveld issue, while the Rietveld message contains | 3906 # contains the link to the Rietveld issue, while the Rietveld message contains |
| 3908 # the commit viewvc url. | 3907 # the commit viewvc url. |
| 3909 # Keep a separate copy for the commit message. | 3908 # Keep a separate copy for the commit message. |
| 3910 if cl.GetIssue(): | 3909 if cl.GetIssue(): |
| 3911 change_desc.update_reviewers(cl.GetApprovingReviewers()) | 3910 change_desc.update_reviewers(cl.GetApprovingReviewers()) |
| 3912 | 3911 |
| 3913 commit_desc = ChangeDescription(change_desc.description) | 3912 commit_desc = ChangeDescription(change_desc.description) |
| (...skipping 97 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 4011 '.*?\nCommitted r(\\d+)', output, re.DOTALL).group(1) | 4010 '.*?\nCommitted r(\\d+)', output, re.DOTALL).group(1) |
| 4012 logging.debug(output) | 4011 logging.debug(output) |
| 4013 finally: | 4012 finally: |
| 4014 # And then swap back to the original branch and clean up. | 4013 # And then swap back to the original branch and clean up. |
| 4015 RunGit(['checkout', '-q', cl.GetBranch()]) | 4014 RunGit(['checkout', '-q', cl.GetBranch()]) |
| 4016 RunGit(['branch', '-D', MERGE_BRANCH]) | 4015 RunGit(['branch', '-D', MERGE_BRANCH]) |
| 4017 if base_has_submodules: | 4016 if base_has_submodules: |
| 4018 RunGit(['branch', '-D', CHERRY_PICK_BRANCH]) | 4017 RunGit(['branch', '-D', CHERRY_PICK_BRANCH]) |
| 4019 | 4018 |
| 4020 if not revision: | 4019 if not revision: |
| 4021 print 'Failed to push. If this persists, please file a bug.' | 4020 print('Failed to push. If this persists, please file a bug.') |
| 4022 return 1 | 4021 return 1 |
| 4023 | 4022 |
| 4024 killed = False | 4023 killed = False |
| 4025 if pushed_to_pending: | 4024 if pushed_to_pending: |
| 4026 try: | 4025 try: |
| 4027 revision = WaitForRealCommit(remote, revision, base_branch, branch) | 4026 revision = WaitForRealCommit(remote, revision, base_branch, branch) |
| 4028 # We set pushed_to_pending to False, since it made it all the way to the | 4027 # We set pushed_to_pending to False, since it made it all the way to the |
| 4029 # real ref. | 4028 # real ref. |
| 4030 pushed_to_pending = False | 4029 pushed_to_pending = False |
| 4031 except KeyboardInterrupt: | 4030 except KeyboardInterrupt: |
| 4032 killed = True | 4031 killed = True |
| 4033 | 4032 |
| 4034 if cl.GetIssue(): | 4033 if cl.GetIssue(): |
| 4035 to_pending = ' to pending queue' if pushed_to_pending else '' | 4034 to_pending = ' to pending queue' if pushed_to_pending else '' |
| 4036 viewvc_url = settings.GetViewVCUrl() | 4035 viewvc_url = settings.GetViewVCUrl() |
| 4037 if not to_pending: | 4036 if not to_pending: |
| 4038 if viewvc_url and revision: | 4037 if viewvc_url and revision: |
| 4039 change_desc.append_footer( | 4038 change_desc.append_footer( |
| 4040 'Committed: %s%s' % (viewvc_url, revision)) | 4039 'Committed: %s%s' % (viewvc_url, revision)) |
| 4041 elif revision: | 4040 elif revision: |
| 4042 change_desc.append_footer('Committed: %s' % (revision,)) | 4041 change_desc.append_footer('Committed: %s' % (revision,)) |
| 4043 print ('Closing issue ' | 4042 print('Closing issue ' |
| 4044 '(you may be prompted for your codereview password)...') | 4043 '(you may be prompted for your codereview password)...') |
| 4045 cl.UpdateDescription(change_desc.description) | 4044 cl.UpdateDescription(change_desc.description) |
| 4046 cl.CloseIssue() | 4045 cl.CloseIssue() |
| 4047 props = cl.GetIssueProperties() | 4046 props = cl.GetIssueProperties() |
| 4048 patch_num = len(props['patchsets']) | 4047 patch_num = len(props['patchsets']) |
| 4049 comment = "Committed patchset #%d (id:%d)%s manually as %s" % ( | 4048 comment = "Committed patchset #%d (id:%d)%s manually as %s" % ( |
| 4050 patch_num, props['patchsets'][-1], to_pending, revision) | 4049 patch_num, props['patchsets'][-1], to_pending, revision) |
| 4051 if options.bypass_hooks: | 4050 if options.bypass_hooks: |
| 4052 comment += ' (tree was closed).' if GetTreeStatus() == 'closed' else '.' | 4051 comment += ' (tree was closed).' if GetTreeStatus() == 'closed' else '.' |
| 4053 else: | 4052 else: |
| 4054 comment += ' (presubmit successful).' | 4053 comment += ' (presubmit successful).' |
| 4055 cl.RpcServer().add_comment(cl.GetIssue(), comment) | 4054 cl.RpcServer().add_comment(cl.GetIssue(), comment) |
| 4056 cl.SetIssue(None) | 4055 cl.SetIssue(None) |
| 4057 | 4056 |
| 4058 if pushed_to_pending: | 4057 if pushed_to_pending: |
| 4059 _, branch = cl.FetchUpstreamTuple(cl.GetBranch()) | 4058 _, branch = cl.FetchUpstreamTuple(cl.GetBranch()) |
| 4060 print 'The commit is in the pending queue (%s).' % pending_ref | 4059 print('The commit is in the pending queue (%s).' % pending_ref) |
| 4061 print ( | 4060 print('It will show up on %s in ~1 min, once it gets a Cr-Commit-Position ' |
| 4062 'It will show up on %s in ~1 min, once it gets a Cr-Commit-Position ' | 4061 'footer.' % branch) |
| 4063 'footer.' % branch) | |
| 4064 | 4062 |
| 4065 hook = POSTUPSTREAM_HOOK_PATTERN % cmd | 4063 hook = POSTUPSTREAM_HOOK_PATTERN % cmd |
| 4066 if os.path.isfile(hook): | 4064 if os.path.isfile(hook): |
| 4067 RunCommand([hook, merge_base], error_ok=True) | 4065 RunCommand([hook, merge_base], error_ok=True) |
| 4068 | 4066 |
| 4069 return 1 if killed else 0 | 4067 return 1 if killed else 0 |
| 4070 | 4068 |
| 4071 | 4069 |
| 4072 def WaitForRealCommit(remote, pushed_commit, local_base_ref, real_ref): | 4070 def WaitForRealCommit(remote, pushed_commit, local_base_ref, real_ref): |
| 4073 print | 4071 print() |
| 4074 print 'Waiting for commit to be landed on %s...' % real_ref | 4072 print('Waiting for commit to be landed on %s...' % real_ref) |
| 4075 print '(If you are impatient, you may Ctrl-C once without harm)' | 4073 print('(If you are impatient, you may Ctrl-C once without harm)') |
| 4076 target_tree = RunGit(['rev-parse', '%s:' % pushed_commit]).strip() | 4074 target_tree = RunGit(['rev-parse', '%s:' % pushed_commit]).strip() |
| 4077 current_rev = RunGit(['rev-parse', local_base_ref]).strip() | 4075 current_rev = RunGit(['rev-parse', local_base_ref]).strip() |
| 4078 mirror = settings.GetGitMirror(remote) | 4076 mirror = settings.GetGitMirror(remote) |
| 4079 | 4077 |
| 4080 loop = 0 | 4078 loop = 0 |
| 4081 while True: | 4079 while True: |
| 4082 sys.stdout.write('fetching (%d)... \r' % loop) | 4080 sys.stdout.write('fetching (%d)... \r' % loop) |
| 4083 sys.stdout.flush() | 4081 sys.stdout.flush() |
| 4084 loop += 1 | 4082 loop += 1 |
| 4085 | 4083 |
| 4086 if mirror: | 4084 if mirror: |
| 4087 mirror.populate() | 4085 mirror.populate() |
| 4088 RunGit(['retry', 'fetch', remote, real_ref], stderr=subprocess2.VOID) | 4086 RunGit(['retry', 'fetch', remote, real_ref], stderr=subprocess2.VOID) |
| 4089 to_rev = RunGit(['rev-parse', 'FETCH_HEAD']).strip() | 4087 to_rev = RunGit(['rev-parse', 'FETCH_HEAD']).strip() |
| 4090 commits = RunGit(['rev-list', '%s..%s' % (current_rev, to_rev)]) | 4088 commits = RunGit(['rev-list', '%s..%s' % (current_rev, to_rev)]) |
| 4091 for commit in commits.splitlines(): | 4089 for commit in commits.splitlines(): |
| 4092 if RunGit(['rev-parse', '%s:' % commit]).strip() == target_tree: | 4090 if RunGit(['rev-parse', '%s:' % commit]).strip() == target_tree: |
| 4093 print 'Found commit on %s' % real_ref | 4091 print('Found commit on %s' % real_ref) |
| 4094 return commit | 4092 return commit |
| 4095 | 4093 |
| 4096 current_rev = to_rev | 4094 current_rev = to_rev |
| 4097 | 4095 |
| 4098 | 4096 |
| 4099 def PushToGitPending(remote, pending_ref, upstream_ref): | 4097 def PushToGitPending(remote, pending_ref, upstream_ref): |
| 4100 """Fetches pending_ref, cherry-picks current HEAD on top of it, pushes. | 4098 """Fetches pending_ref, cherry-picks current HEAD on top of it, pushes. |
| 4101 | 4099 |
| 4102 Returns: | 4100 Returns: |
| 4103 (retcode of last operation, output log of last operation). | 4101 (retcode of last operation, output log of last operation). |
| 4104 """ | 4102 """ |
| 4105 assert pending_ref.startswith('refs/'), pending_ref | 4103 assert pending_ref.startswith('refs/'), pending_ref |
| 4106 local_pending_ref = 'refs/git-cl/' + pending_ref[len('refs/'):] | 4104 local_pending_ref = 'refs/git-cl/' + pending_ref[len('refs/'):] |
| 4107 cherry = RunGit(['rev-parse', 'HEAD']).strip() | 4105 cherry = RunGit(['rev-parse', 'HEAD']).strip() |
| 4108 code = 0 | 4106 code = 0 |
| 4109 out = '' | 4107 out = '' |
| 4110 max_attempts = 3 | 4108 max_attempts = 3 |
| 4111 attempts_left = max_attempts | 4109 attempts_left = max_attempts |
| 4112 while attempts_left: | 4110 while attempts_left: |
| 4113 if attempts_left != max_attempts: | 4111 if attempts_left != max_attempts: |
| 4114 print 'Retrying, %d attempts left...' % (attempts_left - 1,) | 4112 print('Retrying, %d attempts left...' % (attempts_left - 1,)) |
| 4115 attempts_left -= 1 | 4113 attempts_left -= 1 |
| 4116 | 4114 |
| 4117 # Fetch. Retry fetch errors. | 4115 # Fetch. Retry fetch errors. |
| 4118 print 'Fetching pending ref %s...' % pending_ref | 4116 print('Fetching pending ref %s...' % pending_ref) |
| 4119 code, out = RunGitWithCode( | 4117 code, out = RunGitWithCode( |
| 4120 ['retry', 'fetch', remote, '+%s:%s' % (pending_ref, local_pending_ref)]) | 4118 ['retry', 'fetch', remote, '+%s:%s' % (pending_ref, local_pending_ref)]) |
| 4121 if code: | 4119 if code: |
| 4122 print 'Fetch failed with exit code %d.' % code | 4120 print('Fetch failed with exit code %d.' % code) |
| 4123 if out.strip(): | 4121 if out.strip(): |
| 4124 print out.strip() | 4122 print(out.strip()) |
| 4125 continue | 4123 continue |
| 4126 | 4124 |
| 4127 # Try to cherry pick. Abort on merge conflicts. | 4125 # Try to cherry pick. Abort on merge conflicts. |
| 4128 print 'Cherry-picking commit on top of pending ref...' | 4126 print('Cherry-picking commit on top of pending ref...') |
| 4129 RunGitWithCode(['checkout', local_pending_ref], suppress_stderr=True) | 4127 RunGitWithCode(['checkout', local_pending_ref], suppress_stderr=True) |
| 4130 code, out = RunGitWithCode(['cherry-pick', cherry]) | 4128 code, out = RunGitWithCode(['cherry-pick', cherry]) |
| 4131 if code: | 4129 if code: |
| 4132 print ( | 4130 print('Your patch doesn\'t apply cleanly to ref \'%s\', ' |
| 4133 'Your patch doesn\'t apply cleanly to ref \'%s\', ' | 4131 'the following files have merge conflicts:' % pending_ref) |
| 4134 'the following files have merge conflicts:' % pending_ref) | 4132 print(RunGit(['diff', '--name-status', '--diff-filter=U']).strip()) |
| 4135 print RunGit(['diff', '--name-status', '--diff-filter=U']).strip() | 4133 print('Please rebase your patch and try again.') |
| 4136 print 'Please rebase your patch and try again.' | |
| 4137 RunGitWithCode(['cherry-pick', '--abort']) | 4134 RunGitWithCode(['cherry-pick', '--abort']) |
| 4138 return code, out | 4135 return code, out |
| 4139 | 4136 |
| 4140 # Applied cleanly, try to push now. Retry on error (flake or non-ff push). | 4137 # Applied cleanly, try to push now. Retry on error (flake or non-ff push). |
| 4141 print 'Pushing commit to %s... It can take a while.' % pending_ref | 4138 print('Pushing commit to %s... It can take a while.' % pending_ref) |
| 4142 code, out = RunGitWithCode( | 4139 code, out = RunGitWithCode( |
| 4143 ['retry', 'push', '--porcelain', remote, 'HEAD:%s' % pending_ref]) | 4140 ['retry', 'push', '--porcelain', remote, 'HEAD:%s' % pending_ref]) |
| 4144 if code == 0: | 4141 if code == 0: |
| 4145 # Success. | 4142 # Success. |
| 4146 print 'Commit pushed to pending ref successfully!' | 4143 print('Commit pushed to pending ref successfully!') |
| 4147 return code, out | 4144 return code, out |
| 4148 | 4145 |
| 4149 print 'Push failed with exit code %d.' % code | 4146 print('Push failed with exit code %d.' % code) |
| 4150 if out.strip(): | 4147 if out.strip(): |
| 4151 print out.strip() | 4148 print(out.strip()) |
| 4152 if IsFatalPushFailure(out): | 4149 if IsFatalPushFailure(out): |
| 4153 print ( | 4150 print('Fatal push error. Make sure your .netrc credentials and git ' |
| 4154 'Fatal push error. Make sure your .netrc credentials and git ' | 4151 'user.email are correct and you have push access to the repo.') |
| 4155 'user.email are correct and you have push access to the repo.') | |
| 4156 return code, out | 4152 return code, out |
| 4157 | 4153 |
| 4158 print 'All attempts to push to pending ref failed.' | 4154 print('All attempts to push to pending ref failed.') |
| 4159 return code, out | 4155 return code, out |
| 4160 | 4156 |
| 4161 | 4157 |
| 4162 def IsFatalPushFailure(push_stdout): | 4158 def IsFatalPushFailure(push_stdout): |
| 4163 """True if retrying push won't help.""" | 4159 """True if retrying push won't help.""" |
| 4164 return '(prohibited by Gerrit)' in push_stdout | 4160 return '(prohibited by Gerrit)' in push_stdout |
| 4165 | 4161 |
| 4166 | 4162 |
| 4167 @subcommand.usage('[upstream branch to apply against]') | 4163 @subcommand.usage('[upstream branch to apply against]') |
| 4168 def CMDdcommit(parser, args): | 4164 def CMDdcommit(parser, args): |
| (...skipping 181 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 4350 elif result_master != cur_master: | 4346 elif result_master != cur_master: |
| 4351 return None, 'The builders do not belong to the same master.' | 4347 return None, 'The builders do not belong to the same master.' |
| 4352 return result_master, None | 4348 return result_master, None |
| 4353 | 4349 |
| 4354 | 4350 |
| 4355 def CMDtree(parser, args): | 4351 def CMDtree(parser, args): |
| 4356 """Shows the status of the tree.""" | 4352 """Shows the status of the tree.""" |
| 4357 _, args = parser.parse_args(args) | 4353 _, args = parser.parse_args(args) |
| 4358 status = GetTreeStatus() | 4354 status = GetTreeStatus() |
| 4359 if 'unset' == status: | 4355 if 'unset' == status: |
| 4360 print 'You must configure your tree status URL by running "git cl config".' | 4356 print('You must configure your tree status URL by running "git cl config".') |
| 4361 return 2 | 4357 return 2 |
| 4362 | 4358 |
| 4363 print "The tree is %s" % status | 4359 print('The tree is %s' % status) |
| 4364 print | 4360 print() |
| 4365 print GetTreeStatusReason() | 4361 print(GetTreeStatusReason()) |
| 4366 if status != 'open': | 4362 if status != 'open': |
| 4367 return 1 | 4363 return 1 |
| 4368 return 0 | 4364 return 0 |
| 4369 | 4365 |
| 4370 | 4366 |
| 4371 def CMDtry(parser, args): | 4367 def CMDtry(parser, args): |
| 4372 """Triggers try jobs through BuildBucket.""" | 4368 """Triggers try jobs through BuildBucket.""" |
| 4373 group = optparse.OptionGroup(parser, "Try job options") | 4369 group = optparse.OptionGroup(parser, "Try job options") |
| 4374 group.add_option( | 4370 group.add_option( |
| 4375 "-b", "--bot", action="append", | 4371 "-b", "--bot", action="append", |
| (...skipping 146 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 4522 | 4518 |
| 4523 # Return a master map with one master to be backwards compatible. The | 4519 # Return a master map with one master to be backwards compatible. The |
| 4524 # master name defaults to an empty string, which will cause the master | 4520 # master name defaults to an empty string, which will cause the master |
| 4525 # not to be set on rietveld (deprecated). | 4521 # not to be set on rietveld (deprecated). |
| 4526 return {options.master: builders_and_tests} | 4522 return {options.master: builders_and_tests} |
| 4527 | 4523 |
| 4528 masters = GetMasterMap() | 4524 masters = GetMasterMap() |
| 4529 | 4525 |
| 4530 for builders in masters.itervalues(): | 4526 for builders in masters.itervalues(): |
| 4531 if any('triggered' in b for b in builders): | 4527 if any('triggered' in b for b in builders): |
| 4532 print >> sys.stderr, ( | 4528 print('ERROR You are trying to send a job to a triggered bot. This type ' |
| 4533 'ERROR You are trying to send a job to a triggered bot. This type of' | 4529 'of bot requires an\ninitial job from a parent (usually a builder).' |
| 4534 ' bot requires an\ninitial job from a parent (usually a builder). ' | 4530 ' Instead send your job to the parent.\n' |
| 4535 'Instead send your job to the parent.\n' | 4531 'Bot list: %s' % builders, file=sys.stderr) |
| 4536 'Bot list: %s' % builders) | |
| 4537 return 1 | 4532 return 1 |
| 4538 | 4533 |
| 4539 patchset = cl.GetMostRecentPatchset() | 4534 patchset = cl.GetMostRecentPatchset() |
| 4540 if patchset and patchset != cl.GetPatchset(): | 4535 if patchset and patchset != cl.GetPatchset(): |
| 4541 print( | 4536 print( |
| 4542 '\nWARNING Mismatch between local config and server. Did a previous ' | 4537 '\nWARNING Mismatch between local config and server. Did a previous ' |
| 4543 'upload fail?\ngit-cl try always uses latest patchset from rietveld. ' | 4538 'upload fail?\ngit-cl try always uses latest patchset from rietveld. ' |
| 4544 'Continuing using\npatchset %s.\n' % patchset) | 4539 'Continuing using\npatchset %s.\n' % patchset) |
| 4545 if options.luci: | 4540 if options.luci: |
| 4546 trigger_luci_job(cl, masters, options) | 4541 trigger_luci_job(cl, masters, options) |
| 4547 elif not options.use_rietveld: | 4542 elif not options.use_rietveld: |
| 4548 try: | 4543 try: |
| 4549 trigger_try_jobs(auth_config, cl, options, masters, 'git_cl_try') | 4544 trigger_try_jobs(auth_config, cl, options, masters, 'git_cl_try') |
| 4550 except BuildbucketResponseException as ex: | 4545 except BuildbucketResponseException as ex: |
| 4551 print 'ERROR: %s' % ex | 4546 print('ERROR: %s' % ex) |
| 4552 return 1 | 4547 return 1 |
| 4553 except Exception as e: | 4548 except Exception as e: |
| 4554 stacktrace = (''.join(traceback.format_stack()) + traceback.format_exc()) | 4549 stacktrace = (''.join(traceback.format_stack()) + traceback.format_exc()) |
| 4555 print 'ERROR: Exception when trying to trigger tryjobs: %s\n%s' % ( | 4550 print('ERROR: Exception when trying to trigger tryjobs: %s\n%s' % |
| 4556 e, stacktrace) | 4551 (e, stacktrace)) |
| 4557 return 1 | 4552 return 1 |
| 4558 else: | 4553 else: |
| 4559 try: | 4554 try: |
| 4560 cl.RpcServer().trigger_distributed_try_jobs( | 4555 cl.RpcServer().trigger_distributed_try_jobs( |
| 4561 cl.GetIssue(), patchset, options.name, options.clobber, | 4556 cl.GetIssue(), patchset, options.name, options.clobber, |
| 4562 options.revision, masters) | 4557 options.revision, masters) |
| 4563 except urllib2.HTTPError as e: | 4558 except urllib2.HTTPError as e: |
| 4564 if e.code == 404: | 4559 if e.code == 404: |
| 4565 print('404 from rietveld; ' | 4560 print('404 from rietveld; ' |
| 4566 'did you mean to use "git try" instead of "git cl try"?') | 4561 'did you mean to use "git try" instead of "git cl try"?') |
| 4567 return 1 | 4562 return 1 |
| 4568 print('Tried jobs on:') | 4563 print('Tried jobs on:') |
| 4569 | 4564 |
| 4570 for (master, builders) in sorted(masters.iteritems()): | 4565 for (master, builders) in sorted(masters.iteritems()): |
| 4571 if master: | 4566 if master: |
| 4572 print 'Master: %s' % master | 4567 print('Master: %s' % master) |
| 4573 length = max(len(builder) for builder in builders) | 4568 length = max(len(builder) for builder in builders) |
| 4574 for builder in sorted(builders): | 4569 for builder in sorted(builders): |
| 4575 print ' %*s: %s' % (length, builder, ','.join(builders[builder])) | 4570 print(' %*s: %s' % (length, builder, ','.join(builders[builder]))) |
| 4576 return 0 | 4571 return 0 |
| 4577 | 4572 |
| 4578 | 4573 |
| 4579 def CMDtry_results(parser, args): | 4574 def CMDtry_results(parser, args): |
| 4580 group = optparse.OptionGroup(parser, "Try job results options") | 4575 group = optparse.OptionGroup(parser, "Try job results options") |
| 4581 group.add_option( | 4576 group.add_option( |
| 4582 "-p", "--patchset", type=int, help="patchset number if not current.") | 4577 "-p", "--patchset", type=int, help="patchset number if not current.") |
| 4583 group.add_option( | 4578 group.add_option( |
| 4584 "--print-master", action='store_true', help="print master name as well.") | 4579 "--print-master", action='store_true', help="print master name as well.") |
| 4585 group.add_option( | 4580 group.add_option( |
| (...skipping 16 matching lines...) Expand all Loading... |
| 4602 if not options.patchset: | 4597 if not options.patchset: |
| 4603 options.patchset = cl.GetMostRecentPatchset() | 4598 options.patchset = cl.GetMostRecentPatchset() |
| 4604 if options.patchset and options.patchset != cl.GetPatchset(): | 4599 if options.patchset and options.patchset != cl.GetPatchset(): |
| 4605 print( | 4600 print( |
| 4606 '\nWARNING Mismatch between local config and server. Did a previous ' | 4601 '\nWARNING Mismatch between local config and server. Did a previous ' |
| 4607 'upload fail?\ngit-cl try always uses latest patchset from rietveld. ' | 4602 'upload fail?\ngit-cl try always uses latest patchset from rietveld. ' |
| 4608 'Continuing using\npatchset %s.\n' % options.patchset) | 4603 'Continuing using\npatchset %s.\n' % options.patchset) |
| 4609 try: | 4604 try: |
| 4610 jobs = fetch_try_jobs(auth_config, cl, options) | 4605 jobs = fetch_try_jobs(auth_config, cl, options) |
| 4611 except BuildbucketResponseException as ex: | 4606 except BuildbucketResponseException as ex: |
| 4612 print 'Buildbucket error: %s' % ex | 4607 print('Buildbucket error: %s' % ex) |
| 4613 return 1 | 4608 return 1 |
| 4614 except Exception as e: | 4609 except Exception as e: |
| 4615 stacktrace = (''.join(traceback.format_stack()) + traceback.format_exc()) | 4610 stacktrace = (''.join(traceback.format_stack()) + traceback.format_exc()) |
| 4616 print 'ERROR: Exception when trying to fetch tryjobs: %s\n%s' % ( | 4611 print('ERROR: Exception when trying to fetch tryjobs: %s\n%s' % |
| 4617 e, stacktrace) | 4612 (e, stacktrace)) |
| 4618 return 1 | 4613 return 1 |
| 4619 print_tryjobs(options, jobs) | 4614 print_tryjobs(options, jobs) |
| 4620 return 0 | 4615 return 0 |
| 4621 | 4616 |
| 4622 | 4617 |
| 4623 @subcommand.usage('[new upstream branch]') | 4618 @subcommand.usage('[new upstream branch]') |
| 4624 def CMDupstream(parser, args): | 4619 def CMDupstream(parser, args): |
| 4625 """Prints or sets the name of the upstream branch, if any.""" | 4620 """Prints or sets the name of the upstream branch, if any.""" |
| 4626 _, args = parser.parse_args(args) | 4621 _, args = parser.parse_args(args) |
| 4627 if len(args) > 1: | 4622 if len(args) > 1: |
| 4628 parser.error('Unrecognized args: %s' % ' '.join(args)) | 4623 parser.error('Unrecognized args: %s' % ' '.join(args)) |
| 4629 | 4624 |
| 4630 cl = Changelist() | 4625 cl = Changelist() |
| 4631 if args: | 4626 if args: |
| 4632 # One arg means set upstream branch. | 4627 # One arg means set upstream branch. |
| 4633 branch = cl.GetBranch() | 4628 branch = cl.GetBranch() |
| 4634 RunGit(['branch', '--set-upstream', branch, args[0]]) | 4629 RunGit(['branch', '--set-upstream', branch, args[0]]) |
| 4635 cl = Changelist() | 4630 cl = Changelist() |
| 4636 print "Upstream branch set to " + cl.GetUpstreamBranch() | 4631 print('Upstream branch set to %s' % (cl.GetUpstreamBranch(),)) |
| 4637 | 4632 |
| 4638 # Clear configured merge-base, if there is one. | 4633 # Clear configured merge-base, if there is one. |
| 4639 git_common.remove_merge_base(branch) | 4634 git_common.remove_merge_base(branch) |
| 4640 else: | 4635 else: |
| 4641 print cl.GetUpstreamBranch() | 4636 print(cl.GetUpstreamBranch()) |
| 4642 return 0 | 4637 return 0 |
| 4643 | 4638 |
| 4644 | 4639 |
| 4645 def CMDweb(parser, args): | 4640 def CMDweb(parser, args): |
| 4646 """Opens the current CL in the web browser.""" | 4641 """Opens the current CL in the web browser.""" |
| 4647 _, args = parser.parse_args(args) | 4642 _, args = parser.parse_args(args) |
| 4648 if args: | 4643 if args: |
| 4649 parser.error('Unrecognized args: %s' % ' '.join(args)) | 4644 parser.error('Unrecognized args: %s' % ' '.join(args)) |
| 4650 | 4645 |
| 4651 issue_url = Changelist().GetIssueURL() | 4646 issue_url = Changelist().GetIssueURL() |
| 4652 if not issue_url: | 4647 if not issue_url: |
| 4653 print >> sys.stderr, 'ERROR No issue to open' | 4648 print('ERROR No issue to open', file=sys.stderr) |
| 4654 return 1 | 4649 return 1 |
| 4655 | 4650 |
| 4656 webbrowser.open(issue_url) | 4651 webbrowser.open(issue_url) |
| 4657 return 0 | 4652 return 0 |
| 4658 | 4653 |
| 4659 | 4654 |
| 4660 def CMDset_commit(parser, args): | 4655 def CMDset_commit(parser, args): |
| 4661 """Sets the commit bit to trigger the Commit Queue.""" | 4656 """Sets the commit bit to trigger the Commit Queue.""" |
| 4662 parser.add_option('-d', '--dry-run', action='store_true', | 4657 parser.add_option('-d', '--dry-run', action='store_true', |
| 4663 help='trigger in dry run mode') | 4658 help='trigger in dry run mode') |
| (...skipping 245 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 4909 try: | 4904 try: |
| 4910 command = [dart_format.FindDartFmtToolInChromiumTree()] | 4905 command = [dart_format.FindDartFmtToolInChromiumTree()] |
| 4911 if not opts.dry_run and not opts.diff: | 4906 if not opts.dry_run and not opts.diff: |
| 4912 command.append('-w') | 4907 command.append('-w') |
| 4913 command.extend(dart_diff_files) | 4908 command.extend(dart_diff_files) |
| 4914 | 4909 |
| 4915 stdout = RunCommand(command, cwd=top_dir) | 4910 stdout = RunCommand(command, cwd=top_dir) |
| 4916 if opts.dry_run and stdout: | 4911 if opts.dry_run and stdout: |
| 4917 return_value = 2 | 4912 return_value = 2 |
| 4918 except dart_format.NotFoundError as e: | 4913 except dart_format.NotFoundError as e: |
| 4919 print ('Warning: Unable to check Dart code formatting. Dart SDK not ' + | 4914 print('Warning: Unable to check Dart code formatting. Dart SDK not ' |
| 4920 'found in this checkout. Files in other languages are still ' + | 4915 'found in this checkout. Files in other languages are still ' |
| 4921 'formatted.') | 4916 'formatted.') |
| 4922 | 4917 |
| 4923 # Format GN build files. Always run on full build files for canonical form. | 4918 # Format GN build files. Always run on full build files for canonical form. |
| 4924 if gn_diff_files: | 4919 if gn_diff_files: |
| 4925 cmd = ['gn', 'format'] | 4920 cmd = ['gn', 'format'] |
| 4926 if not opts.dry_run and not opts.diff: | 4921 if not opts.dry_run and not opts.diff: |
| 4927 cmd.append('--in-place') | 4922 cmd.append('--in-place') |
| 4928 for gn_diff_file in gn_diff_files: | 4923 for gn_diff_file in gn_diff_files: |
| 4929 stdout = RunCommand(cmd + [gn_diff_file], | 4924 stdout = RunCommand(cmd + [gn_diff_file], |
| 4930 shell=sys.platform == 'win32', | 4925 shell=sys.platform == 'win32', |
| 4931 cwd=top_dir) | 4926 cwd=top_dir) |
| (...skipping 23 matching lines...) Expand all Loading... |
| 4955 r'branch\..*\.%s' % issueprefix], | 4950 r'branch\..*\.%s' % issueprefix], |
| 4956 error_ok=True) | 4951 error_ok=True) |
| 4957 for key, issue in [x.split() for x in output.splitlines()]: | 4952 for key, issue in [x.split() for x in output.splitlines()]: |
| 4958 if issue == target_issue: | 4953 if issue == target_issue: |
| 4959 yield re.sub(r'branch\.(.*)\.%s' % issueprefix, r'\1', key) | 4954 yield re.sub(r'branch\.(.*)\.%s' % issueprefix, r'\1', key) |
| 4960 | 4955 |
| 4961 branches = [] | 4956 branches = [] |
| 4962 for cls in _CODEREVIEW_IMPLEMENTATIONS.values(): | 4957 for cls in _CODEREVIEW_IMPLEMENTATIONS.values(): |
| 4963 branches.extend(find_issues(cls.IssueSettingSuffix())) | 4958 branches.extend(find_issues(cls.IssueSettingSuffix())) |
| 4964 if len(branches) == 0: | 4959 if len(branches) == 0: |
| 4965 print 'No branch found for issue %s.' % target_issue | 4960 print('No branch found for issue %s.' % target_issue) |
| 4966 return 1 | 4961 return 1 |
| 4967 if len(branches) == 1: | 4962 if len(branches) == 1: |
| 4968 RunGit(['checkout', branches[0]]) | 4963 RunGit(['checkout', branches[0]]) |
| 4969 else: | 4964 else: |
| 4970 print 'Multiple branches match issue %s:' % target_issue | 4965 print('Multiple branches match issue %s:' % target_issue) |
| 4971 for i in range(len(branches)): | 4966 for i in range(len(branches)): |
| 4972 print '%d: %s' % (i, branches[i]) | 4967 print('%d: %s' % (i, branches[i])) |
| 4973 which = raw_input('Choose by index: ') | 4968 which = raw_input('Choose by index: ') |
| 4974 try: | 4969 try: |
| 4975 RunGit(['checkout', branches[int(which)]]) | 4970 RunGit(['checkout', branches[int(which)]]) |
| 4976 except (IndexError, ValueError): | 4971 except (IndexError, ValueError): |
| 4977 print 'Invalid selection, not checking out any branch.' | 4972 print('Invalid selection, not checking out any branch.') |
| 4978 return 1 | 4973 return 1 |
| 4979 | 4974 |
| 4980 return 0 | 4975 return 0 |
| 4981 | 4976 |
| 4982 | 4977 |
| 4983 def CMDlol(parser, args): | 4978 def CMDlol(parser, args): |
| 4984 # This command is intentionally undocumented. | 4979 # This command is intentionally undocumented. |
| 4985 print zlib.decompress(base64.b64decode( | 4980 print(zlib.decompress(base64.b64decode( |
| 4986 'eNptkLEOwyAMRHe+wupCIqW57v0Vq84WqWtXyrcXnCBsmgMJ+/SSAxMZgRB6NzE' | 4981 'eNptkLEOwyAMRHe+wupCIqW57v0Vq84WqWtXyrcXnCBsmgMJ+/SSAxMZgRB6NzE' |
| 4987 'E2ObgCKJooYdu4uAQVffUEoE1sRQLxAcqzd7uK2gmStrll1ucV3uZyaY5sXyDd9' | 4982 'E2ObgCKJooYdu4uAQVffUEoE1sRQLxAcqzd7uK2gmStrll1ucV3uZyaY5sXyDd9' |
| 4988 'JAnN+lAXsOMJ90GANAi43mq5/VeeacylKVgi8o6F1SC63FxnagHfJUTfUYdCR/W' | 4983 'JAnN+lAXsOMJ90GANAi43mq5/VeeacylKVgi8o6F1SC63FxnagHfJUTfUYdCR/W' |
| 4989 'Ofe+0dHL7PicpytKP750Fh1q2qnLVof4w8OZWNY')) | 4984 'Ofe+0dHL7PicpytKP750Fh1q2qnLVof4w8OZWNY'))) |
| 4990 return 0 | 4985 return 0 |
| 4991 | 4986 |
| 4992 | 4987 |
| 4993 class OptionParser(optparse.OptionParser): | 4988 class OptionParser(optparse.OptionParser): |
| 4994 """Creates the option parse and add --verbose support.""" | 4989 """Creates the option parse and add --verbose support.""" |
| 4995 def __init__(self, *args, **kwargs): | 4990 def __init__(self, *args, **kwargs): |
| 4996 optparse.OptionParser.__init__( | 4991 optparse.OptionParser.__init__( |
| 4997 self, *args, prog='git cl', version=__version__, **kwargs) | 4992 self, *args, prog='git cl', version=__version__, **kwargs) |
| 4998 self.add_option( | 4993 self.add_option( |
| 4999 '-v', '--verbose', action='count', default=0, | 4994 '-v', '--verbose', action='count', default=0, |
| 5000 help='Use 2 times for more debugging info') | 4995 help='Use 2 times for more debugging info') |
| 5001 | 4996 |
| 5002 def parse_args(self, args=None, values=None): | 4997 def parse_args(self, args=None, values=None): |
| 5003 options, args = optparse.OptionParser.parse_args(self, args, values) | 4998 options, args = optparse.OptionParser.parse_args(self, args, values) |
| 5004 levels = [logging.WARNING, logging.INFO, logging.DEBUG] | 4999 levels = [logging.WARNING, logging.INFO, logging.DEBUG] |
| 5005 logging.basicConfig(level=levels[min(options.verbose, len(levels) - 1)]) | 5000 logging.basicConfig(level=levels[min(options.verbose, len(levels) - 1)]) |
| 5006 return options, args | 5001 return options, args |
| 5007 | 5002 |
| 5008 | 5003 |
| 5009 def main(argv): | 5004 def main(argv): |
| 5010 if sys.hexversion < 0x02060000: | 5005 if sys.hexversion < 0x02060000: |
| 5011 print >> sys.stderr, ( | 5006 print('\nYour python version %s is unsupported, please upgrade.\n' % |
| 5012 '\nYour python version %s is unsupported, please upgrade.\n' % | 5007 (sys.version.split(' ', 1)[0],), file=sys.stderr) |
| 5013 sys.version.split(' ', 1)[0]) | |
| 5014 return 2 | 5008 return 2 |
| 5015 | 5009 |
| 5016 # Reload settings. | 5010 # Reload settings. |
| 5017 global settings | 5011 global settings |
| 5018 settings = Settings() | 5012 settings = Settings() |
| 5019 | 5013 |
| 5020 colorize_CMDstatus_doc() | 5014 colorize_CMDstatus_doc() |
| 5021 dispatcher = subcommand.CommandDispatcher(__name__) | 5015 dispatcher = subcommand.CommandDispatcher(__name__) |
| 5022 try: | 5016 try: |
| 5023 return dispatcher.execute(OptionParser(), argv) | 5017 return dispatcher.execute(OptionParser(), argv) |
| (...skipping 11 matching lines...) Expand all Loading... |
| 5035 if __name__ == '__main__': | 5029 if __name__ == '__main__': |
| 5036 # These affect sys.stdout so do it outside of main() to simplify mocks in | 5030 # These affect sys.stdout so do it outside of main() to simplify mocks in |
| 5037 # unit testing. | 5031 # unit testing. |
| 5038 fix_encoding.fix_encoding() | 5032 fix_encoding.fix_encoding() |
| 5039 setup_color.init() | 5033 setup_color.init() |
| 5040 try: | 5034 try: |
| 5041 sys.exit(main(sys.argv[1:])) | 5035 sys.exit(main(sys.argv[1:])) |
| 5042 except KeyboardInterrupt: | 5036 except KeyboardInterrupt: |
| 5043 sys.stderr.write('interrupted\n') | 5037 sys.stderr.write('interrupted\n') |
| 5044 sys.exit(1) | 5038 sys.exit(1) |
| OLD | NEW |