Chromium Code Reviews| OLD | NEW |
|---|---|
| 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 optparse | 18 import optparse |
| 19 import os | 19 import os |
| 20 import Queue | |
| 21 import re | 20 import re |
| 22 import stat | 21 import stat |
| 23 import sys | 22 import sys |
| 24 import tempfile | |
| 25 import textwrap | 23 import textwrap |
| 26 import time | 24 import time |
| 27 import traceback | 25 import traceback |
| 28 import urllib | 26 import urllib |
| 29 import urllib2 | 27 import urllib2 |
| 30 import urlparse | 28 import urlparse |
| 31 import uuid | 29 import uuid |
| 32 import webbrowser | 30 import webbrowser |
| 33 import zlib | 31 import zlib |
| 34 | 32 |
| (...skipping 2121 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 2156 # Some reply from non-owner. | 2154 # Some reply from non-owner. |
| 2157 return 'reply' | 2155 return 'reply' |
| 2158 | 2156 |
| 2159 return 'waiting' | 2157 return 'waiting' |
| 2160 | 2158 |
| 2161 def GetMostRecentPatchset(self): | 2159 def GetMostRecentPatchset(self): |
| 2162 data = self._GetChangeDetail(['CURRENT_REVISION']) | 2160 data = self._GetChangeDetail(['CURRENT_REVISION']) |
| 2163 return data['revisions'][data['current_revision']]['_number'] | 2161 return data['revisions'][data['current_revision']]['_number'] |
| 2164 | 2162 |
| 2165 def FetchDescription(self): | 2163 def FetchDescription(self): |
| 2166 data = self._GetChangeDetail(['CURRENT_REVISION']) | 2164 data = self._GetChangeDetail(['CURRENT_REVISION']) |
|
tandrii(chromium)
2016/04/26 09:49:51
argh, rebase artifact.
| |
| 2167 current_rev = data['current_revision'] | 2165 current_rev = data['current_revision'] |
| 2168 url = data['revisions'][current_rev]['fetch']['http']['url'] | 2166 url = data['revisions'][current_rev]['fetch']['http']['url'] |
| 2169 return gerrit_util.GetChangeDescriptionFromGitiles(url, current_rev) | 2167 return gerrit_util.GetChangeDescriptionFromGitiles(url, current_rev) |
| 2170 | 2168 |
| 2171 def UpdateDescriptionRemote(self, description): | 2169 def UpdateDescriptionRemote(self, description): |
| 2172 # TODO(tandrii) | 2170 # TODO(tandrii) |
| 2173 raise NotImplementedError() | 2171 raise NotImplementedError() |
| 2174 | 2172 |
| 2175 def CloseIssue(self): | 2173 def CloseIssue(self): |
| 2176 gerrit_util.AbandonChange(self._GetGerritHost(), self.GetIssue(), msg='') | 2174 gerrit_util.AbandonChange(self._GetGerritHost(), self.GetIssue(), msg='') |
| (...skipping 717 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 2894 return { | 2892 return { |
| 2895 'unsent': Fore.RED, | 2893 'unsent': Fore.RED, |
| 2896 'waiting': Fore.BLUE, | 2894 'waiting': Fore.BLUE, |
| 2897 'reply': Fore.YELLOW, | 2895 'reply': Fore.YELLOW, |
| 2898 'lgtm': Fore.GREEN, | 2896 'lgtm': Fore.GREEN, |
| 2899 'commit': Fore.MAGENTA, | 2897 'commit': Fore.MAGENTA, |
| 2900 'closed': Fore.CYAN, | 2898 'closed': Fore.CYAN, |
| 2901 'error': Fore.WHITE, | 2899 'error': Fore.WHITE, |
| 2902 }.get(status, Fore.WHITE) | 2900 }.get(status, Fore.WHITE) |
| 2903 | 2901 |
| 2904 def fetch_cl_status(branch, auth_config=None): | |
| 2905 """Fetches information for an issue and returns (branch, issue, status).""" | |
| 2906 cl = Changelist(branchref=branch, auth_config=auth_config) | |
| 2907 url = cl.GetIssueURL() | |
| 2908 status = cl.GetStatus() | |
| 2909 | |
| 2910 if url and (not status or status == 'error'): | |
| 2911 # The issue probably doesn't exist anymore. | |
| 2912 url += ' (broken)' | |
| 2913 | |
| 2914 return (branch, url, status) | |
| 2915 | |
| 2916 def get_cl_statuses( | 2902 def get_cl_statuses( |
| 2917 branches, fine_grained, max_processes=None, auth_config=None): | 2903 changes, fine_grained, max_processes=None, auth_config=None): |
| 2918 """Returns a blocking iterable of (branch, issue, color) for given branches. | 2904 """Returns a blocking iterable of (cl, status) for given branches. |
|
tandrii(chromium)
2016/04/26 09:49:51
s/for given branches/for given changelists
| |
| 2919 | 2905 |
| 2920 If fine_grained is true, this will fetch CL statuses from the server. | 2906 If fine_grained is true, this will fetch CL statuses from the server. |
| 2921 Otherwise, simply indicate if there's a matching url for the given branches. | 2907 Otherwise, simply indicate if there's a matching url for the given branches. |
| 2922 | 2908 |
| 2923 If max_processes is specified, it is used as the maximum number of processes | 2909 If max_processes is specified, it is used as the maximum number of processes |
| 2924 to spawn to fetch CL status from the server. Otherwise 1 process per branch is | 2910 to spawn to fetch CL status from the server. Otherwise 1 process per branch is |
|
tandrii(chromium)
2016/04/26 09:49:51
s/branch/Changelist
| |
| 2925 spawned. | 2911 spawned. |
| 2926 """ | 2912 """ |
| 2927 # Silence upload.py otherwise it becomes unwieldly. | 2913 # Silence upload.py otherwise it becomes unwieldly. |
| 2928 upload.verbosity = 0 | 2914 upload.verbosity = 0 |
| 2929 | 2915 |
| 2916 change_cls = [Changelist(branchref=c, auth_config=auth_config) | |
| 2917 if type(c) is str else c for c in changes] | |
|
tandrii(chromium)
2016/04/26 09:49:51
Let's keep get_cl_statuses() API clear: changes is
| |
| 2930 if fine_grained: | 2918 if fine_grained: |
| 2931 # Process one branch synchronously to work through authentication, then | 2919 # Process one branch synchronously to work through authentication, then |
| 2932 # spawn processes to process all the other branches in parallel. | 2920 # spawn processes to process all the other branches in parallel. |
| 2933 if branches: | 2921 if changes: |
| 2934 fetch = lambda branch: fetch_cl_status(branch, auth_config=auth_config) | 2922 fetch = lambda cl: (cl, cl.GetStatus()) |
| 2935 yield fetch(branches[0]) | 2923 yield fetch(change_cls[0]) |
| 2936 | 2924 |
| 2937 branches_to_fetch = branches[1:] | 2925 changes_to_fetch = change_cls[1:] |
| 2938 pool = ThreadPool( | 2926 pool = ThreadPool( |
| 2939 min(max_processes, len(branches_to_fetch)) | 2927 min(max_processes, len(changes_to_fetch)) |
| 2940 if max_processes is not None | 2928 if max_processes is not None |
| 2941 else len(branches_to_fetch)) | 2929 else len(changes_to_fetch)) |
| 2942 for x in pool.imap_unordered(fetch, branches_to_fetch): | 2930 for x in pool.imap_unordered(fetch, changes_to_fetch): |
| 2943 yield x | 2931 yield x |
| 2944 else: | 2932 else: |
| 2945 # Do not use GetApprovingReviewers(), since it requires an HTTP request. | 2933 # Do not use GetApprovingReviewers(), since it requires an HTTP request. |
| 2946 for b in branches: | 2934 for cl in change_cls: |
| 2947 cl = Changelist(branchref=b, auth_config=auth_config) | 2935 yield (cl, 'waiting' if cl.GetIssueURL() else 'error') |
| 2948 url = cl.GetIssueURL() | |
| 2949 yield (b, url, 'waiting' if url else 'error') | |
| 2950 | 2936 |
| 2951 | 2937 |
| 2952 def upload_branch_deps(cl, args): | 2938 def upload_branch_deps(cl, args): |
| 2953 """Uploads CLs of local branches that are dependents of the current branch. | 2939 """Uploads CLs of local branches that are dependents of the current branch. |
| 2954 | 2940 |
| 2955 If the local branch dependency tree looks like: | 2941 If the local branch dependency tree looks like: |
| 2956 test1 -> test2.1 -> test3.1 | 2942 test1 -> test2.1 -> test3.1 |
| 2957 -> test3.2 | 2943 -> test3.2 |
| 2958 -> test2.2 -> test3.3 | 2944 -> test2.2 -> test3.3 |
| 2959 | 2945 |
| (...skipping 132 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 3092 url = cl.GetIssueURL() | 3078 url = cl.GetIssueURL() |
| 3093 if url: | 3079 if url: |
| 3094 print url | 3080 print url |
| 3095 return 0 | 3081 return 0 |
| 3096 | 3082 |
| 3097 branches = RunGit(['for-each-ref', '--format=%(refname)', 'refs/heads']) | 3083 branches = RunGit(['for-each-ref', '--format=%(refname)', 'refs/heads']) |
| 3098 if not branches: | 3084 if not branches: |
| 3099 print('No local branch found.') | 3085 print('No local branch found.') |
| 3100 return 0 | 3086 return 0 |
| 3101 | 3087 |
| 3102 changes = ( | 3088 changes = [ |
| 3103 Changelist(branchref=b, auth_config=auth_config) | 3089 Changelist(branchref=b, auth_config=auth_config) |
| 3104 for b in branches.splitlines()) | 3090 for b in branches.splitlines()] |
| 3105 # TODO(tandrii): refactor to use CLs list instead of branches list. | |
| 3106 branches = [c.GetBranch() for c in changes] | |
| 3107 alignment = max(5, max(len(b) for b in branches)) | |
| 3108 print 'Branches associated with reviews:' | 3091 print 'Branches associated with reviews:' |
| 3109 output = get_cl_statuses(branches, | 3092 output = get_cl_statuses(changes, |
| 3110 fine_grained=not options.fast, | 3093 fine_grained=not options.fast, |
| 3111 max_processes=options.maxjobs, | 3094 max_processes=options.maxjobs) |
| 3112 auth_config=auth_config) | |
| 3113 | 3095 |
| 3114 branch_statuses = {} | 3096 branch_statuses = {} |
| 3115 alignment = max(5, max(len(ShortBranchName(b)) for b in branches)) | 3097 alignment = max(5, max(len(ShortBranchName(c.GetBranch())) for c in changes)) |
| 3116 for branch in sorted(branches): | 3098 for cl in sorted(changes, key=lambda c: c.GetBranch()): |
| 3099 branch = cl.GetBranch() | |
| 3117 while branch not in branch_statuses: | 3100 while branch not in branch_statuses: |
| 3118 b, i, status = output.next() | 3101 c, status = output.next() |
| 3119 branch_statuses[b] = (i, status) | 3102 branch_statuses[c.GetBranch()] = status |
| 3120 issue_url, status = branch_statuses.pop(branch) | 3103 status = branch_statuses.pop(branch) |
| 3104 url = cl.GetIssueURL() | |
| 3105 if url and (not status or status == 'error'): | |
| 3106 # The issue probably doesn't exist anymore. | |
| 3107 url += ' (broken)' | |
| 3108 | |
| 3121 color = color_for_status(status) | 3109 color = color_for_status(status) |
| 3122 reset = Fore.RESET | 3110 reset = Fore.RESET |
| 3123 if not setup_color.IS_TTY: | 3111 if not setup_color.IS_TTY: |
| 3124 color = '' | 3112 color = '' |
| 3125 reset = '' | 3113 reset = '' |
| 3126 status_str = '(%s)' % status if status else '' | 3114 status_str = '(%s)' % status if status else '' |
| 3127 print ' %*s : %s%s %s%s' % ( | 3115 print ' %*s : %s%s %s%s' % ( |
| 3128 alignment, ShortBranchName(branch), color, issue_url, status_str, | 3116 alignment, ShortBranchName(branch), color, url, |
| 3129 reset) | 3117 status_str, reset) |
| 3130 | 3118 |
| 3131 cl = Changelist(auth_config=auth_config) | 3119 cl = Changelist(auth_config=auth_config) |
| 3132 print | 3120 print |
| 3133 print 'Current branch:', | 3121 print 'Current branch:', |
| 3134 print cl.GetBranch() | 3122 print cl.GetBranch() |
| 3135 if not cl.GetIssue(): | 3123 if not cl.GetIssue(): |
| 3136 print 'No issue assigned.' | 3124 print 'No issue assigned.' |
| 3137 return 0 | 3125 return 0 |
| 3138 print 'Issue number: %s (%s)' % (cl.GetIssue(), cl.GetIssueURL()) | 3126 print 'Issue number: %s (%s)' % (cl.GetIssue(), cl.GetIssueURL()) |
| 3139 if not options.fast: | 3127 if not options.fast: |
| (...skipping 1690 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 4830 if __name__ == '__main__': | 4818 if __name__ == '__main__': |
| 4831 # These affect sys.stdout so do it outside of main() to simplify mocks in | 4819 # These affect sys.stdout so do it outside of main() to simplify mocks in |
| 4832 # unit testing. | 4820 # unit testing. |
| 4833 fix_encoding.fix_encoding() | 4821 fix_encoding.fix_encoding() |
| 4834 setup_color.init() | 4822 setup_color.init() |
| 4835 try: | 4823 try: |
| 4836 sys.exit(main(sys.argv[1:])) | 4824 sys.exit(main(sys.argv[1:])) |
| 4837 except KeyboardInterrupt: | 4825 except KeyboardInterrupt: |
| 4838 sys.stderr.write('interrupted\n') | 4826 sys.stderr.write('interrupted\n') |
| 4839 sys.exit(1) | 4827 sys.exit(1) |
| OLD | NEW |