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 subprocess | 11 import subprocess |
11 import sys | 12 import sys |
12 import tempfile | 13 import tempfile |
13 import textwrap | 14 import textwrap |
14 import upload | 15 import upload |
15 import urlparse | 16 import urlparse |
16 import urllib2 | 17 import urllib2 |
17 | 18 |
18 try: | 19 try: |
19 import readline | 20 import readline |
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
59 if redirect_stdout: | 60 if redirect_stdout: |
60 stdout = subprocess.PIPE | 61 stdout = subprocess.PIPE |
61 else: | 62 else: |
62 stdout = None | 63 stdout = None |
63 if swallow_stderr: | 64 if swallow_stderr: |
64 stderr = subprocess.PIPE | 65 stderr = subprocess.PIPE |
65 else: | 66 else: |
66 stderr = None | 67 stderr = None |
67 proc = Popen(cmd, stdout=stdout, stderr=stderr, **kwargs) | 68 proc = Popen(cmd, stdout=stdout, stderr=stderr, **kwargs) |
68 output = proc.communicate()[0] | 69 output = proc.communicate()[0] |
70 | |
69 if not error_ok and proc.returncode != 0: | 71 if not error_ok and proc.returncode != 0: |
70 DieWithError('Command "%s" failed.\n' % (' '.join(cmd)) + | 72 DieWithError('Command "%s" failed.\n' % (' '.join(cmd)) + |
71 (error_message or output or '')) | 73 (error_message or output or '')) |
72 return output | 74 return output |
73 | 75 |
74 | 76 |
75 def RunGit(args, **kwargs): | 77 def RunGit(args, **kwargs): |
76 cmd = ['git'] + args | 78 cmd = ['git'] + args |
77 return RunCommand(cmd, **kwargs) | 79 return RunCommand(cmd, **kwargs) |
78 | 80 |
(...skipping 394 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
473 | 475 |
474 SetProperty(settings.GetCCList(), 'CC list', 'cc') | 476 SetProperty(settings.GetCCList(), 'CC list', 'cc') |
475 SetProperty(settings.GetTreeStatusUrl(error_ok=True), 'Tree status URL', | 477 SetProperty(settings.GetTreeStatusUrl(error_ok=True), 'Tree status URL', |
476 'tree-status-url') | 478 'tree-status-url') |
477 SetProperty(settings.GetViewVCUrl(), 'ViewVC URL', 'viewvc-url') | 479 SetProperty(settings.GetViewVCUrl(), 'ViewVC URL', 'viewvc-url') |
478 | 480 |
479 # TODO: configure a default branch to diff against, rather than this | 481 # TODO: configure a default branch to diff against, rather than this |
480 # svn-based hackery. | 482 # svn-based hackery. |
481 | 483 |
482 | 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=(.+)') | |
M-A Ruel
2011/03/11 18:00:06
I wonder if instead it could be a more generic imp
Dirk Pranke
2011/03/11 21:06:31
Yeah, that would probably be a nice enhancement. H
| |
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): | |
509 """Contains a parsed form of the change description.""" | |
510 def __init__(self, subject, log_desc, reviewers): | |
511 self.subject = subject | |
512 self.log_desc = log_desc | |
513 self.reviewers = reviewers | |
514 self.description = self.log_desc | |
515 | |
516 def Update(self): | |
517 initial_text = """# Enter a description of the change. | |
518 # This will displayed on the codereview site. | |
519 # The first line will also be used as the subject of the review. | |
520 """ | |
521 initial_text += self.description | |
522 if 'R=' not in self.description and self.reviewers: | |
523 initial_text += '\nR=' + self.reviewers | |
524 if 'BUG=' not in self.description: | |
525 initial_text += '\nBUG=' | |
526 if 'TEST=' not in self.description: | |
527 initial_text += '\nTEST=' | |
528 self._ParseDescription(UserEditedLog(initial_text)) | |
529 | |
530 def _ParseDescription(self, description): | |
531 if not description: | |
532 self.description = description | |
533 return | |
534 | |
535 parsed_lines = [] | |
536 reviewers_regexp = re.compile('\s*R=(.+)') | |
537 reviewers = '' | |
538 subject = '' | |
539 for l in description.splitlines(): | |
540 if not subject: | |
541 subject = l | |
542 matched_reviewers = reviewers_regexp.match(l) | |
543 if matched_reviewers: | |
544 reviewers = matched_reviewers.group(1) | |
545 parsed_lines.append(l) | |
546 | |
547 self.description = '\n'.join(parsed_lines) + '\n' | |
548 self.subject = subject | |
549 self.reviewers = reviewers | |
550 | |
551 def IsEmpty(self): | |
552 return not self.description | |
553 | |
554 | |
483 def FindCodereviewSettingsFile(filename='codereview.settings'): | 555 def FindCodereviewSettingsFile(filename='codereview.settings'): |
484 """Finds the given file starting in the cwd and going up. | 556 """Finds the given file starting in the cwd and going up. |
485 | 557 |
486 Only looks up to the top of the repository unless an | 558 Only looks up to the top of the repository unless an |
487 'inherit-review-settings-ok' file exists in the root of the repository. | 559 'inherit-review-settings-ok' file exists in the root of the repository. |
488 """ | 560 """ |
489 inherit_ok_file = 'inherit-review-settings-ok' | 561 inherit_ok_file = 'inherit-review-settings-ok' |
490 cwd = os.getcwd() | 562 cwd = os.getcwd() |
491 root = os.path.abspath(RunGit(['rev-parse', '--show-cdup']).strip()) | 563 root = os.path.abspath(RunGit(['rev-parse', '--show-cdup']).strip()) |
492 if os.path.isfile(os.path.join(root, inherit_ok_file)): | 564 if os.path.isfile(os.path.join(root, inherit_ok_file)): |
(...skipping 171 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
664 | 736 |
665 | 737 |
666 def ConvertToInteger(inputval): | 738 def ConvertToInteger(inputval): |
667 """Convert a string to integer, but returns either an int or None.""" | 739 """Convert a string to integer, but returns either an int or None.""" |
668 try: | 740 try: |
669 return int(inputval) | 741 return int(inputval) |
670 except (TypeError, ValueError): | 742 except (TypeError, ValueError): |
671 return None | 743 return None |
672 | 744 |
673 | 745 |
674 def RunHook(committing, upstream_branch): | 746 def RunHook(committing, upstream_branch, rietveld_server, tbr=False): |
675 import presubmit_support | 747 import presubmit_support |
676 import scm | 748 import scm |
677 import watchlists | 749 import watchlists |
678 | 750 |
679 root = RunCommand(['git', 'rev-parse', '--show-cdup']).strip() | 751 root = RunCommand(['git', 'rev-parse', '--show-cdup']).strip() |
680 if not root: | 752 if not root: |
681 root = "." | 753 root = "." |
682 absroot = os.path.abspath(root) | 754 absroot = os.path.abspath(root) |
683 if not root: | 755 if not root: |
684 raise Exception("Could not get root directory.") | 756 raise Exception("Could not get root directory.") |
(...skipping 17 matching lines...) Expand all Loading... | |
702 issue, patchset) | 774 issue, patchset) |
703 | 775 |
704 # Apply watchlists on upload. | 776 # Apply watchlists on upload. |
705 if not committing: | 777 if not committing: |
706 watchlist = watchlists.Watchlists(change.RepositoryRoot()) | 778 watchlist = watchlists.Watchlists(change.RepositoryRoot()) |
707 files = [f.LocalPath() for f in change.AffectedFiles()] | 779 files = [f.LocalPath() for f in change.AffectedFiles()] |
708 watchers = watchlist.GetWatchersForPaths(files) | 780 watchers = watchlist.GetWatchersForPaths(files) |
709 RunCommand(['git', 'config', '--replace-all', | 781 RunCommand(['git', 'config', '--replace-all', |
710 'rietveld.extracc', ','.join(watchers)]) | 782 'rietveld.extracc', ','.join(watchers)]) |
711 | 783 |
712 return presubmit_support.DoPresubmitChecks(change, committing, | 784 output = StringIO.StringIO() |
713 verbose=None, output_stream=sys.stdout, input_stream=sys.stdin, | 785 res = presubmit_support.DoPresubmitChecks(change, committing, |
714 default_presubmit=None, may_prompt=None) | 786 verbose=None, output_stream=output, input_stream=sys.stdin, |
787 default_presubmit=None, may_prompt=None, tbr=tbr, | |
788 host_url=cl.GetRietveldServer()) | |
789 if not res: | |
M-A Ruel
2011/03/11 21:21:55
What about PresubmitNotifyResult?
Dirk Pranke
2011/03/11 21:39:18
Good catch. It would get printed in some but not a
| |
790 print output.getvalue() | |
791 sys.exit(1) | |
792 return HookResults(output.getvalue()) | |
715 | 793 |
716 | 794 |
717 def CMDpresubmit(parser, args): | 795 def CMDpresubmit(parser, args): |
718 """run presubmit tests on the current changelist""" | 796 """run presubmit tests on the current changelist""" |
719 parser.add_option('--upload', action='store_true', | 797 parser.add_option('--upload', action='store_true', |
720 help='Run upload hook instead of the push/dcommit hook') | 798 help='Run upload hook instead of the push/dcommit hook') |
721 (options, args) = parser.parse_args(args) | 799 (options, args) = parser.parse_args(args) |
722 | 800 |
723 # Make sure index is up-to-date before running diff-index. | 801 # Make sure index is up-to-date before running diff-index. |
724 RunGit(['update-index', '--refresh', '-q'], error_ok=True) | 802 RunGit(['update-index', '--refresh', '-q'], error_ok=True) |
725 if RunGit(['diff-index', 'HEAD']): | 803 if RunGit(['diff-index', 'HEAD']): |
726 # TODO(maruel): Is this really necessary? | 804 # TODO(maruel): Is this really necessary? |
727 print 'Cannot presubmit with a dirty tree. You must commit locally first.' | 805 print 'Cannot presubmit with a dirty tree. You must commit locally first.' |
728 return 1 | 806 return 1 |
729 | 807 |
808 cl = Changelist() | |
730 if args: | 809 if args: |
731 base_branch = args[0] | 810 base_branch = args[0] |
732 else: | 811 else: |
733 # Default to diffing against the "upstream" branch. | 812 # Default to diffing against the "upstream" branch. |
734 base_branch = Changelist().GetUpstreamBranch() | 813 base_branch = cl.GetUpstreamBranch() |
735 | 814 |
736 if options.upload: | 815 if options.upload: |
737 print '*** Presubmit checks for UPLOAD would report: ***' | 816 print '*** Presubmit checks for UPLOAD would report: ***' |
738 return RunHook(committing=False, upstream_branch=base_branch) | 817 return RunHook(committing=False, upstream_branch=base_branch, |
818 rietveld_server=cl.GetRietveldServer(), tbr=False).output | |
739 else: | 819 else: |
740 print '*** Presubmit checks for DCOMMIT would report: ***' | 820 print '*** Presubmit checks for DCOMMIT would report: ***' |
741 return RunHook(committing=True, upstream_branch=base_branch) | 821 return RunHook(committing=True, upstream_branch=base_branch, |
822 rietveld_server=cl.GetRietveldServer, tbr=False).output | |
742 | 823 |
743 | 824 |
744 @usage('[args to "git diff"]') | 825 @usage('[args to "git diff"]') |
745 def CMDupload(parser, args): | 826 def CMDupload(parser, args): |
746 """upload the current changelist to codereview""" | 827 """upload the current changelist to codereview""" |
747 parser.add_option('--bypass-hooks', action='store_true', dest='bypass_hooks', | 828 parser.add_option('--bypass-hooks', action='store_true', dest='bypass_hooks', |
748 help='bypass upload presubmit hook') | 829 help='bypass upload presubmit hook') |
749 parser.add_option('-m', dest='message', help='message for patch') | 830 parser.add_option('-m', dest='message', help='message for patch') |
750 parser.add_option('-r', '--reviewers', | 831 parser.add_option('-r', '--reviewers', |
751 help='reviewer email addresses') | 832 help='reviewer email addresses') |
(...skipping 18 matching lines...) Expand all Loading... | |
770 | 851 |
771 cl = Changelist() | 852 cl = Changelist() |
772 if args: | 853 if args: |
773 base_branch = args[0] | 854 base_branch = args[0] |
774 else: | 855 else: |
775 # Default to diffing against the "upstream" branch. | 856 # Default to diffing against the "upstream" branch. |
776 base_branch = cl.GetUpstreamBranch() | 857 base_branch = cl.GetUpstreamBranch() |
777 args = [base_branch + "..."] | 858 args = [base_branch + "..."] |
778 | 859 |
779 if not options.bypass_hooks: | 860 if not options.bypass_hooks: |
780 RunHook(committing=False, upstream_branch=base_branch) | 861 hook_results = RunHook(committing=False, upstream_branch=base_branch, |
862 rietveld_server=cl.GetRietveldServer(), tbr=False) | |
863 else: | |
864 hook_results = HookResults() | |
865 | |
866 if not options.reviewers and hook_results.reviewers: | |
867 options.reviewers = hook_results.reviewers | |
781 | 868 |
782 # --no-ext-diff is broken in some versions of Git, so try to work around | 869 # --no-ext-diff is broken in some versions of Git, so try to work around |
783 # this by overriding the environment (but there is still a problem if the | 870 # this by overriding the environment (but there is still a problem if the |
784 # git config key "diff.external" is used). | 871 # git config key "diff.external" is used). |
785 env = os.environ.copy() | 872 env = os.environ.copy() |
786 if 'GIT_EXTERNAL_DIFF' in env: | 873 if 'GIT_EXTERNAL_DIFF' in env: |
787 del env['GIT_EXTERNAL_DIFF'] | 874 del env['GIT_EXTERNAL_DIFF'] |
788 subprocess.call(['git', 'diff', '--no-ext-diff', '--stat', '-M'] + args, | 875 subprocess.call(['git', 'diff', '--no-ext-diff', '--stat', '-M'] + args, |
789 env=env) | 876 env=env) |
790 | 877 |
791 upload_args = ['--assume_yes'] # Don't ask about untracked files. | 878 upload_args = ['--assume_yes'] # Don't ask about untracked files. |
792 upload_args.extend(['--server', cl.GetRietveldServer()]) | 879 upload_args.extend(['--server', cl.GetRietveldServer()]) |
793 if options.reviewers: | |
794 upload_args.extend(['--reviewers', options.reviewers]) | |
795 if options.emulate_svn_auto_props: | 880 if options.emulate_svn_auto_props: |
796 upload_args.append('--emulate_svn_auto_props') | 881 upload_args.append('--emulate_svn_auto_props') |
797 if options.send_mail: | 882 if options.send_mail: |
798 if not options.reviewers: | 883 if not options.reviewers: |
799 DieWithError("Must specify reviewers to send email.") | 884 DieWithError("Must specify reviewers to send email.") |
800 upload_args.append('--send_mail') | 885 upload_args.append('--send_mail') |
801 if options.from_logs and not options.message: | 886 if options.from_logs and not options.message: |
802 print 'Must set message for subject line if using desc_from_logs' | 887 print 'Must set message for subject line if using desc_from_logs' |
803 return 1 | 888 return 1 |
804 | 889 |
805 change_desc = None | 890 change_desc = None |
806 | 891 |
807 if cl.GetIssue(): | 892 if cl.GetIssue(): |
808 if options.message: | 893 if options.message: |
809 upload_args.extend(['--message', options.message]) | 894 upload_args.extend(['--message', options.message]) |
810 upload_args.extend(['--issue', cl.GetIssue()]) | 895 upload_args.extend(['--issue', cl.GetIssue()]) |
811 print ("This branch is associated with issue %s. " | 896 print ("This branch is associated with issue %s. " |
812 "Adding patch to that issue." % cl.GetIssue()) | 897 "Adding patch to that issue." % cl.GetIssue()) |
813 else: | 898 else: |
814 log_desc = CreateDescriptionFromLog(args) | 899 log_desc = CreateDescriptionFromLog(args) |
815 if options.from_logs: | 900 change_desc = ChangeDescription(options.message, log_desc, |
816 # Uses logs as description and message as subject. | 901 options.reviewers) |
817 subject = options.message | 902 if not options.from_logs: |
818 change_desc = subject + '\n\n' + log_desc | 903 change_desc.Update() |
819 else: | 904 |
820 initial_text = """# Enter a description of the change. | 905 if change_desc.IsEmpty(): |
821 # This will displayed on the codereview site. | |
822 # The first line will also be used as the subject of the review. | |
823 """ | |
824 if 'BUG=' not in log_desc: | |
825 log_desc += '\nBUG=' | |
826 if 'TEST=' not in log_desc: | |
827 log_desc += '\nTEST=' | |
828 change_desc = UserEditedLog(initial_text + log_desc) | |
829 subject = '' | |
830 if change_desc: | |
831 subject = change_desc.splitlines()[0] | |
832 if not change_desc: | |
833 print "Description is empty; aborting." | 906 print "Description is empty; aborting." |
834 return 1 | 907 return 1 |
835 upload_args.extend(['--message', subject]) | 908 |
836 upload_args.extend(['--description', change_desc]) | 909 upload_args.extend(['--message', change_desc.subject]) |
910 upload_args.extend(['--description', change_desc.description]) | |
911 if change_desc.reviewers: | |
912 upload_args.extend(['--reviewers', change_desc.reviewers]) | |
837 cc = ','.join(filter(None, (settings.GetCCList(), options.cc))) | 913 cc = ','.join(filter(None, (settings.GetCCList(), options.cc))) |
838 if cc: | 914 if cc: |
839 upload_args.extend(['--cc', cc]) | 915 upload_args.extend(['--cc', cc]) |
840 | 916 |
841 # Include the upstream repo's URL in the change -- this is useful for | 917 # Include the upstream repo's URL in the change -- this is useful for |
842 # projects that have their source spread across multiple repos. | 918 # projects that have their source spread across multiple repos. |
843 remote_url = None | 919 remote_url = None |
844 if settings.GetIsGitSvn(): | 920 if settings.GetIsGitSvn(): |
845 # URL is dependent on the current directory. | 921 # URL is dependent on the current directory. |
846 data = RunGit(['svn', 'info'], cwd=settings.GetRoot()) | 922 data = RunGit(['svn', 'info'], cwd=settings.GetRoot()) |
(...skipping 11 matching lines...) Expand all Loading... | |
858 try: | 934 try: |
859 issue, patchset = upload.RealMain(['upload'] + upload_args + args) | 935 issue, patchset = upload.RealMain(['upload'] + upload_args + args) |
860 except: | 936 except: |
861 # If we got an exception after the user typed a description for their | 937 # If we got an exception after the user typed a description for their |
862 # change, back up the description before re-raising. | 938 # change, back up the description before re-raising. |
863 if change_desc: | 939 if change_desc: |
864 backup_path = os.path.expanduser(DESCRIPTION_BACKUP_FILE) | 940 backup_path = os.path.expanduser(DESCRIPTION_BACKUP_FILE) |
865 print '\nGot exception while uploading -- saving description to %s\n' \ | 941 print '\nGot exception while uploading -- saving description to %s\n' \ |
866 % backup_path | 942 % backup_path |
867 backup_file = open(backup_path, 'w') | 943 backup_file = open(backup_path, 'w') |
868 backup_file.write(change_desc) | 944 backup_file.write(change_desc.description) |
869 backup_file.close() | 945 backup_file.close() |
870 raise | 946 raise |
871 | 947 |
872 if not cl.GetIssue(): | 948 if not cl.GetIssue(): |
873 cl.SetIssue(issue) | 949 cl.SetIssue(issue) |
874 cl.SetPatchset(patchset) | 950 cl.SetPatchset(patchset) |
875 return 0 | 951 return 0 |
876 | 952 |
877 | 953 |
878 def SendUpstream(parser, args, cmd): | 954 def SendUpstream(parser, args, cmd): |
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
926 svn_head = RunGit(['log', '--grep=^git-svn-id:', '-1', | 1002 svn_head = RunGit(['log', '--grep=^git-svn-id:', '-1', |
927 '--pretty=format:%H']) | 1003 '--pretty=format:%H']) |
928 extra_commits = RunGit(['rev-list', '^' + svn_head, base_branch]) | 1004 extra_commits = RunGit(['rev-list', '^' + svn_head, base_branch]) |
929 if extra_commits: | 1005 if extra_commits: |
930 print ('This branch has %d additional commits not upstreamed yet.' | 1006 print ('This branch has %d additional commits not upstreamed yet.' |
931 % len(extra_commits.splitlines())) | 1007 % len(extra_commits.splitlines())) |
932 print ('Upstream "%s" or rebase this branch on top of the upstream trunk ' | 1008 print ('Upstream "%s" or rebase this branch on top of the upstream trunk ' |
933 'before attempting to %s.' % (base_branch, cmd)) | 1009 'before attempting to %s.' % (base_branch, cmd)) |
934 return 1 | 1010 return 1 |
935 | 1011 |
936 if not options.force and not options.bypass_hooks: | 1012 if not options.bypass_hooks: |
937 RunHook(committing=False, upstream_branch=base_branch) | 1013 hook_results = RunHook(committing=True, upstream_branch=base_branch, |
1014 rietveld_server=cl.GetRietveldServer(), tbr=options.tbr) | |
1015 print hook_results.output | |
938 | 1016 |
939 if cmd == 'dcommit': | 1017 if cmd == 'dcommit': |
940 # Check the tree status if the tree status URL is set. | 1018 # Check the tree status if the tree status URL is set. |
941 status = GetTreeStatus() | 1019 status = GetTreeStatus() |
942 if 'closed' == status: | 1020 if 'closed' == status: |
943 print ('The tree is closed. Please wait for it to reopen. Use ' | 1021 print ('The tree is closed. Please wait for it to reopen. Use ' |
944 '"git cl dcommit -f" to commit on a closed tree.') | 1022 '"git cl dcommit -f" to commit on a closed tree.') |
945 return 1 | 1023 return 1 |
946 elif 'unknown' == status: | 1024 elif 'unknown' == status: |
947 print ('Unable to determine tree status. Please verify manually and ' | 1025 print ('Unable to determine tree status. Please verify manually and ' |
(...skipping 19 matching lines...) Expand all Loading... | |
967 | 1045 |
968 description += "\n\nReview URL: %s" % cl.GetIssueURL() | 1046 description += "\n\nReview URL: %s" % cl.GetIssueURL() |
969 else: | 1047 else: |
970 if not description: | 1048 if not description: |
971 # Submitting TBR. See if there's already a description in Rietveld, else | 1049 # Submitting TBR. See if there's already a description in Rietveld, else |
972 # create a template description. Eitherway, give the user a chance to edit | 1050 # create a template description. Eitherway, give the user a chance to edit |
973 # it to fill in the TBR= field. | 1051 # it to fill in the TBR= field. |
974 if cl.GetIssue(): | 1052 if cl.GetIssue(): |
975 description = cl.GetDescription() | 1053 description = cl.GetDescription() |
976 | 1054 |
1055 # TODO(dpranke): Update to use ChangeDescription object. | |
977 if not description: | 1056 if not description: |
978 description = """# Enter a description of the change. | 1057 description = """# Enter a description of the change. |
979 # This will be used as the change log for the commit. | 1058 # This will be used as the change log for the commit. |
980 | 1059 |
981 """ | 1060 """ |
982 description += CreateDescriptionFromLog(args) | 1061 description += CreateDescriptionFromLog(args) |
983 | 1062 |
984 description = UserEditedLog(description + '\nTBR=') | 1063 description = UserEditedLog(description + '\nTBR=') |
985 | 1064 |
986 if not description: | 1065 if not description: |
(...skipping 321 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1308 ('AppEngine is misbehaving and returned HTTP %d, again. Keep faith ' | 1387 ('AppEngine is misbehaving and returned HTTP %d, again. Keep faith ' |
1309 'and retry or visit go/isgaeup.\n%s') % (e.code, str(e))) | 1388 'and retry or visit go/isgaeup.\n%s') % (e.code, str(e))) |
1310 | 1389 |
1311 # Not a known command. Default to help. | 1390 # Not a known command. Default to help. |
1312 GenUsage(parser, 'help') | 1391 GenUsage(parser, 'help') |
1313 return CMDhelp(parser, argv) | 1392 return CMDhelp(parser, argv) |
1314 | 1393 |
1315 | 1394 |
1316 if __name__ == '__main__': | 1395 if __name__ == '__main__': |
1317 sys.exit(main(sys.argv[1:])) | 1396 sys.exit(main(sys.argv[1:])) |
OLD | NEW |