blob: b14ad24637a8239490359db6ff35ebd71259eb28 [file] [log] [blame]
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001# Copyright (C) 2008 The Android Open Source Project
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
Mike Frysingerebf04a42021-02-23 20:48:04 -050015import functools
Mike Frysingeracf63b22019-06-13 02:24:21 -040016import http.cookiejar as cookielib
Mike Frysinger7b586f22021-02-23 18:38:39 -050017import io
Anthony King85b24ac2014-05-06 15:57:48 +010018import json
Mike Frysingerebf04a42021-02-23 20:48:04 -050019import multiprocessing
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
Mike Frysingeracf63b22019-06-13 02:24:21 -040029import urllib.error
30import urllib.parse
31import urllib.request
32import xmlrpc.client
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070033
Roy Lee18afd7f2010-05-09 04:32:08 +080034try:
35 import threading as _threading
36except ImportError:
37 import dummy_threading as _threading
38
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -070039try:
40 import resource
David Pursehouse819827a2020-02-12 15:20:19 +090041
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -070042 def _rlimit_nofile():
43 return resource.getrlimit(resource.RLIMIT_NOFILE)
44except ImportError:
45 def _rlimit_nofile():
46 return (256, 256)
47
Dave Borowitz18857212012-10-23 17:02:59 -070048try:
49 import multiprocessing
50except ImportError:
51 multiprocessing = None
52
David Rileye0684ad2017-04-05 00:02:59 -070053import event_log
Dave Borowitze2152672012-10-31 12:24:38 -070054from git_command import GIT, git_require
David Pursehouseba7bc732015-08-20 16:55:42 +090055from git_config import GetUrlCookieFile
David Pursehoused94aaef2012-09-07 09:52:04 +090056from git_refs import R_HEADS, HEAD
Raman Tenneti6a872c92021-01-14 19:17:50 -080057import git_superproject
Simran Basibdb52712015-08-10 13:23:23 -070058import gitc_utils
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -070059from project import Project
60from project import RemoteSpec
Mike Frysingerebf04a42021-02-23 20:48:04 -050061from command import Command, MirrorSafeCommand, WORKER_BATCH_SIZE
Raman Tenneti1fd7bc22021-02-04 14:39:38 -080062from error import RepoChangedException, GitError, ManifestParseError
Renaud Paquaya65adf72016-11-03 10:37:53 -070063import platform_utils
Shawn O. Pearce350cde42009-04-16 11:21:18 -070064from project import SyncBuffer
Shawn O. Pearce68194f42009-04-10 16:48:52 -070065from progress import Progress
Conley Owens094cdbe2014-01-30 15:09:59 -080066from wrapper import Wrapper
Dan Willemsen5ea32d12015-09-08 13:27:20 -070067from manifest_xml import GitcManifest
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070068
Dave Borowitz67700e92012-10-23 15:00:54 -070069_ONE_DAY_S = 24 * 60 * 60
70
David Pursehouse819827a2020-02-12 15:20:19 +090071
Doug Andersonfc06ced2011-03-16 15:49:18 -070072class _FetchError(Exception):
73 """Internal error thrown in _FetchHelper() when we don't want stack trace."""
Doug Andersonfc06ced2011-03-16 15:49:18 -070074
David Pursehouse819827a2020-02-12 15:20:19 +090075
Shawn O. Pearcec95583b2009-03-03 17:47:06 -080076class Sync(Command, MirrorSafeCommand):
Roy Lee18afd7f2010-05-09 04:32:08 +080077 jobs = 1
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070078 common = True
79 helpSummary = "Update working tree to the latest revision"
80 helpUsage = """
81%prog [<project>...]
82"""
83 helpDescription = """
84The '%prog' command synchronizes local project directories
85with the remote repositories specified in the manifest. If a local
86project does not yet exist, it will clone a new local directory from
87the remote repository and set up tracking branches as specified in
88the manifest. If the local project already exists, '%prog'
89will update the remote branches and rebase any new local changes
90on top of the new remote changes.
91
92'%prog' will synchronize all projects listed at the command
93line. Projects can be specified either by name, or by a relative
94or absolute path to the project's local directory. If no projects
95are specified, '%prog' will synchronize all projects listed in
96the manifest.
Shawn O. Pearce3e768c92009-04-10 16:59:36 -070097
98The -d/--detach option can be used to switch specified projects
99back to the manifest revision. This option is especially helpful
100if the project is currently on a topic branch, but the manifest
101revision is temporarily needed.
Shawn O. Pearceeb7af872009-04-21 08:02:04 -0700102
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700103The -s/--smart-sync option can be used to sync to a known good
104build as specified by the manifest-server element in the current
Victor Boivie08c880d2011-04-19 10:32:52 +0200105manifest. The -t/--smart-tag option is similar and allows you to
106specify a custom tag/label.
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700107
David Pursehousecf76b1b2012-09-14 10:31:42 +0900108The -u/--manifest-server-username and -p/--manifest-server-password
109options can be used to specify a username and password to authenticate
110with the manifest server when using the -s or -t option.
111
112If -u and -p are not specified when using the -s or -t option, '%prog'
113will attempt to read authentication credentials for the manifest server
114from the user's .netrc file.
115
116'%prog' will not use authentication credentials from -u/-p or .netrc
117if the manifest server specified in the manifest file already includes
118credentials.
119
Mike Frysingerd9e5cf02019-08-26 03:12:55 -0400120By default, all projects will be synced. The --fail-fast option can be used
Mike Frysinger7ae210a2020-05-24 14:56:52 -0400121to halt syncing as soon as possible when the first project fails to sync.
Andrei Warkentin5df6de02010-07-02 17:58:31 -0500122
Kevin Degiabaa7f32014-11-12 11:27:45 -0700123The --force-sync option can be used to overwrite existing git
124directories if they have previously been linked to a different
Roger Shimizuac29ac32020-06-06 02:33:40 +0900125object directory. WARNING: This may cause data to be lost since
Kevin Degiabaa7f32014-11-12 11:27:45 -0700126refs may be removed when overwriting.
127
Oleksii Okolielovd3c0f592018-12-17 19:23:44 -0500128The --force-remove-dirty option can be used to remove previously used
129projects with uncommitted changes. WARNING: This may cause data to be
130lost since uncommitted changes may be removed with projects that no longer
131exist in the manifest.
132
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -0700133The --no-clone-bundle option disables any attempt to use
134$URL/clone.bundle to bootstrap a new Git repository from a
135resumeable bundle file on a content delivery network. This
136may be necessary if there are problems with the local Python
137HTTP client or proxy configuration, but the Git binary works.
138
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800139The --fetch-submodules option enables fetching Git submodules
140of a project from server.
141
David Pursehousef2fad612015-01-29 14:36:28 +0900142The -c/--current-branch option can be used to only fetch objects that
143are on the branch specified by a project's revision.
144
David Pursehouseb1553542014-09-04 21:28:09 +0900145The --optimized-fetch option can be used to only fetch projects that
146are fixed to a sha1 revision if the sha1 revision does not already
147exist locally.
148
David Pursehouse74cfd272015-10-14 10:50:15 +0900149The --prune option can be used to remove any refs that no longer
150exist on the remote.
151
Mike Frysingerb8f7bb02018-10-10 01:05:11 -0400152# SSH Connections
Shawn O. Pearceeb7af872009-04-21 08:02:04 -0700153
154If at least one project remote URL uses an SSH connection (ssh://,
155git+ssh://, or user@host:path syntax) repo will automatically
156enable the SSH ControlMaster option when connecting to that host.
157This feature permits other projects in the same '%prog' session to
158reuse the same SSH tunnel, saving connection setup overheads.
159
160To disable this behavior on UNIX platforms, set the GIT_SSH
161environment variable to 'ssh'. For example:
162
163 export GIT_SSH=ssh
164 %prog
165
Mike Frysingerb8f7bb02018-10-10 01:05:11 -0400166# Compatibility
Shawn O. Pearceeb7af872009-04-21 08:02:04 -0700167
168This feature is automatically disabled on Windows, due to the lack
169of UNIX domain socket support.
170
171This feature is not compatible with url.insteadof rewrites in the
172user's ~/.gitconfig. '%prog' is currently not able to perform the
173rewrite early enough to establish the ControlMaster tunnel.
174
175If the remote SSH daemon is Gerrit Code Review, version 2.0.10 or
176later is required to fix a server side protocol bug.
177
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700178"""
Mike Frysinger6a2400a2021-02-16 01:43:31 -0500179 PARALLEL_JOBS = 1
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700180
Nico Sallembien6623b212010-05-11 12:57:01 -0700181 def _Options(self, p, show_smart=True):
Torne (Richard Coles)7bdbde72012-12-05 10:58:06 +0000182 try:
Mike Frysinger6a2400a2021-02-16 01:43:31 -0500183 self.PARALLEL_JOBS = self.manifest.default.sync_j
Torne (Richard Coles)7bdbde72012-12-05 10:58:06 +0000184 except ManifestParseError:
Mike Frysinger6a2400a2021-02-16 01:43:31 -0500185 pass
186 super()._Options(p)
Shawn O. Pearce6392c872011-09-22 17:44:31 -0700187
Andrei Warkentin5df6de02010-07-02 17:58:31 -0500188 p.add_option('-f', '--force-broken',
Stefan Müller-Klieser46702ed2019-08-30 10:20:15 +0200189 dest='force_broken', action='store_true',
Mike Frysingerd9e5cf02019-08-26 03:12:55 -0400190 help='obsolete option (to be deleted in the future)')
191 p.add_option('--fail-fast',
192 dest='fail_fast', action='store_true',
193 help='stop syncing after first error is hit')
Kevin Degiabaa7f32014-11-12 11:27:45 -0700194 p.add_option('--force-sync',
195 dest='force_sync', action='store_true',
196 help="overwrite an existing git directory if it needs to "
197 "point to a different object directory. WARNING: this "
198 "may cause loss of data")
Oleksii Okolielovd3c0f592018-12-17 19:23:44 -0500199 p.add_option('--force-remove-dirty',
200 dest='force_remove_dirty', action='store_true',
201 help="force remove projects with uncommitted modifications if "
202 "projects no longer exist in the manifest. "
203 "WARNING: this may cause loss of data")
David Pursehouse8f62fb72012-11-14 12:09:38 +0900204 p.add_option('-l', '--local-only',
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700205 dest='local_only', action='store_true',
206 help="only update working tree, don't fetch")
David Pursehouse54a4e602020-02-12 14:31:05 +0900207 p.add_option('--no-manifest-update', '--nmu',
Fredrik de Grootcc960972019-11-22 09:04:31 +0100208 dest='mp_update', action='store_false', default='true',
209 help='use the existing manifest checkout as-is. '
210 '(do not update to the latest revision)')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900211 p.add_option('-n', '--network-only',
Shawn O. Pearce96fdcef2009-04-10 16:29:20 -0700212 dest='network_only', action='store_true',
213 help="fetch only, don't update working tree")
David Pursehouse8f62fb72012-11-14 12:09:38 +0900214 p.add_option('-d', '--detach',
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700215 dest='detach_head', action='store_true',
216 help='detach projects back to manifest revision')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900217 p.add_option('-c', '--current-branch',
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -0700218 dest='current_branch_only', action='store_true',
219 help='fetch only current branch from server')
Mike Frysinger521d01b2020-02-17 01:51:49 -0500220 p.add_option('-v', '--verbose',
221 dest='output_mode', action='store_true',
222 help='show all sync output')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900223 p.add_option('-q', '--quiet',
Mike Frysinger521d01b2020-02-17 01:51:49 -0500224 dest='output_mode', action='store_false',
225 help='only show errors')
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500226 p.add_option('-m', '--manifest-name',
227 dest='manifest_name',
228 help='temporary manifest to use for this sync', metavar='NAME.xml')
Xin Lid79a4bc2020-05-20 16:03:45 -0700229 p.add_option('--clone-bundle', action='store_true',
230 help='enable use of /clone.bundle on HTTP/HTTPS')
231 p.add_option('--no-clone-bundle', dest='clone_bundle', action='store_false',
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -0700232 help='disable use of /clone.bundle on HTTP/HTTPS')
Conley Owens8d070cf2012-11-06 13:14:31 -0800233 p.add_option('-u', '--manifest-server-username', action='store',
234 dest='manifest_server_username',
235 help='username to authenticate with the manifest server')
236 p.add_option('-p', '--manifest-server-password', action='store',
237 dest='manifest_server_password',
238 help='password to authenticate with the manifest server')
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800239 p.add_option('--fetch-submodules',
240 dest='fetch_submodules', action='store_true',
241 help='fetch submodules from server')
Raman Tenneti6a872c92021-01-14 19:17:50 -0800242 p.add_option('--use-superproject', action='store_true',
243 help='use the manifest superproject to sync projects')
Mitchel Humpherys597868b2012-10-29 10:18:34 -0700244 p.add_option('--no-tags',
Mike Frysingerc58ec4d2020-02-17 14:36:08 -0500245 dest='tags', default=True, action='store_false',
Mitchel Humpherys597868b2012-10-29 10:18:34 -0700246 help="don't fetch tags")
David Pursehouseb1553542014-09-04 21:28:09 +0900247 p.add_option('--optimized-fetch',
248 dest='optimized_fetch', action='store_true',
249 help='only fetch projects fixed to sha1 if revision does not exist locally')
George Engelbrecht9bc283e2020-04-02 12:36:09 -0600250 p.add_option('--retry-fetches',
251 default=0, action='store', type='int',
252 help='number of times to retry fetches on transient errors')
David Pursehouse74cfd272015-10-14 10:50:15 +0900253 p.add_option('--prune', dest='prune', action='store_true',
254 help='delete refs that no longer exist on the remote')
Nico Sallembien6623b212010-05-11 12:57:01 -0700255 if show_smart:
256 p.add_option('-s', '--smart-sync',
257 dest='smart_sync', action='store_true',
David Pursehouse79fba682016-04-13 18:03:00 +0900258 help='smart sync using manifest from the latest known good build')
Victor Boivie08c880d2011-04-19 10:32:52 +0200259 p.add_option('-t', '--smart-tag',
260 dest='smart_tag', action='store',
261 help='smart sync using manifest from a known tag')
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700262
Shawn O. Pearcefd89b672009-04-18 11:28:57 -0700263 g = p.add_option_group('repo Version options')
264 g.add_option('--no-repo-verify',
Mike Frysingerc58ec4d2020-02-17 14:36:08 -0500265 dest='repo_verify', default=True, action='store_false',
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700266 help='do not verify repo source code')
Shawn O. Pearcefd89b672009-04-18 11:28:57 -0700267 g.add_option('--repo-upgraded',
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800268 dest='repo_upgraded', action='store_true',
Shawn O. Pearce2a1ccb22009-04-10 16:51:53 -0700269 help=SUPPRESS_HELP)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700270
Raman Tenneti8d43dea2021-02-07 16:30:27 -0800271 def _GetBranch(self):
272 """Returns the branch name for getting the approved manifest."""
273 p = self.manifest.manifestProject
274 b = p.GetBranch(p.CurrentBranch)
275 branch = b.merge
276 if branch.startswith(R_HEADS):
277 branch = branch[len(R_HEADS):]
278 return branch
279
Raman Tenneti2ae44d72021-03-23 15:12:27 -0700280 def _UseSuperproject(self, opt):
281 """Returns True if use-superproject option is enabled"""
282 return (opt.use_superproject or
283 self.manifest.manifestProject.config.GetBoolean(
284 'repo.superproject'))
285
286 def _GetCurrentBranchOnly(self, opt):
287 """Returns True if current-branch or use-superproject options are enabled."""
288 return opt.current_branch_only or self._UseSuperproject(opt)
289
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800290 def _UpdateProjectsRevisionId(self, opt, args):
291 """Update revisionId of every project with the SHA from superproject.
292
293 This function updates each project's revisionId with SHA from superproject.
294 It writes the updated manifest into a file and reloads the manifest from it.
295
296 Args:
297 opt: Program options returned from optparse. See _Options().
298 args: Arguments to pass to GetProjects. See the GetProjects
299 docstring for details.
300
301 Returns:
302 Returns path to the overriding manifest file.
303 """
Raman Tenneti21dce3d2021-02-09 00:26:31 -0800304 superproject = git_superproject.Superproject(self.manifest,
Raman Tennetief99ec02021-03-04 10:29:40 -0800305 self.repodir,
306 quiet=opt.quiet)
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800307 all_projects = self.GetProjects(args,
308 missing_ok=True,
309 submodules_ok=opt.fetch_submodules)
Raman Tenneti21dce3d2021-02-09 00:26:31 -0800310 manifest_path = superproject.UpdateProjectsRevisionId(all_projects)
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800311 if not manifest_path:
312 print('error: Update of revsionId from superproject has failed',
313 file=sys.stderr)
314 sys.exit(1)
315 self._ReloadManifest(manifest_path)
316 return manifest_path
317
Andrew Wheeler7f1ccfb2016-06-17 16:51:07 -0500318 def _FetchProjectList(self, opt, projects, sem, *args, **kwargs):
Xin Li745be2e2019-06-03 11:24:30 -0700319 """Main function of the fetch threads.
Roy Lee18afd7f2010-05-09 04:32:08 +0800320
David James8d201162013-10-11 17:03:19 -0700321 Delegates most of the work to _FetchHelper.
322
323 Args:
324 opt: Program options returned from optparse. See _Options().
325 projects: Projects to fetch.
Andrew Wheeler7f1ccfb2016-06-17 16:51:07 -0500326 sem: We'll release() this semaphore when we exit so that another thread
327 can be started up.
David James89ece422014-01-09 18:51:58 -0800328 *args, **kwargs: Remaining arguments to pass to _FetchHelper. See the
David James8d201162013-10-11 17:03:19 -0700329 _FetchHelper docstring for details.
330 """
Andrew Wheeler7f1ccfb2016-06-17 16:51:07 -0500331 try:
332 for project in projects:
333 success = self._FetchHelper(opt, project, *args, **kwargs)
Mike Frysingerd9e5cf02019-08-26 03:12:55 -0400334 if not success and opt.fail_fast:
Andrew Wheeler7f1ccfb2016-06-17 16:51:07 -0500335 break
336 finally:
337 sem.release()
David James8d201162013-10-11 17:03:19 -0700338
Xin Li745be2e2019-06-03 11:24:30 -0700339 def _FetchHelper(self, opt, project, lock, fetched, pm, err_event,
340 clone_filter):
David James8d201162013-10-11 17:03:19 -0700341 """Fetch git objects for a single project.
342
David Pursehousec1b86a22012-11-14 11:36:51 +0900343 Args:
344 opt: Program options returned from optparse. See _Options().
345 project: Project object for the project to fetch.
346 lock: Lock for accessing objects that are shared amongst multiple
347 _FetchHelper() threads.
348 fetched: set object that we will add project.gitdir to when we're done
349 (with our lock held).
350 pm: Instance of a Project object. We will call pm.update() (with our
351 lock held).
David Pursehousec1b86a22012-11-14 11:36:51 +0900352 err_event: We'll set this event in the case of an error (after printing
353 out info about the error).
Xin Li745be2e2019-06-03 11:24:30 -0700354 clone_filter: Filter for use in a partial clone.
David James8d201162013-10-11 17:03:19 -0700355
356 Returns:
357 Whether the fetch was successful.
David Pursehousec1b86a22012-11-14 11:36:51 +0900358 """
359 # We'll set to true once we've locked the lock.
360 did_lock = False
Doug Andersonfc06ced2011-03-16 15:49:18 -0700361
David Pursehousec1b86a22012-11-14 11:36:51 +0900362 # Encapsulate everything in a try/except/finally so that:
363 # - We always set err_event in the case of an exception.
David Pursehousec1b86a22012-11-14 11:36:51 +0900364 # - We always make sure we unlock the lock if we locked it.
David Rileye0684ad2017-04-05 00:02:59 -0700365 start = time.time()
366 success = False
Mike Frysinger7b586f22021-02-23 18:38:39 -0500367 buf = io.StringIO()
Mike Frysingerfbb95a42021-02-23 17:34:35 -0500368 with lock:
369 pm.start(project.name)
David Pursehousec1b86a22012-11-14 11:36:51 +0900370 try:
Doug Andersonfc06ced2011-03-16 15:49:18 -0700371 try:
David Pursehousec1b86a22012-11-14 11:36:51 +0900372 success = project.Sync_NetworkHalf(
David Pursehouseabdf7502020-02-12 14:58:39 +0900373 quiet=opt.quiet,
Mike Frysinger521d01b2020-02-17 01:51:49 -0500374 verbose=opt.verbose,
Mike Frysinger7b586f22021-02-23 18:38:39 -0500375 output_redir=buf,
Raman Tenneti2ae44d72021-03-23 15:12:27 -0700376 current_branch_only=self._GetCurrentBranchOnly(opt),
David Pursehouseabdf7502020-02-12 14:58:39 +0900377 force_sync=opt.force_sync,
Mike Frysingerc58ec4d2020-02-17 14:36:08 -0500378 clone_bundle=opt.clone_bundle,
379 tags=opt.tags, archive=self.manifest.IsArchive,
David Pursehouseabdf7502020-02-12 14:58:39 +0900380 optimized_fetch=opt.optimized_fetch,
George Engelbrecht9bc283e2020-04-02 12:36:09 -0600381 retry_fetches=opt.retry_fetches,
David Pursehouseabdf7502020-02-12 14:58:39 +0900382 prune=opt.prune,
383 clone_filter=clone_filter)
David Pursehousec1b86a22012-11-14 11:36:51 +0900384 self._fetch_times.Set(project, time.time() - start)
Doug Andersonfc06ced2011-03-16 15:49:18 -0700385
David Pursehousec1b86a22012-11-14 11:36:51 +0900386 # Lock around all the rest of the code, since printing, updating a set
387 # and Progress.update() are not thread safe.
388 lock.acquire()
389 did_lock = True
Doug Andersonfc06ced2011-03-16 15:49:18 -0700390
Mike Frysinger7b586f22021-02-23 18:38:39 -0500391 output = buf.getvalue()
392 if opt.verbose and output:
393 pm.update(inc=0, msg=output.rstrip())
394
David Pursehousec1b86a22012-11-14 11:36:51 +0900395 if not success:
Hu Xiuyune9becc02015-11-25 15:52:26 +0800396 err_event.set()
Marc Herbertffb4b892017-04-04 22:03:53 -0700397 print('error: Cannot fetch %s from %s'
398 % (project.name, project.remote.url),
399 file=sys.stderr)
Mike Frysingerd9e5cf02019-08-26 03:12:55 -0400400 if opt.fail_fast:
David Pursehousec1b86a22012-11-14 11:36:51 +0900401 raise _FetchError()
Doug Andersonfc06ced2011-03-16 15:49:18 -0700402
David Pursehousec1b86a22012-11-14 11:36:51 +0900403 fetched.add(project.gitdir)
David Pursehousec1b86a22012-11-14 11:36:51 +0900404 except _FetchError:
Hu Xiuyune9becc02015-11-25 15:52:26 +0800405 pass
Dan Sandlerc5cd4332015-07-31 09:37:53 -0400406 except Exception as e:
David Pursehouse42339d72020-02-12 14:37:15 +0900407 print('error: Cannot fetch %s (%s: %s)'
David Pursehouseabdf7502020-02-12 14:58:39 +0900408 % (project.name, type(e).__name__, str(e)), file=sys.stderr)
David Pursehousec1b86a22012-11-14 11:36:51 +0900409 err_event.set()
410 raise
411 finally:
Mike Frysingerfbb95a42021-02-23 17:34:35 -0500412 if not did_lock:
413 lock.acquire()
414 pm.finish(project.name)
415 lock.release()
David Rileye0684ad2017-04-05 00:02:59 -0700416 finish = time.time()
417 self.event_log.AddSync(project, event_log.TASK_SYNC_NETWORK,
418 start, finish, success)
Roy Lee18afd7f2010-05-09 04:32:08 +0800419
David James8d201162013-10-11 17:03:19 -0700420 return success
421
Mike Frysinger5a033082019-09-23 19:21:20 -0400422 def _Fetch(self, projects, opt, err_event):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700423 fetched = set()
David James89ece422014-01-09 18:51:58 -0800424 lock = _threading.Lock()
Mike Frysingerfbb95a42021-02-23 17:34:35 -0500425 pm = Progress('Fetching', len(projects))
Roy Lee18afd7f2010-05-09 04:32:08 +0800426
David James89ece422014-01-09 18:51:58 -0800427 objdir_project_map = dict()
428 for project in projects:
429 objdir_project_map.setdefault(project.objdir, []).append(project)
David James8d201162013-10-11 17:03:19 -0700430
David James89ece422014-01-09 18:51:58 -0800431 threads = set()
432 sem = _threading.Semaphore(self.jobs)
David James89ece422014-01-09 18:51:58 -0800433 for project_list in objdir_project_map.values():
434 # Check for any errors before running any more tasks.
435 # ...we'll let existing threads finish, though.
Mike Frysingerbe24a542021-02-23 03:24:12 -0500436 if err_event.is_set() and opt.fail_fast:
David James89ece422014-01-09 18:51:58 -0800437 break
Doug Andersonfc06ced2011-03-16 15:49:18 -0700438
David James89ece422014-01-09 18:51:58 -0800439 sem.acquire()
440 kwargs = dict(opt=opt,
441 projects=project_list,
Andrew Wheeler7f1ccfb2016-06-17 16:51:07 -0500442 sem=sem,
David James89ece422014-01-09 18:51:58 -0800443 lock=lock,
444 fetched=fetched,
445 pm=pm,
Xin Li745be2e2019-06-03 11:24:30 -0700446 err_event=err_event,
447 clone_filter=self.manifest.CloneFilter)
David James89ece422014-01-09 18:51:58 -0800448 if self.jobs > 1:
David Pursehousee5913ae2020-02-12 13:56:59 +0900449 t = _threading.Thread(target=self._FetchProjectList,
450 kwargs=kwargs)
David 'Digit' Turnere2126652012-09-05 10:35:06 +0200451 # Ensure that Ctrl-C will not freeze the repo process.
452 t.daemon = True
Roy Lee18afd7f2010-05-09 04:32:08 +0800453 threads.add(t)
454 t.start()
David James89ece422014-01-09 18:51:58 -0800455 else:
456 self._FetchProjectList(**kwargs)
Roy Lee18afd7f2010-05-09 04:32:08 +0800457
David James89ece422014-01-09 18:51:58 -0800458 for t in threads:
459 t.join()
Roy Lee18afd7f2010-05-09 04:32:08 +0800460
Shawn O. Pearce68194f42009-04-10 16:48:52 -0700461 pm.end()
Dave Borowitz67700e92012-10-23 15:00:54 -0700462 self._fetch_times.Save()
Dave Borowitz18857212012-10-23 17:02:59 -0700463
Julien Campergue335f5ef2013-10-16 11:02:35 +0200464 if not self.manifest.IsArchive:
Mike Frysinger5a033082019-09-23 19:21:20 -0400465 self._GCProjects(projects, opt, err_event)
Julien Campergue335f5ef2013-10-16 11:02:35 +0200466
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700467 return fetched
468
Mike Frysingerebf04a42021-02-23 20:48:04 -0500469 def _CheckoutOne(self, opt, project):
Xin Li745be2e2019-06-03 11:24:30 -0700470 """Checkout work tree for one project
471
472 Args:
473 opt: Program options returned from optparse. See _Options().
474 project: Project object for the project to checkout.
Xin Li745be2e2019-06-03 11:24:30 -0700475
476 Returns:
477 Whether the fetch was successful.
478 """
Xin Li745be2e2019-06-03 11:24:30 -0700479 start = time.time()
480 syncbuf = SyncBuffer(self.manifest.manifestProject.config,
481 detach_head=opt.detach_head)
482 success = False
483 try:
Mike Frysingerebf04a42021-02-23 20:48:04 -0500484 project.Sync_LocalHalf(syncbuf, force_sync=opt.force_sync)
485 success = syncbuf.Finish()
486 except Exception as e:
487 print('error: Cannot checkout %s: %s: %s' %
488 (project.name, type(e).__name__, str(e)),
489 file=sys.stderr)
490 raise
Xin Li745be2e2019-06-03 11:24:30 -0700491
Mike Frysingerebf04a42021-02-23 20:48:04 -0500492 if not success:
493 print('error: Cannot checkout %s' % (project.name), file=sys.stderr)
494 finish = time.time()
495 return (success, project, start, finish)
Xin Li745be2e2019-06-03 11:24:30 -0700496
Mike Frysingerebf04a42021-02-23 20:48:04 -0500497 def _Checkout(self, all_projects, opt, err_results):
Xin Li745be2e2019-06-03 11:24:30 -0700498 """Checkout projects listed in all_projects
499
500 Args:
501 all_projects: List of all projects that should be checked out.
502 opt: Program options returned from optparse. See _Options().
Mike Frysingerebf04a42021-02-23 20:48:04 -0500503 err_results: A list of strings, paths to git repos where checkout failed.
Xin Li745be2e2019-06-03 11:24:30 -0700504 """
Mike Frysingerebf04a42021-02-23 20:48:04 -0500505 ret = True
Xin Li745be2e2019-06-03 11:24:30 -0700506
Mike Frysingerebf04a42021-02-23 20:48:04 -0500507 # Only checkout projects with worktrees.
508 all_projects = [x for x in all_projects if x.worktree]
Xin Li745be2e2019-06-03 11:24:30 -0700509
Mike Frysingerfbb95a42021-02-23 17:34:35 -0500510 pm = Progress('Checking out', len(all_projects))
Xin Li745be2e2019-06-03 11:24:30 -0700511
Mike Frysingerebf04a42021-02-23 20:48:04 -0500512 def _ProcessResults(results):
513 for (success, project, start, finish) in results:
514 self.event_log.AddSync(project, event_log.TASK_SYNC_LOCAL,
515 start, finish, success)
516 # Check for any errors before running any more tasks.
517 # ...we'll let existing threads finish, though.
518 if not success:
519 err_results.append(project.relpath)
520 if opt.fail_fast:
521 return False
522 pm.update(msg=project.name)
523 return True
Xin Li745be2e2019-06-03 11:24:30 -0700524
Mike Frysingerebf04a42021-02-23 20:48:04 -0500525 # NB: Multiprocessing is heavy, so don't spin it up for one job.
526 if len(all_projects) == 1 or opt.jobs == 1:
527 if not _ProcessResults(self._CheckoutOne(opt, x) for x in all_projects):
528 ret = False
529 else:
530 with multiprocessing.Pool(opt.jobs) as pool:
531 results = pool.imap_unordered(
532 functools.partial(self._CheckoutOne, opt),
533 all_projects,
534 chunksize=WORKER_BATCH_SIZE)
535 if not _ProcessResults(results):
536 ret = False
537 pool.close()
Xin Li745be2e2019-06-03 11:24:30 -0700538
539 pm.end()
Xin Li745be2e2019-06-03 11:24:30 -0700540
Mike Frysinger511a0e52021-03-12 20:03:51 -0500541 return ret and not err_results
Mike Frysingerebf04a42021-02-23 20:48:04 -0500542
Mike Frysinger5a033082019-09-23 19:21:20 -0400543 def _GCProjects(self, projects, opt, err_event):
Gabe Black2ff30292014-10-09 17:54:35 -0700544 gc_gitdirs = {}
David James8d201162013-10-11 17:03:19 -0700545 for project in projects:
Mike Frysinger5f2b0452020-02-11 03:23:24 -0500546 # Make sure pruning never kicks in with shared projects.
Mike Frysinger979d5bd2020-02-09 02:28:34 -0500547 if (not project.use_git_worktrees and
David Pursehouseaa611a22020-02-20 10:47:26 +0900548 len(project.manifest.GetProjectsWithName(project.name)) > 1):
Anders Björklund2a2da802021-01-18 10:32:36 +0100549 if not opt.quiet:
550 print('%s: Shared project %s found, disabling pruning.' %
551 (project.relpath, project.name))
Mike Frysinger5f2b0452020-02-11 03:23:24 -0500552 if git_require((2, 7, 0)):
Mike Frysingerf81c72e2020-02-19 15:50:00 -0500553 project.EnableRepositoryExtension('preciousObjects')
Mike Frysinger5f2b0452020-02-11 03:23:24 -0500554 else:
555 # This isn't perfect, but it's the best we can do with old git.
556 print('%s: WARNING: shared projects are unreliable when using old '
557 'versions of git; please upgrade to git-2.7.0+.'
558 % (project.relpath,),
559 file=sys.stderr)
560 project.config.SetString('gc.pruneExpire', 'never')
Gabe Black2ff30292014-10-09 17:54:35 -0700561 gc_gitdirs[project.gitdir] = project.bare_git
David James8d201162013-10-11 17:03:19 -0700562
Mike Frysinger6f1c6262020-02-04 00:09:23 -0500563 if multiprocessing:
Dave Borowitz18857212012-10-23 17:02:59 -0700564 cpu_count = multiprocessing.cpu_count()
565 else:
566 cpu_count = 1
567 jobs = min(self.jobs, cpu_count)
568
569 if jobs < 2:
Gabe Black2ff30292014-10-09 17:54:35 -0700570 for bare_git in gc_gitdirs.values():
David James8d201162013-10-11 17:03:19 -0700571 bare_git.gc('--auto')
Dave Borowitz18857212012-10-23 17:02:59 -0700572 return
573
Mike Frysinger0c0e9342019-06-13 12:42:39 -0400574 config = {'pack.threads': cpu_count // jobs if cpu_count > jobs else 1}
Dave Borowitz18857212012-10-23 17:02:59 -0700575
576 threads = set()
577 sem = _threading.Semaphore(jobs)
Dave Borowitz18857212012-10-23 17:02:59 -0700578
David James8d201162013-10-11 17:03:19 -0700579 def GC(bare_git):
Dave Borowitz18857212012-10-23 17:02:59 -0700580 try:
581 try:
David James8d201162013-10-11 17:03:19 -0700582 bare_git.gc('--auto', config=config)
Dave Borowitz18857212012-10-23 17:02:59 -0700583 except GitError:
584 err_event.set()
David Pursehouse145e35b2020-02-12 15:40:47 +0900585 except Exception:
Dave Borowitz18857212012-10-23 17:02:59 -0700586 err_event.set()
587 raise
588 finally:
589 sem.release()
590
Gabe Black2ff30292014-10-09 17:54:35 -0700591 for bare_git in gc_gitdirs.values():
Mike Frysingerbe24a542021-02-23 03:24:12 -0500592 if err_event.is_set() and opt.fail_fast:
Dave Borowitz18857212012-10-23 17:02:59 -0700593 break
594 sem.acquire()
David James8d201162013-10-11 17:03:19 -0700595 t = _threading.Thread(target=GC, args=(bare_git,))
Dave Borowitz18857212012-10-23 17:02:59 -0700596 t.daemon = True
597 threads.add(t)
598 t.start()
599
600 for t in threads:
601 t.join()
602
Tim Kilbourn07669002013-03-08 15:02:49 -0800603 def _ReloadManifest(self, manifest_name=None):
604 if manifest_name:
605 # Override calls _Unload already
606 self.manifest.Override(manifest_name)
607 else:
608 self.manifest._Unload()
609
Oleksii Okolielovd3c0f592018-12-17 19:23:44 -0500610 def UpdateProjectList(self, opt):
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700611 new_project_paths = []
Colin Cross5acde752012-03-28 20:15:45 -0700612 for project in self.GetProjects(None, missing_ok=True):
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700613 if project.relpath:
614 new_project_paths.append(project.relpath)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700615 file_name = 'project.list'
Mike Frysingere3315bb2021-02-09 23:45:28 -0500616 file_path = os.path.join(self.repodir, file_name)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700617 old_project_paths = []
618
619 if os.path.exists(file_path):
Mike Frysinger3164d402019-11-11 05:40:22 -0500620 with open(file_path, 'r') as fd:
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700621 old_project_paths = fd.read().split('\n')
Kuang-che Wu0d9b16d2019-04-06 00:49:47 +0800622 # In reversed order, so subfolders are deleted before parent folder.
623 for path in sorted(old_project_paths, reverse=True):
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700624 if not path:
625 continue
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700626 if path not in new_project_paths:
David Pursehouse8a68ff92012-09-24 12:15:13 +0900627 # If the path has already been deleted, we don't need to do it
Dan Willemsen43507912016-09-01 16:26:02 -0700628 gitdir = os.path.join(self.manifest.topdir, path, '.git')
629 if os.path.exists(gitdir):
David Pursehousec1b86a22012-11-14 11:36:51 +0900630 project = Project(
David Pursehouseabdf7502020-02-12 14:58:39 +0900631 manifest=self.manifest,
632 name=path,
633 remote=RemoteSpec('origin'),
634 gitdir=gitdir,
635 objdir=gitdir,
Mike Frysingerc0d18662020-02-19 19:19:18 -0500636 use_git_worktrees=os.path.isfile(gitdir),
David Pursehouseabdf7502020-02-12 14:58:39 +0900637 worktree=os.path.join(self.manifest.topdir, path),
638 relpath=path,
639 revisionExpr='HEAD',
640 revisionId=None,
641 groups=None)
Mike Frysingerc0d18662020-02-19 19:19:18 -0500642 if not project.DeleteWorktree(
David Pursehouseaa611a22020-02-20 10:47:26 +0900643 quiet=opt.quiet,
644 force=opt.force_remove_dirty):
Mike Frysingera850ca22019-08-07 17:19:24 -0400645 return 1
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700646
Shawn O. Pearce9fb29ce2009-06-04 20:41:02 -0700647 new_project_paths.sort()
Mike Frysinger3164d402019-11-11 05:40:22 -0500648 with open(file_path, 'w') as fd:
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700649 fd.write('\n'.join(new_project_paths))
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700650 fd.write('\n')
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700651 return 0
652
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400653 def _SmartSyncSetup(self, opt, smart_sync_manifest_path):
654 if not self.manifest.manifest_server:
655 print('error: cannot smart sync: no manifest server defined in '
656 'manifest', file=sys.stderr)
657 sys.exit(1)
658
659 manifest_server = self.manifest.manifest_server
660 if not opt.quiet:
661 print('Using manifest server %s' % manifest_server)
662
David Pursehouseeeff3532020-02-12 11:24:10 +0900663 if '@' not in manifest_server:
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400664 username = None
665 password = None
666 if opt.manifest_server_username and opt.manifest_server_password:
667 username = opt.manifest_server_username
668 password = opt.manifest_server_password
669 else:
670 try:
671 info = netrc.netrc()
672 except IOError:
673 # .netrc file does not exist or could not be opened
674 pass
675 else:
676 try:
677 parse_result = urllib.parse.urlparse(manifest_server)
678 if parse_result.hostname:
679 auth = info.authenticators(parse_result.hostname)
680 if auth:
681 username, _account, password = auth
682 else:
683 print('No credentials found for %s in .netrc'
684 % parse_result.hostname, file=sys.stderr)
685 except netrc.NetrcParseError as e:
686 print('Error parsing .netrc file: %s' % e, file=sys.stderr)
687
688 if (username and password):
689 manifest_server = manifest_server.replace('://', '://%s:%s@' %
690 (username, password),
691 1)
692
693 transport = PersistentTransport(manifest_server)
694 if manifest_server.startswith('persistent-'):
695 manifest_server = manifest_server[len('persistent-'):]
696
697 try:
698 server = xmlrpc.client.Server(manifest_server, transport=transport)
699 if opt.smart_sync:
Raman Tenneti8d43dea2021-02-07 16:30:27 -0800700 branch = self._GetBranch()
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400701
Mike Frysinger56ce3462019-12-04 19:30:48 -0500702 if 'SYNC_TARGET' in os.environ:
Mike Frysingere20da3e2020-03-07 01:53:53 -0500703 target = os.environ['SYNC_TARGET']
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400704 [success, manifest_str] = server.GetApprovedManifest(branch, target)
Mike Frysinger56ce3462019-12-04 19:30:48 -0500705 elif ('TARGET_PRODUCT' in os.environ and
706 'TARGET_BUILD_VARIANT' in os.environ):
Mike Frysingere20da3e2020-03-07 01:53:53 -0500707 target = '%s-%s' % (os.environ['TARGET_PRODUCT'],
708 os.environ['TARGET_BUILD_VARIANT'])
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400709 [success, manifest_str] = server.GetApprovedManifest(branch, target)
710 else:
711 [success, manifest_str] = server.GetApprovedManifest(branch)
712 else:
713 assert(opt.smart_tag)
714 [success, manifest_str] = server.GetManifest(opt.smart_tag)
715
716 if success:
717 manifest_name = os.path.basename(smart_sync_manifest_path)
718 try:
Mike Frysinger3164d402019-11-11 05:40:22 -0500719 with open(smart_sync_manifest_path, 'w') as f:
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400720 f.write(manifest_str)
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400721 except IOError as e:
722 print('error: cannot write manifest to %s:\n%s'
723 % (smart_sync_manifest_path, e),
724 file=sys.stderr)
725 sys.exit(1)
726 self._ReloadManifest(manifest_name)
727 else:
728 print('error: manifest server RPC call failed: %s' %
729 manifest_str, file=sys.stderr)
730 sys.exit(1)
731 except (socket.error, IOError, xmlrpc.client.Fault) as e:
732 print('error: cannot connect to manifest server %s:\n%s'
733 % (self.manifest.manifest_server, e), file=sys.stderr)
734 sys.exit(1)
735 except xmlrpc.client.ProtocolError as e:
736 print('error: cannot connect to manifest server %s:\n%d %s'
737 % (self.manifest.manifest_server, e.errcode, e.errmsg),
738 file=sys.stderr)
739 sys.exit(1)
740
741 return manifest_name
742
Mike Frysingerfb527e32019-08-27 02:34:32 -0400743 def _UpdateManifestProject(self, opt, mp, manifest_name):
744 """Fetch & update the local manifest project."""
745 if not opt.local_only:
746 start = time.time()
Mike Frysinger521d01b2020-02-17 01:51:49 -0500747 success = mp.Sync_NetworkHalf(quiet=opt.quiet, verbose=opt.verbose,
Raman Tenneti2ae44d72021-03-23 15:12:27 -0700748 current_branch_only=self._GetCurrentBranchOnly(opt),
Erwan Yvindc5c4d12019-06-18 13:49:12 +0200749 force_sync=opt.force_sync,
Mike Frysingerc58ec4d2020-02-17 14:36:08 -0500750 tags=opt.tags,
Mike Frysingerfb527e32019-08-27 02:34:32 -0400751 optimized_fetch=opt.optimized_fetch,
George Engelbrecht9bc283e2020-04-02 12:36:09 -0600752 retry_fetches=opt.retry_fetches,
Mike Frysingerfb527e32019-08-27 02:34:32 -0400753 submodules=self.manifest.HasSubmodules,
754 clone_filter=self.manifest.CloneFilter)
755 finish = time.time()
756 self.event_log.AddSync(mp, event_log.TASK_SYNC_NETWORK,
757 start, finish, success)
758
759 if mp.HasChanges:
760 syncbuf = SyncBuffer(mp.config)
761 start = time.time()
762 mp.Sync_LocalHalf(syncbuf, submodules=self.manifest.HasSubmodules)
763 clean = syncbuf.Finish()
764 self.event_log.AddSync(mp, event_log.TASK_SYNC_LOCAL,
765 start, time.time(), clean)
766 if not clean:
767 sys.exit(1)
768 self._ReloadManifest(opt.manifest_name)
769 if opt.jobs is None:
770 self.jobs = self.manifest.default.sync_j
771
Mike Frysingerae6cb082019-08-27 01:10:59 -0400772 def ValidateOptions(self, opt, args):
773 if opt.force_broken:
774 print('warning: -f/--force-broken is now the default behavior, and the '
775 'options are deprecated', file=sys.stderr)
776 if opt.network_only and opt.detach_head:
777 self.OptionParser.error('cannot combine -n and -d')
778 if opt.network_only and opt.local_only:
779 self.OptionParser.error('cannot combine -n and -l')
780 if opt.manifest_name and opt.smart_sync:
781 self.OptionParser.error('cannot combine -m and -s')
782 if opt.manifest_name and opt.smart_tag:
783 self.OptionParser.error('cannot combine -m and -t')
784 if opt.manifest_server_username or opt.manifest_server_password:
785 if not (opt.smart_sync or opt.smart_tag):
786 self.OptionParser.error('-u and -p may only be combined with -s or -t')
787 if None in [opt.manifest_server_username, opt.manifest_server_password]:
788 self.OptionParser.error('both -u and -p must be given')
789
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700790 def Execute(self, opt, args):
Roy Lee18afd7f2010-05-09 04:32:08 +0800791 if opt.jobs:
792 self.jobs = opt.jobs
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -0700793 if self.jobs > 1:
794 soft_limit, _ = _rlimit_nofile()
Mike Frysinger0c0e9342019-06-13 12:42:39 -0400795 self.jobs = min(self.jobs, (soft_limit - 5) // 3)
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -0700796
Mike Frysinger521d01b2020-02-17 01:51:49 -0500797 opt.quiet = opt.output_mode is False
798 opt.verbose = opt.output_mode is True
799
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500800 if opt.manifest_name:
801 self.manifest.Override(opt.manifest_name)
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700802
Chirayu Desaia892b102013-06-11 14:18:46 +0530803 manifest_name = opt.manifest_name
David Pursehouse59b41742015-05-07 14:36:09 +0900804 smart_sync_manifest_path = os.path.join(
David Pursehouseabdf7502020-02-12 14:58:39 +0900805 self.manifest.manifestProject.worktree, 'smart_sync_override.xml')
Chirayu Desaia892b102013-06-11 14:18:46 +0530806
Xin Lid79a4bc2020-05-20 16:03:45 -0700807 if opt.clone_bundle is None:
808 opt.clone_bundle = self.manifest.CloneBundle
809
Victor Boivie08c880d2011-04-19 10:32:52 +0200810 if opt.smart_sync or opt.smart_tag:
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400811 manifest_name = self._SmartSyncSetup(opt, smart_sync_manifest_path)
812 else:
David Pursehouse59b41742015-05-07 14:36:09 +0900813 if os.path.isfile(smart_sync_manifest_path):
814 try:
Renaud Paquay010fed72016-11-11 14:25:29 -0800815 platform_utils.remove(smart_sync_manifest_path)
David Pursehouse59b41742015-05-07 14:36:09 +0900816 except OSError as e:
817 print('error: failed to remove existing smart sync override manifest: %s' %
818 e, file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700819
Mike Frysinger5a033082019-09-23 19:21:20 -0400820 err_event = _threading.Event()
821
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700822 rp = self.manifest.repoProject
823 rp.PreSync()
Mike Frysinger23c900f2020-02-29 02:52:44 -0500824 cb = rp.CurrentBranch
825 if cb:
826 base = rp.GetBranch(cb).merge
827 if not base or not base.startswith('refs/heads/'):
828 print('warning: repo is not tracking a remote branch, so it will not '
Mike Frysinger58ac1672020-03-14 14:35:26 -0400829 'receive updates; run `repo init --repo-rev=stable` to fix.',
Mike Frysinger23c900f2020-02-29 02:52:44 -0500830 file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700831
832 mp = self.manifest.manifestProject
833 mp.PreSync()
834
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800835 if opt.repo_upgraded:
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -0700836 _PostRepoUpgrade(self.manifest, quiet=opt.quiet)
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800837
Fredrik de Grootcc960972019-11-22 09:04:31 +0100838 if not opt.mp_update:
839 print('Skipping update of local manifest project.')
840 else:
841 self._UpdateManifestProject(opt, mp, manifest_name)
Simran Basib9a1b732015-08-20 12:19:28 -0700842
Raman Tenneti2ae44d72021-03-23 15:12:27 -0700843 if self._UseSuperproject(opt):
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800844 manifest_name = self._UpdateProjectsRevisionId(opt, args)
845
Simran Basib9a1b732015-08-20 12:19:28 -0700846 if self.gitc_manifest:
847 gitc_manifest_projects = self.GetProjects(args,
Simran Basib9a1b732015-08-20 12:19:28 -0700848 missing_ok=True)
849 gitc_projects = []
850 opened_projects = []
851 for project in gitc_manifest_projects:
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700852 if project.relpath in self.gitc_manifest.paths and \
853 self.gitc_manifest.paths[project.relpath].old_revision:
854 opened_projects.append(project.relpath)
Simran Basib9a1b732015-08-20 12:19:28 -0700855 else:
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700856 gitc_projects.append(project.relpath)
Simran Basib9a1b732015-08-20 12:19:28 -0700857
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700858 if not args:
859 gitc_projects = None
860
861 if gitc_projects != [] and not opt.local_only:
Simran Basib9a1b732015-08-20 12:19:28 -0700862 print('Updating GITC client: %s' % self.gitc_manifest.gitc_client_name)
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700863 manifest = GitcManifest(self.repodir, self.gitc_manifest.gitc_client_name)
864 if manifest_name:
865 manifest.Override(manifest_name)
866 else:
867 manifest.Override(self.manifest.manifestFile)
868 gitc_utils.generate_gitc_manifest(self.gitc_manifest,
869 manifest,
Simran Basib9a1b732015-08-20 12:19:28 -0700870 gitc_projects)
871 print('GITC client successfully synced.')
872
873 # The opened projects need to be synced as normal, therefore we
874 # generate a new args list to represent the opened projects.
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700875 # TODO: make this more reliable -- if there's a project name/path overlap,
876 # this may choose the wrong project.
David Pursehouse3bcd3052017-07-10 22:42:22 +0900877 args = [os.path.relpath(self.manifest.paths[path].worktree, os.getcwd())
878 for path in opened_projects]
Simran Basib9a1b732015-08-20 12:19:28 -0700879 if not args:
880 return
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800881 all_projects = self.GetProjects(args,
882 missing_ok=True,
883 submodules_ok=opt.fetch_submodules)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700884
Mike Frysinger5a033082019-09-23 19:21:20 -0400885 err_network_sync = False
886 err_update_projects = False
Mike Frysinger5a033082019-09-23 19:21:20 -0400887
Dave Borowitz67700e92012-10-23 15:00:54 -0700888 self._fetch_times = _FetchTimes(self.manifest)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700889 if not opt.local_only:
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700890 to_fetch = []
891 now = time.time()
Dave Borowitz67700e92012-10-23 15:00:54 -0700892 if _ONE_DAY_S <= (now - rp.LastFetch):
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700893 to_fetch.append(rp)
David Pursehouse8a68ff92012-09-24 12:15:13 +0900894 to_fetch.extend(all_projects)
Dave Borowitz67700e92012-10-23 15:00:54 -0700895 to_fetch.sort(key=self._fetch_times.Get, reverse=True)
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700896
Mike Frysinger5a033082019-09-23 19:21:20 -0400897 fetched = self._Fetch(to_fetch, opt, err_event)
898
Mike Frysingerc58ec4d2020-02-17 14:36:08 -0500899 _PostRepoFetch(rp, opt.repo_verify)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700900 if opt.network_only:
901 # bail out now; the rest touches the working tree
Mike Frysingerbe24a542021-02-23 03:24:12 -0500902 if err_event.is_set():
Mike Frysinger5a033082019-09-23 19:21:20 -0400903 print('\nerror: Exited sync due to fetch errors.\n', file=sys.stderr)
904 sys.exit(1)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700905 return
906
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800907 # Iteratively fetch missing and/or nested unregistered submodules
908 previously_missing_set = set()
909 while True:
Victor Boivie53a6c5d2013-03-19 12:20:52 +0100910 self._ReloadManifest(manifest_name)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800911 all_projects = self.GetProjects(args,
912 missing_ok=True,
913 submodules_ok=opt.fetch_submodules)
914 missing = []
915 for project in all_projects:
916 if project.gitdir not in fetched:
917 missing.append(project)
918 if not missing:
919 break
920 # Stop us from non-stopped fetching actually-missing repos: If set of
921 # missing repos has not been changed from last fetch, we break.
922 missing_set = set(p.name for p in missing)
923 if previously_missing_set == missing_set:
924 break
925 previously_missing_set = missing_set
Mike Frysinger5a033082019-09-23 19:21:20 -0400926 fetched.update(self._Fetch(missing, opt, err_event))
927
928 # If we saw an error, exit with code 1 so that other scripts can check.
Mike Frysingerbe24a542021-02-23 03:24:12 -0500929 if err_event.is_set():
Mike Frysinger5a033082019-09-23 19:21:20 -0400930 err_network_sync = True
931 if opt.fail_fast:
932 print('\nerror: Exited sync due to fetch errors.\n'
933 'Local checkouts *not* updated. Resolve network issues & '
934 'retry.\n'
935 '`repo sync -l` will update some local checkouts.',
936 file=sys.stderr)
937 sys.exit(1)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800938
Julien Campergue335f5ef2013-10-16 11:02:35 +0200939 if self.manifest.IsMirror or self.manifest.IsArchive:
Shawn O. Pearcecd1d7ff2009-06-04 16:15:53 -0700940 # bail out now, we have no working tree
941 return
942
Oleksii Okolielovd3c0f592018-12-17 19:23:44 -0500943 if self.UpdateProjectList(opt):
Mike Frysinger5a033082019-09-23 19:21:20 -0400944 err_event.set()
945 err_update_projects = True
946 if opt.fail_fast:
947 print('\nerror: Local checkouts *not* updated.', file=sys.stderr)
948 sys.exit(1)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700949
Mike Frysinger5a033082019-09-23 19:21:20 -0400950 err_results = []
Mike Frysingerebf04a42021-02-23 20:48:04 -0500951 # NB: We don't exit here because this is the last step.
952 err_checkout = not self._Checkout(all_projects, opt, err_results)
953 if err_checkout:
954 err_event.set()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700955
Doug Anderson2b8db3c2010-11-01 15:08:06 -0700956 # If there's a notice that's supposed to print at the end of the sync, print
957 # it now...
958 if self.manifest.notice:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700959 print(self.manifest.notice)
Doug Anderson2b8db3c2010-11-01 15:08:06 -0700960
Mike Frysinger5a033082019-09-23 19:21:20 -0400961 # If we saw an error, exit with code 1 so that other scripts can check.
Mike Frysingerbe24a542021-02-23 03:24:12 -0500962 if err_event.is_set():
Mike Frysinger5a033082019-09-23 19:21:20 -0400963 print('\nerror: Unable to fully sync the tree.', file=sys.stderr)
964 if err_network_sync:
965 print('error: Downloading network changes failed.', file=sys.stderr)
966 if err_update_projects:
967 print('error: Updating local project lists failed.', file=sys.stderr)
968 if err_checkout:
969 print('error: Checking out local projects failed.', file=sys.stderr)
970 if err_results:
971 print('Failing repos:\n%s' % '\n'.join(err_results), file=sys.stderr)
972 print('Try re-running with "-j1 --fail-fast" to exit at the first error.',
973 file=sys.stderr)
974 sys.exit(1)
975
Mike Frysingere19d9e12020-02-12 11:23:32 -0500976 if not opt.quiet:
977 print('repo sync has finished successfully.')
978
David Pursehouse819827a2020-02-12 15:20:19 +0900979
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -0700980def _PostRepoUpgrade(manifest, quiet=False):
Conley Owens094cdbe2014-01-30 15:09:59 -0800981 wrapper = Wrapper()
Conley Owensc9129d92012-10-01 16:12:28 -0700982 if wrapper.NeedSetupGnuPG():
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -0700983 wrapper.SetupGnuPG(quiet)
Conley Owensf2fe2d92014-01-29 13:53:43 -0800984 for project in manifest.projects:
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700985 if project.Exists:
986 project.PostRepoUpgrade()
987
David Pursehouse819827a2020-02-12 15:20:19 +0900988
Mike Frysingerc58ec4d2020-02-17 14:36:08 -0500989def _PostRepoFetch(rp, repo_verify=True, verbose=False):
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700990 if rp.HasChanges:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700991 print('info: A new version of repo is available', file=sys.stderr)
992 print(file=sys.stderr)
Mike Frysingerc58ec4d2020-02-17 14:36:08 -0500993 if not repo_verify or _VerifyTag(rp):
Shawn O. Pearce350cde42009-04-16 11:21:18 -0700994 syncbuf = SyncBuffer(rp.config)
995 rp.Sync_LocalHalf(syncbuf)
996 if not syncbuf.Finish():
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700997 sys.exit(1)
Sarah Owenscecd1d82012-11-01 22:59:27 -0700998 print('info: Restarting repo with latest version', file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700999 raise RepoChangedException(['--repo-upgraded'])
1000 else:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001001 print('warning: Skipped upgrade to unverified version', file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001002 else:
1003 if verbose:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001004 print('repo version %s is current' % rp.work_git.describe(HEAD),
1005 file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001006
David Pursehouse819827a2020-02-12 15:20:19 +09001007
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001008def _VerifyTag(project):
1009 gpg_dir = os.path.expanduser('~/.repoconfig/gnupg')
1010 if not os.path.exists(gpg_dir):
Sarah Owenscecd1d82012-11-01 22:59:27 -07001011 print('warning: GnuPG was not available during last "repo init"\n'
1012 'warning: Cannot automatically authenticate repo."""',
1013 file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001014 return True
1015
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001016 try:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001017 cur = project.bare_git.describe(project.GetRevisionId())
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001018 except GitError:
1019 cur = None
1020
1021 if not cur \
1022 or re.compile(r'^.*-[0-9]{1,}-g[0-9a-f]{1,}$').match(cur):
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001023 rev = project.revisionExpr
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001024 if rev.startswith(R_HEADS):
1025 rev = rev[len(R_HEADS):]
1026
Sarah Owenscecd1d82012-11-01 22:59:27 -07001027 print(file=sys.stderr)
1028 print("warning: project '%s' branch '%s' is not signed"
1029 % (project.name, rev), file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001030 return False
1031
Shawn O. Pearcef18cb762010-12-07 11:41:05 -08001032 env = os.environ.copy()
Mike Frysinger56ce3462019-12-04 19:30:48 -05001033 env['GIT_DIR'] = project.gitdir
1034 env['GNUPGHOME'] = gpg_dir
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001035
1036 cmd = [GIT, 'tag', '-v', cur]
Mike Frysingerfb21d6a2021-02-16 02:37:55 -05001037 result = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
1038 env=env, check=False)
1039 if result.returncode:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001040 print(file=sys.stderr)
Mike Frysingerfb21d6a2021-02-16 02:37:55 -05001041 print(result.stdout, file=sys.stderr)
Sarah Owenscecd1d82012-11-01 22:59:27 -07001042 print(file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001043 return False
1044 return True
Dave Borowitz67700e92012-10-23 15:00:54 -07001045
David Rileye0684ad2017-04-05 00:02:59 -07001046
Dave Borowitz67700e92012-10-23 15:00:54 -07001047class _FetchTimes(object):
Dave Borowitzd9478582012-10-23 16:35:39 -07001048 _ALPHA = 0.5
1049
Dave Borowitz67700e92012-10-23 15:00:54 -07001050 def __init__(self, manifest):
Anthony King85b24ac2014-05-06 15:57:48 +01001051 self._path = os.path.join(manifest.repodir, '.repo_fetchtimes.json')
Dave Borowitz67700e92012-10-23 15:00:54 -07001052 self._times = None
Dave Borowitzd9478582012-10-23 16:35:39 -07001053 self._seen = set()
Dave Borowitz67700e92012-10-23 15:00:54 -07001054
1055 def Get(self, project):
1056 self._Load()
1057 return self._times.get(project.name, _ONE_DAY_S)
1058
1059 def Set(self, project, t):
Dave Borowitzd9478582012-10-23 16:35:39 -07001060 self._Load()
1061 name = project.name
1062 old = self._times.get(name, t)
1063 self._seen.add(name)
1064 a = self._ALPHA
David Pursehouse54a4e602020-02-12 14:31:05 +09001065 self._times[name] = (a * t) + ((1 - a) * old)
Dave Borowitz67700e92012-10-23 15:00:54 -07001066
1067 def _Load(self):
1068 if self._times is None:
1069 try:
Mike Frysinger3164d402019-11-11 05:40:22 -05001070 with open(self._path) as f:
Anthony King85b24ac2014-05-06 15:57:48 +01001071 self._times = json.load(f)
Anthony King85b24ac2014-05-06 15:57:48 +01001072 except (IOError, ValueError):
1073 try:
Renaud Paquay010fed72016-11-11 14:25:29 -08001074 platform_utils.remove(self._path)
Anthony King85b24ac2014-05-06 15:57:48 +01001075 except OSError:
1076 pass
1077 self._times = {}
Dave Borowitz67700e92012-10-23 15:00:54 -07001078
1079 def Save(self):
1080 if self._times is None:
1081 return
Dave Borowitzd9478582012-10-23 16:35:39 -07001082
1083 to_delete = []
1084 for name in self._times:
1085 if name not in self._seen:
1086 to_delete.append(name)
1087 for name in to_delete:
1088 del self._times[name]
1089
Dave Borowitz67700e92012-10-23 15:00:54 -07001090 try:
Mike Frysinger3164d402019-11-11 05:40:22 -05001091 with open(self._path, 'w') as f:
Anthony King85b24ac2014-05-06 15:57:48 +01001092 json.dump(self._times, f, indent=2)
Anthony King85b24ac2014-05-06 15:57:48 +01001093 except (IOError, TypeError):
1094 try:
Renaud Paquay010fed72016-11-11 14:25:29 -08001095 platform_utils.remove(self._path)
Anthony King85b24ac2014-05-06 15:57:48 +01001096 except OSError:
1097 pass
Dan Willemsen0745bb22015-08-17 13:41:45 -07001098
1099# This is a replacement for xmlrpc.client.Transport using urllib2
1100# and supporting persistent-http[s]. It cannot change hosts from
1101# request to request like the normal transport, the real url
1102# is passed during initialization.
David Pursehouse819827a2020-02-12 15:20:19 +09001103
1104
Dan Willemsen0745bb22015-08-17 13:41:45 -07001105class PersistentTransport(xmlrpc.client.Transport):
1106 def __init__(self, orig_host):
1107 self.orig_host = orig_host
1108
1109 def request(self, host, handler, request_body, verbose=False):
1110 with GetUrlCookieFile(self.orig_host, not verbose) as (cookiefile, proxy):
1111 # Python doesn't understand cookies with the #HttpOnly_ prefix
1112 # Since we're only using them for HTTP, copy the file temporarily,
1113 # stripping those prefixes away.
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001114 if cookiefile:
Collin Fijalkoviche1191b32020-02-18 10:57:32 -08001115 tmpcookiefile = tempfile.NamedTemporaryFile(mode='w')
David Pursehouse4c5f74e2015-10-02 11:10:10 +09001116 tmpcookiefile.write("# HTTP Cookie File")
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001117 try:
1118 with open(cookiefile) as f:
1119 for line in f:
1120 if line.startswith("#HttpOnly_"):
1121 line = line[len("#HttpOnly_"):]
1122 tmpcookiefile.write(line)
1123 tmpcookiefile.flush()
Dan Willemsen0745bb22015-08-17 13:41:45 -07001124
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001125 cookiejar = cookielib.MozillaCookieJar(tmpcookiefile.name)
David Pursehouseb1ad2192015-09-30 10:35:43 +09001126 try:
1127 cookiejar.load()
1128 except cookielib.LoadError:
1129 cookiejar = cookielib.CookieJar()
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001130 finally:
1131 tmpcookiefile.close()
1132 else:
1133 cookiejar = cookielib.CookieJar()
Dan Willemsen0745bb22015-08-17 13:41:45 -07001134
1135 proxyhandler = urllib.request.ProxyHandler
1136 if proxy:
1137 proxyhandler = urllib.request.ProxyHandler({
1138 "http": proxy,
David Pursehouse54a4e602020-02-12 14:31:05 +09001139 "https": proxy})
Dan Willemsen0745bb22015-08-17 13:41:45 -07001140
1141 opener = urllib.request.build_opener(
1142 urllib.request.HTTPCookieProcessor(cookiejar),
1143 proxyhandler)
1144
1145 url = urllib.parse.urljoin(self.orig_host, handler)
1146 parse_results = urllib.parse.urlparse(url)
1147
1148 scheme = parse_results.scheme
1149 if scheme == 'persistent-http':
1150 scheme = 'http'
1151 if scheme == 'persistent-https':
1152 # If we're proxying through persistent-https, use http. The
1153 # proxy itself will do the https.
1154 if proxy:
1155 scheme = 'http'
1156 else:
1157 scheme = 'https'
1158
1159 # Parse out any authentication information using the base class
1160 host, extra_headers, _ = self.get_host_info(parse_results.netloc)
1161
1162 url = urllib.parse.urlunparse((
1163 scheme,
1164 host,
1165 parse_results.path,
1166 parse_results.params,
1167 parse_results.query,
1168 parse_results.fragment))
1169
1170 request = urllib.request.Request(url, request_body)
1171 if extra_headers is not None:
1172 for (name, header) in extra_headers:
1173 request.add_header(name, header)
1174 request.add_header('Content-Type', 'text/xml')
1175 try:
1176 response = opener.open(request)
1177 except urllib.error.HTTPError as e:
1178 if e.code == 501:
1179 # We may have been redirected through a login process
1180 # but our POST turned into a GET. Retry.
1181 response = opener.open(request)
1182 else:
1183 raise
1184
1185 p, u = xmlrpc.client.getparser()
1186 while 1:
1187 data = response.read(1024)
1188 if not data:
1189 break
1190 p.feed(data)
1191 p.close()
1192 return u.close()
1193
1194 def close(self):
1195 pass