blob: fa61d5519dab6e96d7176bd5bcd65a2d5a67e809 [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
31import xmlrpc.client
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070032
Roy Lee18afd7f2010-05-09 04:32:08 +080033try:
34 import threading as _threading
35except ImportError:
36 import dummy_threading as _threading
37
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -070038try:
39 import resource
David Pursehouse819827a2020-02-12 15:20:19 +090040
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -070041 def _rlimit_nofile():
42 return resource.getrlimit(resource.RLIMIT_NOFILE)
43except ImportError:
44 def _rlimit_nofile():
45 return (256, 256)
46
David Rileye0684ad2017-04-05 00:02:59 -070047import event_log
Mike Frysinger347f9ed2021-03-15 14:58:52 -040048from git_command import git_require
David Pursehouseba7bc732015-08-20 16:55:42 +090049from git_config import GetUrlCookieFile
David Pursehoused94aaef2012-09-07 09:52:04 +090050from git_refs import R_HEADS, HEAD
Raman Tenneti6a872c92021-01-14 19:17:50 -080051import git_superproject
Simran Basibdb52712015-08-10 13:23:23 -070052import gitc_utils
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -070053from project import Project
54from project import RemoteSpec
Mike Frysingerd41eed02021-04-20 23:21:29 -040055from command import Command, MirrorSafeCommand, WORKER_BATCH_SIZE
Raman Tenneti1fd7bc22021-02-04 14:39:38 -080056from error import RepoChangedException, GitError, ManifestParseError
Renaud Paquaya65adf72016-11-03 10:37:53 -070057import platform_utils
Shawn O. Pearce350cde42009-04-16 11:21:18 -070058from project import SyncBuffer
Shawn O. Pearce68194f42009-04-10 16:48:52 -070059from progress import Progress
Mike Frysinger19e409c2021-05-05 19:44:35 -040060import ssh
Conley Owens094cdbe2014-01-30 15:09:59 -080061from wrapper import Wrapper
Dan Willemsen5ea32d12015-09-08 13:27:20 -070062from manifest_xml import GitcManifest
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070063
Dave Borowitz67700e92012-10-23 15:00:54 -070064_ONE_DAY_S = 24 * 60 * 60
65
David Pursehouse819827a2020-02-12 15:20:19 +090066
Shawn O. Pearcec95583b2009-03-03 17:47:06 -080067class Sync(Command, MirrorSafeCommand):
Roy Lee18afd7f2010-05-09 04:32:08 +080068 jobs = 1
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 Frysinger6a2400a2021-02-16 01:43:31 -0500171 PARALLEL_JOBS = 1
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700172
Mike Frysinger9180a072021-04-13 14:57:40 -0400173 def _CommonOptions(self, p):
LaMont Jonesa46047a2022-04-07 21:57:06 +0000174 if self.outer_client and self.outer_client.manifest:
Mike Frysingerc177f942021-05-04 08:06:36 -0400175 try:
LaMont Jonesa46047a2022-04-07 21:57:06 +0000176 self.PARALLEL_JOBS = self.outer_client.manifest.default.sync_j
Mike Frysingerc177f942021-05-04 08:06:36 -0400177 except ManifestParseError:
178 pass
Mike Frysinger9180a072021-04-13 14:57:40 -0400179 super()._CommonOptions(p)
Shawn O. Pearce6392c872011-09-22 17:44:31 -0700180
Mike Frysinger9180a072021-04-13 14:57:40 -0400181 def _Options(self, p, show_smart=True):
Mike Frysinger49de8ef2021-04-09 00:21:02 -0400182 p.add_option('--jobs-network', default=None, type=int, metavar='JOBS',
183 help='number of network jobs to run in parallel (defaults to --jobs)')
184 p.add_option('--jobs-checkout', default=None, type=int, metavar='JOBS',
185 help='number of local checkout jobs to run in parallel (defaults to --jobs)')
186
Andrei Warkentin5df6de02010-07-02 17:58:31 -0500187 p.add_option('-f', '--force-broken',
Stefan Müller-Klieser46702ed2019-08-30 10:20:15 +0200188 dest='force_broken', action='store_true',
Mike Frysingerd9e5cf02019-08-26 03:12:55 -0400189 help='obsolete option (to be deleted in the future)')
190 p.add_option('--fail-fast',
191 dest='fail_fast', action='store_true',
192 help='stop syncing after first error is hit')
Kevin Degiabaa7f32014-11-12 11:27:45 -0700193 p.add_option('--force-sync',
194 dest='force_sync', action='store_true',
195 help="overwrite an existing git directory if it needs to "
196 "point to a different object directory. WARNING: this "
197 "may cause loss of data")
Oleksii Okolielovd3c0f592018-12-17 19:23:44 -0500198 p.add_option('--force-remove-dirty',
199 dest='force_remove_dirty', action='store_true',
200 help="force remove projects with uncommitted modifications if "
201 "projects no longer exist in the manifest. "
202 "WARNING: this may cause loss of data")
David Pursehouse8f62fb72012-11-14 12:09:38 +0900203 p.add_option('-l', '--local-only',
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700204 dest='local_only', action='store_true',
205 help="only update working tree, don't fetch")
David Pursehouse54a4e602020-02-12 14:31:05 +0900206 p.add_option('--no-manifest-update', '--nmu',
Fredrik de Grootcc960972019-11-22 09:04:31 +0100207 dest='mp_update', action='store_false', default='true',
208 help='use the existing manifest checkout as-is. '
209 '(do not update to the latest revision)')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900210 p.add_option('-n', '--network-only',
Shawn O. Pearce96fdcef2009-04-10 16:29:20 -0700211 dest='network_only', action='store_true',
212 help="fetch only, don't update working tree")
David Pursehouse8f62fb72012-11-14 12:09:38 +0900213 p.add_option('-d', '--detach',
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700214 dest='detach_head', action='store_true',
215 help='detach projects back to manifest revision')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900216 p.add_option('-c', '--current-branch',
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -0700217 dest='current_branch_only', action='store_true',
218 help='fetch only current branch from server')
Mike Frysinger73561142021-05-03 01:10:09 -0400219 p.add_option('--no-current-branch',
220 dest='current_branch_only', action='store_false',
221 help='fetch all branches from server')
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500222 p.add_option('-m', '--manifest-name',
223 dest='manifest_name',
224 help='temporary manifest to use for this sync', metavar='NAME.xml')
Xin Lid79a4bc2020-05-20 16:03:45 -0700225 p.add_option('--clone-bundle', action='store_true',
226 help='enable use of /clone.bundle on HTTP/HTTPS')
227 p.add_option('--no-clone-bundle', dest='clone_bundle', action='store_false',
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -0700228 help='disable use of /clone.bundle on HTTP/HTTPS')
Conley Owens8d070cf2012-11-06 13:14:31 -0800229 p.add_option('-u', '--manifest-server-username', action='store',
230 dest='manifest_server_username',
231 help='username to authenticate with the manifest server')
232 p.add_option('-p', '--manifest-server-password', action='store',
233 dest='manifest_server_password',
234 help='password to authenticate with the manifest server')
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800235 p.add_option('--fetch-submodules',
236 dest='fetch_submodules', action='store_true',
237 help='fetch submodules from server')
Raman Tenneti6a872c92021-01-14 19:17:50 -0800238 p.add_option('--use-superproject', action='store_true',
Raman Tenneti62517292021-11-01 14:49:16 -0700239 help='use the manifest superproject to sync projects; implies -c')
Raman Tenneti23ea7542021-05-07 14:01:54 -0700240 p.add_option('--no-use-superproject', action='store_false',
241 dest='use_superproject',
242 help='disable use of manifest superprojects')
Mike Frysinger2273f462021-11-05 15:10:33 -0400243 p.add_option('--tags', action='store_true',
Mike Frysingerd68ed632021-05-03 01:21:35 -0400244 help='fetch tags')
Mitchel Humpherys597868b2012-10-29 10:18:34 -0700245 p.add_option('--no-tags',
Mike Frysingerd68ed632021-05-03 01:21:35 -0400246 dest='tags', action='store_false',
Mike Frysinger2273f462021-11-05 15:10:33 -0400247 help="don't fetch tags (default)")
David Pursehouseb1553542014-09-04 21:28:09 +0900248 p.add_option('--optimized-fetch',
249 dest='optimized_fetch', action='store_true',
250 help='only fetch projects fixed to sha1 if revision does not exist locally')
George Engelbrecht9bc283e2020-04-02 12:36:09 -0600251 p.add_option('--retry-fetches',
252 default=0, action='store', type='int',
253 help='number of times to retry fetches on transient errors')
Mike Frysinger0531a622021-11-05 15:22:01 -0400254 p.add_option('--prune', action='store_true',
255 help='delete refs that no longer exist on the remote (default)')
256 p.add_option('--no-prune', dest='prune', action='store_false',
257 help='do not delete refs that no longer exist on the remote')
Nico Sallembien6623b212010-05-11 12:57:01 -0700258 if show_smart:
259 p.add_option('-s', '--smart-sync',
260 dest='smart_sync', action='store_true',
David Pursehouse79fba682016-04-13 18:03:00 +0900261 help='smart sync using manifest from the latest known good build')
Victor Boivie08c880d2011-04-19 10:32:52 +0200262 p.add_option('-t', '--smart-tag',
263 dest='smart_tag', action='store',
264 help='smart sync using manifest from a known tag')
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700265
Shawn O. Pearcefd89b672009-04-18 11:28:57 -0700266 g = p.add_option_group('repo Version options')
267 g.add_option('--no-repo-verify',
Mike Frysingerc58ec4d2020-02-17 14:36:08 -0500268 dest='repo_verify', default=True, action='store_false',
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700269 help='do not verify repo source code')
Shawn O. Pearcefd89b672009-04-18 11:28:57 -0700270 g.add_option('--repo-upgraded',
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800271 dest='repo_upgraded', action='store_true',
Shawn O. Pearce2a1ccb22009-04-10 16:51:53 -0700272 help=SUPPRESS_HELP)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700273
LaMont Jonesa46047a2022-04-07 21:57:06 +0000274 def _GetBranch(self, manifest_project):
275 """Returns the branch name for getting the approved smartsync manifest.
276
277 Args:
278 manifest_project: the manifestProject to query.
279 """
280 b = manifest_project.GetBranch(manifest_project.CurrentBranch)
Raman Tenneti8d43dea2021-02-07 16:30:27 -0800281 branch = b.merge
282 if branch.startswith(R_HEADS):
283 branch = branch[len(R_HEADS):]
284 return branch
285
LaMont Jonesa46047a2022-04-07 21:57:06 +0000286 def _GetCurrentBranchOnly(self, opt, manifest):
Daniel Anderssond52ca422022-04-01 12:55:38 +0200287 """Returns whether current-branch or use-superproject options are enabled.
288
LaMont Jonesa46047a2022-04-07 21:57:06 +0000289 Args:
290 opt: Program options returned from optparse. See _Options().
291 manifest: The manifest to use.
292
Daniel Anderssond52ca422022-04-01 12:55:38 +0200293 Returns:
294 True if a superproject is requested, otherwise the value of the
295 current_branch option (True, False or None).
296 """
LaMont Jonesa46047a2022-04-07 21:57:06 +0000297 return git_superproject.UseSuperproject(opt.use_superproject, manifest) or opt.current_branch_only
Raman Tenneti2ae44d72021-03-23 15:12:27 -0700298
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000299 def _UpdateProjectsRevisionId(self, opt, args, superproject_logging_data,
300 manifest):
301 """Update revisionId of projects with the commit hash from the superproject.
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800302
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000303 This function updates each project's revisionId with the commit hash from
304 the superproject. It writes the updated manifest into a file and reloads
305 the manifest from it. When appropriate, sub manifests are also processed.
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800306
307 Args:
308 opt: Program options returned from optparse. See _Options().
309 args: Arguments to pass to GetProjects. See the GetProjects
310 docstring for details.
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000311 superproject_logging_data: A dictionary of superproject data to log.
LaMont Jonesa46047a2022-04-07 21:57:06 +0000312 manifest: The manifest to use.
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800313 """
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000314 have_superproject = manifest.superproject or any(
315 m.superproject for m in manifest.all_children)
316 if not have_superproject:
317 return
318
LaMont Jonesff6b1da2022-06-01 21:03:34 +0000319 if opt.local_only and manifest.superproject:
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000320 manifest_path = manifest.superproject.manifest_path
Raman Tennetiae86a462021-07-27 08:54:59 -0700321 if manifest_path:
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000322 self._ReloadManifest(manifest_path, manifest)
323 return
Raman Tennetiae86a462021-07-27 08:54:59 -0700324
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800325 all_projects = self.GetProjects(args,
326 missing_ok=True,
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000327 submodules_ok=opt.fetch_submodules,
328 manifest=manifest,
329 all_manifests=not opt.this_manifest_only)
330
331 per_manifest = collections.defaultdict(list)
332 manifest_paths = {}
333 if opt.this_manifest_only:
334 per_manifest[manifest.path_prefix] = all_projects
Raman Tenneti784e16f2021-06-11 17:29:45 -0700335 else:
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000336 for p in all_projects:
337 per_manifest[p.manifest.path_prefix].append(p)
338
339 superproject_logging_data = {}
340 need_unload = False
341 for m in self.ManifestList(opt):
342 if not m.path_prefix in per_manifest:
343 continue
344 use_super = git_superproject.UseSuperproject(opt.use_superproject, m)
345 if superproject_logging_data:
346 superproject_logging_data['multimanifest'] = True
347 superproject_logging_data.update(
348 superproject=use_super,
349 haslocalmanifests=bool(m.HasLocalManifests),
350 hassuperprojecttag=bool(m.superproject),
351 )
352 if use_super and (m.IsMirror or m.IsArchive):
353 # Don't use superproject, because we have no working tree.
354 use_super = False
355 superproject_logging_data['superproject'] = False
356 superproject_logging_data['noworktree'] = True
357 if opt.use_superproject is not False:
358 print(f'{m.path_prefix}: not using superproject because there is no '
359 'working tree.')
360
361 if not use_super:
362 continue
363 m.superproject.SetQuiet(opt.quiet)
364 print_messages = git_superproject.PrintMessages(opt.use_superproject, m)
365 m.superproject.SetPrintMessages(print_messages)
366 update_result = m.superproject.UpdateProjectsRevisionId(
367 per_manifest[m.path_prefix], git_event_log=self.git_event_log)
368 manifest_path = update_result.manifest_path
369 superproject_logging_data['updatedrevisionid'] = bool(manifest_path)
370 if manifest_path:
371 m.SetManifestOverride(manifest_path)
372 need_unload = True
373 else:
374 if print_messages:
375 print(f'{m.path_prefix}: warning: Update of revisionId from '
376 'superproject has failed, repo sync will not use superproject '
377 'to fetch the source. ',
378 'Please resync with the --no-use-superproject option to avoid '
379 'this repo warning.',
380 file=sys.stderr)
381 if update_result.fatal and opt.use_superproject is not None:
382 sys.exit(1)
383 if need_unload:
384 m.outer_client.manifest.Unload()
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800385
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500386 def _FetchProjectList(self, opt, projects):
387 """Main function of the fetch worker.
388
389 The projects we're given share the same underlying git object store, so we
390 have to fetch them in serial.
Roy Lee18afd7f2010-05-09 04:32:08 +0800391
David James8d201162013-10-11 17:03:19 -0700392 Delegates most of the work to _FetchHelper.
393
394 Args:
395 opt: Program options returned from optparse. See _Options().
396 projects: Projects to fetch.
David James8d201162013-10-11 17:03:19 -0700397 """
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500398 return [self._FetchOne(opt, x) for x in projects]
David James8d201162013-10-11 17:03:19 -0700399
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500400 def _FetchOne(self, opt, project):
David James8d201162013-10-11 17:03:19 -0700401 """Fetch git objects for a single project.
402
David Pursehousec1b86a22012-11-14 11:36:51 +0900403 Args:
404 opt: Program options returned from optparse. See _Options().
405 project: Project object for the project to fetch.
David James8d201162013-10-11 17:03:19 -0700406
407 Returns:
408 Whether the fetch was successful.
David Pursehousec1b86a22012-11-14 11:36:51 +0900409 """
David Rileye0684ad2017-04-05 00:02:59 -0700410 start = time.time()
411 success = False
Mike Frysinger7b586f22021-02-23 18:38:39 -0500412 buf = io.StringIO()
David Pursehousec1b86a22012-11-14 11:36:51 +0900413 try:
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500414 success = project.Sync_NetworkHalf(
415 quiet=opt.quiet,
416 verbose=opt.verbose,
417 output_redir=buf,
LaMont Jonesa46047a2022-04-07 21:57:06 +0000418 current_branch_only=self._GetCurrentBranchOnly(opt, project.manifest),
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500419 force_sync=opt.force_sync,
420 clone_bundle=opt.clone_bundle,
LaMont Jonesa46047a2022-04-07 21:57:06 +0000421 tags=opt.tags, archive=project.manifest.IsArchive,
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500422 optimized_fetch=opt.optimized_fetch,
423 retry_fetches=opt.retry_fetches,
424 prune=opt.prune,
Mike Frysinger339f2df2021-05-06 00:44:42 -0400425 ssh_proxy=self.ssh_proxy,
LaMont Jonesa46047a2022-04-07 21:57:06 +0000426 clone_filter=project.manifest.CloneFilter,
427 partial_clone_exclude=project.manifest.PartialCloneExclude)
Doug Andersonfc06ced2011-03-16 15:49:18 -0700428
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500429 output = buf.getvalue()
Mike Frysinger58929732021-07-02 00:29:35 -0400430 if (opt.verbose or not success) and output:
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500431 print('\n' + output.rstrip())
Doug Andersonfc06ced2011-03-16 15:49:18 -0700432
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500433 if not success:
434 print('error: Cannot fetch %s from %s'
435 % (project.name, project.remote.url),
436 file=sys.stderr)
Raman Tennetiad8aa692021-04-15 09:20:51 -0700437 except GitError as e:
438 print('error.GitError: Cannot fetch %s' % str(e), file=sys.stderr)
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500439 except Exception as e:
440 print('error: Cannot fetch %s (%s: %s)'
441 % (project.name, type(e).__name__, str(e)), file=sys.stderr)
442 raise
Mike Frysinger7b586f22021-02-23 18:38:39 -0500443
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500444 finish = time.time()
445 return (success, project, start, finish)
David James8d201162013-10-11 17:03:19 -0700446
Mike Frysinger339f2df2021-05-06 00:44:42 -0400447 @classmethod
448 def _FetchInitChild(cls, ssh_proxy):
449 cls.ssh_proxy = ssh_proxy
450
451 def _Fetch(self, projects, opt, err_event, ssh_proxy):
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500452 ret = True
453
Mike Frysinger49de8ef2021-04-09 00:21:02 -0400454 jobs = opt.jobs_network if opt.jobs_network else self.jobs
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700455 fetched = set()
Mike Frysinger151701e2021-04-13 15:07:21 -0400456 pm = Progress('Fetching', len(projects), delay=False, quiet=opt.quiet)
Roy Lee18afd7f2010-05-09 04:32:08 +0800457
David James89ece422014-01-09 18:51:58 -0800458 objdir_project_map = dict()
459 for project in projects:
460 objdir_project_map.setdefault(project.objdir, []).append(project)
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500461 projects_list = list(objdir_project_map.values())
David James8d201162013-10-11 17:03:19 -0700462
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500463 def _ProcessResults(results_sets):
464 ret = True
465 for results in results_sets:
466 for (success, project, start, finish) in results:
467 self._fetch_times.Set(project, finish - start)
468 self.event_log.AddSync(project, event_log.TASK_SYNC_NETWORK,
469 start, finish, success)
470 # Check for any errors before running any more tasks.
471 # ...we'll let existing jobs finish, though.
472 if not success:
473 ret = False
474 else:
475 fetched.add(project.gitdir)
476 pm.update(msg=project.name)
477 if not ret and opt.fail_fast:
478 break
479 return ret
Doug Andersonfc06ced2011-03-16 15:49:18 -0700480
Mike Frysinger339f2df2021-05-06 00:44:42 -0400481 # We pass the ssh proxy settings via the class. This allows multiprocessing
482 # to pickle it up when spawning children. We can't pass it as an argument
483 # to _FetchProjectList below as multiprocessing is unable to pickle those.
484 Sync.ssh_proxy = None
485
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500486 # NB: Multiprocessing is heavy, so don't spin it up for one job.
Mike Frysinger49de8ef2021-04-09 00:21:02 -0400487 if len(projects_list) == 1 or jobs == 1:
Mike Frysinger339f2df2021-05-06 00:44:42 -0400488 self._FetchInitChild(ssh_proxy)
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500489 if not _ProcessResults(self._FetchProjectList(opt, x) for x in projects_list):
490 ret = False
491 else:
492 # Favor throughput over responsiveness when quiet. It seems that imap()
493 # will yield results in batches relative to chunksize, so even as the
494 # children finish a sync, we won't see the result until one child finishes
495 # ~chunksize jobs. When using a large --jobs with large chunksize, this
496 # can be jarring as there will be a large initial delay where repo looks
497 # like it isn't doing anything and sits at 0%, but then suddenly completes
498 # a lot of jobs all at once. Since this code is more network bound, we
499 # can accept a bit more CPU overhead with a smaller chunksize so that the
500 # user sees more immediate & continuous feedback.
501 if opt.quiet:
502 chunksize = WORKER_BATCH_SIZE
David James89ece422014-01-09 18:51:58 -0800503 else:
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500504 pm.update(inc=0, msg='warming up')
505 chunksize = 4
Raman Tenneti4a478ed2021-11-17 18:38:24 -0800506 with multiprocessing.Pool(jobs, initializer=self._FetchInitChild,
507 initargs=(ssh_proxy,)) as pool:
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500508 results = pool.imap_unordered(
509 functools.partial(self._FetchProjectList, opt),
510 projects_list,
511 chunksize=chunksize)
512 if not _ProcessResults(results):
513 ret = False
514 pool.close()
Roy Lee18afd7f2010-05-09 04:32:08 +0800515
Mike Frysinger339f2df2021-05-06 00:44:42 -0400516 # Cleanup the reference now that we're done with it, and we're going to
517 # release any resources it points to. If we don't, later multiprocessing
518 # usage (e.g. checkouts) will try to pickle and then crash.
519 del Sync.ssh_proxy
520
Shawn O. Pearce68194f42009-04-10 16:48:52 -0700521 pm.end()
Dave Borowitz67700e92012-10-23 15:00:54 -0700522 self._fetch_times.Save()
Dave Borowitz18857212012-10-23 17:02:59 -0700523
LaMont Jonesa46047a2022-04-07 21:57:06 +0000524 if not self.outer_client.manifest.IsArchive:
Mike Frysinger5a033082019-09-23 19:21:20 -0400525 self._GCProjects(projects, opt, err_event)
Julien Campergue335f5ef2013-10-16 11:02:35 +0200526
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500527 return (ret, fetched)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700528
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000529 def _FetchMain(self, opt, args, all_projects, err_event,
530 ssh_proxy, manifest):
Mike Frysingerb4429432021-05-05 20:03:26 -0400531 """The main network fetch loop.
532
533 Args:
534 opt: Program options returned from optparse. See _Options().
535 args: Command line args used to filter out projects.
Peter Kjellerstedtd1776092021-05-19 19:37:23 +0200536 all_projects: List of all projects that should be fetched.
Mike Frysingerb4429432021-05-05 20:03:26 -0400537 err_event: Whether an error was hit while processing.
Mike Frysinger339f2df2021-05-06 00:44:42 -0400538 ssh_proxy: SSH manager for clients & masters.
LaMont Jonesa46047a2022-04-07 21:57:06 +0000539 manifest: The manifest to use.
Peter Kjellerstedtd1776092021-05-19 19:37:23 +0200540
541 Returns:
542 List of all projects that should be checked out.
Mike Frysingerb4429432021-05-05 20:03:26 -0400543 """
LaMont Jonesa46047a2022-04-07 21:57:06 +0000544 rp = manifest.repoProject
Mike Frysingerb4429432021-05-05 20:03:26 -0400545
546 to_fetch = []
547 now = time.time()
548 if _ONE_DAY_S <= (now - rp.LastFetch):
549 to_fetch.append(rp)
550 to_fetch.extend(all_projects)
551 to_fetch.sort(key=self._fetch_times.Get, reverse=True)
552
Mike Frysinger339f2df2021-05-06 00:44:42 -0400553 success, fetched = self._Fetch(to_fetch, opt, err_event, ssh_proxy)
Mike Frysingerb4429432021-05-05 20:03:26 -0400554 if not success:
555 err_event.set()
556
557 _PostRepoFetch(rp, opt.repo_verify)
558 if opt.network_only:
559 # bail out now; the rest touches the working tree
560 if err_event.is_set():
561 print('\nerror: Exited sync due to fetch errors.\n', file=sys.stderr)
562 sys.exit(1)
563 return
564
565 # Iteratively fetch missing and/or nested unregistered submodules
566 previously_missing_set = set()
567 while True:
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000568 self._ReloadManifest(None, manifest)
Mike Frysingerb4429432021-05-05 20:03:26 -0400569 all_projects = self.GetProjects(args,
570 missing_ok=True,
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000571 submodules_ok=opt.fetch_submodules,
572 manifest=manifest,
573 all_manifests=not opt.this_manifest_only)
Mike Frysingerb4429432021-05-05 20:03:26 -0400574 missing = []
575 for project in all_projects:
576 if project.gitdir not in fetched:
577 missing.append(project)
578 if not missing:
579 break
580 # Stop us from non-stopped fetching actually-missing repos: If set of
581 # missing repos has not been changed from last fetch, we break.
582 missing_set = set(p.name for p in missing)
583 if previously_missing_set == missing_set:
584 break
585 previously_missing_set = missing_set
Mike Frysinger339f2df2021-05-06 00:44:42 -0400586 success, new_fetched = self._Fetch(missing, opt, err_event, ssh_proxy)
Mike Frysingerb4429432021-05-05 20:03:26 -0400587 if not success:
588 err_event.set()
589 fetched.update(new_fetched)
590
Peter Kjellerstedtd1776092021-05-19 19:37:23 +0200591 return all_projects
592
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500593 def _CheckoutOne(self, detach_head, force_sync, project):
Xin Li745be2e2019-06-03 11:24:30 -0700594 """Checkout work tree for one project
595
596 Args:
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500597 detach_head: Whether to leave a detached HEAD.
598 force_sync: Force checking out of the repo.
Xin Li745be2e2019-06-03 11:24:30 -0700599 project: Project object for the project to checkout.
Xin Li745be2e2019-06-03 11:24:30 -0700600
601 Returns:
602 Whether the fetch was successful.
603 """
Xin Li745be2e2019-06-03 11:24:30 -0700604 start = time.time()
LaMont Jonesa46047a2022-04-07 21:57:06 +0000605 syncbuf = SyncBuffer(project.manifest.manifestProject.config,
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500606 detach_head=detach_head)
Xin Li745be2e2019-06-03 11:24:30 -0700607 success = False
608 try:
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500609 project.Sync_LocalHalf(syncbuf, force_sync=force_sync)
Mike Frysingerebf04a42021-02-23 20:48:04 -0500610 success = syncbuf.Finish()
Raman Tennetiad8aa692021-04-15 09:20:51 -0700611 except GitError as e:
612 print('error.GitError: Cannot checkout %s: %s' %
613 (project.name, str(e)), file=sys.stderr)
Mike Frysingerebf04a42021-02-23 20:48:04 -0500614 except Exception as e:
615 print('error: Cannot checkout %s: %s: %s' %
616 (project.name, type(e).__name__, str(e)),
617 file=sys.stderr)
618 raise
Xin Li745be2e2019-06-03 11:24:30 -0700619
Mike Frysingerebf04a42021-02-23 20:48:04 -0500620 if not success:
621 print('error: Cannot checkout %s' % (project.name), file=sys.stderr)
622 finish = time.time()
623 return (success, project, start, finish)
Xin Li745be2e2019-06-03 11:24:30 -0700624
Mike Frysingerebf04a42021-02-23 20:48:04 -0500625 def _Checkout(self, all_projects, opt, err_results):
Xin Li745be2e2019-06-03 11:24:30 -0700626 """Checkout projects listed in all_projects
627
628 Args:
629 all_projects: List of all projects that should be checked out.
630 opt: Program options returned from optparse. See _Options().
Mike Frysingerebf04a42021-02-23 20:48:04 -0500631 err_results: A list of strings, paths to git repos where checkout failed.
Xin Li745be2e2019-06-03 11:24:30 -0700632 """
Mike Frysingerebf04a42021-02-23 20:48:04 -0500633 # Only checkout projects with worktrees.
634 all_projects = [x for x in all_projects if x.worktree]
Xin Li745be2e2019-06-03 11:24:30 -0700635
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500636 def _ProcessResults(pool, pm, results):
637 ret = True
Mike Frysingerebf04a42021-02-23 20:48:04 -0500638 for (success, project, start, finish) in results:
639 self.event_log.AddSync(project, event_log.TASK_SYNC_LOCAL,
640 start, finish, success)
641 # Check for any errors before running any more tasks.
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500642 # ...we'll let existing jobs finish, though.
Mike Frysingerebf04a42021-02-23 20:48:04 -0500643 if not success:
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500644 ret = False
Mike Frysingerebf04a42021-02-23 20:48:04 -0500645 err_results.append(project.relpath)
646 if opt.fail_fast:
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500647 if pool:
648 pool.close()
649 return ret
Mike Frysingerebf04a42021-02-23 20:48:04 -0500650 pm.update(msg=project.name)
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500651 return ret
Xin Li745be2e2019-06-03 11:24:30 -0700652
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500653 return self.ExecuteInParallel(
654 opt.jobs_checkout if opt.jobs_checkout else self.jobs,
655 functools.partial(self._CheckoutOne, opt.detach_head, opt.force_sync),
656 all_projects,
657 callback=_ProcessResults,
658 output=Progress('Checking out', len(all_projects), quiet=opt.quiet)) and not err_results
Mike Frysingerebf04a42021-02-23 20:48:04 -0500659
Mike Frysinger5a033082019-09-23 19:21:20 -0400660 def _GCProjects(self, projects, opt, err_event):
Mike Frysinger151701e2021-04-13 15:07:21 -0400661 pm = Progress('Garbage collecting', len(projects), delay=False, quiet=opt.quiet)
Mike Frysinger65af2602021-04-08 22:47:44 -0400662 pm.update(inc=0, msg='prescan')
663
Allen Webb4ee4a452021-10-07 10:42:38 -0500664 tidy_dirs = {}
David James8d201162013-10-11 17:03:19 -0700665 for project in projects:
Mike Frysinger5f2b0452020-02-11 03:23:24 -0500666 # Make sure pruning never kicks in with shared projects.
Mike Frysinger979d5bd2020-02-09 02:28:34 -0500667 if (not project.use_git_worktrees and
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000668 len(project.manifest.GetProjectsWithName(project.name, all_manifests=True)) > 1):
Anders Björklund2a2da802021-01-18 10:32:36 +0100669 if not opt.quiet:
Mike Frysinger65af2602021-04-08 22:47:44 -0400670 print('\r%s: Shared project %s found, disabling pruning.' %
Anders Björklund2a2da802021-01-18 10:32:36 +0100671 (project.relpath, project.name))
Mike Frysinger5f2b0452020-02-11 03:23:24 -0500672 if git_require((2, 7, 0)):
Mike Frysingerf81c72e2020-02-19 15:50:00 -0500673 project.EnableRepositoryExtension('preciousObjects')
Mike Frysinger5f2b0452020-02-11 03:23:24 -0500674 else:
675 # This isn't perfect, but it's the best we can do with old git.
Mike Frysinger65af2602021-04-08 22:47:44 -0400676 print('\r%s: WARNING: shared projects are unreliable when using old '
Mike Frysinger5f2b0452020-02-11 03:23:24 -0500677 'versions of git; please upgrade to git-2.7.0+.'
678 % (project.relpath,),
679 file=sys.stderr)
680 project.config.SetString('gc.pruneExpire', 'never')
Allen Webb669efd02021-10-01 15:25:31 -0500681 project.config.SetString('gc.autoDetach', 'false')
Allen Webb4ee4a452021-10-07 10:42:38 -0500682 # Only call git gc once per objdir, but call pack-refs for the remainder.
683 if project.objdir not in tidy_dirs:
684 tidy_dirs[project.objdir] = (
685 True, # Run a full gc.
686 project.bare_git,
687 )
688 elif project.gitdir not in tidy_dirs:
689 tidy_dirs[project.gitdir] = (
690 False, # Do not run a full gc; just run pack-refs.
691 project.bare_git,
692 )
Mike Frysinger65af2602021-04-08 22:47:44 -0400693
694 cpu_count = os.cpu_count()
Dave Borowitz18857212012-10-23 17:02:59 -0700695 jobs = min(self.jobs, cpu_count)
696
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 Frysinger0c0e9342019-06-13 12:42:39 -0400707 config = {'pack.threads': cpu_count // jobs if cpu_count > jobs else 1}
Dave Borowitz18857212012-10-23 17:02:59 -0700708
709 threads = set()
710 sem = _threading.Semaphore(jobs)
Dave Borowitz18857212012-10-23 17:02:59 -0700711
Allen Webb4ee4a452021-10-07 10:42:38 -0500712 def tidy_up(run_gc, bare_git):
Mike Frysinger65af2602021-04-08 22:47:44 -0400713 pm.start(bare_git._project.name)
Dave Borowitz18857212012-10-23 17:02:59 -0700714 try:
715 try:
Allen Webb4ee4a452021-10-07 10:42:38 -0500716 if run_gc:
717 bare_git.gc('--auto', config=config)
718 else:
719 bare_git.pack_refs(config=config)
Dave Borowitz18857212012-10-23 17:02:59 -0700720 except GitError:
721 err_event.set()
David Pursehouse145e35b2020-02-12 15:40:47 +0900722 except Exception:
Dave Borowitz18857212012-10-23 17:02:59 -0700723 err_event.set()
724 raise
725 finally:
Mike Frysinger65af2602021-04-08 22:47:44 -0400726 pm.finish(bare_git._project.name)
Dave Borowitz18857212012-10-23 17:02:59 -0700727 sem.release()
728
Allen Webb4ee4a452021-10-07 10:42:38 -0500729 for (run_gc, bare_git) in tidy_dirs.values():
Mike Frysingerbe24a542021-02-23 03:24:12 -0500730 if err_event.is_set() and opt.fail_fast:
Dave Borowitz18857212012-10-23 17:02:59 -0700731 break
732 sem.acquire()
Allen Webb4ee4a452021-10-07 10:42:38 -0500733 t = _threading.Thread(target=tidy_up, args=(run_gc, bare_git,))
Dave Borowitz18857212012-10-23 17:02:59 -0700734 t.daemon = True
735 threads.add(t)
736 t.start()
737
738 for t in threads:
739 t.join()
Mike Frysinger65af2602021-04-08 22:47:44 -0400740 pm.end()
Dave Borowitz18857212012-10-23 17:02:59 -0700741
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000742 def _ReloadManifest(self, manifest_name, manifest):
Raman Tennetifeb28912021-05-02 19:47:29 -0700743 """Reload the manfiest from the file specified by the |manifest_name|.
744
745 It unloads the manifest if |manifest_name| is None.
746
747 Args:
748 manifest_name: Manifest file to be reloaded.
LaMont Jonesa46047a2022-04-07 21:57:06 +0000749 manifest: The manifest to use.
Raman Tennetifeb28912021-05-02 19:47:29 -0700750 """
Tim Kilbourn07669002013-03-08 15:02:49 -0800751 if manifest_name:
LaMont Jonesa2ff20d2022-04-07 16:49:06 +0000752 # Override calls Unload already
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000753 manifest.Override(manifest_name)
Tim Kilbourn07669002013-03-08 15:02:49 -0800754 else:
LaMont Jonesa46047a2022-04-07 21:57:06 +0000755 manifest.Unload()
Tim Kilbourn07669002013-03-08 15:02:49 -0800756
LaMont Jonesa46047a2022-04-07 21:57:06 +0000757 def UpdateProjectList(self, opt, manifest):
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000758 """Update the cached projects list for |manifest|
759
760 In a multi-manifest checkout, each manifest has its own project.list.
761
762 Args:
763 opt: Program options returned from optparse. See _Options().
764 manifest: The manifest to use.
765
766 Returns:
767 0: success
768 1: failure
769 """
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700770 new_project_paths = []
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000771 for project in self.GetProjects(None, missing_ok=True, manifest=manifest,
772 all_manifests=False):
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700773 if project.relpath:
774 new_project_paths.append(project.relpath)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700775 file_name = 'project.list'
LaMont Jonesa46047a2022-04-07 21:57:06 +0000776 file_path = os.path.join(manifest.subdir, file_name)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700777 old_project_paths = []
778
779 if os.path.exists(file_path):
Mike Frysinger3164d402019-11-11 05:40:22 -0500780 with open(file_path, 'r') as fd:
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700781 old_project_paths = fd.read().split('\n')
Kuang-che Wu0d9b16d2019-04-06 00:49:47 +0800782 # In reversed order, so subfolders are deleted before parent folder.
783 for path in sorted(old_project_paths, reverse=True):
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700784 if not path:
785 continue
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700786 if path not in new_project_paths:
David Pursehouse8a68ff92012-09-24 12:15:13 +0900787 # If the path has already been deleted, we don't need to do it
LaMont Jonesa46047a2022-04-07 21:57:06 +0000788 gitdir = os.path.join(manifest.topdir, path, '.git')
Dan Willemsen43507912016-09-01 16:26:02 -0700789 if os.path.exists(gitdir):
David Pursehousec1b86a22012-11-14 11:36:51 +0900790 project = Project(
LaMont Jonesa46047a2022-04-07 21:57:06 +0000791 manifest=manifest,
David Pursehouseabdf7502020-02-12 14:58:39 +0900792 name=path,
793 remote=RemoteSpec('origin'),
794 gitdir=gitdir,
795 objdir=gitdir,
Mike Frysingerc0d18662020-02-19 19:19:18 -0500796 use_git_worktrees=os.path.isfile(gitdir),
LaMont Jonesa46047a2022-04-07 21:57:06 +0000797 worktree=os.path.join(manifest.topdir, path),
David Pursehouseabdf7502020-02-12 14:58:39 +0900798 relpath=path,
799 revisionExpr='HEAD',
800 revisionId=None,
801 groups=None)
Mike Frysingerc0d18662020-02-19 19:19:18 -0500802 if not project.DeleteWorktree(
David Pursehouseaa611a22020-02-20 10:47:26 +0900803 quiet=opt.quiet,
804 force=opt.force_remove_dirty):
Mike Frysingera850ca22019-08-07 17:19:24 -0400805 return 1
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700806
Shawn O. Pearce9fb29ce2009-06-04 20:41:02 -0700807 new_project_paths.sort()
Mike Frysinger3164d402019-11-11 05:40:22 -0500808 with open(file_path, 'w') as fd:
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700809 fd.write('\n'.join(new_project_paths))
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700810 fd.write('\n')
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700811 return 0
812
LaMont Jonesa46047a2022-04-07 21:57:06 +0000813 def UpdateCopyLinkfileList(self, manifest):
jiajia tanga590e642021-04-25 20:02:02 +0800814 """Save all dests of copyfile and linkfile, and update them if needed.
815
816 Returns:
817 Whether update was successful.
818 """
819 new_paths = {}
820 new_linkfile_paths = []
821 new_copyfile_paths = []
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000822 for project in self.GetProjects(None, missing_ok=True,
823 manifest=manifest, all_manifests=False):
jiajia tanga590e642021-04-25 20:02:02 +0800824 new_linkfile_paths.extend(x.dest for x in project.linkfiles)
825 new_copyfile_paths.extend(x.dest for x in project.copyfiles)
826
827 new_paths = {
828 'linkfile': new_linkfile_paths,
829 'copyfile': new_copyfile_paths,
830 }
831
832 copylinkfile_name = 'copy-link-files.json'
LaMont Jonesa46047a2022-04-07 21:57:06 +0000833 copylinkfile_path = os.path.join(manifest.subdir, copylinkfile_name)
jiajia tanga590e642021-04-25 20:02:02 +0800834 old_copylinkfile_paths = {}
835
836 if os.path.exists(copylinkfile_path):
837 with open(copylinkfile_path, 'rb') as fp:
838 try:
839 old_copylinkfile_paths = json.load(fp)
Raman Tenneti4a478ed2021-11-17 18:38:24 -0800840 except Exception:
jiajia tanga590e642021-04-25 20:02:02 +0800841 print('error: %s is not a json formatted file.' %
842 copylinkfile_path, file=sys.stderr)
843 platform_utils.remove(copylinkfile_path)
844 return False
845
846 need_remove_files = []
847 need_remove_files.extend(
848 set(old_copylinkfile_paths.get('linkfile', [])) -
849 set(new_linkfile_paths))
850 need_remove_files.extend(
851 set(old_copylinkfile_paths.get('copyfile', [])) -
852 set(new_copyfile_paths))
853
854 for need_remove_file in need_remove_files:
Mike Frysinger9d96f582021-09-28 11:27:24 -0400855 # Try to remove the updated copyfile or linkfile.
856 # So, if the file is not exist, nothing need to do.
857 platform_utils.remove(need_remove_file, missing_ok=True)
jiajia tanga590e642021-04-25 20:02:02 +0800858
859 # Create copy-link-files.json, save dest path of "copyfile" and "linkfile".
860 with open(copylinkfile_path, 'w', encoding='utf-8') as fp:
861 json.dump(new_paths, fp)
862 return True
863
LaMont Jonesa46047a2022-04-07 21:57:06 +0000864 def _SmartSyncSetup(self, opt, smart_sync_manifest_path, manifest):
865 if not manifest.manifest_server:
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400866 print('error: cannot smart sync: no manifest server defined in '
867 'manifest', file=sys.stderr)
868 sys.exit(1)
869
LaMont Jonesa46047a2022-04-07 21:57:06 +0000870 manifest_server = manifest.manifest_server
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400871 if not opt.quiet:
872 print('Using manifest server %s' % manifest_server)
873
David Pursehouseeeff3532020-02-12 11:24:10 +0900874 if '@' not in manifest_server:
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400875 username = None
876 password = None
877 if opt.manifest_server_username and opt.manifest_server_password:
878 username = opt.manifest_server_username
879 password = opt.manifest_server_password
880 else:
881 try:
882 info = netrc.netrc()
883 except IOError:
884 # .netrc file does not exist or could not be opened
885 pass
886 else:
887 try:
888 parse_result = urllib.parse.urlparse(manifest_server)
889 if parse_result.hostname:
890 auth = info.authenticators(parse_result.hostname)
891 if auth:
892 username, _account, password = auth
893 else:
894 print('No credentials found for %s in .netrc'
895 % parse_result.hostname, file=sys.stderr)
896 except netrc.NetrcParseError as e:
897 print('Error parsing .netrc file: %s' % e, file=sys.stderr)
898
899 if (username and password):
900 manifest_server = manifest_server.replace('://', '://%s:%s@' %
901 (username, password),
902 1)
903
904 transport = PersistentTransport(manifest_server)
905 if manifest_server.startswith('persistent-'):
906 manifest_server = manifest_server[len('persistent-'):]
907
908 try:
909 server = xmlrpc.client.Server(manifest_server, transport=transport)
910 if opt.smart_sync:
LaMont Jonesa46047a2022-04-07 21:57:06 +0000911 branch = self._GetBranch(manifest.manifestProject)
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400912
Mike Frysinger56ce3462019-12-04 19:30:48 -0500913 if 'SYNC_TARGET' in os.environ:
Mike Frysingere20da3e2020-03-07 01:53:53 -0500914 target = os.environ['SYNC_TARGET']
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400915 [success, manifest_str] = server.GetApprovedManifest(branch, target)
Mike Frysinger56ce3462019-12-04 19:30:48 -0500916 elif ('TARGET_PRODUCT' in os.environ and
917 'TARGET_BUILD_VARIANT' in os.environ):
Mike Frysingere20da3e2020-03-07 01:53:53 -0500918 target = '%s-%s' % (os.environ['TARGET_PRODUCT'],
919 os.environ['TARGET_BUILD_VARIANT'])
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400920 [success, manifest_str] = server.GetApprovedManifest(branch, target)
921 else:
922 [success, manifest_str] = server.GetApprovedManifest(branch)
923 else:
924 assert(opt.smart_tag)
925 [success, manifest_str] = server.GetManifest(opt.smart_tag)
926
927 if success:
928 manifest_name = os.path.basename(smart_sync_manifest_path)
929 try:
Mike Frysinger3164d402019-11-11 05:40:22 -0500930 with open(smart_sync_manifest_path, 'w') as f:
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400931 f.write(manifest_str)
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400932 except IOError as e:
933 print('error: cannot write manifest to %s:\n%s'
934 % (smart_sync_manifest_path, e),
935 file=sys.stderr)
936 sys.exit(1)
LaMont Jonesa46047a2022-04-07 21:57:06 +0000937 self._ReloadManifest(manifest_name, manifest)
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400938 else:
939 print('error: manifest server RPC call failed: %s' %
940 manifest_str, file=sys.stderr)
941 sys.exit(1)
942 except (socket.error, IOError, xmlrpc.client.Fault) as e:
943 print('error: cannot connect to manifest server %s:\n%s'
LaMont Jonesa46047a2022-04-07 21:57:06 +0000944 % (manifest.manifest_server, e), file=sys.stderr)
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400945 sys.exit(1)
946 except xmlrpc.client.ProtocolError as e:
947 print('error: cannot connect to manifest server %s:\n%d %s'
LaMont Jonesa46047a2022-04-07 21:57:06 +0000948 % (manifest.manifest_server, e.errcode, e.errmsg),
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400949 file=sys.stderr)
950 sys.exit(1)
951
952 return manifest_name
953
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000954 def _UpdateAllManifestProjects(self, opt, mp, manifest_name):
955 """Fetch & update the local manifest project.
956
957 After syncing the manifest project, if the manifest has any sub manifests,
958 those are recursively processed.
959
960 Args:
961 opt: Program options returned from optparse. See _Options().
962 mp: the manifestProject to query.
963 manifest_name: Manifest file to be reloaded.
964 """
965 if not mp.standalone_manifest_url:
966 self._UpdateManifestProject(opt, mp, manifest_name)
967
968 if mp.manifest.submanifests:
969 for submanifest in mp.manifest.submanifests.values():
970 child = submanifest.repo_client.manifest
971 child.manifestProject.SyncWithPossibleInit(
972 submanifest,
973 current_branch_only=self._GetCurrentBranchOnly(opt, child),
974 verbose=opt.verbose,
975 tags=opt.tags,
976 git_event_log=self.git_event_log,
977 )
978 self._UpdateAllManifestProjects(opt, child.manifestProject, None)
979
Mike Frysingerfb527e32019-08-27 02:34:32 -0400980 def _UpdateManifestProject(self, opt, mp, manifest_name):
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000981 """Fetch & update the local manifest project.
982
983 Args:
984 opt: Program options returned from optparse. See _Options().
985 mp: the manifestProject to query.
986 manifest_name: Manifest file to be reloaded.
987 """
Mike Frysingerfb527e32019-08-27 02:34:32 -0400988 if not opt.local_only:
989 start = time.time()
Mike Frysinger521d01b2020-02-17 01:51:49 -0500990 success = mp.Sync_NetworkHalf(quiet=opt.quiet, verbose=opt.verbose,
LaMont Jonesa46047a2022-04-07 21:57:06 +0000991 current_branch_only=self._GetCurrentBranchOnly(opt, mp.manifest),
Erwan Yvindc5c4d12019-06-18 13:49:12 +0200992 force_sync=opt.force_sync,
Mike Frysingerc58ec4d2020-02-17 14:36:08 -0500993 tags=opt.tags,
Mike Frysingerfb527e32019-08-27 02:34:32 -0400994 optimized_fetch=opt.optimized_fetch,
George Engelbrecht9bc283e2020-04-02 12:36:09 -0600995 retry_fetches=opt.retry_fetches,
LaMont Jonesa46047a2022-04-07 21:57:06 +0000996 submodules=mp.manifest.HasSubmodules,
997 clone_filter=mp.manifest.CloneFilter,
998 partial_clone_exclude=mp.manifest.PartialCloneExclude)
Mike Frysingerfb527e32019-08-27 02:34:32 -0400999 finish = time.time()
1000 self.event_log.AddSync(mp, event_log.TASK_SYNC_NETWORK,
1001 start, finish, success)
1002
1003 if mp.HasChanges:
1004 syncbuf = SyncBuffer(mp.config)
1005 start = time.time()
LaMont Jonesa46047a2022-04-07 21:57:06 +00001006 mp.Sync_LocalHalf(syncbuf, submodules=mp.manifest.HasSubmodules)
Mike Frysingerfb527e32019-08-27 02:34:32 -04001007 clean = syncbuf.Finish()
1008 self.event_log.AddSync(mp, event_log.TASK_SYNC_LOCAL,
1009 start, time.time(), clean)
1010 if not clean:
1011 sys.exit(1)
LaMont Jonesa46047a2022-04-07 21:57:06 +00001012 self._ReloadManifest(manifest_name, mp.manifest)
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001013
Mike Frysingerfb527e32019-08-27 02:34:32 -04001014 if opt.jobs is None:
LaMont Jonesa46047a2022-04-07 21:57:06 +00001015 self.jobs = mp.manifest.default.sync_j
Mike Frysingerfb527e32019-08-27 02:34:32 -04001016
Mike Frysingerae6cb082019-08-27 01:10:59 -04001017 def ValidateOptions(self, opt, args):
1018 if opt.force_broken:
1019 print('warning: -f/--force-broken is now the default behavior, and the '
1020 'options are deprecated', file=sys.stderr)
1021 if opt.network_only and opt.detach_head:
1022 self.OptionParser.error('cannot combine -n and -d')
1023 if opt.network_only and opt.local_only:
1024 self.OptionParser.error('cannot combine -n and -l')
1025 if opt.manifest_name and opt.smart_sync:
1026 self.OptionParser.error('cannot combine -m and -s')
1027 if opt.manifest_name and opt.smart_tag:
1028 self.OptionParser.error('cannot combine -m and -t')
1029 if opt.manifest_server_username or opt.manifest_server_password:
1030 if not (opt.smart_sync or opt.smart_tag):
1031 self.OptionParser.error('-u and -p may only be combined with -s or -t')
1032 if None in [opt.manifest_server_username, opt.manifest_server_password]:
1033 self.OptionParser.error('both -u and -p must be given')
1034
Mike Frysinger0531a622021-11-05 15:22:01 -04001035 if opt.prune is None:
1036 opt.prune = True
1037
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001038 def Execute(self, opt, args):
Roy Lee18afd7f2010-05-09 04:32:08 +08001039 if opt.jobs:
1040 self.jobs = opt.jobs
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -07001041 if self.jobs > 1:
1042 soft_limit, _ = _rlimit_nofile()
Mike Frysinger0c0e9342019-06-13 12:42:39 -04001043 self.jobs = min(self.jobs, (soft_limit - 5) // 3)
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -07001044
LaMont Jonesa46047a2022-04-07 21:57:06 +00001045 manifest = self.outer_manifest
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001046 if not opt.outer_manifest:
LaMont Jonesa46047a2022-04-07 21:57:06 +00001047 manifest = self.manifest
1048
Chris Wolfee9dc3b32012-01-26 11:36:18 -05001049 if opt.manifest_name:
LaMont Jonesa46047a2022-04-07 21:57:06 +00001050 manifest.Override(opt.manifest_name)
Shawn O. Pearce3e768c92009-04-10 16:59:36 -07001051
Chirayu Desaia892b102013-06-11 14:18:46 +05301052 manifest_name = opt.manifest_name
David Pursehouse59b41742015-05-07 14:36:09 +09001053 smart_sync_manifest_path = os.path.join(
LaMont Jonesa46047a2022-04-07 21:57:06 +00001054 manifest.manifestProject.worktree, 'smart_sync_override.xml')
Chirayu Desaia892b102013-06-11 14:18:46 +05301055
Xin Lid79a4bc2020-05-20 16:03:45 -07001056 if opt.clone_bundle is None:
LaMont Jonesa46047a2022-04-07 21:57:06 +00001057 opt.clone_bundle = manifest.CloneBundle
Xin Lid79a4bc2020-05-20 16:03:45 -07001058
Victor Boivie08c880d2011-04-19 10:32:52 +02001059 if opt.smart_sync or opt.smart_tag:
LaMont Jonesa46047a2022-04-07 21:57:06 +00001060 manifest_name = self._SmartSyncSetup(opt, smart_sync_manifest_path, manifest)
Mike Frysinger01d6c3c2019-08-27 01:56:43 -04001061 else:
David Pursehouse59b41742015-05-07 14:36:09 +09001062 if os.path.isfile(smart_sync_manifest_path):
1063 try:
Renaud Paquay010fed72016-11-11 14:25:29 -08001064 platform_utils.remove(smart_sync_manifest_path)
David Pursehouse59b41742015-05-07 14:36:09 +09001065 except OSError as e:
1066 print('error: failed to remove existing smart sync override manifest: %s' %
1067 e, file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -07001068
Mike Frysingerc99322a2021-05-04 15:32:43 -04001069 err_event = multiprocessing.Event()
Mike Frysinger5a033082019-09-23 19:21:20 -04001070
LaMont Jonesa46047a2022-04-07 21:57:06 +00001071 rp = manifest.repoProject
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001072 rp.PreSync()
Mike Frysinger23c900f2020-02-29 02:52:44 -05001073 cb = rp.CurrentBranch
1074 if cb:
1075 base = rp.GetBranch(cb).merge
1076 if not base or not base.startswith('refs/heads/'):
1077 print('warning: repo is not tracking a remote branch, so it will not '
Mike Frysinger58ac1672020-03-14 14:35:26 -04001078 'receive updates; run `repo init --repo-rev=stable` to fix.',
Mike Frysinger23c900f2020-02-29 02:52:44 -05001079 file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001080
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001081 for m in self.ManifestList(opt):
1082 mp = m.manifestProject
1083 is_standalone_manifest = bool(mp.standalone_manifest_url)
1084 if not is_standalone_manifest:
1085 mp.PreSync()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001086
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001087 if opt.repo_upgraded:
1088 _PostRepoUpgrade(m, quiet=opt.quiet)
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08001089
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001090 if opt.mp_update:
1091 self._UpdateAllManifestProjects(opt, mp, manifest_name)
1092 else:
Fredrik de Grootcc960972019-11-22 09:04:31 +01001093 print('Skipping update of local manifest project.')
Simran Basib9a1b732015-08-20 12:19:28 -07001094
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001095 superproject_logging_data = {}
1096 self._UpdateProjectsRevisionId(opt, args, superproject_logging_data,
1097 manifest)
Raman Tenneti1fd7bc22021-02-04 14:39:38 -08001098
Simran Basib9a1b732015-08-20 12:19:28 -07001099 if self.gitc_manifest:
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001100 gitc_manifest_projects = self.GetProjects(args, missing_ok=True)
Simran Basib9a1b732015-08-20 12:19:28 -07001101 gitc_projects = []
1102 opened_projects = []
1103 for project in gitc_manifest_projects:
Dan Willemsen5ea32d12015-09-08 13:27:20 -07001104 if project.relpath in self.gitc_manifest.paths and \
1105 self.gitc_manifest.paths[project.relpath].old_revision:
1106 opened_projects.append(project.relpath)
Simran Basib9a1b732015-08-20 12:19:28 -07001107 else:
Dan Willemsen5ea32d12015-09-08 13:27:20 -07001108 gitc_projects.append(project.relpath)
Simran Basib9a1b732015-08-20 12:19:28 -07001109
Dan Willemsen5ea32d12015-09-08 13:27:20 -07001110 if not args:
1111 gitc_projects = None
1112
1113 if gitc_projects != [] and not opt.local_only:
Simran Basib9a1b732015-08-20 12:19:28 -07001114 print('Updating GITC client: %s' % self.gitc_manifest.gitc_client_name)
Dan Willemsen5ea32d12015-09-08 13:27:20 -07001115 manifest = GitcManifest(self.repodir, self.gitc_manifest.gitc_client_name)
1116 if manifest_name:
1117 manifest.Override(manifest_name)
1118 else:
LaMont Jonesa46047a2022-04-07 21:57:06 +00001119 manifest.Override(manifest.manifestFile)
Dan Willemsen5ea32d12015-09-08 13:27:20 -07001120 gitc_utils.generate_gitc_manifest(self.gitc_manifest,
1121 manifest,
Simran Basib9a1b732015-08-20 12:19:28 -07001122 gitc_projects)
1123 print('GITC client successfully synced.')
1124
1125 # The opened projects need to be synced as normal, therefore we
1126 # generate a new args list to represent the opened projects.
Dan Willemsen5ea32d12015-09-08 13:27:20 -07001127 # TODO: make this more reliable -- if there's a project name/path overlap,
1128 # this may choose the wrong project.
LaMont Jonesa46047a2022-04-07 21:57:06 +00001129 args = [os.path.relpath(manifest.paths[path].worktree, os.getcwd())
David Pursehouse3bcd3052017-07-10 22:42:22 +09001130 for path in opened_projects]
Simran Basib9a1b732015-08-20 12:19:28 -07001131 if not args:
1132 return
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001133
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001134 all_projects = self.GetProjects(args,
1135 missing_ok=True,
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001136 submodules_ok=opt.fetch_submodules,
1137 manifest=manifest,
1138 all_manifests=not opt.this_manifest_only)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001139
Mike Frysinger5a033082019-09-23 19:21:20 -04001140 err_network_sync = False
1141 err_update_projects = False
Mike Frysinger5a033082019-09-23 19:21:20 -04001142
LaMont Jonesa46047a2022-04-07 21:57:06 +00001143 self._fetch_times = _FetchTimes(manifest)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -07001144 if not opt.local_only:
Mike Frysinger339f2df2021-05-06 00:44:42 -04001145 with multiprocessing.Manager() as manager:
1146 with ssh.ProxyManager(manager) as ssh_proxy:
1147 # Initialize the socket dir once in the parent.
1148 ssh_proxy.sock()
Peter Kjellerstedtd1776092021-05-19 19:37:23 +02001149 all_projects = self._FetchMain(opt, args, all_projects, err_event,
LaMont Jonesa46047a2022-04-07 21:57:06 +00001150 ssh_proxy, manifest)
Mike Frysinger339f2df2021-05-06 00:44:42 -04001151
1152 if opt.network_only:
1153 return
Mike Frysinger5a033082019-09-23 19:21:20 -04001154
1155 # If we saw an error, exit with code 1 so that other scripts can check.
Mike Frysingerbe24a542021-02-23 03:24:12 -05001156 if err_event.is_set():
Mike Frysinger5a033082019-09-23 19:21:20 -04001157 err_network_sync = True
1158 if opt.fail_fast:
1159 print('\nerror: Exited sync due to fetch errors.\n'
1160 'Local checkouts *not* updated. Resolve network issues & '
1161 'retry.\n'
1162 '`repo sync -l` will update some local checkouts.',
1163 file=sys.stderr)
1164 sys.exit(1)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001165
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001166 for m in self.ManifestList(opt):
1167 if m.IsMirror or m.IsArchive:
1168 # bail out now, we have no working tree
1169 continue
Shawn O. Pearcecd1d7ff2009-06-04 16:15:53 -07001170
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001171 if self.UpdateProjectList(opt, m):
1172 err_event.set()
1173 err_update_projects = True
1174 if opt.fail_fast:
1175 print('\nerror: Local checkouts *not* updated.', file=sys.stderr)
1176 sys.exit(1)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -07001177
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001178 err_update_linkfiles = not self.UpdateCopyLinkfileList(m)
1179 if err_update_linkfiles:
1180 err_event.set()
1181 if opt.fail_fast:
1182 print('\nerror: Local update copyfile or linkfile failed.', file=sys.stderr)
1183 sys.exit(1)
jiajia tanga590e642021-04-25 20:02:02 +08001184
Mike Frysinger5a033082019-09-23 19:21:20 -04001185 err_results = []
Mike Frysingerebf04a42021-02-23 20:48:04 -05001186 # NB: We don't exit here because this is the last step.
1187 err_checkout = not self._Checkout(all_projects, opt, err_results)
1188 if err_checkout:
1189 err_event.set()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001190
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001191 printed_notices = set()
1192 # If there's a notice that's supposed to print at the end of the sync,
1193 # print it now... But avoid printing duplicate messages, and preserve
1194 # order.
1195 for m in sorted(self.ManifestList(opt), key=lambda x: x.path_prefix):
1196 if m.notice and m.notice not in printed_notices:
1197 print(m.notice)
1198 printed_notices.add(m.notice)
Doug Anderson2b8db3c2010-11-01 15:08:06 -07001199
Mike Frysinger5a033082019-09-23 19:21:20 -04001200 # If we saw an error, exit with code 1 so that other scripts can check.
Mike Frysingerbe24a542021-02-23 03:24:12 -05001201 if err_event.is_set():
Mike Frysinger5a033082019-09-23 19:21:20 -04001202 print('\nerror: Unable to fully sync the tree.', file=sys.stderr)
1203 if err_network_sync:
1204 print('error: Downloading network changes failed.', file=sys.stderr)
1205 if err_update_projects:
1206 print('error: Updating local project lists failed.', file=sys.stderr)
jiajia tanga590e642021-04-25 20:02:02 +08001207 if err_update_linkfiles:
1208 print('error: Updating copyfiles or linkfiles failed.', file=sys.stderr)
Mike Frysinger5a033082019-09-23 19:21:20 -04001209 if err_checkout:
1210 print('error: Checking out local projects failed.', file=sys.stderr)
1211 if err_results:
1212 print('Failing repos:\n%s' % '\n'.join(err_results), file=sys.stderr)
1213 print('Try re-running with "-j1 --fail-fast" to exit at the first error.',
1214 file=sys.stderr)
1215 sys.exit(1)
1216
Raman Tenneti7954de12021-07-28 14:36:49 -07001217 # Log the previous sync analysis state from the config.
Raman Tenneti6448a4f2021-09-13 17:40:07 -07001218 self.git_event_log.LogDataConfigEvents(mp.config.GetSyncAnalysisStateData(),
1219 'previous_sync_state')
Raman Tenneti7954de12021-07-28 14:36:49 -07001220
1221 # Update and log with the new sync analysis state.
1222 mp.config.UpdateSyncAnalysisState(opt, superproject_logging_data)
Raman Tenneti6448a4f2021-09-13 17:40:07 -07001223 self.git_event_log.LogDataConfigEvents(mp.config.GetSyncAnalysisStateData(),
1224 'current_sync_state')
Raman Tenneti7954de12021-07-28 14:36:49 -07001225
Mike Frysingere19d9e12020-02-12 11:23:32 -05001226 if not opt.quiet:
1227 print('repo sync has finished successfully.')
1228
David Pursehouse819827a2020-02-12 15:20:19 +09001229
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -07001230def _PostRepoUpgrade(manifest, quiet=False):
Mike Frysingerfdeb20f2021-11-14 03:53:04 -05001231 # Link the docs for the internal .repo/ layout for people
1232 link = os.path.join(manifest.repodir, 'internal-fs-layout.md')
1233 if not platform_utils.islink(link):
1234 target = os.path.join('repo', 'docs', 'internal-fs-layout.md')
1235 try:
1236 platform_utils.symlink(target, link)
Raman Tenneti4a478ed2021-11-17 18:38:24 -08001237 except Exception:
Mike Frysingerfdeb20f2021-11-14 03:53:04 -05001238 pass
1239
Conley Owens094cdbe2014-01-30 15:09:59 -08001240 wrapper = Wrapper()
Conley Owensc9129d92012-10-01 16:12:28 -07001241 if wrapper.NeedSetupGnuPG():
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -07001242 wrapper.SetupGnuPG(quiet)
Conley Owensf2fe2d92014-01-29 13:53:43 -08001243 for project in manifest.projects:
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001244 if project.Exists:
1245 project.PostRepoUpgrade()
1246
David Pursehouse819827a2020-02-12 15:20:19 +09001247
Mike Frysingerc58ec4d2020-02-17 14:36:08 -05001248def _PostRepoFetch(rp, repo_verify=True, verbose=False):
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001249 if rp.HasChanges:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001250 print('info: A new version of repo is available', file=sys.stderr)
Mike Frysinger347f9ed2021-03-15 14:58:52 -04001251 wrapper = Wrapper()
1252 try:
1253 rev = rp.bare_git.describe(rp.GetRevisionId())
1254 except GitError:
1255 rev = None
1256 _, new_rev = wrapper.check_repo_rev(rp.gitdir, rev, repo_verify=repo_verify)
1257 # See if we're held back due to missing signed tag.
1258 current_revid = rp.bare_git.rev_parse('HEAD')
1259 new_revid = rp.bare_git.rev_parse('--verify', new_rev)
1260 if current_revid != new_revid:
1261 # We want to switch to the new rev, but also not trash any uncommitted
1262 # changes. This helps with local testing/hacking.
1263 # If a local change has been made, we will throw that away.
1264 # We also have to make sure this will switch to an older commit if that's
1265 # the latest tag in order to support release rollback.
1266 try:
1267 rp.work_git.reset('--keep', new_rev)
1268 except GitError as e:
1269 sys.exit(str(e))
Sarah Owenscecd1d82012-11-01 22:59:27 -07001270 print('info: Restarting repo with latest version', file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001271 raise RepoChangedException(['--repo-upgraded'])
1272 else:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001273 print('warning: Skipped upgrade to unverified version', file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001274 else:
1275 if verbose:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001276 print('repo version %s is current' % rp.work_git.describe(HEAD),
1277 file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001278
David Pursehouse819827a2020-02-12 15:20:19 +09001279
Dave Borowitz67700e92012-10-23 15:00:54 -07001280class _FetchTimes(object):
Dave Borowitzd9478582012-10-23 16:35:39 -07001281 _ALPHA = 0.5
1282
Dave Borowitz67700e92012-10-23 15:00:54 -07001283 def __init__(self, manifest):
Anthony King85b24ac2014-05-06 15:57:48 +01001284 self._path = os.path.join(manifest.repodir, '.repo_fetchtimes.json')
Dave Borowitz67700e92012-10-23 15:00:54 -07001285 self._times = None
Dave Borowitzd9478582012-10-23 16:35:39 -07001286 self._seen = set()
Dave Borowitz67700e92012-10-23 15:00:54 -07001287
1288 def Get(self, project):
1289 self._Load()
1290 return self._times.get(project.name, _ONE_DAY_S)
1291
1292 def Set(self, project, t):
Dave Borowitzd9478582012-10-23 16:35:39 -07001293 self._Load()
1294 name = project.name
1295 old = self._times.get(name, t)
1296 self._seen.add(name)
1297 a = self._ALPHA
David Pursehouse54a4e602020-02-12 14:31:05 +09001298 self._times[name] = (a * t) + ((1 - a) * old)
Dave Borowitz67700e92012-10-23 15:00:54 -07001299
1300 def _Load(self):
1301 if self._times is None:
1302 try:
Mike Frysinger3164d402019-11-11 05:40:22 -05001303 with open(self._path) as f:
Anthony King85b24ac2014-05-06 15:57:48 +01001304 self._times = json.load(f)
Anthony King85b24ac2014-05-06 15:57:48 +01001305 except (IOError, ValueError):
Mike Frysinger9d96f582021-09-28 11:27:24 -04001306 platform_utils.remove(self._path, missing_ok=True)
Anthony King85b24ac2014-05-06 15:57:48 +01001307 self._times = {}
Dave Borowitz67700e92012-10-23 15:00:54 -07001308
1309 def Save(self):
1310 if self._times is None:
1311 return
Dave Borowitzd9478582012-10-23 16:35:39 -07001312
1313 to_delete = []
1314 for name in self._times:
1315 if name not in self._seen:
1316 to_delete.append(name)
1317 for name in to_delete:
1318 del self._times[name]
1319
Dave Borowitz67700e92012-10-23 15:00:54 -07001320 try:
Mike Frysinger3164d402019-11-11 05:40:22 -05001321 with open(self._path, 'w') as f:
Anthony King85b24ac2014-05-06 15:57:48 +01001322 json.dump(self._times, f, indent=2)
Anthony King85b24ac2014-05-06 15:57:48 +01001323 except (IOError, TypeError):
Mike Frysinger9d96f582021-09-28 11:27:24 -04001324 platform_utils.remove(self._path, missing_ok=True)
Dan Willemsen0745bb22015-08-17 13:41:45 -07001325
1326# This is a replacement for xmlrpc.client.Transport using urllib2
1327# and supporting persistent-http[s]. It cannot change hosts from
1328# request to request like the normal transport, the real url
1329# is passed during initialization.
David Pursehouse819827a2020-02-12 15:20:19 +09001330
1331
Dan Willemsen0745bb22015-08-17 13:41:45 -07001332class PersistentTransport(xmlrpc.client.Transport):
1333 def __init__(self, orig_host):
1334 self.orig_host = orig_host
1335
1336 def request(self, host, handler, request_body, verbose=False):
1337 with GetUrlCookieFile(self.orig_host, not verbose) as (cookiefile, proxy):
1338 # Python doesn't understand cookies with the #HttpOnly_ prefix
1339 # Since we're only using them for HTTP, copy the file temporarily,
1340 # stripping those prefixes away.
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001341 if cookiefile:
Collin Fijalkoviche1191b32020-02-18 10:57:32 -08001342 tmpcookiefile = tempfile.NamedTemporaryFile(mode='w')
David Pursehouse4c5f74e2015-10-02 11:10:10 +09001343 tmpcookiefile.write("# HTTP Cookie File")
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001344 try:
1345 with open(cookiefile) as f:
1346 for line in f:
1347 if line.startswith("#HttpOnly_"):
1348 line = line[len("#HttpOnly_"):]
1349 tmpcookiefile.write(line)
1350 tmpcookiefile.flush()
Dan Willemsen0745bb22015-08-17 13:41:45 -07001351
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001352 cookiejar = cookielib.MozillaCookieJar(tmpcookiefile.name)
David Pursehouseb1ad2192015-09-30 10:35:43 +09001353 try:
1354 cookiejar.load()
1355 except cookielib.LoadError:
1356 cookiejar = cookielib.CookieJar()
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001357 finally:
1358 tmpcookiefile.close()
1359 else:
1360 cookiejar = cookielib.CookieJar()
Dan Willemsen0745bb22015-08-17 13:41:45 -07001361
1362 proxyhandler = urllib.request.ProxyHandler
1363 if proxy:
1364 proxyhandler = urllib.request.ProxyHandler({
1365 "http": proxy,
David Pursehouse54a4e602020-02-12 14:31:05 +09001366 "https": proxy})
Dan Willemsen0745bb22015-08-17 13:41:45 -07001367
1368 opener = urllib.request.build_opener(
1369 urllib.request.HTTPCookieProcessor(cookiejar),
1370 proxyhandler)
1371
1372 url = urllib.parse.urljoin(self.orig_host, handler)
1373 parse_results = urllib.parse.urlparse(url)
1374
1375 scheme = parse_results.scheme
1376 if scheme == 'persistent-http':
1377 scheme = 'http'
1378 if scheme == 'persistent-https':
1379 # If we're proxying through persistent-https, use http. The
1380 # proxy itself will do the https.
1381 if proxy:
1382 scheme = 'http'
1383 else:
1384 scheme = 'https'
1385
1386 # Parse out any authentication information using the base class
1387 host, extra_headers, _ = self.get_host_info(parse_results.netloc)
1388
1389 url = urllib.parse.urlunparse((
1390 scheme,
1391 host,
1392 parse_results.path,
1393 parse_results.params,
1394 parse_results.query,
1395 parse_results.fragment))
1396
1397 request = urllib.request.Request(url, request_body)
1398 if extra_headers is not None:
1399 for (name, header) in extra_headers:
1400 request.add_header(name, header)
1401 request.add_header('Content-Type', 'text/xml')
1402 try:
1403 response = opener.open(request)
1404 except urllib.error.HTTPError as e:
1405 if e.code == 501:
1406 # We may have been redirected through a login process
1407 # but our POST turned into a GET. Retry.
1408 response = opener.open(request)
1409 else:
1410 raise
1411
1412 p, u = xmlrpc.client.getparser()
1413 while 1:
1414 data = response.read(1024)
1415 if not data:
1416 break
1417 p.feed(data)
1418 p.close()
1419 return u.close()
1420
1421 def close(self):
1422 pass