blob: 9e9c8f021bf4ae698a80d98c530c42bbe308eb0a [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
LaMont Jones891e8f72022-09-08 20:17:58 +000024import shutil
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -070025import socket
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070026import sys
Dan Willemsen0745bb22015-08-17 13:41:45 -070027import tempfile
Shawn O. Pearcef6906872009-04-18 10:49:00 -070028import time
Mike Frysingeracf63b22019-06-13 02:24:21 -040029import urllib.error
30import urllib.parse
31import urllib.request
Mike Frysinger5951e302022-05-20 23:34:44 -040032import xml.parsers.expat
Mike Frysingeracf63b22019-06-13 02:24:21 -040033import xmlrpc.client
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070034
Roy Lee18afd7f2010-05-09 04:32:08 +080035try:
36 import threading as _threading
37except ImportError:
38 import dummy_threading as _threading
39
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -070040try:
41 import resource
David Pursehouse819827a2020-02-12 15:20:19 +090042
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -070043 def _rlimit_nofile():
44 return resource.getrlimit(resource.RLIMIT_NOFILE)
45except ImportError:
46 def _rlimit_nofile():
47 return (256, 256)
48
David Rileye0684ad2017-04-05 00:02:59 -070049import event_log
Mike Frysinger347f9ed2021-03-15 14:58:52 -040050from git_command import git_require
David Pursehouseba7bc732015-08-20 16:55:42 +090051from git_config import GetUrlCookieFile
David Pursehoused94aaef2012-09-07 09:52:04 +090052from git_refs import R_HEADS, HEAD
Raman Tenneti6a872c92021-01-14 19:17:50 -080053import git_superproject
Simran Basibdb52712015-08-10 13:23:23 -070054import gitc_utils
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -070055from project import Project
56from project import RemoteSpec
Mike Frysinger355f4392022-07-20 17:15:29 -040057from command import Command, DEFAULT_LOCAL_JOBS, MirrorSafeCommand, WORKER_BATCH_SIZE
Raman Tenneti1fd7bc22021-02-04 14:39:38 -080058from error import RepoChangedException, GitError, ManifestParseError
Renaud Paquaya65adf72016-11-03 10:37:53 -070059import platform_utils
Shawn O. Pearce350cde42009-04-16 11:21:18 -070060from project import SyncBuffer
Shawn O. Pearce68194f42009-04-10 16:48:52 -070061from progress import Progress
LaMont Jones891e8f72022-09-08 20:17:58 +000062from repo_trace import IsTrace, Trace
Mike Frysinger19e409c2021-05-05 19:44:35 -040063import ssh
Conley Owens094cdbe2014-01-30 15:09:59 -080064from wrapper import Wrapper
Dan Willemsen5ea32d12015-09-08 13:27:20 -070065from manifest_xml import GitcManifest
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070066
Dave Borowitz67700e92012-10-23 15:00:54 -070067_ONE_DAY_S = 24 * 60 * 60
LaMont Jones891e8f72022-09-08 20:17:58 +000068# Env var to implicitly turn off object backups.
69REPO_BACKUP_OBJECTS = 'REPO_BACKUP_OBJECTS'
70
71_BACKUP_OBJECTS = os.environ.get(REPO_BACKUP_OBJECTS) != '0'
Dave Borowitz67700e92012-10-23 15:00:54 -070072
David Pursehouse819827a2020-02-12 15:20:19 +090073
Shawn O. Pearcec95583b2009-03-03 17:47:06 -080074class Sync(Command, MirrorSafeCommand):
Mike Frysinger4f210542021-06-14 16:05:19 -040075 COMMON = True
LaMont Jonesbdcba7d2022-04-11 22:50:11 +000076 MULTI_MANIFEST_SUPPORT = True
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070077 helpSummary = "Update working tree to the latest revision"
78 helpUsage = """
79%prog [<project>...]
80"""
81 helpDescription = """
82The '%prog' command synchronizes local project directories
83with the remote repositories specified in the manifest. If a local
84project does not yet exist, it will clone a new local directory from
85the remote repository and set up tracking branches as specified in
86the manifest. If the local project already exists, '%prog'
87will update the remote branches and rebase any new local changes
88on top of the new remote changes.
89
90'%prog' will synchronize all projects listed at the command
91line. Projects can be specified either by name, or by a relative
92or absolute path to the project's local directory. If no projects
93are specified, '%prog' will synchronize all projects listed in
94the manifest.
Shawn O. Pearce3e768c92009-04-10 16:59:36 -070095
96The -d/--detach option can be used to switch specified projects
97back to the manifest revision. This option is especially helpful
98if the project is currently on a topic branch, but the manifest
99revision is temporarily needed.
Shawn O. Pearceeb7af872009-04-21 08:02:04 -0700100
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700101The -s/--smart-sync option can be used to sync to a known good
102build as specified by the manifest-server element in the current
Victor Boivie08c880d2011-04-19 10:32:52 +0200103manifest. The -t/--smart-tag option is similar and allows you to
104specify a custom tag/label.
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700105
David Pursehousecf76b1b2012-09-14 10:31:42 +0900106The -u/--manifest-server-username and -p/--manifest-server-password
107options can be used to specify a username and password to authenticate
108with the manifest server when using the -s or -t option.
109
110If -u and -p are not specified when using the -s or -t option, '%prog'
111will attempt to read authentication credentials for the manifest server
112from the user's .netrc file.
113
114'%prog' will not use authentication credentials from -u/-p or .netrc
115if the manifest server specified in the manifest file already includes
116credentials.
117
Mike Frysingerd9e5cf02019-08-26 03:12:55 -0400118By default, all projects will be synced. The --fail-fast option can be used
Mike Frysinger7ae210a2020-05-24 14:56:52 -0400119to halt syncing as soon as possible when the first project fails to sync.
Andrei Warkentin5df6de02010-07-02 17:58:31 -0500120
Kevin Degiabaa7f32014-11-12 11:27:45 -0700121The --force-sync option can be used to overwrite existing git
122directories if they have previously been linked to a different
Roger Shimizuac29ac32020-06-06 02:33:40 +0900123object directory. WARNING: This may cause data to be lost since
Kevin Degiabaa7f32014-11-12 11:27:45 -0700124refs may be removed when overwriting.
125
Oleksii Okolielovd3c0f592018-12-17 19:23:44 -0500126The --force-remove-dirty option can be used to remove previously used
127projects with uncommitted changes. WARNING: This may cause data to be
128lost since uncommitted changes may be removed with projects that no longer
129exist in the manifest.
130
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -0700131The --no-clone-bundle option disables any attempt to use
132$URL/clone.bundle to bootstrap a new Git repository from a
133resumeable bundle file on a content delivery network. This
134may be necessary if there are problems with the local Python
135HTTP client or proxy configuration, but the Git binary works.
136
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800137The --fetch-submodules option enables fetching Git submodules
138of a project from server.
139
David Pursehousef2fad612015-01-29 14:36:28 +0900140The -c/--current-branch option can be used to only fetch objects that
141are on the branch specified by a project's revision.
142
David Pursehouseb1553542014-09-04 21:28:09 +0900143The --optimized-fetch option can be used to only fetch projects that
144are fixed to a sha1 revision if the sha1 revision does not already
145exist locally.
146
David Pursehouse74cfd272015-10-14 10:50:15 +0900147The --prune option can be used to remove any refs that no longer
148exist on the remote.
149
Mike Frysingerb8f7bb02018-10-10 01:05:11 -0400150# SSH Connections
Shawn O. Pearceeb7af872009-04-21 08:02:04 -0700151
152If at least one project remote URL uses an SSH connection (ssh://,
153git+ssh://, or user@host:path syntax) repo will automatically
154enable the SSH ControlMaster option when connecting to that host.
155This feature permits other projects in the same '%prog' session to
156reuse the same SSH tunnel, saving connection setup overheads.
157
158To disable this behavior on UNIX platforms, set the GIT_SSH
159environment variable to 'ssh'. For example:
160
161 export GIT_SSH=ssh
162 %prog
163
Mike Frysingerb8f7bb02018-10-10 01:05:11 -0400164# Compatibility
Shawn O. Pearceeb7af872009-04-21 08:02:04 -0700165
166This feature is automatically disabled on Windows, due to the lack
167of UNIX domain socket support.
168
169This feature is not compatible with url.insteadof rewrites in the
170user's ~/.gitconfig. '%prog' is currently not able to perform the
171rewrite early enough to establish the ControlMaster tunnel.
172
173If the remote SSH daemon is Gerrit Code Review, version 2.0.10 or
174later is required to fix a server side protocol bug.
175
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700176"""
Mike Frysinger355f4392022-07-20 17:15:29 -0400177 # A value of 0 means we want parallel jobs, but we'll determine the default
178 # value later on.
179 PARALLEL_JOBS = 0
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',
Mike Frysinger355f4392022-07-20 17:15:29 -0400183 help='number of network jobs to run in parallel (defaults to --jobs or 1)')
Mike Frysinger49de8ef2021-04-09 00:21:02 -0400184 p.add_option('--jobs-checkout', default=None, type=int, metavar='JOBS',
Mike Frysinger355f4392022-07-20 17:15:29 -0400185 help='number of local checkout jobs to run in parallel (defaults to --jobs or '
186 f'{DEFAULT_LOCAL_JOBS})')
Mike Frysinger49de8ef2021-04-09 00:21:02 -0400187
Andrei Warkentin5df6de02010-07-02 17:58:31 -0500188 p.add_option('-f', '--force-broken',
Stefan Müller-Klieser46702ed2019-08-30 10:20:15 +0200189 dest='force_broken', action='store_true',
Mike Frysingerd9e5cf02019-08-26 03:12:55 -0400190 help='obsolete option (to be deleted in the future)')
191 p.add_option('--fail-fast',
192 dest='fail_fast', action='store_true',
193 help='stop syncing after first error is hit')
Kevin Degiabaa7f32014-11-12 11:27:45 -0700194 p.add_option('--force-sync',
195 dest='force_sync', action='store_true',
196 help="overwrite an existing git directory if it needs to "
197 "point to a different object directory. WARNING: this "
198 "may cause loss of data")
Oleksii Okolielovd3c0f592018-12-17 19:23:44 -0500199 p.add_option('--force-remove-dirty',
200 dest='force_remove_dirty', action='store_true',
201 help="force remove projects with uncommitted modifications if "
202 "projects no longer exist in the manifest. "
203 "WARNING: this may cause loss of data")
David Pursehouse8f62fb72012-11-14 12:09:38 +0900204 p.add_option('-l', '--local-only',
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700205 dest='local_only', action='store_true',
206 help="only update working tree, don't fetch")
David Pursehouse54a4e602020-02-12 14:31:05 +0900207 p.add_option('--no-manifest-update', '--nmu',
Fredrik de Grootcc960972019-11-22 09:04:31 +0100208 dest='mp_update', action='store_false', default='true',
209 help='use the existing manifest checkout as-is. '
210 '(do not update to the latest revision)')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900211 p.add_option('-n', '--network-only',
Shawn O. Pearce96fdcef2009-04-10 16:29:20 -0700212 dest='network_only', action='store_true',
213 help="fetch only, don't update working tree")
David Pursehouse8f62fb72012-11-14 12:09:38 +0900214 p.add_option('-d', '--detach',
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700215 dest='detach_head', action='store_true',
216 help='detach projects back to manifest revision')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900217 p.add_option('-c', '--current-branch',
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -0700218 dest='current_branch_only', action='store_true',
219 help='fetch only current branch from server')
Mike Frysinger73561142021-05-03 01:10:09 -0400220 p.add_option('--no-current-branch',
221 dest='current_branch_only', action='store_false',
222 help='fetch all branches from server')
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500223 p.add_option('-m', '--manifest-name',
224 dest='manifest_name',
225 help='temporary manifest to use for this sync', metavar='NAME.xml')
Xin Lid79a4bc2020-05-20 16:03:45 -0700226 p.add_option('--clone-bundle', action='store_true',
227 help='enable use of /clone.bundle on HTTP/HTTPS')
228 p.add_option('--no-clone-bundle', dest='clone_bundle', action='store_false',
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -0700229 help='disable use of /clone.bundle on HTTP/HTTPS')
Conley Owens8d070cf2012-11-06 13:14:31 -0800230 p.add_option('-u', '--manifest-server-username', action='store',
231 dest='manifest_server_username',
232 help='username to authenticate with the manifest server')
233 p.add_option('-p', '--manifest-server-password', action='store',
234 dest='manifest_server_password',
235 help='password to authenticate with the manifest server')
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800236 p.add_option('--fetch-submodules',
237 dest='fetch_submodules', action='store_true',
238 help='fetch submodules from server')
Raman Tenneti6a872c92021-01-14 19:17:50 -0800239 p.add_option('--use-superproject', action='store_true',
Raman Tenneti62517292021-11-01 14:49:16 -0700240 help='use the manifest superproject to sync projects; implies -c')
Raman Tenneti23ea7542021-05-07 14:01:54 -0700241 p.add_option('--no-use-superproject', action='store_false',
242 dest='use_superproject',
243 help='disable use of manifest superprojects')
Mike Frysinger2273f462021-11-05 15:10:33 -0400244 p.add_option('--tags', action='store_true',
Mike Frysingerd68ed632021-05-03 01:21:35 -0400245 help='fetch tags')
Mitchel Humpherys597868b2012-10-29 10:18:34 -0700246 p.add_option('--no-tags',
Mike Frysingerd68ed632021-05-03 01:21:35 -0400247 dest='tags', action='store_false',
Mike Frysinger2273f462021-11-05 15:10:33 -0400248 help="don't fetch tags (default)")
David Pursehouseb1553542014-09-04 21:28:09 +0900249 p.add_option('--optimized-fetch',
250 dest='optimized_fetch', action='store_true',
251 help='only fetch projects fixed to sha1 if revision does not exist locally')
George Engelbrecht9bc283e2020-04-02 12:36:09 -0600252 p.add_option('--retry-fetches',
253 default=0, action='store', type='int',
254 help='number of times to retry fetches on transient errors')
Mike Frysinger0531a622021-11-05 15:22:01 -0400255 p.add_option('--prune', action='store_true',
256 help='delete refs that no longer exist on the remote (default)')
257 p.add_option('--no-prune', dest='prune', action='store_false',
258 help='do not delete refs that no longer exist on the remote')
Nico Sallembien6623b212010-05-11 12:57:01 -0700259 if show_smart:
260 p.add_option('-s', '--smart-sync',
261 dest='smart_sync', action='store_true',
David Pursehouse79fba682016-04-13 18:03:00 +0900262 help='smart sync using manifest from the latest known good build')
Victor Boivie08c880d2011-04-19 10:32:52 +0200263 p.add_option('-t', '--smart-tag',
264 dest='smart_tag', action='store',
265 help='smart sync using manifest from a known tag')
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700266
Shawn O. Pearcefd89b672009-04-18 11:28:57 -0700267 g = p.add_option_group('repo Version options')
268 g.add_option('--no-repo-verify',
Mike Frysingerc58ec4d2020-02-17 14:36:08 -0500269 dest='repo_verify', default=True, action='store_false',
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700270 help='do not verify repo source code')
Shawn O. Pearcefd89b672009-04-18 11:28:57 -0700271 g.add_option('--repo-upgraded',
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800272 dest='repo_upgraded', action='store_true',
Shawn O. Pearce2a1ccb22009-04-10 16:51:53 -0700273 help=SUPPRESS_HELP)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700274
LaMont Jonesa46047a2022-04-07 21:57:06 +0000275 def _GetBranch(self, manifest_project):
276 """Returns the branch name for getting the approved smartsync manifest.
277
278 Args:
279 manifest_project: the manifestProject to query.
280 """
281 b = manifest_project.GetBranch(manifest_project.CurrentBranch)
Raman Tenneti8d43dea2021-02-07 16:30:27 -0800282 branch = b.merge
283 if branch.startswith(R_HEADS):
284 branch = branch[len(R_HEADS):]
285 return branch
286
LaMont Jonesa46047a2022-04-07 21:57:06 +0000287 def _GetCurrentBranchOnly(self, opt, manifest):
Daniel Anderssond52ca422022-04-01 12:55:38 +0200288 """Returns whether current-branch or use-superproject options are enabled.
289
LaMont Jonesa46047a2022-04-07 21:57:06 +0000290 Args:
291 opt: Program options returned from optparse. See _Options().
292 manifest: The manifest to use.
293
Daniel Anderssond52ca422022-04-01 12:55:38 +0200294 Returns:
295 True if a superproject is requested, otherwise the value of the
296 current_branch option (True, False or None).
297 """
LaMont Jonesa46047a2022-04-07 21:57:06 +0000298 return git_superproject.UseSuperproject(opt.use_superproject, manifest) or opt.current_branch_only
Raman Tenneti2ae44d72021-03-23 15:12:27 -0700299
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000300 def _UpdateProjectsRevisionId(self, opt, args, superproject_logging_data,
301 manifest):
302 """Update revisionId of projects with the commit hash from the superproject.
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800303
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000304 This function updates each project's revisionId with the commit hash from
305 the superproject. It writes the updated manifest into a file and reloads
306 the manifest from it. When appropriate, sub manifests are also processed.
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800307
308 Args:
309 opt: Program options returned from optparse. See _Options().
310 args: Arguments to pass to GetProjects. See the GetProjects
311 docstring for details.
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000312 superproject_logging_data: A dictionary of superproject data to log.
LaMont Jonesa46047a2022-04-07 21:57:06 +0000313 manifest: The manifest to use.
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800314 """
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000315 have_superproject = manifest.superproject or any(
316 m.superproject for m in manifest.all_children)
317 if not have_superproject:
318 return
319
LaMont Jonesff6b1da2022-06-01 21:03:34 +0000320 if opt.local_only and manifest.superproject:
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000321 manifest_path = manifest.superproject.manifest_path
Raman Tennetiae86a462021-07-27 08:54:59 -0700322 if manifest_path:
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000323 self._ReloadManifest(manifest_path, manifest)
324 return
Raman Tennetiae86a462021-07-27 08:54:59 -0700325
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800326 all_projects = self.GetProjects(args,
327 missing_ok=True,
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000328 submodules_ok=opt.fetch_submodules,
329 manifest=manifest,
330 all_manifests=not opt.this_manifest_only)
331
332 per_manifest = collections.defaultdict(list)
333 manifest_paths = {}
334 if opt.this_manifest_only:
335 per_manifest[manifest.path_prefix] = all_projects
Raman Tenneti784e16f2021-06-11 17:29:45 -0700336 else:
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000337 for p in all_projects:
338 per_manifest[p.manifest.path_prefix].append(p)
339
340 superproject_logging_data = {}
341 need_unload = False
342 for m in self.ManifestList(opt):
343 if not m.path_prefix in per_manifest:
344 continue
345 use_super = git_superproject.UseSuperproject(opt.use_superproject, m)
346 if superproject_logging_data:
347 superproject_logging_data['multimanifest'] = True
348 superproject_logging_data.update(
349 superproject=use_super,
350 haslocalmanifests=bool(m.HasLocalManifests),
351 hassuperprojecttag=bool(m.superproject),
352 )
353 if use_super and (m.IsMirror or m.IsArchive):
354 # Don't use superproject, because we have no working tree.
355 use_super = False
356 superproject_logging_data['superproject'] = False
357 superproject_logging_data['noworktree'] = True
358 if opt.use_superproject is not False:
359 print(f'{m.path_prefix}: not using superproject because there is no '
360 'working tree.')
361
362 if not use_super:
363 continue
364 m.superproject.SetQuiet(opt.quiet)
365 print_messages = git_superproject.PrintMessages(opt.use_superproject, m)
366 m.superproject.SetPrintMessages(print_messages)
367 update_result = m.superproject.UpdateProjectsRevisionId(
368 per_manifest[m.path_prefix], git_event_log=self.git_event_log)
369 manifest_path = update_result.manifest_path
370 superproject_logging_data['updatedrevisionid'] = bool(manifest_path)
371 if manifest_path:
372 m.SetManifestOverride(manifest_path)
373 need_unload = True
374 else:
375 if print_messages:
376 print(f'{m.path_prefix}: warning: Update of revisionId from '
377 'superproject has failed, repo sync will not use superproject '
378 'to fetch the source. ',
379 'Please resync with the --no-use-superproject option to avoid '
380 'this repo warning.',
381 file=sys.stderr)
382 if update_result.fatal and opt.use_superproject is not None:
383 sys.exit(1)
384 if need_unload:
385 m.outer_client.manifest.Unload()
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800386
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500387 def _FetchProjectList(self, opt, projects):
388 """Main function of the fetch worker.
389
390 The projects we're given share the same underlying git object store, so we
391 have to fetch them in serial.
Roy Lee18afd7f2010-05-09 04:32:08 +0800392
David James8d201162013-10-11 17:03:19 -0700393 Delegates most of the work to _FetchHelper.
394
395 Args:
396 opt: Program options returned from optparse. See _Options().
397 projects: Projects to fetch.
David James8d201162013-10-11 17:03:19 -0700398 """
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500399 return [self._FetchOne(opt, x) for x in projects]
David James8d201162013-10-11 17:03:19 -0700400
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500401 def _FetchOne(self, opt, project):
David James8d201162013-10-11 17:03:19 -0700402 """Fetch git objects for a single project.
403
David Pursehousec1b86a22012-11-14 11:36:51 +0900404 Args:
405 opt: Program options returned from optparse. See _Options().
406 project: Project object for the project to fetch.
David James8d201162013-10-11 17:03:19 -0700407
408 Returns:
409 Whether the fetch was successful.
David Pursehousec1b86a22012-11-14 11:36:51 +0900410 """
David Rileye0684ad2017-04-05 00:02:59 -0700411 start = time.time()
412 success = False
Mike Frysinger7b586f22021-02-23 18:38:39 -0500413 buf = io.StringIO()
David Pursehousec1b86a22012-11-14 11:36:51 +0900414 try:
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500415 success = project.Sync_NetworkHalf(
416 quiet=opt.quiet,
417 verbose=opt.verbose,
418 output_redir=buf,
LaMont Jonesa46047a2022-04-07 21:57:06 +0000419 current_branch_only=self._GetCurrentBranchOnly(opt, project.manifest),
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500420 force_sync=opt.force_sync,
421 clone_bundle=opt.clone_bundle,
LaMont Jonesa46047a2022-04-07 21:57:06 +0000422 tags=opt.tags, archive=project.manifest.IsArchive,
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500423 optimized_fetch=opt.optimized_fetch,
424 retry_fetches=opt.retry_fetches,
425 prune=opt.prune,
Mike Frysinger339f2df2021-05-06 00:44:42 -0400426 ssh_proxy=self.ssh_proxy,
LaMont Jonesa46047a2022-04-07 21:57:06 +0000427 clone_filter=project.manifest.CloneFilter,
428 partial_clone_exclude=project.manifest.PartialCloneExclude)
Doug Andersonfc06ced2011-03-16 15:49:18 -0700429
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500430 output = buf.getvalue()
Mike Frysinger58929732021-07-02 00:29:35 -0400431 if (opt.verbose or not success) and output:
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500432 print('\n' + output.rstrip())
Doug Andersonfc06ced2011-03-16 15:49:18 -0700433
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500434 if not success:
435 print('error: Cannot fetch %s from %s'
436 % (project.name, project.remote.url),
437 file=sys.stderr)
Raman Tennetiad8aa692021-04-15 09:20:51 -0700438 except GitError as e:
439 print('error.GitError: Cannot fetch %s' % str(e), file=sys.stderr)
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500440 except Exception as e:
441 print('error: Cannot fetch %s (%s: %s)'
442 % (project.name, type(e).__name__, str(e)), file=sys.stderr)
443 raise
Mike Frysinger7b586f22021-02-23 18:38:39 -0500444
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500445 finish = time.time()
446 return (success, project, start, finish)
David James8d201162013-10-11 17:03:19 -0700447
Mike Frysinger339f2df2021-05-06 00:44:42 -0400448 @classmethod
449 def _FetchInitChild(cls, ssh_proxy):
450 cls.ssh_proxy = ssh_proxy
451
452 def _Fetch(self, projects, opt, err_event, ssh_proxy):
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500453 ret = True
454
Mike Frysinger355f4392022-07-20 17:15:29 -0400455 jobs = opt.jobs_network
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700456 fetched = set()
Mike Frysinger151701e2021-04-13 15:07:21 -0400457 pm = Progress('Fetching', len(projects), delay=False, quiet=opt.quiet)
Roy Lee18afd7f2010-05-09 04:32:08 +0800458
David James89ece422014-01-09 18:51:58 -0800459 objdir_project_map = dict()
460 for project in projects:
461 objdir_project_map.setdefault(project.objdir, []).append(project)
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500462 projects_list = list(objdir_project_map.values())
David James8d201162013-10-11 17:03:19 -0700463
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500464 def _ProcessResults(results_sets):
465 ret = True
466 for results in results_sets:
467 for (success, project, start, finish) in results:
468 self._fetch_times.Set(project, finish - start)
469 self.event_log.AddSync(project, event_log.TASK_SYNC_NETWORK,
470 start, finish, success)
471 # Check for any errors before running any more tasks.
472 # ...we'll let existing jobs finish, though.
473 if not success:
474 ret = False
475 else:
476 fetched.add(project.gitdir)
477 pm.update(msg=project.name)
478 if not ret and opt.fail_fast:
479 break
480 return ret
Doug Andersonfc06ced2011-03-16 15:49:18 -0700481
Mike Frysinger339f2df2021-05-06 00:44:42 -0400482 # We pass the ssh proxy settings via the class. This allows multiprocessing
483 # to pickle it up when spawning children. We can't pass it as an argument
484 # to _FetchProjectList below as multiprocessing is unable to pickle those.
485 Sync.ssh_proxy = None
486
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500487 # NB: Multiprocessing is heavy, so don't spin it up for one job.
Mike Frysinger49de8ef2021-04-09 00:21:02 -0400488 if len(projects_list) == 1 or jobs == 1:
Mike Frysinger339f2df2021-05-06 00:44:42 -0400489 self._FetchInitChild(ssh_proxy)
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500490 if not _ProcessResults(self._FetchProjectList(opt, x) for x in projects_list):
491 ret = False
492 else:
493 # Favor throughput over responsiveness when quiet. It seems that imap()
494 # will yield results in batches relative to chunksize, so even as the
495 # children finish a sync, we won't see the result until one child finishes
496 # ~chunksize jobs. When using a large --jobs with large chunksize, this
497 # can be jarring as there will be a large initial delay where repo looks
498 # like it isn't doing anything and sits at 0%, but then suddenly completes
499 # a lot of jobs all at once. Since this code is more network bound, we
500 # can accept a bit more CPU overhead with a smaller chunksize so that the
501 # user sees more immediate & continuous feedback.
502 if opt.quiet:
503 chunksize = WORKER_BATCH_SIZE
David James89ece422014-01-09 18:51:58 -0800504 else:
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500505 pm.update(inc=0, msg='warming up')
506 chunksize = 4
Raman Tenneti4a478ed2021-11-17 18:38:24 -0800507 with multiprocessing.Pool(jobs, initializer=self._FetchInitChild,
508 initargs=(ssh_proxy,)) as pool:
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500509 results = pool.imap_unordered(
510 functools.partial(self._FetchProjectList, opt),
511 projects_list,
512 chunksize=chunksize)
513 if not _ProcessResults(results):
514 ret = False
515 pool.close()
Roy Lee18afd7f2010-05-09 04:32:08 +0800516
Mike Frysinger339f2df2021-05-06 00:44:42 -0400517 # Cleanup the reference now that we're done with it, and we're going to
518 # release any resources it points to. If we don't, later multiprocessing
519 # usage (e.g. checkouts) will try to pickle and then crash.
520 del Sync.ssh_proxy
521
Shawn O. Pearce68194f42009-04-10 16:48:52 -0700522 pm.end()
Dave Borowitz67700e92012-10-23 15:00:54 -0700523 self._fetch_times.Save()
Dave Borowitz18857212012-10-23 17:02:59 -0700524
LaMont Jonesa46047a2022-04-07 21:57:06 +0000525 if not self.outer_client.manifest.IsArchive:
Mike Frysinger5a033082019-09-23 19:21:20 -0400526 self._GCProjects(projects, opt, err_event)
Julien Campergue335f5ef2013-10-16 11:02:35 +0200527
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500528 return (ret, fetched)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700529
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000530 def _FetchMain(self, opt, args, all_projects, err_event,
531 ssh_proxy, manifest):
Mike Frysingerb4429432021-05-05 20:03:26 -0400532 """The main network fetch loop.
533
534 Args:
535 opt: Program options returned from optparse. See _Options().
536 args: Command line args used to filter out projects.
Peter Kjellerstedtd1776092021-05-19 19:37:23 +0200537 all_projects: List of all projects that should be fetched.
Mike Frysingerb4429432021-05-05 20:03:26 -0400538 err_event: Whether an error was hit while processing.
Mike Frysinger339f2df2021-05-06 00:44:42 -0400539 ssh_proxy: SSH manager for clients & masters.
LaMont Jonesa46047a2022-04-07 21:57:06 +0000540 manifest: The manifest to use.
Peter Kjellerstedtd1776092021-05-19 19:37:23 +0200541
542 Returns:
543 List of all projects that should be checked out.
Mike Frysingerb4429432021-05-05 20:03:26 -0400544 """
LaMont Jonesa46047a2022-04-07 21:57:06 +0000545 rp = manifest.repoProject
Mike Frysingerb4429432021-05-05 20:03:26 -0400546
547 to_fetch = []
548 now = time.time()
549 if _ONE_DAY_S <= (now - rp.LastFetch):
550 to_fetch.append(rp)
551 to_fetch.extend(all_projects)
552 to_fetch.sort(key=self._fetch_times.Get, reverse=True)
553
Mike Frysinger339f2df2021-05-06 00:44:42 -0400554 success, fetched = self._Fetch(to_fetch, opt, err_event, ssh_proxy)
Mike Frysingerb4429432021-05-05 20:03:26 -0400555 if not success:
556 err_event.set()
557
558 _PostRepoFetch(rp, opt.repo_verify)
559 if opt.network_only:
560 # bail out now; the rest touches the working tree
561 if err_event.is_set():
562 print('\nerror: Exited sync due to fetch errors.\n', file=sys.stderr)
563 sys.exit(1)
564 return
565
566 # Iteratively fetch missing and/or nested unregistered submodules
567 previously_missing_set = set()
568 while True:
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000569 self._ReloadManifest(None, manifest)
Mike Frysingerb4429432021-05-05 20:03:26 -0400570 all_projects = self.GetProjects(args,
571 missing_ok=True,
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000572 submodules_ok=opt.fetch_submodules,
573 manifest=manifest,
574 all_manifests=not opt.this_manifest_only)
Mike Frysingerb4429432021-05-05 20:03:26 -0400575 missing = []
576 for project in all_projects:
577 if project.gitdir not in fetched:
578 missing.append(project)
579 if not missing:
580 break
581 # Stop us from non-stopped fetching actually-missing repos: If set of
582 # missing repos has not been changed from last fetch, we break.
583 missing_set = set(p.name for p in missing)
584 if previously_missing_set == missing_set:
585 break
586 previously_missing_set = missing_set
Mike Frysinger339f2df2021-05-06 00:44:42 -0400587 success, new_fetched = self._Fetch(missing, opt, err_event, ssh_proxy)
Mike Frysingerb4429432021-05-05 20:03:26 -0400588 if not success:
589 err_event.set()
590 fetched.update(new_fetched)
591
Peter Kjellerstedtd1776092021-05-19 19:37:23 +0200592 return all_projects
593
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500594 def _CheckoutOne(self, detach_head, force_sync, project):
Xin Li745be2e2019-06-03 11:24:30 -0700595 """Checkout work tree for one project
596
597 Args:
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500598 detach_head: Whether to leave a detached HEAD.
599 force_sync: Force checking out of the repo.
Xin Li745be2e2019-06-03 11:24:30 -0700600 project: Project object for the project to checkout.
Xin Li745be2e2019-06-03 11:24:30 -0700601
602 Returns:
603 Whether the fetch was successful.
604 """
Xin Li745be2e2019-06-03 11:24:30 -0700605 start = time.time()
LaMont Jonesa46047a2022-04-07 21:57:06 +0000606 syncbuf = SyncBuffer(project.manifest.manifestProject.config,
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500607 detach_head=detach_head)
Xin Li745be2e2019-06-03 11:24:30 -0700608 success = False
609 try:
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500610 project.Sync_LocalHalf(syncbuf, force_sync=force_sync)
Mike Frysingerebf04a42021-02-23 20:48:04 -0500611 success = syncbuf.Finish()
Raman Tennetiad8aa692021-04-15 09:20:51 -0700612 except GitError as e:
613 print('error.GitError: Cannot checkout %s: %s' %
614 (project.name, str(e)), file=sys.stderr)
Mike Frysingerebf04a42021-02-23 20:48:04 -0500615 except Exception as e:
616 print('error: Cannot checkout %s: %s: %s' %
617 (project.name, type(e).__name__, str(e)),
618 file=sys.stderr)
619 raise
Xin Li745be2e2019-06-03 11:24:30 -0700620
Mike Frysingerebf04a42021-02-23 20:48:04 -0500621 if not success:
622 print('error: Cannot checkout %s' % (project.name), file=sys.stderr)
623 finish = time.time()
624 return (success, project, start, finish)
Xin Li745be2e2019-06-03 11:24:30 -0700625
Mike Frysingerebf04a42021-02-23 20:48:04 -0500626 def _Checkout(self, all_projects, opt, err_results):
Xin Li745be2e2019-06-03 11:24:30 -0700627 """Checkout projects listed in all_projects
628
629 Args:
630 all_projects: List of all projects that should be checked out.
631 opt: Program options returned from optparse. See _Options().
Mike Frysingerebf04a42021-02-23 20:48:04 -0500632 err_results: A list of strings, paths to git repos where checkout failed.
Xin Li745be2e2019-06-03 11:24:30 -0700633 """
Mike Frysingerebf04a42021-02-23 20:48:04 -0500634 # Only checkout projects with worktrees.
635 all_projects = [x for x in all_projects if x.worktree]
Xin Li745be2e2019-06-03 11:24:30 -0700636
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500637 def _ProcessResults(pool, pm, results):
638 ret = True
Mike Frysingerebf04a42021-02-23 20:48:04 -0500639 for (success, project, start, finish) in results:
640 self.event_log.AddSync(project, event_log.TASK_SYNC_LOCAL,
641 start, finish, success)
642 # Check for any errors before running any more tasks.
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500643 # ...we'll let existing jobs finish, though.
Mike Frysingerebf04a42021-02-23 20:48:04 -0500644 if not success:
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500645 ret = False
Mike Frysingerebf04a42021-02-23 20:48:04 -0500646 err_results.append(project.relpath)
647 if opt.fail_fast:
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500648 if pool:
649 pool.close()
650 return ret
Mike Frysingerebf04a42021-02-23 20:48:04 -0500651 pm.update(msg=project.name)
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500652 return ret
Xin Li745be2e2019-06-03 11:24:30 -0700653
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500654 return self.ExecuteInParallel(
Mike Frysinger355f4392022-07-20 17:15:29 -0400655 opt.jobs_checkout,
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500656 functools.partial(self._CheckoutOne, opt.detach_head, opt.force_sync),
657 all_projects,
658 callback=_ProcessResults,
659 output=Progress('Checking out', len(all_projects), quiet=opt.quiet)) and not err_results
Mike Frysingerebf04a42021-02-23 20:48:04 -0500660
LaMont Jonesaefa4d32022-09-15 18:24:56 +0000661 def _backup_cruft(self, bare_git):
662 """Save a copy of any cruft from `git gc`."""
663 # Find any cruft packs in the current gitdir, and save them.
664 # b/221065125 (repo sync complains that objects are missing). This does
665 # not prevent that state, but makes it so that the missing objects are
666 # available.
667 objdir = bare_git._project.objdir
668 pack_dir = os.path.join(objdir, 'pack')
669 bak_dir = os.path.join(objdir, '.repo', 'pack.bak')
670 if not _BACKUP_OBJECTS or not platform_utils.isdir(pack_dir):
671 return
672 saved = []
673 files = set(platform_utils.listdir(pack_dir))
674 to_backup = []
675 for f in files:
676 base, ext = os.path.splitext(f)
677 if base + '.mtimes' in files:
678 to_backup.append(f)
679 if to_backup:
680 os.makedirs(bak_dir, exist_ok=True)
681 for fname in to_backup:
682 bak_fname = os.path.join(bak_dir, fname)
683 if not os.path.exists(bak_fname):
684 saved.append(fname)
685 # Use a tmp file so that we are sure of a complete copy.
686 shutil.copy(os.path.join(pack_dir, fname), bak_fname + '.tmp')
687 shutil.move(bak_fname + '.tmp', bak_fname)
688 if saved:
689 Trace('%s saved %s', bare_git._project.name, ' '.join(saved))
690
Mike Frysinger5a033082019-09-23 19:21:20 -0400691 def _GCProjects(self, projects, opt, err_event):
Mike Frysinger151701e2021-04-13 15:07:21 -0400692 pm = Progress('Garbage collecting', len(projects), delay=False, quiet=opt.quiet)
Mike Frysinger65af2602021-04-08 22:47:44 -0400693 pm.update(inc=0, msg='prescan')
694
Allen Webb4ee4a452021-10-07 10:42:38 -0500695 tidy_dirs = {}
David James8d201162013-10-11 17:03:19 -0700696 for project in projects:
LaMont Jones48ea25c2022-05-20 10:35:04 +0000697 # Make sure pruning never kicks in with shared projects that do not use
698 # alternates to avoid corruption.
Mike Frysinger979d5bd2020-02-09 02:28:34 -0500699 if (not project.use_git_worktrees and
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000700 len(project.manifest.GetProjectsWithName(project.name, all_manifests=True)) > 1):
LaMont Jones48ea25c2022-05-20 10:35:04 +0000701 if project.UseAlternates:
702 # Undo logic set by previous versions of repo.
703 project.config.SetString('extensions.preciousObjects', None)
704 project.config.SetString('gc.pruneExpire', None)
Mike Frysinger5f2b0452020-02-11 03:23:24 -0500705 else:
LaMont Jones48ea25c2022-05-20 10:35:04 +0000706 if not opt.quiet:
707 print('\r%s: Shared project %s found, disabling pruning.' %
708 (project.relpath, project.name))
709 if git_require((2, 7, 0)):
710 project.EnableRepositoryExtension('preciousObjects')
711 else:
712 # This isn't perfect, but it's the best we can do with old git.
713 print('\r%s: WARNING: shared projects are unreliable when using old '
714 'versions of git; please upgrade to git-2.7.0+.'
715 % (project.relpath,),
716 file=sys.stderr)
717 project.config.SetString('gc.pruneExpire', 'never')
Allen Webb669efd02021-10-01 15:25:31 -0500718 project.config.SetString('gc.autoDetach', 'false')
Allen Webb4ee4a452021-10-07 10:42:38 -0500719 # Only call git gc once per objdir, but call pack-refs for the remainder.
720 if project.objdir not in tidy_dirs:
721 tidy_dirs[project.objdir] = (
722 True, # Run a full gc.
723 project.bare_git,
724 )
725 elif project.gitdir not in tidy_dirs:
726 tidy_dirs[project.gitdir] = (
727 False, # Do not run a full gc; just run pack-refs.
728 project.bare_git,
729 )
Mike Frysinger65af2602021-04-08 22:47:44 -0400730
Mike Frysinger355f4392022-07-20 17:15:29 -0400731 jobs = opt.jobs
Dave Borowitz18857212012-10-23 17:02:59 -0700732
LaMont Jones891e8f72022-09-08 20:17:58 +0000733 gc_args = ('--auto', '--cruft')
734 pack_refs_args = ()
Dave Borowitz18857212012-10-23 17:02:59 -0700735 if jobs < 2:
Allen Webb4ee4a452021-10-07 10:42:38 -0500736 for (run_gc, bare_git) in tidy_dirs.values():
Mike Frysinger65af2602021-04-08 22:47:44 -0400737 pm.update(msg=bare_git._project.name)
LaMont Jones891e8f72022-09-08 20:17:58 +0000738
Allen Webb4ee4a452021-10-07 10:42:38 -0500739 if run_gc:
LaMont Jones891e8f72022-09-08 20:17:58 +0000740 bare_git.gc(*gc_args)
Allen Webb4ee4a452021-10-07 10:42:38 -0500741 else:
LaMont Jones891e8f72022-09-08 20:17:58 +0000742 bare_git.pack_refs(*pack_refs_args)
LaMont Jonesaefa4d32022-09-15 18:24:56 +0000743 self._backup_cruft(bare_git)
Mike Frysinger65af2602021-04-08 22:47:44 -0400744 pm.end()
Dave Borowitz18857212012-10-23 17:02:59 -0700745 return
746
Mike Frysinger355f4392022-07-20 17:15:29 -0400747 cpu_count = os.cpu_count()
Mike Frysinger0c0e9342019-06-13 12:42:39 -0400748 config = {'pack.threads': cpu_count // jobs if cpu_count > jobs else 1}
Dave Borowitz18857212012-10-23 17:02:59 -0700749
750 threads = set()
751 sem = _threading.Semaphore(jobs)
Dave Borowitz18857212012-10-23 17:02:59 -0700752
Allen Webb4ee4a452021-10-07 10:42:38 -0500753 def tidy_up(run_gc, bare_git):
Mike Frysinger65af2602021-04-08 22:47:44 -0400754 pm.start(bare_git._project.name)
Dave Borowitz18857212012-10-23 17:02:59 -0700755 try:
756 try:
Allen Webb4ee4a452021-10-07 10:42:38 -0500757 if run_gc:
LaMont Jones891e8f72022-09-08 20:17:58 +0000758 bare_git.gc(*gc_args, config=config)
Allen Webb4ee4a452021-10-07 10:42:38 -0500759 else:
LaMont Jones891e8f72022-09-08 20:17:58 +0000760 bare_git.pack_refs(*pack_refs_args, config=config)
Dave Borowitz18857212012-10-23 17:02:59 -0700761 except GitError:
762 err_event.set()
David Pursehouse145e35b2020-02-12 15:40:47 +0900763 except Exception:
Dave Borowitz18857212012-10-23 17:02:59 -0700764 err_event.set()
765 raise
766 finally:
LaMont Jonesaefa4d32022-09-15 18:24:56 +0000767 self._backup_cruft(bare_git)
Mike Frysinger65af2602021-04-08 22:47:44 -0400768 pm.finish(bare_git._project.name)
Dave Borowitz18857212012-10-23 17:02:59 -0700769 sem.release()
770
Allen Webb4ee4a452021-10-07 10:42:38 -0500771 for (run_gc, bare_git) in tidy_dirs.values():
Mike Frysingerbe24a542021-02-23 03:24:12 -0500772 if err_event.is_set() and opt.fail_fast:
Dave Borowitz18857212012-10-23 17:02:59 -0700773 break
774 sem.acquire()
Allen Webb4ee4a452021-10-07 10:42:38 -0500775 t = _threading.Thread(target=tidy_up, args=(run_gc, bare_git,))
Dave Borowitz18857212012-10-23 17:02:59 -0700776 t.daemon = True
777 threads.add(t)
778 t.start()
779
780 for t in threads:
781 t.join()
Mike Frysinger65af2602021-04-08 22:47:44 -0400782 pm.end()
Dave Borowitz18857212012-10-23 17:02:59 -0700783
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000784 def _ReloadManifest(self, manifest_name, manifest):
Raman Tennetifeb28912021-05-02 19:47:29 -0700785 """Reload the manfiest from the file specified by the |manifest_name|.
786
787 It unloads the manifest if |manifest_name| is None.
788
789 Args:
790 manifest_name: Manifest file to be reloaded.
LaMont Jonesa46047a2022-04-07 21:57:06 +0000791 manifest: The manifest to use.
Raman Tennetifeb28912021-05-02 19:47:29 -0700792 """
Tim Kilbourn07669002013-03-08 15:02:49 -0800793 if manifest_name:
LaMont Jonesa2ff20d2022-04-07 16:49:06 +0000794 # Override calls Unload already
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000795 manifest.Override(manifest_name)
Tim Kilbourn07669002013-03-08 15:02:49 -0800796 else:
LaMont Jonesa46047a2022-04-07 21:57:06 +0000797 manifest.Unload()
Tim Kilbourn07669002013-03-08 15:02:49 -0800798
LaMont Jonesa46047a2022-04-07 21:57:06 +0000799 def UpdateProjectList(self, opt, manifest):
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000800 """Update the cached projects list for |manifest|
801
802 In a multi-manifest checkout, each manifest has its own project.list.
803
804 Args:
805 opt: Program options returned from optparse. See _Options().
806 manifest: The manifest to use.
807
808 Returns:
809 0: success
810 1: failure
811 """
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700812 new_project_paths = []
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000813 for project in self.GetProjects(None, missing_ok=True, manifest=manifest,
814 all_manifests=False):
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700815 if project.relpath:
816 new_project_paths.append(project.relpath)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700817 file_name = 'project.list'
LaMont Jonesa46047a2022-04-07 21:57:06 +0000818 file_path = os.path.join(manifest.subdir, file_name)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700819 old_project_paths = []
820
821 if os.path.exists(file_path):
Mike Frysinger3164d402019-11-11 05:40:22 -0500822 with open(file_path, 'r') as fd:
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700823 old_project_paths = fd.read().split('\n')
Kuang-che Wu0d9b16d2019-04-06 00:49:47 +0800824 # In reversed order, so subfolders are deleted before parent folder.
825 for path in sorted(old_project_paths, reverse=True):
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700826 if not path:
827 continue
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700828 if path not in new_project_paths:
David Pursehouse8a68ff92012-09-24 12:15:13 +0900829 # If the path has already been deleted, we don't need to do it
LaMont Jonesa46047a2022-04-07 21:57:06 +0000830 gitdir = os.path.join(manifest.topdir, path, '.git')
Dan Willemsen43507912016-09-01 16:26:02 -0700831 if os.path.exists(gitdir):
David Pursehousec1b86a22012-11-14 11:36:51 +0900832 project = Project(
LaMont Jonesa46047a2022-04-07 21:57:06 +0000833 manifest=manifest,
David Pursehouseabdf7502020-02-12 14:58:39 +0900834 name=path,
835 remote=RemoteSpec('origin'),
836 gitdir=gitdir,
837 objdir=gitdir,
Mike Frysingerc0d18662020-02-19 19:19:18 -0500838 use_git_worktrees=os.path.isfile(gitdir),
LaMont Jonesa46047a2022-04-07 21:57:06 +0000839 worktree=os.path.join(manifest.topdir, path),
David Pursehouseabdf7502020-02-12 14:58:39 +0900840 relpath=path,
841 revisionExpr='HEAD',
842 revisionId=None,
843 groups=None)
Mike Frysingerc0d18662020-02-19 19:19:18 -0500844 if not project.DeleteWorktree(
David Pursehouseaa611a22020-02-20 10:47:26 +0900845 quiet=opt.quiet,
846 force=opt.force_remove_dirty):
Mike Frysingera850ca22019-08-07 17:19:24 -0400847 return 1
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700848
Shawn O. Pearce9fb29ce2009-06-04 20:41:02 -0700849 new_project_paths.sort()
Mike Frysinger3164d402019-11-11 05:40:22 -0500850 with open(file_path, 'w') as fd:
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700851 fd.write('\n'.join(new_project_paths))
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700852 fd.write('\n')
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700853 return 0
854
LaMont Jonesa46047a2022-04-07 21:57:06 +0000855 def UpdateCopyLinkfileList(self, manifest):
jiajia tanga590e642021-04-25 20:02:02 +0800856 """Save all dests of copyfile and linkfile, and update them if needed.
857
858 Returns:
859 Whether update was successful.
860 """
861 new_paths = {}
862 new_linkfile_paths = []
863 new_copyfile_paths = []
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000864 for project in self.GetProjects(None, missing_ok=True,
865 manifest=manifest, all_manifests=False):
jiajia tanga590e642021-04-25 20:02:02 +0800866 new_linkfile_paths.extend(x.dest for x in project.linkfiles)
867 new_copyfile_paths.extend(x.dest for x in project.copyfiles)
868
869 new_paths = {
870 'linkfile': new_linkfile_paths,
871 'copyfile': new_copyfile_paths,
872 }
873
874 copylinkfile_name = 'copy-link-files.json'
LaMont Jonesa46047a2022-04-07 21:57:06 +0000875 copylinkfile_path = os.path.join(manifest.subdir, copylinkfile_name)
jiajia tanga590e642021-04-25 20:02:02 +0800876 old_copylinkfile_paths = {}
877
878 if os.path.exists(copylinkfile_path):
879 with open(copylinkfile_path, 'rb') as fp:
880 try:
881 old_copylinkfile_paths = json.load(fp)
Raman Tenneti4a478ed2021-11-17 18:38:24 -0800882 except Exception:
jiajia tanga590e642021-04-25 20:02:02 +0800883 print('error: %s is not a json formatted file.' %
884 copylinkfile_path, file=sys.stderr)
885 platform_utils.remove(copylinkfile_path)
886 return False
887
888 need_remove_files = []
889 need_remove_files.extend(
890 set(old_copylinkfile_paths.get('linkfile', [])) -
891 set(new_linkfile_paths))
892 need_remove_files.extend(
893 set(old_copylinkfile_paths.get('copyfile', [])) -
894 set(new_copyfile_paths))
895
896 for need_remove_file in need_remove_files:
Mike Frysinger9d96f582021-09-28 11:27:24 -0400897 # Try to remove the updated copyfile or linkfile.
898 # So, if the file is not exist, nothing need to do.
899 platform_utils.remove(need_remove_file, missing_ok=True)
jiajia tanga590e642021-04-25 20:02:02 +0800900
901 # Create copy-link-files.json, save dest path of "copyfile" and "linkfile".
902 with open(copylinkfile_path, 'w', encoding='utf-8') as fp:
903 json.dump(new_paths, fp)
904 return True
905
LaMont Jonesa46047a2022-04-07 21:57:06 +0000906 def _SmartSyncSetup(self, opt, smart_sync_manifest_path, manifest):
907 if not manifest.manifest_server:
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400908 print('error: cannot smart sync: no manifest server defined in '
909 'manifest', file=sys.stderr)
910 sys.exit(1)
911
LaMont Jonesa46047a2022-04-07 21:57:06 +0000912 manifest_server = manifest.manifest_server
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400913 if not opt.quiet:
914 print('Using manifest server %s' % manifest_server)
915
David Pursehouseeeff3532020-02-12 11:24:10 +0900916 if '@' not in manifest_server:
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400917 username = None
918 password = None
919 if opt.manifest_server_username and opt.manifest_server_password:
920 username = opt.manifest_server_username
921 password = opt.manifest_server_password
922 else:
923 try:
924 info = netrc.netrc()
925 except IOError:
926 # .netrc file does not exist or could not be opened
927 pass
928 else:
929 try:
930 parse_result = urllib.parse.urlparse(manifest_server)
931 if parse_result.hostname:
932 auth = info.authenticators(parse_result.hostname)
933 if auth:
934 username, _account, password = auth
935 else:
936 print('No credentials found for %s in .netrc'
937 % parse_result.hostname, file=sys.stderr)
938 except netrc.NetrcParseError as e:
939 print('Error parsing .netrc file: %s' % e, file=sys.stderr)
940
941 if (username and password):
942 manifest_server = manifest_server.replace('://', '://%s:%s@' %
943 (username, password),
944 1)
945
946 transport = PersistentTransport(manifest_server)
947 if manifest_server.startswith('persistent-'):
948 manifest_server = manifest_server[len('persistent-'):]
949
950 try:
951 server = xmlrpc.client.Server(manifest_server, transport=transport)
952 if opt.smart_sync:
LaMont Jonesa46047a2022-04-07 21:57:06 +0000953 branch = self._GetBranch(manifest.manifestProject)
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400954
Mike Frysinger56ce3462019-12-04 19:30:48 -0500955 if 'SYNC_TARGET' in os.environ:
Mike Frysingere20da3e2020-03-07 01:53:53 -0500956 target = os.environ['SYNC_TARGET']
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400957 [success, manifest_str] = server.GetApprovedManifest(branch, target)
Mike Frysinger56ce3462019-12-04 19:30:48 -0500958 elif ('TARGET_PRODUCT' in os.environ and
959 'TARGET_BUILD_VARIANT' in os.environ):
Mike Frysingere20da3e2020-03-07 01:53:53 -0500960 target = '%s-%s' % (os.environ['TARGET_PRODUCT'],
961 os.environ['TARGET_BUILD_VARIANT'])
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400962 [success, manifest_str] = server.GetApprovedManifest(branch, target)
963 else:
964 [success, manifest_str] = server.GetApprovedManifest(branch)
965 else:
966 assert(opt.smart_tag)
967 [success, manifest_str] = server.GetManifest(opt.smart_tag)
968
969 if success:
970 manifest_name = os.path.basename(smart_sync_manifest_path)
971 try:
Mike Frysinger3164d402019-11-11 05:40:22 -0500972 with open(smart_sync_manifest_path, 'w') as f:
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400973 f.write(manifest_str)
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400974 except IOError as e:
975 print('error: cannot write manifest to %s:\n%s'
976 % (smart_sync_manifest_path, e),
977 file=sys.stderr)
978 sys.exit(1)
LaMont Jonesa46047a2022-04-07 21:57:06 +0000979 self._ReloadManifest(manifest_name, manifest)
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400980 else:
981 print('error: manifest server RPC call failed: %s' %
982 manifest_str, file=sys.stderr)
983 sys.exit(1)
984 except (socket.error, IOError, xmlrpc.client.Fault) as e:
985 print('error: cannot connect to manifest server %s:\n%s'
LaMont Jonesa46047a2022-04-07 21:57:06 +0000986 % (manifest.manifest_server, e), file=sys.stderr)
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400987 sys.exit(1)
988 except xmlrpc.client.ProtocolError as e:
989 print('error: cannot connect to manifest server %s:\n%d %s'
LaMont Jonesa46047a2022-04-07 21:57:06 +0000990 % (manifest.manifest_server, e.errcode, e.errmsg),
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400991 file=sys.stderr)
992 sys.exit(1)
993
994 return manifest_name
995
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000996 def _UpdateAllManifestProjects(self, opt, mp, manifest_name):
997 """Fetch & update the local manifest project.
998
999 After syncing the manifest project, if the manifest has any sub manifests,
1000 those are recursively processed.
1001
1002 Args:
1003 opt: Program options returned from optparse. See _Options().
1004 mp: the manifestProject to query.
1005 manifest_name: Manifest file to be reloaded.
1006 """
1007 if not mp.standalone_manifest_url:
1008 self._UpdateManifestProject(opt, mp, manifest_name)
1009
1010 if mp.manifest.submanifests:
1011 for submanifest in mp.manifest.submanifests.values():
1012 child = submanifest.repo_client.manifest
1013 child.manifestProject.SyncWithPossibleInit(
1014 submanifest,
1015 current_branch_only=self._GetCurrentBranchOnly(opt, child),
1016 verbose=opt.verbose,
1017 tags=opt.tags,
1018 git_event_log=self.git_event_log,
1019 )
1020 self._UpdateAllManifestProjects(opt, child.manifestProject, None)
1021
Mike Frysingerfb527e32019-08-27 02:34:32 -04001022 def _UpdateManifestProject(self, opt, mp, manifest_name):
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001023 """Fetch & update the local manifest project.
1024
1025 Args:
1026 opt: Program options returned from optparse. See _Options().
1027 mp: the manifestProject to query.
1028 manifest_name: Manifest file to be reloaded.
1029 """
Mike Frysingerfb527e32019-08-27 02:34:32 -04001030 if not opt.local_only:
1031 start = time.time()
Mike Frysinger521d01b2020-02-17 01:51:49 -05001032 success = mp.Sync_NetworkHalf(quiet=opt.quiet, verbose=opt.verbose,
LaMont Jonesa46047a2022-04-07 21:57:06 +00001033 current_branch_only=self._GetCurrentBranchOnly(opt, mp.manifest),
Erwan Yvindc5c4d12019-06-18 13:49:12 +02001034 force_sync=opt.force_sync,
Mike Frysingerc58ec4d2020-02-17 14:36:08 -05001035 tags=opt.tags,
Mike Frysingerfb527e32019-08-27 02:34:32 -04001036 optimized_fetch=opt.optimized_fetch,
George Engelbrecht9bc283e2020-04-02 12:36:09 -06001037 retry_fetches=opt.retry_fetches,
LaMont Jonesa46047a2022-04-07 21:57:06 +00001038 submodules=mp.manifest.HasSubmodules,
1039 clone_filter=mp.manifest.CloneFilter,
1040 partial_clone_exclude=mp.manifest.PartialCloneExclude)
Mike Frysingerfb527e32019-08-27 02:34:32 -04001041 finish = time.time()
1042 self.event_log.AddSync(mp, event_log.TASK_SYNC_NETWORK,
1043 start, finish, success)
1044
1045 if mp.HasChanges:
1046 syncbuf = SyncBuffer(mp.config)
1047 start = time.time()
LaMont Jonesa46047a2022-04-07 21:57:06 +00001048 mp.Sync_LocalHalf(syncbuf, submodules=mp.manifest.HasSubmodules)
Mike Frysingerfb527e32019-08-27 02:34:32 -04001049 clean = syncbuf.Finish()
1050 self.event_log.AddSync(mp, event_log.TASK_SYNC_LOCAL,
1051 start, time.time(), clean)
1052 if not clean:
1053 sys.exit(1)
LaMont Jonesa46047a2022-04-07 21:57:06 +00001054 self._ReloadManifest(manifest_name, mp.manifest)
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001055
Mike Frysingerae6cb082019-08-27 01:10:59 -04001056 def ValidateOptions(self, opt, args):
1057 if opt.force_broken:
1058 print('warning: -f/--force-broken is now the default behavior, and the '
1059 'options are deprecated', file=sys.stderr)
1060 if opt.network_only and opt.detach_head:
1061 self.OptionParser.error('cannot combine -n and -d')
1062 if opt.network_only and opt.local_only:
1063 self.OptionParser.error('cannot combine -n and -l')
1064 if opt.manifest_name and opt.smart_sync:
1065 self.OptionParser.error('cannot combine -m and -s')
1066 if opt.manifest_name and opt.smart_tag:
1067 self.OptionParser.error('cannot combine -m and -t')
1068 if opt.manifest_server_username or opt.manifest_server_password:
1069 if not (opt.smart_sync or opt.smart_tag):
1070 self.OptionParser.error('-u and -p may only be combined with -s or -t')
1071 if None in [opt.manifest_server_username, opt.manifest_server_password]:
1072 self.OptionParser.error('both -u and -p must be given')
1073
Mike Frysinger0531a622021-11-05 15:22:01 -04001074 if opt.prune is None:
1075 opt.prune = True
1076
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001077 def Execute(self, opt, args):
LaMont Jonesa46047a2022-04-07 21:57:06 +00001078 manifest = self.outer_manifest
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001079 if not opt.outer_manifest:
LaMont Jonesa46047a2022-04-07 21:57:06 +00001080 manifest = self.manifest
1081
Chris Wolfee9dc3b32012-01-26 11:36:18 -05001082 if opt.manifest_name:
LaMont Jonesa46047a2022-04-07 21:57:06 +00001083 manifest.Override(opt.manifest_name)
Shawn O. Pearce3e768c92009-04-10 16:59:36 -07001084
Chirayu Desaia892b102013-06-11 14:18:46 +05301085 manifest_name = opt.manifest_name
David Pursehouse59b41742015-05-07 14:36:09 +09001086 smart_sync_manifest_path = os.path.join(
LaMont Jonesa46047a2022-04-07 21:57:06 +00001087 manifest.manifestProject.worktree, 'smart_sync_override.xml')
Chirayu Desaia892b102013-06-11 14:18:46 +05301088
Xin Lid79a4bc2020-05-20 16:03:45 -07001089 if opt.clone_bundle is None:
LaMont Jonesa46047a2022-04-07 21:57:06 +00001090 opt.clone_bundle = manifest.CloneBundle
Xin Lid79a4bc2020-05-20 16:03:45 -07001091
Victor Boivie08c880d2011-04-19 10:32:52 +02001092 if opt.smart_sync or opt.smart_tag:
LaMont Jonesa46047a2022-04-07 21:57:06 +00001093 manifest_name = self._SmartSyncSetup(opt, smart_sync_manifest_path, manifest)
Mike Frysinger01d6c3c2019-08-27 01:56:43 -04001094 else:
David Pursehouse59b41742015-05-07 14:36:09 +09001095 if os.path.isfile(smart_sync_manifest_path):
1096 try:
Renaud Paquay010fed72016-11-11 14:25:29 -08001097 platform_utils.remove(smart_sync_manifest_path)
David Pursehouse59b41742015-05-07 14:36:09 +09001098 except OSError as e:
1099 print('error: failed to remove existing smart sync override manifest: %s' %
1100 e, file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -07001101
Mike Frysingerc99322a2021-05-04 15:32:43 -04001102 err_event = multiprocessing.Event()
Mike Frysinger5a033082019-09-23 19:21:20 -04001103
LaMont Jonesa46047a2022-04-07 21:57:06 +00001104 rp = manifest.repoProject
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001105 rp.PreSync()
Mike Frysinger23c900f2020-02-29 02:52:44 -05001106 cb = rp.CurrentBranch
1107 if cb:
1108 base = rp.GetBranch(cb).merge
1109 if not base or not base.startswith('refs/heads/'):
1110 print('warning: repo is not tracking a remote branch, so it will not '
Mike Frysinger58ac1672020-03-14 14:35:26 -04001111 'receive updates; run `repo init --repo-rev=stable` to fix.',
Mike Frysinger23c900f2020-02-29 02:52:44 -05001112 file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001113
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001114 for m in self.ManifestList(opt):
LaMont Jones4112c072022-08-24 17:32:25 +00001115 if not m.manifestProject.standalone_manifest_url:
1116 m.manifestProject.PreSync()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001117
LaMont Jones4112c072022-08-24 17:32:25 +00001118 if opt.repo_upgraded:
1119 _PostRepoUpgrade(manifest, quiet=opt.quiet)
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08001120
LaMont Jones4112c072022-08-24 17:32:25 +00001121 mp = manifest.manifestProject
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001122 if opt.mp_update:
1123 self._UpdateAllManifestProjects(opt, mp, manifest_name)
1124 else:
Fredrik de Grootcc960972019-11-22 09:04:31 +01001125 print('Skipping update of local manifest project.')
Simran Basib9a1b732015-08-20 12:19:28 -07001126
Mike Frysinger355f4392022-07-20 17:15:29 -04001127 # Now that the manifests are up-to-date, setup the jobs value.
1128 if opt.jobs is None:
1129 # User has not made a choice, so use the manifest settings.
1130 opt.jobs = mp.default.sync_j
1131 if opt.jobs is not None:
1132 # Neither user nor manifest have made a choice.
1133 if opt.jobs_network is None:
1134 opt.jobs_network = opt.jobs
1135 if opt.jobs_checkout is None:
1136 opt.jobs_checkout = opt.jobs
1137 # Setup defaults if jobs==0.
1138 if not opt.jobs:
1139 if not opt.jobs_network:
1140 opt.jobs_network = 1
1141 if not opt.jobs_checkout:
1142 opt.jobs_checkout = DEFAULT_LOCAL_JOBS
1143 opt.jobs = os.cpu_count()
1144
1145 # Try to stay under user rlimit settings.
1146 #
1147 # Since each worker requires at 3 file descriptors to run `git fetch`, use
1148 # that to scale down the number of jobs. Unfortunately there isn't an easy
1149 # way to determine this reliably as systems change, but it was last measured
1150 # by hand in 2011.
1151 soft_limit, _ = _rlimit_nofile()
1152 jobs_soft_limit = max(1, (soft_limit - 5) // 3)
1153 opt.jobs = min(opt.jobs, jobs_soft_limit)
1154 opt.jobs_network = min(opt.jobs_network, jobs_soft_limit)
1155 opt.jobs_checkout = min(opt.jobs_checkout, jobs_soft_limit)
1156
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001157 superproject_logging_data = {}
1158 self._UpdateProjectsRevisionId(opt, args, superproject_logging_data,
1159 manifest)
Raman Tenneti1fd7bc22021-02-04 14:39:38 -08001160
Simran Basib9a1b732015-08-20 12:19:28 -07001161 if self.gitc_manifest:
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001162 gitc_manifest_projects = self.GetProjects(args, missing_ok=True)
Simran Basib9a1b732015-08-20 12:19:28 -07001163 gitc_projects = []
1164 opened_projects = []
1165 for project in gitc_manifest_projects:
Dan Willemsen5ea32d12015-09-08 13:27:20 -07001166 if project.relpath in self.gitc_manifest.paths and \
1167 self.gitc_manifest.paths[project.relpath].old_revision:
1168 opened_projects.append(project.relpath)
Simran Basib9a1b732015-08-20 12:19:28 -07001169 else:
Dan Willemsen5ea32d12015-09-08 13:27:20 -07001170 gitc_projects.append(project.relpath)
Simran Basib9a1b732015-08-20 12:19:28 -07001171
Dan Willemsen5ea32d12015-09-08 13:27:20 -07001172 if not args:
1173 gitc_projects = None
1174
1175 if gitc_projects != [] and not opt.local_only:
Simran Basib9a1b732015-08-20 12:19:28 -07001176 print('Updating GITC client: %s' % self.gitc_manifest.gitc_client_name)
Dan Willemsen5ea32d12015-09-08 13:27:20 -07001177 manifest = GitcManifest(self.repodir, self.gitc_manifest.gitc_client_name)
1178 if manifest_name:
1179 manifest.Override(manifest_name)
1180 else:
LaMont Jonesa46047a2022-04-07 21:57:06 +00001181 manifest.Override(manifest.manifestFile)
Dan Willemsen5ea32d12015-09-08 13:27:20 -07001182 gitc_utils.generate_gitc_manifest(self.gitc_manifest,
1183 manifest,
Simran Basib9a1b732015-08-20 12:19:28 -07001184 gitc_projects)
1185 print('GITC client successfully synced.')
1186
1187 # The opened projects need to be synced as normal, therefore we
1188 # generate a new args list to represent the opened projects.
Dan Willemsen5ea32d12015-09-08 13:27:20 -07001189 # TODO: make this more reliable -- if there's a project name/path overlap,
1190 # this may choose the wrong project.
LaMont Jonesa46047a2022-04-07 21:57:06 +00001191 args = [os.path.relpath(manifest.paths[path].worktree, os.getcwd())
David Pursehouse3bcd3052017-07-10 22:42:22 +09001192 for path in opened_projects]
Simran Basib9a1b732015-08-20 12:19:28 -07001193 if not args:
1194 return
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001195
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001196 all_projects = self.GetProjects(args,
1197 missing_ok=True,
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001198 submodules_ok=opt.fetch_submodules,
1199 manifest=manifest,
1200 all_manifests=not opt.this_manifest_only)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001201
Mike Frysinger5a033082019-09-23 19:21:20 -04001202 err_network_sync = False
1203 err_update_projects = False
Mike Frysinger5a033082019-09-23 19:21:20 -04001204
LaMont Jonesa46047a2022-04-07 21:57:06 +00001205 self._fetch_times = _FetchTimes(manifest)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -07001206 if not opt.local_only:
Mike Frysinger339f2df2021-05-06 00:44:42 -04001207 with multiprocessing.Manager() as manager:
1208 with ssh.ProxyManager(manager) as ssh_proxy:
1209 # Initialize the socket dir once in the parent.
1210 ssh_proxy.sock()
Peter Kjellerstedtd1776092021-05-19 19:37:23 +02001211 all_projects = self._FetchMain(opt, args, all_projects, err_event,
LaMont Jonesa46047a2022-04-07 21:57:06 +00001212 ssh_proxy, manifest)
Mike Frysinger339f2df2021-05-06 00:44:42 -04001213
1214 if opt.network_only:
1215 return
Mike Frysinger5a033082019-09-23 19:21:20 -04001216
1217 # If we saw an error, exit with code 1 so that other scripts can check.
Mike Frysingerbe24a542021-02-23 03:24:12 -05001218 if err_event.is_set():
Mike Frysinger5a033082019-09-23 19:21:20 -04001219 err_network_sync = True
1220 if opt.fail_fast:
1221 print('\nerror: Exited sync due to fetch errors.\n'
1222 'Local checkouts *not* updated. Resolve network issues & '
1223 'retry.\n'
1224 '`repo sync -l` will update some local checkouts.',
1225 file=sys.stderr)
1226 sys.exit(1)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001227
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001228 for m in self.ManifestList(opt):
1229 if m.IsMirror or m.IsArchive:
1230 # bail out now, we have no working tree
1231 continue
Shawn O. Pearcecd1d7ff2009-06-04 16:15:53 -07001232
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001233 if self.UpdateProjectList(opt, m):
1234 err_event.set()
1235 err_update_projects = True
1236 if opt.fail_fast:
1237 print('\nerror: Local checkouts *not* updated.', file=sys.stderr)
1238 sys.exit(1)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -07001239
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001240 err_update_linkfiles = not self.UpdateCopyLinkfileList(m)
1241 if err_update_linkfiles:
1242 err_event.set()
1243 if opt.fail_fast:
1244 print('\nerror: Local update copyfile or linkfile failed.', file=sys.stderr)
1245 sys.exit(1)
jiajia tanga590e642021-04-25 20:02:02 +08001246
Mike Frysinger5a033082019-09-23 19:21:20 -04001247 err_results = []
Mike Frysingerebf04a42021-02-23 20:48:04 -05001248 # NB: We don't exit here because this is the last step.
1249 err_checkout = not self._Checkout(all_projects, opt, err_results)
1250 if err_checkout:
1251 err_event.set()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001252
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001253 printed_notices = set()
1254 # If there's a notice that's supposed to print at the end of the sync,
1255 # print it now... But avoid printing duplicate messages, and preserve
1256 # order.
1257 for m in sorted(self.ManifestList(opt), key=lambda x: x.path_prefix):
1258 if m.notice and m.notice not in printed_notices:
1259 print(m.notice)
1260 printed_notices.add(m.notice)
Doug Anderson2b8db3c2010-11-01 15:08:06 -07001261
Mike Frysinger5a033082019-09-23 19:21:20 -04001262 # If we saw an error, exit with code 1 so that other scripts can check.
Mike Frysingerbe24a542021-02-23 03:24:12 -05001263 if err_event.is_set():
Mike Frysinger5a033082019-09-23 19:21:20 -04001264 print('\nerror: Unable to fully sync the tree.', file=sys.stderr)
1265 if err_network_sync:
1266 print('error: Downloading network changes failed.', file=sys.stderr)
1267 if err_update_projects:
1268 print('error: Updating local project lists failed.', file=sys.stderr)
jiajia tanga590e642021-04-25 20:02:02 +08001269 if err_update_linkfiles:
1270 print('error: Updating copyfiles or linkfiles failed.', file=sys.stderr)
Mike Frysinger5a033082019-09-23 19:21:20 -04001271 if err_checkout:
1272 print('error: Checking out local projects failed.', file=sys.stderr)
1273 if err_results:
1274 print('Failing repos:\n%s' % '\n'.join(err_results), file=sys.stderr)
1275 print('Try re-running with "-j1 --fail-fast" to exit at the first error.',
1276 file=sys.stderr)
1277 sys.exit(1)
1278
Raman Tenneti7954de12021-07-28 14:36:49 -07001279 # Log the previous sync analysis state from the config.
Raman Tenneti6448a4f2021-09-13 17:40:07 -07001280 self.git_event_log.LogDataConfigEvents(mp.config.GetSyncAnalysisStateData(),
1281 'previous_sync_state')
Raman Tenneti7954de12021-07-28 14:36:49 -07001282
1283 # Update and log with the new sync analysis state.
1284 mp.config.UpdateSyncAnalysisState(opt, superproject_logging_data)
Raman Tenneti6448a4f2021-09-13 17:40:07 -07001285 self.git_event_log.LogDataConfigEvents(mp.config.GetSyncAnalysisStateData(),
1286 'current_sync_state')
Raman Tenneti7954de12021-07-28 14:36:49 -07001287
Mike Frysingere19d9e12020-02-12 11:23:32 -05001288 if not opt.quiet:
1289 print('repo sync has finished successfully.')
1290
David Pursehouse819827a2020-02-12 15:20:19 +09001291
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -07001292def _PostRepoUpgrade(manifest, quiet=False):
Mike Frysingerfdeb20f2021-11-14 03:53:04 -05001293 # Link the docs for the internal .repo/ layout for people
1294 link = os.path.join(manifest.repodir, 'internal-fs-layout.md')
1295 if not platform_utils.islink(link):
1296 target = os.path.join('repo', 'docs', 'internal-fs-layout.md')
1297 try:
1298 platform_utils.symlink(target, link)
Raman Tenneti4a478ed2021-11-17 18:38:24 -08001299 except Exception:
Mike Frysingerfdeb20f2021-11-14 03:53:04 -05001300 pass
1301
Conley Owens094cdbe2014-01-30 15:09:59 -08001302 wrapper = Wrapper()
Conley Owensc9129d92012-10-01 16:12:28 -07001303 if wrapper.NeedSetupGnuPG():
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -07001304 wrapper.SetupGnuPG(quiet)
Conley Owensf2fe2d92014-01-29 13:53:43 -08001305 for project in manifest.projects:
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001306 if project.Exists:
1307 project.PostRepoUpgrade()
1308
David Pursehouse819827a2020-02-12 15:20:19 +09001309
Mike Frysingerc58ec4d2020-02-17 14:36:08 -05001310def _PostRepoFetch(rp, repo_verify=True, verbose=False):
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001311 if rp.HasChanges:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001312 print('info: A new version of repo is available', file=sys.stderr)
Mike Frysinger347f9ed2021-03-15 14:58:52 -04001313 wrapper = Wrapper()
1314 try:
1315 rev = rp.bare_git.describe(rp.GetRevisionId())
1316 except GitError:
1317 rev = None
1318 _, new_rev = wrapper.check_repo_rev(rp.gitdir, rev, repo_verify=repo_verify)
1319 # See if we're held back due to missing signed tag.
1320 current_revid = rp.bare_git.rev_parse('HEAD')
1321 new_revid = rp.bare_git.rev_parse('--verify', new_rev)
1322 if current_revid != new_revid:
1323 # We want to switch to the new rev, but also not trash any uncommitted
1324 # changes. This helps with local testing/hacking.
1325 # If a local change has been made, we will throw that away.
1326 # We also have to make sure this will switch to an older commit if that's
1327 # the latest tag in order to support release rollback.
1328 try:
1329 rp.work_git.reset('--keep', new_rev)
1330 except GitError as e:
1331 sys.exit(str(e))
Sarah Owenscecd1d82012-11-01 22:59:27 -07001332 print('info: Restarting repo with latest version', file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001333 raise RepoChangedException(['--repo-upgraded'])
1334 else:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001335 print('warning: Skipped upgrade to unverified version', file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001336 else:
1337 if verbose:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001338 print('repo version %s is current' % rp.work_git.describe(HEAD),
1339 file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001340
David Pursehouse819827a2020-02-12 15:20:19 +09001341
Dave Borowitz67700e92012-10-23 15:00:54 -07001342class _FetchTimes(object):
Dave Borowitzd9478582012-10-23 16:35:39 -07001343 _ALPHA = 0.5
1344
Dave Borowitz67700e92012-10-23 15:00:54 -07001345 def __init__(self, manifest):
Anthony King85b24ac2014-05-06 15:57:48 +01001346 self._path = os.path.join(manifest.repodir, '.repo_fetchtimes.json')
Dave Borowitz67700e92012-10-23 15:00:54 -07001347 self._times = None
Dave Borowitzd9478582012-10-23 16:35:39 -07001348 self._seen = set()
Dave Borowitz67700e92012-10-23 15:00:54 -07001349
1350 def Get(self, project):
1351 self._Load()
1352 return self._times.get(project.name, _ONE_DAY_S)
1353
1354 def Set(self, project, t):
Dave Borowitzd9478582012-10-23 16:35:39 -07001355 self._Load()
1356 name = project.name
1357 old = self._times.get(name, t)
1358 self._seen.add(name)
1359 a = self._ALPHA
David Pursehouse54a4e602020-02-12 14:31:05 +09001360 self._times[name] = (a * t) + ((1 - a) * old)
Dave Borowitz67700e92012-10-23 15:00:54 -07001361
1362 def _Load(self):
1363 if self._times is None:
1364 try:
Mike Frysinger3164d402019-11-11 05:40:22 -05001365 with open(self._path) as f:
Anthony King85b24ac2014-05-06 15:57:48 +01001366 self._times = json.load(f)
Anthony King85b24ac2014-05-06 15:57:48 +01001367 except (IOError, ValueError):
Mike Frysinger9d96f582021-09-28 11:27:24 -04001368 platform_utils.remove(self._path, missing_ok=True)
Anthony King85b24ac2014-05-06 15:57:48 +01001369 self._times = {}
Dave Borowitz67700e92012-10-23 15:00:54 -07001370
1371 def Save(self):
1372 if self._times is None:
1373 return
Dave Borowitzd9478582012-10-23 16:35:39 -07001374
1375 to_delete = []
1376 for name in self._times:
1377 if name not in self._seen:
1378 to_delete.append(name)
1379 for name in to_delete:
1380 del self._times[name]
1381
Dave Borowitz67700e92012-10-23 15:00:54 -07001382 try:
Mike Frysinger3164d402019-11-11 05:40:22 -05001383 with open(self._path, 'w') as f:
Anthony King85b24ac2014-05-06 15:57:48 +01001384 json.dump(self._times, f, indent=2)
Anthony King85b24ac2014-05-06 15:57:48 +01001385 except (IOError, TypeError):
Mike Frysinger9d96f582021-09-28 11:27:24 -04001386 platform_utils.remove(self._path, missing_ok=True)
Dan Willemsen0745bb22015-08-17 13:41:45 -07001387
1388# This is a replacement for xmlrpc.client.Transport using urllib2
1389# and supporting persistent-http[s]. It cannot change hosts from
1390# request to request like the normal transport, the real url
1391# is passed during initialization.
David Pursehouse819827a2020-02-12 15:20:19 +09001392
1393
Dan Willemsen0745bb22015-08-17 13:41:45 -07001394class PersistentTransport(xmlrpc.client.Transport):
1395 def __init__(self, orig_host):
1396 self.orig_host = orig_host
1397
1398 def request(self, host, handler, request_body, verbose=False):
1399 with GetUrlCookieFile(self.orig_host, not verbose) as (cookiefile, proxy):
1400 # Python doesn't understand cookies with the #HttpOnly_ prefix
1401 # Since we're only using them for HTTP, copy the file temporarily,
1402 # stripping those prefixes away.
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001403 if cookiefile:
Collin Fijalkoviche1191b32020-02-18 10:57:32 -08001404 tmpcookiefile = tempfile.NamedTemporaryFile(mode='w')
David Pursehouse4c5f74e2015-10-02 11:10:10 +09001405 tmpcookiefile.write("# HTTP Cookie File")
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001406 try:
1407 with open(cookiefile) as f:
1408 for line in f:
1409 if line.startswith("#HttpOnly_"):
1410 line = line[len("#HttpOnly_"):]
1411 tmpcookiefile.write(line)
1412 tmpcookiefile.flush()
Dan Willemsen0745bb22015-08-17 13:41:45 -07001413
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001414 cookiejar = cookielib.MozillaCookieJar(tmpcookiefile.name)
David Pursehouseb1ad2192015-09-30 10:35:43 +09001415 try:
1416 cookiejar.load()
1417 except cookielib.LoadError:
1418 cookiejar = cookielib.CookieJar()
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001419 finally:
1420 tmpcookiefile.close()
1421 else:
1422 cookiejar = cookielib.CookieJar()
Dan Willemsen0745bb22015-08-17 13:41:45 -07001423
1424 proxyhandler = urllib.request.ProxyHandler
1425 if proxy:
1426 proxyhandler = urllib.request.ProxyHandler({
1427 "http": proxy,
David Pursehouse54a4e602020-02-12 14:31:05 +09001428 "https": proxy})
Dan Willemsen0745bb22015-08-17 13:41:45 -07001429
1430 opener = urllib.request.build_opener(
1431 urllib.request.HTTPCookieProcessor(cookiejar),
1432 proxyhandler)
1433
1434 url = urllib.parse.urljoin(self.orig_host, handler)
1435 parse_results = urllib.parse.urlparse(url)
1436
1437 scheme = parse_results.scheme
1438 if scheme == 'persistent-http':
1439 scheme = 'http'
1440 if scheme == 'persistent-https':
1441 # If we're proxying through persistent-https, use http. The
1442 # proxy itself will do the https.
1443 if proxy:
1444 scheme = 'http'
1445 else:
1446 scheme = 'https'
1447
1448 # Parse out any authentication information using the base class
1449 host, extra_headers, _ = self.get_host_info(parse_results.netloc)
1450
1451 url = urllib.parse.urlunparse((
1452 scheme,
1453 host,
1454 parse_results.path,
1455 parse_results.params,
1456 parse_results.query,
1457 parse_results.fragment))
1458
1459 request = urllib.request.Request(url, request_body)
1460 if extra_headers is not None:
1461 for (name, header) in extra_headers:
1462 request.add_header(name, header)
1463 request.add_header('Content-Type', 'text/xml')
1464 try:
1465 response = opener.open(request)
1466 except urllib.error.HTTPError as e:
1467 if e.code == 501:
1468 # We may have been redirected through a login process
1469 # but our POST turned into a GET. Retry.
1470 response = opener.open(request)
1471 else:
1472 raise
1473
1474 p, u = xmlrpc.client.getparser()
Mike Frysinger5951e302022-05-20 23:34:44 -04001475 # Response should be fairly small, so read it all at once.
1476 # This way we can show it to the user in case of error (e.g. HTML).
1477 data = response.read()
1478 try:
Dan Willemsen0745bb22015-08-17 13:41:45 -07001479 p.feed(data)
Mike Frysinger5951e302022-05-20 23:34:44 -04001480 except xml.parsers.expat.ExpatError as e:
1481 raise IOError(
1482 f'Parsing the manifest failed: {e}\n'
1483 f'Please report this to your manifest server admin.\n'
1484 f'Here is the full response:\n{data.decode("utf-8")}')
Dan Willemsen0745bb22015-08-17 13:41:45 -07001485 p.close()
1486 return u.close()
1487
1488 def close(self):
1489 pass