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

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: last changes Created 4 years, 6 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') | no next file with comments »
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 961 matching lines...) Expand 10 before | Expand all | Expand 10 after
2918 return { 2917 return {
2919 'unsent': Fore.RED, 2918 'unsent': Fore.RED,
2920 'waiting': Fore.BLUE, 2919 'waiting': Fore.BLUE,
2921 'reply': Fore.YELLOW, 2920 'reply': Fore.YELLOW,
2922 'lgtm': Fore.GREEN, 2921 'lgtm': Fore.GREEN,
2923 'commit': Fore.MAGENTA, 2922 'commit': Fore.MAGENTA,
2924 'closed': Fore.CYAN, 2923 'closed': Fore.CYAN,
2925 'error': Fore.WHITE, 2924 'error': Fore.WHITE,
2926 }.get(status, Fore.WHITE) 2925 }.get(status, Fore.WHITE)
2927 2926
2928 def fetch_cl_status(branch, auth_config=None):
2929 """Fetches information for an issue and returns (branch, issue, status)."""
2930 cl = Changelist(branchref=branch, auth_config=auth_config)
2931 url = cl.GetIssueURL()
2932 status = cl.GetStatus()
2933 2927
2934 if url and (not status or status == 'error'): 2928 def get_cl_statuses(changes, fine_grained, max_processes=None):
2935 # The issue probably doesn't exist anymore. 2929 """Returns a blocking iterable of (cl, status) for given branches.
2936 url += ' (broken)'
2937
2938 return (branch, url, status)
2939
2940 def get_cl_statuses(
2941 branches, fine_grained, max_processes=None, auth_config=None):
2942 """Returns a blocking iterable of (branch, issue, status) for given branches.
2943 2930
2944 If fine_grained is true, this will fetch CL statuses from the server. 2931 If fine_grained is true, this will fetch CL statuses from the server.
2945 Otherwise, simply indicate if there's a matching url for the given branches. 2932 Otherwise, simply indicate if there's a matching url for the given branches.
2946 2933
2947 If max_processes is specified, it is used as the maximum number of processes 2934 If max_processes is specified, it is used as the maximum number of processes
2948 to spawn to fetch CL status from the server. Otherwise 1 process per branch is 2935 to spawn to fetch CL status from the server. Otherwise 1 process per branch is
2949 spawned. 2936 spawned.
2950 2937
2951 See GetStatus() for a list of possible statuses. 2938 See GetStatus() for a list of possible statuses.
2952 """ 2939 """
2953 def fetch(branch):
2954 if not branch:
2955 return None
2956
2957 return fetch_cl_status(branch, auth_config=auth_config)
2958
2959 # Silence upload.py otherwise it becomes unwieldly. 2940 # Silence upload.py otherwise it becomes unwieldly.
2960 upload.verbosity = 0 2941 upload.verbosity = 0
2961 2942
2962 if fine_grained: 2943 if fine_grained:
2963 # Process one branch synchronously to work through authentication, then 2944 # Process one branch synchronously to work through authentication, then
2964 # spawn processes to process all the other branches in parallel. 2945 # spawn processes to process all the other branches in parallel.
2965 if branches: 2946 if changes:
2947 fetch = lambda cl: (cl, cl.GetStatus())
2948 yield fetch(changes[0])
2966 2949
2967 yield fetch(branches[0]) 2950 changes_to_fetch = changes[1:]
2951 pool = ThreadPool(
2952 min(max_processes, len(changes_to_fetch))
2953 if max_processes is not None
2954 else len(changes_to_fetch))
2968 2955
2969 branches_to_fetch = branches[1:] 2956 fetched_cls = set()
2970 pool = ThreadPool( 2957 it = pool.imap_unordered(fetch, changes_to_fetch).__iter__()
2971 min(max_processes, len(branches_to_fetch))
2972 if max_processes is not None
2973 else len(branches_to_fetch))
2974
2975 fetched_branches = set()
2976 it = pool.imap_unordered(fetch, branches_to_fetch).__iter__()
2977 while True: 2958 while True:
2978 try: 2959 try:
2979 row = it.next(timeout=5) 2960 row = it.next(timeout=5)
2980 except multiprocessing.TimeoutError: 2961 except multiprocessing.TimeoutError:
2981 break 2962 break
2982 2963
2983 fetched_branches.add(row[0]) 2964 fetched_cls.add(row[0])
2984 yield row 2965 yield row
2985 2966
2986 # Add any branches that failed to fetch. 2967 # Add any branches that failed to fetch.
2987 for b in set(branches_to_fetch) - fetched_branches: 2968 for cl in set(changes_to_fetch) - fetched_cls:
2988 cl = Changelist(branchref=b, auth_config=auth_config) 2969 yield (cl, 'error')
2989 yield (b, cl.GetIssueURL() if b else None, 'error')
2990 2970
2991 else: 2971 else:
2992 # Do not use GetApprovingReviewers(), since it requires an HTTP request. 2972 # Do not use GetApprovingReviewers(), since it requires an HTTP request.
2993 for b in branches: 2973 for cl in changes:
2994 cl = Changelist(branchref=b, auth_config=auth_config) 2974 yield (cl, 'waiting' if cl.GetIssueURL() else 'error')
2995 url = cl.GetIssueURL() if b else None
2996 yield (b, url, 'waiting' if url else 'error')
2997 2975
2998 2976
2999 def upload_branch_deps(cl, args): 2977 def upload_branch_deps(cl, args):
3000 """Uploads CLs of local branches that are dependents of the current branch. 2978 """Uploads CLs of local branches that are dependents of the current branch.
3001 2979
3002 If the local branch dependency tree looks like: 2980 If the local branch dependency tree looks like:
3003 test1 -> test2.1 -> test3.1 2981 test1 -> test2.1 -> test3.1
3004 -> test3.2 2982 -> test3.2
3005 -> test2.2 -> test3.3 2983 -> test2.2 -> test3.3
3006 2984
(...skipping 132 matching lines...) Expand 10 before | Expand all | Expand 10 after
3139 url = cl.GetIssueURL() 3117 url = cl.GetIssueURL()
3140 if url: 3118 if url:
3141 print url 3119 print url
3142 return 0 3120 return 0
3143 3121
3144 branches = RunGit(['for-each-ref', '--format=%(refname)', 'refs/heads']) 3122 branches = RunGit(['for-each-ref', '--format=%(refname)', 'refs/heads'])
3145 if not branches: 3123 if not branches:
3146 print('No local branch found.') 3124 print('No local branch found.')
3147 return 0 3125 return 0
3148 3126
3149 changes = ( 3127 changes = [
3150 Changelist(branchref=b, auth_config=auth_config) 3128 Changelist(branchref=b, auth_config=auth_config)
3151 for b in branches.splitlines()) 3129 for b in branches.splitlines()]
3152 # TODO(tandrii): refactor to use CLs list instead of branches list.
3153 branches = [c.GetBranch() for c in changes]
3154 alignment = max(5, max(len(b) for b in branches))
3155 print 'Branches associated with reviews:' 3130 print 'Branches associated with reviews:'
3156 output = get_cl_statuses(branches, 3131 output = get_cl_statuses(changes,
3157 fine_grained=not options.fast, 3132 fine_grained=not options.fast,
3158 max_processes=options.maxjobs, 3133 max_processes=options.maxjobs)
3159 auth_config=auth_config)
3160 3134
3161 branch_statuses = {} 3135 branch_statuses = {}
3162 alignment = max(5, max(len(ShortBranchName(b)) for b in branches)) 3136 alignment = max(5, max(len(ShortBranchName(c.GetBranch())) for c in changes))
3163 for branch in sorted(branches): 3137 for cl in sorted(changes, key=lambda c: c.GetBranch()):
3138 branch = cl.GetBranch()
3164 while branch not in branch_statuses: 3139 while branch not in branch_statuses:
3165 b, i, status = output.next() 3140 c, status = output.next()
3166 branch_statuses[b] = (i, status) 3141 branch_statuses[c.GetBranch()] = status
3167 issue_url, status = branch_statuses.pop(branch) 3142 status = branch_statuses.pop(branch)
3143 url = cl.GetIssueURL()
3144 if url and (not status or status == 'error'):
3145 # The issue probably doesn't exist anymore.
3146 url += ' (broken)'
3147
3168 color = color_for_status(status) 3148 color = color_for_status(status)
3169 reset = Fore.RESET 3149 reset = Fore.RESET
3170 if not setup_color.IS_TTY: 3150 if not setup_color.IS_TTY:
3171 color = '' 3151 color = ''
3172 reset = '' 3152 reset = ''
3173 status_str = '(%s)' % status if status else '' 3153 status_str = '(%s)' % status if status else ''
3174 print ' %*s : %s%s %s%s' % ( 3154 print ' %*s : %s%s %s%s' % (
3175 alignment, ShortBranchName(branch), color, issue_url, status_str, 3155 alignment, ShortBranchName(branch), color, url,
3176 reset) 3156 status_str, reset)
3177 3157
3178 cl = Changelist(auth_config=auth_config) 3158 cl = Changelist(auth_config=auth_config)
3179 print 3159 print
3180 print 'Current branch:', 3160 print 'Current branch:',
3181 print cl.GetBranch() 3161 print cl.GetBranch()
3182 if not cl.GetIssue(): 3162 if not cl.GetIssue():
3183 print 'No issue assigned.' 3163 print 'No issue assigned.'
3184 return 0 3164 return 0
3185 print 'Issue number: %s (%s)' % (cl.GetIssue(), cl.GetIssueURL()) 3165 print 'Issue number: %s (%s)' % (cl.GetIssue(), cl.GetIssueURL())
3186 if not options.fast: 3166 if not options.fast:
(...skipping 1720 matching lines...) Expand 10 before | Expand all | Expand 10 after
4907 if __name__ == '__main__': 4887 if __name__ == '__main__':
4908 # These affect sys.stdout so do it outside of main() to simplify mocks in 4888 # These affect sys.stdout so do it outside of main() to simplify mocks in
4909 # unit testing. 4889 # unit testing.
4910 fix_encoding.fix_encoding() 4890 fix_encoding.fix_encoding()
4911 setup_color.init() 4891 setup_color.init()
4912 try: 4892 try:
4913 sys.exit(main(sys.argv[1:])) 4893 sys.exit(main(sys.argv[1:]))
4914 except KeyboardInterrupt: 4894 except KeyboardInterrupt:
4915 sys.stderr.write('interrupted\n') 4895 sys.stderr.write('interrupted\n')
4916 sys.exit(1) 4896 sys.exit(1)
OLDNEW
« no previous file with comments | « no previous file | git_map_branches.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698