OLD | NEW |
1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. | 2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. |
3 # Use of this source code is governed by a BSD-style license that can be | 3 # Use of this source code is governed by a BSD-style license that can be |
4 # found in the LICENSE file. | 4 # found in the LICENSE file. |
5 | 5 |
6 # Copyright (C) 2008 Evan Martin <martine@danga.com> | 6 # Copyright (C) 2008 Evan Martin <martine@danga.com> |
7 | 7 |
8 """A git-command for integrating reviews on Rietveld and Gerrit.""" | 8 """A git-command for integrating reviews on Rietveld and Gerrit.""" |
9 | 9 |
10 from __future__ import print_function | 10 from __future__ import print_function |
(...skipping 96 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
107 return e.stdout | 107 return e.stdout |
108 | 108 |
109 | 109 |
110 def RunGit(args, **kwargs): | 110 def RunGit(args, **kwargs): |
111 """Returns stdout.""" | 111 """Returns stdout.""" |
112 return RunCommand(['git'] + args, **kwargs) | 112 return RunCommand(['git'] + args, **kwargs) |
113 | 113 |
114 | 114 |
115 def RunGitWithCode(args, suppress_stderr=False): | 115 def RunGitWithCode(args, suppress_stderr=False): |
116 """Returns return code and stdout.""" | 116 """Returns return code and stdout.""" |
| 117 if suppress_stderr: |
| 118 stderr = subprocess2.VOID |
| 119 else: |
| 120 stderr = sys.stderr |
117 try: | 121 try: |
118 if suppress_stderr: | 122 (out, _), code = subprocess2.communicate(['git'] + args, |
119 stderr = subprocess2.VOID | 123 env=GetNoGitPagerEnv(), |
120 else: | 124 stdout=subprocess2.PIPE, |
121 stderr = sys.stderr | 125 stderr=stderr) |
122 out, code = subprocess2.communicate(['git'] + args, | 126 return code, out |
123 env=GetNoGitPagerEnv(), | 127 except subprocess2.CalledProcessError as e: |
124 stdout=subprocess2.PIPE, | 128 logging.debug('Failed running %s', args) |
125 stderr=stderr) | 129 return e.returncode, e.stdout |
126 return code, out[0] | |
127 except ValueError: | |
128 # When the subprocess fails, it returns None. That triggers a ValueError | |
129 # when trying to unpack the return value into (out, code). | |
130 return 1, '' | |
131 | 130 |
132 | 131 |
133 def RunGitSilent(args): | 132 def RunGitSilent(args): |
134 """Returns stdout, suppresses stderr and ignores the return code.""" | 133 """Returns stdout, suppresses stderr and ignores the return code.""" |
135 return RunGitWithCode(args, suppress_stderr=True)[1] | 134 return RunGitWithCode(args, suppress_stderr=True)[1] |
136 | 135 |
137 | 136 |
138 def IsGitVersionAtLeast(min_version): | 137 def IsGitVersionAtLeast(min_version): |
139 prefix = 'git version ' | 138 prefix = 'git version ' |
140 version = RunGit(['--version']).strip() | 139 version = RunGit(['--version']).strip() |
141 return (version.startswith(prefix) and | 140 return (version.startswith(prefix) and |
142 LooseVersion(version[len(prefix):]) >= LooseVersion(min_version)) | 141 LooseVersion(version[len(prefix):]) >= LooseVersion(min_version)) |
143 | 142 |
144 | 143 |
145 def BranchExists(branch): | 144 def BranchExists(branch): |
146 """Return True if specified branch exists.""" | 145 """Return True if specified branch exists.""" |
147 code, _ = RunGitWithCode(['rev-parse', '--verify', branch], | 146 code, _ = RunGitWithCode(['rev-parse', '--verify', branch], |
148 suppress_stderr=True) | 147 suppress_stderr=True) |
149 return not code | 148 return not code |
150 | 149 |
151 | 150 |
152 def ask_for_data(prompt): | 151 def ask_for_data(prompt): |
153 try: | 152 try: |
154 return raw_input(prompt) | 153 return raw_input(prompt) |
155 except KeyboardInterrupt: | 154 except KeyboardInterrupt: |
156 # Hide the exception. | 155 # Hide the exception. |
157 sys.exit(1) | 156 sys.exit(1) |
158 | 157 |
159 | 158 |
160 def git_set_branch_value(key, value): | 159 def _git_branch_config_key(branch, setting): |
161 branch = GetCurrentBranch() | 160 """Helper method to return Git config key for a branch.""" |
162 if not branch: | 161 assert branch |
163 return | 162 return 'branch.%s.%s' % (branch, setting) |
164 | |
165 cmd = ['config'] | |
166 if isinstance(value, int): | |
167 cmd.append('--int') | |
168 git_key = 'branch.%s.%s' % (branch, key) | |
169 RunGit(cmd + [git_key, str(value)]) | |
170 | 163 |
171 | 164 |
172 def git_get_branch_default(key, default): | 165 def _git_get_branch_config_value(setting, default=None, value_type=str, |
173 branch = GetCurrentBranch() | 166 branch=False): |
174 if branch: | 167 assert value_type in (int, str, bool) |
175 git_key = 'branch.%s.%s' % (branch, key) | 168 if branch is False: # Distinguishing default arg value from None. |
176 (_, stdout) = RunGitWithCode(['config', '--int', '--get', git_key]) | 169 branch = GetCurrentBranch() |
177 try: | 170 |
178 return int(stdout.strip()) | 171 if not branch: |
179 except ValueError: | 172 return default |
180 pass | 173 |
| 174 args = ['config'] |
| 175 if value_type == int: |
| 176 args.append('--int') |
| 177 elif value_type == bool: |
| 178 args.append('--bool') |
| 179 args.append(_git_branch_config_key(branch, setting)) |
| 180 code, out = RunGitWithCode(args) |
| 181 if code == 0: |
| 182 value = out.strip() |
| 183 if value_type == int: |
| 184 return int(value) |
| 185 if value_type == bool: |
| 186 return bool(value.lower() == 'true') |
| 187 return value |
181 return default | 188 return default |
182 | 189 |
183 | 190 |
| 191 def _git_unset_branch_config_value(setting, branch=None, **kwargs): |
| 192 if not branch: |
| 193 branch = GetCurrentBranch() |
| 194 assert branch |
| 195 RunGit(['config', '--unset', _git_branch_config_key(branch, setting)], |
| 196 **kwargs) |
| 197 |
| 198 |
| 199 def _git_set_branch_config_value(setting, value, branch=None): |
| 200 if not branch: |
| 201 branch = GetCurrentBranch() |
| 202 assert branch |
| 203 args = ['config'] |
| 204 # Check for boolean first, becuase bool is int, but int is not bool. |
| 205 if isinstance(value, bool): |
| 206 args.append('--bool') |
| 207 value = str(value).lower() |
| 208 elif isinstance(value, int): |
| 209 args.append('--int') |
| 210 value = str(value) |
| 211 else: |
| 212 value = str(value) |
| 213 args.extend([_git_branch_config_key(branch, setting), value]) |
| 214 RunGit(args) |
| 215 |
| 216 |
184 def add_git_similarity(parser): | 217 def add_git_similarity(parser): |
185 parser.add_option( | 218 parser.add_option( |
186 '--similarity', metavar='SIM', type='int', action='store', | 219 '--similarity', metavar='SIM', type=int, action='store', |
187 help='Sets the percentage that a pair of files need to match in order to' | 220 help='Sets the percentage that a pair of files need to match in order to' |
188 ' be considered copies (default 50)') | 221 ' be considered copies (default 50)') |
189 parser.add_option( | 222 parser.add_option( |
190 '--find-copies', action='store_true', | 223 '--find-copies', action='store_true', |
191 help='Allows git to look for copies.') | 224 help='Allows git to look for copies.') |
192 parser.add_option( | 225 parser.add_option( |
193 '--no-find-copies', action='store_false', dest='find_copies', | 226 '--no-find-copies', action='store_false', dest='find_copies', |
194 help='Disallows git from looking for copies.') | 227 help='Disallows git from looking for copies.') |
195 | 228 |
196 old_parser_args = parser.parse_args | 229 old_parser_args = parser.parse_args |
197 def Parse(args): | 230 def Parse(args): |
198 options, args = old_parser_args(args) | 231 options, args = old_parser_args(args) |
199 | 232 |
200 if options.similarity is None: | 233 if options.similarity is None: |
201 options.similarity = git_get_branch_default('git-cl-similarity', 50) | 234 options.similarity = _git_get_branch_config_value( |
| 235 'git-cl-similarity', default=50, value_type=int) |
202 else: | 236 else: |
203 print('Note: Saving similarity of %d%% in git config.' | 237 print('Note: Saving similarity of %d%% in git config.' |
204 % options.similarity) | 238 % options.similarity) |
205 git_set_branch_value('git-cl-similarity', options.similarity) | 239 _git_set_branch_config_value('git-cl-similarity', options.similarity) |
206 | 240 |
207 options.similarity = max(0, min(options.similarity, 100)) | 241 options.similarity = max(0, min(options.similarity, 100)) |
208 | 242 |
209 if options.find_copies is None: | 243 if options.find_copies is None: |
210 options.find_copies = bool( | 244 options.find_copies = _git_get_branch_config_value( |
211 git_get_branch_default('git-find-copies', True)) | 245 'git-find-copies', default=True, value_type=bool) |
212 else: | 246 else: |
213 git_set_branch_value('git-find-copies', int(options.find_copies)) | 247 _git_set_branch_config_value('git-find-copies', bool(options.find_copies)) |
214 | 248 |
215 print('Using %d%% similarity for rename/copy detection. ' | 249 print('Using %d%% similarity for rename/copy detection. ' |
216 'Override with --similarity.' % options.similarity) | 250 'Override with --similarity.' % options.similarity) |
217 | 251 |
218 return options, args | 252 return options, args |
219 parser.parse_args = Parse | 253 parser.parse_args = Parse |
220 | 254 |
221 | 255 |
222 def _get_properties_from_options(options): | 256 def _get_properties_from_options(options): |
223 properties = dict(x.split('=', 1) for x in options.properties) | 257 properties = dict(x.split('=', 1) for x in options.properties) |
(...skipping 675 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
899 | 933 |
900 class Changelist(object): | 934 class Changelist(object): |
901 """Changelist works with one changelist in local branch. | 935 """Changelist works with one changelist in local branch. |
902 | 936 |
903 Supports two codereview backends: Rietveld or Gerrit, selected at object | 937 Supports two codereview backends: Rietveld or Gerrit, selected at object |
904 creation. | 938 creation. |
905 | 939 |
906 Notes: | 940 Notes: |
907 * Not safe for concurrent multi-{thread,process} use. | 941 * Not safe for concurrent multi-{thread,process} use. |
908 * Caches values from current branch. Therefore, re-use after branch change | 942 * Caches values from current branch. Therefore, re-use after branch change |
909 with care. | 943 with great care. |
910 """ | 944 """ |
911 | 945 |
912 def __init__(self, branchref=None, issue=None, codereview=None, **kwargs): | 946 def __init__(self, branchref=None, issue=None, codereview=None, **kwargs): |
913 """Create a new ChangeList instance. | 947 """Create a new ChangeList instance. |
914 | 948 |
915 If issue is given, the codereview must be given too. | 949 If issue is given, the codereview must be given too. |
916 | 950 |
917 If `codereview` is given, it must be 'rietveld' or 'gerrit'. | 951 If `codereview` is given, it must be 'rietveld' or 'gerrit'. |
918 Otherwise, it's decided based on current configuration of the local branch, | 952 Otherwise, it's decided based on current configuration of the local branch, |
919 with default being 'rietveld' for backwards compatibility. | 953 with default being 'rietveld' for backwards compatibility. |
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
959 cls = _CODEREVIEW_IMPLEMENTATIONS[codereview] | 993 cls = _CODEREVIEW_IMPLEMENTATIONS[codereview] |
960 self._codereview = codereview | 994 self._codereview = codereview |
961 self._codereview_impl = cls(self, **kwargs) | 995 self._codereview_impl = cls(self, **kwargs) |
962 return | 996 return |
963 | 997 |
964 # Automatic selection based on issue number set for a current branch. | 998 # Automatic selection based on issue number set for a current branch. |
965 # Rietveld takes precedence over Gerrit. | 999 # Rietveld takes precedence over Gerrit. |
966 assert not self.issue | 1000 assert not self.issue |
967 # Whether we find issue or not, we are doing the lookup. | 1001 # Whether we find issue or not, we are doing the lookup. |
968 self.lookedup_issue = True | 1002 self.lookedup_issue = True |
969 for codereview, cls in _CODEREVIEW_IMPLEMENTATIONS.iteritems(): | 1003 if self.GetBranch(): |
970 setting = cls.IssueSetting(self.GetBranch()) | 1004 for codereview, cls in _CODEREVIEW_IMPLEMENTATIONS.iteritems(): |
971 issue = RunGit(['config', setting], error_ok=True).strip() | 1005 issue = _git_get_branch_config_value( |
972 if issue: | 1006 cls.IssueSettingSuffix(), value_type=int, branch=self.GetBranch()) |
973 self._codereview = codereview | 1007 if issue: |
974 self._codereview_impl = cls(self, **kwargs) | 1008 self._codereview = codereview |
975 self.issue = int(issue) | 1009 self._codereview_impl = cls(self, **kwargs) |
976 return | 1010 self.issue = int(issue) |
| 1011 return |
977 | 1012 |
978 # No issue is set for this branch, so decide based on repo-wide settings. | 1013 # No issue is set for this branch, so decide based on repo-wide settings. |
979 return self._load_codereview_impl( | 1014 return self._load_codereview_impl( |
980 codereview='gerrit' if settings.GetIsGerrit() else 'rietveld', | 1015 codereview='gerrit' if settings.GetIsGerrit() else 'rietveld', |
981 **kwargs) | 1016 **kwargs) |
982 | 1017 |
983 def IsGerrit(self): | 1018 def IsGerrit(self): |
984 return self._codereview == 'gerrit' | 1019 return self._codereview == 'gerrit' |
985 | 1020 |
986 def GetCCList(self): | 1021 def GetCCList(self): |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1018 | 1053 |
1019 def GetBranchRef(self): | 1054 def GetBranchRef(self): |
1020 """Returns the full branch name, e.g. 'refs/heads/master'.""" | 1055 """Returns the full branch name, e.g. 'refs/heads/master'.""" |
1021 self.GetBranch() # Poke the lazy loader. | 1056 self.GetBranch() # Poke the lazy loader. |
1022 return self.branchref | 1057 return self.branchref |
1023 | 1058 |
1024 def ClearBranch(self): | 1059 def ClearBranch(self): |
1025 """Clears cached branch data of this object.""" | 1060 """Clears cached branch data of this object.""" |
1026 self.branch = self.branchref = None | 1061 self.branch = self.branchref = None |
1027 | 1062 |
| 1063 def _GitGetBranchConfigValue(self, setting, default=None, **kwargs): |
| 1064 kwargs['branch'] = self.GetBranch() |
| 1065 return _git_get_branch_config_value(setting, default, **kwargs) |
| 1066 |
| 1067 def _GitSetBranchConfigValue(self, setting, value, **kwargs): |
| 1068 assert self.GetBranch() |
| 1069 kwargs['branch'] = self.GetBranch() |
| 1070 return _git_set_branch_config_value(setting, value, **kwargs) |
| 1071 |
| 1072 def _GitUnsetBranchConfigValue(self, setting, **kwargs): |
| 1073 assert self.GetBranch() |
| 1074 kwargs['branch'] = self.GetBranch() |
| 1075 return _git_unset_branch_config_value(setting, **kwargs) |
| 1076 |
1028 @staticmethod | 1077 @staticmethod |
1029 def FetchUpstreamTuple(branch): | 1078 def FetchUpstreamTuple(branch): |
1030 """Returns a tuple containing remote and remote ref, | 1079 """Returns a tuple containing remote and remote ref, |
1031 e.g. 'origin', 'refs/heads/master' | 1080 e.g. 'origin', 'refs/heads/master' |
1032 """ | 1081 """ |
1033 remote = '.' | 1082 remote = '.' |
1034 upstream_branch = RunGit(['config', 'branch.%s.merge' % branch], | 1083 upstream_branch = _git_get_branch_config_value('merge', branch=branch) |
1035 error_ok=True).strip() | 1084 |
1036 if upstream_branch: | 1085 if upstream_branch: |
1037 remote = RunGit(['config', 'branch.%s.remote' % branch]).strip() | 1086 remote = _git_get_branch_config_value('remote', branch=branch) |
1038 else: | 1087 else: |
1039 upstream_branch = RunGit(['config', 'rietveld.upstream-branch'], | 1088 upstream_branch = RunGit(['config', 'rietveld.upstream-branch'], |
1040 error_ok=True).strip() | 1089 error_ok=True).strip() |
1041 if upstream_branch: | 1090 if upstream_branch: |
1042 remote = RunGit(['config', 'rietveld.upstream-remote']).strip() | 1091 remote = RunGit(['config', 'rietveld.upstream-remote']).strip() |
1043 else: | 1092 else: |
1044 # Fall back on trying a git-svn upstream branch. | 1093 # Fall back on trying a git-svn upstream branch. |
1045 if settings.GetIsGitSvn(): | 1094 if settings.GetIsGitSvn(): |
1046 upstream_branch = settings.GetSVNBranch() | 1095 upstream_branch = settings.GetSVNBranch() |
1047 else: | 1096 else: |
(...skipping 113 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1161 len(common_commits), remote_branch, upstream_git_obj), | 1210 len(common_commits), remote_branch, upstream_git_obj), |
1162 file=sys.stderr) | 1211 file=sys.stderr) |
1163 return False | 1212 return False |
1164 return True | 1213 return True |
1165 | 1214 |
1166 def GetGitBaseUrlFromConfig(self): | 1215 def GetGitBaseUrlFromConfig(self): |
1167 """Return the configured base URL from branch.<branchname>.baseurl. | 1216 """Return the configured base URL from branch.<branchname>.baseurl. |
1168 | 1217 |
1169 Returns None if it is not set. | 1218 Returns None if it is not set. |
1170 """ | 1219 """ |
1171 return RunGit(['config', 'branch.%s.base-url' % self.GetBranch()], | 1220 return self._GitGetBranchConfigValue('base-url') |
1172 error_ok=True).strip() | |
1173 | 1221 |
1174 def GetGitSvnRemoteUrl(self): | 1222 def GetGitSvnRemoteUrl(self): |
1175 """Return the configured git-svn remote URL parsed from git svn info. | 1223 """Return the configured git-svn remote URL parsed from git svn info. |
1176 | 1224 |
1177 Returns None if it is not set. | 1225 Returns None if it is not set. |
1178 """ | 1226 """ |
1179 # URL is dependent on the current directory. | 1227 # URL is dependent on the current directory. |
1180 data = RunGit(['svn', 'info'], cwd=settings.GetRoot()) | 1228 data = RunGit(['svn', 'info'], cwd=settings.GetRoot()) |
1181 if data: | 1229 if data: |
1182 keys = dict(line.split(': ', 1) for line in data.splitlines() | 1230 keys = dict(line.split(': ', 1) for line in data.splitlines() |
(...skipping 12 matching lines...) Expand all Loading... |
1195 # If URL is pointing to a local directory, it is probably a git cache. | 1243 # If URL is pointing to a local directory, it is probably a git cache. |
1196 if os.path.isdir(url): | 1244 if os.path.isdir(url): |
1197 url = RunGit(['config', 'remote.%s.url' % remote], | 1245 url = RunGit(['config', 'remote.%s.url' % remote], |
1198 error_ok=True, | 1246 error_ok=True, |
1199 cwd=url).strip() | 1247 cwd=url).strip() |
1200 return url | 1248 return url |
1201 | 1249 |
1202 def GetIssue(self): | 1250 def GetIssue(self): |
1203 """Returns the issue number as a int or None if not set.""" | 1251 """Returns the issue number as a int or None if not set.""" |
1204 if self.issue is None and not self.lookedup_issue: | 1252 if self.issue is None and not self.lookedup_issue: |
1205 issue = RunGit(['config', | 1253 self.issue = self._GitGetBranchConfigValue( |
1206 self._codereview_impl.IssueSetting(self.GetBranch())], | 1254 self._codereview_impl.IssueSettingSuffix(), value_type=int) |
1207 error_ok=True).strip() | |
1208 self.issue = int(issue) or None if issue else None | |
1209 self.lookedup_issue = True | 1255 self.lookedup_issue = True |
1210 return self.issue | 1256 return self.issue |
1211 | 1257 |
1212 def GetIssueURL(self): | 1258 def GetIssueURL(self): |
1213 """Get the URL for a particular issue.""" | 1259 """Get the URL for a particular issue.""" |
1214 issue = self.GetIssue() | 1260 issue = self.GetIssue() |
1215 if not issue: | 1261 if not issue: |
1216 return None | 1262 return None |
1217 return '%s/%s' % (self._codereview_impl.GetCodereviewServer(), issue) | 1263 return '%s/%s' % (self._codereview_impl.GetCodereviewServer(), issue) |
1218 | 1264 |
1219 def GetDescription(self, pretty=False): | 1265 def GetDescription(self, pretty=False): |
1220 if not self.has_description: | 1266 if not self.has_description: |
1221 if self.GetIssue(): | 1267 if self.GetIssue(): |
1222 self.description = self._codereview_impl.FetchDescription() | 1268 self.description = self._codereview_impl.FetchDescription() |
1223 self.has_description = True | 1269 self.has_description = True |
1224 if pretty: | 1270 if pretty: |
1225 wrapper = textwrap.TextWrapper() | 1271 wrapper = textwrap.TextWrapper() |
1226 wrapper.initial_indent = wrapper.subsequent_indent = ' ' | 1272 wrapper.initial_indent = wrapper.subsequent_indent = ' ' |
1227 return wrapper.fill(self.description) | 1273 return wrapper.fill(self.description) |
1228 return self.description | 1274 return self.description |
1229 | 1275 |
1230 def GetPatchset(self): | 1276 def GetPatchset(self): |
1231 """Returns the patchset number as a int or None if not set.""" | 1277 """Returns the patchset number as a int or None if not set.""" |
1232 if self.patchset is None and not self.lookedup_patchset: | 1278 if self.patchset is None and not self.lookedup_patchset: |
1233 patchset = RunGit(['config', self._codereview_impl.PatchsetSetting()], | 1279 self.patchset = self._GitGetBranchConfigValue( |
1234 error_ok=True).strip() | 1280 self._codereview_impl.PatchsetSettingSuffix(), value_type=int) |
1235 self.patchset = int(patchset) or None if patchset else None | |
1236 self.lookedup_patchset = True | 1281 self.lookedup_patchset = True |
1237 return self.patchset | 1282 return self.patchset |
1238 | 1283 |
1239 def SetPatchset(self, patchset): | 1284 def SetPatchset(self, patchset): |
1240 """Set this branch's patchset. If patchset=0, clears the patchset.""" | 1285 """Set this branch's patchset. If patchset=0, clears the patchset.""" |
1241 patchset_setting = self._codereview_impl.PatchsetSetting() | 1286 assert self.GetBranch() |
1242 if patchset: | 1287 if patchset: |
1243 RunGit(['config', patchset_setting, str(patchset)]) | 1288 patchset = int(patchset) |
| 1289 self._GitSetBranchConfigValue( |
| 1290 self._codereview_impl.PatchsetSettingSuffix(), patchset) |
1244 self.patchset = patchset | 1291 self.patchset = patchset |
1245 else: | 1292 else: |
1246 RunGit(['config', '--unset', patchset_setting], | 1293 self._GitUnsetBranchConfigValue( |
1247 stderr=subprocess2.PIPE, error_ok=True) | 1294 self._codereview_impl.PatchsetSettingSuffix()) |
1248 self.patchset = None | 1295 self.patchset = None |
1249 | 1296 |
1250 def SetIssue(self, issue=None): | 1297 def SetIssue(self, issue=None): |
1251 """Set this branch's issue. If issue isn't given, clears the issue.""" | 1298 """Set this branch's issue. If issue isn't given, clears the issue.""" |
1252 issue_setting = self._codereview_impl.IssueSetting(self.GetBranch()) | 1299 assert self.GetBranch() |
1253 codereview_setting = self._codereview_impl.GetCodereviewServerSetting() | |
1254 if issue: | 1300 if issue: |
| 1301 issue = int(issue) |
| 1302 self._GitSetBranchConfigValue( |
| 1303 self._codereview_impl.IssueSettingSuffix(), issue) |
1255 self.issue = issue | 1304 self.issue = issue |
1256 RunGit(['config', issue_setting, str(issue)]) | |
1257 codereview_server = self._codereview_impl.GetCodereviewServer() | 1305 codereview_server = self._codereview_impl.GetCodereviewServer() |
1258 if codereview_server: | 1306 if codereview_server: |
1259 RunGit(['config', codereview_setting, codereview_server]) | 1307 self._GitSetBranchConfigValue( |
| 1308 self._codereview_impl.CodereviewServerSettingSuffix(), |
| 1309 codereview_server) |
1260 else: | 1310 else: |
1261 # Reset it regardless. It doesn't hurt. | 1311 # Reset all of these just to be clean. |
1262 config_settings = [issue_setting, self._codereview_impl.PatchsetSetting()] | 1312 reset_suffixes = [ |
1263 for prop in (['last-upload-hash'] + | 1313 'last-upload-hash', |
1264 self._codereview_impl._PostUnsetIssueProperties()): | 1314 self._codereview_impl.IssueSettingSuffix(), |
1265 config_settings.append('branch.%s.%s' % (self.GetBranch(), prop)) | 1315 self._codereview_impl.PatchsetSettingSuffix(), |
1266 for setting in config_settings: | 1316 self._codereview_impl.CodereviewServerSettingSuffix(), |
1267 RunGit(['config', '--unset', setting], error_ok=True) | 1317 ] + self._PostUnsetIssueProperties() |
| 1318 for prop in reset_suffixes: |
| 1319 self._GitUnsetBranchConfigValue(prop, error_ok=True) |
1268 self.issue = None | 1320 self.issue = None |
1269 self.patchset = None | 1321 self.patchset = None |
1270 | 1322 |
1271 def GetChange(self, upstream_branch, author): | 1323 def GetChange(self, upstream_branch, author): |
1272 if not self.GitSanityChecks(upstream_branch): | 1324 if not self.GitSanityChecks(upstream_branch): |
1273 DieWithError('\nGit sanity check failure') | 1325 DieWithError('\nGit sanity check failure') |
1274 | 1326 |
1275 root = settings.GetRelativeRoot() | 1327 root = settings.GetRelativeRoot() |
1276 if not root: | 1328 if not root: |
1277 root = '.' | 1329 root = '.' |
(...skipping 123 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1401 ask_for_data('About to upload; enter to confirm.') | 1453 ask_for_data('About to upload; enter to confirm.') |
1402 | 1454 |
1403 print_stats(options.similarity, options.find_copies, git_diff_args) | 1455 print_stats(options.similarity, options.find_copies, git_diff_args) |
1404 ret = self.CMDUploadChange(options, git_diff_args, change) | 1456 ret = self.CMDUploadChange(options, git_diff_args, change) |
1405 if not ret: | 1457 if not ret: |
1406 if options.use_commit_queue: | 1458 if options.use_commit_queue: |
1407 self.SetCQState(_CQState.COMMIT) | 1459 self.SetCQState(_CQState.COMMIT) |
1408 elif options.cq_dry_run: | 1460 elif options.cq_dry_run: |
1409 self.SetCQState(_CQState.DRY_RUN) | 1461 self.SetCQState(_CQState.DRY_RUN) |
1410 | 1462 |
1411 git_set_branch_value('last-upload-hash', | 1463 _git_set_branch_config_value('last-upload-hash', |
1412 RunGit(['rev-parse', 'HEAD']).strip()) | 1464 RunGit(['rev-parse', 'HEAD']).strip()) |
1413 # Run post upload hooks, if specified. | 1465 # Run post upload hooks, if specified. |
1414 if settings.GetRunPostUploadHook(): | 1466 if settings.GetRunPostUploadHook(): |
1415 presubmit_support.DoPostUploadExecuter( | 1467 presubmit_support.DoPostUploadExecuter( |
1416 change, | 1468 change, |
1417 self, | 1469 self, |
1418 settings.GetRoot(), | 1470 settings.GetRoot(), |
1419 options.verbose, | 1471 options.verbose, |
1420 sys.stdout) | 1472 sys.stdout) |
1421 | 1473 |
1422 # Upload all dependencies if specified. | 1474 # Upload all dependencies if specified. |
(...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1489 raise NotImplementedError() | 1541 raise NotImplementedError() |
1490 | 1542 |
1491 def GetCodereviewServer(self): | 1543 def GetCodereviewServer(self): |
1492 """Returns server URL without end slash, like "https://codereview.com".""" | 1544 """Returns server URL without end slash, like "https://codereview.com".""" |
1493 raise NotImplementedError() | 1545 raise NotImplementedError() |
1494 | 1546 |
1495 def FetchDescription(self): | 1547 def FetchDescription(self): |
1496 """Fetches and returns description from the codereview server.""" | 1548 """Fetches and returns description from the codereview server.""" |
1497 raise NotImplementedError() | 1549 raise NotImplementedError() |
1498 | 1550 |
1499 def GetCodereviewServerSetting(self): | 1551 @classmethod |
1500 """Returns git config setting for the codereview server.""" | 1552 def IssueSettingSuffix(cls): |
| 1553 """Returns branch setting storing issue number.""" |
1501 raise NotImplementedError() | 1554 raise NotImplementedError() |
1502 | 1555 |
1503 @classmethod | 1556 @classmethod |
1504 def IssueSetting(cls, branch): | 1557 def PatchsetSettingSuffix(cls): |
1505 return 'branch.%s.%s' % (branch, cls.IssueSettingSuffix()) | 1558 """Returns branch setting storing patchset number.""" |
| 1559 raise NotImplementedError() |
1506 | 1560 |
1507 @classmethod | 1561 @classmethod |
1508 def IssueSettingSuffix(cls): | 1562 def CodereviewServerSettingSuffix(cls): |
1509 """Returns name of git config setting which stores issue number for a given | 1563 """Returns branch setting storing codereview server.""" |
1510 branch.""" | |
1511 raise NotImplementedError() | |
1512 | |
1513 def PatchsetSetting(self): | |
1514 """Returns name of git config setting which stores issue number.""" | |
1515 raise NotImplementedError() | 1564 raise NotImplementedError() |
1516 | 1565 |
1517 def _PostUnsetIssueProperties(self): | 1566 def _PostUnsetIssueProperties(self): |
1518 """Which branch-specific properties to erase when unsettin issue.""" | 1567 """Which branch-specific properties to erase when unsettin issue.""" |
1519 raise NotImplementedError() | 1568 return [] |
1520 | 1569 |
1521 def GetRieveldObjForPresubmit(self): | 1570 def GetRieveldObjForPresubmit(self): |
1522 # This is an unfortunate Rietveld-embeddedness in presubmit. | 1571 # This is an unfortunate Rietveld-embeddedness in presubmit. |
1523 # For non-Rietveld codereviews, this probably should return a dummy object. | 1572 # For non-Rietveld codereviews, this probably should return a dummy object. |
1524 raise NotImplementedError() | 1573 raise NotImplementedError() |
1525 | 1574 |
1526 def GetGerritObjForPresubmit(self): | 1575 def GetGerritObjForPresubmit(self): |
1527 # None is valid return value, otherwise presubmit_support.GerritAccessor. | 1576 # None is valid return value, otherwise presubmit_support.GerritAccessor. |
1528 return None | 1577 return None |
1529 | 1578 |
(...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1596 self._rietveld_server = rietveld_server | 1645 self._rietveld_server = rietveld_server |
1597 self._auth_config = auth_config | 1646 self._auth_config = auth_config |
1598 self._props = None | 1647 self._props = None |
1599 self._rpc_server = None | 1648 self._rpc_server = None |
1600 | 1649 |
1601 def GetCodereviewServer(self): | 1650 def GetCodereviewServer(self): |
1602 if not self._rietveld_server: | 1651 if not self._rietveld_server: |
1603 # If we're on a branch then get the server potentially associated | 1652 # If we're on a branch then get the server potentially associated |
1604 # with that branch. | 1653 # with that branch. |
1605 if self.GetIssue(): | 1654 if self.GetIssue(): |
1606 rietveld_server_setting = self.GetCodereviewServerSetting() | 1655 self._rietveld_server = gclient_utils.UpgradeToHttps( |
1607 if rietveld_server_setting: | 1656 self._GitGetBranchConfigValue(self.CodereviewServerSettingSuffix())) |
1608 self._rietveld_server = gclient_utils.UpgradeToHttps(RunGit( | |
1609 ['config', rietveld_server_setting], error_ok=True).strip()) | |
1610 if not self._rietveld_server: | 1657 if not self._rietveld_server: |
1611 self._rietveld_server = settings.GetDefaultServerUrl() | 1658 self._rietveld_server = settings.GetDefaultServerUrl() |
1612 return self._rietveld_server | 1659 return self._rietveld_server |
1613 | 1660 |
1614 def EnsureAuthenticated(self, force): | 1661 def EnsureAuthenticated(self, force): |
1615 """Best effort check that user is authenticated with Rietveld server.""" | 1662 """Best effort check that user is authenticated with Rietveld server.""" |
1616 if self._auth_config.use_oauth2: | 1663 if self._auth_config.use_oauth2: |
1617 authenticator = auth.get_authenticator_for_host( | 1664 authenticator = auth.get_authenticator_for_host( |
1618 self.GetCodereviewServer(), self._auth_config) | 1665 self.GetCodereviewServer(), self._auth_config) |
1619 if not authenticator.has_cached_credentials(): | 1666 if not authenticator.has_cached_credentials(): |
(...skipping 136 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1756 if not self._rpc_server: | 1803 if not self._rpc_server: |
1757 self._rpc_server = rietveld.CachingRietveld( | 1804 self._rpc_server = rietveld.CachingRietveld( |
1758 self.GetCodereviewServer(), | 1805 self.GetCodereviewServer(), |
1759 self._auth_config or auth.make_auth_config()) | 1806 self._auth_config or auth.make_auth_config()) |
1760 return self._rpc_server | 1807 return self._rpc_server |
1761 | 1808 |
1762 @classmethod | 1809 @classmethod |
1763 def IssueSettingSuffix(cls): | 1810 def IssueSettingSuffix(cls): |
1764 return 'rietveldissue' | 1811 return 'rietveldissue' |
1765 | 1812 |
1766 def PatchsetSetting(self): | 1813 @classmethod |
1767 """Return the git setting that stores this change's most recent patchset.""" | 1814 def PatchsetSettingSuffix(cls): |
1768 return 'branch.%s.rietveldpatchset' % self.GetBranch() | 1815 return 'rietveldpatchset' |
1769 | 1816 |
1770 def GetCodereviewServerSetting(self): | 1817 @classmethod |
1771 """Returns the git setting that stores this change's rietveld server.""" | 1818 def CodereviewServerSettingSuffix(cls): |
1772 branch = self.GetBranch() | 1819 return 'rietveldserver' |
1773 if branch: | |
1774 return 'branch.%s.rietveldserver' % branch | |
1775 return None | |
1776 | |
1777 def _PostUnsetIssueProperties(self): | |
1778 """Which branch-specific properties to erase when unsetting issue.""" | |
1779 return ['rietveldserver'] | |
1780 | 1820 |
1781 def GetRieveldObjForPresubmit(self): | 1821 def GetRieveldObjForPresubmit(self): |
1782 return self.RpcServer() | 1822 return self.RpcServer() |
1783 | 1823 |
1784 def SetCQState(self, new_state): | 1824 def SetCQState(self, new_state): |
1785 props = self.GetIssueProperties() | 1825 props = self.GetIssueProperties() |
1786 if props.get('private'): | 1826 if props.get('private'): |
1787 DieWithError('Cannot set-commit on private issue') | 1827 DieWithError('Cannot set-commit on private issue') |
1788 | 1828 |
1789 if new_state == _CQState.COMMIT: | 1829 if new_state == _CQState.COMMIT: |
(...skipping 273 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2063 | 2103 |
2064 def _GetGitHost(self): | 2104 def _GetGitHost(self): |
2065 """Returns git host to be used when uploading change to Gerrit.""" | 2105 """Returns git host to be used when uploading change to Gerrit.""" |
2066 return urlparse.urlparse(self.GetRemoteUrl()).netloc | 2106 return urlparse.urlparse(self.GetRemoteUrl()).netloc |
2067 | 2107 |
2068 def GetCodereviewServer(self): | 2108 def GetCodereviewServer(self): |
2069 if not self._gerrit_server: | 2109 if not self._gerrit_server: |
2070 # If we're on a branch then get the server potentially associated | 2110 # If we're on a branch then get the server potentially associated |
2071 # with that branch. | 2111 # with that branch. |
2072 if self.GetIssue(): | 2112 if self.GetIssue(): |
2073 gerrit_server_setting = self.GetCodereviewServerSetting() | 2113 self._gerrit_server = self._GitGetBranchConfigValue( |
2074 if gerrit_server_setting: | 2114 self.CodereviewServerSettingSuffix()) |
2075 self._gerrit_server = RunGit(['config', gerrit_server_setting], | 2115 if self._gerrit_server: |
2076 error_ok=True).strip() | 2116 self._gerrit_host = urlparse.urlparse(self._gerrit_server).netloc |
2077 if self._gerrit_server: | |
2078 self._gerrit_host = urlparse.urlparse(self._gerrit_server).netloc | |
2079 if not self._gerrit_server: | 2117 if not self._gerrit_server: |
2080 # We assume repo to be hosted on Gerrit, and hence Gerrit server | 2118 # We assume repo to be hosted on Gerrit, and hence Gerrit server |
2081 # has "-review" suffix for lowest level subdomain. | 2119 # has "-review" suffix for lowest level subdomain. |
2082 parts = self._GetGitHost().split('.') | 2120 parts = self._GetGitHost().split('.') |
2083 parts[0] = parts[0] + '-review' | 2121 parts[0] = parts[0] + '-review' |
2084 self._gerrit_host = '.'.join(parts) | 2122 self._gerrit_host = '.'.join(parts) |
2085 self._gerrit_server = 'https://%s' % self._gerrit_host | 2123 self._gerrit_server = 'https://%s' % self._gerrit_host |
2086 return self._gerrit_server | 2124 return self._gerrit_server |
2087 | 2125 |
2088 @classmethod | 2126 @classmethod |
2089 def IssueSettingSuffix(cls): | 2127 def IssueSettingSuffix(cls): |
2090 return 'gerritissue' | 2128 return 'gerritissue' |
2091 | 2129 |
| 2130 @classmethod |
| 2131 def PatchsetSettingSuffix(cls): |
| 2132 return 'gerritpatchset' |
| 2133 |
| 2134 @classmethod |
| 2135 def CodereviewServerSettingSuffix(cls): |
| 2136 return 'gerritserver' |
| 2137 |
2092 def EnsureAuthenticated(self, force): | 2138 def EnsureAuthenticated(self, force): |
2093 """Best effort check that user is authenticated with Gerrit server.""" | 2139 """Best effort check that user is authenticated with Gerrit server.""" |
2094 if settings.GetGerritSkipEnsureAuthenticated(): | 2140 if settings.GetGerritSkipEnsureAuthenticated(): |
2095 # For projects with unusual authentication schemes. | 2141 # For projects with unusual authentication schemes. |
2096 # See http://crbug.com/603378. | 2142 # See http://crbug.com/603378. |
2097 return | 2143 return |
2098 # Lazy-loader to identify Gerrit and Git hosts. | 2144 # Lazy-loader to identify Gerrit and Git hosts. |
2099 if gerrit_util.GceAuthenticator.is_gce(): | 2145 if gerrit_util.GceAuthenticator.is_gce(): |
2100 return | 2146 return |
2101 self.GetCodereviewServer() | 2147 self.GetCodereviewServer() |
(...skipping 25 matching lines...) Expand all Loading... |
2127 [] if git_auth else [git_host]) | 2173 [] if git_auth else [git_host]) |
2128 DieWithError('Credentials for the following hosts are required:\n' | 2174 DieWithError('Credentials for the following hosts are required:\n' |
2129 ' %s\n' | 2175 ' %s\n' |
2130 'These are read from %s (or legacy %s)\n' | 2176 'These are read from %s (or legacy %s)\n' |
2131 '%s' % ( | 2177 '%s' % ( |
2132 '\n '.join(missing), | 2178 '\n '.join(missing), |
2133 cookie_auth.get_gitcookies_path(), | 2179 cookie_auth.get_gitcookies_path(), |
2134 cookie_auth.get_netrc_path(), | 2180 cookie_auth.get_netrc_path(), |
2135 cookie_auth.get_new_password_message(git_host))) | 2181 cookie_auth.get_new_password_message(git_host))) |
2136 | 2182 |
2137 | |
2138 def PatchsetSetting(self): | |
2139 """Return the git setting that stores this change's most recent patchset.""" | |
2140 return 'branch.%s.gerritpatchset' % self.GetBranch() | |
2141 | |
2142 def GetCodereviewServerSetting(self): | |
2143 """Returns the git setting that stores this change's Gerrit server.""" | |
2144 branch = self.GetBranch() | |
2145 if branch: | |
2146 return 'branch.%s.gerritserver' % branch | |
2147 return None | |
2148 | |
2149 def _PostUnsetIssueProperties(self): | 2183 def _PostUnsetIssueProperties(self): |
2150 """Which branch-specific properties to erase when unsetting issue.""" | 2184 """Which branch-specific properties to erase when unsetting issue.""" |
2151 return [ | 2185 return ['gerritsquashhash'] |
2152 'gerritserver', | |
2153 'gerritsquashhash', | |
2154 ] | |
2155 | 2186 |
2156 def GetRieveldObjForPresubmit(self): | 2187 def GetRieveldObjForPresubmit(self): |
2157 class ThisIsNotRietveldIssue(object): | 2188 class ThisIsNotRietveldIssue(object): |
2158 def __nonzero__(self): | 2189 def __nonzero__(self): |
2159 # This is a hack to make presubmit_support think that rietveld is not | 2190 # This is a hack to make presubmit_support think that rietveld is not |
2160 # defined, yet still ensure that calls directly result in a decent | 2191 # defined, yet still ensure that calls directly result in a decent |
2161 # exception message below. | 2192 # exception message below. |
2162 return False | 2193 return False |
2163 | 2194 |
2164 def __getattr__(self, attr): | 2195 def __getattr__(self, attr): |
(...skipping 102 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2267 return 1 | 2298 return 1 |
2268 detail = self._GetChangeDetail(['CURRENT_REVISION', 'LABELS']) | 2299 detail = self._GetChangeDetail(['CURRENT_REVISION', 'LABELS']) |
2269 if u'Commit-Queue' in detail.get('labels', {}): | 2300 if u'Commit-Queue' in detail.get('labels', {}): |
2270 if not force: | 2301 if not force: |
2271 ask_for_data('\nIt seems this repository has a Commit Queue, ' | 2302 ask_for_data('\nIt seems this repository has a Commit Queue, ' |
2272 'which can test and land changes for you. ' | 2303 'which can test and land changes for you. ' |
2273 'Are you sure you wish to bypass it?\n' | 2304 'Are you sure you wish to bypass it?\n' |
2274 'Press Enter to continue, Ctrl+C to abort.') | 2305 'Press Enter to continue, Ctrl+C to abort.') |
2275 | 2306 |
2276 differs = True | 2307 differs = True |
2277 last_upload = RunGit(['config', | 2308 last_upload = RunGit(['config', self._GitBranchSetting('gerritsquashhash')], |
2278 'branch.%s.gerritsquashhash' % self.GetBranch()], | |
2279 error_ok=True).strip() | 2309 error_ok=True).strip() |
2280 # Note: git diff outputs nothing if there is no diff. | 2310 # Note: git diff outputs nothing if there is no diff. |
2281 if not last_upload or RunGit(['diff', last_upload]).strip(): | 2311 if not last_upload or RunGit(['diff', last_upload]).strip(): |
2282 print('WARNING: some changes from local branch haven\'t been uploaded') | 2312 print('WARNING: some changes from local branch haven\'t been uploaded') |
2283 else: | 2313 else: |
2284 if detail['current_revision'] == last_upload: | 2314 if detail['current_revision'] == last_upload: |
2285 differs = False | 2315 differs = False |
2286 else: | 2316 else: |
2287 print('WARNING: local branch contents differ from latest uploaded ' | 2317 print('WARNING: local branch contents differ from latest uploaded ' |
2288 'patchset') | 2318 'patchset') |
(...skipping 282 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2571 if options.squash: | 2601 if options.squash: |
2572 regex = re.compile(r'remote:\s+https?://[\w\-\.\/]*/(\d+)\s.*') | 2602 regex = re.compile(r'remote:\s+https?://[\w\-\.\/]*/(\d+)\s.*') |
2573 change_numbers = [m.group(1) | 2603 change_numbers = [m.group(1) |
2574 for m in map(regex.match, push_stdout.splitlines()) | 2604 for m in map(regex.match, push_stdout.splitlines()) |
2575 if m] | 2605 if m] |
2576 if len(change_numbers) != 1: | 2606 if len(change_numbers) != 1: |
2577 DieWithError( | 2607 DieWithError( |
2578 ('Created|Updated %d issues on Gerrit, but only 1 expected.\n' | 2608 ('Created|Updated %d issues on Gerrit, but only 1 expected.\n' |
2579 'Change-Id: %s') % (len(change_numbers), change_id)) | 2609 'Change-Id: %s') % (len(change_numbers), change_id)) |
2580 self.SetIssue(change_numbers[0]) | 2610 self.SetIssue(change_numbers[0]) |
2581 RunGit(['config', 'branch.%s.gerritsquashhash' % self.GetBranch(), | 2611 self._GitSetBranchConfigValue('gerritsquashhash', ref_to_push) |
2582 ref_to_push]) | |
2583 return 0 | 2612 return 0 |
2584 | 2613 |
2585 def _AddChangeIdToCommitMessage(self, options, args): | 2614 def _AddChangeIdToCommitMessage(self, options, args): |
2586 """Re-commits using the current message, assumes the commit hook is in | 2615 """Re-commits using the current message, assumes the commit hook is in |
2587 place. | 2616 place. |
2588 """ | 2617 """ |
2589 log_desc = options.message or CreateDescriptionFromLog(args) | 2618 log_desc = options.message or CreateDescriptionFromLog(args) |
2590 git_command = ['commit', '--amend', '-m', log_desc] | 2619 git_command = ['commit', '--amend', '-m', log_desc] |
2591 RunGit(git_command) | 2620 RunGit(git_command) |
2592 new_log_desc = CreateDescriptionFromLog(args) | 2621 new_log_desc = CreateDescriptionFromLog(args) |
(...skipping 2560 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
5153 if __name__ == '__main__': | 5182 if __name__ == '__main__': |
5154 # These affect sys.stdout so do it outside of main() to simplify mocks in | 5183 # These affect sys.stdout so do it outside of main() to simplify mocks in |
5155 # unit testing. | 5184 # unit testing. |
5156 fix_encoding.fix_encoding() | 5185 fix_encoding.fix_encoding() |
5157 setup_color.init() | 5186 setup_color.init() |
5158 try: | 5187 try: |
5159 sys.exit(main(sys.argv[1:])) | 5188 sys.exit(main(sys.argv[1:])) |
5160 except KeyboardInterrupt: | 5189 except KeyboardInterrupt: |
5161 sys.stderr.write('interrupted\n') | 5190 sys.stderr.write('interrupted\n') |
5162 sys.exit(1) | 5191 sys.exit(1) |
OLD | NEW |