blob: 9af90dfdfd8ea9f5cdef2d241ffecda9a345336b [file] [log] [blame]
Mike Frysingerf6013762019-06-13 02:30:51 -04001# -*- coding:utf-8 -*-
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002#
3# Copyright (C) 2008 The Android Open Source Project
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16
Sarah Owenscecd1d82012-11-01 22:59:27 -070017from __future__ import print_function
Chirayu Desai217ea7d2013-03-01 19:14:38 +053018
Dan Willemsen0745bb22015-08-17 13:41:45 -070019import contextlib
20import errno
Anthony King85b24ac2014-05-06 15:57:48 +010021import json
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070022import os
23import re
Mike Frysingeraf1e5de2020-02-17 14:58:37 -050024import signal
Łukasz Gardońbed59ce2017-08-08 10:18:11 +020025import ssl
Shawn O. Pearcefb231612009-04-10 18:53:46 -070026import subprocess
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070027import sys
Doug Anderson0048b692010-12-21 13:39:23 -080028try:
29 import threading as _threading
30except ImportError:
31 import dummy_threading as _threading
Shawn O. Pearcefb231612009-04-10 18:53:46 -070032import time
David Pursehouse59bbb582013-05-17 10:49:33 +090033
34from pyversion import is_python3
35if is_python3():
Sarah Owens1f7627f2012-10-31 09:21:55 -070036 import urllib.request
37 import urllib.error
38else:
David Pursehouse59bbb582013-05-17 10:49:33 +090039 import urllib2
Sarah Owens1f7627f2012-10-31 09:21:55 -070040 import imp
41 urllib = imp.new_module('urllib')
42 urllib.request = urllib2
43 urllib.error = urllib2
Shawn O. Pearcef00e0ce2009-08-22 18:39:49 -070044
Shawn O. Pearceb54a3922009-01-05 16:18:58 -080045from error import GitError, UploadError
Renaud Paquay010fed72016-11-11 14:25:29 -080046import platform_utils
Mike Frysinger8a11f6f2019-08-27 00:26:15 -040047from repo_trace import Trace
David Pursehouseecf8f2b2013-05-24 12:12:23 +090048if is_python3():
49 from http.client import HTTPException
50else:
51 from httplib import HTTPException
Shawn O. Pearceca8c32c2010-05-11 18:21:33 -070052
53from git_command import GitCommand
54from git_command import ssh_sock
55from git_command import terminate_ssh_clients
Zac Livingston9ead97b2017-06-13 08:29:04 -060056from git_refs import R_CHANGES, R_HEADS, R_TAGS
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070057
David Pursehouse1d947b32012-10-25 12:23:11 +090058ID_RE = re.compile(r'^[0-9a-f]{40}$')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070059
Shawn O. Pearce146fe902009-03-25 14:06:43 -070060REVIEW_CACHE = dict()
61
David Pursehouse819827a2020-02-12 15:20:19 +090062
Zac Livingston9ead97b2017-06-13 08:29:04 -060063def IsChange(rev):
64 return rev.startswith(R_CHANGES)
65
David Pursehouse819827a2020-02-12 15:20:19 +090066
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070067def IsId(rev):
68 return ID_RE.match(rev)
69
David Pursehouse819827a2020-02-12 15:20:19 +090070
Zac Livingston9ead97b2017-06-13 08:29:04 -060071def IsTag(rev):
72 return rev.startswith(R_TAGS)
73
David Pursehouse819827a2020-02-12 15:20:19 +090074
Zac Livingston9ead97b2017-06-13 08:29:04 -060075def IsImmutable(rev):
76 return IsChange(rev) or IsId(rev) or IsTag(rev)
77
David Pursehouse819827a2020-02-12 15:20:19 +090078
Shawn O. Pearcef8e32732009-04-17 11:00:31 -070079def _key(name):
80 parts = name.split('.')
81 if len(parts) < 2:
82 return name.lower()
David Pursehouse54a4e602020-02-12 14:31:05 +090083 parts[0] = parts[0].lower()
Shawn O. Pearcef8e32732009-04-17 11:00:31 -070084 parts[-1] = parts[-1].lower()
85 return '.'.join(parts)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070086
David Pursehouse819827a2020-02-12 15:20:19 +090087
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070088class GitConfig(object):
Shawn O. Pearce90be5c02008-10-29 15:21:24 -070089 _ForUser = None
90
Mike Frysingerf841ca42020-02-18 21:31:51 -050091 _USER_CONFIG = '~/.gitconfig'
92
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070093 @classmethod
94 def ForUser(cls):
Shawn O. Pearce90be5c02008-10-29 15:21:24 -070095 if cls._ForUser is None:
Mike Frysingerf841ca42020-02-18 21:31:51 -050096 cls._ForUser = cls(configfile=os.path.expanduser(cls._USER_CONFIG))
Shawn O. Pearce90be5c02008-10-29 15:21:24 -070097 return cls._ForUser
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070098
99 @classmethod
100 def ForRepository(cls, gitdir, defaults=None):
David Pursehousee5913ae2020-02-12 13:56:59 +0900101 return cls(configfile=os.path.join(gitdir, 'config'),
102 defaults=defaults)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700103
Anthony King85b24ac2014-05-06 15:57:48 +0100104 def __init__(self, configfile, defaults=None, jsonFile=None):
David Pursehouse8a68ff92012-09-24 12:15:13 +0900105 self.file = configfile
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700106 self.defaults = defaults
107 self._cache_dict = None
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -0700108 self._section_dict = None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700109 self._remotes = {}
110 self._branches = {}
Shawn O. Pearce1b34c912009-05-21 18:52:49 -0700111
Anthony King85b24ac2014-05-06 15:57:48 +0100112 self._json = jsonFile
113 if self._json is None:
114 self._json = os.path.join(
David Pursehouseabdf7502020-02-12 14:58:39 +0900115 os.path.dirname(self.file),
116 '.repo_' + os.path.basename(self.file) + '.json')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700117
David Pursehousee5913ae2020-02-12 13:56:59 +0900118 def Has(self, name, include_defaults=True):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700119 """Return true if this configuration file has the key.
120 """
Shawn O. Pearcef8e32732009-04-17 11:00:31 -0700121 if _key(name) in self._cache:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700122 return True
123 if include_defaults and self.defaults:
David Pursehousee5913ae2020-02-12 13:56:59 +0900124 return self.defaults.Has(name, include_defaults=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700125 return False
126
Mike Frysinger77b43972020-02-19 17:55:22 -0500127 def GetInt(self, name):
128 """Returns an integer from the configuration file.
129
130 This follows the git config syntax.
131
132 Args:
133 name: The key to lookup.
134
135 Returns:
136 None if the value was not defined, or is not a boolean.
137 Otherwise, the number itself.
138 """
139 v = self.GetString(name)
140 if v is None:
141 return None
142 v = v.strip()
143
144 mult = 1
145 if v.endswith('k'):
146 v = v[:-1]
147 mult = 1024
148 elif v.endswith('m'):
149 v = v[:-1]
150 mult = 1024 * 1024
151 elif v.endswith('g'):
152 v = v[:-1]
153 mult = 1024 * 1024 * 1024
154
155 base = 10
156 if v.startswith('0x'):
157 base = 16
158
159 try:
160 return int(v, base=base) * mult
161 except ValueError:
162 return None
163
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700164 def GetBoolean(self, name):
165 """Returns a boolean from the configuration file.
166 None : The value was not defined, or is not a boolean.
167 True : The value was set to true or yes.
168 False: The value was set to false or no.
169 """
170 v = self.GetString(name)
171 if v is None:
172 return None
173 v = v.lower()
174 if v in ('true', 'yes'):
175 return True
176 if v in ('false', 'no'):
177 return False
178 return None
179
David Pursehouse8a68ff92012-09-24 12:15:13 +0900180 def GetString(self, name, all_keys=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700181 """Get the first value for a key, or None if it is not defined.
182
183 This configuration file is used first, if the key is not
David Pursehouse8a68ff92012-09-24 12:15:13 +0900184 defined or all_keys = True then the defaults are also searched.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700185 """
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700186 try:
Shawn O. Pearcef8e32732009-04-17 11:00:31 -0700187 v = self._cache[_key(name)]
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700188 except KeyError:
189 if self.defaults:
David Pursehousee5913ae2020-02-12 13:56:59 +0900190 return self.defaults.GetString(name, all_keys=all_keys)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700191 v = []
192
David Pursehouse8a68ff92012-09-24 12:15:13 +0900193 if not all_keys:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700194 if v:
195 return v[0]
196 return None
197
198 r = []
199 r.extend(v)
200 if self.defaults:
David Pursehousee5913ae2020-02-12 13:56:59 +0900201 r.extend(self.defaults.GetString(name, all_keys=True))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700202 return r
203
204 def SetString(self, name, value):
205 """Set the value(s) for a key.
206 Only this configuration file is modified.
207
208 The supplied value should be either a string,
209 or a list of strings (to store multiple values).
210 """
Shawn O. Pearcef8e32732009-04-17 11:00:31 -0700211 key = _key(name)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700212
213 try:
Shawn O. Pearcef8e32732009-04-17 11:00:31 -0700214 old = self._cache[key]
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700215 except KeyError:
216 old = []
217
218 if value is None:
219 if old:
Shawn O. Pearcef8e32732009-04-17 11:00:31 -0700220 del self._cache[key]
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700221 self._do('--unset-all', name)
222
223 elif isinstance(value, list):
224 if len(value) == 0:
225 self.SetString(name, None)
226
227 elif len(value) == 1:
228 self.SetString(name, value[0])
229
230 elif old != value:
Shawn O. Pearcef8e32732009-04-17 11:00:31 -0700231 self._cache[key] = list(value)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700232 self._do('--replace-all', name, value[0])
Sarah Owensa6053d52012-11-01 13:36:50 -0700233 for i in range(1, len(value)):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700234 self._do('--add', name, value[i])
235
236 elif len(old) != 1 or old[0] != value:
Shawn O. Pearcef8e32732009-04-17 11:00:31 -0700237 self._cache[key] = [value]
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700238 self._do('--replace-all', name, value)
239
240 def GetRemote(self, name):
241 """Get the remote.$name.* configuration values as an object.
242 """
243 try:
244 r = self._remotes[name]
245 except KeyError:
246 r = Remote(self, name)
247 self._remotes[r.name] = r
248 return r
249
250 def GetBranch(self, name):
251 """Get the branch.$name.* configuration values as an object.
252 """
253 try:
254 b = self._branches[name]
255 except KeyError:
256 b = Branch(self, name)
257 self._branches[b.name] = b
258 return b
259
Shawn O. Pearce366ad212009-05-19 12:47:37 -0700260 def GetSubSections(self, section):
261 """List all subsection names matching $section.*.*
262 """
263 return self._sections.get(section, set())
264
David Pursehousee5913ae2020-02-12 13:56:59 +0900265 def HasSection(self, section, subsection=''):
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -0700266 """Does at least one key in section.subsection exist?
267 """
268 try:
269 return subsection in self._sections[section]
270 except KeyError:
271 return False
272
Shawn O. Pearce13111b42011-09-19 11:00:31 -0700273 def UrlInsteadOf(self, url):
274 """Resolve any url.*.insteadof references.
275 """
276 for new_url in self.GetSubSections('url'):
Dan Willemsen4e4d40f2013-10-28 22:28:42 -0700277 for old_url in self.GetString('url.%s.insteadof' % new_url, True):
278 if old_url is not None and url.startswith(old_url):
279 return new_url + url[len(old_url):]
Shawn O. Pearce13111b42011-09-19 11:00:31 -0700280 return url
281
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -0700282 @property
283 def _sections(self):
284 d = self._section_dict
285 if d is None:
286 d = {}
287 for name in self._cache.keys():
288 p = name.split('.')
289 if 2 == len(p):
290 section = p[0]
291 subsect = ''
292 else:
293 section = p[0]
294 subsect = '.'.join(p[1:-1])
295 if section not in d:
296 d[section] = set()
297 d[section].add(subsect)
298 self._section_dict = d
299 return d
300
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700301 @property
302 def _cache(self):
303 if self._cache_dict is None:
304 self._cache_dict = self._Read()
305 return self._cache_dict
306
307 def _Read(self):
Anthony King85b24ac2014-05-06 15:57:48 +0100308 d = self._ReadJson()
Shawn O. Pearcec12c3602009-04-17 21:03:32 -0700309 if d is None:
310 d = self._ReadGit()
Anthony King85b24ac2014-05-06 15:57:48 +0100311 self._SaveJson(d)
Shawn O. Pearcec12c3602009-04-17 21:03:32 -0700312 return d
313
Anthony King85b24ac2014-05-06 15:57:48 +0100314 def _ReadJson(self):
Shawn O. Pearcec12c3602009-04-17 21:03:32 -0700315 try:
David Pursehouse16a5c3a2020-02-12 15:54:26 +0900316 if os.path.getmtime(self._json) <= os.path.getmtime(self.file):
Renaud Paquay010fed72016-11-11 14:25:29 -0800317 platform_utils.remove(self._json)
Shawn O. Pearcec12c3602009-04-17 21:03:32 -0700318 return None
319 except OSError:
320 return None
321 try:
Anthony King85b24ac2014-05-06 15:57:48 +0100322 Trace(': parsing %s', self.file)
Mike Frysinger3164d402019-11-11 05:40:22 -0500323 with open(self._json) as fd:
Anthony King85b24ac2014-05-06 15:57:48 +0100324 return json.load(fd)
Anthony King85b24ac2014-05-06 15:57:48 +0100325 except (IOError, ValueError):
Renaud Paquay010fed72016-11-11 14:25:29 -0800326 platform_utils.remove(self._json)
Shawn O. Pearcec12c3602009-04-17 21:03:32 -0700327 return None
328
Anthony King85b24ac2014-05-06 15:57:48 +0100329 def _SaveJson(self, cache):
Shawn O. Pearcec12c3602009-04-17 21:03:32 -0700330 try:
Mike Frysinger3164d402019-11-11 05:40:22 -0500331 with open(self._json, 'w') as fd:
Anthony King85b24ac2014-05-06 15:57:48 +0100332 json.dump(cache, fd, indent=2)
Anthony King85b24ac2014-05-06 15:57:48 +0100333 except (IOError, TypeError):
Anthony Kingb1d1fd72015-06-03 17:02:26 +0100334 if os.path.exists(self._json):
Renaud Paquay010fed72016-11-11 14:25:29 -0800335 platform_utils.remove(self._json)
Shawn O. Pearcec12c3602009-04-17 21:03:32 -0700336
337 def _ReadGit(self):
David Aguilar438c5472009-06-28 15:09:16 -0700338 """
339 Read configuration data from git.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700340
David Aguilar438c5472009-06-28 15:09:16 -0700341 This internal method populates the GitConfig cache.
342
343 """
David Aguilar438c5472009-06-28 15:09:16 -0700344 c = {}
Shawn O. Pearcec24c7202009-07-02 16:12:57 -0700345 d = self._do('--null', '--list')
346 if d is None:
347 return c
Dylan Denge469a0c2018-06-23 15:02:26 +0800348 if not is_python3():
349 d = d.decode('utf-8')
350 for line in d.rstrip('\0').split('\0'):
David Aguilar438c5472009-06-28 15:09:16 -0700351 if '\n' in line:
David Pursehousec1b86a22012-11-14 11:36:51 +0900352 key, val = line.split('\n', 1)
David Aguilar438c5472009-06-28 15:09:16 -0700353 else:
David Pursehousec1b86a22012-11-14 11:36:51 +0900354 key = line
355 val = None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700356
357 if key in c:
358 c[key].append(val)
359 else:
360 c[key] = [val]
361
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700362 return c
363
364 def _do(self, *args):
Ulrik Laurénd0ca0f62020-04-28 01:09:57 +0200365 command = ['config', '--file', self.file, '--includes']
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700366 command.extend(args)
367
368 p = GitCommand(None,
369 command,
David Pursehousee5913ae2020-02-12 13:56:59 +0900370 capture_stdout=True,
371 capture_stderr=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700372 if p.Wait() == 0:
373 return p.stdout
374 else:
375 GitError('git config %s: %s' % (str(args), p.stderr))
376
377
Mike Frysingerf841ca42020-02-18 21:31:51 -0500378class RepoConfig(GitConfig):
379 """User settings for repo itself."""
380
381 _USER_CONFIG = '~/.repoconfig/config'
382
383
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700384class RefSpec(object):
385 """A Git refspec line, split into its components:
386
387 forced: True if the line starts with '+'
388 src: Left side of the line
389 dst: Right side of the line
390 """
391
392 @classmethod
393 def FromString(cls, rs):
394 lhs, rhs = rs.split(':', 2)
395 if lhs.startswith('+'):
396 lhs = lhs[1:]
397 forced = True
398 else:
399 forced = False
400 return cls(forced, lhs, rhs)
401
402 def __init__(self, forced, lhs, rhs):
403 self.forced = forced
404 self.src = lhs
405 self.dst = rhs
406
407 def SourceMatches(self, rev):
408 if self.src:
409 if rev == self.src:
410 return True
411 if self.src.endswith('/*') and rev.startswith(self.src[:-1]):
412 return True
413 return False
414
415 def DestMatches(self, ref):
416 if self.dst:
417 if ref == self.dst:
418 return True
419 if self.dst.endswith('/*') and ref.startswith(self.dst[:-1]):
420 return True
421 return False
422
423 def MapSource(self, rev):
424 if self.src.endswith('/*'):
425 return self.dst[:-1] + rev[len(self.src) - 1:]
426 return self.dst
427
428 def __str__(self):
429 s = ''
430 if self.forced:
431 s += '+'
432 if self.src:
433 s += self.src
434 if self.dst:
435 s += ':'
436 s += self.dst
437 return s
438
439
Doug Anderson06d029c2010-10-27 17:06:01 -0700440_master_processes = []
441_master_keys = set()
Shawn O. Pearcefb231612009-04-10 18:53:46 -0700442_ssh_master = True
Doug Anderson0048b692010-12-21 13:39:23 -0800443_master_keys_lock = None
444
David Pursehouse819827a2020-02-12 15:20:19 +0900445
Doug Anderson0048b692010-12-21 13:39:23 -0800446def init_ssh():
447 """Should be called once at the start of repo to init ssh master handling.
448
449 At the moment, all we do is to create our lock.
450 """
451 global _master_keys_lock
452 assert _master_keys_lock is None, "Should only call init_ssh once"
453 _master_keys_lock = _threading.Lock()
Shawn O. Pearcefb231612009-04-10 18:53:46 -0700454
David Pursehouse819827a2020-02-12 15:20:19 +0900455
Josh Guilfoyle71985722009-08-16 09:44:40 -0700456def _open_ssh(host, port=None):
Shawn O. Pearcefb231612009-04-10 18:53:46 -0700457 global _ssh_master
458
Doug Anderson0048b692010-12-21 13:39:23 -0800459 # Acquire the lock. This is needed to prevent opening multiple masters for
460 # the same host when we're running "repo sync -jN" (for N > 1) _and_ the
461 # manifest <remote fetch="ssh://xyz"> specifies a different host from the
462 # one that was passed to repo init.
463 _master_keys_lock.acquire()
Doug Anderson06d029c2010-10-27 17:06:01 -0700464 try:
Doug Anderson06d029c2010-10-27 17:06:01 -0700465
Doug Anderson0048b692010-12-21 13:39:23 -0800466 # Check to see whether we already think that the master is running; if we
467 # think it's already running, return right away.
468 if port is not None:
469 key = '%s:%s' % (host, port)
470 else:
471 key = host
472
473 if key in _master_keys:
Doug Anderson06d029c2010-10-27 17:06:01 -0700474 return True
Doug Anderson06d029c2010-10-27 17:06:01 -0700475
David Pursehouse16a5c3a2020-02-12 15:54:26 +0900476 if (not _ssh_master
477 or 'GIT_SSH' in os.environ
478 or sys.platform in ('win32', 'cygwin')):
Doug Anderson0048b692010-12-21 13:39:23 -0800479 # failed earlier, or cygwin ssh can't do this
480 #
481 return False
Shawn O. Pearcefb231612009-04-10 18:53:46 -0700482
Doug Anderson0048b692010-12-21 13:39:23 -0800483 # We will make two calls to ssh; this is the common part of both calls.
484 command_base = ['ssh',
David Pursehouseabdf7502020-02-12 14:58:39 +0900485 '-o', 'ControlPath %s' % ssh_sock(),
486 host]
Doug Anderson0048b692010-12-21 13:39:23 -0800487 if port is not None:
David Pursehouse8f62fb72012-11-14 12:09:38 +0900488 command_base[1:1] = ['-p', str(port)]
Doug Anderson0048b692010-12-21 13:39:23 -0800489
490 # Since the key wasn't in _master_keys, we think that master isn't running.
491 # ...but before actually starting a master, we'll double-check. This can
492 # be important because we can't tell that that 'git@myhost.com' is the same
493 # as 'myhost.com' where "User git" is setup in the user's ~/.ssh/config file.
David Pursehouse54a4e602020-02-12 14:31:05 +0900494 check_command = command_base + ['-O', 'check']
Doug Anderson0048b692010-12-21 13:39:23 -0800495 try:
496 Trace(': %s', ' '.join(check_command))
497 check_process = subprocess.Popen(check_command,
498 stdout=subprocess.PIPE,
499 stderr=subprocess.PIPE)
David Pursehouse54a4e602020-02-12 14:31:05 +0900500 check_process.communicate() # read output, but ignore it...
Doug Anderson0048b692010-12-21 13:39:23 -0800501 isnt_running = check_process.wait()
502
503 if not isnt_running:
504 # Our double-check found that the master _was_ infact running. Add to
505 # the list of keys.
506 _master_keys.add(key)
507 return True
508 except Exception:
509 # Ignore excpetions. We we will fall back to the normal command and print
510 # to the log there.
511 pass
512
David Pursehouse0ab95ba2020-02-12 15:01:59 +0900513 command = command_base[:1] + ['-M', '-N'] + command_base[1:]
Doug Anderson0048b692010-12-21 13:39:23 -0800514 try:
515 Trace(': %s', ' '.join(command))
516 p = subprocess.Popen(command)
Sarah Owensa5be53f2012-09-09 15:37:57 -0700517 except Exception as e:
Doug Anderson0048b692010-12-21 13:39:23 -0800518 _ssh_master = False
Sarah Owenscecd1d82012-11-01 22:59:27 -0700519 print('\nwarn: cannot enable ssh control master for %s:%s\n%s'
David Pursehouseabdf7502020-02-12 14:58:39 +0900520 % (host, port, str(e)), file=sys.stderr)
Doug Anderson0048b692010-12-21 13:39:23 -0800521 return False
522
Timo Lotterbach05dc46b2016-07-15 16:48:42 +0200523 time.sleep(1)
524 ssh_died = (p.poll() is not None)
525 if ssh_died:
526 return False
527
Doug Anderson0048b692010-12-21 13:39:23 -0800528 _master_processes.append(p)
529 _master_keys.add(key)
Doug Anderson0048b692010-12-21 13:39:23 -0800530 return True
531 finally:
532 _master_keys_lock.release()
Shawn O. Pearcefb231612009-04-10 18:53:46 -0700533
David Pursehouse819827a2020-02-12 15:20:19 +0900534
Shawn O. Pearcefb231612009-04-10 18:53:46 -0700535def close_ssh():
Doug Anderson0048b692010-12-21 13:39:23 -0800536 global _master_keys_lock
537
Shawn O. Pearceca8c32c2010-05-11 18:21:33 -0700538 terminate_ssh_clients()
539
Doug Anderson06d029c2010-10-27 17:06:01 -0700540 for p in _master_processes:
Shawn O. Pearce26120ca2009-06-16 11:49:10 -0700541 try:
Mike Frysingeraf1e5de2020-02-17 14:58:37 -0500542 os.kill(p.pid, signal.SIGTERM)
Shawn O. Pearce26120ca2009-06-16 11:49:10 -0700543 p.wait()
Shawn O. Pearcefb5c8fd2009-06-16 14:57:46 -0700544 except OSError:
Shawn O. Pearce26120ca2009-06-16 11:49:10 -0700545 pass
Doug Anderson06d029c2010-10-27 17:06:01 -0700546 del _master_processes[:]
547 _master_keys.clear()
Shawn O. Pearcefb231612009-04-10 18:53:46 -0700548
Nico Sallembien1c85f4e2010-04-27 14:35:27 -0700549 d = ssh_sock(create=False)
Shawn O. Pearcefb231612009-04-10 18:53:46 -0700550 if d:
551 try:
Renaud Paquaybed8b622018-09-27 10:46:58 -0700552 platform_utils.rmdir(os.path.dirname(d))
Shawn O. Pearcefb231612009-04-10 18:53:46 -0700553 except OSError:
554 pass
555
Doug Anderson0048b692010-12-21 13:39:23 -0800556 # We're done with the lock, so we can delete it.
557 _master_keys_lock = None
558
David Pursehouse819827a2020-02-12 15:20:19 +0900559
Shawn O. Pearcefb231612009-04-10 18:53:46 -0700560URI_SCP = re.compile(r'^([^@:]*@?[^:/]{1,}):')
Shawn O. Pearce898e12a2012-03-14 15:22:28 -0700561URI_ALL = re.compile(r'^([a-z][a-z+-]*)://([^@/]*@?[^/]*)/')
Shawn O. Pearcefb231612009-04-10 18:53:46 -0700562
David Pursehouse819827a2020-02-12 15:20:19 +0900563
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700564def GetSchemeFromUrl(url):
565 m = URI_ALL.match(url)
566 if m:
567 return m.group(1)
568 return None
569
David Pursehouse819827a2020-02-12 15:20:19 +0900570
Dan Willemsen0745bb22015-08-17 13:41:45 -0700571@contextlib.contextmanager
572def GetUrlCookieFile(url, quiet):
573 if url.startswith('persistent-'):
574 try:
575 p = subprocess.Popen(
576 ['git-remote-persistent-https', '-print_config', url],
577 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
578 stderr=subprocess.PIPE)
579 try:
580 cookieprefix = 'http.cookiefile='
581 proxyprefix = 'http.proxy='
582 cookiefile = None
583 proxy = None
584 for line in p.stdout:
Mike Frysingerded477d2020-02-07 23:18:23 -0500585 line = line.strip().decode('utf-8')
Dan Willemsen0745bb22015-08-17 13:41:45 -0700586 if line.startswith(cookieprefix):
Daichi Ueurace7e0262018-02-26 08:49:36 +0900587 cookiefile = os.path.expanduser(line[len(cookieprefix):])
Dan Willemsen0745bb22015-08-17 13:41:45 -0700588 if line.startswith(proxyprefix):
589 proxy = line[len(proxyprefix):]
590 # Leave subprocess open, as cookie file may be transient.
591 if cookiefile or proxy:
592 yield cookiefile, proxy
593 return
594 finally:
595 p.stdin.close()
596 if p.wait():
Mike Frysingerded477d2020-02-07 23:18:23 -0500597 err_msg = p.stderr.read().decode('utf-8')
Dan Willemsen0745bb22015-08-17 13:41:45 -0700598 if ' -print_config' in err_msg:
599 pass # Persistent proxy doesn't support -print_config.
600 elif not quiet:
601 print(err_msg, file=sys.stderr)
602 except OSError as e:
603 if e.errno == errno.ENOENT:
604 pass # No persistent proxy.
605 raise
Daichi Ueurace7e0262018-02-26 08:49:36 +0900606 cookiefile = GitConfig.ForUser().GetString('http.cookiefile')
607 if cookiefile:
608 cookiefile = os.path.expanduser(cookiefile)
609 yield cookiefile, None
Dan Willemsen0745bb22015-08-17 13:41:45 -0700610
David Pursehouse819827a2020-02-12 15:20:19 +0900611
Shawn O. Pearcefb231612009-04-10 18:53:46 -0700612def _preconnect(url):
613 m = URI_ALL.match(url)
614 if m:
615 scheme = m.group(1)
616 host = m.group(2)
617 if ':' in host:
618 host, port = host.split(':')
Shawn O. Pearce896d5df2009-04-21 14:51:04 -0700619 else:
Josh Guilfoyle71985722009-08-16 09:44:40 -0700620 port = None
Shawn O. Pearcefb231612009-04-10 18:53:46 -0700621 if scheme in ('ssh', 'git+ssh', 'ssh+git'):
622 return _open_ssh(host, port)
623 return False
624
625 m = URI_SCP.match(url)
626 if m:
627 host = m.group(1)
Josh Guilfoyle71985722009-08-16 09:44:40 -0700628 return _open_ssh(host)
Shawn O. Pearcefb231612009-04-10 18:53:46 -0700629
Shawn O. Pearce7b4f4352009-06-12 09:06:35 -0700630 return False
Shawn O. Pearcefb231612009-04-10 18:53:46 -0700631
David Pursehouse819827a2020-02-12 15:20:19 +0900632
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700633class Remote(object):
634 """Configuration options related to a remote.
635 """
David Pursehouse819827a2020-02-12 15:20:19 +0900636
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700637 def __init__(self, config, name):
638 self._config = config
639 self.name = name
640 self.url = self._Get('url')
Steve Raed6480452016-08-10 15:00:00 -0700641 self.pushUrl = self._Get('pushurl')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700642 self.review = self._Get('review')
Shawn O. Pearce339ba9f2008-11-06 09:52:51 -0800643 self.projectname = self._Get('projectname')
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530644 self.fetch = list(map(RefSpec.FromString,
David Pursehouseabdf7502020-02-12 14:58:39 +0900645 self._Get('fetch', all_keys=True)))
Shawn O. Pearcec9571422012-01-11 14:58:54 -0800646 self._review_url = None
Shawn O. Pearceb54a3922009-01-05 16:18:58 -0800647
Ulrik Sjolinb6ea3bf2010-01-03 18:20:17 +0100648 def _InsteadOf(self):
649 globCfg = GitConfig.ForUser()
650 urlList = globCfg.GetSubSections('url')
651 longest = ""
652 longestUrl = ""
653
654 for url in urlList:
655 key = "url." + url + ".insteadOf"
David Pursehouse8a68ff92012-09-24 12:15:13 +0900656 insteadOfList = globCfg.GetString(key, all_keys=True)
Ulrik Sjolinb6ea3bf2010-01-03 18:20:17 +0100657
658 for insteadOf in insteadOfList:
David Pursehouse16a5c3a2020-02-12 15:54:26 +0900659 if (self.url.startswith(insteadOf)
660 and len(insteadOf) > len(longest)):
Ulrik Sjolinb6ea3bf2010-01-03 18:20:17 +0100661 longest = insteadOf
662 longestUrl = url
663
664 if len(longest) == 0:
665 return self.url
666
667 return self.url.replace(longest, longestUrl, 1)
668
Shawn O. Pearcefb231612009-04-10 18:53:46 -0700669 def PreConnectFetch(self):
Ulrik Sjolinb6ea3bf2010-01-03 18:20:17 +0100670 connectionUrl = self._InsteadOf()
671 return _preconnect(connectionUrl)
Shawn O. Pearcefb231612009-04-10 18:53:46 -0700672
Łukasz Gardońbed59ce2017-08-08 10:18:11 +0200673 def ReviewUrl(self, userEmail, validate_certs):
Shawn O. Pearcec9571422012-01-11 14:58:54 -0800674 if self._review_url is None:
Shawn O. Pearceb54a3922009-01-05 16:18:58 -0800675 if self.review is None:
676 return None
677
678 u = self.review
Conley Owens7e12e0a2014-10-23 15:40:00 -0700679 if u.startswith('persistent-'):
680 u = u[len('persistent-'):]
Christian Koestlin2ec2a5d2016-12-05 20:32:45 +0100681 if u.split(':')[0] not in ('http', 'https', 'sso', 'ssh'):
Shawn O. Pearceb54a3922009-01-05 16:18:58 -0800682 u = 'http://%s' % u
Shawn O. Pearce13cc3842009-03-25 13:54:54 -0700683 if u.endswith('/Gerrit'):
684 u = u[:len(u) - len('/Gerrit')]
Shawn O. Pearcec9571422012-01-11 14:58:54 -0800685 if u.endswith('/ssh_info'):
686 u = u[:len(u) - len('/ssh_info')]
687 if not u.endswith('/'):
David Pursehouse8a68ff92012-09-24 12:15:13 +0900688 u += '/'
Shawn O. Pearcec9571422012-01-11 14:58:54 -0800689 http_url = u
Shawn O. Pearceb54a3922009-01-05 16:18:58 -0800690
Shawn O. Pearce146fe902009-03-25 14:06:43 -0700691 if u in REVIEW_CACHE:
Shawn O. Pearcec9571422012-01-11 14:58:54 -0800692 self._review_url = REVIEW_CACHE[u]
Shawn O. Pearce1a68dc52011-10-11 14:12:46 -0700693 elif 'REPO_HOST_PORT_INFO' in os.environ:
Shawn O. Pearcec9571422012-01-11 14:58:54 -0800694 host, port = os.environ['REPO_HOST_PORT_INFO'].split()
695 self._review_url = self._SshReviewUrl(userEmail, host, port)
696 REVIEW_CACHE[u] = self._review_url
Christian Koestlin2ec2a5d2016-12-05 20:32:45 +0100697 elif u.startswith('sso:') or u.startswith('ssh:'):
Steve Pucci143d8a72014-01-30 09:45:53 -0800698 self._review_url = u # Assume it's right
699 REVIEW_CACHE[u] = self._review_url
Timo Lotterbacheec726c2016-10-07 10:52:08 +0200700 elif 'REPO_IGNORE_SSH_INFO' in os.environ:
701 self._review_url = http_url
702 REVIEW_CACHE[u] = self._review_url
Shawn O. Pearce146fe902009-03-25 14:06:43 -0700703 else:
704 try:
Shawn O. Pearcec9571422012-01-11 14:58:54 -0800705 info_url = u + 'ssh_info'
Łukasz Gardońbed59ce2017-08-08 10:18:11 +0200706 if not validate_certs:
707 context = ssl._create_unverified_context()
708 info = urllib.request.urlopen(info_url, context=context).read()
709 else:
710 info = urllib.request.urlopen(info_url).read()
Mike Frysinger1b9adab2019-07-04 17:54:54 -0400711 if info == b'NOT_AVAILABLE' or b'<' in info:
Conley Owens745a39b2013-06-05 13:16:18 -0700712 # If `info` contains '<', we assume the server gave us some sort
713 # of HTML response back, like maybe a login page.
Shawn O. Pearce146fe902009-03-25 14:06:43 -0700714 #
Conley Owens745a39b2013-06-05 13:16:18 -0700715 # Assume HTTP if SSH is not enabled or ssh_info doesn't look right.
Conley Owens2cd38a02014-02-04 15:32:29 -0800716 self._review_url = http_url
Shawn O. Pearce146fe902009-03-25 14:06:43 -0700717 else:
Mike Frysinger1b9adab2019-07-04 17:54:54 -0400718 info = info.decode('utf-8')
Shawn O. Pearcec9571422012-01-11 14:58:54 -0800719 host, port = info.split()
Dan Willemsen16889ba2016-09-22 16:39:06 +0000720 self._review_url = self._SshReviewUrl(userEmail, host, port)
Sarah Owens1f7627f2012-10-31 09:21:55 -0700721 except urllib.error.HTTPError as e:
Shawn O. Pearcec9571422012-01-11 14:58:54 -0800722 raise UploadError('%s: %s' % (self.review, str(e)))
Sarah Owens1f7627f2012-10-31 09:21:55 -0700723 except urllib.error.URLError as e:
Shawn O. Pearcebf1fbb22011-10-11 09:31:58 -0700724 raise UploadError('%s: %s' % (self.review, str(e)))
David Pursehouseecf8f2b2013-05-24 12:12:23 +0900725 except HTTPException as e:
726 raise UploadError('%s: %s' % (self.review, e.__class__.__name__))
Shawn O. Pearceb54a3922009-01-05 16:18:58 -0800727
Shawn O. Pearcec9571422012-01-11 14:58:54 -0800728 REVIEW_CACHE[u] = self._review_url
729 return self._review_url + self.projectname
Shawn O. Pearceb54a3922009-01-05 16:18:58 -0800730
Shawn O. Pearcec9571422012-01-11 14:58:54 -0800731 def _SshReviewUrl(self, userEmail, host, port):
Shawn O. Pearce3575b8f2010-07-15 17:00:14 -0700732 username = self._config.GetString('review.%s.username' % self.review)
733 if username is None:
Shawn O. Pearcec9571422012-01-11 14:58:54 -0800734 username = userEmail.split('@')[0]
735 return 'ssh://%s@%s:%s/' % (username, host, port)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700736
737 def ToLocal(self, rev):
738 """Convert a remote revision string to something we have locally.
739 """
Yann Droneaud936183a2013-09-12 10:51:18 +0200740 if self.name == '.' or IsId(rev):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700741 return rev
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700742
743 if not rev.startswith('refs/'):
744 rev = R_HEADS + rev
745
746 for spec in self.fetch:
747 if spec.SourceMatches(rev):
748 return spec.MapSource(rev)
Nasser Grainawi909d58b2014-09-19 12:13:04 -0600749
750 if not rev.startswith(R_HEADS):
751 return rev
752
Mike Frysinger1f2462e2019-08-03 01:57:09 -0400753 raise GitError('%s: remote %s does not have %s' %
754 (self.projectname, self.name, rev))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700755
756 def WritesTo(self, ref):
757 """True if the remote stores to the tracking ref.
758 """
759 for spec in self.fetch:
760 if spec.DestMatches(ref):
761 return True
762 return False
763
Shawn O. Pearcee284ad12008-11-04 07:37:10 -0800764 def ResetFetch(self, mirror=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700765 """Set the fetch refspec to its default value.
766 """
Shawn O. Pearcee284ad12008-11-04 07:37:10 -0800767 if mirror:
768 dst = 'refs/heads/*'
769 else:
770 dst = 'refs/remotes/%s/*' % self.name
771 self.fetch = [RefSpec(True, 'refs/heads/*', dst)]
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700772
773 def Save(self):
774 """Save this remote to the configuration.
775 """
776 self._Set('url', self.url)
Steve Raed6480452016-08-10 15:00:00 -0700777 if self.pushUrl is not None:
778 self._Set('pushurl', self.pushUrl + '/' + self.projectname)
779 else:
780 self._Set('pushurl', self.pushUrl)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700781 self._Set('review', self.review)
Shawn O. Pearce339ba9f2008-11-06 09:52:51 -0800782 self._Set('projectname', self.projectname)
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530783 self._Set('fetch', list(map(str, self.fetch)))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700784
785 def _Set(self, key, value):
786 key = 'remote.%s.%s' % (self.name, key)
787 return self._config.SetString(key, value)
788
David Pursehouse8a68ff92012-09-24 12:15:13 +0900789 def _Get(self, key, all_keys=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700790 key = 'remote.%s.%s' % (self.name, key)
David Pursehousee5913ae2020-02-12 13:56:59 +0900791 return self._config.GetString(key, all_keys=all_keys)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700792
793
794class Branch(object):
795 """Configuration options related to a single branch.
796 """
David Pursehouse819827a2020-02-12 15:20:19 +0900797
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700798 def __init__(self, config, name):
799 self._config = config
800 self.name = name
801 self.merge = self._Get('merge')
802
803 r = self._Get('remote')
804 if r:
805 self.remote = self._config.GetRemote(r)
806 else:
807 self.remote = None
808
809 @property
810 def LocalMerge(self):
811 """Convert the merge spec to a local name.
812 """
813 if self.remote and self.merge:
814 return self.remote.ToLocal(self.merge)
815 return None
816
817 def Save(self):
818 """Save this branch back into the configuration.
819 """
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -0700820 if self._config.HasSection('branch', self.name):
821 if self.remote:
822 self._Set('remote', self.remote.name)
823 else:
824 self._Set('remote', None)
825 self._Set('merge', self.merge)
826
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700827 else:
Mike Frysinger3164d402019-11-11 05:40:22 -0500828 with open(self._config.file, 'a') as fd:
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -0700829 fd.write('[branch "%s"]\n' % self.name)
830 if self.remote:
831 fd.write('\tremote = %s\n' % self.remote.name)
832 if self.merge:
833 fd.write('\tmerge = %s\n' % self.merge)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700834
835 def _Set(self, key, value):
836 key = 'branch.%s.%s' % (self.name, key)
837 return self._config.SetString(key, value)
838
David Pursehouse8a68ff92012-09-24 12:15:13 +0900839 def _Get(self, key, all_keys=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700840 key = 'branch.%s.%s' % (self.name, key)
David Pursehousee5913ae2020-02-12 13:56:59 +0900841 return self._config.GetString(key, all_keys=all_keys)