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 difflib | 10 import difflib |
(...skipping 1015 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1026 urlretrieve(src, dst) | 1026 urlretrieve(src, dst) |
1027 os.chmod(dst, stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR) | 1027 os.chmod(dst, stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR) |
1028 except Exception: | 1028 except Exception: |
1029 if os.path.exists(dst): | 1029 if os.path.exists(dst): |
1030 os.remove(dst) | 1030 os.remove(dst) |
1031 DieWithError('\nFailed to download hooks from %s' % src) | 1031 DieWithError('\nFailed to download hooks from %s' % src) |
1032 | 1032 |
1033 | 1033 |
1034 @usage('[repo root containing codereview.settings]') | 1034 @usage('[repo root containing codereview.settings]') |
1035 def CMDconfig(parser, args): | 1035 def CMDconfig(parser, args): |
1036 """Edits configuration for this tree.""" | 1036 """edit configuration for this tree""" |
1037 | 1037 |
1038 _, args = parser.parse_args(args) | 1038 _, args = parser.parse_args(args) |
1039 if len(args) == 0: | 1039 if len(args) == 0: |
1040 GetCodereviewSettingsInteractively() | 1040 GetCodereviewSettingsInteractively() |
1041 DownloadHooks(True) | 1041 DownloadHooks(True) |
1042 return 0 | 1042 return 0 |
1043 | 1043 |
1044 url = args[0] | 1044 url = args[0] |
1045 if not url.endswith('codereview.settings'): | 1045 if not url.endswith('codereview.settings'): |
1046 url = os.path.join(url, 'codereview.settings') | 1046 url = os.path.join(url, 'codereview.settings') |
1047 | 1047 |
1048 # Load code review settings and download hooks (if available). | 1048 # Load code review settings and download hooks (if available). |
1049 LoadCodereviewSettingsFromFile(urllib2.urlopen(url)) | 1049 LoadCodereviewSettingsFromFile(urllib2.urlopen(url)) |
1050 DownloadHooks(True) | 1050 DownloadHooks(True) |
1051 return 0 | 1051 return 0 |
1052 | 1052 |
1053 | 1053 |
1054 def CMDbaseurl(parser, args): | 1054 def CMDbaseurl(parser, args): |
1055 """Gets or sets base-url for this branch.""" | 1055 """get or set base-url for this branch""" |
1056 branchref = RunGit(['symbolic-ref', 'HEAD']).strip() | 1056 branchref = RunGit(['symbolic-ref', 'HEAD']).strip() |
1057 branch = ShortBranchName(branchref) | 1057 branch = ShortBranchName(branchref) |
1058 _, args = parser.parse_args(args) | 1058 _, args = parser.parse_args(args) |
1059 if not args: | 1059 if not args: |
1060 print("Current base-url:") | 1060 print("Current base-url:") |
1061 return RunGit(['config', 'branch.%s.base-url' % branch], | 1061 return RunGit(['config', 'branch.%s.base-url' % branch], |
1062 error_ok=False).strip() | 1062 error_ok=False).strip() |
1063 else: | 1063 else: |
1064 print("Setting base-url to %s" % args[0]) | 1064 print("Setting base-url to %s" % args[0]) |
1065 return RunGit(['config', 'branch.%s.base-url' % branch, args[0]], | 1065 return RunGit(['config', 'branch.%s.base-url' % branch, args[0]], |
1066 error_ok=False).strip() | 1066 error_ok=False).strip() |
1067 | 1067 |
1068 | 1068 |
1069 def CMDstatus(parser, args): | 1069 def CMDstatus(parser, args): |
1070 """Show status of changelists. | 1070 """show status of changelists""" |
1071 | |
1072 Colors are used to tell the state of the CL unless --fast is used: | |
1073 - Green LGTM'ed | |
1074 - Blue waiting for review | |
1075 - Yellow waiting for you to reply to review | |
1076 - Red not sent for review or broken | |
1077 - Cyan was committed, branch can be deleted | |
1078 | |
1079 Also see 'git cl comments'. | |
1080 """ | |
1081 parser.add_option('--field', | 1071 parser.add_option('--field', |
1082 help='print only specific field (desc|id|patch|url)') | 1072 help='print only specific field (desc|id|patch|url)') |
1083 parser.add_option('-f', '--fast', action='store_true', | 1073 parser.add_option('-f', '--fast', action='store_true', |
1084 help='Do not retrieve review status') | 1074 help='Do not retrieve review status') |
1085 (options, args) = parser.parse_args(args) | 1075 (options, args) = parser.parse_args(args) |
1086 | 1076 |
1087 if options.field: | 1077 if options.field: |
1088 cl = Changelist() | 1078 cl = Changelist() |
1089 if options.field.startswith('desc'): | 1079 if options.field.startswith('desc'): |
1090 print cl.GetDescription() | 1080 print cl.GetDescription() |
(...skipping 21 matching lines...) Expand all Loading... |
1112 alignment = max(5, max(len(b) for b in branches)) | 1102 alignment = max(5, max(len(b) for b in branches)) |
1113 print 'Branches associated with reviews:' | 1103 print 'Branches associated with reviews:' |
1114 # Adhoc thread pool to request data concurrently. | 1104 # Adhoc thread pool to request data concurrently. |
1115 output = Queue.Queue() | 1105 output = Queue.Queue() |
1116 | 1106 |
1117 # Silence upload.py otherwise it becomes unweldly. | 1107 # Silence upload.py otherwise it becomes unweldly. |
1118 upload.verbosity = 0 | 1108 upload.verbosity = 0 |
1119 | 1109 |
1120 if not options.fast: | 1110 if not options.fast: |
1121 def fetch(b): | 1111 def fetch(b): |
1122 """Fetches information for an issue and returns (branch, issue, color).""" | |
1123 c = Changelist(branchref=b) | 1112 c = Changelist(branchref=b) |
1124 i = c.GetIssueURL() | 1113 i = c.GetIssueURL() |
1125 props = {} | 1114 try: |
1126 r = None | 1115 props = c.GetIssueProperties() |
1127 if i: | 1116 r = c.GetApprovingReviewers() if i else None |
1128 try: | 1117 if not props.get('messages'): |
1129 props = c.GetIssueProperties() | 1118 r = None |
1130 r = c.GetApprovingReviewers() if i else None | 1119 except urllib2.HTTPError: |
1131 except urllib2.HTTPError: | 1120 # The issue probably doesn't exist anymore. |
1132 # The issue probably doesn't exist anymore. | 1121 i += ' (broken)' |
1133 i += ' (broken)' | 1122 r = None |
1134 | 1123 output.put((b, i, r)) |
1135 msgs = props.get('messages') or [] | |
1136 | |
1137 if not i: | |
1138 color = Fore.WHITE | |
1139 elif props.get('closed'): | |
1140 # Issue is closed. | |
1141 color = Fore.CYAN | |
1142 elif r: | |
1143 # Was LGTM'ed. | |
1144 color = Fore.GREEN | |
1145 elif not msgs: | |
1146 # No message was sent. | |
1147 color = Fore.RED | |
1148 elif msgs[-1]['sender'] != props.get('owner_email'): | |
1149 color = Fore.YELLOW | |
1150 else: | |
1151 color = Fore.BLUE | |
1152 output.put((b, i, color)) | |
1153 | 1124 |
1154 threads = [threading.Thread(target=fetch, args=(b,)) for b in branches] | 1125 threads = [threading.Thread(target=fetch, args=(b,)) for b in branches] |
1155 for t in threads: | 1126 for t in threads: |
1156 t.daemon = True | 1127 t.daemon = True |
1157 t.start() | 1128 t.start() |
1158 else: | 1129 else: |
1159 # Do not use GetApprovingReviewers(), since it requires an HTTP request. | 1130 # Do not use GetApprovingReviewers(), since it requires an HTTP request. |
1160 for b in branches: | 1131 for b in branches: |
1161 c = Changelist(branchref=b) | 1132 c = Changelist(branchref=b) |
1162 url = c.GetIssueURL() | 1133 output.put((b, c.GetIssue(), None)) |
1163 output.put((b, url, Fore.BLUE if url else Fore.WHITE)) | |
1164 | 1134 |
1165 tmp = {} | 1135 tmp = {} |
1166 alignment = max(5, max(len(ShortBranchName(b)) for b in branches)) | 1136 alignment = max(5, max(len(ShortBranchName(b)) for b in branches)) |
1167 for branch in sorted(branches): | 1137 for branch in sorted(branches): |
1168 while branch not in tmp: | 1138 while branch not in tmp: |
1169 b, i, color = output.get() | 1139 b, i, r = output.get() |
1170 tmp[b] = (i, color) | 1140 tmp[b] = (i, r) |
1171 issue, color = tmp.pop(branch) | 1141 issue, reviewers = tmp.pop(branch) |
| 1142 if not issue: |
| 1143 color = Fore.WHITE |
| 1144 elif reviewers: |
| 1145 # Was approved. |
| 1146 color = Fore.GREEN |
| 1147 elif reviewers is None: |
| 1148 # No message was sent. |
| 1149 color = Fore.RED |
| 1150 else: |
| 1151 color = Fore.BLUE |
1172 print ' %*s: %s%s%s' % ( | 1152 print ' %*s: %s%s%s' % ( |
1173 alignment, ShortBranchName(branch), color, issue, Fore.RESET) | 1153 alignment, ShortBranchName(branch), color, issue, Fore.RESET) |
1174 | 1154 |
1175 cl = Changelist() | 1155 cl = Changelist() |
1176 print | 1156 print |
1177 print 'Current branch:', | 1157 print 'Current branch:', |
1178 if not cl.GetIssue(): | 1158 if not cl.GetIssue(): |
1179 print 'no issue assigned.' | 1159 print 'no issue assigned.' |
1180 return 0 | 1160 return 0 |
1181 print cl.GetBranch() | 1161 print cl.GetBranch() |
1182 print 'Issue number: %s (%s)' % (cl.GetIssue(), cl.GetIssueURL()) | 1162 print 'Issue number: %s (%s)' % (cl.GetIssue(), cl.GetIssueURL()) |
1183 print 'Issue description:' | 1163 print 'Issue description:' |
1184 print cl.GetDescription(pretty=True) | 1164 print cl.GetDescription(pretty=True) |
1185 return 0 | 1165 return 0 |
1186 | 1166 |
1187 | 1167 |
1188 @usage('[issue_number]') | 1168 @usage('[issue_number]') |
1189 def CMDissue(parser, args): | 1169 def CMDissue(parser, args): |
1190 """Sets or displays the current code review issue number. | 1170 """Set or display the current code review issue number. |
1191 | 1171 |
1192 Pass issue number 0 to clear the current issue. | 1172 Pass issue number 0 to clear the current issue. |
1193 """ | 1173 """ |
1194 _, args = parser.parse_args(args) | 1174 _, args = parser.parse_args(args) |
1195 | 1175 |
1196 cl = Changelist() | 1176 cl = Changelist() |
1197 if len(args) > 0: | 1177 if len(args) > 0: |
1198 try: | 1178 try: |
1199 issue = int(args[0]) | 1179 issue = int(args[0]) |
1200 except ValueError: | 1180 except ValueError: |
1201 DieWithError('Pass a number to set the issue or none to list it.\n' | 1181 DieWithError('Pass a number to set the issue or none to list it.\n' |
1202 'Maybe you want to run git cl status?') | 1182 'Maybe you want to run git cl status?') |
1203 cl.SetIssue(issue) | 1183 cl.SetIssue(issue) |
1204 print 'Issue number: %s (%s)' % (cl.GetIssue(), cl.GetIssueURL()) | 1184 print 'Issue number: %s (%s)' % (cl.GetIssue(), cl.GetIssueURL()) |
1205 return 0 | 1185 return 0 |
1206 | 1186 |
1207 | 1187 |
1208 def CMDcomments(parser, args): | 1188 def CMDcomments(parser, args): |
1209 """Shows review comments of the current changelist.""" | 1189 """show review comments of the current changelist""" |
1210 (_, args) = parser.parse_args(args) | 1190 (_, args) = parser.parse_args(args) |
1211 if args: | 1191 if args: |
1212 parser.error('Unsupported argument: %s' % args) | 1192 parser.error('Unsupported argument: %s' % args) |
1213 | 1193 |
1214 cl = Changelist() | 1194 cl = Changelist() |
1215 if cl.GetIssue(): | 1195 if cl.GetIssue(): |
1216 data = cl.GetIssueProperties() | 1196 data = cl.GetIssueProperties() |
1217 for message in sorted(data['messages'], key=lambda x: x['date']): | 1197 for message in sorted(data['messages'], key=lambda x: x['date']): |
1218 if message['disapproval']: | 1198 if message['disapproval']: |
1219 color = Fore.RED | 1199 color = Fore.RED |
1220 elif message['approval']: | 1200 elif message['approval']: |
1221 color = Fore.GREEN | 1201 color = Fore.GREEN |
1222 elif message['sender'] == data['owner_email']: | 1202 elif message['sender'] == data['owner_email']: |
1223 color = Fore.MAGENTA | 1203 color = Fore.MAGENTA |
1224 else: | 1204 else: |
1225 color = Fore.BLUE | 1205 color = Fore.BLUE |
1226 print '\n%s%s %s%s' % ( | 1206 print '\n%s%s %s%s' % ( |
1227 color, message['date'].split('.', 1)[0], message['sender'], | 1207 color, message['date'].split('.', 1)[0], message['sender'], |
1228 Fore.RESET) | 1208 Fore.RESET) |
1229 if message['text'].strip(): | 1209 if message['text'].strip(): |
1230 print '\n'.join(' ' + l for l in message['text'].splitlines()) | 1210 print '\n'.join(' ' + l for l in message['text'].splitlines()) |
1231 return 0 | 1211 return 0 |
1232 | 1212 |
1233 | 1213 |
1234 def CMDdescription(parser, args): | 1214 def CMDdescription(parser, args): |
1235 """Brings up the editor for the current CL's description.""" | 1215 """brings up the editor for the current CL's description.""" |
1236 cl = Changelist() | 1216 cl = Changelist() |
1237 if not cl.GetIssue(): | 1217 if not cl.GetIssue(): |
1238 DieWithError('This branch has no associated changelist.') | 1218 DieWithError('This branch has no associated changelist.') |
1239 description = ChangeDescription(cl.GetDescription()) | 1219 description = ChangeDescription(cl.GetDescription()) |
1240 description.prompt() | 1220 description.prompt() |
1241 cl.UpdateDescription(description.description) | 1221 cl.UpdateDescription(description.description) |
1242 return 0 | 1222 return 0 |
1243 | 1223 |
1244 | 1224 |
1245 def CreateDescriptionFromLog(args): | 1225 def CreateDescriptionFromLog(args): |
1246 """Pulls out the commit log to use as a base for the CL description.""" | 1226 """Pulls out the commit log to use as a base for the CL description.""" |
1247 log_args = [] | 1227 log_args = [] |
1248 if len(args) == 1 and not args[0].endswith('.'): | 1228 if len(args) == 1 and not args[0].endswith('.'): |
1249 log_args = [args[0] + '..'] | 1229 log_args = [args[0] + '..'] |
1250 elif len(args) == 1 and args[0].endswith('...'): | 1230 elif len(args) == 1 and args[0].endswith('...'): |
1251 log_args = [args[0][:-1]] | 1231 log_args = [args[0][:-1]] |
1252 elif len(args) == 2: | 1232 elif len(args) == 2: |
1253 log_args = [args[0] + '..' + args[1]] | 1233 log_args = [args[0] + '..' + args[1]] |
1254 else: | 1234 else: |
1255 log_args = args[:] # Hope for the best! | 1235 log_args = args[:] # Hope for the best! |
1256 return RunGit(['log', '--pretty=format:%s\n\n%b'] + log_args) | 1236 return RunGit(['log', '--pretty=format:%s\n\n%b'] + log_args) |
1257 | 1237 |
1258 | 1238 |
1259 def CMDpresubmit(parser, args): | 1239 def CMDpresubmit(parser, args): |
1260 """Runs presubmit tests on the current changelist.""" | 1240 """run presubmit tests on the current changelist""" |
1261 parser.add_option('-u', '--upload', action='store_true', | 1241 parser.add_option('-u', '--upload', action='store_true', |
1262 help='Run upload hook instead of the push/dcommit hook') | 1242 help='Run upload hook instead of the push/dcommit hook') |
1263 parser.add_option('-f', '--force', action='store_true', | 1243 parser.add_option('-f', '--force', action='store_true', |
1264 help='Run checks even if tree is dirty') | 1244 help='Run checks even if tree is dirty') |
1265 (options, args) = parser.parse_args(args) | 1245 (options, args) = parser.parse_args(args) |
1266 | 1246 |
1267 if not options.force and is_dirty_git_tree('presubmit'): | 1247 if not options.force and is_dirty_git_tree('presubmit'): |
1268 print 'use --force to check even if tree is dirty.' | 1248 print 'use --force to check even if tree is dirty.' |
1269 return 1 | 1249 return 1 |
1270 | 1250 |
(...skipping 166 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1437 So that "--reviewers joe@c,john@c --reviewers joa@c" results in | 1417 So that "--reviewers joe@c,john@c --reviewers joa@c" results in |
1438 options.reviewers == sorted(['joe@c', 'john@c', 'joa@c']). | 1418 options.reviewers == sorted(['joe@c', 'john@c', 'joa@c']). |
1439 """ | 1419 """ |
1440 items = sum((i.split(',') for i in l), []) | 1420 items = sum((i.split(',') for i in l), []) |
1441 stripped_items = (i.strip() for i in items) | 1421 stripped_items = (i.strip() for i in items) |
1442 return sorted(filter(None, stripped_items)) | 1422 return sorted(filter(None, stripped_items)) |
1443 | 1423 |
1444 | 1424 |
1445 @usage('[args to "git diff"]') | 1425 @usage('[args to "git diff"]') |
1446 def CMDupload(parser, args): | 1426 def CMDupload(parser, args): |
1447 """Uploads the current changelist to codereview.""" | 1427 """upload the current changelist to codereview""" |
1448 parser.add_option('--bypass-hooks', action='store_true', dest='bypass_hooks', | 1428 parser.add_option('--bypass-hooks', action='store_true', dest='bypass_hooks', |
1449 help='bypass upload presubmit hook') | 1429 help='bypass upload presubmit hook') |
1450 parser.add_option('--bypass-watchlists', action='store_true', | 1430 parser.add_option('--bypass-watchlists', action='store_true', |
1451 dest='bypass_watchlists', | 1431 dest='bypass_watchlists', |
1452 help='bypass watchlists auto CC-ing reviewers') | 1432 help='bypass watchlists auto CC-ing reviewers') |
1453 parser.add_option('-f', action='store_true', dest='force', | 1433 parser.add_option('-f', action='store_true', dest='force', |
1454 help="force yes to questions (don't prompt)") | 1434 help="force yes to questions (don't prompt)") |
1455 parser.add_option('-m', dest='message', help='message for patchset') | 1435 parser.add_option('-m', dest='message', help='message for patchset') |
1456 parser.add_option('-t', dest='title', help='title for patchset') | 1436 parser.add_option('-t', dest='title', help='title for patchset') |
1457 parser.add_option('-r', '--reviewers', | 1437 parser.add_option('-r', '--reviewers', |
(...skipping 302 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1760 if retcode == 0: | 1740 if retcode == 0: |
1761 hook = POSTUPSTREAM_HOOK_PATTERN % cmd | 1741 hook = POSTUPSTREAM_HOOK_PATTERN % cmd |
1762 if os.path.isfile(hook): | 1742 if os.path.isfile(hook): |
1763 RunCommand([hook, base_branch], error_ok=True) | 1743 RunCommand([hook, base_branch], error_ok=True) |
1764 | 1744 |
1765 return 0 | 1745 return 0 |
1766 | 1746 |
1767 | 1747 |
1768 @usage('[upstream branch to apply against]') | 1748 @usage('[upstream branch to apply against]') |
1769 def CMDdcommit(parser, args): | 1749 def CMDdcommit(parser, args): |
1770 """Commits the current changelist via git-svn.""" | 1750 """commit the current changelist via git-svn""" |
1771 if not settings.GetIsGitSvn(): | 1751 if not settings.GetIsGitSvn(): |
1772 message = """This doesn't appear to be an SVN repository. | 1752 message = """This doesn't appear to be an SVN repository. |
1773 If your project has a git mirror with an upstream SVN master, you probably need | 1753 If your project has a git mirror with an upstream SVN master, you probably need |
1774 to run 'git svn init', see your project's git mirror documentation. | 1754 to run 'git svn init', see your project's git mirror documentation. |
1775 If your project has a true writeable upstream repository, you probably want | 1755 If your project has a true writeable upstream repository, you probably want |
1776 to run 'git cl push' instead. | 1756 to run 'git cl push' instead. |
1777 Choose wisely, if you get this wrong, your commit might appear to succeed but | 1757 Choose wisely, if you get this wrong, your commit might appear to succeed but |
1778 will instead be silently ignored.""" | 1758 will instead be silently ignored.""" |
1779 print(message) | 1759 print(message) |
1780 ask_for_data('[Press enter to dcommit or ctrl-C to quit]') | 1760 ask_for_data('[Press enter to dcommit or ctrl-C to quit]') |
1781 return SendUpstream(parser, args, 'dcommit') | 1761 return SendUpstream(parser, args, 'dcommit') |
1782 | 1762 |
1783 | 1763 |
1784 @usage('[upstream branch to apply against]') | 1764 @usage('[upstream branch to apply against]') |
1785 def CMDpush(parser, args): | 1765 def CMDpush(parser, args): |
1786 """Commits the current changelist via git.""" | 1766 """commit the current changelist via git""" |
1787 if settings.GetIsGitSvn(): | 1767 if settings.GetIsGitSvn(): |
1788 print('This appears to be an SVN repository.') | 1768 print('This appears to be an SVN repository.') |
1789 print('Are you sure you didn\'t mean \'git cl dcommit\'?') | 1769 print('Are you sure you didn\'t mean \'git cl dcommit\'?') |
1790 ask_for_data('[Press enter to push or ctrl-C to quit]') | 1770 ask_for_data('[Press enter to push or ctrl-C to quit]') |
1791 return SendUpstream(parser, args, 'push') | 1771 return SendUpstream(parser, args, 'push') |
1792 | 1772 |
1793 | 1773 |
1794 @usage('<patch url or issue id>') | 1774 @usage('<patch url or issue id>') |
1795 def CMDpatch(parser, args): | 1775 def CMDpatch(parser, args): |
1796 """Patchs in a code review.""" | 1776 """patch in a code review""" |
1797 parser.add_option('-b', dest='newbranch', | 1777 parser.add_option('-b', dest='newbranch', |
1798 help='create a new branch off trunk for the patch') | 1778 help='create a new branch off trunk for the patch') |
1799 parser.add_option('-f', action='store_true', dest='force', | 1779 parser.add_option('-f', action='store_true', dest='force', |
1800 help='with -b, clobber any existing branch') | 1780 help='with -b, clobber any existing branch') |
1801 parser.add_option('--reject', action='store_true', dest='reject', | 1781 parser.add_option('--reject', action='store_true', dest='reject', |
1802 help='failed patches spew .rej files rather than ' | 1782 help='failed patches spew .rej files rather than ' |
1803 'attempting a 3-way merge') | 1783 'attempting a 3-way merge') |
1804 parser.add_option('-n', '--no-commit', action='store_true', dest='nocommit', | 1784 parser.add_option('-n', '--no-commit', action='store_true', dest='nocommit', |
1805 help="don't commit after patch applies") | 1785 help="don't commit after patch applies") |
1806 (options, args) = parser.parse_args(args) | 1786 (options, args) = parser.parse_args(args) |
(...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1876 cl = Changelist() | 1856 cl = Changelist() |
1877 cl.SetIssue(issue) | 1857 cl.SetIssue(issue) |
1878 cl.SetPatchset(patchset) | 1858 cl.SetPatchset(patchset) |
1879 print "Committed patch locally." | 1859 print "Committed patch locally." |
1880 else: | 1860 else: |
1881 print "Patch applied to index." | 1861 print "Patch applied to index." |
1882 return 0 | 1862 return 0 |
1883 | 1863 |
1884 | 1864 |
1885 def CMDrebase(parser, args): | 1865 def CMDrebase(parser, args): |
1886 """Rebases current branch on top of svn repo.""" | 1866 """rebase current branch on top of svn repo""" |
1887 # Provide a wrapper for git svn rebase to help avoid accidental | 1867 # Provide a wrapper for git svn rebase to help avoid accidental |
1888 # git svn dcommit. | 1868 # git svn dcommit. |
1889 # It's the only command that doesn't use parser at all since we just defer | 1869 # It's the only command that doesn't use parser at all since we just defer |
1890 # execution to git-svn. | 1870 # execution to git-svn. |
1891 env = os.environ.copy() | 1871 env = os.environ.copy() |
1892 # 'cat' is a magical git string that disables pagers on all platforms. | 1872 # 'cat' is a magical git string that disables pagers on all platforms. |
1893 env['GIT_PAGER'] = 'cat' | 1873 env['GIT_PAGER'] = 'cat' |
1894 | 1874 |
1895 return subprocess2.call(['git', 'svn', 'rebase'] + args, env=env) | 1875 return subprocess2.call(['git', 'svn', 'rebase'] + args, env=env) |
1896 | 1876 |
(...skipping 17 matching lines...) Expand all Loading... |
1914 with the reason for the tree to be opened or closed.""" | 1894 with the reason for the tree to be opened or closed.""" |
1915 url = settings.GetTreeStatusUrl() | 1895 url = settings.GetTreeStatusUrl() |
1916 json_url = urlparse.urljoin(url, '/current?format=json') | 1896 json_url = urlparse.urljoin(url, '/current?format=json') |
1917 connection = urllib2.urlopen(json_url) | 1897 connection = urllib2.urlopen(json_url) |
1918 status = json.loads(connection.read()) | 1898 status = json.loads(connection.read()) |
1919 connection.close() | 1899 connection.close() |
1920 return status['message'] | 1900 return status['message'] |
1921 | 1901 |
1922 | 1902 |
1923 def CMDtree(parser, args): | 1903 def CMDtree(parser, args): |
1924 """Shows the status of the tree.""" | 1904 """show the status of the tree""" |
1925 _, args = parser.parse_args(args) | 1905 _, args = parser.parse_args(args) |
1926 status = GetTreeStatus() | 1906 status = GetTreeStatus() |
1927 if 'unset' == status: | 1907 if 'unset' == status: |
1928 print 'You must configure your tree status URL by running "git cl config".' | 1908 print 'You must configure your tree status URL by running "git cl config".' |
1929 return 2 | 1909 return 2 |
1930 | 1910 |
1931 print "The tree is %s" % status | 1911 print "The tree is %s" % status |
1932 print | 1912 print |
1933 print GetTreeStatusReason() | 1913 print GetTreeStatusReason() |
1934 if status != 'open': | 1914 if status != 'open': |
(...skipping 95 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2030 builders_and_tests) | 2010 builders_and_tests) |
2031 print('Tried jobs on:') | 2011 print('Tried jobs on:') |
2032 length = max(len(builder) for builder in builders_and_tests) | 2012 length = max(len(builder) for builder in builders_and_tests) |
2033 for builder in sorted(builders_and_tests): | 2013 for builder in sorted(builders_and_tests): |
2034 print ' %*s: %s' % (length, builder, ','.join(builders_and_tests[builder])) | 2014 print ' %*s: %s' % (length, builder, ','.join(builders_and_tests[builder])) |
2035 return 0 | 2015 return 0 |
2036 | 2016 |
2037 | 2017 |
2038 @usage('[new upstream branch]') | 2018 @usage('[new upstream branch]') |
2039 def CMDupstream(parser, args): | 2019 def CMDupstream(parser, args): |
2040 """Prints or sets the name of the upstream branch, if any.""" | 2020 """prints or sets the name of the upstream branch, if any""" |
2041 _, args = parser.parse_args(args) | 2021 _, args = parser.parse_args(args) |
2042 if len(args) > 1: | 2022 if len(args) > 1: |
2043 parser.error('Unrecognized args: %s' % ' '.join(args)) | 2023 parser.error('Unrecognized args: %s' % ' '.join(args)) |
2044 return 0 | 2024 return 0 |
2045 | 2025 |
2046 cl = Changelist() | 2026 cl = Changelist() |
2047 if args: | 2027 if args: |
2048 # One arg means set upstream branch. | 2028 # One arg means set upstream branch. |
2049 RunGit(['branch', '--set-upstream', cl.GetBranch(), args[0]]) | 2029 RunGit(['branch', '--set-upstream', cl.GetBranch(), args[0]]) |
2050 cl = Changelist() | 2030 cl = Changelist() |
2051 print "Upstream branch set to " + cl.GetUpstreamBranch() | 2031 print "Upstream branch set to " + cl.GetUpstreamBranch() |
2052 else: | 2032 else: |
2053 print cl.GetUpstreamBranch() | 2033 print cl.GetUpstreamBranch() |
2054 return 0 | 2034 return 0 |
2055 | 2035 |
2056 | 2036 |
2057 def CMDset_commit(parser, args): | 2037 def CMDset_commit(parser, args): |
2058 """Sets the commit bit to trigger the Commit Queue.""" | 2038 """set the commit bit""" |
2059 _, args = parser.parse_args(args) | 2039 _, args = parser.parse_args(args) |
2060 if args: | 2040 if args: |
2061 parser.error('Unrecognized args: %s' % ' '.join(args)) | 2041 parser.error('Unrecognized args: %s' % ' '.join(args)) |
2062 cl = Changelist() | 2042 cl = Changelist() |
2063 cl.SetFlag('commit', '1') | 2043 cl.SetFlag('commit', '1') |
2064 return 0 | 2044 return 0 |
2065 | 2045 |
2066 | 2046 |
2067 def CMDset_close(parser, args): | 2047 def CMDset_close(parser, args): |
2068 """Closes the issue.""" | 2048 """close the issue""" |
2069 _, args = parser.parse_args(args) | 2049 _, args = parser.parse_args(args) |
2070 if args: | 2050 if args: |
2071 parser.error('Unrecognized args: %s' % ' '.join(args)) | 2051 parser.error('Unrecognized args: %s' % ' '.join(args)) |
2072 cl = Changelist() | 2052 cl = Changelist() |
2073 # Ensure there actually is an issue to close. | 2053 # Ensure there actually is an issue to close. |
2074 cl.GetDescription() | 2054 cl.GetDescription() |
2075 cl.CloseIssue() | 2055 cl.CloseIssue() |
2076 return 0 | 2056 return 0 |
2077 | 2057 |
2078 | 2058 |
2079 def CMDformat(parser, args): | 2059 def CMDformat(parser, args): |
2080 """Runs clang-format on the diff.""" | 2060 """run clang-format on the diff""" |
2081 CLANG_EXTS = ['.cc', '.cpp', '.h'] | 2061 CLANG_EXTS = ['.cc', '.cpp', '.h'] |
2082 parser.add_option('--full', action='store_true', default=False) | 2062 parser.add_option('--full', action='store_true', default=False) |
2083 opts, args = parser.parse_args(args) | 2063 opts, args = parser.parse_args(args) |
2084 if args: | 2064 if args: |
2085 parser.error('Unrecognized args: %s' % ' '.join(args)) | 2065 parser.error('Unrecognized args: %s' % ' '.join(args)) |
2086 | 2066 |
2087 # Generate diff for the current branch's changes. | 2067 # Generate diff for the current branch's changes. |
2088 diff_cmd = ['diff', '--no-ext-diff', '--no-prefix'] | 2068 diff_cmd = ['diff', '--no-ext-diff', '--no-prefix'] |
2089 if opts.full: | 2069 if opts.full: |
2090 # Only list the names of modified files. | 2070 # Only list the names of modified files. |
(...skipping 76 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2167 return | 2147 return |
2168 | 2148 |
2169 if hamming_commands[0][0] < 0.8: | 2149 if hamming_commands[0][0] < 0.8: |
2170 # Not similar enough. Don't be a fool and run a random command. | 2150 # Not similar enough. Don't be a fool and run a random command. |
2171 return | 2151 return |
2172 | 2152 |
2173 return commands[hamming_commands[0][1]] | 2153 return commands[hamming_commands[0][1]] |
2174 | 2154 |
2175 | 2155 |
2176 def CMDhelp(parser, args): | 2156 def CMDhelp(parser, args): |
2177 """Prints list of commands or help for a specific command.""" | 2157 """print list of commands or help for a specific command""" |
2178 _, args = parser.parse_args(args) | 2158 _, args = parser.parse_args(args) |
2179 if len(args) == 1: | 2159 if len(args) == 1: |
2180 return main(args + ['--help']) | 2160 return main(args + ['--help']) |
2181 parser.print_help() | 2161 parser.print_help() |
2182 return 0 | 2162 return 0 |
2183 | 2163 |
2184 | 2164 |
2185 def GenUsage(parser, command): | 2165 def GenUsage(parser, command): |
2186 """Modify an OptParse object with the function's documentation.""" | 2166 """Modify an OptParse object with the function's documentation.""" |
2187 obj = Command(command) | 2167 obj = Command(command) |
2188 # Get back the real command name in case Command() guess the actual command | 2168 # Get back the real command name in case Command() guess the actual command |
2189 # name. | 2169 # name. |
2190 command = obj.__name__[3:] | 2170 command = obj.__name__[3:] |
2191 more = getattr(obj, 'usage_more', '') | 2171 more = getattr(obj, 'usage_more', '') |
2192 if command == 'help': | 2172 if command == 'help': |
2193 command = '<command>' | 2173 command = '<command>' |
2194 else: | 2174 else: |
2195 parser.description = obj.__doc__ | 2175 # OptParser.description prefer nicely non-formatted strings. |
| 2176 parser.description = re.sub('[\r\n ]{2,}', ' ', obj.__doc__) |
2196 parser.set_usage('usage: %%prog %s [options] %s' % (command, more)) | 2177 parser.set_usage('usage: %%prog %s [options] %s' % (command, more)) |
2197 | 2178 |
2198 | 2179 |
2199 class OptionParser(optparse.OptionParser): | |
2200 """Creates the option parse and add --verbose support.""" | |
2201 def __init__(self, *args, **kwargs): | |
2202 optparse.OptionParser.__init__(self, *args, **kwargs) | |
2203 self.add_option( | |
2204 '-v', '--verbose', action='count', default=0, | |
2205 help='Use 2 times for more debugging info') | |
2206 | |
2207 def parse_args(self, args=None, values=None): | |
2208 options, args = optparse.OptionParser.parse_args(self, args, values) | |
2209 levels = [logging.WARNING, logging.INFO, logging.DEBUG] | |
2210 logging.basicConfig(level=levels[min(options.verbose, len(levels) - 1)]) | |
2211 return options, args | |
2212 | |
2213 def format_description(self, _): | |
2214 """Disables automatic reformatting.""" | |
2215 lines = self.description.rstrip().splitlines() | |
2216 lines_fixed = [lines[0]] + [l[2:] if len(l) >= 2 else l for l in lines[1:]] | |
2217 description = ''.join(l + '\n' for l in lines_fixed) | |
2218 return description[0].upper() + description[1:] | |
2219 | |
2220 | |
2221 def main(argv): | 2180 def main(argv): |
2222 """Doesn't parse the arguments here, just find the right subcommand to | 2181 """Doesn't parse the arguments here, just find the right subcommand to |
2223 execute.""" | 2182 execute.""" |
2224 if sys.hexversion < 0x02060000: | 2183 if sys.hexversion < 0x02060000: |
2225 print >> sys.stderr, ( | 2184 print >> sys.stderr, ( |
2226 '\nYour python version %s is unsupported, please upgrade.\n' % | 2185 '\nYour python version %s is unsupported, please upgrade.\n' % |
2227 sys.version.split(' ', 1)[0]) | 2186 sys.version.split(' ', 1)[0]) |
2228 return 2 | 2187 return 2 |
2229 | 2188 |
2230 # Reload settings. | 2189 # Reload settings. |
2231 global settings | 2190 global settings |
2232 settings = Settings() | 2191 settings = Settings() |
2233 | 2192 |
2234 # Do it late so all commands are listed. | 2193 # Do it late so all commands are listed. |
2235 commands = Commands() | 2194 commands = Commands() |
2236 length = max(len(c) for c in commands) | 2195 length = max(len(c) for c in commands) |
2237 | |
2238 def gen_summary(x): | |
2239 """Creates a oneline summary from the docstring.""" | |
2240 line = x.split('\n', 1)[0].rstrip('.') | |
2241 return line[0].lower() + line[1:] | |
2242 | |
2243 docs = sorted( | 2196 docs = sorted( |
2244 (name, gen_summary(handler.__doc__).strip()) | 2197 (name, handler.__doc__.split('\n')[0].strip()) |
2245 for name, handler in commands.iteritems()) | 2198 for name, handler in commands.iteritems()) |
2246 CMDhelp.usage_more = ('\n\nCommands are:\n' + '\n'.join( | 2199 CMDhelp.usage_more = ('\n\nCommands are:\n' + '\n'.join( |
2247 ' %-*s %s' % (length, name, doc) for name, doc in docs)) | 2200 ' %-*s %s' % (length, name, doc) for name, doc in docs)) |
2248 | 2201 |
2249 parser = OptionParser() | 2202 # Create the option parse and add --verbose support. |
| 2203 parser = optparse.OptionParser() |
| 2204 parser.add_option( |
| 2205 '-v', '--verbose', action='count', default=0, |
| 2206 help='Use 2 times for more debugging info') |
| 2207 old_parser_args = parser.parse_args |
| 2208 def Parse(args): |
| 2209 options, args = old_parser_args(args) |
| 2210 if options.verbose >= 2: |
| 2211 logging.basicConfig(level=logging.DEBUG) |
| 2212 elif options.verbose: |
| 2213 logging.basicConfig(level=logging.INFO) |
| 2214 else: |
| 2215 logging.basicConfig(level=logging.WARNING) |
| 2216 return options, args |
| 2217 parser.parse_args = Parse |
| 2218 |
2250 if argv: | 2219 if argv: |
2251 command = Command(argv[0]) | 2220 command = Command(argv[0]) |
2252 if command: | 2221 if command: |
2253 # "fix" the usage and the description now that we know the subcommand. | 2222 # "fix" the usage and the description now that we know the subcommand. |
2254 GenUsage(parser, argv[0]) | 2223 GenUsage(parser, argv[0]) |
2255 try: | 2224 try: |
2256 return command(parser, argv[1:]) | 2225 return command(parser, argv[1:]) |
2257 except urllib2.HTTPError, e: | 2226 except urllib2.HTTPError, e: |
2258 if e.code != 500: | 2227 if e.code != 500: |
2259 raise | 2228 raise |
2260 DieWithError( | 2229 DieWithError( |
2261 ('AppEngine is misbehaving and returned HTTP %d, again. Keep faith ' | 2230 ('AppEngine is misbehaving and returned HTTP %d, again. Keep faith ' |
2262 'and retry or visit go/isgaeup.\n%s') % (e.code, str(e))) | 2231 'and retry or visit go/isgaeup.\n%s') % (e.code, str(e))) |
2263 | 2232 |
2264 # Not a known command. Default to help. | 2233 # Not a known command. Default to help. |
2265 GenUsage(parser, 'help') | 2234 GenUsage(parser, 'help') |
2266 return CMDhelp(parser, argv) | 2235 return CMDhelp(parser, argv) |
2267 | 2236 |
2268 | 2237 |
2269 if __name__ == '__main__': | 2238 if __name__ == '__main__': |
2270 # These affect sys.stdout so do it outside of main() to simplify mocks in | 2239 # These affect sys.stdout so do it outside of main() to simplify mocks in |
2271 # unit testing. | 2240 # unit testing. |
2272 fix_encoding.fix_encoding() | 2241 fix_encoding.fix_encoding() |
2273 colorama.init() | 2242 colorama.init() |
2274 sys.exit(main(sys.argv[1:])) | 2243 sys.exit(main(sys.argv[1:])) |
OLD | NEW |