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 21 matching lines...) Expand all Loading... |
40 from third_party import colorama | 40 from third_party import colorama |
41 from third_party import httplib2 | 41 from third_party import httplib2 |
42 from third_party import upload | 42 from third_party import upload |
43 import auth | 43 import auth |
44 from luci_hacks import trigger_luci_job as luci_trigger | 44 from luci_hacks import trigger_luci_job as luci_trigger |
45 import clang_format | 45 import clang_format |
46 import commit_queue | 46 import commit_queue |
47 import dart_format | 47 import dart_format |
48 import fix_encoding | 48 import fix_encoding |
49 import gclient_utils | 49 import gclient_utils |
| 50 import gerrit_util |
50 import git_cache | 51 import git_cache |
51 import git_common | 52 import git_common |
52 import git_footers | 53 import git_footers |
53 import owners | 54 import owners |
54 import owners_finder | 55 import owners_finder |
55 import presubmit_support | 56 import presubmit_support |
56 import rietveld | 57 import rietveld |
57 import scm | 58 import scm |
58 import subcommand | 59 import subcommand |
59 import subprocess2 | 60 import subprocess2 |
(...skipping 89 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
149 | 150 |
150 def ask_for_data(prompt): | 151 def ask_for_data(prompt): |
151 try: | 152 try: |
152 return raw_input(prompt) | 153 return raw_input(prompt) |
153 except KeyboardInterrupt: | 154 except KeyboardInterrupt: |
154 # Hide the exception. | 155 # Hide the exception. |
155 sys.exit(1) | 156 sys.exit(1) |
156 | 157 |
157 | 158 |
158 def git_set_branch_value(key, value): | 159 def git_set_branch_value(key, value): |
159 branch = Changelist().GetBranch() | 160 branch = GetCurrentBranch() |
160 if not branch: | 161 if not branch: |
161 return | 162 return |
162 | 163 |
163 cmd = ['config'] | 164 cmd = ['config'] |
164 if isinstance(value, int): | 165 if isinstance(value, int): |
165 cmd.append('--int') | 166 cmd.append('--int') |
166 git_key = 'branch.%s.%s' % (branch, key) | 167 git_key = 'branch.%s.%s' % (branch, key) |
167 RunGit(cmd + [git_key, str(value)]) | 168 RunGit(cmd + [git_key, str(value)]) |
168 | 169 |
169 | 170 |
170 def git_get_branch_default(key, default): | 171 def git_get_branch_default(key, default): |
171 branch = Changelist().GetBranch() | 172 branch = GetCurrentBranch() |
172 if branch: | 173 if branch: |
173 git_key = 'branch.%s.%s' % (branch, key) | 174 git_key = 'branch.%s.%s' % (branch, key) |
174 (_, stdout) = RunGitWithCode(['config', '--int', '--get', git_key]) | 175 (_, stdout) = RunGitWithCode(['config', '--int', '--get', git_key]) |
175 try: | 176 try: |
176 return int(stdout.strip()) | 177 return int(stdout.strip()) |
177 except ValueError: | 178 except ValueError: |
178 pass | 179 pass |
179 return default | 180 return default |
180 | 181 |
181 | 182 |
(...skipping 624 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
806 def _GetBranchConfig(self, branch_name, param, **kwargs): | 807 def _GetBranchConfig(self, branch_name, param, **kwargs): |
807 return self._GetConfig('branch.' + branch_name + '.' + param, **kwargs) | 808 return self._GetConfig('branch.' + branch_name + '.' + param, **kwargs) |
808 | 809 |
809 def _GetConfig(self, param, **kwargs): | 810 def _GetConfig(self, param, **kwargs): |
810 self.LazyUpdateIfNeeded() | 811 self.LazyUpdateIfNeeded() |
811 return RunGit(['config', param], **kwargs).strip() | 812 return RunGit(['config', param], **kwargs).strip() |
812 | 813 |
813 | 814 |
814 def ShortBranchName(branch): | 815 def ShortBranchName(branch): |
815 """Convert a name like 'refs/heads/foo' to just 'foo'.""" | 816 """Convert a name like 'refs/heads/foo' to just 'foo'.""" |
816 return branch.replace('refs/heads/', '') | 817 return branch.replace('refs/heads/', '', 1) |
| 818 |
| 819 |
| 820 def GetCurrentBranchRef(): |
| 821 """Returns branch ref (e.g., refs/heads/master) or None.""" |
| 822 return RunGit(['symbolic-ref', 'HEAD'], |
| 823 stderr=subprocess2.VOID, error_ok=True).strip() or None |
| 824 |
| 825 |
| 826 def GetCurrentBranch(): |
| 827 """Returns current branch or None. |
| 828 |
| 829 For refs/heads/* branches, returns just last part. For others, full ref. |
| 830 """ |
| 831 branchref = GetCurrentBranchRef() |
| 832 if branchref: |
| 833 return ShortBranchName(branchref) |
| 834 return None |
817 | 835 |
818 | 836 |
819 class Changelist(object): | 837 class Changelist(object): |
820 def __init__(self, branchref=None, issue=None, auth_config=None): | 838 """Changelist works with one changelist in local branch. |
| 839 |
| 840 Supports two codereview backends: Rietveld or Gerrit, selected at object |
| 841 creation. |
| 842 |
| 843 Not safe for concurrent multi-{thread,process} use. |
| 844 """ |
| 845 |
| 846 def __init__(self, branchref=None, issue=None, codereview=None, **kwargs): |
| 847 """Create a new ChangeList instance. |
| 848 |
| 849 If issue is given, the codereview must be given too. |
| 850 |
| 851 If `codereview` is given, it must be 'rietveld' or 'gerrit'. |
| 852 Otherwise, it's decided based on current configuration of the local branch, |
| 853 with default being 'rietveld' for backwards compatibility. |
| 854 See _load_codereview_impl for more details. |
| 855 |
| 856 **kwargs will be passed directly to codereview implementation. |
| 857 """ |
821 # Poke settings so we get the "configure your server" message if necessary. | 858 # Poke settings so we get the "configure your server" message if necessary. |
822 global settings | 859 global settings |
823 if not settings: | 860 if not settings: |
824 # Happens when git_cl.py is used as a utility library. | 861 # Happens when git_cl.py is used as a utility library. |
825 settings = Settings() | 862 settings = Settings() |
826 settings.GetDefaultServerUrl() | 863 |
| 864 if issue: |
| 865 assert codereview, 'codereview must be known, if issue is known' |
| 866 |
827 self.branchref = branchref | 867 self.branchref = branchref |
828 if self.branchref: | 868 if self.branchref: |
829 self.branch = ShortBranchName(self.branchref) | 869 self.branch = ShortBranchName(self.branchref) |
830 else: | 870 else: |
831 self.branch = None | 871 self.branch = None |
832 self.rietveld_server = None | |
833 self.upstream_branch = None | 872 self.upstream_branch = None |
834 self.lookedup_issue = False | 873 self.lookedup_issue = False |
835 self.issue = issue or None | 874 self.issue = issue or None |
836 self.has_description = False | 875 self.has_description = False |
837 self.description = None | 876 self.description = None |
838 self.lookedup_patchset = False | 877 self.lookedup_patchset = False |
839 self.patchset = None | 878 self.patchset = None |
840 self.cc = None | 879 self.cc = None |
841 self.watchers = () | 880 self.watchers = () |
842 self._auth_config = auth_config | |
843 self._props = None | |
844 self._remote = None | 881 self._remote = None |
845 self._rpc_server = None | |
846 | 882 |
847 @property | 883 self._codereview_impl = None |
848 def auth_config(self): | 884 self._load_codereview_impl(codereview, **kwargs) |
849 return self._auth_config | 885 |
| 886 def _load_codereview_impl(self, codereview=None, **kwargs): |
| 887 if codereview: |
| 888 codereview = codereview.lower() |
| 889 if codereview == 'gerrit': |
| 890 self._codereview_impl = _GerritChangelistImpl(self, **kwargs) |
| 891 elif codereview == 'rietveld': |
| 892 self._codereview_impl = _RietveldChangelistImpl(self, **kwargs) |
| 893 else: |
| 894 assert codereview in ('rietveld', 'gerrit') |
| 895 return |
| 896 |
| 897 # Automatic selection based on issue number set for a current branch. |
| 898 # Rietveld takes precedence over Gerrit. |
| 899 assert not self.issue |
| 900 # Whether we find issue or not, we are doing the lookup. |
| 901 self.lookedup_issue = True |
| 902 for cls in [_RietveldChangelistImpl, _GerritChangelistImpl]: |
| 903 setting = cls.IssueSetting(self.GetBranch()) |
| 904 issue = RunGit(['config', setting], error_ok=True).strip() |
| 905 if issue: |
| 906 self._codereview_impl = cls(self, **kwargs) |
| 907 self.issue = int(issue) |
| 908 return |
| 909 |
| 910 # No issue is set for this branch, so decide based on repo-wide settings. |
| 911 return self._load_codereview_impl( |
| 912 codereview='gerrit' if settings.GetIsGerrit() else 'rietveld', |
| 913 **kwargs) |
| 914 |
850 | 915 |
851 def GetCCList(self): | 916 def GetCCList(self): |
852 """Return the users cc'd on this CL. | 917 """Return the users cc'd on this CL. |
853 | 918 |
854 Return is a string suitable for passing to gcl with the --cc flag. | 919 Return is a string suitable for passing to gcl with the --cc flag. |
855 """ | 920 """ |
856 if self.cc is None: | 921 if self.cc is None: |
857 base_cc = settings.GetDefaultCCList() | 922 base_cc = settings.GetDefaultCCList() |
858 more_cc = ','.join(self.watchers) | 923 more_cc = ','.join(self.watchers) |
859 self.cc = ','.join(filter(None, (base_cc, more_cc))) or '' | 924 self.cc = ','.join(filter(None, (base_cc, more_cc))) or '' |
860 return self.cc | 925 return self.cc |
861 | 926 |
862 def GetCCListWithoutDefault(self): | 927 def GetCCListWithoutDefault(self): |
863 """Return the users cc'd on this CL excluding default ones.""" | 928 """Return the users cc'd on this CL excluding default ones.""" |
864 if self.cc is None: | 929 if self.cc is None: |
865 self.cc = ','.join(self.watchers) | 930 self.cc = ','.join(self.watchers) |
866 return self.cc | 931 return self.cc |
867 | 932 |
868 def SetWatchers(self, watchers): | 933 def SetWatchers(self, watchers): |
869 """Set the list of email addresses that should be cc'd based on the changed | 934 """Set the list of email addresses that should be cc'd based on the changed |
870 files in this CL. | 935 files in this CL. |
871 """ | 936 """ |
872 self.watchers = watchers | 937 self.watchers = watchers |
873 | 938 |
874 def GetBranch(self): | 939 def GetBranch(self): |
875 """Returns the short branch name, e.g. 'master'.""" | 940 """Returns the short branch name, e.g. 'master'.""" |
876 if not self.branch: | 941 if not self.branch: |
877 branchref = RunGit(['symbolic-ref', 'HEAD'], | 942 branchref = GetCurrentBranchRef() |
878 stderr=subprocess2.VOID, error_ok=True).strip() | |
879 if not branchref: | 943 if not branchref: |
880 return None | 944 return None |
881 self.branchref = branchref | 945 self.branchref = branchref |
882 self.branch = ShortBranchName(self.branchref) | 946 self.branch = ShortBranchName(self.branchref) |
883 return self.branch | 947 return self.branch |
884 | 948 |
885 def GetBranchRef(self): | 949 def GetBranchRef(self): |
886 """Returns the full branch name, e.g. 'refs/heads/master'.""" | 950 """Returns the full branch name, e.g. 'refs/heads/master'.""" |
887 self.GetBranch() # Poke the lazy loader. | 951 self.GetBranch() # Poke the lazy loader. |
888 return self.branchref | 952 return self.branchref |
(...skipping 23 matching lines...) Expand all Loading... |
912 if 'origin/master' in remote_branches: | 976 if 'origin/master' in remote_branches: |
913 # Fall back on origin/master if it exits. | 977 # Fall back on origin/master if it exits. |
914 remote = 'origin' | 978 remote = 'origin' |
915 upstream_branch = 'refs/heads/master' | 979 upstream_branch = 'refs/heads/master' |
916 elif 'origin/trunk' in remote_branches: | 980 elif 'origin/trunk' in remote_branches: |
917 # Fall back on origin/trunk if it exists. Generally a shared | 981 # Fall back on origin/trunk if it exists. Generally a shared |
918 # git-svn clone | 982 # git-svn clone |
919 remote = 'origin' | 983 remote = 'origin' |
920 upstream_branch = 'refs/heads/trunk' | 984 upstream_branch = 'refs/heads/trunk' |
921 else: | 985 else: |
922 DieWithError("""Unable to determine default branch to diff against. | 986 DieWithError( |
923 Either pass complete "git diff"-style arguments, like | 987 'Unable to determine default branch to diff against.\n' |
924 git cl upload origin/master | 988 '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 | 989 ' git cl upload origin/master\n' |
926 "git checkout -b ...").""") | 990 'or verify this branch is set up to track another \n' |
| 991 '(via the --track argument to "git checkout -b ...").') |
927 | 992 |
928 return remote, upstream_branch | 993 return remote, upstream_branch |
929 | 994 |
930 def GetCommonAncestorWithUpstream(self): | 995 def GetCommonAncestorWithUpstream(self): |
931 upstream_branch = self.GetUpstreamBranch() | 996 upstream_branch = self.GetUpstreamBranch() |
932 if not BranchExists(upstream_branch): | 997 if not BranchExists(upstream_branch): |
933 DieWithError('The upstream for the current branch (%s) does not exist ' | 998 DieWithError('The upstream for the current branch (%s) does not exist ' |
934 'anymore.\nPlease fix it and try again.' % self.GetBranch()) | 999 'anymore.\nPlease fix it and try again.' % self.GetBranch()) |
935 return git_common.get_or_create_merge_base(self.GetBranch(), | 1000 return git_common.get_or_create_merge_base(self.GetBranch(), |
936 upstream_branch) | 1001 upstream_branch) |
(...skipping 121 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1058 # If URL is pointing to a local directory, it is probably a git cache. | 1123 # If URL is pointing to a local directory, it is probably a git cache. |
1059 if os.path.isdir(url): | 1124 if os.path.isdir(url): |
1060 url = RunGit(['config', 'remote.%s.url' % remote], | 1125 url = RunGit(['config', 'remote.%s.url' % remote], |
1061 error_ok=True, | 1126 error_ok=True, |
1062 cwd=url).strip() | 1127 cwd=url).strip() |
1063 return url | 1128 return url |
1064 | 1129 |
1065 def GetIssue(self): | 1130 def GetIssue(self): |
1066 """Returns the issue number as a int or None if not set.""" | 1131 """Returns the issue number as a int or None if not set.""" |
1067 if self.issue is None and not self.lookedup_issue: | 1132 if self.issue is None and not self.lookedup_issue: |
1068 issue = RunGit(['config', self._IssueSetting()], error_ok=True).strip() | 1133 issue = RunGit(['config', |
| 1134 self._codereview_impl.IssueSetting(self.GetBranch())], |
| 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(self.GetBranch()) |
| 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 codereview_server = self._codereview_impl.GetCodereviewServer() |
1181 RunGit(['config', self._RietveldServer(), self.rietveld_server]) | 1186 if codereview_server: |
| 1187 RunGit(['config', codereview_setting, codereview_server]) |
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) |
| 1256 |
| 1257 # Forward methods to codereview specific implementation. |
| 1258 |
| 1259 def CloseIssue(self): |
| 1260 return self._codereview_impl.CloseIssue() |
| 1261 |
| 1262 def GetStatus(self): |
| 1263 return self._codereview_impl.GetStatus() |
| 1264 |
| 1265 def GetCodereviewServer(self): |
| 1266 return self._codereview_impl.GetCodereviewServer() |
| 1267 |
| 1268 def GetApprovingReviewers(self): |
| 1269 return self._codereview_impl.GetApprovingReviewers() |
| 1270 |
| 1271 def GetMostRecentPatchset(self): |
| 1272 return self._codereview_impl.GetMostRecentPatchset() |
| 1273 |
| 1274 def __getattr__(self, attr): |
| 1275 # This is because lots of untested code accesses Rietveld-specific stuff |
| 1276 # directly, and it's hard to fix for sure. So, just let it work, and fix |
| 1277 # on a cases by case basis. |
| 1278 return getattr(self._codereview_impl, attr) |
| 1279 |
| 1280 |
| 1281 class _ChangelistCodereviewBase(object): |
| 1282 """Abstract base class encapsulating codereview specifics of a changelist.""" |
| 1283 def __init__(self, changelist): |
| 1284 self._changelist = changelist # instance of Changelist |
| 1285 |
| 1286 def __getattr__(self, attr): |
| 1287 # Forward methods to changelist. |
| 1288 # TODO(tandrii): maybe clean up _GerritChangelistImpl and |
| 1289 # _RietveldChangelistImpl to avoid this hack? |
| 1290 return getattr(self._changelist, attr) |
| 1291 |
1235 def GetStatus(self): | 1292 def GetStatus(self): |
1236 """Apply a rough heuristic to give a simple summary of an issue's review | 1293 """Apply a rough heuristic to give a simple summary of an issue's review |
1237 or CQ status, assuming adherence to a common workflow. | 1294 or CQ status, assuming adherence to a common workflow. |
| 1295 |
| 1296 Returns None if no issue for this branch, or specific string keywords. |
| 1297 """ |
| 1298 raise NotImplementedError() |
| 1299 |
| 1300 def GetCodereviewServer(self): |
| 1301 """Returns server URL without end slash, like "https://codereview.com".""" |
| 1302 raise NotImplementedError() |
| 1303 |
| 1304 def FetchDescription(self): |
| 1305 """Fetches and returns description from the codereview server.""" |
| 1306 raise NotImplementedError() |
| 1307 |
| 1308 def GetCodereviewServerSetting(self): |
| 1309 """Returns git config setting for the codereview server.""" |
| 1310 raise NotImplementedError() |
| 1311 |
| 1312 @staticmethod |
| 1313 def IssueSetting(branch): |
| 1314 """Returns name of git config setting which stores issue number for a given |
| 1315 branch.""" |
| 1316 raise NotImplementedError() |
| 1317 |
| 1318 def PatchsetSetting(self): |
| 1319 """Returns name of git config setting which stores issue number.""" |
| 1320 raise NotImplementedError() |
| 1321 |
| 1322 def GetRieveldObjForPresubmit(self): |
| 1323 # This is an unfortunate Rietveld-embeddedness in presubmit. |
| 1324 # For non-Rietveld codereviews, this probably should return a dummy object. |
| 1325 raise NotImplementedError() |
| 1326 |
| 1327 def UpdateDescriptionRemote(self, description): |
| 1328 """Update the description on codereview site.""" |
| 1329 raise NotImplementedError() |
| 1330 |
| 1331 def CloseIssue(self): |
| 1332 """Closes the issue.""" |
| 1333 raise NotImplementedError() |
| 1334 |
| 1335 def GetApprovingReviewers(self): |
| 1336 """Returns a list of reviewers approving the change. |
| 1337 |
| 1338 Note: not necessarily committers. |
| 1339 """ |
| 1340 raise NotImplementedError() |
| 1341 |
| 1342 def GetMostRecentPatchset(self): |
| 1343 """Returns the most recent patchset number from the codereview site.""" |
| 1344 raise NotImplementedError() |
| 1345 |
| 1346 |
| 1347 class _RietveldChangelistImpl(_ChangelistCodereviewBase): |
| 1348 def __init__(self, changelist, auth_config=None, rietveld_server=None): |
| 1349 super(_RietveldChangelistImpl, self).__init__(changelist) |
| 1350 assert settings, 'must be initialized in _ChangelistCodereviewBase' |
| 1351 settings.GetDefaultServerUrl() |
| 1352 |
| 1353 self._rietveld_server = rietveld_server |
| 1354 self._auth_config = auth_config |
| 1355 self._props = None |
| 1356 self._rpc_server = None |
| 1357 |
| 1358 def GetAuthConfig(self): |
| 1359 return self._auth_config |
| 1360 |
| 1361 def GetCodereviewServer(self): |
| 1362 if not self._rietveld_server: |
| 1363 # If we're on a branch then get the server potentially associated |
| 1364 # with that branch. |
| 1365 if self.GetIssue(): |
| 1366 rietveld_server_setting = self.GetCodereviewServerSetting() |
| 1367 if rietveld_server_setting: |
| 1368 self._rietveld_server = gclient_utils.UpgradeToHttps(RunGit( |
| 1369 ['config', rietveld_server_setting], error_ok=True).strip()) |
| 1370 if not self._rietveld_server: |
| 1371 self._rietveld_server = settings.GetDefaultServerUrl() |
| 1372 return self._rietveld_server |
| 1373 |
| 1374 def FetchDescription(self): |
| 1375 issue = self.GetIssue() |
| 1376 assert issue |
| 1377 try: |
| 1378 return self.RpcServer().get_description(issue).strip() |
| 1379 except urllib2.HTTPError as e: |
| 1380 if e.code == 404: |
| 1381 DieWithError( |
| 1382 ('\nWhile fetching the description for issue %d, received a ' |
| 1383 '404 (not found)\n' |
| 1384 'error. It is likely that you deleted this ' |
| 1385 'issue on the server. If this is the\n' |
| 1386 'case, please run\n\n' |
| 1387 ' git cl issue 0\n\n' |
| 1388 'to clear the association with the deleted issue. Then run ' |
| 1389 'this command again.') % issue) |
| 1390 else: |
| 1391 DieWithError( |
| 1392 '\nFailed to fetch issue description. HTTP error %d' % e.code) |
| 1393 except urllib2.URLError as e: |
| 1394 print >> sys.stderr, ( |
| 1395 'Warning: Failed to retrieve CL description due to network ' |
| 1396 'failure.') |
| 1397 return '' |
| 1398 |
| 1399 def GetMostRecentPatchset(self): |
| 1400 return self.GetIssueProperties()['patchsets'][-1] |
| 1401 |
| 1402 def GetPatchSetDiff(self, issue, patchset): |
| 1403 return self.RpcServer().get( |
| 1404 '/download/issue%s_%s.diff' % (issue, patchset)) |
| 1405 |
| 1406 def GetIssueProperties(self): |
| 1407 if self._props is None: |
| 1408 issue = self.GetIssue() |
| 1409 if not issue: |
| 1410 self._props = {} |
| 1411 else: |
| 1412 self._props = self.RpcServer().get_issue_properties(issue, True) |
| 1413 return self._props |
| 1414 |
| 1415 def GetApprovingReviewers(self): |
| 1416 return get_approving_reviewers(self.GetIssueProperties()) |
| 1417 |
| 1418 def AddComment(self, message): |
| 1419 return self.RpcServer().add_comment(self.GetIssue(), message) |
| 1420 |
| 1421 def GetStatus(self): |
| 1422 """Apply a rough heuristic to give a simple summary of an issue's review |
| 1423 or CQ status, assuming adherence to a common workflow. |
1238 | 1424 |
1239 Returns None if no issue for this branch, or one of the following keywords: | 1425 Returns None if no issue for this branch, or one of the following keywords: |
1240 * 'error' - error from review tool (including deleted issues) | 1426 * 'error' - error from review tool (including deleted issues) |
1241 * 'unsent' - not sent for review | 1427 * 'unsent' - not sent for review |
1242 * 'waiting' - waiting for review | 1428 * 'waiting' - waiting for review |
1243 * 'reply' - waiting for owner to reply to review | 1429 * 'reply' - waiting for owner to reply to review |
1244 * 'lgtm' - LGTM from at least one approved reviewer | 1430 * 'lgtm' - LGTM from at least one approved reviewer |
1245 * 'commit' - in the commit queue | 1431 * 'commit' - in the commit queue |
1246 * 'closed' - closed | 1432 * 'closed' - closed |
1247 """ | 1433 """ |
(...skipping 24 matching lines...) Expand all Loading... |
1272 messages = props.get('messages') or [] | 1458 messages = props.get('messages') or [] |
1273 | 1459 |
1274 if not messages: | 1460 if not messages: |
1275 # No message was sent. | 1461 # No message was sent. |
1276 return 'unsent' | 1462 return 'unsent' |
1277 if messages[-1]['sender'] != props.get('owner_email'): | 1463 if messages[-1]['sender'] != props.get('owner_email'): |
1278 # Non-LGTM reply from non-owner | 1464 # Non-LGTM reply from non-owner |
1279 return 'reply' | 1465 return 'reply' |
1280 return 'waiting' | 1466 return 'waiting' |
1281 | 1467 |
1282 def RunHook(self, committing, may_prompt, verbose, change): | 1468 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( | 1469 return self.RpcServer().update_description( |
1298 self.GetIssue(), self.description) | 1470 self.GetIssue(), self.description) |
1299 | 1471 |
1300 def CloseIssue(self): | 1472 def CloseIssue(self): |
1301 """Updates the description and closes the issue.""" | |
1302 return self.RpcServer().close_issue(self.GetIssue()) | 1473 return self.RpcServer().close_issue(self.GetIssue()) |
1303 | 1474 |
1304 def SetFlag(self, flag, value): | 1475 def SetFlag(self, flag, value): |
1305 """Patchset must match.""" | 1476 """Patchset must match.""" |
1306 if not self.GetPatchset(): | 1477 if not self.GetPatchset(): |
1307 DieWithError('The patchset needs to match. Send another patchset.') | 1478 DieWithError('The patchset needs to match. Send another patchset.') |
1308 try: | 1479 try: |
1309 return self.RpcServer().set_flag( | 1480 return self.RpcServer().set_flag( |
1310 self.GetIssue(), self.GetPatchset(), flag, value) | 1481 self.GetIssue(), self.GetPatchset(), flag, value) |
1311 except urllib2.HTTPError, e: | 1482 except urllib2.HTTPError, e: |
1312 if e.code == 404: | 1483 if e.code == 404: |
1313 DieWithError('The issue %s doesn\'t exist.' % self.GetIssue()) | 1484 DieWithError('The issue %s doesn\'t exist.' % self.GetIssue()) |
1314 if e.code == 403: | 1485 if e.code == 403: |
1315 DieWithError( | 1486 DieWithError( |
1316 ('Access denied to issue %s. Maybe the patchset %s doesn\'t ' | 1487 ('Access denied to issue %s. Maybe the patchset %s doesn\'t ' |
1317 'match?') % (self.GetIssue(), self.GetPatchset())) | 1488 'match?') % (self.GetIssue(), self.GetPatchset())) |
1318 raise | 1489 raise |
1319 | 1490 |
1320 def RpcServer(self): | 1491 def RpcServer(self): |
1321 """Returns an upload.RpcServer() to access this review's rietveld instance. | 1492 """Returns an upload.RpcServer() to access this review's rietveld instance. |
1322 """ | 1493 """ |
1323 if not self._rpc_server: | 1494 if not self._rpc_server: |
1324 self._rpc_server = rietveld.CachingRietveld( | 1495 self._rpc_server = rietveld.CachingRietveld( |
1325 self.GetRietveldServer(), | 1496 self.GetCodereviewServer(), |
1326 self._auth_config or auth.make_auth_config()) | 1497 self._auth_config or auth.make_auth_config()) |
1327 return self._rpc_server | 1498 return self._rpc_server |
1328 | 1499 |
1329 def _IssueSetting(self): | 1500 @staticmethod |
1330 """Return the git setting that stores this change's issue.""" | 1501 def IssueSetting(branch): |
1331 return 'branch.%s.rietveldissue' % self.GetBranch() | 1502 return 'branch.%s.rietveldissue' % branch |
1332 | 1503 |
1333 def _PatchsetSetting(self): | 1504 def PatchsetSetting(self): |
1334 """Return the git setting that stores this change's most recent patchset.""" | 1505 """Return the git setting that stores this change's most recent patchset.""" |
1335 return 'branch.%s.rietveldpatchset' % self.GetBranch() | 1506 return 'branch.%s.rietveldpatchset' % self.GetBranch() |
1336 | 1507 |
1337 def _RietveldServer(self): | 1508 def GetCodereviewServerSetting(self): |
1338 """Returns the git setting that stores this change's rietveld server.""" | 1509 """Returns the git setting that stores this change's rietveld server.""" |
1339 branch = self.GetBranch() | 1510 branch = self.GetBranch() |
1340 if branch: | 1511 if branch: |
1341 return 'branch.%s.rietveldserver' % branch | 1512 return 'branch.%s.rietveldserver' % branch |
1342 return None | 1513 return None |
1343 | 1514 |
| 1515 def GetRieveldObjForPresubmit(self): |
| 1516 return self.RpcServer() |
| 1517 |
| 1518 |
| 1519 class _GerritChangelistImpl(_ChangelistCodereviewBase): |
| 1520 def __init__(self, changelist, auth_config=None): |
| 1521 # auth_config is Rietveld thing, kept here to preserve interface only. |
| 1522 super(_GerritChangelistImpl, self).__init__(changelist) |
| 1523 self._change_id = None |
| 1524 self._gerrit_server = None # e.g. https://chromium-review.googlesource.com |
| 1525 self._gerrit_host = None # e.g. chromium-review.googlesource.com |
| 1526 |
| 1527 def _GetGerritHost(self): |
| 1528 # Lazy load of configs. |
| 1529 self.GetCodereviewServer() |
| 1530 return self._gerrit_host |
| 1531 |
| 1532 def GetCodereviewServer(self): |
| 1533 if not self._gerrit_server: |
| 1534 # If we're on a branch then get the server potentially associated |
| 1535 # with that branch. |
| 1536 if self.GetIssue(): |
| 1537 gerrit_server_setting = self.GetCodereviewServerSetting() |
| 1538 if gerrit_server_setting: |
| 1539 self._gerrit_server = RunGit(['config', gerrit_server_setting], |
| 1540 error_ok=True).strip() |
| 1541 if self._gerrit_server: |
| 1542 self._gerrit_host = urlparse.urlparse(self._gerrit_server).netloc |
| 1543 if not self._gerrit_server: |
| 1544 # We assume repo to be hosted on Gerrit, and hence Gerrit server |
| 1545 # has "-review" suffix for lowest level subdomain. |
| 1546 parts = urlparse.urlparse(self.GetRemoteUrl()).netloc.split('.') |
| 1547 parts[0] = parts[0] + '-review' |
| 1548 self._gerrit_host = '.'.join(parts) |
| 1549 self._gerrit_server = 'https://%s' % self._gerrit_host |
| 1550 return self._gerrit_server |
| 1551 |
| 1552 @staticmethod |
| 1553 def IssueSetting(branch): |
| 1554 return 'branch.%s.gerritissue' % branch |
| 1555 |
| 1556 def PatchsetSetting(self): |
| 1557 """Return the git setting that stores this change's most recent patchset.""" |
| 1558 return 'branch.%s.gerritpatchset' % self.GetBranch() |
| 1559 |
| 1560 def GetCodereviewServerSetting(self): |
| 1561 """Returns the git setting that stores this change's Gerrit server.""" |
| 1562 branch = self.GetBranch() |
| 1563 if branch: |
| 1564 return 'branch.%s.gerritserver' % branch |
| 1565 return None |
| 1566 |
| 1567 def GetRieveldObjForPresubmit(self): |
| 1568 class ThisIsNotRietveldIssue(object): |
| 1569 def __nonzero__(self): |
| 1570 # This is a hack to make presubmit_support think that rietveld is not |
| 1571 # defined, yet still ensure that calls directly result in a decent |
| 1572 # exception message below. |
| 1573 return False |
| 1574 |
| 1575 def __getattr__(self, attr): |
| 1576 print( |
| 1577 'You aren\'t using Rietveld at the moment, but Gerrit.\n' |
| 1578 'Using Rietveld in your PRESUBMIT scripts won\'t work.\n' |
| 1579 'Please, either change your PRESUBIT to not use rietveld_obj.%s,\n' |
| 1580 'or use Rietveld for codereview.\n' |
| 1581 'See also http://crbug.com/579160.' % attr) |
| 1582 raise NotImplementedError() |
| 1583 return ThisIsNotRietveldIssue() |
| 1584 |
| 1585 def GetStatus(self): |
| 1586 # TODO(tandrii) |
| 1587 raise NotImplementedError() |
| 1588 |
| 1589 def GetMostRecentPatchset(self): |
| 1590 data = gerrit_util.GetChangeDetail(self._GetGerritHost(), self.GetIssue(), |
| 1591 ['CURRENT_REVISION']) |
| 1592 return data['revisions'][data['current_revision']]['_number'] |
| 1593 |
| 1594 def FetchDescription(self): |
| 1595 data = gerrit_util.GetChangeDetail(self._GetGerritHost(), self.GetIssue(), |
| 1596 ['COMMIT_FOOTERS', 'CURRENT_REVISION']) |
| 1597 return data['revisions'][data['current_revision']]['commit_with_footers'] |
| 1598 |
| 1599 def UpdateDescriptionRemote(self, description): |
| 1600 # TODO(tandrii) |
| 1601 raise NotImplementedError() |
| 1602 |
| 1603 def CloseIssue(self): |
| 1604 gerrit_util.AbandonChange(self._GetGerritHost(), self.GetIssue(), msg='') |
| 1605 |
1344 | 1606 |
1345 class ChangeDescription(object): | 1607 class ChangeDescription(object): |
1346 """Contains a parsed form of the change description.""" | 1608 """Contains a parsed form of the change description.""" |
1347 R_LINE = r'^[ \t]*(TBR|R)[ \t]*=[ \t]*(.*?)[ \t]*$' | 1609 R_LINE = r'^[ \t]*(TBR|R)[ \t]*=[ \t]*(.*?)[ \t]*$' |
1348 BUG_LINE = r'^[ \t]*(BUG)[ \t]*=[ \t]*(.*?)[ \t]*$' | 1610 BUG_LINE = r'^[ \t]*(BUG)[ \t]*=[ \t]*(.*?)[ \t]*$' |
1349 | 1611 |
1350 def __init__(self, description): | 1612 def __init__(self, description): |
1351 self._description_lines = (description or '').strip().splitlines() | 1613 self._description_lines = (description or '').strip().splitlines() |
1352 | 1614 |
1353 @property # www.logilab.org/ticket/89786 | 1615 @property # www.logilab.org/ticket/89786 |
(...skipping 525 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1879 return 0 | 2141 return 0 |
1880 | 2142 |
1881 branches = RunGit(['for-each-ref', '--format=%(refname)', 'refs/heads']) | 2143 branches = RunGit(['for-each-ref', '--format=%(refname)', 'refs/heads']) |
1882 if not branches: | 2144 if not branches: |
1883 print('No local branch found.') | 2145 print('No local branch found.') |
1884 return 0 | 2146 return 0 |
1885 | 2147 |
1886 changes = ( | 2148 changes = ( |
1887 Changelist(branchref=b, auth_config=auth_config) | 2149 Changelist(branchref=b, auth_config=auth_config) |
1888 for b in branches.splitlines()) | 2150 for b in branches.splitlines()) |
| 2151 # TODO(tandrii): refactor to use CLs list instead of branches list. |
1889 branches = [c.GetBranch() for c in changes] | 2152 branches = [c.GetBranch() for c in changes] |
1890 alignment = max(5, max(len(b) for b in branches)) | 2153 alignment = max(5, max(len(b) for b in branches)) |
1891 print 'Branches associated with reviews:' | 2154 print 'Branches associated with reviews:' |
1892 output = get_cl_statuses(branches, | 2155 output = get_cl_statuses(branches, |
1893 fine_grained=not options.fast, | 2156 fine_grained=not options.fast, |
1894 max_processes=options.maxjobs, | 2157 max_processes=options.maxjobs, |
1895 auth_config=auth_config) | 2158 auth_config=auth_config) |
1896 | 2159 |
1897 branch_statuses = {} | 2160 branch_statuses = {} |
1898 alignment = max(5, max(len(ShortBranchName(b)) for b in branches)) | 2161 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) | 2257 options, args = parser.parse_args(args) |
1995 auth_config = auth.extract_auth_config_from_options(options) | 2258 auth_config = auth.extract_auth_config_from_options(options) |
1996 | 2259 |
1997 issue = None | 2260 issue = None |
1998 if options.issue: | 2261 if options.issue: |
1999 try: | 2262 try: |
2000 issue = int(options.issue) | 2263 issue = int(options.issue) |
2001 except ValueError: | 2264 except ValueError: |
2002 DieWithError('A review issue id is expected to be a number') | 2265 DieWithError('A review issue id is expected to be a number') |
2003 | 2266 |
2004 cl = Changelist(issue=issue, auth_config=auth_config) | 2267 cl = Changelist(issue=issue, codereview='rietveld', auth_config=auth_config) |
2005 | 2268 |
2006 if options.comment: | 2269 if options.comment: |
2007 cl.AddComment(options.comment) | 2270 cl.AddComment(options.comment) |
2008 return 0 | 2271 return 0 |
2009 | 2272 |
2010 data = cl.GetIssueProperties() | 2273 data = cl.GetIssueProperties() |
2011 summary = [] | 2274 summary = [] |
2012 for message in sorted(data.get('messages', []), key=lambda x: x['date']): | 2275 for message in sorted(data.get('messages', []), key=lambda x: x['date']): |
2013 summary.append({ | 2276 summary.append({ |
2014 'date': message['date'], | 2277 '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/') | 2636 remote_branch = remote_branch.replace('refs/remotes/', 'refs/') |
2374 # If a pending prefix exists then replace refs/ with it. | 2637 # If a pending prefix exists then replace refs/ with it. |
2375 if pending_prefix: | 2638 if pending_prefix: |
2376 remote_branch = remote_branch.replace('refs/', pending_prefix) | 2639 remote_branch = remote_branch.replace('refs/', pending_prefix) |
2377 return remote_branch | 2640 return remote_branch |
2378 | 2641 |
2379 | 2642 |
2380 def RietveldUpload(options, args, cl, change): | 2643 def RietveldUpload(options, args, cl, change): |
2381 """upload the patch to rietveld.""" | 2644 """upload the patch to rietveld.""" |
2382 upload_args = ['--assume_yes'] # Don't ask about untracked files. | 2645 upload_args = ['--assume_yes'] # Don't ask about untracked files. |
2383 upload_args.extend(['--server', cl.GetRietveldServer()]) | 2646 upload_args.extend(['--server', cl.GetCodereviewServer()]) |
2384 upload_args.extend(auth.auth_config_to_command_options(cl.auth_config)) | 2647 # TODO(tandrii): refactor this ugliness into _RietveldChangelistImpl. |
| 2648 upload_args.extend(auth.auth_config_to_command_options( |
| 2649 cl._codereview_impl.GetAuthConfig())) |
2385 if options.emulate_svn_auto_props: | 2650 if options.emulate_svn_auto_props: |
2386 upload_args.append('--emulate_svn_auto_props') | 2651 upload_args.append('--emulate_svn_auto_props') |
2387 | 2652 |
2388 change_desc = None | 2653 change_desc = None |
2389 | 2654 |
2390 if options.email is not None: | 2655 if options.email is not None: |
2391 upload_args.extend(['--email', options.email]) | 2656 upload_args.extend(['--email', options.email]) |
2392 | 2657 |
2393 if cl.GetIssue(): | 2658 if cl.GetIssue(): |
2394 if options.title: | 2659 if options.title: |
(...skipping 222 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2617 | 2882 |
2618 # Default to diffing against common ancestor of upstream branch | 2883 # Default to diffing against common ancestor of upstream branch |
2619 base_branch = cl.GetCommonAncestorWithUpstream() | 2884 base_branch = cl.GetCommonAncestorWithUpstream() |
2620 args = [base_branch, 'HEAD'] | 2885 args = [base_branch, 'HEAD'] |
2621 | 2886 |
2622 # Make sure authenticated to Rietveld before running expensive hooks. It is | 2887 # Make sure authenticated to Rietveld before running expensive hooks. It is |
2623 # a fast, best efforts check. Rietveld still can reject the authentication | 2888 # a fast, best efforts check. Rietveld still can reject the authentication |
2624 # during the actual upload. | 2889 # during the actual upload. |
2625 if not settings.GetIsGerrit() and auth_config.use_oauth2: | 2890 if not settings.GetIsGerrit() and auth_config.use_oauth2: |
2626 authenticator = auth.get_authenticator_for_host( | 2891 authenticator = auth.get_authenticator_for_host( |
2627 cl.GetRietveldServer(), auth_config) | 2892 cl.GetCodereviewServer(), auth_config) |
2628 if not authenticator.has_cached_credentials(): | 2893 if not authenticator.has_cached_credentials(): |
2629 raise auth.LoginRequiredError(cl.GetRietveldServer()) | 2894 raise auth.LoginRequiredError(cl.GetCodereviewServer()) |
2630 | 2895 |
2631 # Apply watchlists on upload. | 2896 # Apply watchlists on upload. |
2632 change = cl.GetChange(base_branch, None) | 2897 change = cl.GetChange(base_branch, None) |
2633 watchlist = watchlists.Watchlists(change.RepositoryRoot()) | 2898 watchlist = watchlists.Watchlists(change.RepositoryRoot()) |
2634 files = [f.LocalPath() for f in change.AffectedFiles()] | 2899 files = [f.LocalPath() for f in change.AffectedFiles()] |
2635 if not options.bypass_watchlists: | 2900 if not options.bypass_watchlists: |
2636 cl.SetWatchers(watchlist.GetWatchersForPaths(files)) | 2901 cl.SetWatchers(watchlist.GetWatchersForPaths(files)) |
2637 | 2902 |
2638 if not options.bypass_hooks: | 2903 if not options.bypass_hooks: |
2639 if options.reviewers or options.tbr_owners: | 2904 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, | 3478 return PatchIssue(issue_arg, options.reject, options.nocommit, |
3214 options.directory, auth_config) | 3479 options.directory, auth_config) |
3215 | 3480 |
3216 | 3481 |
3217 def PatchIssue(issue_arg, reject, nocommit, directory, auth_config): | 3482 def PatchIssue(issue_arg, reject, nocommit, directory, auth_config): |
3218 # PatchIssue should never be called with a dirty tree. It is up to the | 3483 # 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 | 3484 # caller to check this, but just in case we assert here since the |
3220 # consequences of the caller not checking this could be dire. | 3485 # consequences of the caller not checking this could be dire. |
3221 assert(not git_common.is_dirty_git_tree('apply')) | 3486 assert(not git_common.is_dirty_git_tree('apply')) |
3222 | 3487 |
| 3488 # TODO(tandrii): implement for Gerrit. |
3223 if type(issue_arg) is int or issue_arg.isdigit(): | 3489 if type(issue_arg) is int or issue_arg.isdigit(): |
3224 # Input is an issue id. Figure out the URL. | 3490 # Input is an issue id. Figure out the URL. |
3225 issue = int(issue_arg) | 3491 issue = int(issue_arg) |
3226 cl = Changelist(issue=issue, auth_config=auth_config) | 3492 cl = Changelist(issue=issue, codereview='rietveld', auth_config=auth_config) |
3227 patchset = cl.GetMostRecentPatchset() | 3493 patchset = cl.GetMostRecentPatchset() |
3228 patch_data = cl.GetPatchSetDiff(issue, patchset) | 3494 patch_data = cl._codereview_impl.GetPatchSetDiff(issue, patchset) |
3229 else: | 3495 else: |
3230 # Assume it's a URL to the patch. Default to https. | 3496 # Assume it's a URL to the patch. Default to https. |
3231 issue_url = gclient_utils.UpgradeToHttps(issue_arg) | 3497 issue_url = gclient_utils.UpgradeToHttps(issue_arg) |
3232 match = re.match(r'(.*?)/download/issue(\d+)_(\d+).diff', issue_url) | 3498 match = re.match(r'(.*?)/download/issue(\d+)_(\d+).diff', issue_url) |
3233 if not match: | 3499 if not match: |
3234 DieWithError('Must pass an issue ID or full URL for ' | 3500 DieWithError('Must pass an issue ID or full URL for ' |
3235 '\'Download raw patch set\'') | 3501 '\'Download raw patch set\'') |
3236 issue = int(match.group(2)) | 3502 issue = int(match.group(2)) |
3237 cl = Changelist(issue=issue, auth_config=auth_config) | 3503 cl = Changelist(issue=issue, codereview='rietveld', |
3238 cl.rietveld_server = match.group(1) | 3504 rietvled_server=match.group(1), auth_config=auth_config) |
3239 patchset = int(match.group(3)) | 3505 patchset = int(match.group(3)) |
3240 patch_data = urllib2.urlopen(issue_arg).read() | 3506 patch_data = urllib2.urlopen(issue_arg).read() |
3241 | 3507 |
3242 # Switch up to the top-level directory, if necessary, in preparation for | 3508 # Switch up to the top-level directory, if necessary, in preparation for |
3243 # applying the patch. | 3509 # applying the patch. |
3244 top = settings.GetRelativeRoot() | 3510 top = settings.GetRelativeRoot() |
3245 if top: | 3511 if top: |
3246 os.chdir(top) | 3512 os.chdir(top) |
3247 | 3513 |
3248 # Git patches have a/ at the beginning of source paths. We strip that out | 3514 # 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: | 3538 except subprocess2.CalledProcessError: |
3273 print 'Failed to apply the patch' | 3539 print 'Failed to apply the patch' |
3274 return 1 | 3540 return 1 |
3275 | 3541 |
3276 # If we had an issue, commit the current state and register the issue. | 3542 # If we had an issue, commit the current state and register the issue. |
3277 if not nocommit: | 3543 if not nocommit: |
3278 RunGit(['commit', '-m', (cl.GetDescription() + '\n\n' + | 3544 RunGit(['commit', '-m', (cl.GetDescription() + '\n\n' + |
3279 'patch from issue %(i)s at patchset ' | 3545 'patch from issue %(i)s at patchset ' |
3280 '%(p)s (http://crrev.com/%(i)s#ps%(p)s)' | 3546 '%(p)s (http://crrev.com/%(i)s#ps%(p)s)' |
3281 % {'i': issue, 'p': patchset})]) | 3547 % {'i': issue, 'p': patchset})]) |
3282 cl = Changelist(auth_config=auth_config) | 3548 cl = Changelist(codereview='rietveld', auth_config=auth_config, |
| 3549 rietveld_server=cl.GetCodereviewServer()) |
3283 cl.SetIssue(issue) | 3550 cl.SetIssue(issue) |
3284 cl.SetPatchset(patchset) | 3551 cl.SetPatchset(patchset) |
3285 print "Committed patch locally." | 3552 print "Committed patch locally." |
3286 else: | 3553 else: |
3287 print "Patch applied to index." | 3554 print "Patch applied to index." |
3288 return 0 | 3555 return 0 |
3289 | 3556 |
3290 | 3557 |
3291 def CMDrebase(parser, args): | 3558 def CMDrebase(parser, args): |
3292 """Rebases current branch on top of svn repo.""" | 3559 """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__': | 4274 if __name__ == '__main__': |
4008 # These affect sys.stdout so do it outside of main() to simplify mocks in | 4275 # These affect sys.stdout so do it outside of main() to simplify mocks in |
4009 # unit testing. | 4276 # unit testing. |
4010 fix_encoding.fix_encoding() | 4277 fix_encoding.fix_encoding() |
4011 colorama.init() | 4278 colorama.init() |
4012 try: | 4279 try: |
4013 sys.exit(main(sys.argv[1:])) | 4280 sys.exit(main(sys.argv[1:])) |
4014 except KeyboardInterrupt: | 4281 except KeyboardInterrupt: |
4015 sys.stderr.write('interrupted\n') | 4282 sys.stderr.write('interrupted\n') |
4016 sys.exit(1) | 4283 sys.exit(1) |
OLD | NEW |