blob: 9a844021d9c042f25b7828b996db1d1cde23b577 [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
Mike Frysinger979d5bd2020-02-09 02:28:34 -050018
Anthony King85b24ac2014-05-06 15:57:48 +010019import json
David Pursehouse86d973d2012-08-24 10:21:02 +090020import netrc
Shawn O. Pearce2a1ccb22009-04-10 16:51:53 -070021from optparse import SUPPRESS_HELP
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070022import os
23import re
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -070024import socket
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070025import subprocess
26import sys
Dan Willemsen0745bb22015-08-17 13:41:45 -070027import tempfile
Shawn O. Pearcef6906872009-04-18 10:49:00 -070028import time
David Pursehouse59bbb582013-05-17 10:49:33 +090029
30from pyversion import is_python3
31if is_python3():
Dan Willemsen0745bb22015-08-17 13:41:45 -070032 import http.cookiejar as cookielib
33 import urllib.error
Chirayu Desai217ea7d2013-03-01 19:14:38 +053034 import urllib.parse
Dan Willemsen0745bb22015-08-17 13:41:45 -070035 import urllib.request
David Pursehouse59bbb582013-05-17 10:49:33 +090036 import xmlrpc.client
37else:
Dan Willemsen0745bb22015-08-17 13:41:45 -070038 import cookielib
Chirayu Desai217ea7d2013-03-01 19:14:38 +053039 import imp
Dan Willemsen0745bb22015-08-17 13:41:45 -070040 import urllib2
Chirayu Desai217ea7d2013-03-01 19:14:38 +053041 import urlparse
David Pursehouse59bbb582013-05-17 10:49:33 +090042 import xmlrpclib
Chirayu Desai217ea7d2013-03-01 19:14:38 +053043 urllib = imp.new_module('urllib')
Dan Willemsen0745bb22015-08-17 13:41:45 -070044 urllib.error = urllib2
Chirayu Desaidb2ad9d2013-06-11 13:42:25 +053045 urllib.parse = urlparse
Dan Willemsen0745bb22015-08-17 13:41:45 -070046 urllib.request = urllib2
Chirayu Desai217ea7d2013-03-01 19:14:38 +053047 xmlrpc = imp.new_module('xmlrpc')
48 xmlrpc.client = xmlrpclib
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070049
Roy Lee18afd7f2010-05-09 04:32:08 +080050try:
51 import threading as _threading
52except ImportError:
53 import dummy_threading as _threading
54
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -070055try:
56 import resource
David Pursehouse819827a2020-02-12 15:20:19 +090057
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -070058 def _rlimit_nofile():
59 return resource.getrlimit(resource.RLIMIT_NOFILE)
60except ImportError:
61 def _rlimit_nofile():
62 return (256, 256)
63
Dave Borowitz18857212012-10-23 17:02:59 -070064try:
65 import multiprocessing
66except ImportError:
67 multiprocessing = None
68
David Rileye0684ad2017-04-05 00:02:59 -070069import event_log
Dave Borowitze2152672012-10-31 12:24:38 -070070from git_command import GIT, git_require
David Pursehouseba7bc732015-08-20 16:55:42 +090071from git_config import GetUrlCookieFile
David Pursehoused94aaef2012-09-07 09:52:04 +090072from git_refs import R_HEADS, HEAD
Simran Basibdb52712015-08-10 13:23:23 -070073import gitc_utils
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -070074from project import Project
75from project import RemoteSpec
Shawn O. Pearcec95583b2009-03-03 17:47:06 -080076from command import Command, MirrorSafeCommand
Torne (Richard Coles)7bdbde72012-12-05 10:58:06 +000077from error import RepoChangedException, GitError, ManifestParseError
Renaud Paquaya65adf72016-11-03 10:37:53 -070078import platform_utils
Shawn O. Pearce350cde42009-04-16 11:21:18 -070079from project import SyncBuffer
Shawn O. Pearce68194f42009-04-10 16:48:52 -070080from progress import Progress
Conley Owens094cdbe2014-01-30 15:09:59 -080081from wrapper import Wrapper
Dan Willemsen5ea32d12015-09-08 13:27:20 -070082from manifest_xml import GitcManifest
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070083
Dave Borowitz67700e92012-10-23 15:00:54 -070084_ONE_DAY_S = 24 * 60 * 60
85
David Pursehouse819827a2020-02-12 15:20:19 +090086
Doug Andersonfc06ced2011-03-16 15:49:18 -070087class _FetchError(Exception):
88 """Internal error thrown in _FetchHelper() when we don't want stack trace."""
89 pass
90
David Pursehouse819827a2020-02-12 15:20:19 +090091
Xin Li745be2e2019-06-03 11:24:30 -070092class _CheckoutError(Exception):
93 """Internal error thrown in _CheckoutOne() when we don't want stack trace."""
94
David Pursehouse819827a2020-02-12 15:20:19 +090095
Shawn O. Pearcec95583b2009-03-03 17:47:06 -080096class Sync(Command, MirrorSafeCommand):
Roy Lee18afd7f2010-05-09 04:32:08 +080097 jobs = 1
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070098 common = True
99 helpSummary = "Update working tree to the latest revision"
100 helpUsage = """
101%prog [...]
102"""
103 helpDescription = """
104The '%prog' command synchronizes local project directories
105with the remote repositories specified in the manifest. If a local
106project does not yet exist, it will clone a new local directory from
107the remote repository and set up tracking branches as specified in
108the manifest. If the local project already exists, '%prog'
109will update the remote branches and rebase any new local changes
110on top of the new remote changes.
111
112'%prog' will synchronize all projects listed at the command
113line. Projects can be specified either by name, or by a relative
114or absolute path to the project's local directory. If no projects
115are specified, '%prog' will synchronize all projects listed in
116the manifest.
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700117
118The -d/--detach option can be used to switch specified projects
119back to the manifest revision. This option is especially helpful
120if the project is currently on a topic branch, but the manifest
121revision is temporarily needed.
Shawn O. Pearceeb7af872009-04-21 08:02:04 -0700122
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700123The -s/--smart-sync option can be used to sync to a known good
124build as specified by the manifest-server element in the current
Victor Boivie08c880d2011-04-19 10:32:52 +0200125manifest. The -t/--smart-tag option is similar and allows you to
126specify a custom tag/label.
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700127
David Pursehousecf76b1b2012-09-14 10:31:42 +0900128The -u/--manifest-server-username and -p/--manifest-server-password
129options can be used to specify a username and password to authenticate
130with the manifest server when using the -s or -t option.
131
132If -u and -p are not specified when using the -s or -t option, '%prog'
133will attempt to read authentication credentials for the manifest server
134from the user's .netrc file.
135
136'%prog' will not use authentication credentials from -u/-p or .netrc
137if the manifest server specified in the manifest file already includes
138credentials.
139
Mike Frysingerd9e5cf02019-08-26 03:12:55 -0400140By default, all projects will be synced. The --fail-fast option can be used
Mike Frysinger7ae210a2020-05-24 14:56:52 -0400141to halt syncing as soon as possible when the first project fails to sync.
Andrei Warkentin5df6de02010-07-02 17:58:31 -0500142
Kevin Degiabaa7f32014-11-12 11:27:45 -0700143The --force-sync option can be used to overwrite existing git
144directories if they have previously been linked to a different
Roger Shimizuac29ac32020-06-06 02:33:40 +0900145object directory. WARNING: This may cause data to be lost since
Kevin Degiabaa7f32014-11-12 11:27:45 -0700146refs may be removed when overwriting.
147
Oleksii Okolielovd3c0f592018-12-17 19:23:44 -0500148The --force-remove-dirty option can be used to remove previously used
149projects with uncommitted changes. WARNING: This may cause data to be
150lost since uncommitted changes may be removed with projects that no longer
151exist in the manifest.
152
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -0700153The --no-clone-bundle option disables any attempt to use
154$URL/clone.bundle to bootstrap a new Git repository from a
155resumeable bundle file on a content delivery network. This
156may be necessary if there are problems with the local Python
157HTTP client or proxy configuration, but the Git binary works.
158
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800159The --fetch-submodules option enables fetching Git submodules
160of a project from server.
161
David Pursehousef2fad612015-01-29 14:36:28 +0900162The -c/--current-branch option can be used to only fetch objects that
163are on the branch specified by a project's revision.
164
David Pursehouseb1553542014-09-04 21:28:09 +0900165The --optimized-fetch option can be used to only fetch projects that
166are fixed to a sha1 revision if the sha1 revision does not already
167exist locally.
168
David Pursehouse74cfd272015-10-14 10:50:15 +0900169The --prune option can be used to remove any refs that no longer
170exist on the remote.
171
Mike Frysingerb8f7bb02018-10-10 01:05:11 -0400172# SSH Connections
Shawn O. Pearceeb7af872009-04-21 08:02:04 -0700173
174If at least one project remote URL uses an SSH connection (ssh://,
175git+ssh://, or user@host:path syntax) repo will automatically
176enable the SSH ControlMaster option when connecting to that host.
177This feature permits other projects in the same '%prog' session to
178reuse the same SSH tunnel, saving connection setup overheads.
179
180To disable this behavior on UNIX platforms, set the GIT_SSH
181environment variable to 'ssh'. For example:
182
183 export GIT_SSH=ssh
184 %prog
185
Mike Frysingerb8f7bb02018-10-10 01:05:11 -0400186# Compatibility
Shawn O. Pearceeb7af872009-04-21 08:02:04 -0700187
188This feature is automatically disabled on Windows, due to the lack
189of UNIX domain socket support.
190
191This feature is not compatible with url.insteadof rewrites in the
192user's ~/.gitconfig. '%prog' is currently not able to perform the
193rewrite early enough to establish the ControlMaster tunnel.
194
195If the remote SSH daemon is Gerrit Code Review, version 2.0.10 or
196later is required to fix a server side protocol bug.
197
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700198"""
199
Nico Sallembien6623b212010-05-11 12:57:01 -0700200 def _Options(self, p, show_smart=True):
Torne (Richard Coles)7bdbde72012-12-05 10:58:06 +0000201 try:
202 self.jobs = self.manifest.default.sync_j
203 except ManifestParseError:
204 self.jobs = 1
Shawn O. Pearce6392c872011-09-22 17:44:31 -0700205
Andrei Warkentin5df6de02010-07-02 17:58:31 -0500206 p.add_option('-f', '--force-broken',
Stefan Müller-Klieser46702ed2019-08-30 10:20:15 +0200207 dest='force_broken', action='store_true',
Mike Frysingerd9e5cf02019-08-26 03:12:55 -0400208 help='obsolete option (to be deleted in the future)')
209 p.add_option('--fail-fast',
210 dest='fail_fast', action='store_true',
211 help='stop syncing after first error is hit')
Kevin Degiabaa7f32014-11-12 11:27:45 -0700212 p.add_option('--force-sync',
213 dest='force_sync', action='store_true',
214 help="overwrite an existing git directory if it needs to "
215 "point to a different object directory. WARNING: this "
216 "may cause loss of data")
Oleksii Okolielovd3c0f592018-12-17 19:23:44 -0500217 p.add_option('--force-remove-dirty',
218 dest='force_remove_dirty', action='store_true',
219 help="force remove projects with uncommitted modifications if "
220 "projects no longer exist in the manifest. "
221 "WARNING: this may cause loss of data")
David Pursehouse8f62fb72012-11-14 12:09:38 +0900222 p.add_option('-l', '--local-only',
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700223 dest='local_only', action='store_true',
224 help="only update working tree, don't fetch")
David Pursehouse54a4e602020-02-12 14:31:05 +0900225 p.add_option('--no-manifest-update', '--nmu',
Fredrik de Grootcc960972019-11-22 09:04:31 +0100226 dest='mp_update', action='store_false', default='true',
227 help='use the existing manifest checkout as-is. '
228 '(do not update to the latest revision)')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900229 p.add_option('-n', '--network-only',
Shawn O. Pearce96fdcef2009-04-10 16:29:20 -0700230 dest='network_only', action='store_true',
231 help="fetch only, don't update working tree")
David Pursehouse8f62fb72012-11-14 12:09:38 +0900232 p.add_option('-d', '--detach',
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700233 dest='detach_head', action='store_true',
234 help='detach projects back to manifest revision')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900235 p.add_option('-c', '--current-branch',
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -0700236 dest='current_branch_only', action='store_true',
237 help='fetch only current branch from server')
Mike Frysinger521d01b2020-02-17 01:51:49 -0500238 p.add_option('-v', '--verbose',
239 dest='output_mode', action='store_true',
240 help='show all sync output')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900241 p.add_option('-q', '--quiet',
Mike Frysinger521d01b2020-02-17 01:51:49 -0500242 dest='output_mode', action='store_false',
243 help='only show errors')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900244 p.add_option('-j', '--jobs',
Roy Lee18afd7f2010-05-09 04:32:08 +0800245 dest='jobs', action='store', type='int',
Shawn O. Pearce6392c872011-09-22 17:44:31 -0700246 help="projects to fetch simultaneously (default %d)" % self.jobs)
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500247 p.add_option('-m', '--manifest-name',
248 dest='manifest_name',
249 help='temporary manifest to use for this sync', metavar='NAME.xml')
Xin Lid79a4bc2020-05-20 16:03:45 -0700250 p.add_option('--clone-bundle', action='store_true',
251 help='enable use of /clone.bundle on HTTP/HTTPS')
252 p.add_option('--no-clone-bundle', dest='clone_bundle', action='store_false',
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -0700253 help='disable use of /clone.bundle on HTTP/HTTPS')
Conley Owens8d070cf2012-11-06 13:14:31 -0800254 p.add_option('-u', '--manifest-server-username', action='store',
255 dest='manifest_server_username',
256 help='username to authenticate with the manifest server')
257 p.add_option('-p', '--manifest-server-password', action='store',
258 dest='manifest_server_password',
259 help='password to authenticate with the manifest server')
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800260 p.add_option('--fetch-submodules',
261 dest='fetch_submodules', action='store_true',
262 help='fetch submodules from server')
Mitchel Humpherys597868b2012-10-29 10:18:34 -0700263 p.add_option('--no-tags',
Mike Frysingerc58ec4d2020-02-17 14:36:08 -0500264 dest='tags', default=True, action='store_false',
Mitchel Humpherys597868b2012-10-29 10:18:34 -0700265 help="don't fetch tags")
David Pursehouseb1553542014-09-04 21:28:09 +0900266 p.add_option('--optimized-fetch',
267 dest='optimized_fetch', action='store_true',
268 help='only fetch projects fixed to sha1 if revision does not exist locally')
George Engelbrecht9bc283e2020-04-02 12:36:09 -0600269 p.add_option('--retry-fetches',
270 default=0, action='store', type='int',
271 help='number of times to retry fetches on transient errors')
David Pursehouse74cfd272015-10-14 10:50:15 +0900272 p.add_option('--prune', dest='prune', action='store_true',
273 help='delete refs that no longer exist on the remote')
Nico Sallembien6623b212010-05-11 12:57:01 -0700274 if show_smart:
275 p.add_option('-s', '--smart-sync',
276 dest='smart_sync', action='store_true',
David Pursehouse79fba682016-04-13 18:03:00 +0900277 help='smart sync using manifest from the latest known good build')
Victor Boivie08c880d2011-04-19 10:32:52 +0200278 p.add_option('-t', '--smart-tag',
279 dest='smart_tag', action='store',
280 help='smart sync using manifest from a known tag')
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700281
Shawn O. Pearcefd89b672009-04-18 11:28:57 -0700282 g = p.add_option_group('repo Version options')
283 g.add_option('--no-repo-verify',
Mike Frysingerc58ec4d2020-02-17 14:36:08 -0500284 dest='repo_verify', default=True, action='store_false',
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700285 help='do not verify repo source code')
Shawn O. Pearcefd89b672009-04-18 11:28:57 -0700286 g.add_option('--repo-upgraded',
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800287 dest='repo_upgraded', action='store_true',
Shawn O. Pearce2a1ccb22009-04-10 16:51:53 -0700288 help=SUPPRESS_HELP)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700289
Andrew Wheeler7f1ccfb2016-06-17 16:51:07 -0500290 def _FetchProjectList(self, opt, projects, sem, *args, **kwargs):
Xin Li745be2e2019-06-03 11:24:30 -0700291 """Main function of the fetch threads.
Roy Lee18afd7f2010-05-09 04:32:08 +0800292
David James8d201162013-10-11 17:03:19 -0700293 Delegates most of the work to _FetchHelper.
294
295 Args:
296 opt: Program options returned from optparse. See _Options().
297 projects: Projects to fetch.
Andrew Wheeler7f1ccfb2016-06-17 16:51:07 -0500298 sem: We'll release() this semaphore when we exit so that another thread
299 can be started up.
David James89ece422014-01-09 18:51:58 -0800300 *args, **kwargs: Remaining arguments to pass to _FetchHelper. See the
David James8d201162013-10-11 17:03:19 -0700301 _FetchHelper docstring for details.
302 """
Andrew Wheeler7f1ccfb2016-06-17 16:51:07 -0500303 try:
304 for project in projects:
305 success = self._FetchHelper(opt, project, *args, **kwargs)
Mike Frysingerd9e5cf02019-08-26 03:12:55 -0400306 if not success and opt.fail_fast:
Andrew Wheeler7f1ccfb2016-06-17 16:51:07 -0500307 break
308 finally:
309 sem.release()
David James8d201162013-10-11 17:03:19 -0700310
Xin Li745be2e2019-06-03 11:24:30 -0700311 def _FetchHelper(self, opt, project, lock, fetched, pm, err_event,
312 clone_filter):
David James8d201162013-10-11 17:03:19 -0700313 """Fetch git objects for a single project.
314
David Pursehousec1b86a22012-11-14 11:36:51 +0900315 Args:
316 opt: Program options returned from optparse. See _Options().
317 project: Project object for the project to fetch.
318 lock: Lock for accessing objects that are shared amongst multiple
319 _FetchHelper() threads.
320 fetched: set object that we will add project.gitdir to when we're done
321 (with our lock held).
322 pm: Instance of a Project object. We will call pm.update() (with our
323 lock held).
David Pursehousec1b86a22012-11-14 11:36:51 +0900324 err_event: We'll set this event in the case of an error (after printing
325 out info about the error).
Xin Li745be2e2019-06-03 11:24:30 -0700326 clone_filter: Filter for use in a partial clone.
David James8d201162013-10-11 17:03:19 -0700327
328 Returns:
329 Whether the fetch was successful.
David Pursehousec1b86a22012-11-14 11:36:51 +0900330 """
331 # We'll set to true once we've locked the lock.
332 did_lock = False
Doug Andersonfc06ced2011-03-16 15:49:18 -0700333
David Pursehousec1b86a22012-11-14 11:36:51 +0900334 # Encapsulate everything in a try/except/finally so that:
335 # - We always set err_event in the case of an exception.
David Pursehousec1b86a22012-11-14 11:36:51 +0900336 # - We always make sure we unlock the lock if we locked it.
David Rileye0684ad2017-04-05 00:02:59 -0700337 start = time.time()
338 success = False
David Pursehousec1b86a22012-11-14 11:36:51 +0900339 try:
Doug Andersonfc06ced2011-03-16 15:49:18 -0700340 try:
David Pursehousec1b86a22012-11-14 11:36:51 +0900341 success = project.Sync_NetworkHalf(
David Pursehouseabdf7502020-02-12 14:58:39 +0900342 quiet=opt.quiet,
Mike Frysinger521d01b2020-02-17 01:51:49 -0500343 verbose=opt.verbose,
David Pursehouseabdf7502020-02-12 14:58:39 +0900344 current_branch_only=opt.current_branch_only,
345 force_sync=opt.force_sync,
Mike Frysingerc58ec4d2020-02-17 14:36:08 -0500346 clone_bundle=opt.clone_bundle,
347 tags=opt.tags, archive=self.manifest.IsArchive,
David Pursehouseabdf7502020-02-12 14:58:39 +0900348 optimized_fetch=opt.optimized_fetch,
George Engelbrecht9bc283e2020-04-02 12:36:09 -0600349 retry_fetches=opt.retry_fetches,
David Pursehouseabdf7502020-02-12 14:58:39 +0900350 prune=opt.prune,
351 clone_filter=clone_filter)
David Pursehousec1b86a22012-11-14 11:36:51 +0900352 self._fetch_times.Set(project, time.time() - start)
Doug Andersonfc06ced2011-03-16 15:49:18 -0700353
David Pursehousec1b86a22012-11-14 11:36:51 +0900354 # Lock around all the rest of the code, since printing, updating a set
355 # and Progress.update() are not thread safe.
356 lock.acquire()
357 did_lock = True
Doug Andersonfc06ced2011-03-16 15:49:18 -0700358
David Pursehousec1b86a22012-11-14 11:36:51 +0900359 if not success:
Hu Xiuyune9becc02015-11-25 15:52:26 +0800360 err_event.set()
Marc Herbertffb4b892017-04-04 22:03:53 -0700361 print('error: Cannot fetch %s from %s'
362 % (project.name, project.remote.url),
363 file=sys.stderr)
Mike Frysingerd9e5cf02019-08-26 03:12:55 -0400364 if opt.fail_fast:
David Pursehousec1b86a22012-11-14 11:36:51 +0900365 raise _FetchError()
Doug Andersonfc06ced2011-03-16 15:49:18 -0700366
David Pursehousec1b86a22012-11-14 11:36:51 +0900367 fetched.add(project.gitdir)
Mike Frysinger3538dd22019-08-26 15:32:06 -0400368 pm.update(msg=project.name)
David Pursehousec1b86a22012-11-14 11:36:51 +0900369 except _FetchError:
Hu Xiuyune9becc02015-11-25 15:52:26 +0800370 pass
Dan Sandlerc5cd4332015-07-31 09:37:53 -0400371 except Exception as e:
David Pursehouse42339d72020-02-12 14:37:15 +0900372 print('error: Cannot fetch %s (%s: %s)'
David Pursehouseabdf7502020-02-12 14:58:39 +0900373 % (project.name, type(e).__name__, str(e)), file=sys.stderr)
David Pursehousec1b86a22012-11-14 11:36:51 +0900374 err_event.set()
375 raise
376 finally:
377 if did_lock:
378 lock.release()
David Rileye0684ad2017-04-05 00:02:59 -0700379 finish = time.time()
380 self.event_log.AddSync(project, event_log.TASK_SYNC_NETWORK,
381 start, finish, success)
Roy Lee18afd7f2010-05-09 04:32:08 +0800382
David James8d201162013-10-11 17:03:19 -0700383 return success
384
Mike Frysinger5a033082019-09-23 19:21:20 -0400385 def _Fetch(self, projects, opt, err_event):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700386 fetched = set()
David James89ece422014-01-09 18:51:58 -0800387 lock = _threading.Lock()
Tim Schumacher913327f2017-06-05 15:01:41 +0200388 pm = Progress('Fetching projects', len(projects),
Tim Schumacher7be072e2017-06-28 18:29:23 +0200389 always_print_percentage=opt.quiet)
Roy Lee18afd7f2010-05-09 04:32:08 +0800390
David James89ece422014-01-09 18:51:58 -0800391 objdir_project_map = dict()
392 for project in projects:
393 objdir_project_map.setdefault(project.objdir, []).append(project)
David James8d201162013-10-11 17:03:19 -0700394
David James89ece422014-01-09 18:51:58 -0800395 threads = set()
396 sem = _threading.Semaphore(self.jobs)
David James89ece422014-01-09 18:51:58 -0800397 for project_list in objdir_project_map.values():
398 # Check for any errors before running any more tasks.
399 # ...we'll let existing threads finish, though.
Mike Frysingerd9e5cf02019-08-26 03:12:55 -0400400 if err_event.isSet() and opt.fail_fast:
David James89ece422014-01-09 18:51:58 -0800401 break
Doug Andersonfc06ced2011-03-16 15:49:18 -0700402
David James89ece422014-01-09 18:51:58 -0800403 sem.acquire()
404 kwargs = dict(opt=opt,
405 projects=project_list,
Andrew Wheeler7f1ccfb2016-06-17 16:51:07 -0500406 sem=sem,
David James89ece422014-01-09 18:51:58 -0800407 lock=lock,
408 fetched=fetched,
409 pm=pm,
Xin Li745be2e2019-06-03 11:24:30 -0700410 err_event=err_event,
411 clone_filter=self.manifest.CloneFilter)
David James89ece422014-01-09 18:51:58 -0800412 if self.jobs > 1:
David Pursehousee5913ae2020-02-12 13:56:59 +0900413 t = _threading.Thread(target=self._FetchProjectList,
414 kwargs=kwargs)
David 'Digit' Turnere2126652012-09-05 10:35:06 +0200415 # Ensure that Ctrl-C will not freeze the repo process.
416 t.daemon = True
Roy Lee18afd7f2010-05-09 04:32:08 +0800417 threads.add(t)
418 t.start()
David James89ece422014-01-09 18:51:58 -0800419 else:
420 self._FetchProjectList(**kwargs)
Roy Lee18afd7f2010-05-09 04:32:08 +0800421
David James89ece422014-01-09 18:51:58 -0800422 for t in threads:
423 t.join()
Roy Lee18afd7f2010-05-09 04:32:08 +0800424
Shawn O. Pearce68194f42009-04-10 16:48:52 -0700425 pm.end()
Dave Borowitz67700e92012-10-23 15:00:54 -0700426 self._fetch_times.Save()
Dave Borowitz18857212012-10-23 17:02:59 -0700427
Julien Campergue335f5ef2013-10-16 11:02:35 +0200428 if not self.manifest.IsArchive:
Mike Frysinger5a033082019-09-23 19:21:20 -0400429 self._GCProjects(projects, opt, err_event)
Julien Campergue335f5ef2013-10-16 11:02:35 +0200430
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700431 return fetched
432
Xin Li745be2e2019-06-03 11:24:30 -0700433 def _CheckoutWorker(self, opt, sem, project, *args, **kwargs):
434 """Main function of the fetch threads.
435
436 Delegates most of the work to _CheckoutOne.
437
438 Args:
439 opt: Program options returned from optparse. See _Options().
440 projects: Projects to fetch.
441 sem: We'll release() this semaphore when we exit so that another thread
442 can be started up.
443 *args, **kwargs: Remaining arguments to pass to _CheckoutOne. See the
444 _CheckoutOne docstring for details.
445 """
446 try:
Mike Frysingera34186e2019-08-07 18:07:31 -0400447 return self._CheckoutOne(opt, project, *args, **kwargs)
Xin Li745be2e2019-06-03 11:24:30 -0700448 finally:
449 sem.release()
450
Vadim Bendeburydff91942019-11-06 11:05:00 -0800451 def _CheckoutOne(self, opt, project, lock, pm, err_event, err_results):
Xin Li745be2e2019-06-03 11:24:30 -0700452 """Checkout work tree for one project
453
454 Args:
455 opt: Program options returned from optparse. See _Options().
456 project: Project object for the project to checkout.
457 lock: Lock for accessing objects that are shared amongst multiple
458 _CheckoutWorker() threads.
459 pm: Instance of a Project object. We will call pm.update() (with our
460 lock held).
461 err_event: We'll set this event in the case of an error (after printing
462 out info about the error).
Vadim Bendeburydff91942019-11-06 11:05:00 -0800463 err_results: A list of strings, paths to git repos where checkout
464 failed.
Xin Li745be2e2019-06-03 11:24:30 -0700465
466 Returns:
467 Whether the fetch was successful.
468 """
469 # We'll set to true once we've locked the lock.
470 did_lock = False
471
Xin Li745be2e2019-06-03 11:24:30 -0700472 # Encapsulate everything in a try/except/finally so that:
473 # - We always set err_event in the case of an exception.
474 # - We always make sure we unlock the lock if we locked it.
475 start = time.time()
476 syncbuf = SyncBuffer(self.manifest.manifestProject.config,
477 detach_head=opt.detach_head)
478 success = False
479 try:
480 try:
481 project.Sync_LocalHalf(syncbuf, force_sync=opt.force_sync)
Xin Li745be2e2019-06-03 11:24:30 -0700482
483 # Lock around all the rest of the code, since printing, updating a set
484 # and Progress.update() are not thread safe.
485 lock.acquire()
Mike Frysinger3538dd22019-08-26 15:32:06 -0400486 success = syncbuf.Finish()
Xin Li745be2e2019-06-03 11:24:30 -0700487 did_lock = True
488
489 if not success:
490 err_event.set()
491 print('error: Cannot checkout %s' % (project.name),
492 file=sys.stderr)
493 raise _CheckoutError()
494
Mike Frysinger3538dd22019-08-26 15:32:06 -0400495 pm.update(msg=project.name)
Xin Li745be2e2019-06-03 11:24:30 -0700496 except _CheckoutError:
497 pass
498 except Exception as e:
499 print('error: Cannot checkout %s: %s: %s' %
500 (project.name, type(e).__name__, str(e)),
501 file=sys.stderr)
502 err_event.set()
503 raise
504 finally:
505 if did_lock:
Vadim Bendeburydff91942019-11-06 11:05:00 -0800506 if not success:
507 err_results.append(project.relpath)
Xin Li745be2e2019-06-03 11:24:30 -0700508 lock.release()
509 finish = time.time()
510 self.event_log.AddSync(project, event_log.TASK_SYNC_LOCAL,
511 start, finish, success)
512
513 return success
514
Mike Frysinger5a033082019-09-23 19:21:20 -0400515 def _Checkout(self, all_projects, opt, err_event, err_results):
Xin Li745be2e2019-06-03 11:24:30 -0700516 """Checkout projects listed in all_projects
517
518 Args:
519 all_projects: List of all projects that should be checked out.
520 opt: Program options returned from optparse. See _Options().
Mike Frysinger5a033082019-09-23 19:21:20 -0400521 err_event: We'll set this event in the case of an error (after printing
522 out info about the error).
523 err_results: A list of strings, paths to git repos where checkout
524 failed.
Xin Li745be2e2019-06-03 11:24:30 -0700525 """
526
527 # Perform checkouts in multiple threads when we are using partial clone.
528 # Without partial clone, all needed git objects are already downloaded,
529 # in this situation it's better to use only one process because the checkout
530 # would be mostly disk I/O; with partial clone, the objects are only
531 # downloaded when demanded (at checkout time), which is similar to the
532 # Sync_NetworkHalf case and parallelism would be helpful.
533 if self.manifest.CloneFilter:
534 syncjobs = self.jobs
535 else:
536 syncjobs = 1
537
538 lock = _threading.Lock()
Mike Frysinger3538dd22019-08-26 15:32:06 -0400539 pm = Progress('Checking out projects', len(all_projects))
Xin Li745be2e2019-06-03 11:24:30 -0700540
541 threads = set()
542 sem = _threading.Semaphore(syncjobs)
Xin Li745be2e2019-06-03 11:24:30 -0700543
544 for project in all_projects:
545 # Check for any errors before running any more tasks.
546 # ...we'll let existing threads finish, though.
Mike Frysingerd9e5cf02019-08-26 03:12:55 -0400547 if err_event.isSet() and opt.fail_fast:
Xin Li745be2e2019-06-03 11:24:30 -0700548 break
549
550 sem.acquire()
551 if project.worktree:
552 kwargs = dict(opt=opt,
553 sem=sem,
554 project=project,
555 lock=lock,
556 pm=pm,
Vadim Bendeburydff91942019-11-06 11:05:00 -0800557 err_event=err_event,
558 err_results=err_results)
Xin Li745be2e2019-06-03 11:24:30 -0700559 if syncjobs > 1:
560 t = _threading.Thread(target=self._CheckoutWorker,
561 kwargs=kwargs)
562 # Ensure that Ctrl-C will not freeze the repo process.
563 t.daemon = True
564 threads.add(t)
565 t.start()
566 else:
567 self._CheckoutWorker(**kwargs)
568
569 for t in threads:
570 t.join()
571
572 pm.end()
Xin Li745be2e2019-06-03 11:24:30 -0700573
Mike Frysinger5a033082019-09-23 19:21:20 -0400574 def _GCProjects(self, projects, opt, err_event):
Gabe Black2ff30292014-10-09 17:54:35 -0700575 gc_gitdirs = {}
David James8d201162013-10-11 17:03:19 -0700576 for project in projects:
Mike Frysinger5f2b0452020-02-11 03:23:24 -0500577 # Make sure pruning never kicks in with shared projects.
Mike Frysinger979d5bd2020-02-09 02:28:34 -0500578 if (not project.use_git_worktrees and
David Pursehouseaa611a22020-02-20 10:47:26 +0900579 len(project.manifest.GetProjectsWithName(project.name)) > 1):
Mike Frysinger5f2b0452020-02-11 03:23:24 -0500580 print('%s: Shared project %s found, disabling pruning.' %
581 (project.relpath, project.name))
582 if git_require((2, 7, 0)):
Mike Frysingerf81c72e2020-02-19 15:50:00 -0500583 project.EnableRepositoryExtension('preciousObjects')
Mike Frysinger5f2b0452020-02-11 03:23:24 -0500584 else:
585 # This isn't perfect, but it's the best we can do with old git.
586 print('%s: WARNING: shared projects are unreliable when using old '
587 'versions of git; please upgrade to git-2.7.0+.'
588 % (project.relpath,),
589 file=sys.stderr)
590 project.config.SetString('gc.pruneExpire', 'never')
Gabe Black2ff30292014-10-09 17:54:35 -0700591 gc_gitdirs[project.gitdir] = project.bare_git
David James8d201162013-10-11 17:03:19 -0700592
Mike Frysinger6f1c6262020-02-04 00:09:23 -0500593 if multiprocessing:
Dave Borowitz18857212012-10-23 17:02:59 -0700594 cpu_count = multiprocessing.cpu_count()
595 else:
596 cpu_count = 1
597 jobs = min(self.jobs, cpu_count)
598
599 if jobs < 2:
Gabe Black2ff30292014-10-09 17:54:35 -0700600 for bare_git in gc_gitdirs.values():
David James8d201162013-10-11 17:03:19 -0700601 bare_git.gc('--auto')
Dave Borowitz18857212012-10-23 17:02:59 -0700602 return
603
Mike Frysinger0c0e9342019-06-13 12:42:39 -0400604 config = {'pack.threads': cpu_count // jobs if cpu_count > jobs else 1}
Dave Borowitz18857212012-10-23 17:02:59 -0700605
606 threads = set()
607 sem = _threading.Semaphore(jobs)
Dave Borowitz18857212012-10-23 17:02:59 -0700608
David James8d201162013-10-11 17:03:19 -0700609 def GC(bare_git):
Dave Borowitz18857212012-10-23 17:02:59 -0700610 try:
611 try:
David James8d201162013-10-11 17:03:19 -0700612 bare_git.gc('--auto', config=config)
Dave Borowitz18857212012-10-23 17:02:59 -0700613 except GitError:
614 err_event.set()
David Pursehouse145e35b2020-02-12 15:40:47 +0900615 except Exception:
Dave Borowitz18857212012-10-23 17:02:59 -0700616 err_event.set()
617 raise
618 finally:
619 sem.release()
620
Gabe Black2ff30292014-10-09 17:54:35 -0700621 for bare_git in gc_gitdirs.values():
Mike Frysinger5a033082019-09-23 19:21:20 -0400622 if err_event.isSet() and opt.fail_fast:
Dave Borowitz18857212012-10-23 17:02:59 -0700623 break
624 sem.acquire()
David James8d201162013-10-11 17:03:19 -0700625 t = _threading.Thread(target=GC, args=(bare_git,))
Dave Borowitz18857212012-10-23 17:02:59 -0700626 t.daemon = True
627 threads.add(t)
628 t.start()
629
630 for t in threads:
631 t.join()
632
Tim Kilbourn07669002013-03-08 15:02:49 -0800633 def _ReloadManifest(self, manifest_name=None):
634 if manifest_name:
635 # Override calls _Unload already
636 self.manifest.Override(manifest_name)
637 else:
638 self.manifest._Unload()
639
Oleksii Okolielovd3c0f592018-12-17 19:23:44 -0500640 def UpdateProjectList(self, opt):
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700641 new_project_paths = []
Colin Cross5acde752012-03-28 20:15:45 -0700642 for project in self.GetProjects(None, missing_ok=True):
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700643 if project.relpath:
644 new_project_paths.append(project.relpath)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700645 file_name = 'project.list'
646 file_path = os.path.join(self.manifest.repodir, file_name)
647 old_project_paths = []
648
649 if os.path.exists(file_path):
Mike Frysinger3164d402019-11-11 05:40:22 -0500650 with open(file_path, 'r') as fd:
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700651 old_project_paths = fd.read().split('\n')
Kuang-che Wu0d9b16d2019-04-06 00:49:47 +0800652 # In reversed order, so subfolders are deleted before parent folder.
653 for path in sorted(old_project_paths, reverse=True):
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700654 if not path:
655 continue
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700656 if path not in new_project_paths:
David Pursehouse8a68ff92012-09-24 12:15:13 +0900657 # If the path has already been deleted, we don't need to do it
Dan Willemsen43507912016-09-01 16:26:02 -0700658 gitdir = os.path.join(self.manifest.topdir, path, '.git')
659 if os.path.exists(gitdir):
David Pursehousec1b86a22012-11-14 11:36:51 +0900660 project = Project(
David Pursehouseabdf7502020-02-12 14:58:39 +0900661 manifest=self.manifest,
662 name=path,
663 remote=RemoteSpec('origin'),
664 gitdir=gitdir,
665 objdir=gitdir,
Mike Frysingerc0d18662020-02-19 19:19:18 -0500666 use_git_worktrees=os.path.isfile(gitdir),
David Pursehouseabdf7502020-02-12 14:58:39 +0900667 worktree=os.path.join(self.manifest.topdir, path),
668 relpath=path,
669 revisionExpr='HEAD',
670 revisionId=None,
671 groups=None)
Mike Frysingerc0d18662020-02-19 19:19:18 -0500672 if not project.DeleteWorktree(
David Pursehouseaa611a22020-02-20 10:47:26 +0900673 quiet=opt.quiet,
674 force=opt.force_remove_dirty):
Mike Frysingera850ca22019-08-07 17:19:24 -0400675 return 1
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700676
Shawn O. Pearce9fb29ce2009-06-04 20:41:02 -0700677 new_project_paths.sort()
Mike Frysinger3164d402019-11-11 05:40:22 -0500678 with open(file_path, 'w') as fd:
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700679 fd.write('\n'.join(new_project_paths))
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700680 fd.write('\n')
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700681 return 0
682
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400683 def _SmartSyncSetup(self, opt, smart_sync_manifest_path):
684 if not self.manifest.manifest_server:
685 print('error: cannot smart sync: no manifest server defined in '
686 'manifest', file=sys.stderr)
687 sys.exit(1)
688
689 manifest_server = self.manifest.manifest_server
690 if not opt.quiet:
691 print('Using manifest server %s' % manifest_server)
692
David Pursehouseeeff3532020-02-12 11:24:10 +0900693 if '@' not in manifest_server:
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400694 username = None
695 password = None
696 if opt.manifest_server_username and opt.manifest_server_password:
697 username = opt.manifest_server_username
698 password = opt.manifest_server_password
699 else:
700 try:
701 info = netrc.netrc()
702 except IOError:
703 # .netrc file does not exist or could not be opened
704 pass
705 else:
706 try:
707 parse_result = urllib.parse.urlparse(manifest_server)
708 if parse_result.hostname:
709 auth = info.authenticators(parse_result.hostname)
710 if auth:
711 username, _account, password = auth
712 else:
713 print('No credentials found for %s in .netrc'
714 % parse_result.hostname, file=sys.stderr)
715 except netrc.NetrcParseError as e:
716 print('Error parsing .netrc file: %s' % e, file=sys.stderr)
717
718 if (username and password):
719 manifest_server = manifest_server.replace('://', '://%s:%s@' %
720 (username, password),
721 1)
722
723 transport = PersistentTransport(manifest_server)
724 if manifest_server.startswith('persistent-'):
725 manifest_server = manifest_server[len('persistent-'):]
726
727 try:
728 server = xmlrpc.client.Server(manifest_server, transport=transport)
729 if opt.smart_sync:
730 p = self.manifest.manifestProject
731 b = p.GetBranch(p.CurrentBranch)
732 branch = b.merge
733 if branch.startswith(R_HEADS):
734 branch = branch[len(R_HEADS):]
735
Mike Frysinger56ce3462019-12-04 19:30:48 -0500736 if 'SYNC_TARGET' in os.environ:
Mike Frysingere20da3e2020-03-07 01:53:53 -0500737 target = os.environ['SYNC_TARGET']
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400738 [success, manifest_str] = server.GetApprovedManifest(branch, target)
Mike Frysinger56ce3462019-12-04 19:30:48 -0500739 elif ('TARGET_PRODUCT' in os.environ and
740 'TARGET_BUILD_VARIANT' in os.environ):
Mike Frysingere20da3e2020-03-07 01:53:53 -0500741 target = '%s-%s' % (os.environ['TARGET_PRODUCT'],
742 os.environ['TARGET_BUILD_VARIANT'])
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400743 [success, manifest_str] = server.GetApprovedManifest(branch, target)
744 else:
745 [success, manifest_str] = server.GetApprovedManifest(branch)
746 else:
747 assert(opt.smart_tag)
748 [success, manifest_str] = server.GetManifest(opt.smart_tag)
749
750 if success:
751 manifest_name = os.path.basename(smart_sync_manifest_path)
752 try:
Mike Frysinger3164d402019-11-11 05:40:22 -0500753 with open(smart_sync_manifest_path, 'w') as f:
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400754 f.write(manifest_str)
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400755 except IOError as e:
756 print('error: cannot write manifest to %s:\n%s'
757 % (smart_sync_manifest_path, e),
758 file=sys.stderr)
759 sys.exit(1)
760 self._ReloadManifest(manifest_name)
761 else:
762 print('error: manifest server RPC call failed: %s' %
763 manifest_str, file=sys.stderr)
764 sys.exit(1)
765 except (socket.error, IOError, xmlrpc.client.Fault) as e:
766 print('error: cannot connect to manifest server %s:\n%s'
767 % (self.manifest.manifest_server, e), file=sys.stderr)
768 sys.exit(1)
769 except xmlrpc.client.ProtocolError as e:
770 print('error: cannot connect to manifest server %s:\n%d %s'
771 % (self.manifest.manifest_server, e.errcode, e.errmsg),
772 file=sys.stderr)
773 sys.exit(1)
774
775 return manifest_name
776
Mike Frysingerfb527e32019-08-27 02:34:32 -0400777 def _UpdateManifestProject(self, opt, mp, manifest_name):
778 """Fetch & update the local manifest project."""
779 if not opt.local_only:
780 start = time.time()
Mike Frysinger521d01b2020-02-17 01:51:49 -0500781 success = mp.Sync_NetworkHalf(quiet=opt.quiet, verbose=opt.verbose,
Mike Frysingerfb527e32019-08-27 02:34:32 -0400782 current_branch_only=opt.current_branch_only,
Mike Frysingerc58ec4d2020-02-17 14:36:08 -0500783 tags=opt.tags,
Mike Frysingerfb527e32019-08-27 02:34:32 -0400784 optimized_fetch=opt.optimized_fetch,
George Engelbrecht9bc283e2020-04-02 12:36:09 -0600785 retry_fetches=opt.retry_fetches,
Mike Frysingerfb527e32019-08-27 02:34:32 -0400786 submodules=self.manifest.HasSubmodules,
787 clone_filter=self.manifest.CloneFilter)
788 finish = time.time()
789 self.event_log.AddSync(mp, event_log.TASK_SYNC_NETWORK,
790 start, finish, success)
791
792 if mp.HasChanges:
793 syncbuf = SyncBuffer(mp.config)
794 start = time.time()
795 mp.Sync_LocalHalf(syncbuf, submodules=self.manifest.HasSubmodules)
796 clean = syncbuf.Finish()
797 self.event_log.AddSync(mp, event_log.TASK_SYNC_LOCAL,
798 start, time.time(), clean)
799 if not clean:
800 sys.exit(1)
801 self._ReloadManifest(opt.manifest_name)
802 if opt.jobs is None:
803 self.jobs = self.manifest.default.sync_j
804
Mike Frysingerae6cb082019-08-27 01:10:59 -0400805 def ValidateOptions(self, opt, args):
806 if opt.force_broken:
807 print('warning: -f/--force-broken is now the default behavior, and the '
808 'options are deprecated', file=sys.stderr)
809 if opt.network_only and opt.detach_head:
810 self.OptionParser.error('cannot combine -n and -d')
811 if opt.network_only and opt.local_only:
812 self.OptionParser.error('cannot combine -n and -l')
813 if opt.manifest_name and opt.smart_sync:
814 self.OptionParser.error('cannot combine -m and -s')
815 if opt.manifest_name and opt.smart_tag:
816 self.OptionParser.error('cannot combine -m and -t')
817 if opt.manifest_server_username or opt.manifest_server_password:
818 if not (opt.smart_sync or opt.smart_tag):
819 self.OptionParser.error('-u and -p may only be combined with -s or -t')
820 if None in [opt.manifest_server_username, opt.manifest_server_password]:
821 self.OptionParser.error('both -u and -p must be given')
822
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700823 def Execute(self, opt, args):
Roy Lee18afd7f2010-05-09 04:32:08 +0800824 if opt.jobs:
825 self.jobs = opt.jobs
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -0700826 if self.jobs > 1:
827 soft_limit, _ = _rlimit_nofile()
Mike Frysinger0c0e9342019-06-13 12:42:39 -0400828 self.jobs = min(self.jobs, (soft_limit - 5) // 3)
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -0700829
Mike Frysinger521d01b2020-02-17 01:51:49 -0500830 opt.quiet = opt.output_mode is False
831 opt.verbose = opt.output_mode is True
832
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500833 if opt.manifest_name:
834 self.manifest.Override(opt.manifest_name)
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700835
Chirayu Desaia892b102013-06-11 14:18:46 +0530836 manifest_name = opt.manifest_name
David Pursehouse59b41742015-05-07 14:36:09 +0900837 smart_sync_manifest_path = os.path.join(
David Pursehouseabdf7502020-02-12 14:58:39 +0900838 self.manifest.manifestProject.worktree, 'smart_sync_override.xml')
Chirayu Desaia892b102013-06-11 14:18:46 +0530839
Xin Lid79a4bc2020-05-20 16:03:45 -0700840 if opt.clone_bundle is None:
841 opt.clone_bundle = self.manifest.CloneBundle
842
Victor Boivie08c880d2011-04-19 10:32:52 +0200843 if opt.smart_sync or opt.smart_tag:
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400844 manifest_name = self._SmartSyncSetup(opt, smart_sync_manifest_path)
845 else:
David Pursehouse59b41742015-05-07 14:36:09 +0900846 if os.path.isfile(smart_sync_manifest_path):
847 try:
Renaud Paquay010fed72016-11-11 14:25:29 -0800848 platform_utils.remove(smart_sync_manifest_path)
David Pursehouse59b41742015-05-07 14:36:09 +0900849 except OSError as e:
850 print('error: failed to remove existing smart sync override manifest: %s' %
851 e, file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700852
Mike Frysinger5a033082019-09-23 19:21:20 -0400853 err_event = _threading.Event()
854
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700855 rp = self.manifest.repoProject
856 rp.PreSync()
Mike Frysinger23c900f2020-02-29 02:52:44 -0500857 cb = rp.CurrentBranch
858 if cb:
859 base = rp.GetBranch(cb).merge
860 if not base or not base.startswith('refs/heads/'):
861 print('warning: repo is not tracking a remote branch, so it will not '
Mike Frysinger58ac1672020-03-14 14:35:26 -0400862 'receive updates; run `repo init --repo-rev=stable` to fix.',
Mike Frysinger23c900f2020-02-29 02:52:44 -0500863 file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700864
865 mp = self.manifest.manifestProject
866 mp.PreSync()
867
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800868 if opt.repo_upgraded:
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -0700869 _PostRepoUpgrade(self.manifest, quiet=opt.quiet)
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800870
Fredrik de Grootcc960972019-11-22 09:04:31 +0100871 if not opt.mp_update:
872 print('Skipping update of local manifest project.')
873 else:
874 self._UpdateManifestProject(opt, mp, manifest_name)
Simran Basib9a1b732015-08-20 12:19:28 -0700875
Simran Basib9a1b732015-08-20 12:19:28 -0700876 if self.gitc_manifest:
877 gitc_manifest_projects = self.GetProjects(args,
Simran Basib9a1b732015-08-20 12:19:28 -0700878 missing_ok=True)
879 gitc_projects = []
880 opened_projects = []
881 for project in gitc_manifest_projects:
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700882 if project.relpath in self.gitc_manifest.paths and \
883 self.gitc_manifest.paths[project.relpath].old_revision:
884 opened_projects.append(project.relpath)
Simran Basib9a1b732015-08-20 12:19:28 -0700885 else:
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700886 gitc_projects.append(project.relpath)
Simran Basib9a1b732015-08-20 12:19:28 -0700887
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700888 if not args:
889 gitc_projects = None
890
891 if gitc_projects != [] and not opt.local_only:
Simran Basib9a1b732015-08-20 12:19:28 -0700892 print('Updating GITC client: %s' % self.gitc_manifest.gitc_client_name)
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700893 manifest = GitcManifest(self.repodir, self.gitc_manifest.gitc_client_name)
894 if manifest_name:
895 manifest.Override(manifest_name)
896 else:
897 manifest.Override(self.manifest.manifestFile)
898 gitc_utils.generate_gitc_manifest(self.gitc_manifest,
899 manifest,
Simran Basib9a1b732015-08-20 12:19:28 -0700900 gitc_projects)
901 print('GITC client successfully synced.')
902
903 # The opened projects need to be synced as normal, therefore we
904 # generate a new args list to represent the opened projects.
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700905 # TODO: make this more reliable -- if there's a project name/path overlap,
906 # this may choose the wrong project.
David Pursehouse3bcd3052017-07-10 22:42:22 +0900907 args = [os.path.relpath(self.manifest.paths[path].worktree, os.getcwd())
908 for path in opened_projects]
Simran Basib9a1b732015-08-20 12:19:28 -0700909 if not args:
910 return
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800911 all_projects = self.GetProjects(args,
912 missing_ok=True,
913 submodules_ok=opt.fetch_submodules)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700914
Mike Frysinger5a033082019-09-23 19:21:20 -0400915 err_network_sync = False
916 err_update_projects = False
917 err_checkout = False
918
Dave Borowitz67700e92012-10-23 15:00:54 -0700919 self._fetch_times = _FetchTimes(self.manifest)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700920 if not opt.local_only:
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700921 to_fetch = []
922 now = time.time()
Dave Borowitz67700e92012-10-23 15:00:54 -0700923 if _ONE_DAY_S <= (now - rp.LastFetch):
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700924 to_fetch.append(rp)
David Pursehouse8a68ff92012-09-24 12:15:13 +0900925 to_fetch.extend(all_projects)
Dave Borowitz67700e92012-10-23 15:00:54 -0700926 to_fetch.sort(key=self._fetch_times.Get, reverse=True)
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700927
Mike Frysinger5a033082019-09-23 19:21:20 -0400928 fetched = self._Fetch(to_fetch, opt, err_event)
929
Mike Frysingerc58ec4d2020-02-17 14:36:08 -0500930 _PostRepoFetch(rp, opt.repo_verify)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700931 if opt.network_only:
932 # bail out now; the rest touches the working tree
Mike Frysinger5a033082019-09-23 19:21:20 -0400933 if err_event.isSet():
934 print('\nerror: Exited sync due to fetch errors.\n', file=sys.stderr)
935 sys.exit(1)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700936 return
937
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800938 # Iteratively fetch missing and/or nested unregistered submodules
939 previously_missing_set = set()
940 while True:
Victor Boivie53a6c5d2013-03-19 12:20:52 +0100941 self._ReloadManifest(manifest_name)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800942 all_projects = self.GetProjects(args,
943 missing_ok=True,
944 submodules_ok=opt.fetch_submodules)
945 missing = []
946 for project in all_projects:
947 if project.gitdir not in fetched:
948 missing.append(project)
949 if not missing:
950 break
951 # Stop us from non-stopped fetching actually-missing repos: If set of
952 # missing repos has not been changed from last fetch, we break.
953 missing_set = set(p.name for p in missing)
954 if previously_missing_set == missing_set:
955 break
956 previously_missing_set = missing_set
Mike Frysinger5a033082019-09-23 19:21:20 -0400957 fetched.update(self._Fetch(missing, opt, err_event))
958
959 # If we saw an error, exit with code 1 so that other scripts can check.
960 if err_event.isSet():
961 err_network_sync = True
962 if opt.fail_fast:
963 print('\nerror: Exited sync due to fetch errors.\n'
964 'Local checkouts *not* updated. Resolve network issues & '
965 'retry.\n'
966 '`repo sync -l` will update some local checkouts.',
967 file=sys.stderr)
968 sys.exit(1)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800969
Julien Campergue335f5ef2013-10-16 11:02:35 +0200970 if self.manifest.IsMirror or self.manifest.IsArchive:
Shawn O. Pearcecd1d7ff2009-06-04 16:15:53 -0700971 # bail out now, we have no working tree
972 return
973
Oleksii Okolielovd3c0f592018-12-17 19:23:44 -0500974 if self.UpdateProjectList(opt):
Mike Frysinger5a033082019-09-23 19:21:20 -0400975 err_event.set()
976 err_update_projects = True
977 if opt.fail_fast:
978 print('\nerror: Local checkouts *not* updated.', file=sys.stderr)
979 sys.exit(1)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700980
Mike Frysinger5a033082019-09-23 19:21:20 -0400981 err_results = []
982 self._Checkout(all_projects, opt, err_event, err_results)
983 if err_event.isSet():
984 err_checkout = True
985 # NB: We don't exit here because this is the last step.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700986
Doug Anderson2b8db3c2010-11-01 15:08:06 -0700987 # If there's a notice that's supposed to print at the end of the sync, print
988 # it now...
989 if self.manifest.notice:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700990 print(self.manifest.notice)
Doug Anderson2b8db3c2010-11-01 15:08:06 -0700991
Mike Frysinger5a033082019-09-23 19:21:20 -0400992 # If we saw an error, exit with code 1 so that other scripts can check.
993 if err_event.isSet():
994 print('\nerror: Unable to fully sync the tree.', file=sys.stderr)
995 if err_network_sync:
996 print('error: Downloading network changes failed.', file=sys.stderr)
997 if err_update_projects:
998 print('error: Updating local project lists failed.', file=sys.stderr)
999 if err_checkout:
1000 print('error: Checking out local projects failed.', file=sys.stderr)
1001 if err_results:
1002 print('Failing repos:\n%s' % '\n'.join(err_results), file=sys.stderr)
1003 print('Try re-running with "-j1 --fail-fast" to exit at the first error.',
1004 file=sys.stderr)
1005 sys.exit(1)
1006
Mike Frysingere19d9e12020-02-12 11:23:32 -05001007 if not opt.quiet:
1008 print('repo sync has finished successfully.')
1009
David Pursehouse819827a2020-02-12 15:20:19 +09001010
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -07001011def _PostRepoUpgrade(manifest, quiet=False):
Conley Owens094cdbe2014-01-30 15:09:59 -08001012 wrapper = Wrapper()
Conley Owensc9129d92012-10-01 16:12:28 -07001013 if wrapper.NeedSetupGnuPG():
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -07001014 wrapper.SetupGnuPG(quiet)
Conley Owensf2fe2d92014-01-29 13:53:43 -08001015 for project in manifest.projects:
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001016 if project.Exists:
1017 project.PostRepoUpgrade()
1018
David Pursehouse819827a2020-02-12 15:20:19 +09001019
Mike Frysingerc58ec4d2020-02-17 14:36:08 -05001020def _PostRepoFetch(rp, repo_verify=True, verbose=False):
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001021 if rp.HasChanges:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001022 print('info: A new version of repo is available', file=sys.stderr)
1023 print(file=sys.stderr)
Mike Frysingerc58ec4d2020-02-17 14:36:08 -05001024 if not repo_verify or _VerifyTag(rp):
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001025 syncbuf = SyncBuffer(rp.config)
1026 rp.Sync_LocalHalf(syncbuf)
1027 if not syncbuf.Finish():
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001028 sys.exit(1)
Sarah Owenscecd1d82012-11-01 22:59:27 -07001029 print('info: Restarting repo with latest version', file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001030 raise RepoChangedException(['--repo-upgraded'])
1031 else:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001032 print('warning: Skipped upgrade to unverified version', file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001033 else:
1034 if verbose:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001035 print('repo version %s is current' % rp.work_git.describe(HEAD),
1036 file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001037
David Pursehouse819827a2020-02-12 15:20:19 +09001038
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001039def _VerifyTag(project):
1040 gpg_dir = os.path.expanduser('~/.repoconfig/gnupg')
1041 if not os.path.exists(gpg_dir):
Sarah Owenscecd1d82012-11-01 22:59:27 -07001042 print('warning: GnuPG was not available during last "repo init"\n'
1043 'warning: Cannot automatically authenticate repo."""',
1044 file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001045 return True
1046
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001047 try:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001048 cur = project.bare_git.describe(project.GetRevisionId())
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001049 except GitError:
1050 cur = None
1051
1052 if not cur \
1053 or re.compile(r'^.*-[0-9]{1,}-g[0-9a-f]{1,}$').match(cur):
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001054 rev = project.revisionExpr
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001055 if rev.startswith(R_HEADS):
1056 rev = rev[len(R_HEADS):]
1057
Sarah Owenscecd1d82012-11-01 22:59:27 -07001058 print(file=sys.stderr)
1059 print("warning: project '%s' branch '%s' is not signed"
1060 % (project.name, rev), file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001061 return False
1062
Shawn O. Pearcef18cb762010-12-07 11:41:05 -08001063 env = os.environ.copy()
Mike Frysinger56ce3462019-12-04 19:30:48 -05001064 env['GIT_DIR'] = project.gitdir
1065 env['GNUPGHOME'] = gpg_dir
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001066
1067 cmd = [GIT, 'tag', '-v', cur]
1068 proc = subprocess.Popen(cmd,
David Pursehousee5913ae2020-02-12 13:56:59 +09001069 stdout=subprocess.PIPE,
1070 stderr=subprocess.PIPE,
1071 env=env)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001072 out = proc.stdout.read()
1073 proc.stdout.close()
1074
1075 err = proc.stderr.read()
1076 proc.stderr.close()
1077
1078 if proc.wait() != 0:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001079 print(file=sys.stderr)
1080 print(out, file=sys.stderr)
1081 print(err, file=sys.stderr)
1082 print(file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001083 return False
1084 return True
Dave Borowitz67700e92012-10-23 15:00:54 -07001085
David Rileye0684ad2017-04-05 00:02:59 -07001086
Dave Borowitz67700e92012-10-23 15:00:54 -07001087class _FetchTimes(object):
Dave Borowitzd9478582012-10-23 16:35:39 -07001088 _ALPHA = 0.5
1089
Dave Borowitz67700e92012-10-23 15:00:54 -07001090 def __init__(self, manifest):
Anthony King85b24ac2014-05-06 15:57:48 +01001091 self._path = os.path.join(manifest.repodir, '.repo_fetchtimes.json')
Dave Borowitz67700e92012-10-23 15:00:54 -07001092 self._times = None
Dave Borowitzd9478582012-10-23 16:35:39 -07001093 self._seen = set()
Dave Borowitz67700e92012-10-23 15:00:54 -07001094
1095 def Get(self, project):
1096 self._Load()
1097 return self._times.get(project.name, _ONE_DAY_S)
1098
1099 def Set(self, project, t):
Dave Borowitzd9478582012-10-23 16:35:39 -07001100 self._Load()
1101 name = project.name
1102 old = self._times.get(name, t)
1103 self._seen.add(name)
1104 a = self._ALPHA
David Pursehouse54a4e602020-02-12 14:31:05 +09001105 self._times[name] = (a * t) + ((1 - a) * old)
Dave Borowitz67700e92012-10-23 15:00:54 -07001106
1107 def _Load(self):
1108 if self._times is None:
1109 try:
Mike Frysinger3164d402019-11-11 05:40:22 -05001110 with open(self._path) as f:
Anthony King85b24ac2014-05-06 15:57:48 +01001111 self._times = json.load(f)
Anthony King85b24ac2014-05-06 15:57:48 +01001112 except (IOError, ValueError):
1113 try:
Renaud Paquay010fed72016-11-11 14:25:29 -08001114 platform_utils.remove(self._path)
Anthony King85b24ac2014-05-06 15:57:48 +01001115 except OSError:
1116 pass
1117 self._times = {}
Dave Borowitz67700e92012-10-23 15:00:54 -07001118
1119 def Save(self):
1120 if self._times is None:
1121 return
Dave Borowitzd9478582012-10-23 16:35:39 -07001122
1123 to_delete = []
1124 for name in self._times:
1125 if name not in self._seen:
1126 to_delete.append(name)
1127 for name in to_delete:
1128 del self._times[name]
1129
Dave Borowitz67700e92012-10-23 15:00:54 -07001130 try:
Mike Frysinger3164d402019-11-11 05:40:22 -05001131 with open(self._path, 'w') as f:
Anthony King85b24ac2014-05-06 15:57:48 +01001132 json.dump(self._times, f, indent=2)
Anthony King85b24ac2014-05-06 15:57:48 +01001133 except (IOError, TypeError):
1134 try:
Renaud Paquay010fed72016-11-11 14:25:29 -08001135 platform_utils.remove(self._path)
Anthony King85b24ac2014-05-06 15:57:48 +01001136 except OSError:
1137 pass
Dan Willemsen0745bb22015-08-17 13:41:45 -07001138
1139# This is a replacement for xmlrpc.client.Transport using urllib2
1140# and supporting persistent-http[s]. It cannot change hosts from
1141# request to request like the normal transport, the real url
1142# is passed during initialization.
David Pursehouse819827a2020-02-12 15:20:19 +09001143
1144
Dan Willemsen0745bb22015-08-17 13:41:45 -07001145class PersistentTransport(xmlrpc.client.Transport):
1146 def __init__(self, orig_host):
1147 self.orig_host = orig_host
1148
1149 def request(self, host, handler, request_body, verbose=False):
1150 with GetUrlCookieFile(self.orig_host, not verbose) as (cookiefile, proxy):
1151 # Python doesn't understand cookies with the #HttpOnly_ prefix
1152 # Since we're only using them for HTTP, copy the file temporarily,
1153 # stripping those prefixes away.
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001154 if cookiefile:
Collin Fijalkoviche1191b32020-02-18 10:57:32 -08001155 tmpcookiefile = tempfile.NamedTemporaryFile(mode='w')
David Pursehouse4c5f74e2015-10-02 11:10:10 +09001156 tmpcookiefile.write("# HTTP Cookie File")
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001157 try:
1158 with open(cookiefile) as f:
1159 for line in f:
1160 if line.startswith("#HttpOnly_"):
1161 line = line[len("#HttpOnly_"):]
1162 tmpcookiefile.write(line)
1163 tmpcookiefile.flush()
Dan Willemsen0745bb22015-08-17 13:41:45 -07001164
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001165 cookiejar = cookielib.MozillaCookieJar(tmpcookiefile.name)
David Pursehouseb1ad2192015-09-30 10:35:43 +09001166 try:
1167 cookiejar.load()
1168 except cookielib.LoadError:
1169 cookiejar = cookielib.CookieJar()
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001170 finally:
1171 tmpcookiefile.close()
1172 else:
1173 cookiejar = cookielib.CookieJar()
Dan Willemsen0745bb22015-08-17 13:41:45 -07001174
1175 proxyhandler = urllib.request.ProxyHandler
1176 if proxy:
1177 proxyhandler = urllib.request.ProxyHandler({
1178 "http": proxy,
David Pursehouse54a4e602020-02-12 14:31:05 +09001179 "https": proxy})
Dan Willemsen0745bb22015-08-17 13:41:45 -07001180
1181 opener = urllib.request.build_opener(
1182 urllib.request.HTTPCookieProcessor(cookiejar),
1183 proxyhandler)
1184
1185 url = urllib.parse.urljoin(self.orig_host, handler)
1186 parse_results = urllib.parse.urlparse(url)
1187
1188 scheme = parse_results.scheme
1189 if scheme == 'persistent-http':
1190 scheme = 'http'
1191 if scheme == 'persistent-https':
1192 # If we're proxying through persistent-https, use http. The
1193 # proxy itself will do the https.
1194 if proxy:
1195 scheme = 'http'
1196 else:
1197 scheme = 'https'
1198
1199 # Parse out any authentication information using the base class
1200 host, extra_headers, _ = self.get_host_info(parse_results.netloc)
1201
1202 url = urllib.parse.urlunparse((
1203 scheme,
1204 host,
1205 parse_results.path,
1206 parse_results.params,
1207 parse_results.query,
1208 parse_results.fragment))
1209
1210 request = urllib.request.Request(url, request_body)
1211 if extra_headers is not None:
1212 for (name, header) in extra_headers:
1213 request.add_header(name, header)
1214 request.add_header('Content-Type', 'text/xml')
1215 try:
1216 response = opener.open(request)
1217 except urllib.error.HTTPError as e:
1218 if e.code == 501:
1219 # We may have been redirected through a login process
1220 # but our POST turned into a GET. Retry.
1221 response = opener.open(request)
1222 else:
1223 raise
1224
1225 p, u = xmlrpc.client.getparser()
1226 while 1:
1227 data = response.read(1024)
1228 if not data:
1229 break
1230 p.feed(data)
1231 p.close()
1232 return u.close()
1233
1234 def close(self):
1235 pass