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