Make git-map-branches -vvv show CL status colors.
This CL makes git-map-branches show CL status colors like git cl status
when -vvv is used. Statuses are fetched in parallel for speed.
BUG=379849
Review URL: https://codereview.chromium.org/938583002
git-svn-id: svn://svn.chromium.org/chrome/trunk/tools/depot_tools@294414 0039d316-1c4b-4281-b951-d872f2087c98
diff --git a/git_cl.py b/git_cl.py
index d838b9d..039b35d 100755
--- a/git_cl.py
+++ b/git_cl.py
@@ -8,6 +8,7 @@
"""A git-command for integrating reviews on Rietveld."""
from distutils.version import LooseVersion
+from multiprocessing.pool import ThreadPool
import base64
import glob
import json
@@ -20,7 +21,6 @@
import sys
import tempfile
import textwrap
-import threading
import urllib2
import urlparse
import webbrowser
@@ -1342,6 +1342,51 @@
'error': Fore.WHITE,
}.get(status, Fore.WHITE)
+def fetch_cl_status(b):
+ """Fetches information for an issue and returns (branch, issue, color)."""
+ c = Changelist(branchref=b)
+ i = c.GetIssueURL()
+ status = c.GetStatus()
+ color = color_for_status(status)
+
+ if i and (not status or status == 'error'):
+ # The issue probably doesn't exist anymore.
+ i += ' (broken)'
+
+ return (b, i, color)
+
+def get_cl_statuses(branches, fine_grained, max_processes=None):
+ """Returns a blocking iterable of (branch, issue, color) for given branches.
+
+ If fine_grained is true, this will fetch CL statuses from the server.
+ Otherwise, simply indicate if there's a matching url for the given branches.
+
+ If max_processes is specified, it is used as the maximum number of processes
+ to spawn to fetch CL status from the server. Otherwise 1 process per branch is
+ spawned.
+ """
+ # Silence upload.py otherwise it becomes unwieldly.
+ upload.verbosity = 0
+
+ if fine_grained:
+ # Process one branch synchronously to work through authentication, then
+ # spawn processes to process all the other branches in parallel.
+ if branches:
+ yield fetch_cl_status(branches[0])
+
+ branches_to_fetch = branches[1:]
+ pool = ThreadPool(
+ min(max_processes, len(branches_to_fetch))
+ if max_processes is not None
+ else len(branches_to_fetch))
+ for x in pool.imap_unordered(fetch_cl_status, branches_to_fetch):
+ yield x
+ else:
+ # Do not use GetApprovingReviewers(), since it requires an HTTP request.
+ for b in branches:
+ c = Changelist(branchref=b)
+ url = c.GetIssueURL()
+ yield (b, url, Fore.BLUE if url else Fore.WHITE)
def CMDstatus(parser, args):
"""Show status of changelists.
@@ -1360,6 +1405,9 @@
help='print only specific field (desc|id|patch|url)')
parser.add_option('-f', '--fast', action='store_true',
help='Do not retrieve review status')
+ parser.add_option(
+ '-j', '--maxjobs', action='store', type=int,
+ help='The maximum number of jobs to use when retrieving review status')
(options, args) = parser.parse_args(args)
if args:
parser.error('Unsupported args: %s' % args)
@@ -1391,49 +1439,17 @@
branches = [c.GetBranch() for c in changes]
alignment = max(5, max(len(b) for b in branches))
print 'Branches associated with reviews:'
- # Adhoc thread pool to request data concurrently.
- output = Queue.Queue()
+ output = get_cl_statuses(branches,
+ fine_grained=not options.fast,
+ max_processes=options.maxjobs)
- # Silence upload.py otherwise it becomes unweldly.
- upload.verbosity = 0
-
- if not options.fast:
- def fetch(b):
- """Fetches information for an issue and returns (branch, issue, color)."""
- c = Changelist(branchref=b)
- i = c.GetIssueURL()
- status = c.GetStatus()
- color = color_for_status(status)
-
- if i and (not status or status == 'error'):
- # The issue probably doesn't exist anymore.
- i += ' (broken)'
-
- output.put((b, i, color))
-
- # Process one branch synchronously to work through authentication, then
- # spawn threads to process all the other branches in parallel.
- if branches:
- fetch(branches[0])
- threads = [
- threading.Thread(target=fetch, args=(b,)) for b in branches[1:]]
- for t in threads:
- t.daemon = True
- t.start()
- else:
- # Do not use GetApprovingReviewers(), since it requires an HTTP request.
- for b in branches:
- c = Changelist(branchref=b)
- url = c.GetIssueURL()
- output.put((b, url, Fore.BLUE if url else Fore.WHITE))
-
- tmp = {}
+ branch_statuses = {}
alignment = max(5, max(len(ShortBranchName(b)) for b in branches))
for branch in sorted(branches):
- while branch not in tmp:
- b, i, color = output.get()
- tmp[b] = (i, color)
- issue, color = tmp.pop(branch)
+ while branch not in branch_statuses:
+ b, i, color = output.next()
+ branch_statuses[b] = (i, color)
+ issue, color = branch_statuses.pop(branch)
reset = Fore.RESET
if not sys.stdout.isatty():
color = ''
diff --git a/git_map_branches.py b/git_map_branches.py
index 0bf5749..613abc7 100755
--- a/git_map_branches.py
+++ b/git_map_branches.py
@@ -109,6 +109,7 @@
def __init__(self):
self.verbosity = 0
+ self.maxjobs = 0
self.output = OutputManager()
self.__gone_branches = set()
self.__branches_info = None
@@ -116,10 +117,25 @@
self.__current_branch = None
self.__current_hash = None
self.__tag_set = None
+ self.__status_info = {}
def start(self):
self.__branches_info = get_branches_info(
include_tracking_status=self.verbosity >= 1)
+ if (self.verbosity >= 2):
+ # Avoid heavy import unless necessary.
+ from git_cl import get_cl_statuses
+
+ status_info = get_cl_statuses(self.__branches_info.keys(),
+ fine_grained=self.verbosity > 2,
+ max_processes=self.maxjobs)
+
+ for _ in xrange(len(self.__branches_info)):
+ # This is a blocking get which waits for the remote CL status to be
+ # retrieved.
+ (branch, url, color) = status_info.next()
+ self.__status_info[branch] = (url, color);
+
roots = set()
# A map of parents to a list of their children.
@@ -238,11 +254,9 @@
# The Rietveld issue associated with the branch.
if self.verbosity >= 2:
- import git_cl # avoid heavy import cost unless we need it
none_text = '' if self.__is_invalid_parent(branch) else 'None'
- url = git_cl.Changelist(
- branchref=branch).GetIssueURL() if branch_hash else None
- line.append(url or none_text, color=Fore.BLUE if url else Fore.WHITE)
+ (url, color) = self.__status_info[branch]
+ line.append(url or none_text, color=color)
self.output.append(line)
@@ -265,12 +279,16 @@
help='Display branch hash and Rietveld URL')
parser.add_argument('--no-color', action='store_true', dest='nocolor',
help='Turn off colors.')
+ parser.add_argument(
+ '-j', '--maxjobs', action='store', type=int,
+ help='The number of jobs to use when retrieving review status')
opts = parser.parse_args(argv)
mapper = BranchMapper()
mapper.verbosity = opts.v
mapper.output.nocolor = opts.nocolor
+ mapper.maxjobs = opts.maxjobs
mapper.start()
print mapper.output.as_formatted_string()
return 0