Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(57)

Side by Side Diff: git_cl.py

Issue 1893563002: Use CLs more consistently instead of branch names (Closed) Base URL: https://chromium.googlesource.com/chromium/tools/depot_tools.git@master
Patch Set: fix Created 4 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | git_map_branches.py » ('j') | git_map_branches.py » ('J')
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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 and Gerrit.""" 8 """A git-command for integrating reviews on Rietveld and Gerrit."""
9 9
10 from distutils.version import LooseVersion 10 from distutils.version import LooseVersion
11 from multiprocessing.pool import ThreadPool 11 from multiprocessing.pool import ThreadPool
12 import base64 12 import base64
13 import collections 13 import collections
14 import glob 14 import glob
15 import httplib 15 import httplib
16 import json 16 import json
17 import logging 17 import logging
18 import multiprocessing 18 import multiprocessing
19 import optparse 19 import optparse
20 import os 20 import os
21 import Queue
22 import re 21 import re
23 import stat 22 import stat
24 import sys 23 import sys
25 import tempfile
26 import textwrap 24 import textwrap
27 import time 25 import time
28 import traceback 26 import traceback
29 import urllib 27 import urllib
30 import urllib2 28 import urllib2
31 import urlparse 29 import urlparse
32 import uuid 30 import uuid
33 import webbrowser 31 import webbrowser
34 import zlib 32 import zlib
35 33
(...skipping 89 matching lines...) Expand 10 before | Expand all | Expand 10 after
125 stdout=subprocess2.PIPE, 123 stdout=subprocess2.PIPE,
126 stderr=stderr) 124 stderr=stderr)
127 return code, out[0] 125 return code, out[0]
128 except ValueError: 126 except ValueError:
129 # When the subprocess fails, it returns None. That triggers a ValueError 127 # When the subprocess fails, it returns None. That triggers a ValueError
130 # when trying to unpack the return value into (out, code). 128 # when trying to unpack the return value into (out, code).
131 return 1, '' 129 return 1, ''
132 130
133 131
134 def RunGitSilent(args): 132 def RunGitSilent(args):
135 """Returns stdout, suppresses stderr and ingores the return code.""" 133 """Returns stdout, suppresses stderr and ignores the return code."""
136 return RunGitWithCode(args, suppress_stderr=True)[1] 134 return RunGitWithCode(args, suppress_stderr=True)[1]
137 135
138 136
139 def IsGitVersionAtLeast(min_version): 137 def IsGitVersionAtLeast(min_version):
140 prefix = 'git version ' 138 prefix = 'git version '
141 version = RunGit(['--version']).strip() 139 version = RunGit(['--version']).strip()
142 return (version.startswith(prefix) and 140 return (version.startswith(prefix) and
143 LooseVersion(version[len(prefix):]) >= LooseVersion(min_version)) 141 LooseVersion(version[len(prefix):]) >= LooseVersion(min_version))
144 142
145 143
(...skipping 776 matching lines...) Expand 10 before | Expand all | Expand 10 after
922 global settings 920 global settings
923 if not settings: 921 if not settings:
924 # Happens when git_cl.py is used as a utility library. 922 # Happens when git_cl.py is used as a utility library.
925 settings = Settings() 923 settings = Settings()
926 924
927 if issue: 925 if issue:
928 assert codereview, 'codereview must be known, if issue is known' 926 assert codereview, 'codereview must be known, if issue is known'
929 927
930 self.branchref = branchref 928 self.branchref = branchref
931 if self.branchref: 929 if self.branchref:
930 assert branchref.startswith('refs/heads/')
932 self.branch = ShortBranchName(self.branchref) 931 self.branch = ShortBranchName(self.branchref)
933 else: 932 else:
934 self.branch = None 933 self.branch = None
935 self.upstream_branch = None 934 self.upstream_branch = None
936 self.lookedup_issue = False 935 self.lookedup_issue = False
937 self.issue = issue or None 936 self.issue = issue or None
938 self.has_description = False 937 self.has_description = False
939 self.description = None 938 self.description = None
940 self.lookedup_patchset = False 939 self.lookedup_patchset = False
941 self.patchset = None 940 self.patchset = None
(...skipping 500 matching lines...) Expand 10 before | Expand all | Expand 10 after
1442 1441
1443 def GetApprovingReviewers(self): 1442 def GetApprovingReviewers(self):
1444 return self._codereview_impl.GetApprovingReviewers() 1443 return self._codereview_impl.GetApprovingReviewers()
1445 1444
1446 def GetMostRecentPatchset(self): 1445 def GetMostRecentPatchset(self):
1447 return self._codereview_impl.GetMostRecentPatchset() 1446 return self._codereview_impl.GetMostRecentPatchset()
1448 1447
1449 def __getattr__(self, attr): 1448 def __getattr__(self, attr):
1450 # This is because lots of untested code accesses Rietveld-specific stuff 1449 # This is because lots of untested code accesses Rietveld-specific stuff
1451 # directly, and it's hard to fix for sure. So, just let it work, and fix 1450 # directly, and it's hard to fix for sure. So, just let it work, and fix
1452 # on a cases by case basis. 1451 # on a case by case basis.
1453 return getattr(self._codereview_impl, attr) 1452 return getattr(self._codereview_impl, attr)
1454 1453
1455 1454
1456 class _ChangelistCodereviewBase(object): 1455 class _ChangelistCodereviewBase(object):
1457 """Abstract base class encapsulating codereview specifics of a changelist.""" 1456 """Abstract base class encapsulating codereview specifics of a changelist."""
1458 def __init__(self, changelist): 1457 def __init__(self, changelist):
1459 self._changelist = changelist # instance of Changelist 1458 self._changelist = changelist # instance of Changelist
1460 1459
1461 def __getattr__(self, attr): 1460 def __getattr__(self, attr):
1462 # Forward methods to changelist. 1461 # Forward methods to changelist.
(...skipping 465 matching lines...) Expand 10 before | Expand all | Expand 10 after
1928 target_ref = GetTargetRef(remote, remote_branch, options.target_branch, 1927 target_ref = GetTargetRef(remote, remote_branch, options.target_branch,
1929 settings.GetPendingRefPrefix()) 1928 settings.GetPendingRefPrefix())
1930 if target_ref: 1929 if target_ref:
1931 upload_args.extend(['--target_ref', target_ref]) 1930 upload_args.extend(['--target_ref', target_ref])
1932 1931
1933 # Look for dependent patchsets. See crbug.com/480453 for more details. 1932 # Look for dependent patchsets. See crbug.com/480453 for more details.
1934 remote, upstream_branch = self.FetchUpstreamTuple(self.GetBranch()) 1933 remote, upstream_branch = self.FetchUpstreamTuple(self.GetBranch())
1935 upstream_branch = ShortBranchName(upstream_branch) 1934 upstream_branch = ShortBranchName(upstream_branch)
1936 if remote is '.': 1935 if remote is '.':
1937 # A local branch is being tracked. 1936 # A local branch is being tracked.
1938 local_branch = ShortBranchName(upstream_branch) 1937 local_branch = upstream_branch
1939 if settings.GetIsSkipDependencyUpload(local_branch): 1938 if settings.GetIsSkipDependencyUpload(local_branch):
1940 print 1939 print
1941 print ('Skipping dependency patchset upload because git config ' 1940 print ('Skipping dependency patchset upload because git config '
1942 'branch.%s.skip-deps-uploads is set to True.' % local_branch) 1941 'branch.%s.skip-deps-uploads is set to True.' % local_branch)
1943 print 1942 print
1944 else: 1943 else:
1945 auth_config = auth.extract_auth_config_from_options(options) 1944 auth_config = auth.extract_auth_config_from_options(options)
1946 branch_cl = Changelist(branchref=local_branch, 1945 branch_cl = Changelist(branchref='refs/heads/'+local_branch,
1947 auth_config=auth_config) 1946 auth_config=auth_config)
1948 branch_cl_issue_url = branch_cl.GetIssueURL() 1947 branch_cl_issue_url = branch_cl.GetIssueURL()
1949 branch_cl_issue = branch_cl.GetIssue() 1948 branch_cl_issue = branch_cl.GetIssue()
1950 branch_cl_patchset = branch_cl.GetPatchset() 1949 branch_cl_patchset = branch_cl.GetPatchset()
1951 if branch_cl_issue_url and branch_cl_issue and branch_cl_patchset: 1950 if branch_cl_issue_url and branch_cl_issue and branch_cl_patchset:
1952 upload_args.extend( 1951 upload_args.extend(
1953 ['--depends_on_patchset', '%s:%s' % ( 1952 ['--depends_on_patchset', '%s:%s' % (
1954 branch_cl_issue, branch_cl_patchset)]) 1953 branch_cl_issue, branch_cl_patchset)])
1955 print( 1954 print(
1956 '\n' 1955 '\n'
(...skipping 955 matching lines...) Expand 10 before | Expand all | Expand 10 after
2912 return { 2911 return {
2913 'unsent': Fore.RED, 2912 'unsent': Fore.RED,
2914 'waiting': Fore.BLUE, 2913 'waiting': Fore.BLUE,
2915 'reply': Fore.YELLOW, 2914 'reply': Fore.YELLOW,
2916 'lgtm': Fore.GREEN, 2915 'lgtm': Fore.GREEN,
2917 'commit': Fore.MAGENTA, 2916 'commit': Fore.MAGENTA,
2918 'closed': Fore.CYAN, 2917 'closed': Fore.CYAN,
2919 'error': Fore.WHITE, 2918 'error': Fore.WHITE,
2920 }.get(status, Fore.WHITE) 2919 }.get(status, Fore.WHITE)
2921 2920
2922 def fetch_cl_status(branch, auth_config=None):
2923 """Fetches information for an issue and returns (branch, issue, status)."""
2924 cl = Changelist(branchref=branch, auth_config=auth_config)
2925 url = cl.GetIssueURL()
2926 status = cl.GetStatus()
2927
2928 if url and (not status or status == 'error'):
2929 # The issue probably doesn't exist anymore.
2930 url += ' (broken)'
2931
2932 return (branch, url, status)
2933 2921
2934 def get_cl_statuses( 2922 def get_cl_statuses(
tandrii(chromium) 2016/05/20 20:56:06 nit: merge two lines.
2935 branches, fine_grained, max_processes=None, auth_config=None): 2923 changes, fine_grained, max_processes=None):
2936 """Returns a blocking iterable of (branch, issue, status) for given branches. 2924 """Returns a blocking iterable of (cl, status) for given branches.
2937 2925
2938 If fine_grained is true, this will fetch CL statuses from the server. 2926 If fine_grained is true, this will fetch CL statuses from the server.
2939 Otherwise, simply indicate if there's a matching url for the given branches. 2927 Otherwise, simply indicate if there's a matching url for the given branches.
2940 2928
2941 If max_processes is specified, it is used as the maximum number of processes 2929 If max_processes is specified, it is used as the maximum number of processes
2942 to spawn to fetch CL status from the server. Otherwise 1 process per branch is 2930 to spawn to fetch CL status from the server. Otherwise 1 process per branch is
2943 spawned. 2931 spawned.
2944 2932
2945 See GetStatus() for a list of possible statuses. 2933 See GetStatus() for a list of possible statuses.
2946 """ 2934 """
2947 def fetch(branch):
2948 if not branch:
2949 return None
2950
2951 return fetch_cl_status(branch, auth_config=auth_config)
2952
2953 # Silence upload.py otherwise it becomes unwieldly. 2935 # Silence upload.py otherwise it becomes unwieldly.
2954 upload.verbosity = 0 2936 upload.verbosity = 0
2955 2937
2956 if fine_grained: 2938 if fine_grained:
2957 # Process one branch synchronously to work through authentication, then 2939 # Process one branch synchronously to work through authentication, then
2958 # spawn processes to process all the other branches in parallel. 2940 # spawn processes to process all the other branches in parallel.
2959 if branches: 2941 if changes:
2942 fetch = lambda cl: (cl, cl.GetStatus())
2943 yield fetch(changes[0])
2960 2944
2961 yield fetch(branches[0]) 2945 changes_to_fetch = changes[1:]
2946 pool = ThreadPool(
2947 min(max_processes, len(changes_to_fetch))
2948 if max_processes is not None
2949 else len(changes_to_fetch))
2962 2950
2963 branches_to_fetch = branches[1:] 2951 fetched_cls = set()
2964 pool = ThreadPool( 2952 it = pool.imap_unordered(fetch, changes_to_fetch).__iter__()
2965 min(max_processes, len(branches_to_fetch))
2966 if max_processes is not None
2967 else len(branches_to_fetch))
2968
2969 fetched_branches = set()
2970 it = pool.imap_unordered(fetch, branches_to_fetch).__iter__()
2971 while True: 2953 while True:
2972 try: 2954 try:
2973 row = it.next(timeout=5) 2955 row = it.next(timeout=5)
2974 except multiprocessing.TimeoutError: 2956 except multiprocessing.TimeoutError:
2975 break 2957 break
2976 2958
2977 fetched_branches.add(row[0]) 2959 fetched_cls.add(row[0])
2978 yield row 2960 yield row
2979 2961
2980 # Add any branches that failed to fetch. 2962 # Add any branches that failed to fetch.
2981 for b in set(branches_to_fetch) - fetched_branches: 2963 for cl in set(changes_to_fetch) - fetched_cls:
2982 cl = Changelist(branchref=b, auth_config=auth_config) 2964 yield (cl, 'error')
2983 yield (b, cl.GetIssueURL() if b else None, 'error')
2984 2965
2985 else: 2966 else:
2986 # Do not use GetApprovingReviewers(), since it requires an HTTP request. 2967 # Do not use GetApprovingReviewers(), since it requires an HTTP request.
2987 for b in branches: 2968 for cl in changes:
2988 cl = Changelist(branchref=b, auth_config=auth_config) 2969 yield (cl, 'waiting' if cl.GetIssueURL() else 'error')
2989 url = cl.GetIssueURL() if b else None
2990 yield (b, url, 'waiting' if url else 'error')
2991 2970
2992 2971
2993 def upload_branch_deps(cl, args): 2972 def upload_branch_deps(cl, args):
2994 """Uploads CLs of local branches that are dependents of the current branch. 2973 """Uploads CLs of local branches that are dependents of the current branch.
2995 2974
2996 If the local branch dependency tree looks like: 2975 If the local branch dependency tree looks like:
2997 test1 -> test2.1 -> test3.1 2976 test1 -> test2.1 -> test3.1
2998 -> test3.2 2977 -> test3.2
2999 -> test2.2 -> test3.3 2978 -> test2.2 -> test3.3
3000 2979
(...skipping 132 matching lines...) Expand 10 before | Expand all | Expand 10 after
3133 url = cl.GetIssueURL() 3112 url = cl.GetIssueURL()
3134 if url: 3113 if url:
3135 print url 3114 print url
3136 return 0 3115 return 0
3137 3116
3138 branches = RunGit(['for-each-ref', '--format=%(refname)', 'refs/heads']) 3117 branches = RunGit(['for-each-ref', '--format=%(refname)', 'refs/heads'])
3139 if not branches: 3118 if not branches:
3140 print('No local branch found.') 3119 print('No local branch found.')
3141 return 0 3120 return 0
3142 3121
3143 changes = ( 3122 changes = [
3144 Changelist(branchref=b, auth_config=auth_config) 3123 Changelist(branchref=b, auth_config=auth_config)
3145 for b in branches.splitlines()) 3124 for b in branches.splitlines()]
3146 # TODO(tandrii): refactor to use CLs list instead of branches list.
3147 branches = [c.GetBranch() for c in changes]
3148 alignment = max(5, max(len(b) for b in branches))
3149 print 'Branches associated with reviews:' 3125 print 'Branches associated with reviews:'
3150 output = get_cl_statuses(branches, 3126 output = get_cl_statuses(changes,
3151 fine_grained=not options.fast, 3127 fine_grained=not options.fast,
3152 max_processes=options.maxjobs, 3128 max_processes=options.maxjobs)
3153 auth_config=auth_config)
3154 3129
3155 branch_statuses = {} 3130 branch_statuses = {}
3156 alignment = max(5, max(len(ShortBranchName(b)) for b in branches)) 3131 alignment = max(5, max(len(ShortBranchName(c.GetBranch())) for c in changes))
3157 for branch in sorted(branches): 3132 for cl in sorted(changes, key=lambda c: c.GetBranch()):
3133 branch = cl.GetBranch()
3158 while branch not in branch_statuses: 3134 while branch not in branch_statuses:
3159 b, i, status = output.next() 3135 c, status = output.next()
3160 branch_statuses[b] = (i, status) 3136 branch_statuses[c.GetBranch()] = status
3161 issue_url, status = branch_statuses.pop(branch) 3137 status = branch_statuses.pop(branch)
3138 url = cl.GetIssueURL()
3139 if url and (not status or status == 'error'):
3140 # The issue probably doesn't exist anymore.
3141 url += ' (broken)'
3142
3162 color = color_for_status(status) 3143 color = color_for_status(status)
3163 reset = Fore.RESET 3144 reset = Fore.RESET
3164 if not setup_color.IS_TTY: 3145 if not setup_color.IS_TTY:
3165 color = '' 3146 color = ''
3166 reset = '' 3147 reset = ''
3167 status_str = '(%s)' % status if status else '' 3148 status_str = '(%s)' % status if status else ''
3168 print ' %*s : %s%s %s%s' % ( 3149 print ' %*s : %s%s %s%s' % (
3169 alignment, ShortBranchName(branch), color, issue_url, status_str, 3150 alignment, ShortBranchName(branch), color, url,
3170 reset) 3151 status_str, reset)
3171 3152
3172 cl = Changelist(auth_config=auth_config) 3153 cl = Changelist(auth_config=auth_config)
3173 print 3154 print
3174 print 'Current branch:', 3155 print 'Current branch:',
3175 print cl.GetBranch() 3156 print cl.GetBranch()
3176 if not cl.GetIssue(): 3157 if not cl.GetIssue():
3177 print 'No issue assigned.' 3158 print 'No issue assigned.'
3178 return 0 3159 return 0
3179 print 'Issue number: %s (%s)' % (cl.GetIssue(), cl.GetIssueURL()) 3160 print 'Issue number: %s (%s)' % (cl.GetIssue(), cl.GetIssueURL())
3180 if not options.fast: 3161 if not options.fast:
(...skipping 1720 matching lines...) Expand 10 before | Expand all | Expand 10 after
4901 if __name__ == '__main__': 4882 if __name__ == '__main__':
4902 # These affect sys.stdout so do it outside of main() to simplify mocks in 4883 # These affect sys.stdout so do it outside of main() to simplify mocks in
4903 # unit testing. 4884 # unit testing.
4904 fix_encoding.fix_encoding() 4885 fix_encoding.fix_encoding()
4905 setup_color.init() 4886 setup_color.init()
4906 try: 4887 try:
4907 sys.exit(main(sys.argv[1:])) 4888 sys.exit(main(sys.argv[1:]))
4908 except KeyboardInterrupt: 4889 except KeyboardInterrupt:
4909 sys.stderr.write('interrupted\n') 4890 sys.stderr.write('interrupted\n')
4910 sys.exit(1) 4891 sys.exit(1)
OLDNEW
« no previous file with comments | « no previous file | git_map_branches.py » ('j') | git_map_branches.py » ('J')

Powered by Google App Engine
This is Rietveld 408576698