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