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.""" | 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 |
| (...skipping 130 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 149 | 149 |
| 150 def ask_for_data(prompt): | 150 def ask_for_data(prompt): |
| 151 try: | 151 try: |
| 152 return raw_input(prompt) | 152 return raw_input(prompt) |
| 153 except KeyboardInterrupt: | 153 except KeyboardInterrupt: |
| 154 # Hide the exception. | 154 # Hide the exception. |
| 155 sys.exit(1) | 155 sys.exit(1) |
| 156 | 156 |
| 157 | 157 |
| 158 def git_set_branch_value(key, value): | 158 def git_set_branch_value(key, value): |
| 159 branch = Changelist().GetBranch() | 159 branch = GetCurrentBranch() |
| 160 if not branch: | 160 if not branch: |
| 161 return | 161 return |
| 162 | 162 |
| 163 cmd = ['config'] | 163 cmd = ['config'] |
| 164 if isinstance(value, int): | 164 if isinstance(value, int): |
| 165 cmd.append('--int') | 165 cmd.append('--int') |
| 166 git_key = 'branch.%s.%s' % (branch, key) | 166 git_key = 'branch.%s.%s' % (branch, key) |
| 167 RunGit(cmd + [git_key, str(value)]) | 167 RunGit(cmd + [git_key, str(value)]) |
| 168 | 168 |
| 169 | 169 |
| 170 def git_get_branch_default(key, default): | 170 def git_get_branch_default(key, default): |
| 171 branch = Changelist().GetBranch() | 171 branch = GetCurrentBranch() |
| 172 if branch: | 172 if branch: |
| 173 git_key = 'branch.%s.%s' % (branch, key) | 173 git_key = 'branch.%s.%s' % (branch, key) |
| 174 (_, stdout) = RunGitWithCode(['config', '--int', '--get', git_key]) | 174 (_, stdout) = RunGitWithCode(['config', '--int', '--get', git_key]) |
| 175 try: | 175 try: |
| 176 return int(stdout.strip()) | 176 return int(stdout.strip()) |
| 177 except ValueError: | 177 except ValueError: |
| 178 pass | 178 pass |
| 179 return default | 179 return default |
| 180 | 180 |
| 181 | 181 |
| (...skipping 624 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 806 def _GetBranchConfig(self, branch_name, param, **kwargs): | 806 def _GetBranchConfig(self, branch_name, param, **kwargs): |
| 807 return self._GetConfig('branch.' + branch_name + '.' + param, **kwargs) | 807 return self._GetConfig('branch.' + branch_name + '.' + param, **kwargs) |
| 808 | 808 |
| 809 def _GetConfig(self, param, **kwargs): | 809 def _GetConfig(self, param, **kwargs): |
| 810 self.LazyUpdateIfNeeded() | 810 self.LazyUpdateIfNeeded() |
| 811 return RunGit(['config', param], **kwargs).strip() | 811 return RunGit(['config', param], **kwargs).strip() |
| 812 | 812 |
| 813 | 813 |
| 814 def ShortBranchName(branch): | 814 def ShortBranchName(branch): |
| 815 """Convert a name like 'refs/heads/foo' to just 'foo'.""" | 815 """Convert a name like 'refs/heads/foo' to just 'foo'.""" |
| 816 return branch.replace('refs/heads/', '') | 816 return branch.replace('refs/heads/', '', 1) |
| 817 | |
| 818 | |
| 819 def GetCurrentBranchRef(): | |
| 820 """Returns branch ref (e.g., refs/heads/master) or None.""" | |
| 821 return RunGit(['symbolic-ref', 'HEAD'], | |
| 822 stderr=subprocess2.VOID, error_ok=True).strip() or None | |
| 823 | |
| 824 | |
| 825 def GetCurrentBranch(): | |
| 826 """Returns current branch or None. | |
| 827 | |
| 828 For refs/heads/* branches, returns just last part. For others, full ref. | |
| 829 """ | |
| 830 branchref = GetCurrentBranchRef() | |
| 831 if branchref: | |
| 832 return ShortBranchName(branchref) | |
| 833 return None | |
| 817 | 834 |
| 818 | 835 |
| 819 class Changelist(object): | 836 class Changelist(object): |
| 820 def __init__(self, branchref=None, issue=None, auth_config=None): | 837 """Changelist works with one changelist in local branch. |
| 838 | |
| 839 Supports two codereview backends: Rietveld or Gerrit, selected at object | |
| 840 creation. | |
| 841 | |
| 842 Not safe for concurrent multi-{thread,process} use. | |
| 843 """ | |
| 844 | |
| 845 def __init__(self, branchref=None, issue=None, codereview=None, **kwargs): | |
| 846 """Create a new ChangeList instance. | |
| 847 | |
| 848 If issue is given, the codereview must be given too. | |
| 849 | |
| 850 If `codereview` is given, it must be 'rietveld' or 'gerrit'. | |
| 851 Otherwise, it's decided based on current configuration of the local branch, | |
| 852 with default being 'rietveld' for backwards compatibility. | |
| 853 See _load_codereview_impl for more details. | |
| 854 | |
| 855 **kwargs will be passed directly to codereview implementation. | |
| 856 """ | |
| 821 # Poke settings so we get the "configure your server" message if necessary. | 857 # Poke settings so we get the "configure your server" message if necessary. |
| 822 global settings | 858 global settings |
| 823 if not settings: | 859 if not settings: |
| 824 # Happens when git_cl.py is used as a utility library. | 860 # Happens when git_cl.py is used as a utility library. |
| 825 settings = Settings() | 861 settings = Settings() |
| 826 settings.GetDefaultServerUrl() | 862 |
| 863 if issue: | |
| 864 assert codereview, 'codereview must be known, if issue is known' | |
| 865 | |
| 827 self.branchref = branchref | 866 self.branchref = branchref |
| 828 if self.branchref: | 867 if self.branchref: |
| 829 self.branch = ShortBranchName(self.branchref) | 868 self.branch = ShortBranchName(self.branchref) |
| 830 else: | 869 else: |
| 831 self.branch = None | 870 self.branch = None |
| 832 self.rietveld_server = None | |
| 833 self.upstream_branch = None | 871 self.upstream_branch = None |
| 834 self.lookedup_issue = False | 872 self.lookedup_issue = False |
| 835 self.issue = issue or None | 873 self.issue = issue or None |
| 836 self.has_description = False | 874 self.has_description = False |
| 837 self.description = None | 875 self.description = None |
| 838 self.lookedup_patchset = False | 876 self.lookedup_patchset = False |
| 839 self.patchset = None | 877 self.patchset = None |
| 840 self.cc = None | 878 self.cc = None |
| 841 self.watchers = () | 879 self.watchers = () |
| 842 self._auth_config = auth_config | |
| 843 self._props = None | |
| 844 self._remote = None | 880 self._remote = None |
| 845 self._rpc_server = None | |
| 846 | 881 |
| 847 @property | 882 self._codereview_impl = None |
| 848 def auth_config(self): | 883 self._load_codereview_impl(codereview, **kwargs) |
| 849 return self._auth_config | 884 |
| 885 def _load_codereview_impl(self, codereview=None, **kwargs): | |
| 886 if codereview: | |
| 887 codereview = codereview.lower() | |
| 888 if codereview == 'gerrit': | |
|
Sergiy Byelozyorov
2016/03/21 17:17:06
Why not use an enum? Or at least define these as c
tandrii(chromium)
2016/03/22 17:41:52
because it's python, and having RIETVELD = 'rietve
Sergiy Byelozyorov
2016/03/23 10:22:42
Acknowledged.
| |
| 889 self._codereview_impl = _GerritChangelistImpl(self) | |
| 890 elif codereview == 'rietveld': | |
| 891 self._codereview_impl = _RietveldChangelistImpl(self, **kwargs) | |
| 892 else: | |
| 893 assert codereview in ('rietveld', 'gerrit') | |
|
Sergiy Byelozyorov
2016/03/21 17:17:06
nit: IMHO
assert False, 'unsupported codereview'
tandrii(chromium)
2016/03/22 17:41:52
yes, but AssertionError is much nicer when you see
Sergiy Byelozyorov
2016/03/23 10:22:42
Acknowledged.
| |
| 894 return | |
| 895 | |
| 896 # Automatic selection. | |
| 897 assert not self.issue | |
| 898 # Check if this branch is associated with Rietveld => Rieveld. | |
| 899 self._codereview_impl = _RietveldChangelistImpl(self, **kwargs) | |
| 900 if self.GetIssue(force_lookup=True): | |
| 901 return | |
| 902 | |
| 903 tmp_rietveld = self._codereview_impl # Save Rietveld object. | |
|
Sergiy Byelozyorov
2016/03/21 17:17:06
Why not check if it's gerrit first? Then you won't
tandrii(chromium)
2016/03/22 17:41:52
because if Rietveld issue is defined, it should be
Sergiy Byelozyorov
2016/03/23 10:22:42
Acknowledged.
| |
| 904 | |
| 905 # Check if this branch has Gerrit issue associated => Gerrit. | |
| 906 self._codereview_impl = _GerritChangelistImpl(self) | |
| 907 if self.GetIssue(force_lookup=True): | |
| 908 return | |
| 909 # If Gerrit is set repo-wide => Gerrit. | |
| 910 if settings.GetIsGerrit(): | |
| 911 return | |
| 912 | |
| 913 self._codereview_impl = tmp_rietveld | |
| 914 return | |
| 915 | |
| 850 | 916 |
| 851 def GetCCList(self): | 917 def GetCCList(self): |
| 852 """Return the users cc'd on this CL. | 918 """Return the users cc'd on this CL. |
| 853 | 919 |
| 854 Return is a string suitable for passing to gcl with the --cc flag. | 920 Return is a string suitable for passing to gcl with the --cc flag. |
| 855 """ | 921 """ |
| 856 if self.cc is None: | 922 if self.cc is None: |
| 857 base_cc = settings.GetDefaultCCList() | 923 base_cc = settings.GetDefaultCCList() |
| 858 more_cc = ','.join(self.watchers) | 924 more_cc = ','.join(self.watchers) |
| 859 self.cc = ','.join(filter(None, (base_cc, more_cc))) or '' | 925 self.cc = ','.join(filter(None, (base_cc, more_cc))) or '' |
| 860 return self.cc | 926 return self.cc |
| 861 | 927 |
| 862 def GetCCListWithoutDefault(self): | 928 def GetCCListWithoutDefault(self): |
| 863 """Return the users cc'd on this CL excluding default ones.""" | 929 """Return the users cc'd on this CL excluding default ones.""" |
| 864 if self.cc is None: | 930 if self.cc is None: |
| 865 self.cc = ','.join(self.watchers) | 931 self.cc = ','.join(self.watchers) |
| 866 return self.cc | 932 return self.cc |
| 867 | 933 |
| 868 def SetWatchers(self, watchers): | 934 def SetWatchers(self, watchers): |
| 869 """Set the list of email addresses that should be cc'd based on the changed | 935 """Set the list of email addresses that should be cc'd based on the changed |
| 870 files in this CL. | 936 files in this CL. |
| 871 """ | 937 """ |
| 872 self.watchers = watchers | 938 self.watchers = watchers |
| 873 | 939 |
| 874 def GetBranch(self): | 940 def GetBranch(self): |
| 875 """Returns the short branch name, e.g. 'master'.""" | 941 """Returns the short branch name, e.g. 'master'.""" |
| 876 if not self.branch: | 942 if not self.branch: |
| 877 branchref = RunGit(['symbolic-ref', 'HEAD'], | 943 branchref = GetCurrentBranchRef() |
| 878 stderr=subprocess2.VOID, error_ok=True).strip() | |
| 879 if not branchref: | 944 if not branchref: |
| 880 return None | 945 return None |
| 881 self.branchref = branchref | 946 self.branchref = branchref |
| 882 self.branch = ShortBranchName(self.branchref) | 947 self.branch = ShortBranchName(self.branchref) |
| 883 return self.branch | 948 return self.branch |
| 884 | 949 |
| 885 def GetBranchRef(self): | 950 def GetBranchRef(self): |
| 886 """Returns the full branch name, e.g. 'refs/heads/master'.""" | 951 """Returns the full branch name, e.g. 'refs/heads/master'.""" |
| 887 self.GetBranch() # Poke the lazy loader. | 952 self.GetBranch() # Poke the lazy loader. |
| 888 return self.branchref | 953 return self.branchref |
| (...skipping 23 matching lines...) Expand all Loading... | |
| 912 if 'origin/master' in remote_branches: | 977 if 'origin/master' in remote_branches: |
| 913 # Fall back on origin/master if it exits. | 978 # Fall back on origin/master if it exits. |
| 914 remote = 'origin' | 979 remote = 'origin' |
| 915 upstream_branch = 'refs/heads/master' | 980 upstream_branch = 'refs/heads/master' |
| 916 elif 'origin/trunk' in remote_branches: | 981 elif 'origin/trunk' in remote_branches: |
| 917 # Fall back on origin/trunk if it exists. Generally a shared | 982 # Fall back on origin/trunk if it exists. Generally a shared |
| 918 # git-svn clone | 983 # git-svn clone |
| 919 remote = 'origin' | 984 remote = 'origin' |
| 920 upstream_branch = 'refs/heads/trunk' | 985 upstream_branch = 'refs/heads/trunk' |
| 921 else: | 986 else: |
| 922 DieWithError("""Unable to determine default branch to diff against. | 987 DieWithError( |
| 923 Either pass complete "git diff"-style arguments, like | 988 'Unable to determine default branch to diff against.\n' |
| 924 git cl upload origin/master | 989 'Either pass complete "git diff"-style arguments, like\n' |
| 925 or verify this branch is set up to track another (via the --track argument to | 990 ' git cl upload origin/master\n' |
| 926 "git checkout -b ...").""") | 991 'or verify this branch is set up to track another \n' |
|
Sergiy Byelozyorov
2016/03/21 17:17:06
nit: remove \n in the end - let the terminal break
tandrii(chromium)
2016/03/22 17:41:52
well, it's apparently customary in this file to br
Sergiy Byelozyorov
2016/03/23 10:22:42
Acknowledged.
| |
| 992 '(via the --track argument to "git checkout -b ...").') | |
| 927 | 993 |
| 928 return remote, upstream_branch | 994 return remote, upstream_branch |
| 929 | 995 |
| 930 def GetCommonAncestorWithUpstream(self): | 996 def GetCommonAncestorWithUpstream(self): |
| 931 upstream_branch = self.GetUpstreamBranch() | 997 upstream_branch = self.GetUpstreamBranch() |
| 932 if not BranchExists(upstream_branch): | 998 if not BranchExists(upstream_branch): |
| 933 DieWithError('The upstream for the current branch (%s) does not exist ' | 999 DieWithError('The upstream for the current branch (%s) does not exist ' |
| 934 'anymore.\nPlease fix it and try again.' % self.GetBranch()) | 1000 'anymore.\nPlease fix it and try again.' % self.GetBranch()) |
| 935 return git_common.get_or_create_merge_base(self.GetBranch(), | 1001 return git_common.get_or_create_merge_base(self.GetBranch(), |
| 936 upstream_branch) | 1002 upstream_branch) |
| (...skipping 118 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1055 remote, _ = self.GetRemoteBranch() | 1121 remote, _ = self.GetRemoteBranch() |
| 1056 url = RunGit(['config', 'remote.%s.url' % remote], error_ok=True).strip() | 1122 url = RunGit(['config', 'remote.%s.url' % remote], error_ok=True).strip() |
| 1057 | 1123 |
| 1058 # If URL is pointing to a local directory, it is probably a git cache. | 1124 # If URL is pointing to a local directory, it is probably a git cache. |
| 1059 if os.path.isdir(url): | 1125 if os.path.isdir(url): |
| 1060 url = RunGit(['config', 'remote.%s.url' % remote], | 1126 url = RunGit(['config', 'remote.%s.url' % remote], |
| 1061 error_ok=True, | 1127 error_ok=True, |
| 1062 cwd=url).strip() | 1128 cwd=url).strip() |
| 1063 return url | 1129 return url |
| 1064 | 1130 |
| 1065 def GetIssue(self): | 1131 def GetIssue(self, force_lookup=False): |
| 1066 """Returns the issue number as a int or None if not set.""" | 1132 """Returns the issue number as a int or None if not set.""" |
| 1067 if self.issue is None and not self.lookedup_issue: | 1133 if force_lookup or (self.issue is None and not self.lookedup_issue): |
| 1068 issue = RunGit(['config', self._IssueSetting()], error_ok=True).strip() | 1134 issue = RunGit(['config', self._codereview_impl.IssueSetting()], |
| 1135 error_ok=True).strip() | |
| 1069 self.issue = int(issue) or None if issue else None | 1136 self.issue = int(issue) or None if issue else None |
| 1070 self.lookedup_issue = True | 1137 self.lookedup_issue = True |
| 1071 return self.issue | 1138 return self.issue |
| 1072 | 1139 |
| 1073 def GetRietveldServer(self): | |
| 1074 if not self.rietveld_server: | |
| 1075 # If we're on a branch then get the server potentially associated | |
| 1076 # with that branch. | |
| 1077 if self.GetIssue(): | |
| 1078 rietveld_server_config = self._RietveldServer() | |
| 1079 if rietveld_server_config: | |
| 1080 self.rietveld_server = gclient_utils.UpgradeToHttps(RunGit( | |
| 1081 ['config', rietveld_server_config], error_ok=True).strip()) | |
| 1082 if not self.rietveld_server: | |
| 1083 self.rietveld_server = settings.GetDefaultServerUrl() | |
| 1084 return self.rietveld_server | |
| 1085 | |
| 1086 def GetGerritServer(self): | |
| 1087 # We don't support multiple Gerrit servers, and assume it to be same as | |
| 1088 # origin, except with a '-review' suffix for first subdomain. | |
| 1089 parts = urlparse.urlparse(self.GetRemoteUrl()).netloc.split('.') | |
| 1090 parts[0] = parts[0] + '-review' | |
| 1091 return 'https://%s' % '.'.join(parts) | |
| 1092 | |
| 1093 def GetIssueURL(self): | 1140 def GetIssueURL(self): |
| 1094 """Get the URL for a particular issue.""" | 1141 """Get the URL for a particular issue.""" |
| 1095 if not self.GetIssue(): | 1142 issue = self.GetIssue() |
| 1143 if not issue: | |
| 1096 return None | 1144 return None |
| 1097 if settings.GetIsGerrit(): | 1145 return '%s/%s' % (self._codereview_impl.GetCodereviewServer(), issue) |
| 1098 return '%s/%s' % (self.GetGerritServer(), self.GetIssue()) | |
| 1099 return '%s/%s' % (self.GetRietveldServer(), self.GetIssue()) | |
| 1100 | 1146 |
| 1101 def GetDescription(self, pretty=False): | 1147 def GetDescription(self, pretty=False): |
| 1102 if not self.has_description: | 1148 if not self.has_description: |
| 1103 if self.GetIssue(): | 1149 if self.GetIssue(): |
| 1104 issue = self.GetIssue() | 1150 self.description = self._codereview_impl.FetchDescription() |
| 1105 try: | |
| 1106 self.description = self.RpcServer().get_description(issue).strip() | |
| 1107 except urllib2.HTTPError as e: | |
| 1108 if e.code == 404: | |
| 1109 DieWithError( | |
| 1110 ('\nWhile fetching the description for issue %d, received a ' | |
| 1111 '404 (not found)\n' | |
| 1112 'error. It is likely that you deleted this ' | |
| 1113 'issue on the server. If this is the\n' | |
| 1114 'case, please run\n\n' | |
| 1115 ' git cl issue 0\n\n' | |
| 1116 'to clear the association with the deleted issue. Then run ' | |
| 1117 'this command again.') % issue) | |
| 1118 else: | |
| 1119 DieWithError( | |
| 1120 '\nFailed to fetch issue description. HTTP error %d' % e.code) | |
| 1121 except urllib2.URLError as e: | |
| 1122 print >> sys.stderr, ( | |
| 1123 'Warning: Failed to retrieve CL description due to network ' | |
| 1124 'failure.') | |
| 1125 self.description = '' | |
| 1126 | |
| 1127 self.has_description = True | 1151 self.has_description = True |
| 1128 if pretty: | 1152 if pretty: |
| 1129 wrapper = textwrap.TextWrapper() | 1153 wrapper = textwrap.TextWrapper() |
| 1130 wrapper.initial_indent = wrapper.subsequent_indent = ' ' | 1154 wrapper.initial_indent = wrapper.subsequent_indent = ' ' |
| 1131 return wrapper.fill(self.description) | 1155 return wrapper.fill(self.description) |
| 1132 return self.description | 1156 return self.description |
| 1133 | 1157 |
| 1134 def GetPatchset(self): | 1158 def GetPatchset(self): |
| 1135 """Returns the patchset number as a int or None if not set.""" | 1159 """Returns the patchset number as a int or None if not set.""" |
| 1136 if self.patchset is None and not self.lookedup_patchset: | 1160 if self.patchset is None and not self.lookedup_patchset: |
| 1137 patchset = RunGit(['config', self._PatchsetSetting()], | 1161 patchset = RunGit(['config', self._codereview_impl.PatchsetSetting()], |
| 1138 error_ok=True).strip() | 1162 error_ok=True).strip() |
| 1139 self.patchset = int(patchset) or None if patchset else None | 1163 self.patchset = int(patchset) or None if patchset else None |
| 1140 self.lookedup_patchset = True | 1164 self.lookedup_patchset = True |
| 1141 return self.patchset | 1165 return self.patchset |
| 1142 | 1166 |
| 1143 def SetPatchset(self, patchset): | 1167 def SetPatchset(self, patchset): |
| 1144 """Set this branch's patchset. If patchset=0, clears the patchset.""" | 1168 """Set this branch's patchset. If patchset=0, clears the patchset.""" |
| 1169 patchset_setting = self._codereview_impl.PatchsetSetting() | |
| 1145 if patchset: | 1170 if patchset: |
| 1146 RunGit(['config', self._PatchsetSetting(), str(patchset)]) | 1171 RunGit(['config', patchset_setting, str(patchset)]) |
| 1147 self.patchset = patchset | 1172 self.patchset = patchset |
| 1148 else: | 1173 else: |
| 1149 RunGit(['config', '--unset', self._PatchsetSetting()], | 1174 RunGit(['config', '--unset', patchset_setting], |
| 1150 stderr=subprocess2.PIPE, error_ok=True) | 1175 stderr=subprocess2.PIPE, error_ok=True) |
| 1151 self.patchset = None | 1176 self.patchset = None |
| 1152 | 1177 |
| 1153 def GetMostRecentPatchset(self): | |
| 1154 return self.GetIssueProperties()['patchsets'][-1] | |
| 1155 | |
| 1156 def GetPatchSetDiff(self, issue, patchset): | |
| 1157 return self.RpcServer().get( | |
| 1158 '/download/issue%s_%s.diff' % (issue, patchset)) | |
| 1159 | |
| 1160 def GetIssueProperties(self): | |
| 1161 if self._props is None: | |
| 1162 issue = self.GetIssue() | |
| 1163 if not issue: | |
| 1164 self._props = {} | |
| 1165 else: | |
| 1166 self._props = self.RpcServer().get_issue_properties(issue, True) | |
| 1167 return self._props | |
| 1168 | |
| 1169 def GetApprovingReviewers(self): | |
| 1170 return get_approving_reviewers(self.GetIssueProperties()) | |
| 1171 | |
| 1172 def AddComment(self, message): | |
| 1173 return self.RpcServer().add_comment(self.GetIssue(), message) | |
| 1174 | |
| 1175 def SetIssue(self, issue=None): | 1178 def SetIssue(self, issue=None): |
| 1176 """Set this branch's issue. If issue isn't given, clears the issue.""" | 1179 """Set this branch's issue. If issue isn't given, clears the issue.""" |
| 1180 issue_setting = self._codereview_impl.IssueSetting() | |
| 1181 codereview_setting = self._codereview_impl.GetCodereviewServerSetting() | |
| 1177 if issue: | 1182 if issue: |
| 1178 self.issue = issue | 1183 self.issue = issue |
| 1179 RunGit(['config', self._IssueSetting(), str(issue)]) | 1184 RunGit(['config', issue_setting, str(issue)]) |
| 1180 if not settings.GetIsGerrit() and self.rietveld_server: | 1185 if self._codereview_impl.GetCodereviewServer(): |
|
Sergiy Byelozyorov
2016/03/21 17:17:06
nit: codereview_server = self._codereview_impl.Get
tandrii(chromium)
2016/03/22 17:41:52
Done.
| |
| 1181 RunGit(['config', self._RietveldServer(), self.rietveld_server]) | 1186 RunGit(['config', codereview_setting, |
| 1187 self._codereview_impl.GetCodereviewServer()]) | |
| 1182 else: | 1188 else: |
| 1183 current_issue = self.GetIssue() | 1189 current_issue = self.GetIssue() |
| 1184 if current_issue: | 1190 if current_issue: |
| 1185 RunGit(['config', '--unset', self._IssueSetting()]) | 1191 RunGit(['config', '--unset', issue_setting]) |
| 1186 self.issue = None | 1192 self.issue = None |
| 1187 self.SetPatchset(None) | 1193 self.SetPatchset(None) |
| 1188 | 1194 |
| 1189 def GetChange(self, upstream_branch, author): | 1195 def GetChange(self, upstream_branch, author): |
| 1190 if not self.GitSanityChecks(upstream_branch): | 1196 if not self.GitSanityChecks(upstream_branch): |
| 1191 DieWithError('\nGit sanity check failure') | 1197 DieWithError('\nGit sanity check failure') |
| 1192 | 1198 |
| 1193 root = settings.GetRelativeRoot() | 1199 root = settings.GetRelativeRoot() |
| 1194 if not root: | 1200 if not root: |
| 1195 root = '.' | 1201 root = '.' |
| (...skipping 29 matching lines...) Expand all Loading... | |
| 1225 return presubmit_support.GitChange( | 1231 return presubmit_support.GitChange( |
| 1226 name, | 1232 name, |
| 1227 description, | 1233 description, |
| 1228 absroot, | 1234 absroot, |
| 1229 files, | 1235 files, |
| 1230 issue, | 1236 issue, |
| 1231 patchset, | 1237 patchset, |
| 1232 author, | 1238 author, |
| 1233 upstream=upstream_branch) | 1239 upstream=upstream_branch) |
| 1234 | 1240 |
| 1241 def UpdateDescription(self, description): | |
| 1242 self.description = description | |
| 1243 return self._codereview_impl.UpdateDescriptionRemote(description) | |
| 1244 | |
| 1245 def RunHook(self, committing, may_prompt, verbose, change): | |
| 1246 """Calls sys.exit() if the hook fails; returns a HookResults otherwise.""" | |
| 1247 try: | |
| 1248 return presubmit_support.DoPresubmitChecks(change, committing, | |
| 1249 verbose=verbose, output_stream=sys.stdout, input_stream=sys.stdin, | |
| 1250 default_presubmit=None, may_prompt=may_prompt, | |
| 1251 rietveld_obj=self._codereview_impl.GetRieveldObjForPresubmit()) | |
| 1252 except presubmit_support.PresubmitFailure, e: | |
| 1253 DieWithError( | |
| 1254 ('%s\nMaybe your depot_tools is out of date?\n' | |
| 1255 'If all fails, contact maruel@') % e) | |
|
Sergiy Byelozyorov
2016/03/21 17:17:06
discussed this with tandrii offline: 'maruel@' was
tandrii(chromium)
2016/03/22 17:41:52
Acknowledged.
| |
| 1256 | |
| 1257 def __getattr__(self, attr): | |
| 1258 # Forward methods to codereview specific implementation. | |
|
Sergiy Byelozyorov
2016/03/21 17:17:06
I think this was intended to be above the def __ge
tandrii(chromium)
2016/03/22 17:41:52
Yes, though I still need specific comment for why
Sergiy Byelozyorov
2016/03/23 10:22:42
Acknowledged.
| |
| 1259 return getattr(self._codereview_impl, attr) | |
| 1260 | |
| 1261 def CloseIssue(self): | |
| 1262 return self._codereview_impl.CloseIssue() | |
| 1263 | |
| 1264 def GetStatus(self): | |
| 1265 return self._codereview_impl.GetStatus() | |
| 1266 | |
| 1267 def GetCodereviewServer(self): | |
| 1268 return self._codereview_impl.GetCodereviewServer() | |
| 1269 | |
| 1270 def GetApprovingReviewers(self): | |
| 1271 return self._codereview_impl.GetApprovingReviewers() | |
| 1272 | |
| 1273 def GetMostRecentPatchset(self): | |
| 1274 return self._codereview_impl.GetMostRecentPatchset() | |
| 1275 | |
| 1276 | |
| 1277 class _ChangelistCodereviewBase(object): | |
| 1278 """Abstract base class encapsulating codereview specifics of a changelist.""" | |
| 1279 def __init__(self, changelist): | |
| 1280 self._changelist = changelist # instance of Changelist | |
| 1281 | |
| 1282 def __getattr__(self, attr): | |
| 1283 # Forward methods to changelist. | |
| 1284 # TODO(tandrii): maybe clean up _GerritChangelistImpl and | |
| 1285 # _RietveldChangelistImpl to avoid this hack? | |
| 1286 return getattr(self._changelist, attr) | |
| 1287 | |
| 1235 def GetStatus(self): | 1288 def GetStatus(self): |
| 1236 """Apply a rough heuristic to give a simple summary of an issue's review | 1289 """Apply a rough heuristic to give a simple summary of an issue's review |
| 1237 or CQ status, assuming adherence to a common workflow. | 1290 or CQ status, assuming adherence to a common workflow. |
| 1291 | |
| 1292 Returns None if no issue for this branch, or specific string keywords. | |
| 1293 """ | |
| 1294 raise NotImplementedError() | |
| 1295 | |
| 1296 def GetCodereviewServer(self): | |
| 1297 """Returns server URL without end slash, like "https://codereview.com".""" | |
| 1298 raise NotImplementedError() | |
| 1299 | |
| 1300 def FetchDescription(self): | |
| 1301 """Fetches and returns description from the codereview server.""" | |
| 1302 raise NotImplementedError() | |
| 1303 | |
| 1304 def GetCodereviewServerSetting(self): | |
| 1305 """Returns git config setting for the codereview server.""" | |
| 1306 raise NotImplementedError() | |
| 1307 | |
| 1308 def IssueSetting(self): | |
| 1309 """Returns name of git config setting which stores issue number.""" | |
| 1310 raise NotImplementedError() | |
| 1311 | |
| 1312 def PatchsetSetting(self): | |
| 1313 """Returns name of git config setting which stores issue number.""" | |
| 1314 raise NotImplementedError() | |
| 1315 | |
| 1316 def GetRieveldObjForPresubmit(self): | |
|
Sergiy Byelozyorov
2016/03/21 17:17:06
Would be nice to have a doc here to know what kind
tandrii(chromium)
2016/03/22 17:41:52
Done.
| |
| 1317 # This is an unfortunate Rietveld-embeddedness in presubmit. | |
| 1318 raise NotImplementedError() | |
| 1319 | |
| 1320 def UpdateDescriptionRemote(self, description): | |
| 1321 """Update the description on codereview site.""" | |
| 1322 raise NotImplementedError() | |
| 1323 | |
| 1324 def CloseIssue(self): | |
| 1325 """Closes the issue.""" | |
| 1326 raise NotImplementedError() | |
| 1327 | |
| 1328 def GetApprovingReviewers(self): | |
| 1329 """Returns a list of reviewers approving the change. | |
| 1330 | |
| 1331 Note: not necessarily committers. | |
| 1332 """ | |
| 1333 raise NotImplementedError() | |
| 1334 | |
| 1335 def GetMostRecentPatchset(self): | |
| 1336 """Returns the most recent patchset number from the codereview site.""" | |
| 1337 raise NotImplementedError() | |
| 1338 | |
| 1339 | |
| 1340 class _RietveldChangelistImpl(_ChangelistCodereviewBase): | |
| 1341 def __init__(self, changelist, auth_config=None, rietveld_server=None): | |
| 1342 super(_RietveldChangelistImpl, self).__init__(changelist) | |
| 1343 assert settings | |
|
Sergiy Byelozyorov
2016/03/21 17:17:06
nit: please add
global settings
on the line befo
tandrii(chromium)
2016/03/22 17:41:52
good point.
| |
| 1344 settings.GetDefaultServerUrl() | |
| 1345 | |
| 1346 self._rietveld_server = rietveld_server | |
| 1347 self._auth_config = auth_config | |
| 1348 self._props = None | |
| 1349 self._rpc_server = None | |
| 1350 | |
| 1351 def GetAuthConfig(self): | |
| 1352 return self._auth_config | |
| 1353 | |
| 1354 def GetCodereviewServer(self): | |
| 1355 if not self._rietveld_server: | |
| 1356 # If we're on a branch then get the server potentially associated | |
| 1357 # with that branch. | |
| 1358 if self.GetIssue(): | |
| 1359 rietveld_server_setting = self.GetCodereviewServerSetting() | |
| 1360 if rietveld_server_setting: | |
| 1361 self._rietveld_server = gclient_utils.UpgradeToHttps(RunGit( | |
| 1362 ['config', rietveld_server_setting], error_ok=True).strip()) | |
| 1363 if not self._rietveld_server: | |
| 1364 self._rietveld_server = settings.GetDefaultServerUrl() | |
| 1365 return self._rietveld_server | |
| 1366 | |
| 1367 def FetchDescription(self): | |
| 1368 issue = self.GetIssue() | |
| 1369 assert issue | |
| 1370 try: | |
| 1371 return self.RpcServer().get_description(issue).strip() | |
| 1372 except urllib2.HTTPError as e: | |
| 1373 if e.code == 404: | |
| 1374 DieWithError( | |
| 1375 ('\nWhile fetching the description for issue %d, received a ' | |
| 1376 '404 (not found)\n' | |
|
Sergiy Byelozyorov
2016/03/21 17:17:06
nit: please remove \n everywhere a sentence contin
tandrii(chromium)
2016/03/22 17:41:52
see above - this seems to be the style here. And t
Sergiy Byelozyorov
2016/03/23 10:22:42
what a can of worms...
| |
| 1377 'error. It is likely that you deleted this ' | |
| 1378 'issue on the server. If this is the\n' | |
| 1379 'case, please run\n\n' | |
| 1380 ' git cl issue 0\n\n' | |
| 1381 'to clear the association with the deleted issue. Then run ' | |
| 1382 'this command again.') % issue) | |
| 1383 else: | |
| 1384 DieWithError( | |
| 1385 '\nFailed to fetch issue description. HTTP error %d' % e.code) | |
| 1386 except urllib2.URLError as e: | |
| 1387 print >> sys.stderr, ( | |
| 1388 'Warning: Failed to retrieve CL description due to network ' | |
| 1389 'failure.') | |
| 1390 return '' | |
| 1391 | |
| 1392 def GetMostRecentPatchset(self): | |
| 1393 return self.GetIssueProperties()['patchsets'][-1] | |
| 1394 | |
| 1395 def GetPatchSetDiff(self, issue, patchset): | |
| 1396 return self.RpcServer().get( | |
| 1397 '/download/issue%s_%s.diff' % (issue, patchset)) | |
| 1398 | |
| 1399 def GetIssueProperties(self): | |
| 1400 if self._props is None: | |
| 1401 issue = self.GetIssue() | |
| 1402 if not issue: | |
| 1403 self._props = {} | |
| 1404 else: | |
| 1405 self._props = self.RpcServer().get_issue_properties(issue, True) | |
| 1406 return self._props | |
| 1407 | |
| 1408 def GetApprovingReviewers(self): | |
| 1409 return get_approving_reviewers(self.GetIssueProperties()) | |
| 1410 | |
| 1411 def AddComment(self, message): | |
| 1412 return self.RpcServer().add_comment(self.GetIssue(), message) | |
| 1413 | |
| 1414 def GetStatus(self): | |
| 1415 """Apply a rough heuristic to give a simple summary of an issue's review | |
| 1416 or CQ status, assuming adherence to a common workflow. | |
| 1238 | 1417 |
| 1239 Returns None if no issue for this branch, or one of the following keywords: | 1418 Returns None if no issue for this branch, or one of the following keywords: |
| 1240 * 'error' - error from review tool (including deleted issues) | 1419 * 'error' - error from review tool (including deleted issues) |
| 1241 * 'unsent' - not sent for review | 1420 * 'unsent' - not sent for review |
| 1242 * 'waiting' - waiting for review | 1421 * 'waiting' - waiting for review |
| 1243 * 'reply' - waiting for owner to reply to review | 1422 * 'reply' - waiting for owner to reply to review |
| 1244 * 'lgtm' - LGTM from at least one approved reviewer | 1423 * 'lgtm' - LGTM from at least one approved reviewer |
| 1245 * 'commit' - in the commit queue | 1424 * 'commit' - in the commit queue |
| 1246 * 'closed' - closed | 1425 * 'closed' - closed |
| 1247 """ | 1426 """ |
| (...skipping 24 matching lines...) Expand all Loading... | |
| 1272 messages = props.get('messages') or [] | 1451 messages = props.get('messages') or [] |
| 1273 | 1452 |
| 1274 if not messages: | 1453 if not messages: |
| 1275 # No message was sent. | 1454 # No message was sent. |
| 1276 return 'unsent' | 1455 return 'unsent' |
| 1277 if messages[-1]['sender'] != props.get('owner_email'): | 1456 if messages[-1]['sender'] != props.get('owner_email'): |
| 1278 # Non-LGTM reply from non-owner | 1457 # Non-LGTM reply from non-owner |
| 1279 return 'reply' | 1458 return 'reply' |
| 1280 return 'waiting' | 1459 return 'waiting' |
| 1281 | 1460 |
| 1282 def RunHook(self, committing, may_prompt, verbose, change): | 1461 def UpdateDescriptionRemote(self, description): |
| 1283 """Calls sys.exit() if the hook fails; returns a HookResults otherwise.""" | |
| 1284 | |
| 1285 try: | |
| 1286 return presubmit_support.DoPresubmitChecks(change, committing, | |
| 1287 verbose=verbose, output_stream=sys.stdout, input_stream=sys.stdin, | |
| 1288 default_presubmit=None, may_prompt=may_prompt, | |
| 1289 rietveld_obj=self.RpcServer()) | |
| 1290 except presubmit_support.PresubmitFailure, e: | |
| 1291 DieWithError( | |
| 1292 ('%s\nMaybe your depot_tools is out of date?\n' | |
| 1293 'If all fails, contact maruel@') % e) | |
| 1294 | |
| 1295 def UpdateDescription(self, description): | |
| 1296 self.description = description | |
| 1297 return self.RpcServer().update_description( | 1462 return self.RpcServer().update_description( |
| 1298 self.GetIssue(), self.description) | 1463 self.GetIssue(), self.description) |
| 1299 | 1464 |
| 1300 def CloseIssue(self): | 1465 def CloseIssue(self): |
| 1301 """Updates the description and closes the issue.""" | |
| 1302 return self.RpcServer().close_issue(self.GetIssue()) | 1466 return self.RpcServer().close_issue(self.GetIssue()) |
| 1303 | 1467 |
| 1304 def SetFlag(self, flag, value): | 1468 def SetFlag(self, flag, value): |
| 1305 """Patchset must match.""" | 1469 """Patchset must match.""" |
| 1306 if not self.GetPatchset(): | 1470 if not self.GetPatchset(): |
| 1307 DieWithError('The patchset needs to match. Send another patchset.') | 1471 DieWithError('The patchset needs to match. Send another patchset.') |
| 1308 try: | 1472 try: |
| 1309 return self.RpcServer().set_flag( | 1473 return self.RpcServer().set_flag( |
| 1310 self.GetIssue(), self.GetPatchset(), flag, value) | 1474 self.GetIssue(), self.GetPatchset(), flag, value) |
| 1311 except urllib2.HTTPError, e: | 1475 except urllib2.HTTPError, e: |
| 1312 if e.code == 404: | 1476 if e.code == 404: |
| 1313 DieWithError('The issue %s doesn\'t exist.' % self.GetIssue()) | 1477 DieWithError('The issue %s doesn\'t exist.' % self.GetIssue()) |
| 1314 if e.code == 403: | 1478 if e.code == 403: |
| 1315 DieWithError( | 1479 DieWithError( |
| 1316 ('Access denied to issue %s. Maybe the patchset %s doesn\'t ' | 1480 ('Access denied to issue %s. Maybe the patchset %s doesn\'t ' |
| 1317 'match?') % (self.GetIssue(), self.GetPatchset())) | 1481 'match?') % (self.GetIssue(), self.GetPatchset())) |
| 1318 raise | 1482 raise |
| 1319 | 1483 |
| 1320 def RpcServer(self): | 1484 def RpcServer(self): |
| 1321 """Returns an upload.RpcServer() to access this review's rietveld instance. | 1485 """Returns an upload.RpcServer() to access this review's rietveld instance. |
| 1322 """ | 1486 """ |
| 1323 if not self._rpc_server: | 1487 if not self._rpc_server: |
| 1324 self._rpc_server = rietveld.CachingRietveld( | 1488 self._rpc_server = rietveld.CachingRietveld( |
| 1325 self.GetRietveldServer(), | 1489 self.GetCodereviewServer(), |
| 1326 self._auth_config or auth.make_auth_config()) | 1490 self._auth_config or auth.make_auth_config()) |
| 1327 return self._rpc_server | 1491 return self._rpc_server |
| 1328 | 1492 |
| 1329 def _IssueSetting(self): | 1493 def IssueSetting(self): |
| 1330 """Return the git setting that stores this change's issue.""" | 1494 """Return the git setting that stores this change's issue.""" |
| 1331 return 'branch.%s.rietveldissue' % self.GetBranch() | 1495 return 'branch.%s.rietveldissue' % self.GetBranch() |
| 1332 | 1496 |
| 1333 def _PatchsetSetting(self): | 1497 def PatchsetSetting(self): |
| 1334 """Return the git setting that stores this change's most recent patchset.""" | 1498 """Return the git setting that stores this change's most recent patchset.""" |
| 1335 return 'branch.%s.rietveldpatchset' % self.GetBranch() | 1499 return 'branch.%s.rietveldpatchset' % self.GetBranch() |
| 1336 | 1500 |
| 1337 def _RietveldServer(self): | 1501 def GetCodereviewServerSetting(self): |
|
Sergiy Byelozyorov
2016/03/21 17:17:06
OMG, I would never have guessed what this function
| |
| 1338 """Returns the git setting that stores this change's rietveld server.""" | 1502 """Returns the git setting that stores this change's rietveld server.""" |
| 1339 branch = self.GetBranch() | 1503 branch = self.GetBranch() |
| 1340 if branch: | 1504 if branch: |
| 1341 return 'branch.%s.rietveldserver' % branch | 1505 return 'branch.%s.rietveldserver' % branch |
| 1342 return None | 1506 return None |
| 1343 | 1507 |
| 1508 def GetRieveldObjForPresubmit(self): | |
| 1509 return self.RpcServer() | |
| 1510 | |
| 1511 | |
| 1512 class _GerritChangelistImpl(_ChangelistCodereviewBase): | |
| 1513 def __init__(self, changelist, auth_config=None): | |
| 1514 super(_GerritChangelistImpl, self).__init__(changelist) | |
| 1515 self._change_id = None | |
| 1516 self._gerrit_server = None | |
| 1517 | |
| 1518 def GetCodereviewServer(self): | |
| 1519 if not self._gerrit_server: | |
| 1520 # If we're on a branch then get the server potentially associated | |
| 1521 # with that branch. | |
| 1522 if self.GetIssue(): | |
| 1523 gerrit_server_setting = self.GetCodereviewServerSetting() | |
| 1524 if gerrit_server_setting: | |
| 1525 self._gerrit_server = RunGit(['config', gerrit_server_setting], | |
| 1526 error_ok=True).strip() | |
| 1527 if not self._gerrit_server: | |
| 1528 # We assume repo to be hosted on Gerrit, and hence Gerrit server | |
| 1529 # has "-review" suffix for lowest level subdomain. | |
| 1530 parts = urlparse.urlparse(self.GetRemoteUrl()).netloc.split('.') | |
| 1531 parts[0] = parts[0] + '-review' | |
| 1532 self._gerrit_server = 'https://%s' % '.'.join(parts) | |
| 1533 return self._gerrit_server | |
| 1534 | |
| 1535 def IssueSetting(self): | |
| 1536 """Return the git setting that stores this change's issue.""" | |
| 1537 return 'branch.%s.gerritissue' % self.GetBranch() | |
| 1538 | |
| 1539 def PatchsetSetting(self): | |
| 1540 """Return the git setting that stores this change's most recent patchset.""" | |
| 1541 return 'branch.%s.gerritpatchset' % self.GetBranch() | |
| 1542 | |
| 1543 def GetCodereviewServerSetting(self): | |
| 1544 """Returns the git setting that stores this change's Gerrit server.""" | |
| 1545 branch = self.GetBranch() | |
| 1546 if branch: | |
| 1547 return 'branch.%s.gerritserver' % branch | |
| 1548 return None | |
| 1549 | |
| 1550 def GetRieveldObjForPresubmit(self): | |
| 1551 class ThisIsNotRietveldIssue(object): | |
| 1552 def __getattr__(self, attr): | |
| 1553 def handler(*_, **__): | |
| 1554 print('You aren\'t using Rietveld at the moment, but Gerrit.\n' | |
| 1555 'Using Rietveld in your PRESUBMIT scripts won\'t work.\n' | |
| 1556 'Either change your PRESUBIT to not use rietveld_obj.%s,\n' | |
| 1557 'or use Rietveld for codereview.\n' % attr) | |
| 1558 raise NotImplementedError() | |
| 1559 return handler | |
| 1560 return ThisIsNotRietveldIssue() | |
| 1561 | |
| 1562 def GetStatus(self): | |
| 1563 raise NotImplementedError() | |
| 1564 | |
| 1565 def FetchDescription(self): | |
| 1566 raise NotImplementedError() | |
| 1567 | |
| 1568 def UpdateDescriptionRemote(self, description): | |
| 1569 raise NotImplementedError() | |
| 1570 | |
| 1571 def CloseIssue(self): | |
| 1572 raise NotImplementedError() | |
| 1573 | |
| 1344 | 1574 |
| 1345 class ChangeDescription(object): | 1575 class ChangeDescription(object): |
| 1346 """Contains a parsed form of the change description.""" | 1576 """Contains a parsed form of the change description.""" |
| 1347 R_LINE = r'^[ \t]*(TBR|R)[ \t]*=[ \t]*(.*?)[ \t]*$' | 1577 R_LINE = r'^[ \t]*(TBR|R)[ \t]*=[ \t]*(.*?)[ \t]*$' |
| 1348 BUG_LINE = r'^[ \t]*(BUG)[ \t]*=[ \t]*(.*?)[ \t]*$' | 1578 BUG_LINE = r'^[ \t]*(BUG)[ \t]*=[ \t]*(.*?)[ \t]*$' |
| 1349 | 1579 |
| 1350 def __init__(self, description): | 1580 def __init__(self, description): |
| 1351 self._description_lines = (description or '').strip().splitlines() | 1581 self._description_lines = (description or '').strip().splitlines() |
| 1352 | 1582 |
| 1353 @property # www.logilab.org/ticket/89786 | 1583 @property # www.logilab.org/ticket/89786 |
| (...skipping 525 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1879 return 0 | 2109 return 0 |
| 1880 | 2110 |
| 1881 branches = RunGit(['for-each-ref', '--format=%(refname)', 'refs/heads']) | 2111 branches = RunGit(['for-each-ref', '--format=%(refname)', 'refs/heads']) |
| 1882 if not branches: | 2112 if not branches: |
| 1883 print('No local branch found.') | 2113 print('No local branch found.') |
| 1884 return 0 | 2114 return 0 |
| 1885 | 2115 |
| 1886 changes = ( | 2116 changes = ( |
| 1887 Changelist(branchref=b, auth_config=auth_config) | 2117 Changelist(branchref=b, auth_config=auth_config) |
| 1888 for b in branches.splitlines()) | 2118 for b in branches.splitlines()) |
| 2119 # TODO(tandrii): refactor to use CLs list instead of branches list. | |
|
Sergiy Byelozyorov
2016/03/21 17:17:06
Why? Sometimes I use 'git cl status' to see if a b
tandrii(chromium)
2016/03/22 17:41:52
see line 2111 - there is a list of branches.
then
Sergiy Byelozyorov
2016/03/23 10:22:42
Acknowledged.
| |
| 1889 branches = [c.GetBranch() for c in changes] | 2120 branches = [c.GetBranch() for c in changes] |
| 1890 alignment = max(5, max(len(b) for b in branches)) | 2121 alignment = max(5, max(len(b) for b in branches)) |
| 1891 print 'Branches associated with reviews:' | 2122 print 'Branches associated with reviews:' |
| 1892 output = get_cl_statuses(branches, | 2123 output = get_cl_statuses(branches, |
| 1893 fine_grained=not options.fast, | 2124 fine_grained=not options.fast, |
| 1894 max_processes=options.maxjobs, | 2125 max_processes=options.maxjobs, |
| 1895 auth_config=auth_config) | 2126 auth_config=auth_config) |
| 1896 | 2127 |
| 1897 branch_statuses = {} | 2128 branch_statuses = {} |
| 1898 alignment = max(5, max(len(ShortBranchName(b)) for b in branches)) | 2129 alignment = max(5, max(len(ShortBranchName(b)) for b in branches)) |
| (...skipping 95 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1994 options, args = parser.parse_args(args) | 2225 options, args = parser.parse_args(args) |
| 1995 auth_config = auth.extract_auth_config_from_options(options) | 2226 auth_config = auth.extract_auth_config_from_options(options) |
| 1996 | 2227 |
| 1997 issue = None | 2228 issue = None |
| 1998 if options.issue: | 2229 if options.issue: |
| 1999 try: | 2230 try: |
| 2000 issue = int(options.issue) | 2231 issue = int(options.issue) |
| 2001 except ValueError: | 2232 except ValueError: |
| 2002 DieWithError('A review issue id is expected to be a number') | 2233 DieWithError('A review issue id is expected to be a number') |
| 2003 | 2234 |
| 2004 cl = Changelist(issue=issue, auth_config=auth_config) | 2235 cl = Changelist(issue=issue, codereview='rietveld', auth_config=auth_config) |
| 2005 | 2236 |
| 2006 if options.comment: | 2237 if options.comment: |
| 2007 cl.AddComment(options.comment) | 2238 cl.AddComment(options.comment) |
| 2008 return 0 | 2239 return 0 |
| 2009 | 2240 |
| 2010 data = cl.GetIssueProperties() | 2241 data = cl.GetIssueProperties() |
| 2011 summary = [] | 2242 summary = [] |
| 2012 for message in sorted(data.get('messages', []), key=lambda x: x['date']): | 2243 for message in sorted(data.get('messages', []), key=lambda x: x['date']): |
| 2013 summary.append({ | 2244 summary.append({ |
| 2014 'date': message['date'], | 2245 'date': message['date'], |
| (...skipping 358 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 2373 remote_branch = remote_branch.replace('refs/remotes/', 'refs/') | 2604 remote_branch = remote_branch.replace('refs/remotes/', 'refs/') |
| 2374 # If a pending prefix exists then replace refs/ with it. | 2605 # If a pending prefix exists then replace refs/ with it. |
| 2375 if pending_prefix: | 2606 if pending_prefix: |
| 2376 remote_branch = remote_branch.replace('refs/', pending_prefix) | 2607 remote_branch = remote_branch.replace('refs/', pending_prefix) |
| 2377 return remote_branch | 2608 return remote_branch |
| 2378 | 2609 |
| 2379 | 2610 |
| 2380 def RietveldUpload(options, args, cl, change): | 2611 def RietveldUpload(options, args, cl, change): |
| 2381 """upload the patch to rietveld.""" | 2612 """upload the patch to rietveld.""" |
| 2382 upload_args = ['--assume_yes'] # Don't ask about untracked files. | 2613 upload_args = ['--assume_yes'] # Don't ask about untracked files. |
| 2383 upload_args.extend(['--server', cl.GetRietveldServer()]) | 2614 upload_args.extend(['--server', cl.GetCodereviewServer()]) |
| 2384 upload_args.extend(auth.auth_config_to_command_options(cl.auth_config)) | 2615 upload_args.extend(auth.auth_config_to_command_options( |
| 2616 cl._codereview_impl.GetAuthConfig())) | |
|
Sergiy Byelozyorov
2016/03/21 17:17:06
Since this is also Rietveld-specific and accesses
tandrii(chromium)
2016/03/22 17:41:52
that's exactly what I intended to do! But not in t
Sergiy Byelozyorov
2016/03/23 10:22:42
Acknowledged.
| |
| 2385 if options.emulate_svn_auto_props: | 2617 if options.emulate_svn_auto_props: |
| 2386 upload_args.append('--emulate_svn_auto_props') | 2618 upload_args.append('--emulate_svn_auto_props') |
| 2387 | 2619 |
| 2388 change_desc = None | 2620 change_desc = None |
| 2389 | 2621 |
| 2390 if options.email is not None: | 2622 if options.email is not None: |
| 2391 upload_args.extend(['--email', options.email]) | 2623 upload_args.extend(['--email', options.email]) |
| 2392 | 2624 |
| 2393 if cl.GetIssue(): | 2625 if cl.GetIssue(): |
| 2394 if options.title: | 2626 if options.title: |
| (...skipping 222 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 2617 | 2849 |
| 2618 # Default to diffing against common ancestor of upstream branch | 2850 # Default to diffing against common ancestor of upstream branch |
| 2619 base_branch = cl.GetCommonAncestorWithUpstream() | 2851 base_branch = cl.GetCommonAncestorWithUpstream() |
| 2620 args = [base_branch, 'HEAD'] | 2852 args = [base_branch, 'HEAD'] |
| 2621 | 2853 |
| 2622 # Make sure authenticated to Rietveld before running expensive hooks. It is | 2854 # Make sure authenticated to Rietveld before running expensive hooks. It is |
| 2623 # a fast, best efforts check. Rietveld still can reject the authentication | 2855 # a fast, best efforts check. Rietveld still can reject the authentication |
| 2624 # during the actual upload. | 2856 # during the actual upload. |
| 2625 if not settings.GetIsGerrit() and auth_config.use_oauth2: | 2857 if not settings.GetIsGerrit() and auth_config.use_oauth2: |
| 2626 authenticator = auth.get_authenticator_for_host( | 2858 authenticator = auth.get_authenticator_for_host( |
| 2627 cl.GetRietveldServer(), auth_config) | 2859 cl.GetCodereviewServer(), auth_config) |
| 2628 if not authenticator.has_cached_credentials(): | 2860 if not authenticator.has_cached_credentials(): |
| 2629 raise auth.LoginRequiredError(cl.GetRietveldServer()) | 2861 raise auth.LoginRequiredError(cl.GetCodereviewServer()) |
| 2630 | 2862 |
| 2631 # Apply watchlists on upload. | 2863 # Apply watchlists on upload. |
| 2632 change = cl.GetChange(base_branch, None) | 2864 change = cl.GetChange(base_branch, None) |
| 2633 watchlist = watchlists.Watchlists(change.RepositoryRoot()) | 2865 watchlist = watchlists.Watchlists(change.RepositoryRoot()) |
| 2634 files = [f.LocalPath() for f in change.AffectedFiles()] | 2866 files = [f.LocalPath() for f in change.AffectedFiles()] |
| 2635 if not options.bypass_watchlists: | 2867 if not options.bypass_watchlists: |
| 2636 cl.SetWatchers(watchlist.GetWatchersForPaths(files)) | 2868 cl.SetWatchers(watchlist.GetWatchersForPaths(files)) |
| 2637 | 2869 |
| 2638 if not options.bypass_hooks: | 2870 if not options.bypass_hooks: |
| 2639 if options.reviewers or options.tbr_owners: | 2871 if options.reviewers or options.tbr_owners: |
| (...skipping 573 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 3213 return PatchIssue(issue_arg, options.reject, options.nocommit, | 3445 return PatchIssue(issue_arg, options.reject, options.nocommit, |
| 3214 options.directory, auth_config) | 3446 options.directory, auth_config) |
| 3215 | 3447 |
| 3216 | 3448 |
| 3217 def PatchIssue(issue_arg, reject, nocommit, directory, auth_config): | 3449 def PatchIssue(issue_arg, reject, nocommit, directory, auth_config): |
| 3218 # PatchIssue should never be called with a dirty tree. It is up to the | 3450 # PatchIssue should never be called with a dirty tree. It is up to the |
| 3219 # caller to check this, but just in case we assert here since the | 3451 # caller to check this, but just in case we assert here since the |
| 3220 # consequences of the caller not checking this could be dire. | 3452 # consequences of the caller not checking this could be dire. |
| 3221 assert(not git_common.is_dirty_git_tree('apply')) | 3453 assert(not git_common.is_dirty_git_tree('apply')) |
| 3222 | 3454 |
| 3455 # TODO(tandrii): implement for Gerrit. | |
| 3223 if type(issue_arg) is int or issue_arg.isdigit(): | 3456 if type(issue_arg) is int or issue_arg.isdigit(): |
| 3224 # Input is an issue id. Figure out the URL. | 3457 # Input is an issue id. Figure out the URL. |
| 3225 issue = int(issue_arg) | 3458 issue = int(issue_arg) |
| 3226 cl = Changelist(issue=issue, auth_config=auth_config) | 3459 cl = Changelist(issue=issue, codereview='rietveld', auth_config=auth_config) |
| 3227 patchset = cl.GetMostRecentPatchset() | 3460 patchset = cl.GetMostRecentPatchset() |
| 3228 patch_data = cl.GetPatchSetDiff(issue, patchset) | 3461 patch_data = cl._codereview_impl.GetPatchSetDiff(issue, patchset) |
| 3229 else: | 3462 else: |
| 3230 # Assume it's a URL to the patch. Default to https. | 3463 # Assume it's a URL to the patch. Default to https. |
| 3231 issue_url = gclient_utils.UpgradeToHttps(issue_arg) | 3464 issue_url = gclient_utils.UpgradeToHttps(issue_arg) |
| 3232 match = re.match(r'(.*?)/download/issue(\d+)_(\d+).diff', issue_url) | 3465 match = re.match(r'(.*?)/download/issue(\d+)_(\d+).diff', issue_url) |
| 3233 if not match: | 3466 if not match: |
| 3234 DieWithError('Must pass an issue ID or full URL for ' | 3467 DieWithError('Must pass an issue ID or full URL for ' |
| 3235 '\'Download raw patch set\'') | 3468 '\'Download raw patch set\'') |
| 3236 issue = int(match.group(2)) | 3469 issue = int(match.group(2)) |
| 3237 cl = Changelist(issue=issue, auth_config=auth_config) | 3470 cl = Changelist(issue=issue, codereview='rietveld', |
| 3238 cl.rietveld_server = match.group(1) | 3471 rietvled_server=match.group(1), auth_config=auth_config) |
| 3239 patchset = int(match.group(3)) | 3472 patchset = int(match.group(3)) |
| 3240 patch_data = urllib2.urlopen(issue_arg).read() | 3473 patch_data = urllib2.urlopen(issue_arg).read() |
| 3241 | 3474 |
| 3242 # Switch up to the top-level directory, if necessary, in preparation for | 3475 # Switch up to the top-level directory, if necessary, in preparation for |
| 3243 # applying the patch. | 3476 # applying the patch. |
| 3244 top = settings.GetRelativeRoot() | 3477 top = settings.GetRelativeRoot() |
| 3245 if top: | 3478 if top: |
| 3246 os.chdir(top) | 3479 os.chdir(top) |
| 3247 | 3480 |
| 3248 # Git patches have a/ at the beginning of source paths. We strip that out | 3481 # Git patches have a/ at the beginning of source paths. We strip that out |
| (...skipping 23 matching lines...) Expand all Loading... | |
| 3272 except subprocess2.CalledProcessError: | 3505 except subprocess2.CalledProcessError: |
| 3273 print 'Failed to apply the patch' | 3506 print 'Failed to apply the patch' |
| 3274 return 1 | 3507 return 1 |
| 3275 | 3508 |
| 3276 # If we had an issue, commit the current state and register the issue. | 3509 # If we had an issue, commit the current state and register the issue. |
| 3277 if not nocommit: | 3510 if not nocommit: |
| 3278 RunGit(['commit', '-m', (cl.GetDescription() + '\n\n' + | 3511 RunGit(['commit', '-m', (cl.GetDescription() + '\n\n' + |
| 3279 'patch from issue %(i)s at patchset ' | 3512 'patch from issue %(i)s at patchset ' |
| 3280 '%(p)s (http://crrev.com/%(i)s#ps%(p)s)' | 3513 '%(p)s (http://crrev.com/%(i)s#ps%(p)s)' |
| 3281 % {'i': issue, 'p': patchset})]) | 3514 % {'i': issue, 'p': patchset})]) |
| 3282 cl = Changelist(auth_config=auth_config) | 3515 cl = Changelist(codereview='rietveld', auth_config=auth_config, |
| 3516 rietveld_server=cl.GetCodereviewServer()) | |
| 3283 cl.SetIssue(issue) | 3517 cl.SetIssue(issue) |
| 3284 cl.SetPatchset(patchset) | 3518 cl.SetPatchset(patchset) |
| 3285 print "Committed patch locally." | 3519 print "Committed patch locally." |
| 3286 else: | 3520 else: |
| 3287 print "Patch applied to index." | 3521 print "Patch applied to index." |
| 3288 return 0 | 3522 return 0 |
| 3289 | 3523 |
| 3290 | 3524 |
| 3291 def CMDrebase(parser, args): | 3525 def CMDrebase(parser, args): |
| 3292 """Rebases current branch on top of svn repo.""" | 3526 """Rebases current branch on top of svn repo.""" |
| (...skipping 714 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 4007 if __name__ == '__main__': | 4241 if __name__ == '__main__': |
| 4008 # These affect sys.stdout so do it outside of main() to simplify mocks in | 4242 # These affect sys.stdout so do it outside of main() to simplify mocks in |
| 4009 # unit testing. | 4243 # unit testing. |
| 4010 fix_encoding.fix_encoding() | 4244 fix_encoding.fix_encoding() |
| 4011 colorama.init() | 4245 colorama.init() |
| 4012 try: | 4246 try: |
| 4013 sys.exit(main(sys.argv[1:])) | 4247 sys.exit(main(sys.argv[1:])) |
| 4014 except KeyboardInterrupt: | 4248 except KeyboardInterrupt: |
| 4015 sys.stderr.write('interrupted\n') | 4249 sys.stderr.write('interrupted\n') |
| 4016 sys.exit(1) | 4250 sys.exit(1) |
| OLD | NEW |