blob: 507d1c9452d5bf8b5c297bf5073bb9c3d0bc9666 [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 Frysinger355f4392022-07-20 17:15:29 -040055from command import Command, DEFAULT_LOCAL_JOBS, 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):
Mike Frysinger4f210542021-06-14 16:05:19 -040068 COMMON = True
LaMont Jonesbdcba7d2022-04-11 22:50:11 +000069 MULTI_MANIFEST_SUPPORT = True
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070070 helpSummary = "Update working tree to the latest revision"
71 helpUsage = """
72%prog [<project>...]
73"""
74 helpDescription = """
75The '%prog' command synchronizes local project directories
76with the remote repositories specified in the manifest. If a local
77project does not yet exist, it will clone a new local directory from
78the remote repository and set up tracking branches as specified in
79the manifest. If the local project already exists, '%prog'
80will update the remote branches and rebase any new local changes
81on top of the new remote changes.
82
83'%prog' will synchronize all projects listed at the command
84line. Projects can be specified either by name, or by a relative
85or absolute path to the project's local directory. If no projects
86are specified, '%prog' will synchronize all projects listed in
87the manifest.
Shawn O. Pearce3e768c92009-04-10 16:59:36 -070088
89The -d/--detach option can be used to switch specified projects
90back to the manifest revision. This option is especially helpful
91if the project is currently on a topic branch, but the manifest
92revision is temporarily needed.
Shawn O. Pearceeb7af872009-04-21 08:02:04 -070093
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -070094The -s/--smart-sync option can be used to sync to a known good
95build as specified by the manifest-server element in the current
Victor Boivie08c880d2011-04-19 10:32:52 +020096manifest. The -t/--smart-tag option is similar and allows you to
97specify a custom tag/label.
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -070098
David Pursehousecf76b1b2012-09-14 10:31:42 +090099The -u/--manifest-server-username and -p/--manifest-server-password
100options can be used to specify a username and password to authenticate
101with the manifest server when using the -s or -t option.
102
103If -u and -p are not specified when using the -s or -t option, '%prog'
104will attempt to read authentication credentials for the manifest server
105from the user's .netrc file.
106
107'%prog' will not use authentication credentials from -u/-p or .netrc
108if the manifest server specified in the manifest file already includes
109credentials.
110
Mike Frysingerd9e5cf02019-08-26 03:12:55 -0400111By default, all projects will be synced. The --fail-fast option can be used
Mike Frysinger7ae210a2020-05-24 14:56:52 -0400112to halt syncing as soon as possible when the first project fails to sync.
Andrei Warkentin5df6de02010-07-02 17:58:31 -0500113
Kevin Degiabaa7f32014-11-12 11:27:45 -0700114The --force-sync option can be used to overwrite existing git
115directories if they have previously been linked to a different
Roger Shimizuac29ac32020-06-06 02:33:40 +0900116object directory. WARNING: This may cause data to be lost since
Kevin Degiabaa7f32014-11-12 11:27:45 -0700117refs may be removed when overwriting.
118
Oleksii Okolielovd3c0f592018-12-17 19:23:44 -0500119The --force-remove-dirty option can be used to remove previously used
120projects with uncommitted changes. WARNING: This may cause data to be
121lost since uncommitted changes may be removed with projects that no longer
122exist in the manifest.
123
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -0700124The --no-clone-bundle option disables any attempt to use
125$URL/clone.bundle to bootstrap a new Git repository from a
126resumeable bundle file on a content delivery network. This
127may be necessary if there are problems with the local Python
128HTTP client or proxy configuration, but the Git binary works.
129
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800130The --fetch-submodules option enables fetching Git submodules
131of a project from server.
132
David Pursehousef2fad612015-01-29 14:36:28 +0900133The -c/--current-branch option can be used to only fetch objects that
134are on the branch specified by a project's revision.
135
David Pursehouseb1553542014-09-04 21:28:09 +0900136The --optimized-fetch option can be used to only fetch projects that
137are fixed to a sha1 revision if the sha1 revision does not already
138exist locally.
139
David Pursehouse74cfd272015-10-14 10:50:15 +0900140The --prune option can be used to remove any refs that no longer
141exist on the remote.
142
Mike Frysingerb8f7bb02018-10-10 01:05:11 -0400143# SSH Connections
Shawn O. Pearceeb7af872009-04-21 08:02:04 -0700144
145If at least one project remote URL uses an SSH connection (ssh://,
146git+ssh://, or user@host:path syntax) repo will automatically
147enable the SSH ControlMaster option when connecting to that host.
148This feature permits other projects in the same '%prog' session to
149reuse the same SSH tunnel, saving connection setup overheads.
150
151To disable this behavior on UNIX platforms, set the GIT_SSH
152environment variable to 'ssh'. For example:
153
154 export GIT_SSH=ssh
155 %prog
156
Mike Frysingerb8f7bb02018-10-10 01:05:11 -0400157# Compatibility
Shawn O. Pearceeb7af872009-04-21 08:02:04 -0700158
159This feature is automatically disabled on Windows, due to the lack
160of UNIX domain socket support.
161
162This feature is not compatible with url.insteadof rewrites in the
163user's ~/.gitconfig. '%prog' is currently not able to perform the
164rewrite early enough to establish the ControlMaster tunnel.
165
166If the remote SSH daemon is Gerrit Code Review, version 2.0.10 or
167later is required to fix a server side protocol bug.
168
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700169"""
Mike Frysinger355f4392022-07-20 17:15:29 -0400170 # A value of 0 means we want parallel jobs, but we'll determine the default
171 # value later on.
172 PARALLEL_JOBS = 0
Shawn O. Pearce6392c872011-09-22 17:44:31 -0700173
Mike Frysinger9180a072021-04-13 14:57:40 -0400174 def _Options(self, p, show_smart=True):
Mike Frysinger49de8ef2021-04-09 00:21:02 -0400175 p.add_option('--jobs-network', default=None, type=int, metavar='JOBS',
Mike Frysinger355f4392022-07-20 17:15:29 -0400176 help='number of network jobs to run in parallel (defaults to --jobs or 1)')
Mike Frysinger49de8ef2021-04-09 00:21:02 -0400177 p.add_option('--jobs-checkout', default=None, type=int, metavar='JOBS',
Mike Frysinger355f4392022-07-20 17:15:29 -0400178 help='number of local checkout jobs to run in parallel (defaults to --jobs or '
179 f'{DEFAULT_LOCAL_JOBS})')
Mike Frysinger49de8ef2021-04-09 00:21:02 -0400180
Andrei Warkentin5df6de02010-07-02 17:58:31 -0500181 p.add_option('-f', '--force-broken',
Stefan Müller-Klieser46702ed2019-08-30 10:20:15 +0200182 dest='force_broken', action='store_true',
Mike Frysingerd9e5cf02019-08-26 03:12:55 -0400183 help='obsolete option (to be deleted in the future)')
184 p.add_option('--fail-fast',
185 dest='fail_fast', action='store_true',
186 help='stop syncing after first error is hit')
Kevin Degiabaa7f32014-11-12 11:27:45 -0700187 p.add_option('--force-sync',
188 dest='force_sync', action='store_true',
189 help="overwrite an existing git directory if it needs to "
190 "point to a different object directory. WARNING: this "
191 "may cause loss of data")
Oleksii Okolielovd3c0f592018-12-17 19:23:44 -0500192 p.add_option('--force-remove-dirty',
193 dest='force_remove_dirty', action='store_true',
194 help="force remove projects with uncommitted modifications if "
195 "projects no longer exist in the manifest. "
196 "WARNING: this may cause loss of data")
David Pursehouse8f62fb72012-11-14 12:09:38 +0900197 p.add_option('-l', '--local-only',
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700198 dest='local_only', action='store_true',
199 help="only update working tree, don't fetch")
David Pursehouse54a4e602020-02-12 14:31:05 +0900200 p.add_option('--no-manifest-update', '--nmu',
Fredrik de Grootcc960972019-11-22 09:04:31 +0100201 dest='mp_update', action='store_false', default='true',
202 help='use the existing manifest checkout as-is. '
203 '(do not update to the latest revision)')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900204 p.add_option('-n', '--network-only',
Shawn O. Pearce96fdcef2009-04-10 16:29:20 -0700205 dest='network_only', action='store_true',
206 help="fetch only, don't update working tree")
David Pursehouse8f62fb72012-11-14 12:09:38 +0900207 p.add_option('-d', '--detach',
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700208 dest='detach_head', action='store_true',
209 help='detach projects back to manifest revision')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900210 p.add_option('-c', '--current-branch',
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -0700211 dest='current_branch_only', action='store_true',
212 help='fetch only current branch from server')
Mike Frysinger73561142021-05-03 01:10:09 -0400213 p.add_option('--no-current-branch',
214 dest='current_branch_only', action='store_false',
215 help='fetch all branches from server')
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500216 p.add_option('-m', '--manifest-name',
217 dest='manifest_name',
218 help='temporary manifest to use for this sync', metavar='NAME.xml')
Xin Lid79a4bc2020-05-20 16:03:45 -0700219 p.add_option('--clone-bundle', action='store_true',
220 help='enable use of /clone.bundle on HTTP/HTTPS')
221 p.add_option('--no-clone-bundle', dest='clone_bundle', action='store_false',
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -0700222 help='disable use of /clone.bundle on HTTP/HTTPS')
Conley Owens8d070cf2012-11-06 13:14:31 -0800223 p.add_option('-u', '--manifest-server-username', action='store',
224 dest='manifest_server_username',
225 help='username to authenticate with the manifest server')
226 p.add_option('-p', '--manifest-server-password', action='store',
227 dest='manifest_server_password',
228 help='password to authenticate with the manifest server')
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800229 p.add_option('--fetch-submodules',
230 dest='fetch_submodules', action='store_true',
231 help='fetch submodules from server')
Raman Tenneti6a872c92021-01-14 19:17:50 -0800232 p.add_option('--use-superproject', action='store_true',
Raman Tenneti62517292021-11-01 14:49:16 -0700233 help='use the manifest superproject to sync projects; implies -c')
Raman Tenneti23ea7542021-05-07 14:01:54 -0700234 p.add_option('--no-use-superproject', action='store_false',
235 dest='use_superproject',
236 help='disable use of manifest superprojects')
Mike Frysinger2273f462021-11-05 15:10:33 -0400237 p.add_option('--tags', action='store_true',
Mike Frysingerd68ed632021-05-03 01:21:35 -0400238 help='fetch tags')
Mitchel Humpherys597868b2012-10-29 10:18:34 -0700239 p.add_option('--no-tags',
Mike Frysingerd68ed632021-05-03 01:21:35 -0400240 dest='tags', action='store_false',
Mike Frysinger2273f462021-11-05 15:10:33 -0400241 help="don't fetch tags (default)")
David Pursehouseb1553542014-09-04 21:28:09 +0900242 p.add_option('--optimized-fetch',
243 dest='optimized_fetch', action='store_true',
244 help='only fetch projects fixed to sha1 if revision does not exist locally')
George Engelbrecht9bc283e2020-04-02 12:36:09 -0600245 p.add_option('--retry-fetches',
246 default=0, action='store', type='int',
247 help='number of times to retry fetches on transient errors')
Mike Frysinger0531a622021-11-05 15:22:01 -0400248 p.add_option('--prune', action='store_true',
249 help='delete refs that no longer exist on the remote (default)')
250 p.add_option('--no-prune', dest='prune', action='store_false',
251 help='do not delete refs that no longer exist on the remote')
Nico Sallembien6623b212010-05-11 12:57:01 -0700252 if show_smart:
253 p.add_option('-s', '--smart-sync',
254 dest='smart_sync', action='store_true',
David Pursehouse79fba682016-04-13 18:03:00 +0900255 help='smart sync using manifest from the latest known good build')
Victor Boivie08c880d2011-04-19 10:32:52 +0200256 p.add_option('-t', '--smart-tag',
257 dest='smart_tag', action='store',
258 help='smart sync using manifest from a known tag')
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700259
Shawn O. Pearcefd89b672009-04-18 11:28:57 -0700260 g = p.add_option_group('repo Version options')
261 g.add_option('--no-repo-verify',
Mike Frysingerc58ec4d2020-02-17 14:36:08 -0500262 dest='repo_verify', default=True, action='store_false',
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700263 help='do not verify repo source code')
Shawn O. Pearcefd89b672009-04-18 11:28:57 -0700264 g.add_option('--repo-upgraded',
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800265 dest='repo_upgraded', action='store_true',
Shawn O. Pearce2a1ccb22009-04-10 16:51:53 -0700266 help=SUPPRESS_HELP)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700267
LaMont Jonesa46047a2022-04-07 21:57:06 +0000268 def _GetBranch(self, manifest_project):
269 """Returns the branch name for getting the approved smartsync manifest.
270
271 Args:
272 manifest_project: the manifestProject to query.
273 """
274 b = manifest_project.GetBranch(manifest_project.CurrentBranch)
Raman Tenneti8d43dea2021-02-07 16:30:27 -0800275 branch = b.merge
276 if branch.startswith(R_HEADS):
277 branch = branch[len(R_HEADS):]
278 return branch
279
LaMont Jonesa46047a2022-04-07 21:57:06 +0000280 def _GetCurrentBranchOnly(self, opt, manifest):
Daniel Anderssond52ca422022-04-01 12:55:38 +0200281 """Returns whether current-branch or use-superproject options are enabled.
282
LaMont Jonesa46047a2022-04-07 21:57:06 +0000283 Args:
284 opt: Program options returned from optparse. See _Options().
285 manifest: The manifest to use.
286
Daniel Anderssond52ca422022-04-01 12:55:38 +0200287 Returns:
288 True if a superproject is requested, otherwise the value of the
289 current_branch option (True, False or None).
290 """
LaMont Jonesa46047a2022-04-07 21:57:06 +0000291 return git_superproject.UseSuperproject(opt.use_superproject, manifest) or opt.current_branch_only
Raman Tenneti2ae44d72021-03-23 15:12:27 -0700292
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000293 def _UpdateProjectsRevisionId(self, opt, args, superproject_logging_data,
294 manifest):
295 """Update revisionId of projects with the commit hash from the superproject.
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800296
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000297 This function updates each project's revisionId with the commit hash from
298 the superproject. It writes the updated manifest into a file and reloads
299 the manifest from it. When appropriate, sub manifests are also processed.
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800300
301 Args:
302 opt: Program options returned from optparse. See _Options().
303 args: Arguments to pass to GetProjects. See the GetProjects
304 docstring for details.
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000305 superproject_logging_data: A dictionary of superproject data to log.
LaMont Jonesa46047a2022-04-07 21:57:06 +0000306 manifest: The manifest to use.
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800307 """
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000308 have_superproject = manifest.superproject or any(
309 m.superproject for m in manifest.all_children)
310 if not have_superproject:
311 return
312
LaMont Jonesff6b1da2022-06-01 21:03:34 +0000313 if opt.local_only and manifest.superproject:
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000314 manifest_path = manifest.superproject.manifest_path
Raman Tennetiae86a462021-07-27 08:54:59 -0700315 if manifest_path:
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000316 self._ReloadManifest(manifest_path, manifest)
317 return
Raman Tennetiae86a462021-07-27 08:54:59 -0700318
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800319 all_projects = self.GetProjects(args,
320 missing_ok=True,
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000321 submodules_ok=opt.fetch_submodules,
322 manifest=manifest,
323 all_manifests=not opt.this_manifest_only)
324
325 per_manifest = collections.defaultdict(list)
326 manifest_paths = {}
327 if opt.this_manifest_only:
328 per_manifest[manifest.path_prefix] = all_projects
Raman Tenneti784e16f2021-06-11 17:29:45 -0700329 else:
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000330 for p in all_projects:
331 per_manifest[p.manifest.path_prefix].append(p)
332
333 superproject_logging_data = {}
334 need_unload = False
335 for m in self.ManifestList(opt):
336 if not m.path_prefix in per_manifest:
337 continue
338 use_super = git_superproject.UseSuperproject(opt.use_superproject, m)
339 if superproject_logging_data:
340 superproject_logging_data['multimanifest'] = True
341 superproject_logging_data.update(
342 superproject=use_super,
343 haslocalmanifests=bool(m.HasLocalManifests),
344 hassuperprojecttag=bool(m.superproject),
345 )
346 if use_super and (m.IsMirror or m.IsArchive):
347 # Don't use superproject, because we have no working tree.
348 use_super = False
349 superproject_logging_data['superproject'] = False
350 superproject_logging_data['noworktree'] = True
351 if opt.use_superproject is not False:
352 print(f'{m.path_prefix}: not using superproject because there is no '
353 'working tree.')
354
355 if not use_super:
356 continue
357 m.superproject.SetQuiet(opt.quiet)
358 print_messages = git_superproject.PrintMessages(opt.use_superproject, m)
359 m.superproject.SetPrintMessages(print_messages)
360 update_result = m.superproject.UpdateProjectsRevisionId(
361 per_manifest[m.path_prefix], git_event_log=self.git_event_log)
362 manifest_path = update_result.manifest_path
363 superproject_logging_data['updatedrevisionid'] = bool(manifest_path)
364 if manifest_path:
365 m.SetManifestOverride(manifest_path)
366 need_unload = True
367 else:
368 if print_messages:
369 print(f'{m.path_prefix}: warning: Update of revisionId from '
370 'superproject has failed, repo sync will not use superproject '
371 'to fetch the source. ',
372 'Please resync with the --no-use-superproject option to avoid '
373 'this repo warning.',
374 file=sys.stderr)
375 if update_result.fatal and opt.use_superproject is not None:
376 sys.exit(1)
377 if need_unload:
378 m.outer_client.manifest.Unload()
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800379
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500380 def _FetchProjectList(self, opt, projects):
381 """Main function of the fetch worker.
382
383 The projects we're given share the same underlying git object store, so we
384 have to fetch them in serial.
Roy Lee18afd7f2010-05-09 04:32:08 +0800385
David James8d201162013-10-11 17:03:19 -0700386 Delegates most of the work to _FetchHelper.
387
388 Args:
389 opt: Program options returned from optparse. See _Options().
390 projects: Projects to fetch.
David James8d201162013-10-11 17:03:19 -0700391 """
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500392 return [self._FetchOne(opt, x) for x in projects]
David James8d201162013-10-11 17:03:19 -0700393
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500394 def _FetchOne(self, opt, project):
David James8d201162013-10-11 17:03:19 -0700395 """Fetch git objects for a single project.
396
David Pursehousec1b86a22012-11-14 11:36:51 +0900397 Args:
398 opt: Program options returned from optparse. See _Options().
399 project: Project object for the project to fetch.
David James8d201162013-10-11 17:03:19 -0700400
401 Returns:
402 Whether the fetch was successful.
David Pursehousec1b86a22012-11-14 11:36:51 +0900403 """
David Rileye0684ad2017-04-05 00:02:59 -0700404 start = time.time()
405 success = False
Mike Frysinger7b586f22021-02-23 18:38:39 -0500406 buf = io.StringIO()
David Pursehousec1b86a22012-11-14 11:36:51 +0900407 try:
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500408 success = project.Sync_NetworkHalf(
409 quiet=opt.quiet,
410 verbose=opt.verbose,
411 output_redir=buf,
LaMont Jonesa46047a2022-04-07 21:57:06 +0000412 current_branch_only=self._GetCurrentBranchOnly(opt, project.manifest),
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500413 force_sync=opt.force_sync,
414 clone_bundle=opt.clone_bundle,
LaMont Jonesa46047a2022-04-07 21:57:06 +0000415 tags=opt.tags, archive=project.manifest.IsArchive,
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500416 optimized_fetch=opt.optimized_fetch,
417 retry_fetches=opt.retry_fetches,
418 prune=opt.prune,
Mike Frysinger339f2df2021-05-06 00:44:42 -0400419 ssh_proxy=self.ssh_proxy,
LaMont Jonesa46047a2022-04-07 21:57:06 +0000420 clone_filter=project.manifest.CloneFilter,
421 partial_clone_exclude=project.manifest.PartialCloneExclude)
Doug Andersonfc06ced2011-03-16 15:49:18 -0700422
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500423 output = buf.getvalue()
Mike Frysinger58929732021-07-02 00:29:35 -0400424 if (opt.verbose or not success) and output:
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500425 print('\n' + output.rstrip())
Doug Andersonfc06ced2011-03-16 15:49:18 -0700426
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500427 if not success:
428 print('error: Cannot fetch %s from %s'
429 % (project.name, project.remote.url),
430 file=sys.stderr)
Raman Tennetiad8aa692021-04-15 09:20:51 -0700431 except GitError as e:
432 print('error.GitError: Cannot fetch %s' % str(e), file=sys.stderr)
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500433 except Exception as e:
434 print('error: Cannot fetch %s (%s: %s)'
435 % (project.name, type(e).__name__, str(e)), file=sys.stderr)
436 raise
Mike Frysinger7b586f22021-02-23 18:38:39 -0500437
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500438 finish = time.time()
439 return (success, project, start, finish)
David James8d201162013-10-11 17:03:19 -0700440
Mike Frysinger339f2df2021-05-06 00:44:42 -0400441 @classmethod
442 def _FetchInitChild(cls, ssh_proxy):
443 cls.ssh_proxy = ssh_proxy
444
445 def _Fetch(self, projects, opt, err_event, ssh_proxy):
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500446 ret = True
447
Mike Frysinger355f4392022-07-20 17:15:29 -0400448 jobs = opt.jobs_network
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700449 fetched = set()
Mike Frysinger151701e2021-04-13 15:07:21 -0400450 pm = Progress('Fetching', len(projects), delay=False, quiet=opt.quiet)
Roy Lee18afd7f2010-05-09 04:32:08 +0800451
David James89ece422014-01-09 18:51:58 -0800452 objdir_project_map = dict()
453 for project in projects:
454 objdir_project_map.setdefault(project.objdir, []).append(project)
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500455 projects_list = list(objdir_project_map.values())
David James8d201162013-10-11 17:03:19 -0700456
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500457 def _ProcessResults(results_sets):
458 ret = True
459 for results in results_sets:
460 for (success, project, start, finish) in results:
461 self._fetch_times.Set(project, finish - start)
462 self.event_log.AddSync(project, event_log.TASK_SYNC_NETWORK,
463 start, finish, success)
464 # Check for any errors before running any more tasks.
465 # ...we'll let existing jobs finish, though.
466 if not success:
467 ret = False
468 else:
469 fetched.add(project.gitdir)
470 pm.update(msg=project.name)
471 if not ret and opt.fail_fast:
472 break
473 return ret
Doug Andersonfc06ced2011-03-16 15:49:18 -0700474
Mike Frysinger339f2df2021-05-06 00:44:42 -0400475 # We pass the ssh proxy settings via the class. This allows multiprocessing
476 # to pickle it up when spawning children. We can't pass it as an argument
477 # to _FetchProjectList below as multiprocessing is unable to pickle those.
478 Sync.ssh_proxy = None
479
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500480 # NB: Multiprocessing is heavy, so don't spin it up for one job.
Mike Frysinger49de8ef2021-04-09 00:21:02 -0400481 if len(projects_list) == 1 or jobs == 1:
Mike Frysinger339f2df2021-05-06 00:44:42 -0400482 self._FetchInitChild(ssh_proxy)
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500483 if not _ProcessResults(self._FetchProjectList(opt, x) for x in projects_list):
484 ret = False
485 else:
486 # Favor throughput over responsiveness when quiet. It seems that imap()
487 # will yield results in batches relative to chunksize, so even as the
488 # children finish a sync, we won't see the result until one child finishes
489 # ~chunksize jobs. When using a large --jobs with large chunksize, this
490 # can be jarring as there will be a large initial delay where repo looks
491 # like it isn't doing anything and sits at 0%, but then suddenly completes
492 # a lot of jobs all at once. Since this code is more network bound, we
493 # can accept a bit more CPU overhead with a smaller chunksize so that the
494 # user sees more immediate & continuous feedback.
495 if opt.quiet:
496 chunksize = WORKER_BATCH_SIZE
David James89ece422014-01-09 18:51:58 -0800497 else:
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500498 pm.update(inc=0, msg='warming up')
499 chunksize = 4
Raman Tenneti4a478ed2021-11-17 18:38:24 -0800500 with multiprocessing.Pool(jobs, initializer=self._FetchInitChild,
501 initargs=(ssh_proxy,)) as pool:
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500502 results = pool.imap_unordered(
503 functools.partial(self._FetchProjectList, opt),
504 projects_list,
505 chunksize=chunksize)
506 if not _ProcessResults(results):
507 ret = False
508 pool.close()
Roy Lee18afd7f2010-05-09 04:32:08 +0800509
Mike Frysinger339f2df2021-05-06 00:44:42 -0400510 # Cleanup the reference now that we're done with it, and we're going to
511 # release any resources it points to. If we don't, later multiprocessing
512 # usage (e.g. checkouts) will try to pickle and then crash.
513 del Sync.ssh_proxy
514
Shawn O. Pearce68194f42009-04-10 16:48:52 -0700515 pm.end()
Dave Borowitz67700e92012-10-23 15:00:54 -0700516 self._fetch_times.Save()
Dave Borowitz18857212012-10-23 17:02:59 -0700517
LaMont Jonesa46047a2022-04-07 21:57:06 +0000518 if not self.outer_client.manifest.IsArchive:
Mike Frysinger5a033082019-09-23 19:21:20 -0400519 self._GCProjects(projects, opt, err_event)
Julien Campergue335f5ef2013-10-16 11:02:35 +0200520
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500521 return (ret, fetched)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700522
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000523 def _FetchMain(self, opt, args, all_projects, err_event,
524 ssh_proxy, manifest):
Mike Frysingerb4429432021-05-05 20:03:26 -0400525 """The main network fetch loop.
526
527 Args:
528 opt: Program options returned from optparse. See _Options().
529 args: Command line args used to filter out projects.
Peter Kjellerstedtd1776092021-05-19 19:37:23 +0200530 all_projects: List of all projects that should be fetched.
Mike Frysingerb4429432021-05-05 20:03:26 -0400531 err_event: Whether an error was hit while processing.
Mike Frysinger339f2df2021-05-06 00:44:42 -0400532 ssh_proxy: SSH manager for clients & masters.
LaMont Jonesa46047a2022-04-07 21:57:06 +0000533 manifest: The manifest to use.
Peter Kjellerstedtd1776092021-05-19 19:37:23 +0200534
535 Returns:
536 List of all projects that should be checked out.
Mike Frysingerb4429432021-05-05 20:03:26 -0400537 """
LaMont Jonesa46047a2022-04-07 21:57:06 +0000538 rp = manifest.repoProject
Mike Frysingerb4429432021-05-05 20:03:26 -0400539
540 to_fetch = []
541 now = time.time()
542 if _ONE_DAY_S <= (now - rp.LastFetch):
543 to_fetch.append(rp)
544 to_fetch.extend(all_projects)
545 to_fetch.sort(key=self._fetch_times.Get, reverse=True)
546
Mike Frysinger339f2df2021-05-06 00:44:42 -0400547 success, fetched = self._Fetch(to_fetch, opt, err_event, ssh_proxy)
Mike Frysingerb4429432021-05-05 20:03:26 -0400548 if not success:
549 err_event.set()
550
551 _PostRepoFetch(rp, opt.repo_verify)
552 if opt.network_only:
553 # bail out now; the rest touches the working tree
554 if err_event.is_set():
555 print('\nerror: Exited sync due to fetch errors.\n', file=sys.stderr)
556 sys.exit(1)
557 return
558
559 # Iteratively fetch missing and/or nested unregistered submodules
560 previously_missing_set = set()
561 while True:
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000562 self._ReloadManifest(None, manifest)
Mike Frysingerb4429432021-05-05 20:03:26 -0400563 all_projects = self.GetProjects(args,
564 missing_ok=True,
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000565 submodules_ok=opt.fetch_submodules,
566 manifest=manifest,
567 all_manifests=not opt.this_manifest_only)
Mike Frysingerb4429432021-05-05 20:03:26 -0400568 missing = []
569 for project in all_projects:
570 if project.gitdir not in fetched:
571 missing.append(project)
572 if not missing:
573 break
574 # Stop us from non-stopped fetching actually-missing repos: If set of
575 # missing repos has not been changed from last fetch, we break.
576 missing_set = set(p.name for p in missing)
577 if previously_missing_set == missing_set:
578 break
579 previously_missing_set = missing_set
Mike Frysinger339f2df2021-05-06 00:44:42 -0400580 success, new_fetched = self._Fetch(missing, opt, err_event, ssh_proxy)
Mike Frysingerb4429432021-05-05 20:03:26 -0400581 if not success:
582 err_event.set()
583 fetched.update(new_fetched)
584
Peter Kjellerstedtd1776092021-05-19 19:37:23 +0200585 return all_projects
586
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500587 def _CheckoutOne(self, detach_head, force_sync, project):
Xin Li745be2e2019-06-03 11:24:30 -0700588 """Checkout work tree for one project
589
590 Args:
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500591 detach_head: Whether to leave a detached HEAD.
592 force_sync: Force checking out of the repo.
Xin Li745be2e2019-06-03 11:24:30 -0700593 project: Project object for the project to checkout.
Xin Li745be2e2019-06-03 11:24:30 -0700594
595 Returns:
596 Whether the fetch was successful.
597 """
Xin Li745be2e2019-06-03 11:24:30 -0700598 start = time.time()
LaMont Jonesa46047a2022-04-07 21:57:06 +0000599 syncbuf = SyncBuffer(project.manifest.manifestProject.config,
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500600 detach_head=detach_head)
Xin Li745be2e2019-06-03 11:24:30 -0700601 success = False
602 try:
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500603 project.Sync_LocalHalf(syncbuf, force_sync=force_sync)
Mike Frysingerebf04a42021-02-23 20:48:04 -0500604 success = syncbuf.Finish()
Raman Tennetiad8aa692021-04-15 09:20:51 -0700605 except GitError as e:
606 print('error.GitError: Cannot checkout %s: %s' %
607 (project.name, str(e)), file=sys.stderr)
Mike Frysingerebf04a42021-02-23 20:48:04 -0500608 except Exception as e:
609 print('error: Cannot checkout %s: %s: %s' %
610 (project.name, type(e).__name__, str(e)),
611 file=sys.stderr)
612 raise
Xin Li745be2e2019-06-03 11:24:30 -0700613
Mike Frysingerebf04a42021-02-23 20:48:04 -0500614 if not success:
615 print('error: Cannot checkout %s' % (project.name), file=sys.stderr)
616 finish = time.time()
617 return (success, project, start, finish)
Xin Li745be2e2019-06-03 11:24:30 -0700618
Mike Frysingerebf04a42021-02-23 20:48:04 -0500619 def _Checkout(self, all_projects, opt, err_results):
Xin Li745be2e2019-06-03 11:24:30 -0700620 """Checkout projects listed in all_projects
621
622 Args:
623 all_projects: List of all projects that should be checked out.
624 opt: Program options returned from optparse. See _Options().
Mike Frysingerebf04a42021-02-23 20:48:04 -0500625 err_results: A list of strings, paths to git repos where checkout failed.
Xin Li745be2e2019-06-03 11:24:30 -0700626 """
Mike Frysingerebf04a42021-02-23 20:48:04 -0500627 # Only checkout projects with worktrees.
628 all_projects = [x for x in all_projects if x.worktree]
Xin Li745be2e2019-06-03 11:24:30 -0700629
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500630 def _ProcessResults(pool, pm, results):
631 ret = True
Mike Frysingerebf04a42021-02-23 20:48:04 -0500632 for (success, project, start, finish) in results:
633 self.event_log.AddSync(project, event_log.TASK_SYNC_LOCAL,
634 start, finish, success)
635 # Check for any errors before running any more tasks.
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500636 # ...we'll let existing jobs finish, though.
Mike Frysingerebf04a42021-02-23 20:48:04 -0500637 if not success:
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500638 ret = False
Mike Frysingerebf04a42021-02-23 20:48:04 -0500639 err_results.append(project.relpath)
640 if opt.fail_fast:
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500641 if pool:
642 pool.close()
643 return ret
Mike Frysingerebf04a42021-02-23 20:48:04 -0500644 pm.update(msg=project.name)
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500645 return ret
Xin Li745be2e2019-06-03 11:24:30 -0700646
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500647 return self.ExecuteInParallel(
Mike Frysinger355f4392022-07-20 17:15:29 -0400648 opt.jobs_checkout,
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500649 functools.partial(self._CheckoutOne, opt.detach_head, opt.force_sync),
650 all_projects,
651 callback=_ProcessResults,
652 output=Progress('Checking out', len(all_projects), quiet=opt.quiet)) and not err_results
Mike Frysingerebf04a42021-02-23 20:48:04 -0500653
Mike Frysinger5a033082019-09-23 19:21:20 -0400654 def _GCProjects(self, projects, opt, err_event):
Mike Frysinger151701e2021-04-13 15:07:21 -0400655 pm = Progress('Garbage collecting', len(projects), delay=False, quiet=opt.quiet)
Mike Frysinger65af2602021-04-08 22:47:44 -0400656 pm.update(inc=0, msg='prescan')
657
Allen Webb4ee4a452021-10-07 10:42:38 -0500658 tidy_dirs = {}
David James8d201162013-10-11 17:03:19 -0700659 for project in projects:
LaMont Jones48ea25c2022-05-20 10:35:04 +0000660 # Make sure pruning never kicks in with shared projects that do not use
661 # alternates to avoid corruption.
Mike Frysinger979d5bd2020-02-09 02:28:34 -0500662 if (not project.use_git_worktrees and
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000663 len(project.manifest.GetProjectsWithName(project.name, all_manifests=True)) > 1):
LaMont Jones48ea25c2022-05-20 10:35:04 +0000664 if project.UseAlternates:
665 # Undo logic set by previous versions of repo.
666 project.config.SetString('extensions.preciousObjects', None)
667 project.config.SetString('gc.pruneExpire', None)
Mike Frysinger5f2b0452020-02-11 03:23:24 -0500668 else:
LaMont Jones48ea25c2022-05-20 10:35:04 +0000669 if not opt.quiet:
670 print('\r%s: Shared project %s found, disabling pruning.' %
671 (project.relpath, project.name))
672 if git_require((2, 7, 0)):
673 project.EnableRepositoryExtension('preciousObjects')
674 else:
675 # This isn't perfect, but it's the best we can do with old git.
676 print('\r%s: WARNING: shared projects are unreliable when using old '
677 '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
Mike Frysinger355f4392022-07-20 17:15:29 -0400694 jobs = opt.jobs
Dave Borowitz18857212012-10-23 17:02:59 -0700695
696 if jobs < 2:
Allen Webb4ee4a452021-10-07 10:42:38 -0500697 for (run_gc, bare_git) in tidy_dirs.values():
Mike Frysinger65af2602021-04-08 22:47:44 -0400698 pm.update(msg=bare_git._project.name)
Allen Webb4ee4a452021-10-07 10:42:38 -0500699 if run_gc:
700 bare_git.gc('--auto')
701 else:
702 bare_git.pack_refs()
Mike Frysinger65af2602021-04-08 22:47:44 -0400703 pm.end()
Dave Borowitz18857212012-10-23 17:02:59 -0700704 return
705
Mike Frysinger355f4392022-07-20 17:15:29 -0400706 cpu_count = os.cpu_count()
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 Frysingerae6cb082019-08-27 01:10:59 -04001014 def ValidateOptions(self, opt, args):
1015 if opt.force_broken:
1016 print('warning: -f/--force-broken is now the default behavior, and the '
1017 'options are deprecated', file=sys.stderr)
1018 if opt.network_only and opt.detach_head:
1019 self.OptionParser.error('cannot combine -n and -d')
1020 if opt.network_only and opt.local_only:
1021 self.OptionParser.error('cannot combine -n and -l')
1022 if opt.manifest_name and opt.smart_sync:
1023 self.OptionParser.error('cannot combine -m and -s')
1024 if opt.manifest_name and opt.smart_tag:
1025 self.OptionParser.error('cannot combine -m and -t')
1026 if opt.manifest_server_username or opt.manifest_server_password:
1027 if not (opt.smart_sync or opt.smart_tag):
1028 self.OptionParser.error('-u and -p may only be combined with -s or -t')
1029 if None in [opt.manifest_server_username, opt.manifest_server_password]:
1030 self.OptionParser.error('both -u and -p must be given')
1031
Mike Frysinger0531a622021-11-05 15:22:01 -04001032 if opt.prune is None:
1033 opt.prune = True
1034
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001035 def Execute(self, opt, args):
LaMont Jonesa46047a2022-04-07 21:57:06 +00001036 manifest = self.outer_manifest
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001037 if not opt.outer_manifest:
LaMont Jonesa46047a2022-04-07 21:57:06 +00001038 manifest = self.manifest
1039
Chris Wolfee9dc3b32012-01-26 11:36:18 -05001040 if opt.manifest_name:
LaMont Jonesa46047a2022-04-07 21:57:06 +00001041 manifest.Override(opt.manifest_name)
Shawn O. Pearce3e768c92009-04-10 16:59:36 -07001042
Chirayu Desaia892b102013-06-11 14:18:46 +05301043 manifest_name = opt.manifest_name
David Pursehouse59b41742015-05-07 14:36:09 +09001044 smart_sync_manifest_path = os.path.join(
LaMont Jonesa46047a2022-04-07 21:57:06 +00001045 manifest.manifestProject.worktree, 'smart_sync_override.xml')
Chirayu Desaia892b102013-06-11 14:18:46 +05301046
Xin Lid79a4bc2020-05-20 16:03:45 -07001047 if opt.clone_bundle is None:
LaMont Jonesa46047a2022-04-07 21:57:06 +00001048 opt.clone_bundle = manifest.CloneBundle
Xin Lid79a4bc2020-05-20 16:03:45 -07001049
Victor Boivie08c880d2011-04-19 10:32:52 +02001050 if opt.smart_sync or opt.smart_tag:
LaMont Jonesa46047a2022-04-07 21:57:06 +00001051 manifest_name = self._SmartSyncSetup(opt, smart_sync_manifest_path, manifest)
Mike Frysinger01d6c3c2019-08-27 01:56:43 -04001052 else:
David Pursehouse59b41742015-05-07 14:36:09 +09001053 if os.path.isfile(smart_sync_manifest_path):
1054 try:
Renaud Paquay010fed72016-11-11 14:25:29 -08001055 platform_utils.remove(smart_sync_manifest_path)
David Pursehouse59b41742015-05-07 14:36:09 +09001056 except OSError as e:
1057 print('error: failed to remove existing smart sync override manifest: %s' %
1058 e, file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -07001059
Mike Frysingerc99322a2021-05-04 15:32:43 -04001060 err_event = multiprocessing.Event()
Mike Frysinger5a033082019-09-23 19:21:20 -04001061
LaMont Jonesa46047a2022-04-07 21:57:06 +00001062 rp = manifest.repoProject
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001063 rp.PreSync()
Mike Frysinger23c900f2020-02-29 02:52:44 -05001064 cb = rp.CurrentBranch
1065 if cb:
1066 base = rp.GetBranch(cb).merge
1067 if not base or not base.startswith('refs/heads/'):
1068 print('warning: repo is not tracking a remote branch, so it will not '
Mike Frysinger58ac1672020-03-14 14:35:26 -04001069 'receive updates; run `repo init --repo-rev=stable` to fix.',
Mike Frysinger23c900f2020-02-29 02:52:44 -05001070 file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001071
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001072 for m in self.ManifestList(opt):
1073 mp = m.manifestProject
1074 is_standalone_manifest = bool(mp.standalone_manifest_url)
1075 if not is_standalone_manifest:
1076 mp.PreSync()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001077
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001078 if opt.repo_upgraded:
1079 _PostRepoUpgrade(m, quiet=opt.quiet)
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08001080
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001081 if opt.mp_update:
1082 self._UpdateAllManifestProjects(opt, mp, manifest_name)
1083 else:
Fredrik de Grootcc960972019-11-22 09:04:31 +01001084 print('Skipping update of local manifest project.')
Simran Basib9a1b732015-08-20 12:19:28 -07001085
Mike Frysinger355f4392022-07-20 17:15:29 -04001086 # Now that the manifests are up-to-date, setup the jobs value.
1087 if opt.jobs is None:
1088 # User has not made a choice, so use the manifest settings.
1089 opt.jobs = mp.default.sync_j
1090 if opt.jobs is not None:
1091 # Neither user nor manifest have made a choice.
1092 if opt.jobs_network is None:
1093 opt.jobs_network = opt.jobs
1094 if opt.jobs_checkout is None:
1095 opt.jobs_checkout = opt.jobs
1096 # Setup defaults if jobs==0.
1097 if not opt.jobs:
1098 if not opt.jobs_network:
1099 opt.jobs_network = 1
1100 if not opt.jobs_checkout:
1101 opt.jobs_checkout = DEFAULT_LOCAL_JOBS
1102 opt.jobs = os.cpu_count()
1103
1104 # Try to stay under user rlimit settings.
1105 #
1106 # Since each worker requires at 3 file descriptors to run `git fetch`, use
1107 # that to scale down the number of jobs. Unfortunately there isn't an easy
1108 # way to determine this reliably as systems change, but it was last measured
1109 # by hand in 2011.
1110 soft_limit, _ = _rlimit_nofile()
1111 jobs_soft_limit = max(1, (soft_limit - 5) // 3)
1112 opt.jobs = min(opt.jobs, jobs_soft_limit)
1113 opt.jobs_network = min(opt.jobs_network, jobs_soft_limit)
1114 opt.jobs_checkout = min(opt.jobs_checkout, jobs_soft_limit)
1115
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001116 superproject_logging_data = {}
1117 self._UpdateProjectsRevisionId(opt, args, superproject_logging_data,
1118 manifest)
Raman Tenneti1fd7bc22021-02-04 14:39:38 -08001119
Simran Basib9a1b732015-08-20 12:19:28 -07001120 if self.gitc_manifest:
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001121 gitc_manifest_projects = self.GetProjects(args, missing_ok=True)
Simran Basib9a1b732015-08-20 12:19:28 -07001122 gitc_projects = []
1123 opened_projects = []
1124 for project in gitc_manifest_projects:
Dan Willemsen5ea32d12015-09-08 13:27:20 -07001125 if project.relpath in self.gitc_manifest.paths and \
1126 self.gitc_manifest.paths[project.relpath].old_revision:
1127 opened_projects.append(project.relpath)
Simran Basib9a1b732015-08-20 12:19:28 -07001128 else:
Dan Willemsen5ea32d12015-09-08 13:27:20 -07001129 gitc_projects.append(project.relpath)
Simran Basib9a1b732015-08-20 12:19:28 -07001130
Dan Willemsen5ea32d12015-09-08 13:27:20 -07001131 if not args:
1132 gitc_projects = None
1133
1134 if gitc_projects != [] and not opt.local_only:
Simran Basib9a1b732015-08-20 12:19:28 -07001135 print('Updating GITC client: %s' % self.gitc_manifest.gitc_client_name)
Dan Willemsen5ea32d12015-09-08 13:27:20 -07001136 manifest = GitcManifest(self.repodir, self.gitc_manifest.gitc_client_name)
1137 if manifest_name:
1138 manifest.Override(manifest_name)
1139 else:
LaMont Jonesa46047a2022-04-07 21:57:06 +00001140 manifest.Override(manifest.manifestFile)
Dan Willemsen5ea32d12015-09-08 13:27:20 -07001141 gitc_utils.generate_gitc_manifest(self.gitc_manifest,
1142 manifest,
Simran Basib9a1b732015-08-20 12:19:28 -07001143 gitc_projects)
1144 print('GITC client successfully synced.')
1145
1146 # The opened projects need to be synced as normal, therefore we
1147 # generate a new args list to represent the opened projects.
Dan Willemsen5ea32d12015-09-08 13:27:20 -07001148 # TODO: make this more reliable -- if there's a project name/path overlap,
1149 # this may choose the wrong project.
LaMont Jonesa46047a2022-04-07 21:57:06 +00001150 args = [os.path.relpath(manifest.paths[path].worktree, os.getcwd())
David Pursehouse3bcd3052017-07-10 22:42:22 +09001151 for path in opened_projects]
Simran Basib9a1b732015-08-20 12:19:28 -07001152 if not args:
1153 return
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001154
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001155 all_projects = self.GetProjects(args,
1156 missing_ok=True,
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001157 submodules_ok=opt.fetch_submodules,
1158 manifest=manifest,
1159 all_manifests=not opt.this_manifest_only)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001160
Mike Frysinger5a033082019-09-23 19:21:20 -04001161 err_network_sync = False
1162 err_update_projects = False
Mike Frysinger5a033082019-09-23 19:21:20 -04001163
LaMont Jonesa46047a2022-04-07 21:57:06 +00001164 self._fetch_times = _FetchTimes(manifest)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -07001165 if not opt.local_only:
Mike Frysinger339f2df2021-05-06 00:44:42 -04001166 with multiprocessing.Manager() as manager:
1167 with ssh.ProxyManager(manager) as ssh_proxy:
1168 # Initialize the socket dir once in the parent.
1169 ssh_proxy.sock()
Peter Kjellerstedtd1776092021-05-19 19:37:23 +02001170 all_projects = self._FetchMain(opt, args, all_projects, err_event,
LaMont Jonesa46047a2022-04-07 21:57:06 +00001171 ssh_proxy, manifest)
Mike Frysinger339f2df2021-05-06 00:44:42 -04001172
1173 if opt.network_only:
1174 return
Mike Frysinger5a033082019-09-23 19:21:20 -04001175
1176 # If we saw an error, exit with code 1 so that other scripts can check.
Mike Frysingerbe24a542021-02-23 03:24:12 -05001177 if err_event.is_set():
Mike Frysinger5a033082019-09-23 19:21:20 -04001178 err_network_sync = True
1179 if opt.fail_fast:
1180 print('\nerror: Exited sync due to fetch errors.\n'
1181 'Local checkouts *not* updated. Resolve network issues & '
1182 'retry.\n'
1183 '`repo sync -l` will update some local checkouts.',
1184 file=sys.stderr)
1185 sys.exit(1)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001186
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001187 for m in self.ManifestList(opt):
1188 if m.IsMirror or m.IsArchive:
1189 # bail out now, we have no working tree
1190 continue
Shawn O. Pearcecd1d7ff2009-06-04 16:15:53 -07001191
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001192 if self.UpdateProjectList(opt, m):
1193 err_event.set()
1194 err_update_projects = True
1195 if opt.fail_fast:
1196 print('\nerror: Local checkouts *not* updated.', file=sys.stderr)
1197 sys.exit(1)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -07001198
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001199 err_update_linkfiles = not self.UpdateCopyLinkfileList(m)
1200 if err_update_linkfiles:
1201 err_event.set()
1202 if opt.fail_fast:
1203 print('\nerror: Local update copyfile or linkfile failed.', file=sys.stderr)
1204 sys.exit(1)
jiajia tanga590e642021-04-25 20:02:02 +08001205
Mike Frysinger5a033082019-09-23 19:21:20 -04001206 err_results = []
Mike Frysingerebf04a42021-02-23 20:48:04 -05001207 # NB: We don't exit here because this is the last step.
1208 err_checkout = not self._Checkout(all_projects, opt, err_results)
1209 if err_checkout:
1210 err_event.set()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001211
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001212 printed_notices = set()
1213 # If there's a notice that's supposed to print at the end of the sync,
1214 # print it now... But avoid printing duplicate messages, and preserve
1215 # order.
1216 for m in sorted(self.ManifestList(opt), key=lambda x: x.path_prefix):
1217 if m.notice and m.notice not in printed_notices:
1218 print(m.notice)
1219 printed_notices.add(m.notice)
Doug Anderson2b8db3c2010-11-01 15:08:06 -07001220
Mike Frysinger5a033082019-09-23 19:21:20 -04001221 # If we saw an error, exit with code 1 so that other scripts can check.
Mike Frysingerbe24a542021-02-23 03:24:12 -05001222 if err_event.is_set():
Mike Frysinger5a033082019-09-23 19:21:20 -04001223 print('\nerror: Unable to fully sync the tree.', file=sys.stderr)
1224 if err_network_sync:
1225 print('error: Downloading network changes failed.', file=sys.stderr)
1226 if err_update_projects:
1227 print('error: Updating local project lists failed.', file=sys.stderr)
jiajia tanga590e642021-04-25 20:02:02 +08001228 if err_update_linkfiles:
1229 print('error: Updating copyfiles or linkfiles failed.', file=sys.stderr)
Mike Frysinger5a033082019-09-23 19:21:20 -04001230 if err_checkout:
1231 print('error: Checking out local projects failed.', file=sys.stderr)
1232 if err_results:
1233 print('Failing repos:\n%s' % '\n'.join(err_results), file=sys.stderr)
1234 print('Try re-running with "-j1 --fail-fast" to exit at the first error.',
1235 file=sys.stderr)
1236 sys.exit(1)
1237
Raman Tenneti7954de12021-07-28 14:36:49 -07001238 # Log the previous sync analysis state from the config.
Raman Tenneti6448a4f2021-09-13 17:40:07 -07001239 self.git_event_log.LogDataConfigEvents(mp.config.GetSyncAnalysisStateData(),
1240 'previous_sync_state')
Raman Tenneti7954de12021-07-28 14:36:49 -07001241
1242 # Update and log with the new sync analysis state.
1243 mp.config.UpdateSyncAnalysisState(opt, superproject_logging_data)
Raman Tenneti6448a4f2021-09-13 17:40:07 -07001244 self.git_event_log.LogDataConfigEvents(mp.config.GetSyncAnalysisStateData(),
1245 'current_sync_state')
Raman Tenneti7954de12021-07-28 14:36:49 -07001246
Mike Frysingere19d9e12020-02-12 11:23:32 -05001247 if not opt.quiet:
1248 print('repo sync has finished successfully.')
1249
David Pursehouse819827a2020-02-12 15:20:19 +09001250
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -07001251def _PostRepoUpgrade(manifest, quiet=False):
Mike Frysingerfdeb20f2021-11-14 03:53:04 -05001252 # Link the docs for the internal .repo/ layout for people
1253 link = os.path.join(manifest.repodir, 'internal-fs-layout.md')
1254 if not platform_utils.islink(link):
1255 target = os.path.join('repo', 'docs', 'internal-fs-layout.md')
1256 try:
1257 platform_utils.symlink(target, link)
Raman Tenneti4a478ed2021-11-17 18:38:24 -08001258 except Exception:
Mike Frysingerfdeb20f2021-11-14 03:53:04 -05001259 pass
1260
Conley Owens094cdbe2014-01-30 15:09:59 -08001261 wrapper = Wrapper()
Conley Owensc9129d92012-10-01 16:12:28 -07001262 if wrapper.NeedSetupGnuPG():
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -07001263 wrapper.SetupGnuPG(quiet)
Conley Owensf2fe2d92014-01-29 13:53:43 -08001264 for project in manifest.projects:
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001265 if project.Exists:
1266 project.PostRepoUpgrade()
1267
David Pursehouse819827a2020-02-12 15:20:19 +09001268
Mike Frysingerc58ec4d2020-02-17 14:36:08 -05001269def _PostRepoFetch(rp, repo_verify=True, verbose=False):
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001270 if rp.HasChanges:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001271 print('info: A new version of repo is available', file=sys.stderr)
Mike Frysinger347f9ed2021-03-15 14:58:52 -04001272 wrapper = Wrapper()
1273 try:
1274 rev = rp.bare_git.describe(rp.GetRevisionId())
1275 except GitError:
1276 rev = None
1277 _, new_rev = wrapper.check_repo_rev(rp.gitdir, rev, repo_verify=repo_verify)
1278 # See if we're held back due to missing signed tag.
1279 current_revid = rp.bare_git.rev_parse('HEAD')
1280 new_revid = rp.bare_git.rev_parse('--verify', new_rev)
1281 if current_revid != new_revid:
1282 # We want to switch to the new rev, but also not trash any uncommitted
1283 # changes. This helps with local testing/hacking.
1284 # If a local change has been made, we will throw that away.
1285 # We also have to make sure this will switch to an older commit if that's
1286 # the latest tag in order to support release rollback.
1287 try:
1288 rp.work_git.reset('--keep', new_rev)
1289 except GitError as e:
1290 sys.exit(str(e))
Sarah Owenscecd1d82012-11-01 22:59:27 -07001291 print('info: Restarting repo with latest version', file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001292 raise RepoChangedException(['--repo-upgraded'])
1293 else:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001294 print('warning: Skipped upgrade to unverified version', file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001295 else:
1296 if verbose:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001297 print('repo version %s is current' % rp.work_git.describe(HEAD),
1298 file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001299
David Pursehouse819827a2020-02-12 15:20:19 +09001300
Dave Borowitz67700e92012-10-23 15:00:54 -07001301class _FetchTimes(object):
Dave Borowitzd9478582012-10-23 16:35:39 -07001302 _ALPHA = 0.5
1303
Dave Borowitz67700e92012-10-23 15:00:54 -07001304 def __init__(self, manifest):
Anthony King85b24ac2014-05-06 15:57:48 +01001305 self._path = os.path.join(manifest.repodir, '.repo_fetchtimes.json')
Dave Borowitz67700e92012-10-23 15:00:54 -07001306 self._times = None
Dave Borowitzd9478582012-10-23 16:35:39 -07001307 self._seen = set()
Dave Borowitz67700e92012-10-23 15:00:54 -07001308
1309 def Get(self, project):
1310 self._Load()
1311 return self._times.get(project.name, _ONE_DAY_S)
1312
1313 def Set(self, project, t):
Dave Borowitzd9478582012-10-23 16:35:39 -07001314 self._Load()
1315 name = project.name
1316 old = self._times.get(name, t)
1317 self._seen.add(name)
1318 a = self._ALPHA
David Pursehouse54a4e602020-02-12 14:31:05 +09001319 self._times[name] = (a * t) + ((1 - a) * old)
Dave Borowitz67700e92012-10-23 15:00:54 -07001320
1321 def _Load(self):
1322 if self._times is None:
1323 try:
Mike Frysinger3164d402019-11-11 05:40:22 -05001324 with open(self._path) as f:
Anthony King85b24ac2014-05-06 15:57:48 +01001325 self._times = json.load(f)
Anthony King85b24ac2014-05-06 15:57:48 +01001326 except (IOError, ValueError):
Mike Frysinger9d96f582021-09-28 11:27:24 -04001327 platform_utils.remove(self._path, missing_ok=True)
Anthony King85b24ac2014-05-06 15:57:48 +01001328 self._times = {}
Dave Borowitz67700e92012-10-23 15:00:54 -07001329
1330 def Save(self):
1331 if self._times is None:
1332 return
Dave Borowitzd9478582012-10-23 16:35:39 -07001333
1334 to_delete = []
1335 for name in self._times:
1336 if name not in self._seen:
1337 to_delete.append(name)
1338 for name in to_delete:
1339 del self._times[name]
1340
Dave Borowitz67700e92012-10-23 15:00:54 -07001341 try:
Mike Frysinger3164d402019-11-11 05:40:22 -05001342 with open(self._path, 'w') as f:
Anthony King85b24ac2014-05-06 15:57:48 +01001343 json.dump(self._times, f, indent=2)
Anthony King85b24ac2014-05-06 15:57:48 +01001344 except (IOError, TypeError):
Mike Frysinger9d96f582021-09-28 11:27:24 -04001345 platform_utils.remove(self._path, missing_ok=True)
Dan Willemsen0745bb22015-08-17 13:41:45 -07001346
1347# This is a replacement for xmlrpc.client.Transport using urllib2
1348# and supporting persistent-http[s]. It cannot change hosts from
1349# request to request like the normal transport, the real url
1350# is passed during initialization.
David Pursehouse819827a2020-02-12 15:20:19 +09001351
1352
Dan Willemsen0745bb22015-08-17 13:41:45 -07001353class PersistentTransport(xmlrpc.client.Transport):
1354 def __init__(self, orig_host):
1355 self.orig_host = orig_host
1356
1357 def request(self, host, handler, request_body, verbose=False):
1358 with GetUrlCookieFile(self.orig_host, not verbose) as (cookiefile, proxy):
1359 # Python doesn't understand cookies with the #HttpOnly_ prefix
1360 # Since we're only using them for HTTP, copy the file temporarily,
1361 # stripping those prefixes away.
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001362 if cookiefile:
Collin Fijalkoviche1191b32020-02-18 10:57:32 -08001363 tmpcookiefile = tempfile.NamedTemporaryFile(mode='w')
David Pursehouse4c5f74e2015-10-02 11:10:10 +09001364 tmpcookiefile.write("# HTTP Cookie File")
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001365 try:
1366 with open(cookiefile) as f:
1367 for line in f:
1368 if line.startswith("#HttpOnly_"):
1369 line = line[len("#HttpOnly_"):]
1370 tmpcookiefile.write(line)
1371 tmpcookiefile.flush()
Dan Willemsen0745bb22015-08-17 13:41:45 -07001372
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001373 cookiejar = cookielib.MozillaCookieJar(tmpcookiefile.name)
David Pursehouseb1ad2192015-09-30 10:35:43 +09001374 try:
1375 cookiejar.load()
1376 except cookielib.LoadError:
1377 cookiejar = cookielib.CookieJar()
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001378 finally:
1379 tmpcookiefile.close()
1380 else:
1381 cookiejar = cookielib.CookieJar()
Dan Willemsen0745bb22015-08-17 13:41:45 -07001382
1383 proxyhandler = urllib.request.ProxyHandler
1384 if proxy:
1385 proxyhandler = urllib.request.ProxyHandler({
1386 "http": proxy,
David Pursehouse54a4e602020-02-12 14:31:05 +09001387 "https": proxy})
Dan Willemsen0745bb22015-08-17 13:41:45 -07001388
1389 opener = urllib.request.build_opener(
1390 urllib.request.HTTPCookieProcessor(cookiejar),
1391 proxyhandler)
1392
1393 url = urllib.parse.urljoin(self.orig_host, handler)
1394 parse_results = urllib.parse.urlparse(url)
1395
1396 scheme = parse_results.scheme
1397 if scheme == 'persistent-http':
1398 scheme = 'http'
1399 if scheme == 'persistent-https':
1400 # If we're proxying through persistent-https, use http. The
1401 # proxy itself will do the https.
1402 if proxy:
1403 scheme = 'http'
1404 else:
1405 scheme = 'https'
1406
1407 # Parse out any authentication information using the base class
1408 host, extra_headers, _ = self.get_host_info(parse_results.netloc)
1409
1410 url = urllib.parse.urlunparse((
1411 scheme,
1412 host,
1413 parse_results.path,
1414 parse_results.params,
1415 parse_results.query,
1416 parse_results.fragment))
1417
1418 request = urllib.request.Request(url, request_body)
1419 if extra_headers is not None:
1420 for (name, header) in extra_headers:
1421 request.add_header(name, header)
1422 request.add_header('Content-Type', 'text/xml')
1423 try:
1424 response = opener.open(request)
1425 except urllib.error.HTTPError as e:
1426 if e.code == 501:
1427 # We may have been redirected through a login process
1428 # but our POST turned into a GET. Retry.
1429 response = opener.open(request)
1430 else:
1431 raise
1432
1433 p, u = xmlrpc.client.getparser()
1434 while 1:
1435 data = response.read(1024)
1436 if not data:
1437 break
1438 p.feed(data)
1439 p.close()
1440 return u.close()
1441
1442 def close(self):
1443 pass