Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(790)

Side by Side Diff: git_cl.py

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

Powered by Google App Engine
This is Rietveld 408576698