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

Side by Side Diff: git_cl.py

Issue 1895463002: Mark outdated branches on 'cl status' (Closed) Base URL: https://chromium.googlesource.com/chromium/tools/depot_tools.git@show-outdated-cls
Patch Set: fix for NO_UPSTREAM 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') | 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
(...skipping 2894 matching lines...) Expand 10 before | Expand all | Expand 10 after
2905 return { 2905 return {
2906 'unsent': Fore.RED, 2906 'unsent': Fore.RED,
2907 'waiting': Fore.BLUE, 2907 'waiting': Fore.BLUE,
2908 'reply': Fore.YELLOW, 2908 'reply': Fore.YELLOW,
2909 'lgtm': Fore.GREEN, 2909 'lgtm': Fore.GREEN,
2910 'commit': Fore.MAGENTA, 2910 'commit': Fore.MAGENTA,
2911 'closed': Fore.CYAN, 2911 'closed': Fore.CYAN,
2912 'error': Fore.WHITE, 2912 'error': Fore.WHITE,
2913 }.get(status, Fore.WHITE) 2913 }.get(status, Fore.WHITE)
2914 2914
2915 def split_diff_files(diff):
2916 """Splits a diff for the individual files and outputs a dict
2917 {file name: diff}.
2918 """
2919 files = {}
2920 current_lines = []
2921 current_file = ''
2922 def append():
2923 if current_file:
2924 files[current_file] = '\n'.join(current_lines+[''])
2925 for line in diff.splitlines():
2926 if line.startswith('Index: '):
2927 continue
2928 if line.startswith('diff '):
2929 append()
2930 current_lines = []
2931 full_file_name = line.split()[-1]
2932 current_file = '/'.join(full_file_name.split('/')[1:])
2933 line = re.sub(' [ab]/'+re.escape(current_file), ' '+current_file, line)
2934 if line.startswith('+++ ') or line.startswith('--- '):
2935 line = re.sub(' [ab]/'+re.escape(current_file), ' '+current_file, line)
2936 current_lines.append(line)
2937 append()
2938 return files
2939
2940 def diff_compare(left, right):
2941 """Compares two diffs without regarding actual file names, line number
2942 information or commit hashes.
2943 Returns True if they are equal, False otherwise.
2944 """
2945 file_pattern = re.compile('^(diff |\+\+\+ |--- ).*$', re.MULTILINE)
2946 line_pattern = re.compile('^@@[ +\-0-9,]+@@', re.MULTILINE)
2947 index_pattern = re.compile('^index [0-9a-f]+\.\.[0-9a-f]+', re.MULTILINE)
2948 left = re.sub(line_pattern, '@@', re.sub(index_pattern, 'index',
2949 re.sub(file_pattern, '\\1', left)))
2950 right = re.sub(line_pattern, '@@', re.sub(index_pattern, 'index',
2951 re.sub(file_pattern, '\\1', right)))
2952 return left == right
2953
2954 def fetch_cl_status(cl, auth_config=None):
2955 """Fetches information for an issue and returns (cl, status, outdated).
2956 """
2957 status = cl.GetStatus()
2958 outdated = False
2959 if cl.GetIssue():
2960 latest_patchset = cl.GetMostRecentPatchset()
2961 uploaded_diff = cl.GetPatchSetDiff(cl.GetIssue(), latest_patchset)
2962 uploaded_file_diffs = split_diff_files(uploaded_diff)
2963 change = cl.GetChange(cl.GetCommonAncestorWithUpstream(), None)
2964 local_file_diffs = {f.LocalPath(): f.GenerateScmDiff()
2965 for f in change.AffectedFiles()}
2966 files = sorted(uploaded_file_diffs.keys())
2967 if local_file_diffs and \
2968 (files != sorted(local_file_diffs.keys()) or \
2969 any(not diff_compare(uploaded_file_diffs[f], local_file_diffs[f])
2970 for f in uploaded_file_diffs.keys())):
2971 outdated = True
2972
2973 return (cl, status, outdated)
2974
2915 def get_cl_statuses( 2975 def get_cl_statuses(
2916 changes, fine_grained, max_processes=None, auth_config=None): 2976 changes, fine_grained, max_processes=None, auth_config=None):
2917 """Returns a blocking iterable of (cl, status) for given branches. 2977 """Returns a blocking iterable of (cl, status, outdated) for given branches.
2918 2978
2919 If fine_grained is true, this will fetch CL statuses from the server. 2979 If fine_grained is true, this will fetch CL statuses from the server.
2920 Otherwise, simply indicate if there's a matching url for the given branches. 2980 Otherwise, simply indicate if there's a matching url for the given branches.
2921 2981
2922 If max_processes is specified, it is used as the maximum number of processes 2982 If max_processes is specified, it is used as the maximum number of processes
2923 to spawn to fetch CL status from the server. Otherwise 1 process per branch is 2983 to spawn to fetch CL status from the server. Otherwise 1 process per branch is
2924 spawned. 2984 spawned.
2925 """ 2985 """
2926 # Silence upload.py otherwise it becomes unwieldly. 2986 # Silence upload.py otherwise it becomes unwieldly.
2927 upload.verbosity = 0 2987 upload.verbosity = 0
2928 2988
2929 change_cls = [Changelist(branchref=c, auth_config=auth_config) 2989 change_cls = [Changelist(branchref=c, auth_config=auth_config)
2930 if type(c) is str else c for c in changes] 2990 if type(c) is str else c for c in changes]
2931 if fine_grained: 2991 if fine_grained:
2932 # Process one branch synchronously to work through authentication, then 2992 # Process one branch synchronously to work through authentication, then
2933 # spawn processes to process all the other branches in parallel. 2993 # spawn processes to process all the other branches in parallel.
2934 if changes: 2994 if changes:
2935 fetch = lambda cl: (cl, cl.GetStatus()) 2995 yield fetch_cl_status(change_cls[0])
2936 yield fetch(change_cls[0])
2937 2996
2938 changes_to_fetch = change_cls[1:] 2997 changes_to_fetch = change_cls[1:]
2939 pool = ThreadPool( 2998 pool = ThreadPool(
2940 min(max_processes, len(changes_to_fetch)) 2999 min(max_processes, len(changes_to_fetch))
2941 if max_processes is not None 3000 if max_processes is not None
2942 else len(changes_to_fetch)) 3001 else len(changes_to_fetch))
2943 for x in pool.imap_unordered(fetch, changes_to_fetch): 3002 for x in pool.imap_unordered(fetch_cl_status, changes_to_fetch):
2944 yield x 3003 yield x
2945 else: 3004 else:
2946 # Do not use GetApprovingReviewers(), since it requires an HTTP request. 3005 # Do not use GetApprovingReviewers(), since it requires an HTTP request.
2947 for cl in change_cls: 3006 for cl in change_cls:
2948 yield (cl, 'waiting' if cl.GetIssueURL() else 'error') 3007 yield (cl, 'waiting' if cl.GetIssueURL() else 'error', False)
2949 3008
2950 3009
2951 def upload_branch_deps(cl, args): 3010 def upload_branch_deps(cl, args):
2952 """Uploads CLs of local branches that are dependents of the current branch. 3011 """Uploads CLs of local branches that are dependents of the current branch.
2953 3012
2954 If the local branch dependency tree looks like: 3013 If the local branch dependency tree looks like:
2955 test1 -> test2.1 -> test3.1 3014 test1 -> test2.1 -> test3.1
2956 -> test3.2 3015 -> test3.2
2957 -> test2.2 -> test3.3 3016 -> test2.2 -> test3.3
2958 3017
(...skipping 145 matching lines...) Expand 10 before | Expand all | Expand 10 after
3104 print 'Branches associated with reviews:' 3163 print 'Branches associated with reviews:'
3105 output = get_cl_statuses(changes, 3164 output = get_cl_statuses(changes,
3106 fine_grained=not options.fast, 3165 fine_grained=not options.fast,
3107 max_processes=options.maxjobs) 3166 max_processes=options.maxjobs)
3108 3167
3109 branch_statuses = {} 3168 branch_statuses = {}
3110 alignment = max(5, max(len(ShortBranchName(c.GetBranch())) for c in changes)) 3169 alignment = max(5, max(len(ShortBranchName(c.GetBranch())) for c in changes))
3111 for cl in sorted(changes, key=lambda c: c.GetBranch()): 3170 for cl in sorted(changes, key=lambda c: c.GetBranch()):
3112 branch = cl.GetBranch() 3171 branch = cl.GetBranch()
3113 while branch not in branch_statuses: 3172 while branch not in branch_statuses:
3114 c, status = output.next() 3173 c, status, outdated = output.next()
3115 branch_statuses[c.GetBranch()] = status 3174 branch_statuses[c.GetBranch()] = (status, outdated)
3116 status = branch_statuses.pop(branch) 3175 status, outdated = branch_statuses.pop(branch)
3117 url = cl.GetIssueURL() 3176 url = cl.GetIssueURL()
3118 if url and (not status or status == 'error'): 3177 if url and (not status or status == 'error'):
3119 # The issue probably doesn't exist anymore. 3178 # The issue probably doesn't exist anymore.
3120 url += ' (broken)' 3179 url += ' (broken)'
3121 3180 make_color = lambda c : c if setup_color.IS_TTY else ''
3122 color = color_for_status(status) 3181 color = make_color(color_for_status(status))
3123 reset = Fore.RESET 3182 reset = make_color(Fore.RESET)
3124 if not setup_color.IS_TTY: 3183 color_outdated = make_color(Fore.RED)
3125 color = ''
3126 reset = ''
3127 status_str = '(%s)' % status if status else '' 3184 status_str = '(%s)' % status if status else ''
3128 print ' %*s : %s%s %s%s' % ( 3185 outdated_str = '%soutdated%s ' % (color_outdated, color) if outdated else ''
3129 alignment, ShortBranchName(branch), color, url, 3186 print ' %*s : %s%s %s%s%s' % (
3187 alignment, ShortBranchName(branch), color, url, outdated_str,
3130 status_str, reset) 3188 status_str, reset)
3131 3189
3132 cl = Changelist(auth_config=auth_config) 3190 cl = Changelist(auth_config=auth_config)
3133 print 3191 print
3134 print 'Current branch:', 3192 print 'Current branch:',
3135 print cl.GetBranch() 3193 print cl.GetBranch()
3136 if not cl.GetIssue(): 3194 if not cl.GetIssue():
3137 print 'No issue assigned.' 3195 print 'No issue assigned.'
3138 return 0 3196 return 0
3139 print 'Issue number: %s (%s)' % (cl.GetIssue(), cl.GetIssueURL()) 3197 print 'Issue number: %s (%s)' % (cl.GetIssue(), cl.GetIssueURL())
(...skipping 1708 matching lines...) Expand 10 before | Expand all | Expand 10 after
4848 if __name__ == '__main__': 4906 if __name__ == '__main__':
4849 # These affect sys.stdout so do it outside of main() to simplify mocks in 4907 # These affect sys.stdout so do it outside of main() to simplify mocks in
4850 # unit testing. 4908 # unit testing.
4851 fix_encoding.fix_encoding() 4909 fix_encoding.fix_encoding()
4852 setup_color.init() 4910 setup_color.init()
4853 try: 4911 try:
4854 sys.exit(main(sys.argv[1:])) 4912 sys.exit(main(sys.argv[1:]))
4855 except KeyboardInterrupt: 4913 except KeyboardInterrupt:
4856 sys.stderr.write('interrupted\n') 4914 sys.stderr.write('interrupted\n')
4857 sys.exit(1) 4915 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