blob: 978f6a59c19f42b38c698580ffe54e6ad44ac45f [file] [log] [blame]
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001# Copyright (C) 2008 The Android Open Source Project
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
Dan Willemsen0745bb22015-08-17 13:41:45 -070015import contextlib
16import errno
Mike Frysingeracf63b22019-06-13 02:24:21 -040017from http.client import HTTPException
Anthony King85b24ac2014-05-06 15:57:48 +010018import json
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070019import os
20import re
Łukasz Gardońbed59ce2017-08-08 10:18:11 +020021import ssl
Shawn O. Pearcefb231612009-04-10 18:53:46 -070022import subprocess
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070023import sys
Mike Frysingeracf63b22019-06-13 02:24:21 -040024import urllib.error
25import urllib.request
Shawn O. Pearcef00e0ce2009-08-22 18:39:49 -070026
Shawn O. Pearceb54a3922009-01-05 16:18:58 -080027from error import GitError, UploadError
Renaud Paquay010fed72016-11-11 14:25:29 -080028import platform_utils
Mike Frysinger8a11f6f2019-08-27 00:26:15 -040029from repo_trace import Trace
Shawn O. Pearceca8c32c2010-05-11 18:21:33 -070030from git_command import GitCommand
Zac Livingston9ead97b2017-06-13 08:29:04 -060031from git_refs import R_CHANGES, R_HEADS, R_TAGS
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070032
David Pursehouse1d947b32012-10-25 12:23:11 +090033ID_RE = re.compile(r'^[0-9a-f]{40}$')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070034
Shawn O. Pearce146fe902009-03-25 14:06:43 -070035REVIEW_CACHE = dict()
36
David Pursehouse819827a2020-02-12 15:20:19 +090037
Zac Livingston9ead97b2017-06-13 08:29:04 -060038def IsChange(rev):
39 return rev.startswith(R_CHANGES)
40
David Pursehouse819827a2020-02-12 15:20:19 +090041
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070042def IsId(rev):
43 return ID_RE.match(rev)
44
David Pursehouse819827a2020-02-12 15:20:19 +090045
Zac Livingston9ead97b2017-06-13 08:29:04 -060046def IsTag(rev):
47 return rev.startswith(R_TAGS)
48
David Pursehouse819827a2020-02-12 15:20:19 +090049
Zac Livingston9ead97b2017-06-13 08:29:04 -060050def IsImmutable(rev):
51 return IsChange(rev) or IsId(rev) or IsTag(rev)
52
David Pursehouse819827a2020-02-12 15:20:19 +090053
Shawn O. Pearcef8e32732009-04-17 11:00:31 -070054def _key(name):
55 parts = name.split('.')
56 if len(parts) < 2:
57 return name.lower()
David Pursehouse54a4e602020-02-12 14:31:05 +090058 parts[0] = parts[0].lower()
Shawn O. Pearcef8e32732009-04-17 11:00:31 -070059 parts[-1] = parts[-1].lower()
60 return '.'.join(parts)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070061
David Pursehouse819827a2020-02-12 15:20:19 +090062
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070063class GitConfig(object):
Shawn O. Pearce90be5c02008-10-29 15:21:24 -070064 _ForUser = None
65
Mike Frysingerf841ca42020-02-18 21:31:51 -050066 _USER_CONFIG = '~/.gitconfig'
67
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070068 @classmethod
69 def ForUser(cls):
Shawn O. Pearce90be5c02008-10-29 15:21:24 -070070 if cls._ForUser is None:
Mike Frysingerf841ca42020-02-18 21:31:51 -050071 cls._ForUser = cls(configfile=os.path.expanduser(cls._USER_CONFIG))
Shawn O. Pearce90be5c02008-10-29 15:21:24 -070072 return cls._ForUser
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070073
74 @classmethod
75 def ForRepository(cls, gitdir, defaults=None):
David Pursehousee5913ae2020-02-12 13:56:59 +090076 return cls(configfile=os.path.join(gitdir, 'config'),
77 defaults=defaults)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070078
Anthony King85b24ac2014-05-06 15:57:48 +010079 def __init__(self, configfile, defaults=None, jsonFile=None):
David Pursehouse8a68ff92012-09-24 12:15:13 +090080 self.file = configfile
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070081 self.defaults = defaults
82 self._cache_dict = None
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -070083 self._section_dict = None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070084 self._remotes = {}
85 self._branches = {}
Shawn O. Pearce1b34c912009-05-21 18:52:49 -070086
Anthony King85b24ac2014-05-06 15:57:48 +010087 self._json = jsonFile
88 if self._json is None:
89 self._json = os.path.join(
David Pursehouseabdf7502020-02-12 14:58:39 +090090 os.path.dirname(self.file),
91 '.repo_' + os.path.basename(self.file) + '.json')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070092
David Pursehousee5913ae2020-02-12 13:56:59 +090093 def Has(self, name, include_defaults=True):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070094 """Return true if this configuration file has the key.
95 """
Shawn O. Pearcef8e32732009-04-17 11:00:31 -070096 if _key(name) in self._cache:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070097 return True
98 if include_defaults and self.defaults:
David Pursehousee5913ae2020-02-12 13:56:59 +090099 return self.defaults.Has(name, include_defaults=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700100 return False
101
Mike Frysinger77b43972020-02-19 17:55:22 -0500102 def GetInt(self, name):
103 """Returns an integer from the configuration file.
104
105 This follows the git config syntax.
106
107 Args:
108 name: The key to lookup.
109
110 Returns:
111 None if the value was not defined, or is not a boolean.
112 Otherwise, the number itself.
113 """
114 v = self.GetString(name)
115 if v is None:
116 return None
117 v = v.strip()
118
119 mult = 1
120 if v.endswith('k'):
121 v = v[:-1]
122 mult = 1024
123 elif v.endswith('m'):
124 v = v[:-1]
125 mult = 1024 * 1024
126 elif v.endswith('g'):
127 v = v[:-1]
128 mult = 1024 * 1024 * 1024
129
130 base = 10
131 if v.startswith('0x'):
132 base = 16
133
134 try:
135 return int(v, base=base) * mult
136 except ValueError:
137 return None
138
Ian Kasprzak835a34b2021-03-05 11:04:49 -0800139 def DumpConfigDict(self):
140 """Returns the current configuration dict.
141
142 Configuration data is information only (e.g. logging) and
143 should not be considered a stable data-source.
144
145 Returns:
146 dict of {<key>, <value>} for git configuration cache.
147 <value> are strings converted by GetString.
148 """
149 config_dict = {}
150 for key in self._cache:
151 config_dict[key] = self.GetString(key)
152 return config_dict
153
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700154 def GetBoolean(self, name):
155 """Returns a boolean from the configuration file.
156 None : The value was not defined, or is not a boolean.
157 True : The value was set to true or yes.
158 False: The value was set to false or no.
159 """
160 v = self.GetString(name)
161 if v is None:
162 return None
163 v = v.lower()
164 if v in ('true', 'yes'):
165 return True
166 if v in ('false', 'no'):
167 return False
168 return None
169
Mike Frysinger38867fb2021-02-09 23:14:41 -0500170 def SetBoolean(self, name, value):
171 """Set the truthy value for a key."""
172 if value is not None:
173 value = 'true' if value else 'false'
174 self.SetString(name, value)
175
David Pursehouse8a68ff92012-09-24 12:15:13 +0900176 def GetString(self, name, all_keys=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700177 """Get the first value for a key, or None if it is not defined.
178
179 This configuration file is used first, if the key is not
David Pursehouse8a68ff92012-09-24 12:15:13 +0900180 defined or all_keys = True then the defaults are also searched.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700181 """
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700182 try:
Shawn O. Pearcef8e32732009-04-17 11:00:31 -0700183 v = self._cache[_key(name)]
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700184 except KeyError:
185 if self.defaults:
David Pursehousee5913ae2020-02-12 13:56:59 +0900186 return self.defaults.GetString(name, all_keys=all_keys)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700187 v = []
188
David Pursehouse8a68ff92012-09-24 12:15:13 +0900189 if not all_keys:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700190 if v:
191 return v[0]
192 return None
193
194 r = []
195 r.extend(v)
196 if self.defaults:
David Pursehousee5913ae2020-02-12 13:56:59 +0900197 r.extend(self.defaults.GetString(name, all_keys=True))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700198 return r
199
200 def SetString(self, name, value):
201 """Set the value(s) for a key.
202 Only this configuration file is modified.
203
204 The supplied value should be either a string,
205 or a list of strings (to store multiple values).
206 """
Shawn O. Pearcef8e32732009-04-17 11:00:31 -0700207 key = _key(name)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700208
209 try:
Shawn O. Pearcef8e32732009-04-17 11:00:31 -0700210 old = self._cache[key]
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700211 except KeyError:
212 old = []
213
214 if value is None:
215 if old:
Shawn O. Pearcef8e32732009-04-17 11:00:31 -0700216 del self._cache[key]
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700217 self._do('--unset-all', name)
218
219 elif isinstance(value, list):
220 if len(value) == 0:
221 self.SetString(name, None)
222
223 elif len(value) == 1:
224 self.SetString(name, value[0])
225
226 elif old != value:
Shawn O. Pearcef8e32732009-04-17 11:00:31 -0700227 self._cache[key] = list(value)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700228 self._do('--replace-all', name, value[0])
Sarah Owensa6053d52012-11-01 13:36:50 -0700229 for i in range(1, len(value)):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700230 self._do('--add', name, value[i])
231
232 elif len(old) != 1 or old[0] != value:
Shawn O. Pearcef8e32732009-04-17 11:00:31 -0700233 self._cache[key] = [value]
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700234 self._do('--replace-all', name, value)
235
236 def GetRemote(self, name):
237 """Get the remote.$name.* configuration values as an object.
238 """
239 try:
240 r = self._remotes[name]
241 except KeyError:
242 r = Remote(self, name)
243 self._remotes[r.name] = r
244 return r
245
246 def GetBranch(self, name):
247 """Get the branch.$name.* configuration values as an object.
248 """
249 try:
250 b = self._branches[name]
251 except KeyError:
252 b = Branch(self, name)
253 self._branches[b.name] = b
254 return b
255
Shawn O. Pearce366ad212009-05-19 12:47:37 -0700256 def GetSubSections(self, section):
257 """List all subsection names matching $section.*.*
258 """
259 return self._sections.get(section, set())
260
David Pursehousee5913ae2020-02-12 13:56:59 +0900261 def HasSection(self, section, subsection=''):
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -0700262 """Does at least one key in section.subsection exist?
263 """
264 try:
265 return subsection in self._sections[section]
266 except KeyError:
267 return False
268
Shawn O. Pearce13111b42011-09-19 11:00:31 -0700269 def UrlInsteadOf(self, url):
270 """Resolve any url.*.insteadof references.
271 """
272 for new_url in self.GetSubSections('url'):
Dan Willemsen4e4d40f2013-10-28 22:28:42 -0700273 for old_url in self.GetString('url.%s.insteadof' % new_url, True):
274 if old_url is not None and url.startswith(old_url):
275 return new_url + url[len(old_url):]
Shawn O. Pearce13111b42011-09-19 11:00:31 -0700276 return url
277
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -0700278 @property
279 def _sections(self):
280 d = self._section_dict
281 if d is None:
282 d = {}
283 for name in self._cache.keys():
284 p = name.split('.')
285 if 2 == len(p):
286 section = p[0]
287 subsect = ''
288 else:
289 section = p[0]
290 subsect = '.'.join(p[1:-1])
291 if section not in d:
292 d[section] = set()
293 d[section].add(subsect)
294 self._section_dict = d
295 return d
296
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700297 @property
298 def _cache(self):
299 if self._cache_dict is None:
300 self._cache_dict = self._Read()
301 return self._cache_dict
302
303 def _Read(self):
Anthony King85b24ac2014-05-06 15:57:48 +0100304 d = self._ReadJson()
Shawn O. Pearcec12c3602009-04-17 21:03:32 -0700305 if d is None:
306 d = self._ReadGit()
Anthony King85b24ac2014-05-06 15:57:48 +0100307 self._SaveJson(d)
Shawn O. Pearcec12c3602009-04-17 21:03:32 -0700308 return d
309
Anthony King85b24ac2014-05-06 15:57:48 +0100310 def _ReadJson(self):
Shawn O. Pearcec12c3602009-04-17 21:03:32 -0700311 try:
David Pursehouse16a5c3a2020-02-12 15:54:26 +0900312 if os.path.getmtime(self._json) <= os.path.getmtime(self.file):
Renaud Paquay010fed72016-11-11 14:25:29 -0800313 platform_utils.remove(self._json)
Shawn O. Pearcec12c3602009-04-17 21:03:32 -0700314 return None
315 except OSError:
316 return None
317 try:
Anthony King85b24ac2014-05-06 15:57:48 +0100318 Trace(': parsing %s', self.file)
Mike Frysinger3164d402019-11-11 05:40:22 -0500319 with open(self._json) as fd:
Anthony King85b24ac2014-05-06 15:57:48 +0100320 return json.load(fd)
Anthony King85b24ac2014-05-06 15:57:48 +0100321 except (IOError, ValueError):
Renaud Paquay010fed72016-11-11 14:25:29 -0800322 platform_utils.remove(self._json)
Shawn O. Pearcec12c3602009-04-17 21:03:32 -0700323 return None
324
Anthony King85b24ac2014-05-06 15:57:48 +0100325 def _SaveJson(self, cache):
Shawn O. Pearcec12c3602009-04-17 21:03:32 -0700326 try:
Mike Frysinger3164d402019-11-11 05:40:22 -0500327 with open(self._json, 'w') as fd:
Anthony King85b24ac2014-05-06 15:57:48 +0100328 json.dump(cache, fd, indent=2)
Anthony King85b24ac2014-05-06 15:57:48 +0100329 except (IOError, TypeError):
Anthony Kingb1d1fd72015-06-03 17:02:26 +0100330 if os.path.exists(self._json):
Renaud Paquay010fed72016-11-11 14:25:29 -0800331 platform_utils.remove(self._json)
Shawn O. Pearcec12c3602009-04-17 21:03:32 -0700332
333 def _ReadGit(self):
David Aguilar438c5472009-06-28 15:09:16 -0700334 """
335 Read configuration data from git.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700336
David Aguilar438c5472009-06-28 15:09:16 -0700337 This internal method populates the GitConfig cache.
338
339 """
David Aguilar438c5472009-06-28 15:09:16 -0700340 c = {}
Shawn O. Pearcec24c7202009-07-02 16:12:57 -0700341 d = self._do('--null', '--list')
342 if d is None:
343 return c
Dylan Denge469a0c2018-06-23 15:02:26 +0800344 for line in d.rstrip('\0').split('\0'):
David Aguilar438c5472009-06-28 15:09:16 -0700345 if '\n' in line:
David Pursehousec1b86a22012-11-14 11:36:51 +0900346 key, val = line.split('\n', 1)
David Aguilar438c5472009-06-28 15:09:16 -0700347 else:
David Pursehousec1b86a22012-11-14 11:36:51 +0900348 key = line
349 val = None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700350
351 if key in c:
352 c[key].append(val)
353 else:
354 c[key] = [val]
355
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700356 return c
357
358 def _do(self, *args):
Ulrik Laurénd0ca0f62020-04-28 01:09:57 +0200359 command = ['config', '--file', self.file, '--includes']
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700360 command.extend(args)
361
362 p = GitCommand(None,
363 command,
David Pursehousee5913ae2020-02-12 13:56:59 +0900364 capture_stdout=True,
365 capture_stderr=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700366 if p.Wait() == 0:
367 return p.stdout
368 else:
369 GitError('git config %s: %s' % (str(args), p.stderr))
370
371
Mike Frysingerf841ca42020-02-18 21:31:51 -0500372class RepoConfig(GitConfig):
373 """User settings for repo itself."""
374
375 _USER_CONFIG = '~/.repoconfig/config'
376
377
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700378class RefSpec(object):
379 """A Git refspec line, split into its components:
380
381 forced: True if the line starts with '+'
382 src: Left side of the line
383 dst: Right side of the line
384 """
385
386 @classmethod
387 def FromString(cls, rs):
388 lhs, rhs = rs.split(':', 2)
389 if lhs.startswith('+'):
390 lhs = lhs[1:]
391 forced = True
392 else:
393 forced = False
394 return cls(forced, lhs, rhs)
395
396 def __init__(self, forced, lhs, rhs):
397 self.forced = forced
398 self.src = lhs
399 self.dst = rhs
400
401 def SourceMatches(self, rev):
402 if self.src:
403 if rev == self.src:
404 return True
405 if self.src.endswith('/*') and rev.startswith(self.src[:-1]):
406 return True
407 return False
408
409 def DestMatches(self, ref):
410 if self.dst:
411 if ref == self.dst:
412 return True
413 if self.dst.endswith('/*') and ref.startswith(self.dst[:-1]):
414 return True
415 return False
416
417 def MapSource(self, rev):
418 if self.src.endswith('/*'):
419 return self.dst[:-1] + rev[len(self.src) - 1:]
420 return self.dst
421
422 def __str__(self):
423 s = ''
424 if self.forced:
425 s += '+'
426 if self.src:
427 s += self.src
428 if self.dst:
429 s += ':'
430 s += self.dst
431 return s
432
433
Shawn O. Pearce898e12a2012-03-14 15:22:28 -0700434URI_ALL = re.compile(r'^([a-z][a-z+-]*)://([^@/]*@?[^/]*)/')
Shawn O. Pearcefb231612009-04-10 18:53:46 -0700435
David Pursehouse819827a2020-02-12 15:20:19 +0900436
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700437def GetSchemeFromUrl(url):
438 m = URI_ALL.match(url)
439 if m:
440 return m.group(1)
441 return None
442
David Pursehouse819827a2020-02-12 15:20:19 +0900443
Dan Willemsen0745bb22015-08-17 13:41:45 -0700444@contextlib.contextmanager
445def GetUrlCookieFile(url, quiet):
446 if url.startswith('persistent-'):
447 try:
448 p = subprocess.Popen(
449 ['git-remote-persistent-https', '-print_config', url],
450 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
451 stderr=subprocess.PIPE)
452 try:
453 cookieprefix = 'http.cookiefile='
454 proxyprefix = 'http.proxy='
455 cookiefile = None
456 proxy = None
457 for line in p.stdout:
Mike Frysingerded477d2020-02-07 23:18:23 -0500458 line = line.strip().decode('utf-8')
Dan Willemsen0745bb22015-08-17 13:41:45 -0700459 if line.startswith(cookieprefix):
Daichi Ueurace7e0262018-02-26 08:49:36 +0900460 cookiefile = os.path.expanduser(line[len(cookieprefix):])
Dan Willemsen0745bb22015-08-17 13:41:45 -0700461 if line.startswith(proxyprefix):
462 proxy = line[len(proxyprefix):]
463 # Leave subprocess open, as cookie file may be transient.
464 if cookiefile or proxy:
465 yield cookiefile, proxy
466 return
467 finally:
468 p.stdin.close()
469 if p.wait():
Mike Frysingerded477d2020-02-07 23:18:23 -0500470 err_msg = p.stderr.read().decode('utf-8')
Dan Willemsen0745bb22015-08-17 13:41:45 -0700471 if ' -print_config' in err_msg:
472 pass # Persistent proxy doesn't support -print_config.
473 elif not quiet:
474 print(err_msg, file=sys.stderr)
475 except OSError as e:
476 if e.errno == errno.ENOENT:
477 pass # No persistent proxy.
478 raise
Daichi Ueurace7e0262018-02-26 08:49:36 +0900479 cookiefile = GitConfig.ForUser().GetString('http.cookiefile')
480 if cookiefile:
481 cookiefile = os.path.expanduser(cookiefile)
482 yield cookiefile, None
Dan Willemsen0745bb22015-08-17 13:41:45 -0700483
David Pursehouse819827a2020-02-12 15:20:19 +0900484
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700485class Remote(object):
486 """Configuration options related to a remote.
487 """
David Pursehouse819827a2020-02-12 15:20:19 +0900488
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700489 def __init__(self, config, name):
490 self._config = config
491 self.name = name
492 self.url = self._Get('url')
Steve Raed6480452016-08-10 15:00:00 -0700493 self.pushUrl = self._Get('pushurl')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700494 self.review = self._Get('review')
Shawn O. Pearce339ba9f2008-11-06 09:52:51 -0800495 self.projectname = self._Get('projectname')
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530496 self.fetch = list(map(RefSpec.FromString,
David Pursehouseabdf7502020-02-12 14:58:39 +0900497 self._Get('fetch', all_keys=True)))
Shawn O. Pearcec9571422012-01-11 14:58:54 -0800498 self._review_url = None
Shawn O. Pearceb54a3922009-01-05 16:18:58 -0800499
Ulrik Sjolinb6ea3bf2010-01-03 18:20:17 +0100500 def _InsteadOf(self):
501 globCfg = GitConfig.ForUser()
502 urlList = globCfg.GetSubSections('url')
503 longest = ""
504 longestUrl = ""
505
506 for url in urlList:
507 key = "url." + url + ".insteadOf"
David Pursehouse8a68ff92012-09-24 12:15:13 +0900508 insteadOfList = globCfg.GetString(key, all_keys=True)
Ulrik Sjolinb6ea3bf2010-01-03 18:20:17 +0100509
510 for insteadOf in insteadOfList:
David Pursehouse16a5c3a2020-02-12 15:54:26 +0900511 if (self.url.startswith(insteadOf)
512 and len(insteadOf) > len(longest)):
Ulrik Sjolinb6ea3bf2010-01-03 18:20:17 +0100513 longest = insteadOf
514 longestUrl = url
515
516 if len(longest) == 0:
517 return self.url
518
519 return self.url.replace(longest, longestUrl, 1)
520
Mike Frysinger339f2df2021-05-06 00:44:42 -0400521 def PreConnectFetch(self, ssh_proxy):
Mike Frysinger19e409c2021-05-05 19:44:35 -0400522 """Run any setup for this remote before we connect to it.
523
524 In practice, if the remote is using SSH, we'll attempt to create a new
525 SSH master session to it for reuse across projects.
526
Mike Frysinger339f2df2021-05-06 00:44:42 -0400527 Args:
528 ssh_proxy: The SSH settings for managing master sessions.
529
Mike Frysinger19e409c2021-05-05 19:44:35 -0400530 Returns:
531 Whether the preconnect phase for this remote was successful.
532 """
Mike Frysinger339f2df2021-05-06 00:44:42 -0400533 if not ssh_proxy:
534 return True
535
Ulrik Sjolinb6ea3bf2010-01-03 18:20:17 +0100536 connectionUrl = self._InsteadOf()
Mike Frysinger339f2df2021-05-06 00:44:42 -0400537 return ssh_proxy.preconnect(connectionUrl)
Shawn O. Pearcefb231612009-04-10 18:53:46 -0700538
Łukasz Gardońbed59ce2017-08-08 10:18:11 +0200539 def ReviewUrl(self, userEmail, validate_certs):
Shawn O. Pearcec9571422012-01-11 14:58:54 -0800540 if self._review_url is None:
Shawn O. Pearceb54a3922009-01-05 16:18:58 -0800541 if self.review is None:
542 return None
543
544 u = self.review
Conley Owens7e12e0a2014-10-23 15:40:00 -0700545 if u.startswith('persistent-'):
546 u = u[len('persistent-'):]
Christian Koestlin2ec2a5d2016-12-05 20:32:45 +0100547 if u.split(':')[0] not in ('http', 'https', 'sso', 'ssh'):
Shawn O. Pearceb54a3922009-01-05 16:18:58 -0800548 u = 'http://%s' % u
Shawn O. Pearce13cc3842009-03-25 13:54:54 -0700549 if u.endswith('/Gerrit'):
550 u = u[:len(u) - len('/Gerrit')]
Shawn O. Pearcec9571422012-01-11 14:58:54 -0800551 if u.endswith('/ssh_info'):
552 u = u[:len(u) - len('/ssh_info')]
553 if not u.endswith('/'):
David Pursehouse8a68ff92012-09-24 12:15:13 +0900554 u += '/'
Shawn O. Pearcec9571422012-01-11 14:58:54 -0800555 http_url = u
Shawn O. Pearceb54a3922009-01-05 16:18:58 -0800556
Shawn O. Pearce146fe902009-03-25 14:06:43 -0700557 if u in REVIEW_CACHE:
Shawn O. Pearcec9571422012-01-11 14:58:54 -0800558 self._review_url = REVIEW_CACHE[u]
Shawn O. Pearce1a68dc52011-10-11 14:12:46 -0700559 elif 'REPO_HOST_PORT_INFO' in os.environ:
Shawn O. Pearcec9571422012-01-11 14:58:54 -0800560 host, port = os.environ['REPO_HOST_PORT_INFO'].split()
561 self._review_url = self._SshReviewUrl(userEmail, host, port)
562 REVIEW_CACHE[u] = self._review_url
Christian Koestlin2ec2a5d2016-12-05 20:32:45 +0100563 elif u.startswith('sso:') or u.startswith('ssh:'):
Steve Pucci143d8a72014-01-30 09:45:53 -0800564 self._review_url = u # Assume it's right
565 REVIEW_CACHE[u] = self._review_url
Timo Lotterbacheec726c2016-10-07 10:52:08 +0200566 elif 'REPO_IGNORE_SSH_INFO' in os.environ:
567 self._review_url = http_url
568 REVIEW_CACHE[u] = self._review_url
Shawn O. Pearce146fe902009-03-25 14:06:43 -0700569 else:
570 try:
Shawn O. Pearcec9571422012-01-11 14:58:54 -0800571 info_url = u + 'ssh_info'
Łukasz Gardońbed59ce2017-08-08 10:18:11 +0200572 if not validate_certs:
573 context = ssl._create_unverified_context()
574 info = urllib.request.urlopen(info_url, context=context).read()
575 else:
576 info = urllib.request.urlopen(info_url).read()
Mike Frysinger1b9adab2019-07-04 17:54:54 -0400577 if info == b'NOT_AVAILABLE' or b'<' in info:
Conley Owens745a39b2013-06-05 13:16:18 -0700578 # If `info` contains '<', we assume the server gave us some sort
579 # of HTML response back, like maybe a login page.
Shawn O. Pearce146fe902009-03-25 14:06:43 -0700580 #
Conley Owens745a39b2013-06-05 13:16:18 -0700581 # Assume HTTP if SSH is not enabled or ssh_info doesn't look right.
Conley Owens2cd38a02014-02-04 15:32:29 -0800582 self._review_url = http_url
Shawn O. Pearce146fe902009-03-25 14:06:43 -0700583 else:
Mike Frysinger1b9adab2019-07-04 17:54:54 -0400584 info = info.decode('utf-8')
Shawn O. Pearcec9571422012-01-11 14:58:54 -0800585 host, port = info.split()
Dan Willemsen16889ba2016-09-22 16:39:06 +0000586 self._review_url = self._SshReviewUrl(userEmail, host, port)
Sarah Owens1f7627f2012-10-31 09:21:55 -0700587 except urllib.error.HTTPError as e:
Shawn O. Pearcec9571422012-01-11 14:58:54 -0800588 raise UploadError('%s: %s' % (self.review, str(e)))
Sarah Owens1f7627f2012-10-31 09:21:55 -0700589 except urllib.error.URLError as e:
Shawn O. Pearcebf1fbb22011-10-11 09:31:58 -0700590 raise UploadError('%s: %s' % (self.review, str(e)))
David Pursehouseecf8f2b2013-05-24 12:12:23 +0900591 except HTTPException as e:
592 raise UploadError('%s: %s' % (self.review, e.__class__.__name__))
Shawn O. Pearceb54a3922009-01-05 16:18:58 -0800593
Shawn O. Pearcec9571422012-01-11 14:58:54 -0800594 REVIEW_CACHE[u] = self._review_url
595 return self._review_url + self.projectname
Shawn O. Pearceb54a3922009-01-05 16:18:58 -0800596
Shawn O. Pearcec9571422012-01-11 14:58:54 -0800597 def _SshReviewUrl(self, userEmail, host, port):
Shawn O. Pearce3575b8f2010-07-15 17:00:14 -0700598 username = self._config.GetString('review.%s.username' % self.review)
599 if username is None:
Shawn O. Pearcec9571422012-01-11 14:58:54 -0800600 username = userEmail.split('@')[0]
601 return 'ssh://%s@%s:%s/' % (username, host, port)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700602
603 def ToLocal(self, rev):
604 """Convert a remote revision string to something we have locally.
605 """
Yann Droneaud936183a2013-09-12 10:51:18 +0200606 if self.name == '.' or IsId(rev):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700607 return rev
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700608
609 if not rev.startswith('refs/'):
610 rev = R_HEADS + rev
611
612 for spec in self.fetch:
613 if spec.SourceMatches(rev):
614 return spec.MapSource(rev)
Nasser Grainawi909d58b2014-09-19 12:13:04 -0600615
616 if not rev.startswith(R_HEADS):
617 return rev
618
Mike Frysinger1f2462e2019-08-03 01:57:09 -0400619 raise GitError('%s: remote %s does not have %s' %
620 (self.projectname, self.name, rev))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700621
622 def WritesTo(self, ref):
623 """True if the remote stores to the tracking ref.
624 """
625 for spec in self.fetch:
626 if spec.DestMatches(ref):
627 return True
628 return False
629
Shawn O. Pearcee284ad12008-11-04 07:37:10 -0800630 def ResetFetch(self, mirror=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700631 """Set the fetch refspec to its default value.
632 """
Shawn O. Pearcee284ad12008-11-04 07:37:10 -0800633 if mirror:
634 dst = 'refs/heads/*'
635 else:
636 dst = 'refs/remotes/%s/*' % self.name
637 self.fetch = [RefSpec(True, 'refs/heads/*', dst)]
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700638
639 def Save(self):
640 """Save this remote to the configuration.
641 """
642 self._Set('url', self.url)
Steve Raed6480452016-08-10 15:00:00 -0700643 if self.pushUrl is not None:
644 self._Set('pushurl', self.pushUrl + '/' + self.projectname)
645 else:
646 self._Set('pushurl', self.pushUrl)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700647 self._Set('review', self.review)
Shawn O. Pearce339ba9f2008-11-06 09:52:51 -0800648 self._Set('projectname', self.projectname)
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530649 self._Set('fetch', list(map(str, self.fetch)))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700650
651 def _Set(self, key, value):
652 key = 'remote.%s.%s' % (self.name, key)
653 return self._config.SetString(key, value)
654
David Pursehouse8a68ff92012-09-24 12:15:13 +0900655 def _Get(self, key, all_keys=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700656 key = 'remote.%s.%s' % (self.name, key)
David Pursehousee5913ae2020-02-12 13:56:59 +0900657 return self._config.GetString(key, all_keys=all_keys)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700658
659
660class Branch(object):
661 """Configuration options related to a single branch.
662 """
David Pursehouse819827a2020-02-12 15:20:19 +0900663
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700664 def __init__(self, config, name):
665 self._config = config
666 self.name = name
667 self.merge = self._Get('merge')
668
669 r = self._Get('remote')
670 if r:
671 self.remote = self._config.GetRemote(r)
672 else:
673 self.remote = None
674
675 @property
676 def LocalMerge(self):
677 """Convert the merge spec to a local name.
678 """
679 if self.remote and self.merge:
680 return self.remote.ToLocal(self.merge)
681 return None
682
683 def Save(self):
684 """Save this branch back into the configuration.
685 """
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -0700686 if self._config.HasSection('branch', self.name):
687 if self.remote:
688 self._Set('remote', self.remote.name)
689 else:
690 self._Set('remote', None)
691 self._Set('merge', self.merge)
692
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700693 else:
Mike Frysinger3164d402019-11-11 05:40:22 -0500694 with open(self._config.file, 'a') as fd:
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -0700695 fd.write('[branch "%s"]\n' % self.name)
696 if self.remote:
697 fd.write('\tremote = %s\n' % self.remote.name)
698 if self.merge:
699 fd.write('\tmerge = %s\n' % self.merge)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700700
701 def _Set(self, key, value):
702 key = 'branch.%s.%s' % (self.name, key)
703 return self._config.SetString(key, value)
704
David Pursehouse8a68ff92012-09-24 12:15:13 +0900705 def _Get(self, key, all_keys=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700706 key = 'branch.%s.%s' % (self.name, key)
David Pursehousee5913ae2020-02-12 13:56:59 +0900707 return self._config.GetString(key, all_keys=all_keys)