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

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: rebase Created 4 years, 8 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 | no next file » | 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 2882 matching lines...) Expand 10 before | Expand all | Expand 10 after
2893 return { 2893 return {
2894 'unsent': Fore.RED, 2894 'unsent': Fore.RED,
2895 'waiting': Fore.BLUE, 2895 'waiting': Fore.BLUE,
2896 'reply': Fore.YELLOW, 2896 'reply': Fore.YELLOW,
2897 'lgtm': Fore.GREEN, 2897 'lgtm': Fore.GREEN,
2898 'commit': Fore.MAGENTA, 2898 'commit': Fore.MAGENTA,
2899 'closed': Fore.CYAN, 2899 'closed': Fore.CYAN,
2900 'error': Fore.WHITE, 2900 'error': Fore.WHITE,
2901 }.get(status, Fore.WHITE) 2901 }.get(status, Fore.WHITE)
2902 2902
2903 def split_diff_files(diff):
2904 """Splits a diff for the individual files and outputs a dict
2905 {file name: diff}.
2906 """
2907 files = {}
2908 current_lines = []
2909 current_file = ''
2910 def append():
2911 if current_file:
2912 files[current_file] = '\n'.join(current_lines+[''])
2913 for line in diff.splitlines():
2914 if line.startswith('Index: '):
2915 continue
2916 if line.startswith('diff '):
2917 append()
2918 current_lines = []
2919 full_file_name = line.split()[-1]
2920 current_file = '/'.join(full_file_name.split('/')[1:])
2921 line = re.sub(' [ab]/'+re.escape(current_file), ' '+current_file, line)
2922 if line.startswith('+++ ') or line.startswith('--- '):
2923 line = re.sub(' [ab]/'+re.escape(current_file), ' '+current_file, line)
2924 current_lines.append(line)
2925 append()
2926 return files
2927
2928 def diff_compare(left, right):
2929 """Compares two diffs without regarding actual file names, line number
2930 information or commit hashes.
tandrii(chromium) 2016/04/15 15:15:13 nit: add empty line.
2931 Returns True if they are equal, False otherwise.
2932 """
2933 file_pattern = re.compile('^(diff |\+\+\+ |--- ).*$', re.MULTILINE)
2934 line_pattern = re.compile('^@@[ +\-0-9,]+@@', re.MULTILINE)
2935 index_pattern = re.compile('^index [0-9a-f]+\.\.[0-9a-f]+', re.MULTILINE)
2936 left = re.sub(line_pattern, '@@', re.sub(index_pattern, 'index',
2937 re.sub(file_pattern, '\\1', left)))
2938 right = re.sub(line_pattern, '@@', re.sub(index_pattern, 'index',
2939 re.sub(file_pattern, '\\1', right)))
2940 return left == right
2941
2903 def fetch_cl_status(cl, auth_config=None): 2942 def fetch_cl_status(cl, auth_config=None):
2904 """Fetches information for an issue and returns (branch, issue, status).""" 2943 """Fetches information for an issue and returns
2944 (branch, issue, status, outdated).
2945 """
2905 url = cl.GetIssueURL() 2946 url = cl.GetIssueURL()
2906 status = cl.GetStatus() 2947 status = cl.GetStatus()
2948 outdated = False
2949 if cl.GetIssue():
2950 latest_patchset = cl.GetMostRecentPatchset()
2951 uploaded_diff = cl.GetPatchSetDiff(cl.GetIssue(), latest_patchset)
2952 uploaded_file_diffs = split_diff_files(uploaded_diff)
2953 change = cl.GetChange(cl.GetCommonAncestorWithUpstream(), None)
2954 local_file_diffs = {f.LocalPath(): f.GenerateScmDiff()
2955 for f in change.AffectedFiles()}
2956 files = sorted(uploaded_file_diffs.keys())
2957 if files != sorted(local_file_diffs.keys()) or \
2958 any(not diff_compare(uploaded_file_diffs[f], local_file_diffs[f])
2959 for f in uploaded_file_diffs.keys()):
2960 outdated = True
2907 2961
2908 if url and (not status or status == 'error'): 2962 if url and (not status or status == 'error'):
2909 # The issue probably doesn't exist anymore. 2963 # The issue probably doesn't exist anymore.
2910 url += ' (broken)' 2964 url += ' (broken)'
2911 2965
2912 return (cl, url, status) 2966 return (cl, url, status, outdated)
2913 2967
2914 def get_cl_statuses( 2968 def get_cl_statuses(
2915 changes, fine_grained, max_processes=None, auth_config=None): 2969 changes, fine_grained, max_processes=None, auth_config=None):
2916 """Returns a blocking iterable of (branch, issue, color) for given branches. 2970 """Returns a blocking iterable of (branch, issue, color, outdated) for given
2971 branches.
2917 2972
2918 If fine_grained is true, this will fetch CL statuses from the server. 2973 If fine_grained is true, this will fetch CL statuses from the server.
2919 Otherwise, simply indicate if there's a matching url for the given branches. 2974 Otherwise, simply indicate if there's a matching url for the given branches.
2920 2975
2921 If max_processes is specified, it is used as the maximum number of processes 2976 If max_processes is specified, it is used as the maximum number of processes
2922 to spawn to fetch CL status from the server. Otherwise 1 process per branch is 2977 to spawn to fetch CL status from the server. Otherwise 1 process per branch is
2923 spawned. 2978 spawned.
2924 """ 2979 """
2925 # Silence upload.py otherwise it becomes unwieldly. 2980 # Silence upload.py otherwise it becomes unwieldly.
2926 upload.verbosity = 0 2981 upload.verbosity = 0
(...skipping 10 matching lines...) Expand all
2937 min(max_processes, len(changes_to_fetch)) 2992 min(max_processes, len(changes_to_fetch))
2938 if max_processes is not None 2993 if max_processes is not None
2939 else len(changes_to_fetch)) 2994 else len(changes_to_fetch))
2940 for x in pool.imap_unordered(fetch, changes_to_fetch): 2995 for x in pool.imap_unordered(fetch, changes_to_fetch):
2941 yield x 2996 yield x
2942 else: 2997 else:
2943 # Do not use GetApprovingReviewers(), since it requires an HTTP request. 2998 # Do not use GetApprovingReviewers(), since it requires an HTTP request.
2944 for c in changes: 2999 for c in changes:
2945 cl = Changelist(branchref=c.GetBranch(), auth_config=auth_config) 3000 cl = Changelist(branchref=c.GetBranch(), auth_config=auth_config)
2946 url = cl.GetIssueURL() 3001 url = cl.GetIssueURL()
2947 yield (c, url, 'waiting' if url else 'error') 3002 yield (c, url, 'waiting' if url else 'error', False)
2948 3003
2949 3004
2950 def upload_branch_deps(cl, args): 3005 def upload_branch_deps(cl, args):
2951 """Uploads CLs of local branches that are dependents of the current branch. 3006 """Uploads CLs of local branches that are dependents of the current branch.
2952 3007
2953 If the local branch dependency tree looks like: 3008 If the local branch dependency tree looks like:
2954 test1 -> test2.1 -> test3.1 3009 test1 -> test2.1 -> test3.1
2955 -> test3.2 3010 -> test3.2
2956 -> test2.2 -> test3.3 3011 -> test2.2 -> test3.3
2957 3012
(...skipping 146 matching lines...) Expand 10 before | Expand all | Expand 10 after
3104 output = get_cl_statuses(changes, 3159 output = get_cl_statuses(changes,
3105 fine_grained=not options.fast, 3160 fine_grained=not options.fast,
3106 max_processes=options.maxjobs, 3161 max_processes=options.maxjobs,
3107 auth_config=auth_config) 3162 auth_config=auth_config)
3108 3163
3109 branch_statuses = {} 3164 branch_statuses = {}
3110 alignment = max(5, max(len(ShortBranchName(c.GetBranch())) for c in changes)) 3165 alignment = max(5, max(len(ShortBranchName(c.GetBranch())) for c in changes))
3111 for cl in sorted(changes, key = lambda c : c.GetBranch()): 3166 for cl in sorted(changes, key = lambda c : c.GetBranch()):
3112 branch = cl.GetBranch() 3167 branch = cl.GetBranch()
3113 while branch not in branch_statuses: 3168 while branch not in branch_statuses:
3114 c, i, status = output.next() 3169 c, i, status, outdated = output.next()
3115 branch_statuses[c.GetBranch()] = (i, status) 3170 branch_statuses[c.GetBranch()] = (i, status, outdated)
3116 issue_url, status = branch_statuses.pop(branch) 3171 issue_url, status, outdated = branch_statuses.pop(branch)
3117 color = color_for_status(status) 3172 make_color = lambda c : c if setup_color.IS_TTY else ''
3118 reset = Fore.RESET 3173 color = make_color(color_for_status(status))
3119 if not setup_color.IS_TTY: 3174 reset = make_color(Fore.RESET)
3120 color = '' 3175 color_outdated = make_color(Fore.RED)
3121 reset = ''
3122 status_str = '(%s)' % status if status else '' 3176 status_str = '(%s)' % status if status else ''
3123 print ' %*s : %s%s %s%s' % ( 3177 outdated_str = '%soutdated%s ' % (color_outdated, color) if outdated else ''
3124 alignment, ShortBranchName(branch), color, issue_url, 3178 print ' %*s : %s%s %s%s%s' % (
3179 alignment, ShortBranchName(branch), color, issue_url, outdated_str,
3125 status_str, reset) 3180 status_str, reset)
3126 3181
3127 cl = Changelist(auth_config=auth_config) 3182 cl = Changelist(auth_config=auth_config)
3128 print 3183 print
3129 print 'Current branch:', 3184 print 'Current branch:',
3130 print cl.GetBranch() 3185 print cl.GetBranch()
3131 if not cl.GetIssue(): 3186 if not cl.GetIssue():
3132 print 'No issue assigned.' 3187 print 'No issue assigned.'
3133 return 0 3188 return 0
3134 print 'Issue number: %s (%s)' % (cl.GetIssue(), cl.GetIssueURL()) 3189 print 'Issue number: %s (%s)' % (cl.GetIssue(), cl.GetIssueURL())
(...skipping 1691 matching lines...) Expand 10 before | Expand all | Expand 10 after
4826 if __name__ == '__main__': 4881 if __name__ == '__main__':
4827 # These affect sys.stdout so do it outside of main() to simplify mocks in 4882 # These affect sys.stdout so do it outside of main() to simplify mocks in
4828 # unit testing. 4883 # unit testing.
4829 fix_encoding.fix_encoding() 4884 fix_encoding.fix_encoding()
4830 setup_color.init() 4885 setup_color.init()
4831 try: 4886 try:
4832 sys.exit(main(sys.argv[1:])) 4887 sys.exit(main(sys.argv[1:]))
4833 except KeyboardInterrupt: 4888 except KeyboardInterrupt:
4834 sys.stderr.write('interrupted\n') 4889 sys.stderr.write('interrupted\n')
4835 sys.exit(1) 4890 sys.exit(1)
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698