blob: a2b376bc8d92b4e7564a9f62239706bf77af9d98 [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
LaMont Jonesbdcba7d2022-04-11 22:50:11 +000015import collections
Mike Frysingerebf04a42021-02-23 20:48:04 -050016import functools
Mike Frysingeracf63b22019-06-13 02:24:21 -040017import http.cookiejar as cookielib
Mike Frysinger7b586f22021-02-23 18:38:39 -050018import io
Anthony King85b24ac2014-05-06 15:57:48 +010019import json
Mike Frysingerebf04a42021-02-23 20:48:04 -050020import multiprocessing
David Pursehouse86d973d2012-08-24 10:21:02 +090021import netrc
Shawn O. Pearce2a1ccb22009-04-10 16:51:53 -070022from optparse import SUPPRESS_HELP
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070023import os
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -070024import socket
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070025import sys
Dan Willemsen0745bb22015-08-17 13:41:45 -070026import tempfile
Shawn O. Pearcef6906872009-04-18 10:49:00 -070027import time
Mike Frysingeracf63b22019-06-13 02:24:21 -040028import urllib.error
29import urllib.parse
30import urllib.request
Mike Frysinger5951e302022-05-20 23:34:44 -040031import xml.parsers.expat
Mike Frysingeracf63b22019-06-13 02:24:21 -040032import 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
David Rileye0684ad2017-04-05 00:02:59 -070048import event_log
Mike Frysinger347f9ed2021-03-15 14:58:52 -040049from git_command import git_require
David Pursehouseba7bc732015-08-20 16:55:42 +090050from git_config import GetUrlCookieFile
David Pursehoused94aaef2012-09-07 09:52:04 +090051from git_refs import R_HEADS, HEAD
Raman Tenneti6a872c92021-01-14 19:17:50 -080052import git_superproject
Simran Basibdb52712015-08-10 13:23:23 -070053import gitc_utils
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -070054from project import Project
55from project import RemoteSpec
Mike Frysinger355f4392022-07-20 17:15:29 -040056from command import Command, DEFAULT_LOCAL_JOBS, MirrorSafeCommand, WORKER_BATCH_SIZE
Raman Tenneti1fd7bc22021-02-04 14:39:38 -080057from error import RepoChangedException, GitError, ManifestParseError
Renaud Paquaya65adf72016-11-03 10:37:53 -070058import platform_utils
Shawn O. Pearce350cde42009-04-16 11:21:18 -070059from project import SyncBuffer
Shawn O. Pearce68194f42009-04-10 16:48:52 -070060from progress import Progress
Mike Frysinger19e409c2021-05-05 19:44:35 -040061import ssh
Conley Owens094cdbe2014-01-30 15:09:59 -080062from wrapper import Wrapper
Dan Willemsen5ea32d12015-09-08 13:27:20 -070063from manifest_xml import GitcManifest
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070064
Dave Borowitz67700e92012-10-23 15:00:54 -070065_ONE_DAY_S = 24 * 60 * 60
66
David Pursehouse819827a2020-02-12 15:20:19 +090067
Shawn O. Pearcec95583b2009-03-03 17:47:06 -080068class Sync(Command, MirrorSafeCommand):
Mike Frysinger4f210542021-06-14 16:05:19 -040069 COMMON = True
LaMont Jonesbdcba7d2022-04-11 22:50:11 +000070 MULTI_MANIFEST_SUPPORT = True
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070071 helpSummary = "Update working tree to the latest revision"
72 helpUsage = """
73%prog [<project>...]
74"""
75 helpDescription = """
76The '%prog' command synchronizes local project directories
77with the remote repositories specified in the manifest. If a local
78project does not yet exist, it will clone a new local directory from
79the remote repository and set up tracking branches as specified in
80the manifest. If the local project already exists, '%prog'
81will update the remote branches and rebase any new local changes
82on top of the new remote changes.
83
84'%prog' will synchronize all projects listed at the command
85line. Projects can be specified either by name, or by a relative
86or absolute path to the project's local directory. If no projects
87are specified, '%prog' will synchronize all projects listed in
88the manifest.
Shawn O. Pearce3e768c92009-04-10 16:59:36 -070089
90The -d/--detach option can be used to switch specified projects
91back to the manifest revision. This option is especially helpful
92if the project is currently on a topic branch, but the manifest
93revision is temporarily needed.
Shawn O. Pearceeb7af872009-04-21 08:02:04 -070094
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -070095The -s/--smart-sync option can be used to sync to a known good
96build as specified by the manifest-server element in the current
Victor Boivie08c880d2011-04-19 10:32:52 +020097manifest. The -t/--smart-tag option is similar and allows you to
98specify a custom tag/label.
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -070099
David Pursehousecf76b1b2012-09-14 10:31:42 +0900100The -u/--manifest-server-username and -p/--manifest-server-password
101options can be used to specify a username and password to authenticate
102with the manifest server when using the -s or -t option.
103
104If -u and -p are not specified when using the -s or -t option, '%prog'
105will attempt to read authentication credentials for the manifest server
106from the user's .netrc file.
107
108'%prog' will not use authentication credentials from -u/-p or .netrc
109if the manifest server specified in the manifest file already includes
110credentials.
111
Mike Frysingerd9e5cf02019-08-26 03:12:55 -0400112By default, all projects will be synced. The --fail-fast option can be used
Mike Frysinger7ae210a2020-05-24 14:56:52 -0400113to halt syncing as soon as possible when the first project fails to sync.
Andrei Warkentin5df6de02010-07-02 17:58:31 -0500114
Kevin Degiabaa7f32014-11-12 11:27:45 -0700115The --force-sync option can be used to overwrite existing git
116directories if they have previously been linked to a different
Roger Shimizuac29ac32020-06-06 02:33:40 +0900117object directory. WARNING: This may cause data to be lost since
Kevin Degiabaa7f32014-11-12 11:27:45 -0700118refs may be removed when overwriting.
119
Oleksii Okolielovd3c0f592018-12-17 19:23:44 -0500120The --force-remove-dirty option can be used to remove previously used
121projects with uncommitted changes. WARNING: This may cause data to be
122lost since uncommitted changes may be removed with projects that no longer
123exist in the manifest.
124
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -0700125The --no-clone-bundle option disables any attempt to use
126$URL/clone.bundle to bootstrap a new Git repository from a
127resumeable bundle file on a content delivery network. This
128may be necessary if there are problems with the local Python
129HTTP client or proxy configuration, but the Git binary works.
130
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800131The --fetch-submodules option enables fetching Git submodules
132of a project from server.
133
David Pursehousef2fad612015-01-29 14:36:28 +0900134The -c/--current-branch option can be used to only fetch objects that
135are on the branch specified by a project's revision.
136
David Pursehouseb1553542014-09-04 21:28:09 +0900137The --optimized-fetch option can be used to only fetch projects that
138are fixed to a sha1 revision if the sha1 revision does not already
139exist locally.
140
David Pursehouse74cfd272015-10-14 10:50:15 +0900141The --prune option can be used to remove any refs that no longer
142exist on the remote.
143
Mike Frysingerb8f7bb02018-10-10 01:05:11 -0400144# SSH Connections
Shawn O. Pearceeb7af872009-04-21 08:02:04 -0700145
146If at least one project remote URL uses an SSH connection (ssh://,
147git+ssh://, or user@host:path syntax) repo will automatically
148enable the SSH ControlMaster option when connecting to that host.
149This feature permits other projects in the same '%prog' session to
150reuse the same SSH tunnel, saving connection setup overheads.
151
152To disable this behavior on UNIX platforms, set the GIT_SSH
153environment variable to 'ssh'. For example:
154
155 export GIT_SSH=ssh
156 %prog
157
Mike Frysingerb8f7bb02018-10-10 01:05:11 -0400158# Compatibility
Shawn O. Pearceeb7af872009-04-21 08:02:04 -0700159
160This feature is automatically disabled on Windows, due to the lack
161of UNIX domain socket support.
162
163This feature is not compatible with url.insteadof rewrites in the
164user's ~/.gitconfig. '%prog' is currently not able to perform the
165rewrite early enough to establish the ControlMaster tunnel.
166
167If the remote SSH daemon is Gerrit Code Review, version 2.0.10 or
168later is required to fix a server side protocol bug.
169
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700170"""
Mike Frysinger355f4392022-07-20 17:15:29 -0400171 # A value of 0 means we want parallel jobs, but we'll determine the default
172 # value later on.
173 PARALLEL_JOBS = 0
Shawn O. Pearce6392c872011-09-22 17:44:31 -0700174
Mike Frysinger9180a072021-04-13 14:57:40 -0400175 def _Options(self, p, show_smart=True):
Mike Frysinger49de8ef2021-04-09 00:21:02 -0400176 p.add_option('--jobs-network', default=None, type=int, metavar='JOBS',
Mike Frysinger355f4392022-07-20 17:15:29 -0400177 help='number of network jobs to run in parallel (defaults to --jobs or 1)')
Mike Frysinger49de8ef2021-04-09 00:21:02 -0400178 p.add_option('--jobs-checkout', default=None, type=int, metavar='JOBS',
Mike Frysinger355f4392022-07-20 17:15:29 -0400179 help='number of local checkout jobs to run in parallel (defaults to --jobs or '
180 f'{DEFAULT_LOCAL_JOBS})')
Mike Frysinger49de8ef2021-04-09 00:21:02 -0400181
Andrei Warkentin5df6de02010-07-02 17:58:31 -0500182 p.add_option('-f', '--force-broken',
Stefan Müller-Klieser46702ed2019-08-30 10:20:15 +0200183 dest='force_broken', action='store_true',
Mike Frysingerd9e5cf02019-08-26 03:12:55 -0400184 help='obsolete option (to be deleted in the future)')
185 p.add_option('--fail-fast',
186 dest='fail_fast', action='store_true',
187 help='stop syncing after first error is hit')
Kevin Degiabaa7f32014-11-12 11:27:45 -0700188 p.add_option('--force-sync',
189 dest='force_sync', action='store_true',
190 help="overwrite an existing git directory if it needs to "
191 "point to a different object directory. WARNING: this "
192 "may cause loss of data")
Oleksii Okolielovd3c0f592018-12-17 19:23:44 -0500193 p.add_option('--force-remove-dirty',
194 dest='force_remove_dirty', action='store_true',
195 help="force remove projects with uncommitted modifications if "
196 "projects no longer exist in the manifest. "
197 "WARNING: this may cause loss of data")
David Pursehouse8f62fb72012-11-14 12:09:38 +0900198 p.add_option('-l', '--local-only',
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700199 dest='local_only', action='store_true',
200 help="only update working tree, don't fetch")
David Pursehouse54a4e602020-02-12 14:31:05 +0900201 p.add_option('--no-manifest-update', '--nmu',
Fredrik de Grootcc960972019-11-22 09:04:31 +0100202 dest='mp_update', action='store_false', default='true',
203 help='use the existing manifest checkout as-is. '
204 '(do not update to the latest revision)')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900205 p.add_option('-n', '--network-only',
Shawn O. Pearce96fdcef2009-04-10 16:29:20 -0700206 dest='network_only', action='store_true',
207 help="fetch only, don't update working tree")
David Pursehouse8f62fb72012-11-14 12:09:38 +0900208 p.add_option('-d', '--detach',
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700209 dest='detach_head', action='store_true',
210 help='detach projects back to manifest revision')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900211 p.add_option('-c', '--current-branch',
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -0700212 dest='current_branch_only', action='store_true',
213 help='fetch only current branch from server')
Mike Frysinger73561142021-05-03 01:10:09 -0400214 p.add_option('--no-current-branch',
215 dest='current_branch_only', action='store_false',
216 help='fetch all branches from server')
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500217 p.add_option('-m', '--manifest-name',
218 dest='manifest_name',
219 help='temporary manifest to use for this sync', metavar='NAME.xml')
Xin Lid79a4bc2020-05-20 16:03:45 -0700220 p.add_option('--clone-bundle', action='store_true',
221 help='enable use of /clone.bundle on HTTP/HTTPS')
222 p.add_option('--no-clone-bundle', dest='clone_bundle', action='store_false',
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -0700223 help='disable use of /clone.bundle on HTTP/HTTPS')
Conley Owens8d070cf2012-11-06 13:14:31 -0800224 p.add_option('-u', '--manifest-server-username', action='store',
225 dest='manifest_server_username',
226 help='username to authenticate with the manifest server')
227 p.add_option('-p', '--manifest-server-password', action='store',
228 dest='manifest_server_password',
229 help='password to authenticate with the manifest server')
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800230 p.add_option('--fetch-submodules',
231 dest='fetch_submodules', action='store_true',
232 help='fetch submodules from server')
Raman Tenneti6a872c92021-01-14 19:17:50 -0800233 p.add_option('--use-superproject', action='store_true',
Raman Tenneti62517292021-11-01 14:49:16 -0700234 help='use the manifest superproject to sync projects; implies -c')
Raman Tenneti23ea7542021-05-07 14:01:54 -0700235 p.add_option('--no-use-superproject', action='store_false',
236 dest='use_superproject',
237 help='disable use of manifest superprojects')
Mike Frysinger2273f462021-11-05 15:10:33 -0400238 p.add_option('--tags', action='store_true',
Mike Frysingerd68ed632021-05-03 01:21:35 -0400239 help='fetch tags')
Mitchel Humpherys597868b2012-10-29 10:18:34 -0700240 p.add_option('--no-tags',
Mike Frysingerd68ed632021-05-03 01:21:35 -0400241 dest='tags', action='store_false',
Mike Frysinger2273f462021-11-05 15:10:33 -0400242 help="don't fetch tags (default)")
David Pursehouseb1553542014-09-04 21:28:09 +0900243 p.add_option('--optimized-fetch',
244 dest='optimized_fetch', action='store_true',
245 help='only fetch projects fixed to sha1 if revision does not exist locally')
George Engelbrecht9bc283e2020-04-02 12:36:09 -0600246 p.add_option('--retry-fetches',
247 default=0, action='store', type='int',
248 help='number of times to retry fetches on transient errors')
Mike Frysinger0531a622021-11-05 15:22:01 -0400249 p.add_option('--prune', action='store_true',
250 help='delete refs that no longer exist on the remote (default)')
251 p.add_option('--no-prune', dest='prune', action='store_false',
252 help='do not delete refs that no longer exist on the remote')
Nico Sallembien6623b212010-05-11 12:57:01 -0700253 if show_smart:
254 p.add_option('-s', '--smart-sync',
255 dest='smart_sync', action='store_true',
David Pursehouse79fba682016-04-13 18:03:00 +0900256 help='smart sync using manifest from the latest known good build')
Victor Boivie08c880d2011-04-19 10:32:52 +0200257 p.add_option('-t', '--smart-tag',
258 dest='smart_tag', action='store',
259 help='smart sync using manifest from a known tag')
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700260
Shawn O. Pearcefd89b672009-04-18 11:28:57 -0700261 g = p.add_option_group('repo Version options')
262 g.add_option('--no-repo-verify',
Mike Frysingerc58ec4d2020-02-17 14:36:08 -0500263 dest='repo_verify', default=True, action='store_false',
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700264 help='do not verify repo source code')
Shawn O. Pearcefd89b672009-04-18 11:28:57 -0700265 g.add_option('--repo-upgraded',
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800266 dest='repo_upgraded', action='store_true',
Shawn O. Pearce2a1ccb22009-04-10 16:51:53 -0700267 help=SUPPRESS_HELP)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700268
LaMont Jonesa46047a2022-04-07 21:57:06 +0000269 def _GetBranch(self, manifest_project):
270 """Returns the branch name for getting the approved smartsync manifest.
271
272 Args:
273 manifest_project: the manifestProject to query.
274 """
275 b = manifest_project.GetBranch(manifest_project.CurrentBranch)
Raman Tenneti8d43dea2021-02-07 16:30:27 -0800276 branch = b.merge
277 if branch.startswith(R_HEADS):
278 branch = branch[len(R_HEADS):]
279 return branch
280
LaMont Jonesa46047a2022-04-07 21:57:06 +0000281 def _GetCurrentBranchOnly(self, opt, manifest):
Daniel Anderssond52ca422022-04-01 12:55:38 +0200282 """Returns whether current-branch or use-superproject options are enabled.
283
LaMont Jonesa46047a2022-04-07 21:57:06 +0000284 Args:
285 opt: Program options returned from optparse. See _Options().
286 manifest: The manifest to use.
287
Daniel Anderssond52ca422022-04-01 12:55:38 +0200288 Returns:
289 True if a superproject is requested, otherwise the value of the
290 current_branch option (True, False or None).
291 """
LaMont Jonesa46047a2022-04-07 21:57:06 +0000292 return git_superproject.UseSuperproject(opt.use_superproject, manifest) or opt.current_branch_only
Raman Tenneti2ae44d72021-03-23 15:12:27 -0700293
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000294 def _UpdateProjectsRevisionId(self, opt, args, superproject_logging_data,
295 manifest):
296 """Update revisionId of projects with the commit hash from the superproject.
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800297
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000298 This function updates each project's revisionId with the commit hash from
299 the superproject. It writes the updated manifest into a file and reloads
300 the manifest from it. When appropriate, sub manifests are also processed.
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800301
302 Args:
303 opt: Program options returned from optparse. See _Options().
304 args: Arguments to pass to GetProjects. See the GetProjects
305 docstring for details.
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000306 superproject_logging_data: A dictionary of superproject data to log.
LaMont Jonesa46047a2022-04-07 21:57:06 +0000307 manifest: The manifest to use.
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800308 """
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000309 have_superproject = manifest.superproject or any(
310 m.superproject for m in manifest.all_children)
311 if not have_superproject:
312 return
313
LaMont Jonesff6b1da2022-06-01 21:03:34 +0000314 if opt.local_only and manifest.superproject:
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000315 manifest_path = manifest.superproject.manifest_path
Raman Tennetiae86a462021-07-27 08:54:59 -0700316 if manifest_path:
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000317 self._ReloadManifest(manifest_path, manifest)
318 return
Raman Tennetiae86a462021-07-27 08:54:59 -0700319
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800320 all_projects = self.GetProjects(args,
321 missing_ok=True,
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000322 submodules_ok=opt.fetch_submodules,
323 manifest=manifest,
324 all_manifests=not opt.this_manifest_only)
325
326 per_manifest = collections.defaultdict(list)
327 manifest_paths = {}
328 if opt.this_manifest_only:
329 per_manifest[manifest.path_prefix] = all_projects
Raman Tenneti784e16f2021-06-11 17:29:45 -0700330 else:
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000331 for p in all_projects:
332 per_manifest[p.manifest.path_prefix].append(p)
333
334 superproject_logging_data = {}
335 need_unload = False
336 for m in self.ManifestList(opt):
337 if not m.path_prefix in per_manifest:
338 continue
339 use_super = git_superproject.UseSuperproject(opt.use_superproject, m)
340 if superproject_logging_data:
341 superproject_logging_data['multimanifest'] = True
342 superproject_logging_data.update(
343 superproject=use_super,
344 haslocalmanifests=bool(m.HasLocalManifests),
345 hassuperprojecttag=bool(m.superproject),
346 )
347 if use_super and (m.IsMirror or m.IsArchive):
348 # Don't use superproject, because we have no working tree.
349 use_super = False
350 superproject_logging_data['superproject'] = False
351 superproject_logging_data['noworktree'] = True
352 if opt.use_superproject is not False:
353 print(f'{m.path_prefix}: not using superproject because there is no '
354 'working tree.')
355
356 if not use_super:
357 continue
358 m.superproject.SetQuiet(opt.quiet)
359 print_messages = git_superproject.PrintMessages(opt.use_superproject, m)
360 m.superproject.SetPrintMessages(print_messages)
361 update_result = m.superproject.UpdateProjectsRevisionId(
362 per_manifest[m.path_prefix], git_event_log=self.git_event_log)
363 manifest_path = update_result.manifest_path
364 superproject_logging_data['updatedrevisionid'] = bool(manifest_path)
365 if manifest_path:
366 m.SetManifestOverride(manifest_path)
367 need_unload = True
368 else:
369 if print_messages:
370 print(f'{m.path_prefix}: warning: Update of revisionId from '
371 'superproject has failed, repo sync will not use superproject '
372 'to fetch the source. ',
373 'Please resync with the --no-use-superproject option to avoid '
374 'this repo warning.',
375 file=sys.stderr)
376 if update_result.fatal and opt.use_superproject is not None:
377 sys.exit(1)
378 if need_unload:
379 m.outer_client.manifest.Unload()
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800380
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500381 def _FetchProjectList(self, opt, projects):
382 """Main function of the fetch worker.
383
384 The projects we're given share the same underlying git object store, so we
385 have to fetch them in serial.
Roy Lee18afd7f2010-05-09 04:32:08 +0800386
David James8d201162013-10-11 17:03:19 -0700387 Delegates most of the work to _FetchHelper.
388
389 Args:
390 opt: Program options returned from optparse. See _Options().
391 projects: Projects to fetch.
David James8d201162013-10-11 17:03:19 -0700392 """
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500393 return [self._FetchOne(opt, x) for x in projects]
David James8d201162013-10-11 17:03:19 -0700394
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500395 def _FetchOne(self, opt, project):
David James8d201162013-10-11 17:03:19 -0700396 """Fetch git objects for a single project.
397
David Pursehousec1b86a22012-11-14 11:36:51 +0900398 Args:
399 opt: Program options returned from optparse. See _Options().
400 project: Project object for the project to fetch.
David James8d201162013-10-11 17:03:19 -0700401
402 Returns:
403 Whether the fetch was successful.
David Pursehousec1b86a22012-11-14 11:36:51 +0900404 """
David Rileye0684ad2017-04-05 00:02:59 -0700405 start = time.time()
406 success = False
Mike Frysinger7b586f22021-02-23 18:38:39 -0500407 buf = io.StringIO()
David Pursehousec1b86a22012-11-14 11:36:51 +0900408 try:
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500409 success = project.Sync_NetworkHalf(
410 quiet=opt.quiet,
411 verbose=opt.verbose,
412 output_redir=buf,
LaMont Jonesa46047a2022-04-07 21:57:06 +0000413 current_branch_only=self._GetCurrentBranchOnly(opt, project.manifest),
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500414 force_sync=opt.force_sync,
415 clone_bundle=opt.clone_bundle,
LaMont Jonesa46047a2022-04-07 21:57:06 +0000416 tags=opt.tags, archive=project.manifest.IsArchive,
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500417 optimized_fetch=opt.optimized_fetch,
418 retry_fetches=opt.retry_fetches,
419 prune=opt.prune,
Mike Frysinger339f2df2021-05-06 00:44:42 -0400420 ssh_proxy=self.ssh_proxy,
LaMont Jonesa46047a2022-04-07 21:57:06 +0000421 clone_filter=project.manifest.CloneFilter,
422 partial_clone_exclude=project.manifest.PartialCloneExclude)
Doug Andersonfc06ced2011-03-16 15:49:18 -0700423
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500424 output = buf.getvalue()
Mike Frysinger58929732021-07-02 00:29:35 -0400425 if (opt.verbose or not success) and output:
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500426 print('\n' + output.rstrip())
Doug Andersonfc06ced2011-03-16 15:49:18 -0700427
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500428 if not success:
429 print('error: Cannot fetch %s from %s'
430 % (project.name, project.remote.url),
431 file=sys.stderr)
Raman Tennetiad8aa692021-04-15 09:20:51 -0700432 except GitError as e:
433 print('error.GitError: Cannot fetch %s' % str(e), file=sys.stderr)
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500434 except Exception as e:
435 print('error: Cannot fetch %s (%s: %s)'
436 % (project.name, type(e).__name__, str(e)), file=sys.stderr)
437 raise
Mike Frysinger7b586f22021-02-23 18:38:39 -0500438
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500439 finish = time.time()
440 return (success, project, start, finish)
David James8d201162013-10-11 17:03:19 -0700441
Mike Frysinger339f2df2021-05-06 00:44:42 -0400442 @classmethod
443 def _FetchInitChild(cls, ssh_proxy):
444 cls.ssh_proxy = ssh_proxy
445
446 def _Fetch(self, projects, opt, err_event, ssh_proxy):
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500447 ret = True
448
Mike Frysinger355f4392022-07-20 17:15:29 -0400449 jobs = opt.jobs_network
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700450 fetched = set()
Mike Frysinger151701e2021-04-13 15:07:21 -0400451 pm = Progress('Fetching', len(projects), delay=False, quiet=opt.quiet)
Roy Lee18afd7f2010-05-09 04:32:08 +0800452
David James89ece422014-01-09 18:51:58 -0800453 objdir_project_map = dict()
454 for project in projects:
455 objdir_project_map.setdefault(project.objdir, []).append(project)
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500456 projects_list = list(objdir_project_map.values())
David James8d201162013-10-11 17:03:19 -0700457
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500458 def _ProcessResults(results_sets):
459 ret = True
460 for results in results_sets:
461 for (success, project, start, finish) in results:
462 self._fetch_times.Set(project, finish - start)
463 self.event_log.AddSync(project, event_log.TASK_SYNC_NETWORK,
464 start, finish, success)
465 # Check for any errors before running any more tasks.
466 # ...we'll let existing jobs finish, though.
467 if not success:
468 ret = False
469 else:
470 fetched.add(project.gitdir)
471 pm.update(msg=project.name)
472 if not ret and opt.fail_fast:
473 break
474 return ret
Doug Andersonfc06ced2011-03-16 15:49:18 -0700475
Mike Frysinger339f2df2021-05-06 00:44:42 -0400476 # We pass the ssh proxy settings via the class. This allows multiprocessing
477 # to pickle it up when spawning children. We can't pass it as an argument
478 # to _FetchProjectList below as multiprocessing is unable to pickle those.
479 Sync.ssh_proxy = None
480
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500481 # NB: Multiprocessing is heavy, so don't spin it up for one job.
Mike Frysinger49de8ef2021-04-09 00:21:02 -0400482 if len(projects_list) == 1 or jobs == 1:
Mike Frysinger339f2df2021-05-06 00:44:42 -0400483 self._FetchInitChild(ssh_proxy)
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500484 if not _ProcessResults(self._FetchProjectList(opt, x) for x in projects_list):
485 ret = False
486 else:
487 # Favor throughput over responsiveness when quiet. It seems that imap()
488 # will yield results in batches relative to chunksize, so even as the
489 # children finish a sync, we won't see the result until one child finishes
490 # ~chunksize jobs. When using a large --jobs with large chunksize, this
491 # can be jarring as there will be a large initial delay where repo looks
492 # like it isn't doing anything and sits at 0%, but then suddenly completes
493 # a lot of jobs all at once. Since this code is more network bound, we
494 # can accept a bit more CPU overhead with a smaller chunksize so that the
495 # user sees more immediate & continuous feedback.
496 if opt.quiet:
497 chunksize = WORKER_BATCH_SIZE
David James89ece422014-01-09 18:51:58 -0800498 else:
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500499 pm.update(inc=0, msg='warming up')
500 chunksize = 4
Raman Tenneti4a478ed2021-11-17 18:38:24 -0800501 with multiprocessing.Pool(jobs, initializer=self._FetchInitChild,
502 initargs=(ssh_proxy,)) as pool:
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500503 results = pool.imap_unordered(
504 functools.partial(self._FetchProjectList, opt),
505 projects_list,
506 chunksize=chunksize)
507 if not _ProcessResults(results):
508 ret = False
509 pool.close()
Roy Lee18afd7f2010-05-09 04:32:08 +0800510
Mike Frysinger339f2df2021-05-06 00:44:42 -0400511 # Cleanup the reference now that we're done with it, and we're going to
512 # release any resources it points to. If we don't, later multiprocessing
513 # usage (e.g. checkouts) will try to pickle and then crash.
514 del Sync.ssh_proxy
515
Shawn O. Pearce68194f42009-04-10 16:48:52 -0700516 pm.end()
Dave Borowitz67700e92012-10-23 15:00:54 -0700517 self._fetch_times.Save()
Dave Borowitz18857212012-10-23 17:02:59 -0700518
LaMont Jonesa46047a2022-04-07 21:57:06 +0000519 if not self.outer_client.manifest.IsArchive:
Mike Frysinger5a033082019-09-23 19:21:20 -0400520 self._GCProjects(projects, opt, err_event)
Julien Campergue335f5ef2013-10-16 11:02:35 +0200521
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500522 return (ret, fetched)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700523
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000524 def _FetchMain(self, opt, args, all_projects, err_event,
525 ssh_proxy, manifest):
Mike Frysingerb4429432021-05-05 20:03:26 -0400526 """The main network fetch loop.
527
528 Args:
529 opt: Program options returned from optparse. See _Options().
530 args: Command line args used to filter out projects.
Peter Kjellerstedtd1776092021-05-19 19:37:23 +0200531 all_projects: List of all projects that should be fetched.
Mike Frysingerb4429432021-05-05 20:03:26 -0400532 err_event: Whether an error was hit while processing.
Mike Frysinger339f2df2021-05-06 00:44:42 -0400533 ssh_proxy: SSH manager for clients & masters.
LaMont Jonesa46047a2022-04-07 21:57:06 +0000534 manifest: The manifest to use.
Peter Kjellerstedtd1776092021-05-19 19:37:23 +0200535
536 Returns:
537 List of all projects that should be checked out.
Mike Frysingerb4429432021-05-05 20:03:26 -0400538 """
LaMont Jonesa46047a2022-04-07 21:57:06 +0000539 rp = manifest.repoProject
Mike Frysingerb4429432021-05-05 20:03:26 -0400540
541 to_fetch = []
542 now = time.time()
543 if _ONE_DAY_S <= (now - rp.LastFetch):
544 to_fetch.append(rp)
545 to_fetch.extend(all_projects)
546 to_fetch.sort(key=self._fetch_times.Get, reverse=True)
547
Mike Frysinger339f2df2021-05-06 00:44:42 -0400548 success, fetched = self._Fetch(to_fetch, opt, err_event, ssh_proxy)
Mike Frysingerb4429432021-05-05 20:03:26 -0400549 if not success:
550 err_event.set()
551
552 _PostRepoFetch(rp, opt.repo_verify)
553 if opt.network_only:
554 # bail out now; the rest touches the working tree
555 if err_event.is_set():
556 print('\nerror: Exited sync due to fetch errors.\n', file=sys.stderr)
557 sys.exit(1)
558 return
559
560 # Iteratively fetch missing and/or nested unregistered submodules
561 previously_missing_set = set()
562 while True:
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000563 self._ReloadManifest(None, manifest)
Mike Frysingerb4429432021-05-05 20:03:26 -0400564 all_projects = self.GetProjects(args,
565 missing_ok=True,
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000566 submodules_ok=opt.fetch_submodules,
567 manifest=manifest,
568 all_manifests=not opt.this_manifest_only)
Mike Frysingerb4429432021-05-05 20:03:26 -0400569 missing = []
570 for project in all_projects:
571 if project.gitdir not in fetched:
572 missing.append(project)
573 if not missing:
574 break
575 # Stop us from non-stopped fetching actually-missing repos: If set of
576 # missing repos has not been changed from last fetch, we break.
577 missing_set = set(p.name for p in missing)
578 if previously_missing_set == missing_set:
579 break
580 previously_missing_set = missing_set
Mike Frysinger339f2df2021-05-06 00:44:42 -0400581 success, new_fetched = self._Fetch(missing, opt, err_event, ssh_proxy)
Mike Frysingerb4429432021-05-05 20:03:26 -0400582 if not success:
583 err_event.set()
584 fetched.update(new_fetched)
585
Peter Kjellerstedtd1776092021-05-19 19:37:23 +0200586 return all_projects
587
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500588 def _CheckoutOne(self, detach_head, force_sync, project):
Xin Li745be2e2019-06-03 11:24:30 -0700589 """Checkout work tree for one project
590
591 Args:
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500592 detach_head: Whether to leave a detached HEAD.
593 force_sync: Force checking out of the repo.
Xin Li745be2e2019-06-03 11:24:30 -0700594 project: Project object for the project to checkout.
Xin Li745be2e2019-06-03 11:24:30 -0700595
596 Returns:
597 Whether the fetch was successful.
598 """
Xin Li745be2e2019-06-03 11:24:30 -0700599 start = time.time()
LaMont Jonesa46047a2022-04-07 21:57:06 +0000600 syncbuf = SyncBuffer(project.manifest.manifestProject.config,
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500601 detach_head=detach_head)
Xin Li745be2e2019-06-03 11:24:30 -0700602 success = False
603 try:
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500604 project.Sync_LocalHalf(syncbuf, force_sync=force_sync)
Mike Frysingerebf04a42021-02-23 20:48:04 -0500605 success = syncbuf.Finish()
Raman Tennetiad8aa692021-04-15 09:20:51 -0700606 except GitError as e:
607 print('error.GitError: Cannot checkout %s: %s' %
608 (project.name, str(e)), file=sys.stderr)
Mike Frysingerebf04a42021-02-23 20:48:04 -0500609 except Exception as e:
610 print('error: Cannot checkout %s: %s: %s' %
611 (project.name, type(e).__name__, str(e)),
612 file=sys.stderr)
613 raise
Xin Li745be2e2019-06-03 11:24:30 -0700614
Mike Frysingerebf04a42021-02-23 20:48:04 -0500615 if not success:
616 print('error: Cannot checkout %s' % (project.name), file=sys.stderr)
617 finish = time.time()
618 return (success, project, start, finish)
Xin Li745be2e2019-06-03 11:24:30 -0700619
Mike Frysingerebf04a42021-02-23 20:48:04 -0500620 def _Checkout(self, all_projects, opt, err_results):
Xin Li745be2e2019-06-03 11:24:30 -0700621 """Checkout projects listed in all_projects
622
623 Args:
624 all_projects: List of all projects that should be checked out.
625 opt: Program options returned from optparse. See _Options().
Mike Frysingerebf04a42021-02-23 20:48:04 -0500626 err_results: A list of strings, paths to git repos where checkout failed.
Xin Li745be2e2019-06-03 11:24:30 -0700627 """
Mike Frysingerebf04a42021-02-23 20:48:04 -0500628 # Only checkout projects with worktrees.
629 all_projects = [x for x in all_projects if x.worktree]
Xin Li745be2e2019-06-03 11:24:30 -0700630
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500631 def _ProcessResults(pool, pm, results):
632 ret = True
Mike Frysingerebf04a42021-02-23 20:48:04 -0500633 for (success, project, start, finish) in results:
634 self.event_log.AddSync(project, event_log.TASK_SYNC_LOCAL,
635 start, finish, success)
636 # Check for any errors before running any more tasks.
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500637 # ...we'll let existing jobs finish, though.
Mike Frysingerebf04a42021-02-23 20:48:04 -0500638 if not success:
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500639 ret = False
Mike Frysingerebf04a42021-02-23 20:48:04 -0500640 err_results.append(project.relpath)
641 if opt.fail_fast:
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500642 if pool:
643 pool.close()
644 return ret
Mike Frysingerebf04a42021-02-23 20:48:04 -0500645 pm.update(msg=project.name)
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500646 return ret
Xin Li745be2e2019-06-03 11:24:30 -0700647
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500648 return self.ExecuteInParallel(
Mike Frysinger355f4392022-07-20 17:15:29 -0400649 opt.jobs_checkout,
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500650 functools.partial(self._CheckoutOne, opt.detach_head, opt.force_sync),
651 all_projects,
652 callback=_ProcessResults,
653 output=Progress('Checking out', len(all_projects), quiet=opt.quiet)) and not err_results
Mike Frysingerebf04a42021-02-23 20:48:04 -0500654
Mike Frysinger5a033082019-09-23 19:21:20 -0400655 def _GCProjects(self, projects, opt, err_event):
Mike Frysinger151701e2021-04-13 15:07:21 -0400656 pm = Progress('Garbage collecting', len(projects), delay=False, quiet=opt.quiet)
Mike Frysinger65af2602021-04-08 22:47:44 -0400657 pm.update(inc=0, msg='prescan')
658
Allen Webb4ee4a452021-10-07 10:42:38 -0500659 tidy_dirs = {}
David James8d201162013-10-11 17:03:19 -0700660 for project in projects:
LaMont Jones48ea25c2022-05-20 10:35:04 +0000661 # Make sure pruning never kicks in with shared projects that do not use
662 # alternates to avoid corruption.
Mike Frysinger979d5bd2020-02-09 02:28:34 -0500663 if (not project.use_git_worktrees and
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000664 len(project.manifest.GetProjectsWithName(project.name, all_manifests=True)) > 1):
LaMont Jones48ea25c2022-05-20 10:35:04 +0000665 if project.UseAlternates:
666 # Undo logic set by previous versions of repo.
667 project.config.SetString('extensions.preciousObjects', None)
668 project.config.SetString('gc.pruneExpire', None)
Mike Frysinger5f2b0452020-02-11 03:23:24 -0500669 else:
LaMont Jones48ea25c2022-05-20 10:35:04 +0000670 if not opt.quiet:
671 print('\r%s: Shared project %s found, disabling pruning.' %
672 (project.relpath, project.name))
673 if git_require((2, 7, 0)):
674 project.EnableRepositoryExtension('preciousObjects')
675 else:
676 # This isn't perfect, but it's the best we can do with old git.
677 print('\r%s: WARNING: shared projects are unreliable when using old '
678 'versions of git; please upgrade to git-2.7.0+.'
679 % (project.relpath,),
680 file=sys.stderr)
681 project.config.SetString('gc.pruneExpire', 'never')
Allen Webb669efd02021-10-01 15:25:31 -0500682 project.config.SetString('gc.autoDetach', 'false')
Allen Webb4ee4a452021-10-07 10:42:38 -0500683 # Only call git gc once per objdir, but call pack-refs for the remainder.
684 if project.objdir not in tidy_dirs:
685 tidy_dirs[project.objdir] = (
686 True, # Run a full gc.
687 project.bare_git,
688 )
689 elif project.gitdir not in tidy_dirs:
690 tidy_dirs[project.gitdir] = (
691 False, # Do not run a full gc; just run pack-refs.
692 project.bare_git,
693 )
Mike Frysinger65af2602021-04-08 22:47:44 -0400694
Mike Frysinger355f4392022-07-20 17:15:29 -0400695 jobs = opt.jobs
Dave Borowitz18857212012-10-23 17:02:59 -0700696
697 if jobs < 2:
Allen Webb4ee4a452021-10-07 10:42:38 -0500698 for (run_gc, bare_git) in tidy_dirs.values():
Mike Frysinger65af2602021-04-08 22:47:44 -0400699 pm.update(msg=bare_git._project.name)
Allen Webb4ee4a452021-10-07 10:42:38 -0500700 if run_gc:
701 bare_git.gc('--auto')
702 else:
703 bare_git.pack_refs()
Mike Frysinger65af2602021-04-08 22:47:44 -0400704 pm.end()
Dave Borowitz18857212012-10-23 17:02:59 -0700705 return
706
Mike Frysinger355f4392022-07-20 17:15:29 -0400707 cpu_count = os.cpu_count()
Mike Frysinger0c0e9342019-06-13 12:42:39 -0400708 config = {'pack.threads': cpu_count // jobs if cpu_count > jobs else 1}
Dave Borowitz18857212012-10-23 17:02:59 -0700709
710 threads = set()
711 sem = _threading.Semaphore(jobs)
Dave Borowitz18857212012-10-23 17:02:59 -0700712
Allen Webb4ee4a452021-10-07 10:42:38 -0500713 def tidy_up(run_gc, bare_git):
Mike Frysinger65af2602021-04-08 22:47:44 -0400714 pm.start(bare_git._project.name)
Dave Borowitz18857212012-10-23 17:02:59 -0700715 try:
716 try:
Allen Webb4ee4a452021-10-07 10:42:38 -0500717 if run_gc:
718 bare_git.gc('--auto', config=config)
719 else:
720 bare_git.pack_refs(config=config)
Dave Borowitz18857212012-10-23 17:02:59 -0700721 except GitError:
722 err_event.set()
David Pursehouse145e35b2020-02-12 15:40:47 +0900723 except Exception:
Dave Borowitz18857212012-10-23 17:02:59 -0700724 err_event.set()
725 raise
726 finally:
Mike Frysinger65af2602021-04-08 22:47:44 -0400727 pm.finish(bare_git._project.name)
Dave Borowitz18857212012-10-23 17:02:59 -0700728 sem.release()
729
Allen Webb4ee4a452021-10-07 10:42:38 -0500730 for (run_gc, bare_git) in tidy_dirs.values():
Mike Frysingerbe24a542021-02-23 03:24:12 -0500731 if err_event.is_set() and opt.fail_fast:
Dave Borowitz18857212012-10-23 17:02:59 -0700732 break
733 sem.acquire()
Allen Webb4ee4a452021-10-07 10:42:38 -0500734 t = _threading.Thread(target=tidy_up, args=(run_gc, bare_git,))
Dave Borowitz18857212012-10-23 17:02:59 -0700735 t.daemon = True
736 threads.add(t)
737 t.start()
738
739 for t in threads:
740 t.join()
Mike Frysinger65af2602021-04-08 22:47:44 -0400741 pm.end()
Dave Borowitz18857212012-10-23 17:02:59 -0700742
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000743 def _ReloadManifest(self, manifest_name, manifest):
Raman Tennetifeb28912021-05-02 19:47:29 -0700744 """Reload the manfiest from the file specified by the |manifest_name|.
745
746 It unloads the manifest if |manifest_name| is None.
747
748 Args:
749 manifest_name: Manifest file to be reloaded.
LaMont Jonesa46047a2022-04-07 21:57:06 +0000750 manifest: The manifest to use.
Raman Tennetifeb28912021-05-02 19:47:29 -0700751 """
Tim Kilbourn07669002013-03-08 15:02:49 -0800752 if manifest_name:
LaMont Jonesa2ff20d2022-04-07 16:49:06 +0000753 # Override calls Unload already
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000754 manifest.Override(manifest_name)
Tim Kilbourn07669002013-03-08 15:02:49 -0800755 else:
LaMont Jonesa46047a2022-04-07 21:57:06 +0000756 manifest.Unload()
Tim Kilbourn07669002013-03-08 15:02:49 -0800757
LaMont Jonesa46047a2022-04-07 21:57:06 +0000758 def UpdateProjectList(self, opt, manifest):
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000759 """Update the cached projects list for |manifest|
760
761 In a multi-manifest checkout, each manifest has its own project.list.
762
763 Args:
764 opt: Program options returned from optparse. See _Options().
765 manifest: The manifest to use.
766
767 Returns:
768 0: success
769 1: failure
770 """
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700771 new_project_paths = []
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000772 for project in self.GetProjects(None, missing_ok=True, manifest=manifest,
773 all_manifests=False):
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700774 if project.relpath:
775 new_project_paths.append(project.relpath)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700776 file_name = 'project.list'
LaMont Jonesa46047a2022-04-07 21:57:06 +0000777 file_path = os.path.join(manifest.subdir, file_name)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700778 old_project_paths = []
779
780 if os.path.exists(file_path):
Mike Frysinger3164d402019-11-11 05:40:22 -0500781 with open(file_path, 'r') as fd:
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700782 old_project_paths = fd.read().split('\n')
Kuang-che Wu0d9b16d2019-04-06 00:49:47 +0800783 # In reversed order, so subfolders are deleted before parent folder.
784 for path in sorted(old_project_paths, reverse=True):
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700785 if not path:
786 continue
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700787 if path not in new_project_paths:
David Pursehouse8a68ff92012-09-24 12:15:13 +0900788 # If the path has already been deleted, we don't need to do it
LaMont Jonesa46047a2022-04-07 21:57:06 +0000789 gitdir = os.path.join(manifest.topdir, path, '.git')
Dan Willemsen43507912016-09-01 16:26:02 -0700790 if os.path.exists(gitdir):
David Pursehousec1b86a22012-11-14 11:36:51 +0900791 project = Project(
LaMont Jonesa46047a2022-04-07 21:57:06 +0000792 manifest=manifest,
David Pursehouseabdf7502020-02-12 14:58:39 +0900793 name=path,
794 remote=RemoteSpec('origin'),
795 gitdir=gitdir,
796 objdir=gitdir,
Mike Frysingerc0d18662020-02-19 19:19:18 -0500797 use_git_worktrees=os.path.isfile(gitdir),
LaMont Jonesa46047a2022-04-07 21:57:06 +0000798 worktree=os.path.join(manifest.topdir, path),
David Pursehouseabdf7502020-02-12 14:58:39 +0900799 relpath=path,
800 revisionExpr='HEAD',
801 revisionId=None,
802 groups=None)
Mike Frysingerc0d18662020-02-19 19:19:18 -0500803 if not project.DeleteWorktree(
David Pursehouseaa611a22020-02-20 10:47:26 +0900804 quiet=opt.quiet,
805 force=opt.force_remove_dirty):
Mike Frysingera850ca22019-08-07 17:19:24 -0400806 return 1
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700807
Shawn O. Pearce9fb29ce2009-06-04 20:41:02 -0700808 new_project_paths.sort()
Mike Frysinger3164d402019-11-11 05:40:22 -0500809 with open(file_path, 'w') as fd:
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700810 fd.write('\n'.join(new_project_paths))
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700811 fd.write('\n')
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700812 return 0
813
LaMont Jonesa46047a2022-04-07 21:57:06 +0000814 def UpdateCopyLinkfileList(self, manifest):
jiajia tanga590e642021-04-25 20:02:02 +0800815 """Save all dests of copyfile and linkfile, and update them if needed.
816
817 Returns:
818 Whether update was successful.
819 """
820 new_paths = {}
821 new_linkfile_paths = []
822 new_copyfile_paths = []
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000823 for project in self.GetProjects(None, missing_ok=True,
824 manifest=manifest, all_manifests=False):
jiajia tanga590e642021-04-25 20:02:02 +0800825 new_linkfile_paths.extend(x.dest for x in project.linkfiles)
826 new_copyfile_paths.extend(x.dest for x in project.copyfiles)
827
828 new_paths = {
829 'linkfile': new_linkfile_paths,
830 'copyfile': new_copyfile_paths,
831 }
832
833 copylinkfile_name = 'copy-link-files.json'
LaMont Jonesa46047a2022-04-07 21:57:06 +0000834 copylinkfile_path = os.path.join(manifest.subdir, copylinkfile_name)
jiajia tanga590e642021-04-25 20:02:02 +0800835 old_copylinkfile_paths = {}
836
837 if os.path.exists(copylinkfile_path):
838 with open(copylinkfile_path, 'rb') as fp:
839 try:
840 old_copylinkfile_paths = json.load(fp)
Raman Tenneti4a478ed2021-11-17 18:38:24 -0800841 except Exception:
jiajia tanga590e642021-04-25 20:02:02 +0800842 print('error: %s is not a json formatted file.' %
843 copylinkfile_path, file=sys.stderr)
844 platform_utils.remove(copylinkfile_path)
845 return False
846
847 need_remove_files = []
848 need_remove_files.extend(
849 set(old_copylinkfile_paths.get('linkfile', [])) -
850 set(new_linkfile_paths))
851 need_remove_files.extend(
852 set(old_copylinkfile_paths.get('copyfile', [])) -
853 set(new_copyfile_paths))
854
855 for need_remove_file in need_remove_files:
Mike Frysinger9d96f582021-09-28 11:27:24 -0400856 # Try to remove the updated copyfile or linkfile.
857 # So, if the file is not exist, nothing need to do.
858 platform_utils.remove(need_remove_file, missing_ok=True)
jiajia tanga590e642021-04-25 20:02:02 +0800859
860 # Create copy-link-files.json, save dest path of "copyfile" and "linkfile".
861 with open(copylinkfile_path, 'w', encoding='utf-8') as fp:
862 json.dump(new_paths, fp)
863 return True
864
LaMont Jonesa46047a2022-04-07 21:57:06 +0000865 def _SmartSyncSetup(self, opt, smart_sync_manifest_path, manifest):
866 if not manifest.manifest_server:
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400867 print('error: cannot smart sync: no manifest server defined in '
868 'manifest', file=sys.stderr)
869 sys.exit(1)
870
LaMont Jonesa46047a2022-04-07 21:57:06 +0000871 manifest_server = manifest.manifest_server
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400872 if not opt.quiet:
873 print('Using manifest server %s' % manifest_server)
874
David Pursehouseeeff3532020-02-12 11:24:10 +0900875 if '@' not in manifest_server:
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400876 username = None
877 password = None
878 if opt.manifest_server_username and opt.manifest_server_password:
879 username = opt.manifest_server_username
880 password = opt.manifest_server_password
881 else:
882 try:
883 info = netrc.netrc()
884 except IOError:
885 # .netrc file does not exist or could not be opened
886 pass
887 else:
888 try:
889 parse_result = urllib.parse.urlparse(manifest_server)
890 if parse_result.hostname:
891 auth = info.authenticators(parse_result.hostname)
892 if auth:
893 username, _account, password = auth
894 else:
895 print('No credentials found for %s in .netrc'
896 % parse_result.hostname, file=sys.stderr)
897 except netrc.NetrcParseError as e:
898 print('Error parsing .netrc file: %s' % e, file=sys.stderr)
899
900 if (username and password):
901 manifest_server = manifest_server.replace('://', '://%s:%s@' %
902 (username, password),
903 1)
904
905 transport = PersistentTransport(manifest_server)
906 if manifest_server.startswith('persistent-'):
907 manifest_server = manifest_server[len('persistent-'):]
908
909 try:
910 server = xmlrpc.client.Server(manifest_server, transport=transport)
911 if opt.smart_sync:
LaMont Jonesa46047a2022-04-07 21:57:06 +0000912 branch = self._GetBranch(manifest.manifestProject)
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400913
Mike Frysinger56ce3462019-12-04 19:30:48 -0500914 if 'SYNC_TARGET' in os.environ:
Mike Frysingere20da3e2020-03-07 01:53:53 -0500915 target = os.environ['SYNC_TARGET']
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400916 [success, manifest_str] = server.GetApprovedManifest(branch, target)
Mike Frysinger56ce3462019-12-04 19:30:48 -0500917 elif ('TARGET_PRODUCT' in os.environ and
918 'TARGET_BUILD_VARIANT' in os.environ):
Mike Frysingere20da3e2020-03-07 01:53:53 -0500919 target = '%s-%s' % (os.environ['TARGET_PRODUCT'],
920 os.environ['TARGET_BUILD_VARIANT'])
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400921 [success, manifest_str] = server.GetApprovedManifest(branch, target)
922 else:
923 [success, manifest_str] = server.GetApprovedManifest(branch)
924 else:
925 assert(opt.smart_tag)
926 [success, manifest_str] = server.GetManifest(opt.smart_tag)
927
928 if success:
929 manifest_name = os.path.basename(smart_sync_manifest_path)
930 try:
Mike Frysinger3164d402019-11-11 05:40:22 -0500931 with open(smart_sync_manifest_path, 'w') as f:
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400932 f.write(manifest_str)
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400933 except IOError as e:
934 print('error: cannot write manifest to %s:\n%s'
935 % (smart_sync_manifest_path, e),
936 file=sys.stderr)
937 sys.exit(1)
LaMont Jonesa46047a2022-04-07 21:57:06 +0000938 self._ReloadManifest(manifest_name, manifest)
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400939 else:
940 print('error: manifest server RPC call failed: %s' %
941 manifest_str, file=sys.stderr)
942 sys.exit(1)
943 except (socket.error, IOError, xmlrpc.client.Fault) as e:
944 print('error: cannot connect to manifest server %s:\n%s'
LaMont Jonesa46047a2022-04-07 21:57:06 +0000945 % (manifest.manifest_server, e), file=sys.stderr)
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400946 sys.exit(1)
947 except xmlrpc.client.ProtocolError as e:
948 print('error: cannot connect to manifest server %s:\n%d %s'
LaMont Jonesa46047a2022-04-07 21:57:06 +0000949 % (manifest.manifest_server, e.errcode, e.errmsg),
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400950 file=sys.stderr)
951 sys.exit(1)
952
953 return manifest_name
954
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000955 def _UpdateAllManifestProjects(self, opt, mp, manifest_name):
956 """Fetch & update the local manifest project.
957
958 After syncing the manifest project, if the manifest has any sub manifests,
959 those are recursively processed.
960
961 Args:
962 opt: Program options returned from optparse. See _Options().
963 mp: the manifestProject to query.
964 manifest_name: Manifest file to be reloaded.
965 """
966 if not mp.standalone_manifest_url:
967 self._UpdateManifestProject(opt, mp, manifest_name)
968
969 if mp.manifest.submanifests:
970 for submanifest in mp.manifest.submanifests.values():
971 child = submanifest.repo_client.manifest
972 child.manifestProject.SyncWithPossibleInit(
973 submanifest,
974 current_branch_only=self._GetCurrentBranchOnly(opt, child),
975 verbose=opt.verbose,
976 tags=opt.tags,
977 git_event_log=self.git_event_log,
978 )
979 self._UpdateAllManifestProjects(opt, child.manifestProject, None)
980
Mike Frysingerfb527e32019-08-27 02:34:32 -0400981 def _UpdateManifestProject(self, opt, mp, manifest_name):
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000982 """Fetch & update the local manifest project.
983
984 Args:
985 opt: Program options returned from optparse. See _Options().
986 mp: the manifestProject to query.
987 manifest_name: Manifest file to be reloaded.
988 """
Mike Frysingerfb527e32019-08-27 02:34:32 -0400989 if not opt.local_only:
990 start = time.time()
Mike Frysinger521d01b2020-02-17 01:51:49 -0500991 success = mp.Sync_NetworkHalf(quiet=opt.quiet, verbose=opt.verbose,
LaMont Jonesa46047a2022-04-07 21:57:06 +0000992 current_branch_only=self._GetCurrentBranchOnly(opt, mp.manifest),
Erwan Yvindc5c4d12019-06-18 13:49:12 +0200993 force_sync=opt.force_sync,
Mike Frysingerc58ec4d2020-02-17 14:36:08 -0500994 tags=opt.tags,
Mike Frysingerfb527e32019-08-27 02:34:32 -0400995 optimized_fetch=opt.optimized_fetch,
George Engelbrecht9bc283e2020-04-02 12:36:09 -0600996 retry_fetches=opt.retry_fetches,
LaMont Jonesa46047a2022-04-07 21:57:06 +0000997 submodules=mp.manifest.HasSubmodules,
998 clone_filter=mp.manifest.CloneFilter,
999 partial_clone_exclude=mp.manifest.PartialCloneExclude)
Mike Frysingerfb527e32019-08-27 02:34:32 -04001000 finish = time.time()
1001 self.event_log.AddSync(mp, event_log.TASK_SYNC_NETWORK,
1002 start, finish, success)
1003
1004 if mp.HasChanges:
1005 syncbuf = SyncBuffer(mp.config)
1006 start = time.time()
LaMont Jonesa46047a2022-04-07 21:57:06 +00001007 mp.Sync_LocalHalf(syncbuf, submodules=mp.manifest.HasSubmodules)
Mike Frysingerfb527e32019-08-27 02:34:32 -04001008 clean = syncbuf.Finish()
1009 self.event_log.AddSync(mp, event_log.TASK_SYNC_LOCAL,
1010 start, time.time(), clean)
1011 if not clean:
1012 sys.exit(1)
LaMont Jonesa46047a2022-04-07 21:57:06 +00001013 self._ReloadManifest(manifest_name, mp.manifest)
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001014
Mike Frysingerae6cb082019-08-27 01:10:59 -04001015 def ValidateOptions(self, opt, args):
1016 if opt.force_broken:
1017 print('warning: -f/--force-broken is now the default behavior, and the '
1018 'options are deprecated', file=sys.stderr)
1019 if opt.network_only and opt.detach_head:
1020 self.OptionParser.error('cannot combine -n and -d')
1021 if opt.network_only and opt.local_only:
1022 self.OptionParser.error('cannot combine -n and -l')
1023 if opt.manifest_name and opt.smart_sync:
1024 self.OptionParser.error('cannot combine -m and -s')
1025 if opt.manifest_name and opt.smart_tag:
1026 self.OptionParser.error('cannot combine -m and -t')
1027 if opt.manifest_server_username or opt.manifest_server_password:
1028 if not (opt.smart_sync or opt.smart_tag):
1029 self.OptionParser.error('-u and -p may only be combined with -s or -t')
1030 if None in [opt.manifest_server_username, opt.manifest_server_password]:
1031 self.OptionParser.error('both -u and -p must be given')
1032
Mike Frysinger0531a622021-11-05 15:22:01 -04001033 if opt.prune is None:
1034 opt.prune = True
1035
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001036 def Execute(self, opt, args):
LaMont Jonesa46047a2022-04-07 21:57:06 +00001037 manifest = self.outer_manifest
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001038 if not opt.outer_manifest:
LaMont Jonesa46047a2022-04-07 21:57:06 +00001039 manifest = self.manifest
1040
Chris Wolfee9dc3b32012-01-26 11:36:18 -05001041 if opt.manifest_name:
LaMont Jonesa46047a2022-04-07 21:57:06 +00001042 manifest.Override(opt.manifest_name)
Shawn O. Pearce3e768c92009-04-10 16:59:36 -07001043
Chirayu Desaia892b102013-06-11 14:18:46 +05301044 manifest_name = opt.manifest_name
David Pursehouse59b41742015-05-07 14:36:09 +09001045 smart_sync_manifest_path = os.path.join(
LaMont Jonesa46047a2022-04-07 21:57:06 +00001046 manifest.manifestProject.worktree, 'smart_sync_override.xml')
Chirayu Desaia892b102013-06-11 14:18:46 +05301047
Xin Lid79a4bc2020-05-20 16:03:45 -07001048 if opt.clone_bundle is None:
LaMont Jonesa46047a2022-04-07 21:57:06 +00001049 opt.clone_bundle = manifest.CloneBundle
Xin Lid79a4bc2020-05-20 16:03:45 -07001050
Victor Boivie08c880d2011-04-19 10:32:52 +02001051 if opt.smart_sync or opt.smart_tag:
LaMont Jonesa46047a2022-04-07 21:57:06 +00001052 manifest_name = self._SmartSyncSetup(opt, smart_sync_manifest_path, manifest)
Mike Frysinger01d6c3c2019-08-27 01:56:43 -04001053 else:
David Pursehouse59b41742015-05-07 14:36:09 +09001054 if os.path.isfile(smart_sync_manifest_path):
1055 try:
Renaud Paquay010fed72016-11-11 14:25:29 -08001056 platform_utils.remove(smart_sync_manifest_path)
David Pursehouse59b41742015-05-07 14:36:09 +09001057 except OSError as e:
1058 print('error: failed to remove existing smart sync override manifest: %s' %
1059 e, file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -07001060
Mike Frysingerc99322a2021-05-04 15:32:43 -04001061 err_event = multiprocessing.Event()
Mike Frysinger5a033082019-09-23 19:21:20 -04001062
LaMont Jonesa46047a2022-04-07 21:57:06 +00001063 rp = manifest.repoProject
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001064 rp.PreSync()
Mike Frysinger23c900f2020-02-29 02:52:44 -05001065 cb = rp.CurrentBranch
1066 if cb:
1067 base = rp.GetBranch(cb).merge
1068 if not base or not base.startswith('refs/heads/'):
1069 print('warning: repo is not tracking a remote branch, so it will not '
Mike Frysinger58ac1672020-03-14 14:35:26 -04001070 'receive updates; run `repo init --repo-rev=stable` to fix.',
Mike Frysinger23c900f2020-02-29 02:52:44 -05001071 file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001072
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001073 for m in self.ManifestList(opt):
1074 mp = m.manifestProject
1075 is_standalone_manifest = bool(mp.standalone_manifest_url)
1076 if not is_standalone_manifest:
1077 mp.PreSync()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001078
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001079 if opt.repo_upgraded:
1080 _PostRepoUpgrade(m, quiet=opt.quiet)
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08001081
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001082 if opt.mp_update:
1083 self._UpdateAllManifestProjects(opt, mp, manifest_name)
1084 else:
Fredrik de Grootcc960972019-11-22 09:04:31 +01001085 print('Skipping update of local manifest project.')
Simran Basib9a1b732015-08-20 12:19:28 -07001086
Mike Frysinger355f4392022-07-20 17:15:29 -04001087 # Now that the manifests are up-to-date, setup the jobs value.
1088 if opt.jobs is None:
1089 # User has not made a choice, so use the manifest settings.
1090 opt.jobs = mp.default.sync_j
1091 if opt.jobs is not None:
1092 # Neither user nor manifest have made a choice.
1093 if opt.jobs_network is None:
1094 opt.jobs_network = opt.jobs
1095 if opt.jobs_checkout is None:
1096 opt.jobs_checkout = opt.jobs
1097 # Setup defaults if jobs==0.
1098 if not opt.jobs:
1099 if not opt.jobs_network:
1100 opt.jobs_network = 1
1101 if not opt.jobs_checkout:
1102 opt.jobs_checkout = DEFAULT_LOCAL_JOBS
1103 opt.jobs = os.cpu_count()
1104
1105 # Try to stay under user rlimit settings.
1106 #
1107 # Since each worker requires at 3 file descriptors to run `git fetch`, use
1108 # that to scale down the number of jobs. Unfortunately there isn't an easy
1109 # way to determine this reliably as systems change, but it was last measured
1110 # by hand in 2011.
1111 soft_limit, _ = _rlimit_nofile()
1112 jobs_soft_limit = max(1, (soft_limit - 5) // 3)
1113 opt.jobs = min(opt.jobs, jobs_soft_limit)
1114 opt.jobs_network = min(opt.jobs_network, jobs_soft_limit)
1115 opt.jobs_checkout = min(opt.jobs_checkout, jobs_soft_limit)
1116
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001117 superproject_logging_data = {}
1118 self._UpdateProjectsRevisionId(opt, args, superproject_logging_data,
1119 manifest)
Raman Tenneti1fd7bc22021-02-04 14:39:38 -08001120
Simran Basib9a1b732015-08-20 12:19:28 -07001121 if self.gitc_manifest:
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001122 gitc_manifest_projects = self.GetProjects(args, missing_ok=True)
Simran Basib9a1b732015-08-20 12:19:28 -07001123 gitc_projects = []
1124 opened_projects = []
1125 for project in gitc_manifest_projects:
Dan Willemsen5ea32d12015-09-08 13:27:20 -07001126 if project.relpath in self.gitc_manifest.paths and \
1127 self.gitc_manifest.paths[project.relpath].old_revision:
1128 opened_projects.append(project.relpath)
Simran Basib9a1b732015-08-20 12:19:28 -07001129 else:
Dan Willemsen5ea32d12015-09-08 13:27:20 -07001130 gitc_projects.append(project.relpath)
Simran Basib9a1b732015-08-20 12:19:28 -07001131
Dan Willemsen5ea32d12015-09-08 13:27:20 -07001132 if not args:
1133 gitc_projects = None
1134
1135 if gitc_projects != [] and not opt.local_only:
Simran Basib9a1b732015-08-20 12:19:28 -07001136 print('Updating GITC client: %s' % self.gitc_manifest.gitc_client_name)
Dan Willemsen5ea32d12015-09-08 13:27:20 -07001137 manifest = GitcManifest(self.repodir, self.gitc_manifest.gitc_client_name)
1138 if manifest_name:
1139 manifest.Override(manifest_name)
1140 else:
LaMont Jonesa46047a2022-04-07 21:57:06 +00001141 manifest.Override(manifest.manifestFile)
Dan Willemsen5ea32d12015-09-08 13:27:20 -07001142 gitc_utils.generate_gitc_manifest(self.gitc_manifest,
1143 manifest,
Simran Basib9a1b732015-08-20 12:19:28 -07001144 gitc_projects)
1145 print('GITC client successfully synced.')
1146
1147 # The opened projects need to be synced as normal, therefore we
1148 # generate a new args list to represent the opened projects.
Dan Willemsen5ea32d12015-09-08 13:27:20 -07001149 # TODO: make this more reliable -- if there's a project name/path overlap,
1150 # this may choose the wrong project.
LaMont Jonesa46047a2022-04-07 21:57:06 +00001151 args = [os.path.relpath(manifest.paths[path].worktree, os.getcwd())
David Pursehouse3bcd3052017-07-10 22:42:22 +09001152 for path in opened_projects]
Simran Basib9a1b732015-08-20 12:19:28 -07001153 if not args:
1154 return
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001155
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001156 all_projects = self.GetProjects(args,
1157 missing_ok=True,
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001158 submodules_ok=opt.fetch_submodules,
1159 manifest=manifest,
1160 all_manifests=not opt.this_manifest_only)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001161
Mike Frysinger5a033082019-09-23 19:21:20 -04001162 err_network_sync = False
1163 err_update_projects = False
Mike Frysinger5a033082019-09-23 19:21:20 -04001164
LaMont Jonesa46047a2022-04-07 21:57:06 +00001165 self._fetch_times = _FetchTimes(manifest)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -07001166 if not opt.local_only:
Mike Frysinger339f2df2021-05-06 00:44:42 -04001167 with multiprocessing.Manager() as manager:
1168 with ssh.ProxyManager(manager) as ssh_proxy:
1169 # Initialize the socket dir once in the parent.
1170 ssh_proxy.sock()
Peter Kjellerstedtd1776092021-05-19 19:37:23 +02001171 all_projects = self._FetchMain(opt, args, all_projects, err_event,
LaMont Jonesa46047a2022-04-07 21:57:06 +00001172 ssh_proxy, manifest)
Mike Frysinger339f2df2021-05-06 00:44:42 -04001173
1174 if opt.network_only:
1175 return
Mike Frysinger5a033082019-09-23 19:21:20 -04001176
1177 # If we saw an error, exit with code 1 so that other scripts can check.
Mike Frysingerbe24a542021-02-23 03:24:12 -05001178 if err_event.is_set():
Mike Frysinger5a033082019-09-23 19:21:20 -04001179 err_network_sync = True
1180 if opt.fail_fast:
1181 print('\nerror: Exited sync due to fetch errors.\n'
1182 'Local checkouts *not* updated. Resolve network issues & '
1183 'retry.\n'
1184 '`repo sync -l` will update some local checkouts.',
1185 file=sys.stderr)
1186 sys.exit(1)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001187
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001188 for m in self.ManifestList(opt):
1189 if m.IsMirror or m.IsArchive:
1190 # bail out now, we have no working tree
1191 continue
Shawn O. Pearcecd1d7ff2009-06-04 16:15:53 -07001192
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001193 if self.UpdateProjectList(opt, m):
1194 err_event.set()
1195 err_update_projects = True
1196 if opt.fail_fast:
1197 print('\nerror: Local checkouts *not* updated.', file=sys.stderr)
1198 sys.exit(1)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -07001199
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001200 err_update_linkfiles = not self.UpdateCopyLinkfileList(m)
1201 if err_update_linkfiles:
1202 err_event.set()
1203 if opt.fail_fast:
1204 print('\nerror: Local update copyfile or linkfile failed.', file=sys.stderr)
1205 sys.exit(1)
jiajia tanga590e642021-04-25 20:02:02 +08001206
Mike Frysinger5a033082019-09-23 19:21:20 -04001207 err_results = []
Mike Frysingerebf04a42021-02-23 20:48:04 -05001208 # NB: We don't exit here because this is the last step.
1209 err_checkout = not self._Checkout(all_projects, opt, err_results)
1210 if err_checkout:
1211 err_event.set()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001212
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001213 printed_notices = set()
1214 # If there's a notice that's supposed to print at the end of the sync,
1215 # print it now... But avoid printing duplicate messages, and preserve
1216 # order.
1217 for m in sorted(self.ManifestList(opt), key=lambda x: x.path_prefix):
1218 if m.notice and m.notice not in printed_notices:
1219 print(m.notice)
1220 printed_notices.add(m.notice)
Doug Anderson2b8db3c2010-11-01 15:08:06 -07001221
Mike Frysinger5a033082019-09-23 19:21:20 -04001222 # If we saw an error, exit with code 1 so that other scripts can check.
Mike Frysingerbe24a542021-02-23 03:24:12 -05001223 if err_event.is_set():
Mike Frysinger5a033082019-09-23 19:21:20 -04001224 print('\nerror: Unable to fully sync the tree.', file=sys.stderr)
1225 if err_network_sync:
1226 print('error: Downloading network changes failed.', file=sys.stderr)
1227 if err_update_projects:
1228 print('error: Updating local project lists failed.', file=sys.stderr)
jiajia tanga590e642021-04-25 20:02:02 +08001229 if err_update_linkfiles:
1230 print('error: Updating copyfiles or linkfiles failed.', file=sys.stderr)
Mike Frysinger5a033082019-09-23 19:21:20 -04001231 if err_checkout:
1232 print('error: Checking out local projects failed.', file=sys.stderr)
1233 if err_results:
1234 print('Failing repos:\n%s' % '\n'.join(err_results), file=sys.stderr)
1235 print('Try re-running with "-j1 --fail-fast" to exit at the first error.',
1236 file=sys.stderr)
1237 sys.exit(1)
1238
Raman Tenneti7954de12021-07-28 14:36:49 -07001239 # Log the previous sync analysis state from the config.
Raman Tenneti6448a4f2021-09-13 17:40:07 -07001240 self.git_event_log.LogDataConfigEvents(mp.config.GetSyncAnalysisStateData(),
1241 'previous_sync_state')
Raman Tenneti7954de12021-07-28 14:36:49 -07001242
1243 # Update and log with the new sync analysis state.
1244 mp.config.UpdateSyncAnalysisState(opt, superproject_logging_data)
Raman Tenneti6448a4f2021-09-13 17:40:07 -07001245 self.git_event_log.LogDataConfigEvents(mp.config.GetSyncAnalysisStateData(),
1246 'current_sync_state')
Raman Tenneti7954de12021-07-28 14:36:49 -07001247
Mike Frysingere19d9e12020-02-12 11:23:32 -05001248 if not opt.quiet:
1249 print('repo sync has finished successfully.')
1250
David Pursehouse819827a2020-02-12 15:20:19 +09001251
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -07001252def _PostRepoUpgrade(manifest, quiet=False):
Mike Frysingerfdeb20f2021-11-14 03:53:04 -05001253 # Link the docs for the internal .repo/ layout for people
1254 link = os.path.join(manifest.repodir, 'internal-fs-layout.md')
1255 if not platform_utils.islink(link):
1256 target = os.path.join('repo', 'docs', 'internal-fs-layout.md')
1257 try:
1258 platform_utils.symlink(target, link)
Raman Tenneti4a478ed2021-11-17 18:38:24 -08001259 except Exception:
Mike Frysingerfdeb20f2021-11-14 03:53:04 -05001260 pass
1261
Conley Owens094cdbe2014-01-30 15:09:59 -08001262 wrapper = Wrapper()
Conley Owensc9129d92012-10-01 16:12:28 -07001263 if wrapper.NeedSetupGnuPG():
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -07001264 wrapper.SetupGnuPG(quiet)
Conley Owensf2fe2d92014-01-29 13:53:43 -08001265 for project in manifest.projects:
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001266 if project.Exists:
1267 project.PostRepoUpgrade()
1268
David Pursehouse819827a2020-02-12 15:20:19 +09001269
Mike Frysingerc58ec4d2020-02-17 14:36:08 -05001270def _PostRepoFetch(rp, repo_verify=True, verbose=False):
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001271 if rp.HasChanges:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001272 print('info: A new version of repo is available', file=sys.stderr)
Mike Frysinger347f9ed2021-03-15 14:58:52 -04001273 wrapper = Wrapper()
1274 try:
1275 rev = rp.bare_git.describe(rp.GetRevisionId())
1276 except GitError:
1277 rev = None
1278 _, new_rev = wrapper.check_repo_rev(rp.gitdir, rev, repo_verify=repo_verify)
1279 # See if we're held back due to missing signed tag.
1280 current_revid = rp.bare_git.rev_parse('HEAD')
1281 new_revid = rp.bare_git.rev_parse('--verify', new_rev)
1282 if current_revid != new_revid:
1283 # We want to switch to the new rev, but also not trash any uncommitted
1284 # changes. This helps with local testing/hacking.
1285 # If a local change has been made, we will throw that away.
1286 # We also have to make sure this will switch to an older commit if that's
1287 # the latest tag in order to support release rollback.
1288 try:
1289 rp.work_git.reset('--keep', new_rev)
1290 except GitError as e:
1291 sys.exit(str(e))
Sarah Owenscecd1d82012-11-01 22:59:27 -07001292 print('info: Restarting repo with latest version', file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001293 raise RepoChangedException(['--repo-upgraded'])
1294 else:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001295 print('warning: Skipped upgrade to unverified version', file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001296 else:
1297 if verbose:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001298 print('repo version %s is current' % rp.work_git.describe(HEAD),
1299 file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001300
David Pursehouse819827a2020-02-12 15:20:19 +09001301
Dave Borowitz67700e92012-10-23 15:00:54 -07001302class _FetchTimes(object):
Dave Borowitzd9478582012-10-23 16:35:39 -07001303 _ALPHA = 0.5
1304
Dave Borowitz67700e92012-10-23 15:00:54 -07001305 def __init__(self, manifest):
Anthony King85b24ac2014-05-06 15:57:48 +01001306 self._path = os.path.join(manifest.repodir, '.repo_fetchtimes.json')
Dave Borowitz67700e92012-10-23 15:00:54 -07001307 self._times = None
Dave Borowitzd9478582012-10-23 16:35:39 -07001308 self._seen = set()
Dave Borowitz67700e92012-10-23 15:00:54 -07001309
1310 def Get(self, project):
1311 self._Load()
1312 return self._times.get(project.name, _ONE_DAY_S)
1313
1314 def Set(self, project, t):
Dave Borowitzd9478582012-10-23 16:35:39 -07001315 self._Load()
1316 name = project.name
1317 old = self._times.get(name, t)
1318 self._seen.add(name)
1319 a = self._ALPHA
David Pursehouse54a4e602020-02-12 14:31:05 +09001320 self._times[name] = (a * t) + ((1 - a) * old)
Dave Borowitz67700e92012-10-23 15:00:54 -07001321
1322 def _Load(self):
1323 if self._times is None:
1324 try:
Mike Frysinger3164d402019-11-11 05:40:22 -05001325 with open(self._path) as f:
Anthony King85b24ac2014-05-06 15:57:48 +01001326 self._times = json.load(f)
Anthony King85b24ac2014-05-06 15:57:48 +01001327 except (IOError, ValueError):
Mike Frysinger9d96f582021-09-28 11:27:24 -04001328 platform_utils.remove(self._path, missing_ok=True)
Anthony King85b24ac2014-05-06 15:57:48 +01001329 self._times = {}
Dave Borowitz67700e92012-10-23 15:00:54 -07001330
1331 def Save(self):
1332 if self._times is None:
1333 return
Dave Borowitzd9478582012-10-23 16:35:39 -07001334
1335 to_delete = []
1336 for name in self._times:
1337 if name not in self._seen:
1338 to_delete.append(name)
1339 for name in to_delete:
1340 del self._times[name]
1341
Dave Borowitz67700e92012-10-23 15:00:54 -07001342 try:
Mike Frysinger3164d402019-11-11 05:40:22 -05001343 with open(self._path, 'w') as f:
Anthony King85b24ac2014-05-06 15:57:48 +01001344 json.dump(self._times, f, indent=2)
Anthony King85b24ac2014-05-06 15:57:48 +01001345 except (IOError, TypeError):
Mike Frysinger9d96f582021-09-28 11:27:24 -04001346 platform_utils.remove(self._path, missing_ok=True)
Dan Willemsen0745bb22015-08-17 13:41:45 -07001347
1348# This is a replacement for xmlrpc.client.Transport using urllib2
1349# and supporting persistent-http[s]. It cannot change hosts from
1350# request to request like the normal transport, the real url
1351# is passed during initialization.
David Pursehouse819827a2020-02-12 15:20:19 +09001352
1353
Dan Willemsen0745bb22015-08-17 13:41:45 -07001354class PersistentTransport(xmlrpc.client.Transport):
1355 def __init__(self, orig_host):
1356 self.orig_host = orig_host
1357
1358 def request(self, host, handler, request_body, verbose=False):
1359 with GetUrlCookieFile(self.orig_host, not verbose) as (cookiefile, proxy):
1360 # Python doesn't understand cookies with the #HttpOnly_ prefix
1361 # Since we're only using them for HTTP, copy the file temporarily,
1362 # stripping those prefixes away.
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001363 if cookiefile:
Collin Fijalkoviche1191b32020-02-18 10:57:32 -08001364 tmpcookiefile = tempfile.NamedTemporaryFile(mode='w')
David Pursehouse4c5f74e2015-10-02 11:10:10 +09001365 tmpcookiefile.write("# HTTP Cookie File")
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001366 try:
1367 with open(cookiefile) as f:
1368 for line in f:
1369 if line.startswith("#HttpOnly_"):
1370 line = line[len("#HttpOnly_"):]
1371 tmpcookiefile.write(line)
1372 tmpcookiefile.flush()
Dan Willemsen0745bb22015-08-17 13:41:45 -07001373
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001374 cookiejar = cookielib.MozillaCookieJar(tmpcookiefile.name)
David Pursehouseb1ad2192015-09-30 10:35:43 +09001375 try:
1376 cookiejar.load()
1377 except cookielib.LoadError:
1378 cookiejar = cookielib.CookieJar()
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001379 finally:
1380 tmpcookiefile.close()
1381 else:
1382 cookiejar = cookielib.CookieJar()
Dan Willemsen0745bb22015-08-17 13:41:45 -07001383
1384 proxyhandler = urllib.request.ProxyHandler
1385 if proxy:
1386 proxyhandler = urllib.request.ProxyHandler({
1387 "http": proxy,
David Pursehouse54a4e602020-02-12 14:31:05 +09001388 "https": proxy})
Dan Willemsen0745bb22015-08-17 13:41:45 -07001389
1390 opener = urllib.request.build_opener(
1391 urllib.request.HTTPCookieProcessor(cookiejar),
1392 proxyhandler)
1393
1394 url = urllib.parse.urljoin(self.orig_host, handler)
1395 parse_results = urllib.parse.urlparse(url)
1396
1397 scheme = parse_results.scheme
1398 if scheme == 'persistent-http':
1399 scheme = 'http'
1400 if scheme == 'persistent-https':
1401 # If we're proxying through persistent-https, use http. The
1402 # proxy itself will do the https.
1403 if proxy:
1404 scheme = 'http'
1405 else:
1406 scheme = 'https'
1407
1408 # Parse out any authentication information using the base class
1409 host, extra_headers, _ = self.get_host_info(parse_results.netloc)
1410
1411 url = urllib.parse.urlunparse((
1412 scheme,
1413 host,
1414 parse_results.path,
1415 parse_results.params,
1416 parse_results.query,
1417 parse_results.fragment))
1418
1419 request = urllib.request.Request(url, request_body)
1420 if extra_headers is not None:
1421 for (name, header) in extra_headers:
1422 request.add_header(name, header)
1423 request.add_header('Content-Type', 'text/xml')
1424 try:
1425 response = opener.open(request)
1426 except urllib.error.HTTPError as e:
1427 if e.code == 501:
1428 # We may have been redirected through a login process
1429 # but our POST turned into a GET. Retry.
1430 response = opener.open(request)
1431 else:
1432 raise
1433
1434 p, u = xmlrpc.client.getparser()
Mike Frysinger5951e302022-05-20 23:34:44 -04001435 # Response should be fairly small, so read it all at once.
1436 # This way we can show it to the user in case of error (e.g. HTML).
1437 data = response.read()
1438 try:
Dan Willemsen0745bb22015-08-17 13:41:45 -07001439 p.feed(data)
Mike Frysinger5951e302022-05-20 23:34:44 -04001440 except xml.parsers.expat.ExpatError as e:
1441 raise IOError(
1442 f'Parsing the manifest failed: {e}\n'
1443 f'Please report this to your manifest server admin.\n'
1444 f'Here is the full response:\n{data.decode("utf-8")}')
Dan Willemsen0745bb22015-08-17 13:41:45 -07001445 p.close()
1446 return u.close()
1447
1448 def close(self):
1449 pass