blob: 57ee0540a98d1f4736ff59f0a8d8971b45f8778b [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:
Mike Frysinger5f2b0452020-02-11 03:23:24 -0500660 # Make sure pruning never kicks in with shared projects.
Mike Frysinger979d5bd2020-02-09 02:28:34 -0500661 if (not project.use_git_worktrees and
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000662 len(project.manifest.GetProjectsWithName(project.name, all_manifests=True)) > 1):
Anders Björklund2a2da802021-01-18 10:32:36 +0100663 if not opt.quiet:
Mike Frysinger65af2602021-04-08 22:47:44 -0400664 print('\r%s: Shared project %s found, disabling pruning.' %
Anders Björklund2a2da802021-01-18 10:32:36 +0100665 (project.relpath, project.name))
Mike Frysinger5f2b0452020-02-11 03:23:24 -0500666 if git_require((2, 7, 0)):
Mike Frysingerf81c72e2020-02-19 15:50:00 -0500667 project.EnableRepositoryExtension('preciousObjects')
Mike Frysinger5f2b0452020-02-11 03:23:24 -0500668 else:
669 # This isn't perfect, but it's the best we can do with old git.
Mike Frysinger65af2602021-04-08 22:47:44 -0400670 print('\r%s: WARNING: shared projects are unreliable when using old '
Mike Frysinger5f2b0452020-02-11 03:23:24 -0500671 'versions of git; please upgrade to git-2.7.0+.'
672 % (project.relpath,),
673 file=sys.stderr)
674 project.config.SetString('gc.pruneExpire', 'never')
Allen Webb669efd02021-10-01 15:25:31 -0500675 project.config.SetString('gc.autoDetach', 'false')
Allen Webb4ee4a452021-10-07 10:42:38 -0500676 # Only call git gc once per objdir, but call pack-refs for the remainder.
677 if project.objdir not in tidy_dirs:
678 tidy_dirs[project.objdir] = (
679 True, # Run a full gc.
680 project.bare_git,
681 )
682 elif project.gitdir not in tidy_dirs:
683 tidy_dirs[project.gitdir] = (
684 False, # Do not run a full gc; just run pack-refs.
685 project.bare_git,
686 )
Mike Frysinger65af2602021-04-08 22:47:44 -0400687
Mike Frysinger355f4392022-07-20 17:15:29 -0400688 jobs = opt.jobs
Dave Borowitz18857212012-10-23 17:02:59 -0700689
690 if jobs < 2:
Allen Webb4ee4a452021-10-07 10:42:38 -0500691 for (run_gc, bare_git) in tidy_dirs.values():
Mike Frysinger65af2602021-04-08 22:47:44 -0400692 pm.update(msg=bare_git._project.name)
Allen Webb4ee4a452021-10-07 10:42:38 -0500693 if run_gc:
694 bare_git.gc('--auto')
695 else:
696 bare_git.pack_refs()
Mike Frysinger65af2602021-04-08 22:47:44 -0400697 pm.end()
Dave Borowitz18857212012-10-23 17:02:59 -0700698 return
699
Mike Frysinger355f4392022-07-20 17:15:29 -0400700 cpu_count = os.cpu_count()
Mike Frysinger0c0e9342019-06-13 12:42:39 -0400701 config = {'pack.threads': cpu_count // jobs if cpu_count > jobs else 1}
Dave Borowitz18857212012-10-23 17:02:59 -0700702
703 threads = set()
704 sem = _threading.Semaphore(jobs)
Dave Borowitz18857212012-10-23 17:02:59 -0700705
Allen Webb4ee4a452021-10-07 10:42:38 -0500706 def tidy_up(run_gc, bare_git):
Mike Frysinger65af2602021-04-08 22:47:44 -0400707 pm.start(bare_git._project.name)
Dave Borowitz18857212012-10-23 17:02:59 -0700708 try:
709 try:
Allen Webb4ee4a452021-10-07 10:42:38 -0500710 if run_gc:
711 bare_git.gc('--auto', config=config)
712 else:
713 bare_git.pack_refs(config=config)
Dave Borowitz18857212012-10-23 17:02:59 -0700714 except GitError:
715 err_event.set()
David Pursehouse145e35b2020-02-12 15:40:47 +0900716 except Exception:
Dave Borowitz18857212012-10-23 17:02:59 -0700717 err_event.set()
718 raise
719 finally:
Mike Frysinger65af2602021-04-08 22:47:44 -0400720 pm.finish(bare_git._project.name)
Dave Borowitz18857212012-10-23 17:02:59 -0700721 sem.release()
722
Allen Webb4ee4a452021-10-07 10:42:38 -0500723 for (run_gc, bare_git) in tidy_dirs.values():
Mike Frysingerbe24a542021-02-23 03:24:12 -0500724 if err_event.is_set() and opt.fail_fast:
Dave Borowitz18857212012-10-23 17:02:59 -0700725 break
726 sem.acquire()
Allen Webb4ee4a452021-10-07 10:42:38 -0500727 t = _threading.Thread(target=tidy_up, args=(run_gc, bare_git,))
Dave Borowitz18857212012-10-23 17:02:59 -0700728 t.daemon = True
729 threads.add(t)
730 t.start()
731
732 for t in threads:
733 t.join()
Mike Frysinger65af2602021-04-08 22:47:44 -0400734 pm.end()
Dave Borowitz18857212012-10-23 17:02:59 -0700735
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000736 def _ReloadManifest(self, manifest_name, manifest):
Raman Tennetifeb28912021-05-02 19:47:29 -0700737 """Reload the manfiest from the file specified by the |manifest_name|.
738
739 It unloads the manifest if |manifest_name| is None.
740
741 Args:
742 manifest_name: Manifest file to be reloaded.
LaMont Jonesa46047a2022-04-07 21:57:06 +0000743 manifest: The manifest to use.
Raman Tennetifeb28912021-05-02 19:47:29 -0700744 """
Tim Kilbourn07669002013-03-08 15:02:49 -0800745 if manifest_name:
LaMont Jonesa2ff20d2022-04-07 16:49:06 +0000746 # Override calls Unload already
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000747 manifest.Override(manifest_name)
Tim Kilbourn07669002013-03-08 15:02:49 -0800748 else:
LaMont Jonesa46047a2022-04-07 21:57:06 +0000749 manifest.Unload()
Tim Kilbourn07669002013-03-08 15:02:49 -0800750
LaMont Jonesa46047a2022-04-07 21:57:06 +0000751 def UpdateProjectList(self, opt, manifest):
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000752 """Update the cached projects list for |manifest|
753
754 In a multi-manifest checkout, each manifest has its own project.list.
755
756 Args:
757 opt: Program options returned from optparse. See _Options().
758 manifest: The manifest to use.
759
760 Returns:
761 0: success
762 1: failure
763 """
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700764 new_project_paths = []
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000765 for project in self.GetProjects(None, missing_ok=True, manifest=manifest,
766 all_manifests=False):
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700767 if project.relpath:
768 new_project_paths.append(project.relpath)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700769 file_name = 'project.list'
LaMont Jonesa46047a2022-04-07 21:57:06 +0000770 file_path = os.path.join(manifest.subdir, file_name)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700771 old_project_paths = []
772
773 if os.path.exists(file_path):
Mike Frysinger3164d402019-11-11 05:40:22 -0500774 with open(file_path, 'r') as fd:
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700775 old_project_paths = fd.read().split('\n')
Kuang-che Wu0d9b16d2019-04-06 00:49:47 +0800776 # In reversed order, so subfolders are deleted before parent folder.
777 for path in sorted(old_project_paths, reverse=True):
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700778 if not path:
779 continue
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700780 if path not in new_project_paths:
David Pursehouse8a68ff92012-09-24 12:15:13 +0900781 # If the path has already been deleted, we don't need to do it
LaMont Jonesa46047a2022-04-07 21:57:06 +0000782 gitdir = os.path.join(manifest.topdir, path, '.git')
Dan Willemsen43507912016-09-01 16:26:02 -0700783 if os.path.exists(gitdir):
David Pursehousec1b86a22012-11-14 11:36:51 +0900784 project = Project(
LaMont Jonesa46047a2022-04-07 21:57:06 +0000785 manifest=manifest,
David Pursehouseabdf7502020-02-12 14:58:39 +0900786 name=path,
787 remote=RemoteSpec('origin'),
788 gitdir=gitdir,
789 objdir=gitdir,
Mike Frysingerc0d18662020-02-19 19:19:18 -0500790 use_git_worktrees=os.path.isfile(gitdir),
LaMont Jonesa46047a2022-04-07 21:57:06 +0000791 worktree=os.path.join(manifest.topdir, path),
David Pursehouseabdf7502020-02-12 14:58:39 +0900792 relpath=path,
793 revisionExpr='HEAD',
794 revisionId=None,
795 groups=None)
Mike Frysingerc0d18662020-02-19 19:19:18 -0500796 if not project.DeleteWorktree(
David Pursehouseaa611a22020-02-20 10:47:26 +0900797 quiet=opt.quiet,
798 force=opt.force_remove_dirty):
Mike Frysingera850ca22019-08-07 17:19:24 -0400799 return 1
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700800
Shawn O. Pearce9fb29ce2009-06-04 20:41:02 -0700801 new_project_paths.sort()
Mike Frysinger3164d402019-11-11 05:40:22 -0500802 with open(file_path, 'w') as fd:
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700803 fd.write('\n'.join(new_project_paths))
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700804 fd.write('\n')
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700805 return 0
806
LaMont Jonesa46047a2022-04-07 21:57:06 +0000807 def UpdateCopyLinkfileList(self, manifest):
jiajia tanga590e642021-04-25 20:02:02 +0800808 """Save all dests of copyfile and linkfile, and update them if needed.
809
810 Returns:
811 Whether update was successful.
812 """
813 new_paths = {}
814 new_linkfile_paths = []
815 new_copyfile_paths = []
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000816 for project in self.GetProjects(None, missing_ok=True,
817 manifest=manifest, all_manifests=False):
jiajia tanga590e642021-04-25 20:02:02 +0800818 new_linkfile_paths.extend(x.dest for x in project.linkfiles)
819 new_copyfile_paths.extend(x.dest for x in project.copyfiles)
820
821 new_paths = {
822 'linkfile': new_linkfile_paths,
823 'copyfile': new_copyfile_paths,
824 }
825
826 copylinkfile_name = 'copy-link-files.json'
LaMont Jonesa46047a2022-04-07 21:57:06 +0000827 copylinkfile_path = os.path.join(manifest.subdir, copylinkfile_name)
jiajia tanga590e642021-04-25 20:02:02 +0800828 old_copylinkfile_paths = {}
829
830 if os.path.exists(copylinkfile_path):
831 with open(copylinkfile_path, 'rb') as fp:
832 try:
833 old_copylinkfile_paths = json.load(fp)
Raman Tenneti4a478ed2021-11-17 18:38:24 -0800834 except Exception:
jiajia tanga590e642021-04-25 20:02:02 +0800835 print('error: %s is not a json formatted file.' %
836 copylinkfile_path, file=sys.stderr)
837 platform_utils.remove(copylinkfile_path)
838 return False
839
840 need_remove_files = []
841 need_remove_files.extend(
842 set(old_copylinkfile_paths.get('linkfile', [])) -
843 set(new_linkfile_paths))
844 need_remove_files.extend(
845 set(old_copylinkfile_paths.get('copyfile', [])) -
846 set(new_copyfile_paths))
847
848 for need_remove_file in need_remove_files:
Mike Frysinger9d96f582021-09-28 11:27:24 -0400849 # Try to remove the updated copyfile or linkfile.
850 # So, if the file is not exist, nothing need to do.
851 platform_utils.remove(need_remove_file, missing_ok=True)
jiajia tanga590e642021-04-25 20:02:02 +0800852
853 # Create copy-link-files.json, save dest path of "copyfile" and "linkfile".
854 with open(copylinkfile_path, 'w', encoding='utf-8') as fp:
855 json.dump(new_paths, fp)
856 return True
857
LaMont Jonesa46047a2022-04-07 21:57:06 +0000858 def _SmartSyncSetup(self, opt, smart_sync_manifest_path, manifest):
859 if not manifest.manifest_server:
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400860 print('error: cannot smart sync: no manifest server defined in '
861 'manifest', file=sys.stderr)
862 sys.exit(1)
863
LaMont Jonesa46047a2022-04-07 21:57:06 +0000864 manifest_server = manifest.manifest_server
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400865 if not opt.quiet:
866 print('Using manifest server %s' % manifest_server)
867
David Pursehouseeeff3532020-02-12 11:24:10 +0900868 if '@' not in manifest_server:
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400869 username = None
870 password = None
871 if opt.manifest_server_username and opt.manifest_server_password:
872 username = opt.manifest_server_username
873 password = opt.manifest_server_password
874 else:
875 try:
876 info = netrc.netrc()
877 except IOError:
878 # .netrc file does not exist or could not be opened
879 pass
880 else:
881 try:
882 parse_result = urllib.parse.urlparse(manifest_server)
883 if parse_result.hostname:
884 auth = info.authenticators(parse_result.hostname)
885 if auth:
886 username, _account, password = auth
887 else:
888 print('No credentials found for %s in .netrc'
889 % parse_result.hostname, file=sys.stderr)
890 except netrc.NetrcParseError as e:
891 print('Error parsing .netrc file: %s' % e, file=sys.stderr)
892
893 if (username and password):
894 manifest_server = manifest_server.replace('://', '://%s:%s@' %
895 (username, password),
896 1)
897
898 transport = PersistentTransport(manifest_server)
899 if manifest_server.startswith('persistent-'):
900 manifest_server = manifest_server[len('persistent-'):]
901
902 try:
903 server = xmlrpc.client.Server(manifest_server, transport=transport)
904 if opt.smart_sync:
LaMont Jonesa46047a2022-04-07 21:57:06 +0000905 branch = self._GetBranch(manifest.manifestProject)
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400906
Mike Frysinger56ce3462019-12-04 19:30:48 -0500907 if 'SYNC_TARGET' in os.environ:
Mike Frysingere20da3e2020-03-07 01:53:53 -0500908 target = os.environ['SYNC_TARGET']
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400909 [success, manifest_str] = server.GetApprovedManifest(branch, target)
Mike Frysinger56ce3462019-12-04 19:30:48 -0500910 elif ('TARGET_PRODUCT' in os.environ and
911 'TARGET_BUILD_VARIANT' in os.environ):
Mike Frysingere20da3e2020-03-07 01:53:53 -0500912 target = '%s-%s' % (os.environ['TARGET_PRODUCT'],
913 os.environ['TARGET_BUILD_VARIANT'])
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400914 [success, manifest_str] = server.GetApprovedManifest(branch, target)
915 else:
916 [success, manifest_str] = server.GetApprovedManifest(branch)
917 else:
918 assert(opt.smart_tag)
919 [success, manifest_str] = server.GetManifest(opt.smart_tag)
920
921 if success:
922 manifest_name = os.path.basename(smart_sync_manifest_path)
923 try:
Mike Frysinger3164d402019-11-11 05:40:22 -0500924 with open(smart_sync_manifest_path, 'w') as f:
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400925 f.write(manifest_str)
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400926 except IOError as e:
927 print('error: cannot write manifest to %s:\n%s'
928 % (smart_sync_manifest_path, e),
929 file=sys.stderr)
930 sys.exit(1)
LaMont Jonesa46047a2022-04-07 21:57:06 +0000931 self._ReloadManifest(manifest_name, manifest)
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400932 else:
933 print('error: manifest server RPC call failed: %s' %
934 manifest_str, file=sys.stderr)
935 sys.exit(1)
936 except (socket.error, IOError, xmlrpc.client.Fault) as e:
937 print('error: cannot connect to manifest server %s:\n%s'
LaMont Jonesa46047a2022-04-07 21:57:06 +0000938 % (manifest.manifest_server, e), file=sys.stderr)
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400939 sys.exit(1)
940 except xmlrpc.client.ProtocolError as e:
941 print('error: cannot connect to manifest server %s:\n%d %s'
LaMont Jonesa46047a2022-04-07 21:57:06 +0000942 % (manifest.manifest_server, e.errcode, e.errmsg),
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400943 file=sys.stderr)
944 sys.exit(1)
945
946 return manifest_name
947
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000948 def _UpdateAllManifestProjects(self, opt, mp, manifest_name):
949 """Fetch & update the local manifest project.
950
951 After syncing the manifest project, if the manifest has any sub manifests,
952 those are recursively processed.
953
954 Args:
955 opt: Program options returned from optparse. See _Options().
956 mp: the manifestProject to query.
957 manifest_name: Manifest file to be reloaded.
958 """
959 if not mp.standalone_manifest_url:
960 self._UpdateManifestProject(opt, mp, manifest_name)
961
962 if mp.manifest.submanifests:
963 for submanifest in mp.manifest.submanifests.values():
964 child = submanifest.repo_client.manifest
965 child.manifestProject.SyncWithPossibleInit(
966 submanifest,
967 current_branch_only=self._GetCurrentBranchOnly(opt, child),
968 verbose=opt.verbose,
969 tags=opt.tags,
970 git_event_log=self.git_event_log,
971 )
972 self._UpdateAllManifestProjects(opt, child.manifestProject, None)
973
Mike Frysingerfb527e32019-08-27 02:34:32 -0400974 def _UpdateManifestProject(self, opt, mp, manifest_name):
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000975 """Fetch & update the local manifest project.
976
977 Args:
978 opt: Program options returned from optparse. See _Options().
979 mp: the manifestProject to query.
980 manifest_name: Manifest file to be reloaded.
981 """
Mike Frysingerfb527e32019-08-27 02:34:32 -0400982 if not opt.local_only:
983 start = time.time()
Mike Frysinger521d01b2020-02-17 01:51:49 -0500984 success = mp.Sync_NetworkHalf(quiet=opt.quiet, verbose=opt.verbose,
LaMont Jonesa46047a2022-04-07 21:57:06 +0000985 current_branch_only=self._GetCurrentBranchOnly(opt, mp.manifest),
Erwan Yvindc5c4d12019-06-18 13:49:12 +0200986 force_sync=opt.force_sync,
Mike Frysingerc58ec4d2020-02-17 14:36:08 -0500987 tags=opt.tags,
Mike Frysingerfb527e32019-08-27 02:34:32 -0400988 optimized_fetch=opt.optimized_fetch,
George Engelbrecht9bc283e2020-04-02 12:36:09 -0600989 retry_fetches=opt.retry_fetches,
LaMont Jonesa46047a2022-04-07 21:57:06 +0000990 submodules=mp.manifest.HasSubmodules,
991 clone_filter=mp.manifest.CloneFilter,
992 partial_clone_exclude=mp.manifest.PartialCloneExclude)
Mike Frysingerfb527e32019-08-27 02:34:32 -0400993 finish = time.time()
994 self.event_log.AddSync(mp, event_log.TASK_SYNC_NETWORK,
995 start, finish, success)
996
997 if mp.HasChanges:
998 syncbuf = SyncBuffer(mp.config)
999 start = time.time()
LaMont Jonesa46047a2022-04-07 21:57:06 +00001000 mp.Sync_LocalHalf(syncbuf, submodules=mp.manifest.HasSubmodules)
Mike Frysingerfb527e32019-08-27 02:34:32 -04001001 clean = syncbuf.Finish()
1002 self.event_log.AddSync(mp, event_log.TASK_SYNC_LOCAL,
1003 start, time.time(), clean)
1004 if not clean:
1005 sys.exit(1)
LaMont Jonesa46047a2022-04-07 21:57:06 +00001006 self._ReloadManifest(manifest_name, mp.manifest)
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001007
Mike Frysingerae6cb082019-08-27 01:10:59 -04001008 def ValidateOptions(self, opt, args):
1009 if opt.force_broken:
1010 print('warning: -f/--force-broken is now the default behavior, and the '
1011 'options are deprecated', file=sys.stderr)
1012 if opt.network_only and opt.detach_head:
1013 self.OptionParser.error('cannot combine -n and -d')
1014 if opt.network_only and opt.local_only:
1015 self.OptionParser.error('cannot combine -n and -l')
1016 if opt.manifest_name and opt.smart_sync:
1017 self.OptionParser.error('cannot combine -m and -s')
1018 if opt.manifest_name and opt.smart_tag:
1019 self.OptionParser.error('cannot combine -m and -t')
1020 if opt.manifest_server_username or opt.manifest_server_password:
1021 if not (opt.smart_sync or opt.smart_tag):
1022 self.OptionParser.error('-u and -p may only be combined with -s or -t')
1023 if None in [opt.manifest_server_username, opt.manifest_server_password]:
1024 self.OptionParser.error('both -u and -p must be given')
1025
Mike Frysinger0531a622021-11-05 15:22:01 -04001026 if opt.prune is None:
1027 opt.prune = True
1028
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001029 def Execute(self, opt, args):
LaMont Jonesa46047a2022-04-07 21:57:06 +00001030 manifest = self.outer_manifest
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001031 if not opt.outer_manifest:
LaMont Jonesa46047a2022-04-07 21:57:06 +00001032 manifest = self.manifest
1033
Chris Wolfee9dc3b32012-01-26 11:36:18 -05001034 if opt.manifest_name:
LaMont Jonesa46047a2022-04-07 21:57:06 +00001035 manifest.Override(opt.manifest_name)
Shawn O. Pearce3e768c92009-04-10 16:59:36 -07001036
Chirayu Desaia892b102013-06-11 14:18:46 +05301037 manifest_name = opt.manifest_name
David Pursehouse59b41742015-05-07 14:36:09 +09001038 smart_sync_manifest_path = os.path.join(
LaMont Jonesa46047a2022-04-07 21:57:06 +00001039 manifest.manifestProject.worktree, 'smart_sync_override.xml')
Chirayu Desaia892b102013-06-11 14:18:46 +05301040
Xin Lid79a4bc2020-05-20 16:03:45 -07001041 if opt.clone_bundle is None:
LaMont Jonesa46047a2022-04-07 21:57:06 +00001042 opt.clone_bundle = manifest.CloneBundle
Xin Lid79a4bc2020-05-20 16:03:45 -07001043
Victor Boivie08c880d2011-04-19 10:32:52 +02001044 if opt.smart_sync or opt.smart_tag:
LaMont Jonesa46047a2022-04-07 21:57:06 +00001045 manifest_name = self._SmartSyncSetup(opt, smart_sync_manifest_path, manifest)
Mike Frysinger01d6c3c2019-08-27 01:56:43 -04001046 else:
David Pursehouse59b41742015-05-07 14:36:09 +09001047 if os.path.isfile(smart_sync_manifest_path):
1048 try:
Renaud Paquay010fed72016-11-11 14:25:29 -08001049 platform_utils.remove(smart_sync_manifest_path)
David Pursehouse59b41742015-05-07 14:36:09 +09001050 except OSError as e:
1051 print('error: failed to remove existing smart sync override manifest: %s' %
1052 e, file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -07001053
Mike Frysingerc99322a2021-05-04 15:32:43 -04001054 err_event = multiprocessing.Event()
Mike Frysinger5a033082019-09-23 19:21:20 -04001055
LaMont Jonesa46047a2022-04-07 21:57:06 +00001056 rp = manifest.repoProject
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001057 rp.PreSync()
Mike Frysinger23c900f2020-02-29 02:52:44 -05001058 cb = rp.CurrentBranch
1059 if cb:
1060 base = rp.GetBranch(cb).merge
1061 if not base or not base.startswith('refs/heads/'):
1062 print('warning: repo is not tracking a remote branch, so it will not '
Mike Frysinger58ac1672020-03-14 14:35:26 -04001063 'receive updates; run `repo init --repo-rev=stable` to fix.',
Mike Frysinger23c900f2020-02-29 02:52:44 -05001064 file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001065
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001066 for m in self.ManifestList(opt):
1067 mp = m.manifestProject
1068 is_standalone_manifest = bool(mp.standalone_manifest_url)
1069 if not is_standalone_manifest:
1070 mp.PreSync()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001071
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001072 if opt.repo_upgraded:
1073 _PostRepoUpgrade(m, quiet=opt.quiet)
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08001074
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001075 if opt.mp_update:
1076 self._UpdateAllManifestProjects(opt, mp, manifest_name)
1077 else:
Fredrik de Grootcc960972019-11-22 09:04:31 +01001078 print('Skipping update of local manifest project.')
Simran Basib9a1b732015-08-20 12:19:28 -07001079
Mike Frysinger355f4392022-07-20 17:15:29 -04001080 # Now that the manifests are up-to-date, setup the jobs value.
1081 if opt.jobs is None:
1082 # User has not made a choice, so use the manifest settings.
1083 opt.jobs = mp.default.sync_j
1084 if opt.jobs is not None:
1085 # Neither user nor manifest have made a choice.
1086 if opt.jobs_network is None:
1087 opt.jobs_network = opt.jobs
1088 if opt.jobs_checkout is None:
1089 opt.jobs_checkout = opt.jobs
1090 # Setup defaults if jobs==0.
1091 if not opt.jobs:
1092 if not opt.jobs_network:
1093 opt.jobs_network = 1
1094 if not opt.jobs_checkout:
1095 opt.jobs_checkout = DEFAULT_LOCAL_JOBS
1096 opt.jobs = os.cpu_count()
1097
1098 # Try to stay under user rlimit settings.
1099 #
1100 # Since each worker requires at 3 file descriptors to run `git fetch`, use
1101 # that to scale down the number of jobs. Unfortunately there isn't an easy
1102 # way to determine this reliably as systems change, but it was last measured
1103 # by hand in 2011.
1104 soft_limit, _ = _rlimit_nofile()
1105 jobs_soft_limit = max(1, (soft_limit - 5) // 3)
1106 opt.jobs = min(opt.jobs, jobs_soft_limit)
1107 opt.jobs_network = min(opt.jobs_network, jobs_soft_limit)
1108 opt.jobs_checkout = min(opt.jobs_checkout, jobs_soft_limit)
1109
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001110 superproject_logging_data = {}
1111 self._UpdateProjectsRevisionId(opt, args, superproject_logging_data,
1112 manifest)
Raman Tenneti1fd7bc22021-02-04 14:39:38 -08001113
Simran Basib9a1b732015-08-20 12:19:28 -07001114 if self.gitc_manifest:
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001115 gitc_manifest_projects = self.GetProjects(args, missing_ok=True)
Simran Basib9a1b732015-08-20 12:19:28 -07001116 gitc_projects = []
1117 opened_projects = []
1118 for project in gitc_manifest_projects:
Dan Willemsen5ea32d12015-09-08 13:27:20 -07001119 if project.relpath in self.gitc_manifest.paths and \
1120 self.gitc_manifest.paths[project.relpath].old_revision:
1121 opened_projects.append(project.relpath)
Simran Basib9a1b732015-08-20 12:19:28 -07001122 else:
Dan Willemsen5ea32d12015-09-08 13:27:20 -07001123 gitc_projects.append(project.relpath)
Simran Basib9a1b732015-08-20 12:19:28 -07001124
Dan Willemsen5ea32d12015-09-08 13:27:20 -07001125 if not args:
1126 gitc_projects = None
1127
1128 if gitc_projects != [] and not opt.local_only:
Simran Basib9a1b732015-08-20 12:19:28 -07001129 print('Updating GITC client: %s' % self.gitc_manifest.gitc_client_name)
Dan Willemsen5ea32d12015-09-08 13:27:20 -07001130 manifest = GitcManifest(self.repodir, self.gitc_manifest.gitc_client_name)
1131 if manifest_name:
1132 manifest.Override(manifest_name)
1133 else:
LaMont Jonesa46047a2022-04-07 21:57:06 +00001134 manifest.Override(manifest.manifestFile)
Dan Willemsen5ea32d12015-09-08 13:27:20 -07001135 gitc_utils.generate_gitc_manifest(self.gitc_manifest,
1136 manifest,
Simran Basib9a1b732015-08-20 12:19:28 -07001137 gitc_projects)
1138 print('GITC client successfully synced.')
1139
1140 # The opened projects need to be synced as normal, therefore we
1141 # generate a new args list to represent the opened projects.
Dan Willemsen5ea32d12015-09-08 13:27:20 -07001142 # TODO: make this more reliable -- if there's a project name/path overlap,
1143 # this may choose the wrong project.
LaMont Jonesa46047a2022-04-07 21:57:06 +00001144 args = [os.path.relpath(manifest.paths[path].worktree, os.getcwd())
David Pursehouse3bcd3052017-07-10 22:42:22 +09001145 for path in opened_projects]
Simran Basib9a1b732015-08-20 12:19:28 -07001146 if not args:
1147 return
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001148
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001149 all_projects = self.GetProjects(args,
1150 missing_ok=True,
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001151 submodules_ok=opt.fetch_submodules,
1152 manifest=manifest,
1153 all_manifests=not opt.this_manifest_only)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001154
Mike Frysinger5a033082019-09-23 19:21:20 -04001155 err_network_sync = False
1156 err_update_projects = False
Mike Frysinger5a033082019-09-23 19:21:20 -04001157
LaMont Jonesa46047a2022-04-07 21:57:06 +00001158 self._fetch_times = _FetchTimes(manifest)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -07001159 if not opt.local_only:
Mike Frysinger339f2df2021-05-06 00:44:42 -04001160 with multiprocessing.Manager() as manager:
1161 with ssh.ProxyManager(manager) as ssh_proxy:
1162 # Initialize the socket dir once in the parent.
1163 ssh_proxy.sock()
Peter Kjellerstedtd1776092021-05-19 19:37:23 +02001164 all_projects = self._FetchMain(opt, args, all_projects, err_event,
LaMont Jonesa46047a2022-04-07 21:57:06 +00001165 ssh_proxy, manifest)
Mike Frysinger339f2df2021-05-06 00:44:42 -04001166
1167 if opt.network_only:
1168 return
Mike Frysinger5a033082019-09-23 19:21:20 -04001169
1170 # If we saw an error, exit with code 1 so that other scripts can check.
Mike Frysingerbe24a542021-02-23 03:24:12 -05001171 if err_event.is_set():
Mike Frysinger5a033082019-09-23 19:21:20 -04001172 err_network_sync = True
1173 if opt.fail_fast:
1174 print('\nerror: Exited sync due to fetch errors.\n'
1175 'Local checkouts *not* updated. Resolve network issues & '
1176 'retry.\n'
1177 '`repo sync -l` will update some local checkouts.',
1178 file=sys.stderr)
1179 sys.exit(1)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001180
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001181 for m in self.ManifestList(opt):
1182 if m.IsMirror or m.IsArchive:
1183 # bail out now, we have no working tree
1184 continue
Shawn O. Pearcecd1d7ff2009-06-04 16:15:53 -07001185
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001186 if self.UpdateProjectList(opt, m):
1187 err_event.set()
1188 err_update_projects = True
1189 if opt.fail_fast:
1190 print('\nerror: Local checkouts *not* updated.', file=sys.stderr)
1191 sys.exit(1)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -07001192
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001193 err_update_linkfiles = not self.UpdateCopyLinkfileList(m)
1194 if err_update_linkfiles:
1195 err_event.set()
1196 if opt.fail_fast:
1197 print('\nerror: Local update copyfile or linkfile failed.', file=sys.stderr)
1198 sys.exit(1)
jiajia tanga590e642021-04-25 20:02:02 +08001199
Mike Frysinger5a033082019-09-23 19:21:20 -04001200 err_results = []
Mike Frysingerebf04a42021-02-23 20:48:04 -05001201 # NB: We don't exit here because this is the last step.
1202 err_checkout = not self._Checkout(all_projects, opt, err_results)
1203 if err_checkout:
1204 err_event.set()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001205
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001206 printed_notices = set()
1207 # If there's a notice that's supposed to print at the end of the sync,
1208 # print it now... But avoid printing duplicate messages, and preserve
1209 # order.
1210 for m in sorted(self.ManifestList(opt), key=lambda x: x.path_prefix):
1211 if m.notice and m.notice not in printed_notices:
1212 print(m.notice)
1213 printed_notices.add(m.notice)
Doug Anderson2b8db3c2010-11-01 15:08:06 -07001214
Mike Frysinger5a033082019-09-23 19:21:20 -04001215 # If we saw an error, exit with code 1 so that other scripts can check.
Mike Frysingerbe24a542021-02-23 03:24:12 -05001216 if err_event.is_set():
Mike Frysinger5a033082019-09-23 19:21:20 -04001217 print('\nerror: Unable to fully sync the tree.', file=sys.stderr)
1218 if err_network_sync:
1219 print('error: Downloading network changes failed.', file=sys.stderr)
1220 if err_update_projects:
1221 print('error: Updating local project lists failed.', file=sys.stderr)
jiajia tanga590e642021-04-25 20:02:02 +08001222 if err_update_linkfiles:
1223 print('error: Updating copyfiles or linkfiles failed.', file=sys.stderr)
Mike Frysinger5a033082019-09-23 19:21:20 -04001224 if err_checkout:
1225 print('error: Checking out local projects failed.', file=sys.stderr)
1226 if err_results:
1227 print('Failing repos:\n%s' % '\n'.join(err_results), file=sys.stderr)
1228 print('Try re-running with "-j1 --fail-fast" to exit at the first error.',
1229 file=sys.stderr)
1230 sys.exit(1)
1231
Raman Tenneti7954de12021-07-28 14:36:49 -07001232 # Log the previous sync analysis state from the config.
Raman Tenneti6448a4f2021-09-13 17:40:07 -07001233 self.git_event_log.LogDataConfigEvents(mp.config.GetSyncAnalysisStateData(),
1234 'previous_sync_state')
Raman Tenneti7954de12021-07-28 14:36:49 -07001235
1236 # Update and log with the new sync analysis state.
1237 mp.config.UpdateSyncAnalysisState(opt, superproject_logging_data)
Raman Tenneti6448a4f2021-09-13 17:40:07 -07001238 self.git_event_log.LogDataConfigEvents(mp.config.GetSyncAnalysisStateData(),
1239 'current_sync_state')
Raman Tenneti7954de12021-07-28 14:36:49 -07001240
Mike Frysingere19d9e12020-02-12 11:23:32 -05001241 if not opt.quiet:
1242 print('repo sync has finished successfully.')
1243
David Pursehouse819827a2020-02-12 15:20:19 +09001244
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -07001245def _PostRepoUpgrade(manifest, quiet=False):
Mike Frysingerfdeb20f2021-11-14 03:53:04 -05001246 # Link the docs for the internal .repo/ layout for people
1247 link = os.path.join(manifest.repodir, 'internal-fs-layout.md')
1248 if not platform_utils.islink(link):
1249 target = os.path.join('repo', 'docs', 'internal-fs-layout.md')
1250 try:
1251 platform_utils.symlink(target, link)
Raman Tenneti4a478ed2021-11-17 18:38:24 -08001252 except Exception:
Mike Frysingerfdeb20f2021-11-14 03:53:04 -05001253 pass
1254
Conley Owens094cdbe2014-01-30 15:09:59 -08001255 wrapper = Wrapper()
Conley Owensc9129d92012-10-01 16:12:28 -07001256 if wrapper.NeedSetupGnuPG():
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -07001257 wrapper.SetupGnuPG(quiet)
Conley Owensf2fe2d92014-01-29 13:53:43 -08001258 for project in manifest.projects:
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001259 if project.Exists:
1260 project.PostRepoUpgrade()
1261
David Pursehouse819827a2020-02-12 15:20:19 +09001262
Mike Frysingerc58ec4d2020-02-17 14:36:08 -05001263def _PostRepoFetch(rp, repo_verify=True, verbose=False):
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001264 if rp.HasChanges:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001265 print('info: A new version of repo is available', file=sys.stderr)
Mike Frysinger347f9ed2021-03-15 14:58:52 -04001266 wrapper = Wrapper()
1267 try:
1268 rev = rp.bare_git.describe(rp.GetRevisionId())
1269 except GitError:
1270 rev = None
1271 _, new_rev = wrapper.check_repo_rev(rp.gitdir, rev, repo_verify=repo_verify)
1272 # See if we're held back due to missing signed tag.
1273 current_revid = rp.bare_git.rev_parse('HEAD')
1274 new_revid = rp.bare_git.rev_parse('--verify', new_rev)
1275 if current_revid != new_revid:
1276 # We want to switch to the new rev, but also not trash any uncommitted
1277 # changes. This helps with local testing/hacking.
1278 # If a local change has been made, we will throw that away.
1279 # We also have to make sure this will switch to an older commit if that's
1280 # the latest tag in order to support release rollback.
1281 try:
1282 rp.work_git.reset('--keep', new_rev)
1283 except GitError as e:
1284 sys.exit(str(e))
Sarah Owenscecd1d82012-11-01 22:59:27 -07001285 print('info: Restarting repo with latest version', file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001286 raise RepoChangedException(['--repo-upgraded'])
1287 else:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001288 print('warning: Skipped upgrade to unverified version', file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001289 else:
1290 if verbose:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001291 print('repo version %s is current' % rp.work_git.describe(HEAD),
1292 file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001293
David Pursehouse819827a2020-02-12 15:20:19 +09001294
Dave Borowitz67700e92012-10-23 15:00:54 -07001295class _FetchTimes(object):
Dave Borowitzd9478582012-10-23 16:35:39 -07001296 _ALPHA = 0.5
1297
Dave Borowitz67700e92012-10-23 15:00:54 -07001298 def __init__(self, manifest):
Anthony King85b24ac2014-05-06 15:57:48 +01001299 self._path = os.path.join(manifest.repodir, '.repo_fetchtimes.json')
Dave Borowitz67700e92012-10-23 15:00:54 -07001300 self._times = None
Dave Borowitzd9478582012-10-23 16:35:39 -07001301 self._seen = set()
Dave Borowitz67700e92012-10-23 15:00:54 -07001302
1303 def Get(self, project):
1304 self._Load()
1305 return self._times.get(project.name, _ONE_DAY_S)
1306
1307 def Set(self, project, t):
Dave Borowitzd9478582012-10-23 16:35:39 -07001308 self._Load()
1309 name = project.name
1310 old = self._times.get(name, t)
1311 self._seen.add(name)
1312 a = self._ALPHA
David Pursehouse54a4e602020-02-12 14:31:05 +09001313 self._times[name] = (a * t) + ((1 - a) * old)
Dave Borowitz67700e92012-10-23 15:00:54 -07001314
1315 def _Load(self):
1316 if self._times is None:
1317 try:
Mike Frysinger3164d402019-11-11 05:40:22 -05001318 with open(self._path) as f:
Anthony King85b24ac2014-05-06 15:57:48 +01001319 self._times = json.load(f)
Anthony King85b24ac2014-05-06 15:57:48 +01001320 except (IOError, ValueError):
Mike Frysinger9d96f582021-09-28 11:27:24 -04001321 platform_utils.remove(self._path, missing_ok=True)
Anthony King85b24ac2014-05-06 15:57:48 +01001322 self._times = {}
Dave Borowitz67700e92012-10-23 15:00:54 -07001323
1324 def Save(self):
1325 if self._times is None:
1326 return
Dave Borowitzd9478582012-10-23 16:35:39 -07001327
1328 to_delete = []
1329 for name in self._times:
1330 if name not in self._seen:
1331 to_delete.append(name)
1332 for name in to_delete:
1333 del self._times[name]
1334
Dave Borowitz67700e92012-10-23 15:00:54 -07001335 try:
Mike Frysinger3164d402019-11-11 05:40:22 -05001336 with open(self._path, 'w') as f:
Anthony King85b24ac2014-05-06 15:57:48 +01001337 json.dump(self._times, f, indent=2)
Anthony King85b24ac2014-05-06 15:57:48 +01001338 except (IOError, TypeError):
Mike Frysinger9d96f582021-09-28 11:27:24 -04001339 platform_utils.remove(self._path, missing_ok=True)
Dan Willemsen0745bb22015-08-17 13:41:45 -07001340
1341# This is a replacement for xmlrpc.client.Transport using urllib2
1342# and supporting persistent-http[s]. It cannot change hosts from
1343# request to request like the normal transport, the real url
1344# is passed during initialization.
David Pursehouse819827a2020-02-12 15:20:19 +09001345
1346
Dan Willemsen0745bb22015-08-17 13:41:45 -07001347class PersistentTransport(xmlrpc.client.Transport):
1348 def __init__(self, orig_host):
1349 self.orig_host = orig_host
1350
1351 def request(self, host, handler, request_body, verbose=False):
1352 with GetUrlCookieFile(self.orig_host, not verbose) as (cookiefile, proxy):
1353 # Python doesn't understand cookies with the #HttpOnly_ prefix
1354 # Since we're only using them for HTTP, copy the file temporarily,
1355 # stripping those prefixes away.
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001356 if cookiefile:
Collin Fijalkoviche1191b32020-02-18 10:57:32 -08001357 tmpcookiefile = tempfile.NamedTemporaryFile(mode='w')
David Pursehouse4c5f74e2015-10-02 11:10:10 +09001358 tmpcookiefile.write("# HTTP Cookie File")
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001359 try:
1360 with open(cookiefile) as f:
1361 for line in f:
1362 if line.startswith("#HttpOnly_"):
1363 line = line[len("#HttpOnly_"):]
1364 tmpcookiefile.write(line)
1365 tmpcookiefile.flush()
Dan Willemsen0745bb22015-08-17 13:41:45 -07001366
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001367 cookiejar = cookielib.MozillaCookieJar(tmpcookiefile.name)
David Pursehouseb1ad2192015-09-30 10:35:43 +09001368 try:
1369 cookiejar.load()
1370 except cookielib.LoadError:
1371 cookiejar = cookielib.CookieJar()
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001372 finally:
1373 tmpcookiefile.close()
1374 else:
1375 cookiejar = cookielib.CookieJar()
Dan Willemsen0745bb22015-08-17 13:41:45 -07001376
1377 proxyhandler = urllib.request.ProxyHandler
1378 if proxy:
1379 proxyhandler = urllib.request.ProxyHandler({
1380 "http": proxy,
David Pursehouse54a4e602020-02-12 14:31:05 +09001381 "https": proxy})
Dan Willemsen0745bb22015-08-17 13:41:45 -07001382
1383 opener = urllib.request.build_opener(
1384 urllib.request.HTTPCookieProcessor(cookiejar),
1385 proxyhandler)
1386
1387 url = urllib.parse.urljoin(self.orig_host, handler)
1388 parse_results = urllib.parse.urlparse(url)
1389
1390 scheme = parse_results.scheme
1391 if scheme == 'persistent-http':
1392 scheme = 'http'
1393 if scheme == 'persistent-https':
1394 # If we're proxying through persistent-https, use http. The
1395 # proxy itself will do the https.
1396 if proxy:
1397 scheme = 'http'
1398 else:
1399 scheme = 'https'
1400
1401 # Parse out any authentication information using the base class
1402 host, extra_headers, _ = self.get_host_info(parse_results.netloc)
1403
1404 url = urllib.parse.urlunparse((
1405 scheme,
1406 host,
1407 parse_results.path,
1408 parse_results.params,
1409 parse_results.query,
1410 parse_results.fragment))
1411
1412 request = urllib.request.Request(url, request_body)
1413 if extra_headers is not None:
1414 for (name, header) in extra_headers:
1415 request.add_header(name, header)
1416 request.add_header('Content-Type', 'text/xml')
1417 try:
1418 response = opener.open(request)
1419 except urllib.error.HTTPError as e:
1420 if e.code == 501:
1421 # We may have been redirected through a login process
1422 # but our POST turned into a GET. Retry.
1423 response = opener.open(request)
1424 else:
1425 raise
1426
1427 p, u = xmlrpc.client.getparser()
1428 while 1:
1429 data = response.read(1024)
1430 if not data:
1431 break
1432 p.feed(data)
1433 p.close()
1434 return u.close()
1435
1436 def close(self):
1437 pass