| OLD | NEW |
| 1 #!/usr/bin/python | 1 #!/usr/bin/python |
| 2 # git-cl -- a git-command for integrating reviews on Rietveld | 2 # git-cl -- a git-command for integrating reviews on Rietveld |
| 3 # Copyright (C) 2008 Evan Martin <martine@danga.com> | 3 # Copyright (C) 2008 Evan Martin <martine@danga.com> |
| 4 | 4 |
| 5 import errno | 5 import errno |
| 6 import logging | 6 import logging |
| 7 import optparse | 7 import optparse |
| 8 import os | 8 import os |
| 9 import re | 9 import re |
| 10 import StringIO | 10 import StringIO |
| (...skipping 464 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 475 | 475 |
| 476 SetProperty(settings.GetCCList(), 'CC list', 'cc') | 476 SetProperty(settings.GetCCList(), 'CC list', 'cc') |
| 477 SetProperty(settings.GetTreeStatusUrl(error_ok=True), 'Tree status URL', | 477 SetProperty(settings.GetTreeStatusUrl(error_ok=True), 'Tree status URL', |
| 478 'tree-status-url') | 478 'tree-status-url') |
| 479 SetProperty(settings.GetViewVCUrl(), 'ViewVC URL', 'viewvc-url') | 479 SetProperty(settings.GetViewVCUrl(), 'ViewVC URL', 'viewvc-url') |
| 480 | 480 |
| 481 # TODO: configure a default branch to diff against, rather than this | 481 # TODO: configure a default branch to diff against, rather than this |
| 482 # svn-based hackery. | 482 # svn-based hackery. |
| 483 | 483 |
| 484 | 484 |
| 485 class HookResults(object): | |
| 486 """Contains the parsed output of the presubmit hooks.""" | |
| 487 def __init__(self, output_from_hooks=None): | |
| 488 self.reviewers = [] | |
| 489 self.output = None | |
| 490 self._ParseOutputFromHooks(output_from_hooks) | |
| 491 | |
| 492 def _ParseOutputFromHooks(self, output_from_hooks): | |
| 493 if not output_from_hooks: | |
| 494 return | |
| 495 lines = [] | |
| 496 reviewers = [] | |
| 497 reviewer_regexp = re.compile('ADD: R=(.+)') | |
| 498 for l in output_from_hooks.splitlines(): | |
| 499 m = reviewer_regexp.match(l) | |
| 500 if m: | |
| 501 reviewers.extend(m.group(1).split(',')) | |
| 502 else: | |
| 503 lines.append(l) | |
| 504 self.output = '\n'.join(lines) | |
| 505 self.reviewers = ','.join(reviewers) | |
| 506 | |
| 507 | |
| 508 class ChangeDescription(object): | 485 class ChangeDescription(object): |
| 509 """Contains a parsed form of the change description.""" | 486 """Contains a parsed form of the change description.""" |
| 510 def __init__(self, subject, log_desc, reviewers): | 487 def __init__(self, subject, log_desc, reviewers): |
| 511 self.subject = subject | 488 self.subject = subject |
| 512 self.log_desc = log_desc | 489 self.log_desc = log_desc |
| 513 self.reviewers = reviewers | 490 self.reviewers = reviewers |
| 514 self.description = self.log_desc | 491 self.description = self.log_desc |
| 515 | 492 |
| 516 def Update(self): | 493 def Update(self): |
| 517 initial_text = """# Enter a description of the change. | 494 initial_text = """# Enter a description of the change. |
| (...skipping 247 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 765 issue, patchset) | 742 issue, patchset) |
| 766 | 743 |
| 767 # Apply watchlists on upload. | 744 # Apply watchlists on upload. |
| 768 if not committing: | 745 if not committing: |
| 769 watchlist = watchlists.Watchlists(change.RepositoryRoot()) | 746 watchlist = watchlists.Watchlists(change.RepositoryRoot()) |
| 770 files = [f.LocalPath() for f in change.AffectedFiles()] | 747 files = [f.LocalPath() for f in change.AffectedFiles()] |
| 771 watchers = watchlist.GetWatchersForPaths(files) | 748 watchers = watchlist.GetWatchersForPaths(files) |
| 772 RunCommand(['git', 'config', '--replace-all', | 749 RunCommand(['git', 'config', '--replace-all', |
| 773 'rietveld.extracc', ','.join(watchers)]) | 750 'rietveld.extracc', ','.join(watchers)]) |
| 774 | 751 |
| 775 output = StringIO.StringIO() | 752 output = presubmit_support.DoPresubmitChecks(change, committing, |
| 776 should_continue = presubmit_support.DoPresubmitChecks(change, committing, | 753 verbose=False, output_stream=sys.stdout, input_stream=sys.stdin, |
| 777 verbose=None, output_stream=output, input_stream=sys.stdin, | 754 default_presubmit=None, may_prompt=may_prompt, tbr=tbr, |
| 778 default_presubmit=None, may_prompt=False, tbr=tbr, | |
| 779 host_url=cl.GetRietveldServer()) | 755 host_url=cl.GetRietveldServer()) |
| 780 hook_results = HookResults(output.getvalue()) | |
| 781 if hook_results.output: | |
| 782 print hook_results.output | |
| 783 | 756 |
| 784 # TODO(dpranke): We should propagate the error out instead of calling exit(). | 757 # TODO(dpranke): We should propagate the error out instead of calling exit(). |
| 785 if should_continue and hook_results.output and ( | 758 if not output.should_continue(): |
| 786 '** Presubmit ERRORS **\n' in hook_results.output or | 759 sys.exit(1) |
| 787 '** Presubmit WARNINGS **\n' in hook_results.output): | |
| 788 should_continue = False | |
| 789 | 760 |
| 790 if not should_continue: | 761 return output |
| 791 if may_prompt: | |
| 792 response = raw_input('Are you sure you want to continue? (y/N): ') | |
| 793 if not response.lower().startswith('y'): | |
| 794 sys.exit(1) | |
| 795 else: | |
| 796 sys.exit(1) | |
| 797 | |
| 798 | |
| 799 return hook_results | |
| 800 | 762 |
| 801 | 763 |
| 802 def CMDpresubmit(parser, args): | 764 def CMDpresubmit(parser, args): |
| 803 """run presubmit tests on the current changelist""" | 765 """run presubmit tests on the current changelist""" |
| 804 parser.add_option('--upload', action='store_true', | 766 parser.add_option('--upload', action='store_true', |
| 805 help='Run upload hook instead of the push/dcommit hook') | 767 help='Run upload hook instead of the push/dcommit hook') |
| 806 (options, args) = parser.parse_args(args) | 768 (options, args) = parser.parse_args(args) |
| 807 | 769 |
| 808 # Make sure index is up-to-date before running diff-index. | 770 # Make sure index is up-to-date before running diff-index. |
| 809 RunGit(['update-index', '--refresh', '-q'], error_ok=True) | 771 RunGit(['update-index', '--refresh', '-q'], error_ok=True) |
| (...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 863 return 1 | 825 return 1 |
| 864 | 826 |
| 865 cl = Changelist() | 827 cl = Changelist() |
| 866 if args: | 828 if args: |
| 867 base_branch = args[0] | 829 base_branch = args[0] |
| 868 else: | 830 else: |
| 869 # Default to diffing against the "upstream" branch. | 831 # Default to diffing against the "upstream" branch. |
| 870 base_branch = cl.GetUpstreamBranch() | 832 base_branch = cl.GetUpstreamBranch() |
| 871 args = [base_branch + "..."] | 833 args = [base_branch + "..."] |
| 872 | 834 |
| 873 if not options.bypass_hooks: | 835 if not options.bypass_hooks and not options.force: |
| 874 hook_results = RunHook(committing=False, upstream_branch=base_branch, | 836 hook_results = RunHook(committing=False, upstream_branch=base_branch, |
| 875 rietveld_server=cl.GetRietveldServer(), tbr=False, | 837 rietveld_server=cl.GetRietveldServer(), tbr=False, |
| 876 may_prompt=(not options.force)) | 838 may_prompt=True) |
| 877 else: | 839 if not options.reviewers and hook_results.reviewers: |
| 878 hook_results = HookResults() | 840 options.reviewers = hook_results.reviewers |
| 879 | 841 |
| 880 if not options.reviewers and hook_results.reviewers: | |
| 881 options.reviewers = hook_results.reviewers | |
| 882 | 842 |
| 883 # --no-ext-diff is broken in some versions of Git, so try to work around | 843 # --no-ext-diff is broken in some versions of Git, so try to work around |
| 884 # this by overriding the environment (but there is still a problem if the | 844 # this by overriding the environment (but there is still a problem if the |
| 885 # git config key "diff.external" is used). | 845 # git config key "diff.external" is used). |
| 886 env = os.environ.copy() | 846 env = os.environ.copy() |
| 887 if 'GIT_EXTERNAL_DIFF' in env: | 847 if 'GIT_EXTERNAL_DIFF' in env: |
| 888 del env['GIT_EXTERNAL_DIFF'] | 848 del env['GIT_EXTERNAL_DIFF'] |
| 889 subprocess.call(['git', 'diff', '--no-ext-diff', '--stat', '-M'] + args, | 849 subprocess.call(['git', 'diff', '--no-ext-diff', '--stat', '-M'] + args, |
| 890 env=env) | 850 env=env) |
| 891 | 851 |
| (...skipping 124 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1016 svn_head = RunGit(['log', '--grep=^git-svn-id:', '-1', | 976 svn_head = RunGit(['log', '--grep=^git-svn-id:', '-1', |
| 1017 '--pretty=format:%H']) | 977 '--pretty=format:%H']) |
| 1018 extra_commits = RunGit(['rev-list', '^' + svn_head, base_branch]) | 978 extra_commits = RunGit(['rev-list', '^' + svn_head, base_branch]) |
| 1019 if extra_commits: | 979 if extra_commits: |
| 1020 print ('This branch has %d additional commits not upstreamed yet.' | 980 print ('This branch has %d additional commits not upstreamed yet.' |
| 1021 % len(extra_commits.splitlines())) | 981 % len(extra_commits.splitlines())) |
| 1022 print ('Upstream "%s" or rebase this branch on top of the upstream trunk ' | 982 print ('Upstream "%s" or rebase this branch on top of the upstream trunk ' |
| 1023 'before attempting to %s.' % (base_branch, cmd)) | 983 'before attempting to %s.' % (base_branch, cmd)) |
| 1024 return 1 | 984 return 1 |
| 1025 | 985 |
| 1026 if not options.bypass_hooks: | 986 if not options.bypass_hooks and not options.force: |
| 1027 RunHook(committing=True, upstream_branch=base_branch, | 987 RunHook(committing=True, upstream_branch=base_branch, |
| 1028 rietveld_server=cl.GetRietveldServer(), tbr=options.tbr, | 988 rietveld_server=cl.GetRietveldServer(), tbr=options.tbr, |
| 1029 may_prompt=(not options.force)) | 989 may_prompt=True) |
| 1030 | 990 |
| 1031 if not options.force and not options.bypass_hooks: | |
| 1032 if cmd == 'dcommit': | 991 if cmd == 'dcommit': |
| 1033 # Check the tree status if the tree status URL is set. | 992 # Check the tree status if the tree status URL is set. |
| 1034 status = GetTreeStatus() | 993 status = GetTreeStatus() |
| 1035 if 'closed' == status: | 994 if 'closed' == status: |
| 1036 print ('The tree is closed. Please wait for it to reopen. Use ' | 995 print ('The tree is closed. Please wait for it to reopen. Use ' |
| 1037 '"git cl dcommit -f" to commit on a closed tree.') | 996 '"git cl dcommit -f" to commit on a closed tree.') |
| 1038 return 1 | 997 return 1 |
| 1039 elif 'unknown' == status: | 998 elif 'unknown' == status: |
| 1040 print ('Unable to determine tree status. Please verify manually and ' | 999 print ('Unable to determine tree status. Please verify manually and ' |
| 1041 'use "git cl dcommit -f" to commit on a closed tree.') | 1000 'use "git cl dcommit -f" to commit on a closed tree.') |
| (...skipping 366 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1408 ('AppEngine is misbehaving and returned HTTP %d, again. Keep faith ' | 1367 ('AppEngine is misbehaving and returned HTTP %d, again. Keep faith ' |
| 1409 'and retry or visit go/isgaeup.\n%s') % (e.code, str(e))) | 1368 'and retry or visit go/isgaeup.\n%s') % (e.code, str(e))) |
| 1410 | 1369 |
| 1411 # Not a known command. Default to help. | 1370 # Not a known command. Default to help. |
| 1412 GenUsage(parser, 'help') | 1371 GenUsage(parser, 'help') |
| 1413 return CMDhelp(parser, argv) | 1372 return CMDhelp(parser, argv) |
| 1414 | 1373 |
| 1415 | 1374 |
| 1416 if __name__ == '__main__': | 1375 if __name__ == '__main__': |
| 1417 sys.exit(main(sys.argv[1:])) | 1376 sys.exit(main(sys.argv[1:])) |
| OLD | NEW |