| 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.""" | 8 """A git-command for integrating reviews on Rietveld.""" |
| 9 | 9 |
| 10 import json | 10 import json |
| (...skipping 418 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 429 | 429 |
| 430 def GetRemoteUrl(self): | 430 def GetRemoteUrl(self): |
| 431 """Return the configured remote URL, e.g. 'git://example.org/foo.git/'. | 431 """Return the configured remote URL, e.g. 'git://example.org/foo.git/'. |
| 432 | 432 |
| 433 Returns None if there is no remote. | 433 Returns None if there is no remote. |
| 434 """ | 434 """ |
| 435 remote = self.GetRemote() | 435 remote = self.GetRemote() |
| 436 return RunGit(['config', 'remote.%s.url' % remote], error_ok=True).strip() | 436 return RunGit(['config', 'remote.%s.url' % remote], error_ok=True).strip() |
| 437 | 437 |
| 438 def GetIssue(self): | 438 def GetIssue(self): |
| 439 """Returns the issue number as a int or None if not set.""" |
| 439 if not self.has_issue: | 440 if not self.has_issue: |
| 440 issue = RunGit(['config', self._IssueSetting()], error_ok=True).strip() | 441 issue = RunGit(['config', self._IssueSetting()], error_ok=True).strip() |
| 441 if issue: | 442 if issue: |
| 442 self.issue = issue | 443 self.issue = int(issue) |
| 443 else: | 444 else: |
| 444 self.issue = None | 445 self.issue = None |
| 445 self.has_issue = True | 446 self.has_issue = True |
| 446 return self.issue | 447 return self.issue |
| 447 | 448 |
| 448 def GetRietveldServer(self): | 449 def GetRietveldServer(self): |
| 449 if not self.rietveld_server: | 450 if not self.rietveld_server: |
| 450 # If we're on a branch then get the server potentially associated | 451 # If we're on a branch then get the server potentially associated |
| 451 # with that branch. | 452 # with that branch. |
| 452 if self.GetIssue(): | 453 if self.GetIssue(): |
| 453 self.rietveld_server = gclient_utils.UpgradeToHttps(RunGit( | 454 self.rietveld_server = gclient_utils.UpgradeToHttps(RunGit( |
| 454 ['config', self._RietveldServer()], error_ok=True).strip()) | 455 ['config', self._RietveldServer()], error_ok=True).strip()) |
| 455 if not self.rietveld_server: | 456 if not self.rietveld_server: |
| 456 self.rietveld_server = settings.GetDefaultServerUrl() | 457 self.rietveld_server = settings.GetDefaultServerUrl() |
| 457 return self.rietveld_server | 458 return self.rietveld_server |
| 458 | 459 |
| 459 def GetIssueURL(self): | 460 def GetIssueURL(self): |
| 460 """Get the URL for a particular issue.""" | 461 """Get the URL for a particular issue.""" |
| 461 return '%s/%s' % (self.GetRietveldServer(), self.GetIssue()) | 462 return '%s/%s' % (self.GetRietveldServer(), self.GetIssue()) |
| 462 | 463 |
| 463 def GetDescription(self, pretty=False): | 464 def GetDescription(self, pretty=False): |
| 464 if not self.has_description: | 465 if not self.has_description: |
| 465 if self.GetIssue(): | 466 if self.GetIssue(): |
| 466 issue = int(self.GetIssue()) | 467 issue = self.GetIssue() |
| 467 try: | 468 try: |
| 468 self.description = self.RpcServer().get_description(issue).strip() | 469 self.description = self.RpcServer().get_description(issue).strip() |
| 469 except urllib2.HTTPError, e: | 470 except urllib2.HTTPError, e: |
| 470 if e.code == 404: | 471 if e.code == 404: |
| 471 DieWithError( | 472 DieWithError( |
| 472 ('\nWhile fetching the description for issue %d, received a ' | 473 ('\nWhile fetching the description for issue %d, received a ' |
| 473 '404 (not found)\n' | 474 '404 (not found)\n' |
| 474 'error. It is likely that you deleted this ' | 475 'error. It is likely that you deleted this ' |
| 475 'issue on the server. If this is the\n' | 476 'issue on the server. If this is the\n' |
| 476 'case, please run\n\n' | 477 'case, please run\n\n' |
| 477 ' git cl issue 0\n\n' | 478 ' git cl issue 0\n\n' |
| 478 'to clear the association with the deleted issue. Then run ' | 479 'to clear the association with the deleted issue. Then run ' |
| 479 'this command again.') % issue) | 480 'this command again.') % issue) |
| 480 else: | 481 else: |
| 481 DieWithError( | 482 DieWithError( |
| 482 '\nFailed to fetch issue description. HTTP error ' + e.code) | 483 '\nFailed to fetch issue description. HTTP error ' + e.code) |
| 483 self.has_description = True | 484 self.has_description = True |
| 484 if pretty: | 485 if pretty: |
| 485 wrapper = textwrap.TextWrapper() | 486 wrapper = textwrap.TextWrapper() |
| 486 wrapper.initial_indent = wrapper.subsequent_indent = ' ' | 487 wrapper.initial_indent = wrapper.subsequent_indent = ' ' |
| 487 return wrapper.fill(self.description) | 488 return wrapper.fill(self.description) |
| 488 return self.description | 489 return self.description |
| 489 | 490 |
| 490 def GetPatchset(self): | 491 def GetPatchset(self): |
| 492 """Returns the patchset number as a int or None if not set.""" |
| 491 if not self.has_patchset: | 493 if not self.has_patchset: |
| 492 patchset = RunGit(['config', self._PatchsetSetting()], | 494 patchset = RunGit(['config', self._PatchsetSetting()], |
| 493 error_ok=True).strip() | 495 error_ok=True).strip() |
| 494 if patchset: | 496 if patchset: |
| 495 self.patchset = patchset | 497 self.patchset = int(patchset) |
| 496 else: | 498 else: |
| 497 self.patchset = None | 499 self.patchset = None |
| 498 self.has_patchset = True | 500 self.has_patchset = True |
| 499 return self.patchset | 501 return self.patchset |
| 500 | 502 |
| 501 def SetPatchset(self, patchset): | 503 def SetPatchset(self, patchset): |
| 502 """Set this branch's patchset. If patchset=0, clears the patchset.""" | 504 """Set this branch's patchset. If patchset=0, clears the patchset.""" |
| 503 if patchset: | 505 if patchset: |
| 504 RunGit(['config', self._PatchsetSetting(), str(patchset)]) | 506 RunGit(['config', self._PatchsetSetting(), str(patchset)]) |
| 505 else: | 507 else: |
| (...skipping 29 matching lines...) Expand all Loading... |
| 535 files = scm.GIT.CaptureStatus([root], '.', upstream_branch) | 537 files = scm.GIT.CaptureStatus([root], '.', upstream_branch) |
| 536 except subprocess2.CalledProcessError: | 538 except subprocess2.CalledProcessError: |
| 537 DieWithError( | 539 DieWithError( |
| 538 ('\nFailed to diff against upstream branch %s!\n\n' | 540 ('\nFailed to diff against upstream branch %s!\n\n' |
| 539 'This branch probably doesn\'t exist anymore. To reset the\n' | 541 'This branch probably doesn\'t exist anymore. To reset the\n' |
| 540 'tracking branch, please run\n' | 542 'tracking branch, please run\n' |
| 541 ' git branch --set-upstream %s trunk\n' | 543 ' git branch --set-upstream %s trunk\n' |
| 542 'replacing trunk with origin/master or the relevant branch') % | 544 'replacing trunk with origin/master or the relevant branch') % |
| 543 (upstream_branch, self.GetBranch())) | 545 (upstream_branch, self.GetBranch())) |
| 544 | 546 |
| 545 issue = ConvertToInteger(self.GetIssue()) | 547 issue = self.GetIssue() |
| 546 patchset = ConvertToInteger(self.GetPatchset()) | 548 patchset = self.GetPatchset() |
| 547 if issue: | 549 if issue: |
| 548 description = self.GetDescription() | 550 description = self.GetDescription() |
| 549 else: | 551 else: |
| 550 # If the change was never uploaded, use the log messages of all commits | 552 # If the change was never uploaded, use the log messages of all commits |
| 551 # up to the branch point, as git cl upload will prefill the description | 553 # up to the branch point, as git cl upload will prefill the description |
| 552 # with these log messages. | 554 # with these log messages. |
| 553 description = RunCommand(['git', 'log', '--pretty=format:%s%n%n%b', | 555 description = RunCommand(['git', 'log', '--pretty=format:%s%n%n%b', |
| 554 '%s...' % (upstream_branch)]).strip() | 556 '%s...' % (upstream_branch)]).strip() |
| 555 | 557 |
| 556 if not author: | 558 if not author: |
| (...skipping 22 matching lines...) Expand all Loading... |
| 579 verbose=verbose, output_stream=sys.stdout, input_stream=sys.stdin, | 581 verbose=verbose, output_stream=sys.stdout, input_stream=sys.stdin, |
| 580 default_presubmit=None, may_prompt=may_prompt, | 582 default_presubmit=None, may_prompt=may_prompt, |
| 581 rietveld_obj=self.RpcServer()) | 583 rietveld_obj=self.RpcServer()) |
| 582 except presubmit_support.PresubmitFailure, e: | 584 except presubmit_support.PresubmitFailure, e: |
| 583 DieWithError( | 585 DieWithError( |
| 584 ('%s\nMaybe your depot_tools is out of date?\n' | 586 ('%s\nMaybe your depot_tools is out of date?\n' |
| 585 'If all fails, contact maruel@') % e) | 587 'If all fails, contact maruel@') % e) |
| 586 | 588 |
| 587 def CloseIssue(self): | 589 def CloseIssue(self): |
| 588 """Updates the description and closes the issue.""" | 590 """Updates the description and closes the issue.""" |
| 589 issue = int(self.GetIssue()) | 591 issue = self.GetIssue() |
| 590 self.RpcServer().update_description(issue, self.description) | 592 self.RpcServer().update_description(issue, self.description) |
| 591 return self.RpcServer().close_issue(issue) | 593 return self.RpcServer().close_issue(issue) |
| 592 | 594 |
| 593 def SetFlag(self, flag, value): | 595 def SetFlag(self, flag, value): |
| 594 """Patchset must match.""" | 596 """Patchset must match.""" |
| 595 if not self.GetPatchset(): | 597 if not self.GetPatchset(): |
| 596 DieWithError('The patchset needs to match. Send another patchset.') | 598 DieWithError('The patchset needs to match. Send another patchset.') |
| 597 try: | 599 try: |
| 598 return self.RpcServer().set_flag( | 600 return self.RpcServer().set_flag( |
| 599 int(self.GetIssue()), int(self.GetPatchset()), flag, value) | 601 self.GetIssue(), self.GetPatchset(), flag, value) |
| 600 except urllib2.HTTPError, e: | 602 except urllib2.HTTPError, e: |
| 601 if e.code == 404: | 603 if e.code == 404: |
| 602 DieWithError('The issue %s doesn\'t exist.' % self.GetIssue()) | 604 DieWithError('The issue %s doesn\'t exist.' % self.GetIssue()) |
| 603 if e.code == 403: | 605 if e.code == 403: |
| 604 DieWithError( | 606 DieWithError( |
| 605 ('Access denied to issue %s. Maybe the patchset %s doesn\'t ' | 607 ('Access denied to issue %s. Maybe the patchset %s doesn\'t ' |
| 606 'match?') % (self.GetIssue(), self.GetPatchset())) | 608 'match?') % (self.GetIssue(), self.GetPatchset())) |
| 607 raise | 609 raise |
| 608 | 610 |
| 609 def RpcServer(self): | 611 def RpcServer(self): |
| (...skipping 247 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 857 url = cl.GetIssueURL() | 859 url = cl.GetIssueURL() |
| 858 if url: | 860 if url: |
| 859 print url | 861 print url |
| 860 else: | 862 else: |
| 861 print | 863 print |
| 862 print 'Current branch:', | 864 print 'Current branch:', |
| 863 if not cl.GetIssue(): | 865 if not cl.GetIssue(): |
| 864 print 'no issue assigned.' | 866 print 'no issue assigned.' |
| 865 return 0 | 867 return 0 |
| 866 print cl.GetBranch() | 868 print cl.GetBranch() |
| 867 print 'Issue number:', cl.GetIssue(), '(%s)' % cl.GetIssueURL() | 869 print 'Issue number: %s (%s)' % (cl.GetIssue(), cl.GetIssueURL()) |
| 868 print 'Issue description:' | 870 print 'Issue description:' |
| 869 print cl.GetDescription(pretty=True) | 871 print cl.GetDescription(pretty=True) |
| 870 return 0 | 872 return 0 |
| 871 | 873 |
| 872 | 874 |
| 873 @usage('[issue_number]') | 875 @usage('[issue_number]') |
| 874 def CMDissue(parser, args): | 876 def CMDissue(parser, args): |
| 875 """Set or display the current code review issue number. | 877 """Set or display the current code review issue number. |
| 876 | 878 |
| 877 Pass issue number 0 to clear the current issue. | 879 Pass issue number 0 to clear the current issue. |
| 878 """ | 880 """ |
| 879 _, args = parser.parse_args(args) | 881 _, args = parser.parse_args(args) |
| 880 | 882 |
| 881 cl = Changelist() | 883 cl = Changelist() |
| 882 if len(args) > 0: | 884 if len(args) > 0: |
| 883 try: | 885 try: |
| 884 issue = int(args[0]) | 886 issue = int(args[0]) |
| 885 except ValueError: | 887 except ValueError: |
| 886 DieWithError('Pass a number to set the issue or none to list it.\n' | 888 DieWithError('Pass a number to set the issue or none to list it.\n' |
| 887 'Maybe you want to run git cl status?') | 889 'Maybe you want to run git cl status?') |
| 888 cl.SetIssue(issue) | 890 cl.SetIssue(issue) |
| 889 print 'Issue number:', cl.GetIssue(), '(%s)' % cl.GetIssueURL() | 891 print 'Issue number: %s (%s)' % (cl.GetIssue(), cl.GetIssueURL()) |
| 890 return 0 | 892 return 0 |
| 891 | 893 |
| 892 | 894 |
| 893 def CMDcomments(parser, args): | 895 def CMDcomments(parser, args): |
| 894 """show review comments of the current changelist""" | 896 """show review comments of the current changelist""" |
| 895 (_, args) = parser.parse_args(args) | 897 (_, args) = parser.parse_args(args) |
| 896 if args: | 898 if args: |
| 897 parser.error('Unsupported argument: %s' % args) | 899 parser.error('Unsupported argument: %s' % args) |
| 898 | 900 |
| 899 cl = Changelist() | 901 cl = Changelist() |
| (...skipping 13 matching lines...) Expand all Loading... |
| 913 log_args = [args[0] + '..'] | 915 log_args = [args[0] + '..'] |
| 914 elif len(args) == 1 and args[0].endswith('...'): | 916 elif len(args) == 1 and args[0].endswith('...'): |
| 915 log_args = [args[0][:-1]] | 917 log_args = [args[0][:-1]] |
| 916 elif len(args) == 2: | 918 elif len(args) == 2: |
| 917 log_args = [args[0] + '..' + args[1]] | 919 log_args = [args[0] + '..' + args[1]] |
| 918 else: | 920 else: |
| 919 log_args = args[:] # Hope for the best! | 921 log_args = args[:] # Hope for the best! |
| 920 return RunGit(['log', '--pretty=format:%s\n\n%b'] + log_args) | 922 return RunGit(['log', '--pretty=format:%s\n\n%b'] + log_args) |
| 921 | 923 |
| 922 | 924 |
| 923 def ConvertToInteger(inputval): | |
| 924 """Convert a string to integer, but returns either an int or None.""" | |
| 925 try: | |
| 926 return int(inputval) | |
| 927 except (TypeError, ValueError): | |
| 928 return None | |
| 929 | |
| 930 | |
| 931 def CMDpresubmit(parser, args): | 925 def CMDpresubmit(parser, args): |
| 932 """run presubmit tests on the current changelist""" | 926 """run presubmit tests on the current changelist""" |
| 933 parser.add_option('--upload', action='store_true', | 927 parser.add_option('--upload', action='store_true', |
| 934 help='Run upload hook instead of the push/dcommit hook') | 928 help='Run upload hook instead of the push/dcommit hook') |
| 935 (options, args) = parser.parse_args(args) | 929 (options, args) = parser.parse_args(args) |
| 936 | 930 |
| 937 # Make sure index is up-to-date before running diff-index. | 931 # Make sure index is up-to-date before running diff-index. |
| 938 RunGit(['update-index', '--refresh', '-q'], error_ok=True) | 932 RunGit(['update-index', '--refresh', '-q'], error_ok=True) |
| 939 if RunGit(['diff-index', 'HEAD']): | 933 if RunGit(['diff-index', 'HEAD']): |
| 940 # TODO(maruel): Is this really necessary? | 934 # TODO(maruel): Is this really necessary? |
| (...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1004 change_desc = None | 998 change_desc = None |
| 1005 | 999 |
| 1006 if cl.GetIssue(): | 1000 if cl.GetIssue(): |
| 1007 if options.title: | 1001 if options.title: |
| 1008 upload_args.extend(['--title', options.title]) | 1002 upload_args.extend(['--title', options.title]) |
| 1009 elif options.message: | 1003 elif options.message: |
| 1010 # TODO(rogerta): for now, the -m option will also set the --title option | 1004 # TODO(rogerta): for now, the -m option will also set the --title option |
| 1011 # for upload.py. Soon this will be changed to set the --message option. | 1005 # for upload.py. Soon this will be changed to set the --message option. |
| 1012 # Will wait until people are used to typing -t instead of -m. | 1006 # Will wait until people are used to typing -t instead of -m. |
| 1013 upload_args.extend(['--title', options.message]) | 1007 upload_args.extend(['--title', options.message]) |
| 1014 upload_args.extend(['--issue', cl.GetIssue()]) | 1008 upload_args.extend(['--issue', str(cl.GetIssue())]) |
| 1015 print ("This branch is associated with issue %s. " | 1009 print ("This branch is associated with issue %s. " |
| 1016 "Adding patch to that issue." % cl.GetIssue()) | 1010 "Adding patch to that issue." % cl.GetIssue()) |
| 1017 else: | 1011 else: |
| 1018 if options.title: | 1012 if options.title: |
| 1019 upload_args.extend(['--title', options.title]) | 1013 upload_args.extend(['--title', options.title]) |
| 1020 message = options.message or CreateDescriptionFromLog(args) | 1014 message = options.message or CreateDescriptionFromLog(args) |
| 1021 change_desc = ChangeDescription(message, options.reviewers) | 1015 change_desc = ChangeDescription(message, options.reviewers) |
| 1022 if not options.force: | 1016 if not options.force: |
| 1023 change_desc.Prompt() | 1017 change_desc.Prompt() |
| 1024 change_desc.ParseDescription() | 1018 change_desc.ParseDescription() |
| (...skipping 373 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1398 help="don't commit after patch applies") | 1392 help="don't commit after patch applies") |
| 1399 (options, args) = parser.parse_args(args) | 1393 (options, args) = parser.parse_args(args) |
| 1400 if len(args) != 1: | 1394 if len(args) != 1: |
| 1401 parser.print_help() | 1395 parser.print_help() |
| 1402 return 1 | 1396 return 1 |
| 1403 issue_arg = args[0] | 1397 issue_arg = args[0] |
| 1404 | 1398 |
| 1405 # TODO(maruel): Use apply_issue.py | 1399 # TODO(maruel): Use apply_issue.py |
| 1406 # TODO(ukai): use gerrit-cherry-pick for gerrit repository? | 1400 # TODO(ukai): use gerrit-cherry-pick for gerrit repository? |
| 1407 | 1401 |
| 1408 if re.match(r'\d+', issue_arg): | 1402 if issue_arg.isdigit(): |
| 1409 # Input is an issue id. Figure out the URL. | 1403 # Input is an issue id. Figure out the URL. |
| 1410 issue = issue_arg | 1404 issue = int(issue_arg) |
| 1411 patch_data = Changelist().GetPatchSetDiff(issue) | 1405 patch_data = Changelist().GetPatchSetDiff(issue) |
| 1412 else: | 1406 else: |
| 1413 # Assume it's a URL to the patch. Default to https. | 1407 # Assume it's a URL to the patch. Default to https. |
| 1414 issue_url = gclient_utils.UpgradeToHttps(issue_arg) | 1408 issue_url = gclient_utils.UpgradeToHttps(issue_arg) |
| 1415 match = re.match(r'.*?/issue(\d+)_\d+.diff', issue_url) | 1409 match = re.match(r'.*?/issue(\d+)_\d+.diff', issue_url) |
| 1416 if not match: | 1410 if not match: |
| 1417 DieWithError('Must pass an issue ID or full URL for ' | 1411 DieWithError('Must pass an issue ID or full URL for ' |
| 1418 '\'Download raw patch set\'') | 1412 '\'Download raw patch set\'') |
| 1419 issue = match.group(1) | 1413 issue = int(match.group(1)) |
| 1420 patch_data = urllib2.urlopen(issue_arg).read() | 1414 patch_data = urllib2.urlopen(issue_arg).read() |
| 1421 | 1415 |
| 1422 if options.newbranch: | 1416 if options.newbranch: |
| 1423 if options.force: | 1417 if options.force: |
| 1424 RunGit(['branch', '-D', options.newbranch], | 1418 RunGit(['branch', '-D', options.newbranch], |
| 1425 stderr=subprocess2.PIPE, error_ok=True) | 1419 stderr=subprocess2.PIPE, error_ok=True) |
| 1426 RunGit(['checkout', '-b', options.newbranch, | 1420 RunGit(['checkout', '-b', options.newbranch, |
| 1427 Changelist().GetUpstreamBranch()]) | 1421 Changelist().GetUpstreamBranch()]) |
| 1428 | 1422 |
| 1429 # Switch up to the top-level directory, if necessary, in preparation for | 1423 # Switch up to the top-level directory, if necessary, in preparation for |
| (...skipping 187 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1617 'and retry or visit go/isgaeup.\n%s') % (e.code, str(e))) | 1611 'and retry or visit go/isgaeup.\n%s') % (e.code, str(e))) |
| 1618 | 1612 |
| 1619 # Not a known command. Default to help. | 1613 # Not a known command. Default to help. |
| 1620 GenUsage(parser, 'help') | 1614 GenUsage(parser, 'help') |
| 1621 return CMDhelp(parser, argv) | 1615 return CMDhelp(parser, argv) |
| 1622 | 1616 |
| 1623 | 1617 |
| 1624 if __name__ == '__main__': | 1618 if __name__ == '__main__': |
| 1625 fix_encoding.fix_encoding() | 1619 fix_encoding.fix_encoding() |
| 1626 sys.exit(main(sys.argv[1:])) | 1620 sys.exit(main(sys.argv[1:])) |
| OLD | NEW |