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 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
47 | 47 |
48 | 48 |
49 def DieWithError(message): | 49 def DieWithError(message): |
50 print >> sys.stderr, message | 50 print >> sys.stderr, message |
51 sys.exit(1) | 51 sys.exit(1) |
52 | 52 |
53 | 53 |
54 def RunCommand(args, error_ok=False, error_message=None, **kwargs): | 54 def RunCommand(args, error_ok=False, error_message=None, **kwargs): |
55 try: | 55 try: |
56 return subprocess2.check_output(args, shell=False, **kwargs) | 56 return subprocess2.check_output(args, shell=False, **kwargs) |
57 except subprocess2.CalledProcessError, e: | 57 except subprocess2.CalledProcessError as e: |
| 58 logging.debug('Failed running %s', args) |
58 if not error_ok: | 59 if not error_ok: |
59 DieWithError( | 60 DieWithError( |
60 'Command "%s" failed.\n%s' % ( | 61 'Command "%s" failed.\n%s' % ( |
61 ' '.join(args), error_message or e.stdout or '')) | 62 ' '.join(args), error_message or e.stdout or '')) |
62 return e.stdout | 63 return e.stdout |
63 | 64 |
64 | 65 |
65 def RunGit(args, **kwargs): | 66 def RunGit(args, **kwargs): |
66 """Returns stdout.""" | 67 """Returns stdout.""" |
67 return RunCommand(['git'] + args, **kwargs) | 68 return RunCommand(['git'] + args, **kwargs) |
(...skipping 722 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
790 SetProperty(settings.GetTreeStatusUrl(error_ok=True), 'Tree status URL', | 791 SetProperty(settings.GetTreeStatusUrl(error_ok=True), 'Tree status URL', |
791 'tree-status-url', False) | 792 'tree-status-url', False) |
792 SetProperty(settings.GetViewVCUrl(), 'ViewVC URL', 'viewvc-url', True) | 793 SetProperty(settings.GetViewVCUrl(), 'ViewVC URL', 'viewvc-url', True) |
793 | 794 |
794 # TODO: configure a default branch to diff against, rather than this | 795 # TODO: configure a default branch to diff against, rather than this |
795 # svn-based hackery. | 796 # svn-based hackery. |
796 | 797 |
797 | 798 |
798 class ChangeDescription(object): | 799 class ChangeDescription(object): |
799 """Contains a parsed form of the change description.""" | 800 """Contains a parsed form of the change description.""" |
800 def __init__(self, log_desc, reviewers): | 801 R_LINE = r'^\s*(TBR|R)\s*=\s*(.+)\s*$' |
801 self.log_desc = log_desc | |
802 self.reviewers = reviewers | |
803 self.description = self.log_desc | |
804 | 802 |
805 def Prompt(self): | 803 def __init__(self, description): |
806 content = """# Enter a description of the change. | 804 self._description = (description or '').strip() |
807 # This will displayed on the codereview site. | 805 |
808 # The first line will also be used as the subject of the review. | 806 @property |
809 """ | 807 def description(self): |
810 content += self.description | 808 return self._description |
811 if ('\nR=' not in self.description and | 809 |
812 '\nTBR=' not in self.description and | 810 def update_reviewers(self, reviewers): |
813 self.reviewers): | 811 """Rewrites the R=/TBR= line(s) as a single line.""" |
814 content += '\nR=' + self.reviewers | 812 assert isinstance(reviewers, list), reviewers |
815 if '\nBUG=' not in self.description: | 813 if not reviewers: |
816 content += '\nBUG=' | 814 return |
817 content = content.rstrip('\n') + '\n' | 815 regexp = re.compile(self.R_LINE, re.MULTILINE) |
818 content = gclient_utils.RunEditor(content, True) | 816 matches = list(regexp.finditer(self._description)) |
| 817 is_tbr = any(m.group(1) == 'TBR' for m in matches) |
| 818 if len(matches) > 1: |
| 819 # Erase all except the first one. |
| 820 for i in xrange(len(matches) - 1, 0, -1): |
| 821 self._description = ( |
| 822 self._description[:matches[i].start()] + |
| 823 self._description[matches[i].end()+1:]) |
| 824 |
| 825 if is_tbr: |
| 826 new_r_line = 'TBR=' + ', '.join(reviewers) |
| 827 else: |
| 828 new_r_line = 'R=' + ', '.join(reviewers) |
| 829 |
| 830 if matches: |
| 831 self._description = ( |
| 832 self._description[:matches[0].start()] + new_r_line + |
| 833 self._description[matches[0].end()+1:]) |
| 834 else: |
| 835 self.append_footer(new_r_line) |
| 836 |
| 837 def prompt(self): |
| 838 """Asks the user to update the description.""" |
| 839 self._description = ( |
| 840 '# Enter a description of the change.\n' |
| 841 '# This will displayed on the codereview site.\n' |
| 842 '# The first line will also be used as the subject of the review.\n' |
| 843 ) + self._description |
| 844 |
| 845 if '\nBUG=' not in self._description: |
| 846 self.append_footer('BUG=') |
| 847 content = gclient_utils.RunEditor(self._description, True) |
819 if not content: | 848 if not content: |
820 DieWithError('Running editor failed') | 849 DieWithError('Running editor failed') |
| 850 |
| 851 # Strip off comments. |
821 content = re.compile(r'^#.*$', re.MULTILINE).sub('', content).strip() | 852 content = re.compile(r'^#.*$', re.MULTILINE).sub('', content).strip() |
822 if not content.strip(): | 853 if not content: |
823 DieWithError('No CL description, aborting') | 854 DieWithError('No CL description, aborting') |
824 self.description = content | 855 self._description = content |
825 | 856 |
826 def ParseDescription(self): | 857 def append_footer(self, line): |
827 """Updates the list of reviewers and subject from the description.""" | 858 # Adds a LF if the last line did not have 'FOO=BAR' or if the new one isn't. |
828 self.description = self.description.strip('\n') + '\n' | 859 if self._description: |
829 # Retrieves all reviewer lines | 860 if '\n' not in self._description: |
830 regexp = re.compile(r'^\s*(TBR|R)=(.+)$', re.MULTILINE) | 861 self._description += '\n' |
831 reviewers = ', '.join( | 862 else: |
832 i.group(2).strip() for i in regexp.finditer(self.description)) | 863 last_line = self._description.rsplit('\n', 1)[1] |
833 if reviewers: | 864 if (not presubmit_support.Change.TAG_LINE_RE.match(last_line) or |
834 self.reviewers = reviewers | 865 not presubmit_support.Change.TAG_LINE_RE.match(line)): |
| 866 self._description += '\n' |
| 867 self._description += '\n' + line |
835 | 868 |
836 def IsEmpty(self): | 869 def get_reviewers(self): |
837 return not self.description | 870 """Retrieves the list of reviewers.""" |
| 871 regexp = re.compile(self.R_LINE, re.MULTILINE) |
| 872 reviewers = [i.group(2) for i in regexp.finditer(self._description)] |
| 873 return cleanup_list(reviewers) |
838 | 874 |
839 | 875 |
840 def FindCodereviewSettingsFile(filename='codereview.settings'): | 876 def FindCodereviewSettingsFile(filename='codereview.settings'): |
841 """Finds the given file starting in the cwd and going up. | 877 """Finds the given file starting in the cwd and going up. |
842 | 878 |
843 Only looks up to the top of the repository unless an | 879 Only looks up to the top of the repository unless an |
844 'inherit-review-settings-ok' file exists in the root of the repository. | 880 'inherit-review-settings-ok' file exists in the root of the repository. |
845 """ | 881 """ |
846 inherit_ok_file = 'inherit-review-settings-ok' | 882 inherit_ok_file = 'inherit-review-settings-ok' |
847 cwd = os.getcwd() | 883 cwd = os.getcwd() |
(...skipping 247 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1095 | 1131 |
1096 def GerritUpload(options, args, cl): | 1132 def GerritUpload(options, args, cl): |
1097 """upload the current branch to gerrit.""" | 1133 """upload the current branch to gerrit.""" |
1098 # We assume the remote called "origin" is the one we want. | 1134 # We assume the remote called "origin" is the one we want. |
1099 # It is probably not worthwhile to support different workflows. | 1135 # It is probably not worthwhile to support different workflows. |
1100 remote = 'origin' | 1136 remote = 'origin' |
1101 branch = 'master' | 1137 branch = 'master' |
1102 if options.target_branch: | 1138 if options.target_branch: |
1103 branch = options.target_branch | 1139 branch = options.target_branch |
1104 | 1140 |
1105 log_desc = options.message or CreateDescriptionFromLog(args) | 1141 change_desc = ChangeDescription( |
1106 if CHANGE_ID not in log_desc: | 1142 options.message or CreateDescriptionFromLog(args)) |
| 1143 if not change_desc.description: |
| 1144 print "Description is empty; aborting." |
| 1145 return 1 |
| 1146 if CHANGE_ID not in change_desc.description: |
1107 AddChangeIdToCommitMessage(options, args) | 1147 AddChangeIdToCommitMessage(options, args) |
1108 if options.reviewers: | 1148 if options.reviewers: |
1109 log_desc += '\nR=' + ', '.join(options.reviewers) | 1149 change_desc.update_reviewers(options.reviewers) |
1110 change_desc = ChangeDescription(log_desc, ', '.join(options.reviewers)) | |
1111 change_desc.ParseDescription() | |
1112 if change_desc.IsEmpty(): | |
1113 print "Description is empty; aborting." | |
1114 return 1 | |
1115 | 1150 |
1116 receive_options = [] | 1151 receive_options = [] |
1117 cc = cl.GetCCList().split(',') | 1152 cc = cl.GetCCList().split(',') |
1118 if options.cc: | 1153 if options.cc: |
1119 cc.extend(options.cc) | 1154 cc.extend(options.cc) |
1120 cc = filter(None, cc) | 1155 cc = filter(None, cc) |
1121 if cc: | 1156 if cc: |
1122 receive_options += ['--cc=' + email for email in cc] | 1157 receive_options += ['--cc=' + email for email in cc] |
1123 if change_desc.reviewers: | 1158 if change_desc.get_reviewers(): |
1124 reviewers = filter( | 1159 receive_options.extend( |
1125 None, (r.strip() for r in change_desc.reviewers.split(','))) | 1160 '--reviewer=' + email for email in change_desc.get_reviewers()) |
1126 if reviewers: | |
1127 receive_options += ['--reviewer=' + email for email in reviewers] | |
1128 | 1161 |
1129 git_command = ['push'] | 1162 git_command = ['push'] |
1130 if receive_options: | 1163 if receive_options: |
1131 git_command.append('--receive-pack=git receive-pack %s' % | 1164 git_command.append('--receive-pack=git receive-pack %s' % |
1132 ' '.join(receive_options)) | 1165 ' '.join(receive_options)) |
1133 git_command += [remote, 'HEAD:refs/for/' + branch] | 1166 git_command += [remote, 'HEAD:refs/for/' + branch] |
1134 RunGit(git_command) | 1167 RunGit(git_command) |
1135 # TODO(ukai): parse Change-Id: and set issue number? | 1168 # TODO(ukai): parse Change-Id: and set issue number? |
1136 return 0 | 1169 return 0 |
1137 | 1170 |
(...skipping 15 matching lines...) Expand all Loading... |
1153 # for upload.py. Soon this will be changed to set the --message option. | 1186 # for upload.py. Soon this will be changed to set the --message option. |
1154 # Will wait until people are used to typing -t instead of -m. | 1187 # Will wait until people are used to typing -t instead of -m. |
1155 upload_args.extend(['--title', options.message]) | 1188 upload_args.extend(['--title', options.message]) |
1156 upload_args.extend(['--issue', str(cl.GetIssue())]) | 1189 upload_args.extend(['--issue', str(cl.GetIssue())]) |
1157 print ("This branch is associated with issue %s. " | 1190 print ("This branch is associated with issue %s. " |
1158 "Adding patch to that issue." % cl.GetIssue()) | 1191 "Adding patch to that issue." % cl.GetIssue()) |
1159 else: | 1192 else: |
1160 if options.title: | 1193 if options.title: |
1161 upload_args.extend(['--title', options.title]) | 1194 upload_args.extend(['--title', options.title]) |
1162 message = options.title or options.message or CreateDescriptionFromLog(args) | 1195 message = options.title or options.message or CreateDescriptionFromLog(args) |
1163 change_desc = ChangeDescription(message, ','.join(options.reviewers)) | 1196 change_desc = ChangeDescription(message) |
| 1197 if options.reviewers: |
| 1198 change_desc.update_reviewers(options.reviewers) |
1164 if not options.force: | 1199 if not options.force: |
1165 change_desc.Prompt() | 1200 change_desc.prompt() |
1166 change_desc.ParseDescription() | |
1167 | 1201 |
1168 if change_desc.IsEmpty(): | 1202 if not change_desc.description: |
1169 print "Description is empty; aborting." | 1203 print "Description is empty; aborting." |
1170 return 1 | 1204 return 1 |
1171 | 1205 |
1172 upload_args.extend(['--message', change_desc.description]) | 1206 upload_args.extend(['--message', change_desc.description]) |
1173 if change_desc.reviewers: | 1207 if change_desc.get_reviewers(): |
1174 upload_args.extend( | 1208 upload_args.append('--reviewers=' + ','.join(change_desc.get_reviewers())) |
1175 [ | |
1176 '--reviewers', | |
1177 ','.join(r.strip() for r in change_desc.reviewers.split(',')), | |
1178 ]) | |
1179 if options.send_mail: | 1209 if options.send_mail: |
1180 if not change_desc.reviewers: | 1210 if not change_desc.get_reviewers(): |
1181 DieWithError("Must specify reviewers to send email.") | 1211 DieWithError("Must specify reviewers to send email.") |
1182 upload_args.append('--send_mail') | 1212 upload_args.append('--send_mail') |
1183 cc = ','.join(filter(None, (cl.GetCCList(), ','.join(options.cc)))) | 1213 cc = ','.join(filter(None, (cl.GetCCList(), ','.join(options.cc)))) |
1184 if cc: | 1214 if cc: |
1185 upload_args.extend(['--cc', cc]) | 1215 upload_args.extend(['--cc', cc]) |
1186 | 1216 |
1187 upload_args.extend(['--git_similarity', str(options.similarity)]) | 1217 upload_args.extend(['--git_similarity', str(options.similarity)]) |
1188 if not options.find_copies: | 1218 if not options.find_copies: |
1189 upload_args.extend(['--git_no_find_copies']) | 1219 upload_args.extend(['--git_no_find_copies']) |
1190 | 1220 |
(...skipping 242 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1433 elif 'unknown' == status: | 1463 elif 'unknown' == status: |
1434 print('Unable to determine tree status. Please verify manually and ' | 1464 print('Unable to determine tree status. Please verify manually and ' |
1435 'use "git cl dcommit --bypass-hooks" to commit on a closed tree.') | 1465 'use "git cl dcommit --bypass-hooks" to commit on a closed tree.') |
1436 else: | 1466 else: |
1437 breakpad.SendStack( | 1467 breakpad.SendStack( |
1438 'GitClHooksBypassedCommit', | 1468 'GitClHooksBypassedCommit', |
1439 'Issue %s/%s bypassed hook when committing' % | 1469 'Issue %s/%s bypassed hook when committing' % |
1440 (cl.GetRietveldServer(), cl.GetIssue()), | 1470 (cl.GetRietveldServer(), cl.GetIssue()), |
1441 verbose=False) | 1471 verbose=False) |
1442 | 1472 |
1443 description = options.message | 1473 change_desc = ChangeDescription(options.message) |
1444 if not description and cl.GetIssue(): | 1474 if not change_desc.description and cl.GetIssue(): |
1445 description = cl.GetDescription() | 1475 change_desc = ChangeDescription(cl.GetDescription()) |
1446 | 1476 |
1447 if not description: | 1477 if not change_desc.description: |
1448 if not cl.GetIssue() and options.bypass_hooks: | 1478 if not cl.GetIssue() and options.bypass_hooks: |
1449 description = CreateDescriptionFromLog([base_branch]) | 1479 change_desc = ChangeDescription(CreateDescriptionFromLog([base_branch])) |
1450 else: | 1480 else: |
1451 print 'No description set.' | 1481 print 'No description set.' |
1452 print 'Visit %s/edit to set it.' % (cl.GetIssueURL()) | 1482 print 'Visit %s/edit to set it.' % (cl.GetIssueURL()) |
1453 return 1 | 1483 return 1 |
1454 | 1484 |
| 1485 # Keep a separate copy for the commit message, because the commit message |
| 1486 # contains the link to the Rietveld issue, while the Rietveld message contains |
| 1487 # the commit viewvc url. |
| 1488 commit_desc = ChangeDescription(change_desc.description) |
1455 if cl.GetIssue(): | 1489 if cl.GetIssue(): |
1456 description += "\n\nReview URL: %s" % cl.GetIssueURL() | 1490 commit_desc.append_footer('Review URL: %s' % cl.GetIssueURL()) |
| 1491 if options.contributor: |
| 1492 commit_desc.append_footer('Patch from %s.' % options.contributor) |
1457 | 1493 |
1458 if options.contributor: | 1494 print 'Description:', repr(commit_desc.description) |
1459 description += "\nPatch from %s." % options.contributor | |
1460 print 'Description:', repr(description) | |
1461 | 1495 |
1462 branches = [base_branch, cl.GetBranchRef()] | 1496 branches = [base_branch, cl.GetBranchRef()] |
1463 if not options.force: | 1497 if not options.force: |
1464 print_stats(options.similarity, options.find_copies, branches) | 1498 print_stats(options.similarity, options.find_copies, branches) |
1465 ask_for_data('About to commit; enter to confirm.') | 1499 ask_for_data('About to commit; enter to confirm.') |
1466 | 1500 |
1467 # We want to squash all this branch's commits into one commit with the proper | 1501 # We want to squash all this branch's commits into one commit with the proper |
1468 # description. We do this by doing a "reset --soft" to the base branch (which | 1502 # description. We do this by doing a "reset --soft" to the base branch (which |
1469 # keeps the working copy the same), then dcommitting that. If origin/master | 1503 # keeps the working copy the same), then dcommitting that. If origin/master |
1470 # has a submodule merge commit, we'll also need to cherry-pick the squashed | 1504 # has a submodule merge commit, we'll also need to cherry-pick the squashed |
(...skipping 15 matching lines...) Expand all Loading... |
1486 os.chdir(rel_base_path) | 1520 os.chdir(rel_base_path) |
1487 | 1521 |
1488 # Stuff our change into the merge branch. | 1522 # Stuff our change into the merge branch. |
1489 # We wrap in a try...finally block so if anything goes wrong, | 1523 # We wrap in a try...finally block so if anything goes wrong, |
1490 # we clean up the branches. | 1524 # we clean up the branches. |
1491 retcode = -1 | 1525 retcode = -1 |
1492 try: | 1526 try: |
1493 RunGit(['checkout', '-q', '-b', MERGE_BRANCH]) | 1527 RunGit(['checkout', '-q', '-b', MERGE_BRANCH]) |
1494 RunGit(['reset', '--soft', base_branch]) | 1528 RunGit(['reset', '--soft', base_branch]) |
1495 if options.contributor: | 1529 if options.contributor: |
1496 RunGit(['commit', '--author', options.contributor, '-m', description]) | 1530 RunGit( |
| 1531 [ |
| 1532 'commit', '--author', options.contributor, |
| 1533 '-m', commit_desc.description, |
| 1534 ]) |
1497 else: | 1535 else: |
1498 RunGit(['commit', '-m', description]) | 1536 RunGit(['commit', '-m', commit_desc.description]) |
1499 if base_has_submodules: | 1537 if base_has_submodules: |
1500 cherry_pick_commit = RunGit(['rev-list', 'HEAD^!']).rstrip() | 1538 cherry_pick_commit = RunGit(['rev-list', 'HEAD^!']).rstrip() |
1501 RunGit(['branch', CHERRY_PICK_BRANCH, svn_head]) | 1539 RunGit(['branch', CHERRY_PICK_BRANCH, svn_head]) |
1502 RunGit(['checkout', CHERRY_PICK_BRANCH]) | 1540 RunGit(['checkout', CHERRY_PICK_BRANCH]) |
1503 RunGit(['cherry-pick', cherry_pick_commit]) | 1541 RunGit(['cherry-pick', cherry_pick_commit]) |
1504 if cmd == 'push': | 1542 if cmd == 'push': |
1505 # push the merge branch. | 1543 # push the merge branch. |
1506 remote, branch = cl.FetchUpstreamTuple(cl.GetBranch()) | 1544 remote, branch = cl.FetchUpstreamTuple(cl.GetBranch()) |
1507 retcode, output = RunGitWithCode( | 1545 retcode, output = RunGitWithCode( |
1508 ['push', '--porcelain', remote, 'HEAD:%s' % branch]) | 1546 ['push', '--porcelain', remote, 'HEAD:%s' % branch]) |
(...skipping 18 matching lines...) Expand all Loading... |
1527 for l in output.splitlines(False)) | 1565 for l in output.splitlines(False)) |
1528 match = filter(None, match) | 1566 match = filter(None, match) |
1529 if len(match) != 1: | 1567 if len(match) != 1: |
1530 DieWithError("Couldn't parse ouput to extract the committed hash:\n%s" % | 1568 DieWithError("Couldn't parse ouput to extract the committed hash:\n%s" % |
1531 output) | 1569 output) |
1532 revision = match[0].group(2) | 1570 revision = match[0].group(2) |
1533 else: | 1571 else: |
1534 return 1 | 1572 return 1 |
1535 viewvc_url = settings.GetViewVCUrl() | 1573 viewvc_url = settings.GetViewVCUrl() |
1536 if viewvc_url and revision: | 1574 if viewvc_url and revision: |
1537 cl.description += ('\n\nCommitted: ' + viewvc_url + revision) | 1575 change_desc.append_footer('Committed: ' + viewvc_url + revision) |
1538 elif revision: | 1576 elif revision: |
1539 cl.description += ('\n\nCommitted: ' + revision) | 1577 change_desc.append_footer('Committed: ' + revision) |
1540 print ('Closing issue ' | 1578 print ('Closing issue ' |
1541 '(you may be prompted for your codereview password)...') | 1579 '(you may be prompted for your codereview password)...') |
1542 cl.UpdateDescription(cl.description) | 1580 cl.UpdateDescription(change_desc.description) |
1543 cl.CloseIssue() | 1581 cl.CloseIssue() |
1544 props = cl.RpcServer().get_issue_properties(cl.GetIssue(), False) | 1582 props = cl.RpcServer().get_issue_properties(cl.GetIssue(), False) |
1545 patch_num = len(props['patchsets']) | 1583 patch_num = len(props['patchsets']) |
1546 comment = "Committed patchset #%d manually as r%s" % (patch_num, revision) | 1584 comment = "Committed patchset #%d manually as r%s" % (patch_num, revision) |
1547 comment += ' (presubmit successful).' if not options.bypass_hooks else '.' | 1585 comment += ' (presubmit successful).' if not options.bypass_hooks else '.' |
1548 cl.RpcServer().add_comment(cl.GetIssue(), comment) | 1586 cl.RpcServer().add_comment(cl.GetIssue(), comment) |
1549 cl.SetIssue(0) | 1587 cl.SetIssue(0) |
1550 | 1588 |
1551 if retcode == 0: | 1589 if retcode == 0: |
1552 hook = POSTUPSTREAM_HOOK_PATTERN % cmd | 1590 hook = POSTUPSTREAM_HOOK_PATTERN % cmd |
(...skipping 376 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1929 'and retry or visit go/isgaeup.\n%s') % (e.code, str(e))) | 1967 'and retry or visit go/isgaeup.\n%s') % (e.code, str(e))) |
1930 | 1968 |
1931 # Not a known command. Default to help. | 1969 # Not a known command. Default to help. |
1932 GenUsage(parser, 'help') | 1970 GenUsage(parser, 'help') |
1933 return CMDhelp(parser, argv) | 1971 return CMDhelp(parser, argv) |
1934 | 1972 |
1935 | 1973 |
1936 if __name__ == '__main__': | 1974 if __name__ == '__main__': |
1937 fix_encoding.fix_encoding() | 1975 fix_encoding.fix_encoding() |
1938 sys.exit(main(sys.argv[1:])) | 1976 sys.exit(main(sys.argv[1:])) |
OLD | NEW |