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 from distutils.version import LooseVersion | 10 from distutils.version import LooseVersion |
11 import base64 | 11 import base64 |
12 import glob | 12 import glob |
13 import json | 13 import json |
14 import logging | 14 import logging |
15 import multiprocessing | |
15 import optparse | 16 import optparse |
16 import os | 17 import os |
17 import Queue | 18 import Queue |
18 import re | 19 import re |
19 import stat | 20 import stat |
20 import sys | 21 import sys |
21 import tempfile | 22 import tempfile |
22 import textwrap | 23 import textwrap |
23 import threading | |
24 import urllib2 | 24 import urllib2 |
25 import urlparse | 25 import urlparse |
26 import webbrowser | 26 import webbrowser |
27 import zlib | 27 import zlib |
28 | 28 |
29 try: | 29 try: |
30 import readline # pylint: disable=F0401,W0611 | 30 import readline # pylint: disable=F0401,W0611 |
31 except ImportError: | 31 except ImportError: |
32 pass | 32 pass |
33 | 33 |
(...skipping 1297 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1331 return { | 1331 return { |
1332 'unsent': Fore.RED, | 1332 'unsent': Fore.RED, |
1333 'waiting': Fore.BLUE, | 1333 'waiting': Fore.BLUE, |
1334 'reply': Fore.YELLOW, | 1334 'reply': Fore.YELLOW, |
1335 'lgtm': Fore.GREEN, | 1335 'lgtm': Fore.GREEN, |
1336 'commit': Fore.MAGENTA, | 1336 'commit': Fore.MAGENTA, |
1337 'closed': Fore.CYAN, | 1337 'closed': Fore.CYAN, |
1338 'error': Fore.WHITE, | 1338 'error': Fore.WHITE, |
1339 }.get(status, Fore.WHITE) | 1339 }.get(status, Fore.WHITE) |
1340 | 1340 |
1341 def fetch_cl_status(b): | |
1342 """Fetches information for an issue and returns (branch, issue, color).""" | |
1343 c = Changelist(branchref=b) | |
1344 i = c.GetIssueURL() | |
1345 status = c.GetStatus() | |
1346 color = color_for_status(status) | |
1347 | |
1348 if i and (not status or status == 'error'): | |
1349 # The issue probably doesn't exist anymore. | |
1350 i += ' (broken)' | |
1351 | |
1352 return (b, i, color) | |
1353 | |
1354 def get_cl_statuses(branches, fine_grained, max_processes=None): | |
1355 """Returns a blocking iterable of (branch, issue, color) for given branches. | |
1356 | |
1357 If fine_grained is true, this will fetch CL statuses from the server. | |
1358 Otherwise, simply indicate if there's a matching url for the given branches. | |
1359 | |
1360 If max_processes is specified, it is used as the maximum number of processes | |
1361 to spawn to fetch CL status from the server. Otherwise 1 process per branch is | |
1362 spawned. | |
1363 """ | |
1364 # Silence upload.py otherwise it becomes unwieldly. | |
1365 upload.verbosity = 0 | |
1366 | |
1367 if fine_grained: | |
1368 # Process one branch synchronously to work through authentication, then | |
1369 # spawn processes to process all the other branches in parallel. | |
1370 if branches: | |
1371 yield fetch_cl_status(branches[0]) | |
1372 | |
1373 branches_to_fetch = branches[1:] | |
1374 pool = multiprocessing.Pool( | |
1375 min(max_processes, len(branches_to_fetch)) | |
1376 if max_processes is not None | |
1377 else len(branches_to_fetch)) | |
1378 for x in pool.imap_unordered(fetch_cl_status, branches_to_fetch): | |
1379 yield x | |
1380 else: | |
1381 # Do not use GetApprovingReviewers(), since it requires an HTTP request. | |
1382 for b in branches: | |
1383 c = Changelist(branchref=b) | |
1384 url = c.GetIssueURL() | |
1385 yield (b, url, Fore.BLUE if url else Fore.WHITE) | |
1341 | 1386 |
1342 def CMDstatus(parser, args): | 1387 def CMDstatus(parser, args): |
1343 """Show status of changelists. | 1388 """Show status of changelists. |
1344 | 1389 |
1345 Colors are used to tell the state of the CL unless --fast is used: | 1390 Colors are used to tell the state of the CL unless --fast is used: |
1346 - Red not sent for review or broken | 1391 - Red not sent for review or broken |
1347 - Blue waiting for review | 1392 - Blue waiting for review |
1348 - Yellow waiting for you to reply to review | 1393 - Yellow waiting for you to reply to review |
1349 - Green LGTM'ed | 1394 - Green LGTM'ed |
1350 - Magenta in the commit queue | 1395 - Magenta in the commit queue |
1351 - Cyan was committed, branch can be deleted | 1396 - Cyan was committed, branch can be deleted |
1352 | 1397 |
1353 Also see 'git cl comments'. | 1398 Also see 'git cl comments'. |
1354 """ | 1399 """ |
1355 parser.add_option('--field', | 1400 parser.add_option('--field', |
1356 help='print only specific field (desc|id|patch|url)') | 1401 help='print only specific field (desc|id|patch|url)') |
1357 parser.add_option('-f', '--fast', action='store_true', | 1402 parser.add_option('-f', '--fast', action='store_true', |
1358 help='Do not retrieve review status') | 1403 help='Do not retrieve review status') |
1404 parser.add_option( | |
1405 '-j', '--maxjobs', action='store', type=int, | |
1406 help='The maximum number of jobs to use when retrieving review status') | |
1359 (options, args) = parser.parse_args(args) | 1407 (options, args) = parser.parse_args(args) |
1360 if args: | 1408 if args: |
1361 parser.error('Unsupported args: %s' % args) | 1409 parser.error('Unsupported args: %s' % args) |
1362 | 1410 |
1363 if options.field: | 1411 if options.field: |
1364 cl = Changelist() | 1412 cl = Changelist() |
1365 if options.field.startswith('desc'): | 1413 if options.field.startswith('desc'): |
1366 print cl.GetDescription() | 1414 print cl.GetDescription() |
1367 elif options.field == 'id': | 1415 elif options.field == 'id': |
1368 issueid = cl.GetIssue() | 1416 issueid = cl.GetIssue() |
(...skipping 11 matching lines...) Expand all Loading... | |
1380 | 1428 |
1381 branches = RunGit(['for-each-ref', '--format=%(refname)', 'refs/heads']) | 1429 branches = RunGit(['for-each-ref', '--format=%(refname)', 'refs/heads']) |
1382 if not branches: | 1430 if not branches: |
1383 print('No local branch found.') | 1431 print('No local branch found.') |
1384 return 0 | 1432 return 0 |
1385 | 1433 |
1386 changes = (Changelist(branchref=b) for b in branches.splitlines()) | 1434 changes = (Changelist(branchref=b) for b in branches.splitlines()) |
1387 branches = [c.GetBranch() for c in changes] | 1435 branches = [c.GetBranch() for c in changes] |
1388 alignment = max(5, max(len(b) for b in branches)) | 1436 alignment = max(5, max(len(b) for b in branches)) |
1389 print 'Branches associated with reviews:' | 1437 print 'Branches associated with reviews:' |
1390 # Adhoc thread pool to request data concurrently. | 1438 output = get_cl_statuses(branches, |
1391 output = Queue.Queue() | 1439 fine_grained=not options.fast, |
1392 | 1440 max_processes=options.maxjobs) |
1393 # Silence upload.py otherwise it becomes unweldly. | |
1394 upload.verbosity = 0 | |
1395 | |
1396 if not options.fast: | |
1397 def fetch(b): | |
1398 """Fetches information for an issue and returns (branch, issue, color).""" | |
1399 c = Changelist(branchref=b) | |
1400 i = c.GetIssueURL() | |
1401 status = c.GetStatus() | |
1402 color = color_for_status(status) | |
1403 | |
1404 if i and (not status or status == 'error'): | |
1405 # The issue probably doesn't exist anymore. | |
1406 i += ' (broken)' | |
1407 | |
1408 output.put((b, i, color)) | |
1409 | |
1410 # Process one branch synchronously to work through authentication, then | |
1411 # spawn threads to process all the other branches in parallel. | |
1412 if branches: | |
1413 fetch(branches[0]) | |
1414 threads = [ | |
1415 threading.Thread(target=fetch, args=(b,)) for b in branches[1:]] | |
1416 for t in threads: | |
1417 t.daemon = True | |
1418 t.start() | |
1419 else: | |
1420 # Do not use GetApprovingReviewers(), since it requires an HTTP request. | |
1421 for b in branches: | |
1422 c = Changelist(branchref=b) | |
1423 url = c.GetIssueURL() | |
1424 output.put((b, url, Fore.BLUE if url else Fore.WHITE)) | |
1425 | 1441 |
1426 tmp = {} | 1442 tmp = {} |
iannucci
2015/03/11 20:18:46
can we rename this to e.g. branch_status or someth
calamity
2015/03/11 22:28:27
Done.
| |
1427 alignment = max(5, max(len(ShortBranchName(b)) for b in branches)) | 1443 alignment = max(5, max(len(ShortBranchName(b)) for b in branches)) |
1428 for branch in sorted(branches): | 1444 for branch in sorted(branches): |
1429 while branch not in tmp: | 1445 while branch not in tmp: |
1430 b, i, color = output.get() | 1446 b, i, color = output.next() |
1431 tmp[b] = (i, color) | 1447 tmp[b] = (i, color) |
1432 issue, color = tmp.pop(branch) | 1448 issue, color = tmp.pop(branch) |
1433 reset = Fore.RESET | 1449 reset = Fore.RESET |
1434 if not sys.stdout.isatty(): | 1450 if not sys.stdout.isatty(): |
1435 color = '' | 1451 color = '' |
1436 reset = '' | 1452 reset = '' |
1437 print ' %*s : %s%s%s' % ( | 1453 print ' %*s : %s%s%s' % ( |
1438 alignment, ShortBranchName(branch), color, issue, reset) | 1454 alignment, ShortBranchName(branch), color, issue, reset) |
1439 | 1455 |
1440 cl = Changelist() | 1456 cl = Changelist() |
(...skipping 1653 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
3094 if __name__ == '__main__': | 3110 if __name__ == '__main__': |
3095 # These affect sys.stdout so do it outside of main() to simplify mocks in | 3111 # These affect sys.stdout so do it outside of main() to simplify mocks in |
3096 # unit testing. | 3112 # unit testing. |
3097 fix_encoding.fix_encoding() | 3113 fix_encoding.fix_encoding() |
3098 colorama.init() | 3114 colorama.init() |
3099 try: | 3115 try: |
3100 sys.exit(main(sys.argv[1:])) | 3116 sys.exit(main(sys.argv[1:])) |
3101 except KeyboardInterrupt: | 3117 except KeyboardInterrupt: |
3102 sys.stderr.write('interrupted\n') | 3118 sys.stderr.write('interrupted\n') |
3103 sys.exit(1) | 3119 sys.exit(1) |
OLD | NEW |