blob: 6b1f7107d1f153cf832d151adcd26eb240774157 [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
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070091 @classmethod
92 def ForUser(cls):
Shawn O. Pearce90be5c02008-10-29 15:21:24 -070093 if cls._ForUser is None:
David Pursehousee5913ae2020-02-12 13:56:59 +090094 cls._ForUser = cls(configfile=os.path.expanduser('~/.gitconfig'))
Shawn O. Pearce90be5c02008-10-29 15:21:24 -070095 return cls._ForUser
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070096
97 @classmethod
98 def ForRepository(cls, gitdir, defaults=None):
David Pursehousee5913ae2020-02-12 13:56:59 +090099 return cls(configfile=os.path.join(gitdir, 'config'),
100 defaults=defaults)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700101
Anthony King85b24ac2014-05-06 15:57:48 +0100102 def __init__(self, configfile, defaults=None, jsonFile=None):
David Pursehouse8a68ff92012-09-24 12:15:13 +0900103 self.file = configfile
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700104 self.defaults = defaults
105 self._cache_dict = None
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -0700106 self._section_dict = None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700107 self._remotes = {}
108 self._branches = {}
Shawn O. Pearce1b34c912009-05-21 18:52:49 -0700109
Anthony King85b24ac2014-05-06 15:57:48 +0100110 self._json = jsonFile
111 if self._json is None:
112 self._json = os.path.join(
David Pursehouseabdf7502020-02-12 14:58:39 +0900113 os.path.dirname(self.file),
114 '.repo_' + os.path.basename(self.file) + '.json')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700115
David Pursehousee5913ae2020-02-12 13:56:59 +0900116 def Has(self, name, include_defaults=True):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700117 """Return true if this configuration file has the key.
118 """
Shawn O. Pearcef8e32732009-04-17 11:00:31 -0700119 if _key(name) in self._cache:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700120 return True
121 if include_defaults and self.defaults:
David Pursehousee5913ae2020-02-12 13:56:59 +0900122 return self.defaults.Has(name, include_defaults=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700123 return False
124
Mike Frysinger77b43972020-02-19 17:55:22 -0500125 def GetInt(self, name):
126 """Returns an integer from the configuration file.
127
128 This follows the git config syntax.
129
130 Args:
131 name: The key to lookup.
132
133 Returns:
134 None if the value was not defined, or is not a boolean.
135 Otherwise, the number itself.
136 """
137 v = self.GetString(name)
138 if v is None:
139 return None
140 v = v.strip()
141
142 mult = 1
143 if v.endswith('k'):
144 v = v[:-1]
145 mult = 1024
146 elif v.endswith('m'):
147 v = v[:-1]
148 mult = 1024 * 1024
149 elif v.endswith('g'):
150 v = v[:-1]
151 mult = 1024 * 1024 * 1024
152
153 base = 10
154 if v.startswith('0x'):
155 base = 16
156
157 try:
158 return int(v, base=base) * mult
159 except ValueError:
160 return None
161
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700162 def GetBoolean(self, name):
163 """Returns a boolean from the configuration file.
164 None : The value was not defined, or is not a boolean.
165 True : The value was set to true or yes.
166 False: The value was set to false or no.
167 """
168 v = self.GetString(name)
169 if v is None:
170 return None
171 v = v.lower()
172 if v in ('true', 'yes'):
173 return True
174 if v in ('false', 'no'):
175 return False
176 return None
177
David Pursehouse8a68ff92012-09-24 12:15:13 +0900178 def GetString(self, name, all_keys=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700179 """Get the first value for a key, or None if it is not defined.
180
181 This configuration file is used first, if the key is not
David Pursehouse8a68ff92012-09-24 12:15:13 +0900182 defined or all_keys = True then the defaults are also searched.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700183 """
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700184 try:
Shawn O. Pearcef8e32732009-04-17 11:00:31 -0700185 v = self._cache[_key(name)]
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700186 except KeyError:
187 if self.defaults:
David Pursehousee5913ae2020-02-12 13:56:59 +0900188 return self.defaults.GetString(name, all_keys=all_keys)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700189 v = []
190
David Pursehouse8a68ff92012-09-24 12:15:13 +0900191 if not all_keys:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700192 if v:
193 return v[0]
194 return None
195
196 r = []
197 r.extend(v)
198 if self.defaults:
David Pursehousee5913ae2020-02-12 13:56:59 +0900199 r.extend(self.defaults.GetString(name, all_keys=True))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700200 return r
201
202 def SetString(self, name, value):
203 """Set the value(s) for a key.
204 Only this configuration file is modified.
205
206 The supplied value should be either a string,
207 or a list of strings (to store multiple values).
208 """
Shawn O. Pearcef8e32732009-04-17 11:00:31 -0700209 key = _key(name)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700210
211 try:
Shawn O. Pearcef8e32732009-04-17 11:00:31 -0700212 old = self._cache[key]
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700213 except KeyError:
214 old = []
215
216 if value is None:
217 if old:
Shawn O. Pearcef8e32732009-04-17 11:00:31 -0700218 del self._cache[key]
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700219 self._do('--unset-all', name)
220
221 elif isinstance(value, list):
222 if len(value) == 0:
223 self.SetString(name, None)
224
225 elif len(value) == 1:
226 self.SetString(name, value[0])
227
228 elif old != value:
Shawn O. Pearcef8e32732009-04-17 11:00:31 -0700229 self._cache[key] = list(value)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700230 self._do('--replace-all', name, value[0])
Sarah Owensa6053d52012-11-01 13:36:50 -0700231 for i in range(1, len(value)):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700232 self._do('--add', name, value[i])
233
234 elif len(old) != 1 or old[0] != value:
Shawn O. Pearcef8e32732009-04-17 11:00:31 -0700235 self._cache[key] = [value]
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700236 self._do('--replace-all', name, value)
237
238 def GetRemote(self, name):
239 """Get the remote.$name.* configuration values as an object.
240 """
241 try:
242 r = self._remotes[name]
243 except KeyError:
244 r = Remote(self, name)
245 self._remotes[r.name] = r
246 return r
247
248 def GetBranch(self, name):
249 """Get the branch.$name.* configuration values as an object.
250 """
251 try:
252 b = self._branches[name]
253 except KeyError:
254 b = Branch(self, name)
255 self._branches[b.name] = b
256 return b
257
Shawn O. Pearce366ad212009-05-19 12:47:37 -0700258 def GetSubSections(self, section):
259 """List all subsection names matching $section.*.*
260 """
261 return self._sections.get(section, set())
262
David Pursehousee5913ae2020-02-12 13:56:59 +0900263 def HasSection(self, section, subsection=''):
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -0700264 """Does at least one key in section.subsection exist?
265 """
266 try:
267 return subsection in self._sections[section]
268 except KeyError:
269 return False
270
Shawn O. Pearce13111b42011-09-19 11:00:31 -0700271 def UrlInsteadOf(self, url):
272 """Resolve any url.*.insteadof references.
273 """
274 for new_url in self.GetSubSections('url'):
Dan Willemsen4e4d40f2013-10-28 22:28:42 -0700275 for old_url in self.GetString('url.%s.insteadof' % new_url, True):
276 if old_url is not None and url.startswith(old_url):
277 return new_url + url[len(old_url):]
Shawn O. Pearce13111b42011-09-19 11:00:31 -0700278 return url
279
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -0700280 @property
281 def _sections(self):
282 d = self._section_dict
283 if d is None:
284 d = {}
285 for name in self._cache.keys():
286 p = name.split('.')
287 if 2 == len(p):
288 section = p[0]
289 subsect = ''
290 else:
291 section = p[0]
292 subsect = '.'.join(p[1:-1])
293 if section not in d:
294 d[section] = set()
295 d[section].add(subsect)
296 self._section_dict = d
297 return d
298
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700299 @property
300 def _cache(self):
301 if self._cache_dict is None:
302 self._cache_dict = self._Read()
303 return self._cache_dict
304
305 def _Read(self):
Anthony King85b24ac2014-05-06 15:57:48 +0100306 d = self._ReadJson()
Shawn O. Pearcec12c3602009-04-17 21:03:32 -0700307 if d is None:
308 d = self._ReadGit()
Anthony King85b24ac2014-05-06 15:57:48 +0100309 self._SaveJson(d)
Shawn O. Pearcec12c3602009-04-17 21:03:32 -0700310 return d
311
Anthony King85b24ac2014-05-06 15:57:48 +0100312 def _ReadJson(self):
Shawn O. Pearcec12c3602009-04-17 21:03:32 -0700313 try:
David Pursehouse16a5c3a2020-02-12 15:54:26 +0900314 if os.path.getmtime(self._json) <= os.path.getmtime(self.file):
Renaud Paquay010fed72016-11-11 14:25:29 -0800315 platform_utils.remove(self._json)
Shawn O. Pearcec12c3602009-04-17 21:03:32 -0700316 return None
317 except OSError:
318 return None
319 try:
Anthony King85b24ac2014-05-06 15:57:48 +0100320 Trace(': parsing %s', self.file)
Mike Frysinger3164d402019-11-11 05:40:22 -0500321 with open(self._json) as fd:
Anthony King85b24ac2014-05-06 15:57:48 +0100322 return json.load(fd)
Anthony King85b24ac2014-05-06 15:57:48 +0100323 except (IOError, ValueError):
Renaud Paquay010fed72016-11-11 14:25:29 -0800324 platform_utils.remove(self._json)
Shawn O. Pearcec12c3602009-04-17 21:03:32 -0700325 return None
326
Anthony King85b24ac2014-05-06 15:57:48 +0100327 def _SaveJson(self, cache):
Shawn O. Pearcec12c3602009-04-17 21:03:32 -0700328 try:
Mike Frysinger3164d402019-11-11 05:40:22 -0500329 with open(self._json, 'w') as fd:
Anthony King85b24ac2014-05-06 15:57:48 +0100330 json.dump(cache, fd, indent=2)
Anthony King85b24ac2014-05-06 15:57:48 +0100331 except (IOError, TypeError):
Anthony Kingb1d1fd72015-06-03 17:02:26 +0100332 if os.path.exists(self._json):
Renaud Paquay010fed72016-11-11 14:25:29 -0800333 platform_utils.remove(self._json)
Shawn O. Pearcec12c3602009-04-17 21:03:32 -0700334
335 def _ReadGit(self):
David Aguilar438c5472009-06-28 15:09:16 -0700336 """
337 Read configuration data from git.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700338
David Aguilar438c5472009-06-28 15:09:16 -0700339 This internal method populates the GitConfig cache.
340
341 """
David Aguilar438c5472009-06-28 15:09:16 -0700342 c = {}
Shawn O. Pearcec24c7202009-07-02 16:12:57 -0700343 d = self._do('--null', '--list')
344 if d is None:
345 return c
Dylan Denge469a0c2018-06-23 15:02:26 +0800346 if not is_python3():
347 d = d.decode('utf-8')
348 for line in d.rstrip('\0').split('\0'):
David Aguilar438c5472009-06-28 15:09:16 -0700349 if '\n' in line:
David Pursehousec1b86a22012-11-14 11:36:51 +0900350 key, val = line.split('\n', 1)
David Aguilar438c5472009-06-28 15:09:16 -0700351 else:
David Pursehousec1b86a22012-11-14 11:36:51 +0900352 key = line
353 val = None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700354
355 if key in c:
356 c[key].append(val)
357 else:
358 c[key] = [val]
359
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700360 return c
361
362 def _do(self, *args):
363 command = ['config', '--file', self.file]
364 command.extend(args)
365
366 p = GitCommand(None,
367 command,
David Pursehousee5913ae2020-02-12 13:56:59 +0900368 capture_stdout=True,
369 capture_stderr=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700370 if p.Wait() == 0:
371 return p.stdout
372 else:
373 GitError('git config %s: %s' % (str(args), p.stderr))
374
375
376class RefSpec(object):
377 """A Git refspec line, split into its components:
378
379 forced: True if the line starts with '+'
380 src: Left side of the line
381 dst: Right side of the line
382 """
383
384 @classmethod
385 def FromString(cls, rs):
386 lhs, rhs = rs.split(':', 2)
387 if lhs.startswith('+'):
388 lhs = lhs[1:]
389 forced = True
390 else:
391 forced = False
392 return cls(forced, lhs, rhs)
393
394 def __init__(self, forced, lhs, rhs):
395 self.forced = forced
396 self.src = lhs
397 self.dst = rhs
398
399 def SourceMatches(self, rev):
400 if self.src:
401 if rev == self.src:
402 return True
403 if self.src.endswith('/*') and rev.startswith(self.src[:-1]):
404 return True
405 return False
406
407 def DestMatches(self, ref):
408 if self.dst:
409 if ref == self.dst:
410 return True
411 if self.dst.endswith('/*') and ref.startswith(self.dst[:-1]):
412 return True
413 return False
414
415 def MapSource(self, rev):
416 if self.src.endswith('/*'):
417 return self.dst[:-1] + rev[len(self.src) - 1:]
418 return self.dst
419
420 def __str__(self):
421 s = ''
422 if self.forced:
423 s += '+'
424 if self.src:
425 s += self.src
426 if self.dst:
427 s += ':'
428 s += self.dst
429 return s
430
431
Doug Anderson06d029c2010-10-27 17:06:01 -0700432_master_processes = []
433_master_keys = set()
Shawn O. Pearcefb231612009-04-10 18:53:46 -0700434_ssh_master = True
Doug Anderson0048b692010-12-21 13:39:23 -0800435_master_keys_lock = None
436
David Pursehouse819827a2020-02-12 15:20:19 +0900437
Doug Anderson0048b692010-12-21 13:39:23 -0800438def init_ssh():
439 """Should be called once at the start of repo to init ssh master handling.
440
441 At the moment, all we do is to create our lock.
442 """
443 global _master_keys_lock
444 assert _master_keys_lock is None, "Should only call init_ssh once"
445 _master_keys_lock = _threading.Lock()
Shawn O. Pearcefb231612009-04-10 18:53:46 -0700446
David Pursehouse819827a2020-02-12 15:20:19 +0900447
Josh Guilfoyle71985722009-08-16 09:44:40 -0700448def _open_ssh(host, port=None):
Shawn O. Pearcefb231612009-04-10 18:53:46 -0700449 global _ssh_master
450
Doug Anderson0048b692010-12-21 13:39:23 -0800451 # Acquire the lock. This is needed to prevent opening multiple masters for
452 # the same host when we're running "repo sync -jN" (for N > 1) _and_ the
453 # manifest <remote fetch="ssh://xyz"> specifies a different host from the
454 # one that was passed to repo init.
455 _master_keys_lock.acquire()
Doug Anderson06d029c2010-10-27 17:06:01 -0700456 try:
Doug Anderson06d029c2010-10-27 17:06:01 -0700457
Doug Anderson0048b692010-12-21 13:39:23 -0800458 # Check to see whether we already think that the master is running; if we
459 # think it's already running, return right away.
460 if port is not None:
461 key = '%s:%s' % (host, port)
462 else:
463 key = host
464
465 if key in _master_keys:
Doug Anderson06d029c2010-10-27 17:06:01 -0700466 return True
Doug Anderson06d029c2010-10-27 17:06:01 -0700467
David Pursehouse16a5c3a2020-02-12 15:54:26 +0900468 if (not _ssh_master
469 or 'GIT_SSH' in os.environ
470 or sys.platform in ('win32', 'cygwin')):
Doug Anderson0048b692010-12-21 13:39:23 -0800471 # failed earlier, or cygwin ssh can't do this
472 #
473 return False
Shawn O. Pearcefb231612009-04-10 18:53:46 -0700474
Doug Anderson0048b692010-12-21 13:39:23 -0800475 # We will make two calls to ssh; this is the common part of both calls.
476 command_base = ['ssh',
David Pursehouseabdf7502020-02-12 14:58:39 +0900477 '-o', 'ControlPath %s' % ssh_sock(),
478 host]
Doug Anderson0048b692010-12-21 13:39:23 -0800479 if port is not None:
David Pursehouse8f62fb72012-11-14 12:09:38 +0900480 command_base[1:1] = ['-p', str(port)]
Doug Anderson0048b692010-12-21 13:39:23 -0800481
482 # Since the key wasn't in _master_keys, we think that master isn't running.
483 # ...but before actually starting a master, we'll double-check. This can
484 # be important because we can't tell that that 'git@myhost.com' is the same
485 # as 'myhost.com' where "User git" is setup in the user's ~/.ssh/config file.
David Pursehouse54a4e602020-02-12 14:31:05 +0900486 check_command = command_base + ['-O', 'check']
Doug Anderson0048b692010-12-21 13:39:23 -0800487 try:
488 Trace(': %s', ' '.join(check_command))
489 check_process = subprocess.Popen(check_command,
490 stdout=subprocess.PIPE,
491 stderr=subprocess.PIPE)
David Pursehouse54a4e602020-02-12 14:31:05 +0900492 check_process.communicate() # read output, but ignore it...
Doug Anderson0048b692010-12-21 13:39:23 -0800493 isnt_running = check_process.wait()
494
495 if not isnt_running:
496 # Our double-check found that the master _was_ infact running. Add to
497 # the list of keys.
498 _master_keys.add(key)
499 return True
500 except Exception:
501 # Ignore excpetions. We we will fall back to the normal command and print
502 # to the log there.
503 pass
504
David Pursehouse0ab95ba2020-02-12 15:01:59 +0900505 command = command_base[:1] + ['-M', '-N'] + command_base[1:]
Doug Anderson0048b692010-12-21 13:39:23 -0800506 try:
507 Trace(': %s', ' '.join(command))
508 p = subprocess.Popen(command)
Sarah Owensa5be53f2012-09-09 15:37:57 -0700509 except Exception as e:
Doug Anderson0048b692010-12-21 13:39:23 -0800510 _ssh_master = False
Sarah Owenscecd1d82012-11-01 22:59:27 -0700511 print('\nwarn: cannot enable ssh control master for %s:%s\n%s'
David Pursehouseabdf7502020-02-12 14:58:39 +0900512 % (host, port, str(e)), file=sys.stderr)
Doug Anderson0048b692010-12-21 13:39:23 -0800513 return False
514
Timo Lotterbach05dc46b2016-07-15 16:48:42 +0200515 time.sleep(1)
516 ssh_died = (p.poll() is not None)
517 if ssh_died:
518 return False
519
Doug Anderson0048b692010-12-21 13:39:23 -0800520 _master_processes.append(p)
521 _master_keys.add(key)
Doug Anderson0048b692010-12-21 13:39:23 -0800522 return True
523 finally:
524 _master_keys_lock.release()
Shawn O. Pearcefb231612009-04-10 18:53:46 -0700525
David Pursehouse819827a2020-02-12 15:20:19 +0900526
Shawn O. Pearcefb231612009-04-10 18:53:46 -0700527def close_ssh():
Doug Anderson0048b692010-12-21 13:39:23 -0800528 global _master_keys_lock
529
Shawn O. Pearceca8c32c2010-05-11 18:21:33 -0700530 terminate_ssh_clients()
531
Doug Anderson06d029c2010-10-27 17:06:01 -0700532 for p in _master_processes:
Shawn O. Pearce26120ca2009-06-16 11:49:10 -0700533 try:
Mike Frysingeraf1e5de2020-02-17 14:58:37 -0500534 os.kill(p.pid, signal.SIGTERM)
Shawn O. Pearce26120ca2009-06-16 11:49:10 -0700535 p.wait()
Shawn O. Pearcefb5c8fd2009-06-16 14:57:46 -0700536 except OSError:
Shawn O. Pearce26120ca2009-06-16 11:49:10 -0700537 pass
Doug Anderson06d029c2010-10-27 17:06:01 -0700538 del _master_processes[:]
539 _master_keys.clear()
Shawn O. Pearcefb231612009-04-10 18:53:46 -0700540
Nico Sallembien1c85f4e2010-04-27 14:35:27 -0700541 d = ssh_sock(create=False)
Shawn O. Pearcefb231612009-04-10 18:53:46 -0700542 if d:
543 try:
Renaud Paquaybed8b622018-09-27 10:46:58 -0700544 platform_utils.rmdir(os.path.dirname(d))
Shawn O. Pearcefb231612009-04-10 18:53:46 -0700545 except OSError:
546 pass
547
Doug Anderson0048b692010-12-21 13:39:23 -0800548 # We're done with the lock, so we can delete it.
549 _master_keys_lock = None
550
David Pursehouse819827a2020-02-12 15:20:19 +0900551
Shawn O. Pearcefb231612009-04-10 18:53:46 -0700552URI_SCP = re.compile(r'^([^@:]*@?[^:/]{1,}):')
Shawn O. Pearce898e12a2012-03-14 15:22:28 -0700553URI_ALL = re.compile(r'^([a-z][a-z+-]*)://([^@/]*@?[^/]*)/')
Shawn O. Pearcefb231612009-04-10 18:53:46 -0700554
David Pursehouse819827a2020-02-12 15:20:19 +0900555
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700556def GetSchemeFromUrl(url):
557 m = URI_ALL.match(url)
558 if m:
559 return m.group(1)
560 return None
561
David Pursehouse819827a2020-02-12 15:20:19 +0900562
Dan Willemsen0745bb22015-08-17 13:41:45 -0700563@contextlib.contextmanager
564def GetUrlCookieFile(url, quiet):
565 if url.startswith('persistent-'):
566 try:
567 p = subprocess.Popen(
568 ['git-remote-persistent-https', '-print_config', url],
569 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
570 stderr=subprocess.PIPE)
571 try:
572 cookieprefix = 'http.cookiefile='
573 proxyprefix = 'http.proxy='
574 cookiefile = None
575 proxy = None
576 for line in p.stdout:
Mike Frysingerded477d2020-02-07 23:18:23 -0500577 line = line.strip().decode('utf-8')
Dan Willemsen0745bb22015-08-17 13:41:45 -0700578 if line.startswith(cookieprefix):
Daichi Ueurace7e0262018-02-26 08:49:36 +0900579 cookiefile = os.path.expanduser(line[len(cookieprefix):])
Dan Willemsen0745bb22015-08-17 13:41:45 -0700580 if line.startswith(proxyprefix):
581 proxy = line[len(proxyprefix):]
582 # Leave subprocess open, as cookie file may be transient.
583 if cookiefile or proxy:
584 yield cookiefile, proxy
585 return
586 finally:
587 p.stdin.close()
588 if p.wait():
Mike Frysingerded477d2020-02-07 23:18:23 -0500589 err_msg = p.stderr.read().decode('utf-8')
Dan Willemsen0745bb22015-08-17 13:41:45 -0700590 if ' -print_config' in err_msg:
591 pass # Persistent proxy doesn't support -print_config.
592 elif not quiet:
593 print(err_msg, file=sys.stderr)
594 except OSError as e:
595 if e.errno == errno.ENOENT:
596 pass # No persistent proxy.
597 raise
Daichi Ueurace7e0262018-02-26 08:49:36 +0900598 cookiefile = GitConfig.ForUser().GetString('http.cookiefile')
599 if cookiefile:
600 cookiefile = os.path.expanduser(cookiefile)
601 yield cookiefile, None
Dan Willemsen0745bb22015-08-17 13:41:45 -0700602
David Pursehouse819827a2020-02-12 15:20:19 +0900603
Shawn O. Pearcefb231612009-04-10 18:53:46 -0700604def _preconnect(url):
605 m = URI_ALL.match(url)
606 if m:
607 scheme = m.group(1)
608 host = m.group(2)
609 if ':' in host:
610 host, port = host.split(':')
Shawn O. Pearce896d5df2009-04-21 14:51:04 -0700611 else:
Josh Guilfoyle71985722009-08-16 09:44:40 -0700612 port = None
Shawn O. Pearcefb231612009-04-10 18:53:46 -0700613 if scheme in ('ssh', 'git+ssh', 'ssh+git'):
614 return _open_ssh(host, port)
615 return False
616
617 m = URI_SCP.match(url)
618 if m:
619 host = m.group(1)
Josh Guilfoyle71985722009-08-16 09:44:40 -0700620 return _open_ssh(host)
Shawn O. Pearcefb231612009-04-10 18:53:46 -0700621
Shawn O. Pearce7b4f4352009-06-12 09:06:35 -0700622 return False
Shawn O. Pearcefb231612009-04-10 18:53:46 -0700623
David Pursehouse819827a2020-02-12 15:20:19 +0900624
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700625class Remote(object):
626 """Configuration options related to a remote.
627 """
David Pursehouse819827a2020-02-12 15:20:19 +0900628
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700629 def __init__(self, config, name):
630 self._config = config
631 self.name = name
632 self.url = self._Get('url')
Steve Raed6480452016-08-10 15:00:00 -0700633 self.pushUrl = self._Get('pushurl')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700634 self.review = self._Get('review')
Shawn O. Pearce339ba9f2008-11-06 09:52:51 -0800635 self.projectname = self._Get('projectname')
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530636 self.fetch = list(map(RefSpec.FromString,
David Pursehouseabdf7502020-02-12 14:58:39 +0900637 self._Get('fetch', all_keys=True)))
Shawn O. Pearcec9571422012-01-11 14:58:54 -0800638 self._review_url = None
Shawn O. Pearceb54a3922009-01-05 16:18:58 -0800639
Ulrik Sjolinb6ea3bf2010-01-03 18:20:17 +0100640 def _InsteadOf(self):
641 globCfg = GitConfig.ForUser()
642 urlList = globCfg.GetSubSections('url')
643 longest = ""
644 longestUrl = ""
645
646 for url in urlList:
647 key = "url." + url + ".insteadOf"
David Pursehouse8a68ff92012-09-24 12:15:13 +0900648 insteadOfList = globCfg.GetString(key, all_keys=True)
Ulrik Sjolinb6ea3bf2010-01-03 18:20:17 +0100649
650 for insteadOf in insteadOfList:
David Pursehouse16a5c3a2020-02-12 15:54:26 +0900651 if (self.url.startswith(insteadOf)
652 and len(insteadOf) > len(longest)):
Ulrik Sjolinb6ea3bf2010-01-03 18:20:17 +0100653 longest = insteadOf
654 longestUrl = url
655
656 if len(longest) == 0:
657 return self.url
658
659 return self.url.replace(longest, longestUrl, 1)
660
Shawn O. Pearcefb231612009-04-10 18:53:46 -0700661 def PreConnectFetch(self):
Ulrik Sjolinb6ea3bf2010-01-03 18:20:17 +0100662 connectionUrl = self._InsteadOf()
663 return _preconnect(connectionUrl)
Shawn O. Pearcefb231612009-04-10 18:53:46 -0700664
Łukasz Gardońbed59ce2017-08-08 10:18:11 +0200665 def ReviewUrl(self, userEmail, validate_certs):
Shawn O. Pearcec9571422012-01-11 14:58:54 -0800666 if self._review_url is None:
Shawn O. Pearceb54a3922009-01-05 16:18:58 -0800667 if self.review is None:
668 return None
669
670 u = self.review
Conley Owens7e12e0a2014-10-23 15:40:00 -0700671 if u.startswith('persistent-'):
672 u = u[len('persistent-'):]
Christian Koestlin2ec2a5d2016-12-05 20:32:45 +0100673 if u.split(':')[0] not in ('http', 'https', 'sso', 'ssh'):
Shawn O. Pearceb54a3922009-01-05 16:18:58 -0800674 u = 'http://%s' % u
Shawn O. Pearce13cc3842009-03-25 13:54:54 -0700675 if u.endswith('/Gerrit'):
676 u = u[:len(u) - len('/Gerrit')]
Shawn O. Pearcec9571422012-01-11 14:58:54 -0800677 if u.endswith('/ssh_info'):
678 u = u[:len(u) - len('/ssh_info')]
679 if not u.endswith('/'):
David Pursehouse8a68ff92012-09-24 12:15:13 +0900680 u += '/'
Shawn O. Pearcec9571422012-01-11 14:58:54 -0800681 http_url = u
Shawn O. Pearceb54a3922009-01-05 16:18:58 -0800682
Shawn O. Pearce146fe902009-03-25 14:06:43 -0700683 if u in REVIEW_CACHE:
Shawn O. Pearcec9571422012-01-11 14:58:54 -0800684 self._review_url = REVIEW_CACHE[u]
Shawn O. Pearce1a68dc52011-10-11 14:12:46 -0700685 elif 'REPO_HOST_PORT_INFO' in os.environ:
Shawn O. Pearcec9571422012-01-11 14:58:54 -0800686 host, port = os.environ['REPO_HOST_PORT_INFO'].split()
687 self._review_url = self._SshReviewUrl(userEmail, host, port)
688 REVIEW_CACHE[u] = self._review_url
Christian Koestlin2ec2a5d2016-12-05 20:32:45 +0100689 elif u.startswith('sso:') or u.startswith('ssh:'):
Steve Pucci143d8a72014-01-30 09:45:53 -0800690 self._review_url = u # Assume it's right
691 REVIEW_CACHE[u] = self._review_url
Timo Lotterbacheec726c2016-10-07 10:52:08 +0200692 elif 'REPO_IGNORE_SSH_INFO' in os.environ:
693 self._review_url = http_url
694 REVIEW_CACHE[u] = self._review_url
Shawn O. Pearce146fe902009-03-25 14:06:43 -0700695 else:
696 try:
Shawn O. Pearcec9571422012-01-11 14:58:54 -0800697 info_url = u + 'ssh_info'
Łukasz Gardońbed59ce2017-08-08 10:18:11 +0200698 if not validate_certs:
699 context = ssl._create_unverified_context()
700 info = urllib.request.urlopen(info_url, context=context).read()
701 else:
702 info = urllib.request.urlopen(info_url).read()
Mike Frysinger1b9adab2019-07-04 17:54:54 -0400703 if info == b'NOT_AVAILABLE' or b'<' in info:
Conley Owens745a39b2013-06-05 13:16:18 -0700704 # If `info` contains '<', we assume the server gave us some sort
705 # of HTML response back, like maybe a login page.
Shawn O. Pearce146fe902009-03-25 14:06:43 -0700706 #
Conley Owens745a39b2013-06-05 13:16:18 -0700707 # Assume HTTP if SSH is not enabled or ssh_info doesn't look right.
Conley Owens2cd38a02014-02-04 15:32:29 -0800708 self._review_url = http_url
Shawn O. Pearce146fe902009-03-25 14:06:43 -0700709 else:
Mike Frysinger1b9adab2019-07-04 17:54:54 -0400710 info = info.decode('utf-8')
Shawn O. Pearcec9571422012-01-11 14:58:54 -0800711 host, port = info.split()
Dan Willemsen16889ba2016-09-22 16:39:06 +0000712 self._review_url = self._SshReviewUrl(userEmail, host, port)
Sarah Owens1f7627f2012-10-31 09:21:55 -0700713 except urllib.error.HTTPError as e:
Shawn O. Pearcec9571422012-01-11 14:58:54 -0800714 raise UploadError('%s: %s' % (self.review, str(e)))
Sarah Owens1f7627f2012-10-31 09:21:55 -0700715 except urllib.error.URLError as e:
Shawn O. Pearcebf1fbb22011-10-11 09:31:58 -0700716 raise UploadError('%s: %s' % (self.review, str(e)))
David Pursehouseecf8f2b2013-05-24 12:12:23 +0900717 except HTTPException as e:
718 raise UploadError('%s: %s' % (self.review, e.__class__.__name__))
Shawn O. Pearceb54a3922009-01-05 16:18:58 -0800719
Shawn O. Pearcec9571422012-01-11 14:58:54 -0800720 REVIEW_CACHE[u] = self._review_url
721 return self._review_url + self.projectname
Shawn O. Pearceb54a3922009-01-05 16:18:58 -0800722
Shawn O. Pearcec9571422012-01-11 14:58:54 -0800723 def _SshReviewUrl(self, userEmail, host, port):
Shawn O. Pearce3575b8f2010-07-15 17:00:14 -0700724 username = self._config.GetString('review.%s.username' % self.review)
725 if username is None:
Shawn O. Pearcec9571422012-01-11 14:58:54 -0800726 username = userEmail.split('@')[0]
727 return 'ssh://%s@%s:%s/' % (username, host, port)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700728
729 def ToLocal(self, rev):
730 """Convert a remote revision string to something we have locally.
731 """
Yann Droneaud936183a2013-09-12 10:51:18 +0200732 if self.name == '.' or IsId(rev):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700733 return rev
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700734
735 if not rev.startswith('refs/'):
736 rev = R_HEADS + rev
737
738 for spec in self.fetch:
739 if spec.SourceMatches(rev):
740 return spec.MapSource(rev)
Nasser Grainawi909d58b2014-09-19 12:13:04 -0600741
742 if not rev.startswith(R_HEADS):
743 return rev
744
Mike Frysinger1f2462e2019-08-03 01:57:09 -0400745 raise GitError('%s: remote %s does not have %s' %
746 (self.projectname, self.name, rev))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700747
748 def WritesTo(self, ref):
749 """True if the remote stores to the tracking ref.
750 """
751 for spec in self.fetch:
752 if spec.DestMatches(ref):
753 return True
754 return False
755
Shawn O. Pearcee284ad12008-11-04 07:37:10 -0800756 def ResetFetch(self, mirror=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700757 """Set the fetch refspec to its default value.
758 """
Shawn O. Pearcee284ad12008-11-04 07:37:10 -0800759 if mirror:
760 dst = 'refs/heads/*'
761 else:
762 dst = 'refs/remotes/%s/*' % self.name
763 self.fetch = [RefSpec(True, 'refs/heads/*', dst)]
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700764
765 def Save(self):
766 """Save this remote to the configuration.
767 """
768 self._Set('url', self.url)
Steve Raed6480452016-08-10 15:00:00 -0700769 if self.pushUrl is not None:
770 self._Set('pushurl', self.pushUrl + '/' + self.projectname)
771 else:
772 self._Set('pushurl', self.pushUrl)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700773 self._Set('review', self.review)
Shawn O. Pearce339ba9f2008-11-06 09:52:51 -0800774 self._Set('projectname', self.projectname)
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530775 self._Set('fetch', list(map(str, self.fetch)))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700776
777 def _Set(self, key, value):
778 key = 'remote.%s.%s' % (self.name, key)
779 return self._config.SetString(key, value)
780
David Pursehouse8a68ff92012-09-24 12:15:13 +0900781 def _Get(self, key, all_keys=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700782 key = 'remote.%s.%s' % (self.name, key)
David Pursehousee5913ae2020-02-12 13:56:59 +0900783 return self._config.GetString(key, all_keys=all_keys)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700784
785
786class Branch(object):
787 """Configuration options related to a single branch.
788 """
David Pursehouse819827a2020-02-12 15:20:19 +0900789
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700790 def __init__(self, config, name):
791 self._config = config
792 self.name = name
793 self.merge = self._Get('merge')
794
795 r = self._Get('remote')
796 if r:
797 self.remote = self._config.GetRemote(r)
798 else:
799 self.remote = None
800
801 @property
802 def LocalMerge(self):
803 """Convert the merge spec to a local name.
804 """
805 if self.remote and self.merge:
806 return self.remote.ToLocal(self.merge)
807 return None
808
809 def Save(self):
810 """Save this branch back into the configuration.
811 """
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -0700812 if self._config.HasSection('branch', self.name):
813 if self.remote:
814 self._Set('remote', self.remote.name)
815 else:
816 self._Set('remote', None)
817 self._Set('merge', self.merge)
818
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700819 else:
Mike Frysinger3164d402019-11-11 05:40:22 -0500820 with open(self._config.file, 'a') as fd:
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -0700821 fd.write('[branch "%s"]\n' % self.name)
822 if self.remote:
823 fd.write('\tremote = %s\n' % self.remote.name)
824 if self.merge:
825 fd.write('\tmerge = %s\n' % self.merge)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700826
827 def _Set(self, key, value):
828 key = 'branch.%s.%s' % (self.name, key)
829 return self._config.SetString(key, value)
830
David Pursehouse8a68ff92012-09-24 12:15:13 +0900831 def _Get(self, key, all_keys=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700832 key = 'branch.%s.%s' % (self.name, key)
David Pursehousee5913ae2020-02-12 13:56:59 +0900833 return self._config.GetString(key, all_keys=all_keys)