blob: 9f8de9e5ab697bb38acb29e10b9315955462dcc2 [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
jiajia tanga590e642021-04-25 20:02:02 +080015import errno
Mike Frysingerebf04a42021-02-23 20:48:04 -050016import functools
Mike Frysingeracf63b22019-06-13 02:24:21 -040017import http.cookiejar as cookielib
Mike Frysinger7b586f22021-02-23 18:38:39 -050018import io
Anthony King85b24ac2014-05-06 15:57:48 +010019import json
Mike Frysingerebf04a42021-02-23 20:48:04 -050020import multiprocessing
David Pursehouse86d973d2012-08-24 10:21:02 +090021import netrc
Shawn O. Pearce2a1ccb22009-04-10 16:51:53 -070022from optparse import SUPPRESS_HELP
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070023import os
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -070024import socket
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070025import sys
Dan Willemsen0745bb22015-08-17 13:41:45 -070026import tempfile
Shawn O. Pearcef6906872009-04-18 10:49:00 -070027import time
Mike Frysingeracf63b22019-06-13 02:24:21 -040028import urllib.error
29import urllib.parse
30import urllib.request
31import xmlrpc.client
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070032
Roy Lee18afd7f2010-05-09 04:32:08 +080033try:
34 import threading as _threading
35except ImportError:
36 import dummy_threading as _threading
37
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -070038try:
39 import resource
David Pursehouse819827a2020-02-12 15:20:19 +090040
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -070041 def _rlimit_nofile():
42 return resource.getrlimit(resource.RLIMIT_NOFILE)
43except ImportError:
44 def _rlimit_nofile():
45 return (256, 256)
46
David Rileye0684ad2017-04-05 00:02:59 -070047import event_log
Mike Frysinger347f9ed2021-03-15 14:58:52 -040048from git_command import git_require
David Pursehouseba7bc732015-08-20 16:55:42 +090049from git_config import GetUrlCookieFile
David Pursehoused94aaef2012-09-07 09:52:04 +090050from git_refs import R_HEADS, HEAD
Raman Tenneti6a872c92021-01-14 19:17:50 -080051import git_superproject
Simran Basibdb52712015-08-10 13:23:23 -070052import gitc_utils
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -070053from project import Project
54from project import RemoteSpec
Mike Frysingerd41eed02021-04-20 23:21:29 -040055from command import Command, MirrorSafeCommand, WORKER_BATCH_SIZE
Raman Tenneti1fd7bc22021-02-04 14:39:38 -080056from error import RepoChangedException, GitError, ManifestParseError
Renaud Paquaya65adf72016-11-03 10:37:53 -070057import platform_utils
Shawn O. Pearce350cde42009-04-16 11:21:18 -070058from project import SyncBuffer
Shawn O. Pearce68194f42009-04-10 16:48:52 -070059from progress import Progress
Conley Owens094cdbe2014-01-30 15:09:59 -080060from wrapper import Wrapper
Dan Willemsen5ea32d12015-09-08 13:27:20 -070061from manifest_xml import GitcManifest
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070062
Dave Borowitz67700e92012-10-23 15:00:54 -070063_ONE_DAY_S = 24 * 60 * 60
64
David Pursehouse819827a2020-02-12 15:20:19 +090065
Shawn O. Pearcec95583b2009-03-03 17:47:06 -080066class Sync(Command, MirrorSafeCommand):
Roy Lee18afd7f2010-05-09 04:32:08 +080067 jobs = 1
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070068 common = True
69 helpSummary = "Update working tree to the latest revision"
70 helpUsage = """
71%prog [<project>...]
72"""
73 helpDescription = """
74The '%prog' command synchronizes local project directories
75with the remote repositories specified in the manifest. If a local
76project does not yet exist, it will clone a new local directory from
77the remote repository and set up tracking branches as specified in
78the manifest. If the local project already exists, '%prog'
79will update the remote branches and rebase any new local changes
80on top of the new remote changes.
81
82'%prog' will synchronize all projects listed at the command
83line. Projects can be specified either by name, or by a relative
84or absolute path to the project's local directory. If no projects
85are specified, '%prog' will synchronize all projects listed in
86the manifest.
Shawn O. Pearce3e768c92009-04-10 16:59:36 -070087
88The -d/--detach option can be used to switch specified projects
89back to the manifest revision. This option is especially helpful
90if the project is currently on a topic branch, but the manifest
91revision is temporarily needed.
Shawn O. Pearceeb7af872009-04-21 08:02:04 -070092
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -070093The -s/--smart-sync option can be used to sync to a known good
94build as specified by the manifest-server element in the current
Victor Boivie08c880d2011-04-19 10:32:52 +020095manifest. The -t/--smart-tag option is similar and allows you to
96specify a custom tag/label.
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -070097
David Pursehousecf76b1b2012-09-14 10:31:42 +090098The -u/--manifest-server-username and -p/--manifest-server-password
99options can be used to specify a username and password to authenticate
100with the manifest server when using the -s or -t option.
101
102If -u and -p are not specified when using the -s or -t option, '%prog'
103will attempt to read authentication credentials for the manifest server
104from the user's .netrc file.
105
106'%prog' will not use authentication credentials from -u/-p or .netrc
107if the manifest server specified in the manifest file already includes
108credentials.
109
Mike Frysingerd9e5cf02019-08-26 03:12:55 -0400110By default, all projects will be synced. The --fail-fast option can be used
Mike Frysinger7ae210a2020-05-24 14:56:52 -0400111to halt syncing as soon as possible when the first project fails to sync.
Andrei Warkentin5df6de02010-07-02 17:58:31 -0500112
Kevin Degiabaa7f32014-11-12 11:27:45 -0700113The --force-sync option can be used to overwrite existing git
114directories if they have previously been linked to a different
Roger Shimizuac29ac32020-06-06 02:33:40 +0900115object directory. WARNING: This may cause data to be lost since
Kevin Degiabaa7f32014-11-12 11:27:45 -0700116refs may be removed when overwriting.
117
Oleksii Okolielovd3c0f592018-12-17 19:23:44 -0500118The --force-remove-dirty option can be used to remove previously used
119projects with uncommitted changes. WARNING: This may cause data to be
120lost since uncommitted changes may be removed with projects that no longer
121exist in the manifest.
122
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -0700123The --no-clone-bundle option disables any attempt to use
124$URL/clone.bundle to bootstrap a new Git repository from a
125resumeable bundle file on a content delivery network. This
126may be necessary if there are problems with the local Python
127HTTP client or proxy configuration, but the Git binary works.
128
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800129The --fetch-submodules option enables fetching Git submodules
130of a project from server.
131
David Pursehousef2fad612015-01-29 14:36:28 +0900132The -c/--current-branch option can be used to only fetch objects that
133are on the branch specified by a project's revision.
134
David Pursehouseb1553542014-09-04 21:28:09 +0900135The --optimized-fetch option can be used to only fetch projects that
136are fixed to a sha1 revision if the sha1 revision does not already
137exist locally.
138
David Pursehouse74cfd272015-10-14 10:50:15 +0900139The --prune option can be used to remove any refs that no longer
140exist on the remote.
141
Mike Frysingerb8f7bb02018-10-10 01:05:11 -0400142# SSH Connections
Shawn O. Pearceeb7af872009-04-21 08:02:04 -0700143
144If at least one project remote URL uses an SSH connection (ssh://,
145git+ssh://, or user@host:path syntax) repo will automatically
146enable the SSH ControlMaster option when connecting to that host.
147This feature permits other projects in the same '%prog' session to
148reuse the same SSH tunnel, saving connection setup overheads.
149
150To disable this behavior on UNIX platforms, set the GIT_SSH
151environment variable to 'ssh'. For example:
152
153 export GIT_SSH=ssh
154 %prog
155
Mike Frysingerb8f7bb02018-10-10 01:05:11 -0400156# Compatibility
Shawn O. Pearceeb7af872009-04-21 08:02:04 -0700157
158This feature is automatically disabled on Windows, due to the lack
159of UNIX domain socket support.
160
161This feature is not compatible with url.insteadof rewrites in the
162user's ~/.gitconfig. '%prog' is currently not able to perform the
163rewrite early enough to establish the ControlMaster tunnel.
164
165If the remote SSH daemon is Gerrit Code Review, version 2.0.10 or
166later is required to fix a server side protocol bug.
167
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700168"""
Mike Frysinger6a2400a2021-02-16 01:43:31 -0500169 PARALLEL_JOBS = 1
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700170
Mike Frysinger9180a072021-04-13 14:57:40 -0400171 def _CommonOptions(self, p):
Torne (Richard Coles)7bdbde72012-12-05 10:58:06 +0000172 try:
Mike Frysinger6a2400a2021-02-16 01:43:31 -0500173 self.PARALLEL_JOBS = self.manifest.default.sync_j
Torne (Richard Coles)7bdbde72012-12-05 10:58:06 +0000174 except ManifestParseError:
Mike Frysinger6a2400a2021-02-16 01:43:31 -0500175 pass
Mike Frysinger9180a072021-04-13 14:57:40 -0400176 super()._CommonOptions(p)
Shawn O. Pearce6392c872011-09-22 17:44:31 -0700177
Mike Frysinger9180a072021-04-13 14:57:40 -0400178 def _Options(self, p, show_smart=True):
Mike Frysinger49de8ef2021-04-09 00:21:02 -0400179 p.add_option('--jobs-network', default=None, type=int, metavar='JOBS',
180 help='number of network jobs to run in parallel (defaults to --jobs)')
181 p.add_option('--jobs-checkout', default=None, type=int, metavar='JOBS',
182 help='number of local checkout jobs to run in parallel (defaults to --jobs)')
183
Andrei Warkentin5df6de02010-07-02 17:58:31 -0500184 p.add_option('-f', '--force-broken',
Stefan Müller-Klieser46702ed2019-08-30 10:20:15 +0200185 dest='force_broken', action='store_true',
Mike Frysingerd9e5cf02019-08-26 03:12:55 -0400186 help='obsolete option (to be deleted in the future)')
187 p.add_option('--fail-fast',
188 dest='fail_fast', action='store_true',
189 help='stop syncing after first error is hit')
Kevin Degiabaa7f32014-11-12 11:27:45 -0700190 p.add_option('--force-sync',
191 dest='force_sync', action='store_true',
192 help="overwrite an existing git directory if it needs to "
193 "point to a different object directory. WARNING: this "
194 "may cause loss of data")
Oleksii Okolielovd3c0f592018-12-17 19:23:44 -0500195 p.add_option('--force-remove-dirty',
196 dest='force_remove_dirty', action='store_true',
197 help="force remove projects with uncommitted modifications if "
198 "projects no longer exist in the manifest. "
199 "WARNING: this may cause loss of data")
David Pursehouse8f62fb72012-11-14 12:09:38 +0900200 p.add_option('-l', '--local-only',
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700201 dest='local_only', action='store_true',
202 help="only update working tree, don't fetch")
David Pursehouse54a4e602020-02-12 14:31:05 +0900203 p.add_option('--no-manifest-update', '--nmu',
Fredrik de Grootcc960972019-11-22 09:04:31 +0100204 dest='mp_update', action='store_false', default='true',
205 help='use the existing manifest checkout as-is. '
206 '(do not update to the latest revision)')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900207 p.add_option('-n', '--network-only',
Shawn O. Pearce96fdcef2009-04-10 16:29:20 -0700208 dest='network_only', action='store_true',
209 help="fetch only, don't update working tree")
David Pursehouse8f62fb72012-11-14 12:09:38 +0900210 p.add_option('-d', '--detach',
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700211 dest='detach_head', action='store_true',
212 help='detach projects back to manifest revision')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900213 p.add_option('-c', '--current-branch',
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -0700214 dest='current_branch_only', action='store_true',
215 help='fetch only current branch 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',
233 help='use the manifest superproject to sync projects')
Mitchel Humpherys597868b2012-10-29 10:18:34 -0700234 p.add_option('--no-tags',
Mike Frysingerc58ec4d2020-02-17 14:36:08 -0500235 dest='tags', default=True, action='store_false',
Mitchel Humpherys597868b2012-10-29 10:18:34 -0700236 help="don't fetch tags")
David Pursehouseb1553542014-09-04 21:28:09 +0900237 p.add_option('--optimized-fetch',
238 dest='optimized_fetch', action='store_true',
239 help='only fetch projects fixed to sha1 if revision does not exist locally')
George Engelbrecht9bc283e2020-04-02 12:36:09 -0600240 p.add_option('--retry-fetches',
241 default=0, action='store', type='int',
242 help='number of times to retry fetches on transient errors')
David Pursehouse74cfd272015-10-14 10:50:15 +0900243 p.add_option('--prune', dest='prune', action='store_true',
244 help='delete refs that no longer exist on the remote')
Nico Sallembien6623b212010-05-11 12:57:01 -0700245 if show_smart:
246 p.add_option('-s', '--smart-sync',
247 dest='smart_sync', action='store_true',
David Pursehouse79fba682016-04-13 18:03:00 +0900248 help='smart sync using manifest from the latest known good build')
Victor Boivie08c880d2011-04-19 10:32:52 +0200249 p.add_option('-t', '--smart-tag',
250 dest='smart_tag', action='store',
251 help='smart sync using manifest from a known tag')
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700252
Shawn O. Pearcefd89b672009-04-18 11:28:57 -0700253 g = p.add_option_group('repo Version options')
254 g.add_option('--no-repo-verify',
Mike Frysingerc58ec4d2020-02-17 14:36:08 -0500255 dest='repo_verify', default=True, action='store_false',
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700256 help='do not verify repo source code')
Shawn O. Pearcefd89b672009-04-18 11:28:57 -0700257 g.add_option('--repo-upgraded',
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800258 dest='repo_upgraded', action='store_true',
Shawn O. Pearce2a1ccb22009-04-10 16:51:53 -0700259 help=SUPPRESS_HELP)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700260
Raman Tenneti8d43dea2021-02-07 16:30:27 -0800261 def _GetBranch(self):
262 """Returns the branch name for getting the approved manifest."""
263 p = self.manifest.manifestProject
264 b = p.GetBranch(p.CurrentBranch)
265 branch = b.merge
266 if branch.startswith(R_HEADS):
267 branch = branch[len(R_HEADS):]
268 return branch
269
Raman Tenneti2ae44d72021-03-23 15:12:27 -0700270 def _UseSuperproject(self, opt):
271 """Returns True if use-superproject option is enabled"""
272 return (opt.use_superproject or
273 self.manifest.manifestProject.config.GetBoolean(
274 'repo.superproject'))
275
276 def _GetCurrentBranchOnly(self, opt):
277 """Returns True if current-branch or use-superproject options are enabled."""
278 return opt.current_branch_only or self._UseSuperproject(opt)
279
Raman Tennetifeb28912021-05-02 19:47:29 -0700280 def _UpdateProjectsRevisionId(self, opt, args, load_local_manifests):
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800281 """Update revisionId of every project with the SHA from superproject.
282
283 This function updates each project's revisionId with SHA from superproject.
284 It writes the updated manifest into a file and reloads the manifest from it.
285
286 Args:
287 opt: Program options returned from optparse. See _Options().
288 args: Arguments to pass to GetProjects. See the GetProjects
289 docstring for details.
Raman Tennetifeb28912021-05-02 19:47:29 -0700290 load_local_manifests: Whether to load local manifests.
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800291
292 Returns:
293 Returns path to the overriding manifest file.
294 """
Raman Tenneti21dce3d2021-02-09 00:26:31 -0800295 superproject = git_superproject.Superproject(self.manifest,
Raman Tennetief99ec02021-03-04 10:29:40 -0800296 self.repodir,
297 quiet=opt.quiet)
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800298 all_projects = self.GetProjects(args,
299 missing_ok=True,
300 submodules_ok=opt.fetch_submodules)
Raman Tenneti21dce3d2021-02-09 00:26:31 -0800301 manifest_path = superproject.UpdateProjectsRevisionId(all_projects)
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800302 if not manifest_path:
303 print('error: Update of revsionId from superproject has failed',
304 file=sys.stderr)
305 sys.exit(1)
Raman Tennetifeb28912021-05-02 19:47:29 -0700306 self._ReloadManifest(manifest_path, load_local_manifests)
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800307 return manifest_path
308
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500309 def _FetchProjectList(self, opt, projects):
310 """Main function of the fetch worker.
311
312 The projects we're given share the same underlying git object store, so we
313 have to fetch them in serial.
Roy Lee18afd7f2010-05-09 04:32:08 +0800314
David James8d201162013-10-11 17:03:19 -0700315 Delegates most of the work to _FetchHelper.
316
317 Args:
318 opt: Program options returned from optparse. See _Options().
319 projects: Projects to fetch.
David James8d201162013-10-11 17:03:19 -0700320 """
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500321 return [self._FetchOne(opt, x) for x in projects]
David James8d201162013-10-11 17:03:19 -0700322
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500323 def _FetchOne(self, opt, project):
David James8d201162013-10-11 17:03:19 -0700324 """Fetch git objects for a single project.
325
David Pursehousec1b86a22012-11-14 11:36:51 +0900326 Args:
327 opt: Program options returned from optparse. See _Options().
328 project: Project object for the project to fetch.
David James8d201162013-10-11 17:03:19 -0700329
330 Returns:
331 Whether the fetch was successful.
David Pursehousec1b86a22012-11-14 11:36:51 +0900332 """
David Rileye0684ad2017-04-05 00:02:59 -0700333 start = time.time()
334 success = False
Mike Frysinger7b586f22021-02-23 18:38:39 -0500335 buf = io.StringIO()
David Pursehousec1b86a22012-11-14 11:36:51 +0900336 try:
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500337 success = project.Sync_NetworkHalf(
338 quiet=opt.quiet,
339 verbose=opt.verbose,
340 output_redir=buf,
341 current_branch_only=self._GetCurrentBranchOnly(opt),
342 force_sync=opt.force_sync,
343 clone_bundle=opt.clone_bundle,
344 tags=opt.tags, archive=self.manifest.IsArchive,
345 optimized_fetch=opt.optimized_fetch,
346 retry_fetches=opt.retry_fetches,
347 prune=opt.prune,
Raman Tennetif32f2432021-04-12 20:57:25 -0700348 clone_filter=self.manifest.CloneFilter,
349 partial_clone_exclude=self.manifest.PartialCloneExclude)
Doug Andersonfc06ced2011-03-16 15:49:18 -0700350
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500351 output = buf.getvalue()
352 if opt.verbose and output:
353 print('\n' + output.rstrip())
Doug Andersonfc06ced2011-03-16 15:49:18 -0700354
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500355 if not success:
356 print('error: Cannot fetch %s from %s'
357 % (project.name, project.remote.url),
358 file=sys.stderr)
Raman Tennetiad8aa692021-04-15 09:20:51 -0700359 except GitError as e:
360 print('error.GitError: Cannot fetch %s' % str(e), file=sys.stderr)
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500361 except Exception as e:
362 print('error: Cannot fetch %s (%s: %s)'
363 % (project.name, type(e).__name__, str(e)), file=sys.stderr)
364 raise
Mike Frysinger7b586f22021-02-23 18:38:39 -0500365
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500366 finish = time.time()
367 return (success, project, start, finish)
David James8d201162013-10-11 17:03:19 -0700368
Mike Frysinger5a033082019-09-23 19:21:20 -0400369 def _Fetch(self, projects, opt, err_event):
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500370 ret = True
371
Mike Frysinger49de8ef2021-04-09 00:21:02 -0400372 jobs = opt.jobs_network if opt.jobs_network else self.jobs
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700373 fetched = set()
Mike Frysinger151701e2021-04-13 15:07:21 -0400374 pm = Progress('Fetching', len(projects), delay=False, quiet=opt.quiet)
Roy Lee18afd7f2010-05-09 04:32:08 +0800375
David James89ece422014-01-09 18:51:58 -0800376 objdir_project_map = dict()
377 for project in projects:
378 objdir_project_map.setdefault(project.objdir, []).append(project)
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500379 projects_list = list(objdir_project_map.values())
David James8d201162013-10-11 17:03:19 -0700380
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500381 def _ProcessResults(results_sets):
382 ret = True
383 for results in results_sets:
384 for (success, project, start, finish) in results:
385 self._fetch_times.Set(project, finish - start)
386 self.event_log.AddSync(project, event_log.TASK_SYNC_NETWORK,
387 start, finish, success)
388 # Check for any errors before running any more tasks.
389 # ...we'll let existing jobs finish, though.
390 if not success:
391 ret = False
392 else:
393 fetched.add(project.gitdir)
394 pm.update(msg=project.name)
395 if not ret and opt.fail_fast:
396 break
397 return ret
Doug Andersonfc06ced2011-03-16 15:49:18 -0700398
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500399 # NB: Multiprocessing is heavy, so don't spin it up for one job.
Mike Frysinger49de8ef2021-04-09 00:21:02 -0400400 if len(projects_list) == 1 or jobs == 1:
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500401 if not _ProcessResults(self._FetchProjectList(opt, x) for x in projects_list):
402 ret = False
403 else:
404 # Favor throughput over responsiveness when quiet. It seems that imap()
405 # will yield results in batches relative to chunksize, so even as the
406 # children finish a sync, we won't see the result until one child finishes
407 # ~chunksize jobs. When using a large --jobs with large chunksize, this
408 # can be jarring as there will be a large initial delay where repo looks
409 # like it isn't doing anything and sits at 0%, but then suddenly completes
410 # a lot of jobs all at once. Since this code is more network bound, we
411 # can accept a bit more CPU overhead with a smaller chunksize so that the
412 # user sees more immediate & continuous feedback.
413 if opt.quiet:
414 chunksize = WORKER_BATCH_SIZE
David James89ece422014-01-09 18:51:58 -0800415 else:
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500416 pm.update(inc=0, msg='warming up')
417 chunksize = 4
Mike Frysinger49de8ef2021-04-09 00:21:02 -0400418 with multiprocessing.Pool(jobs) as pool:
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500419 results = pool.imap_unordered(
420 functools.partial(self._FetchProjectList, opt),
421 projects_list,
422 chunksize=chunksize)
423 if not _ProcessResults(results):
424 ret = False
425 pool.close()
Roy Lee18afd7f2010-05-09 04:32:08 +0800426
Shawn O. Pearce68194f42009-04-10 16:48:52 -0700427 pm.end()
Dave Borowitz67700e92012-10-23 15:00:54 -0700428 self._fetch_times.Save()
Dave Borowitz18857212012-10-23 17:02:59 -0700429
Julien Campergue335f5ef2013-10-16 11:02:35 +0200430 if not self.manifest.IsArchive:
Mike Frysinger5a033082019-09-23 19:21:20 -0400431 self._GCProjects(projects, opt, err_event)
Julien Campergue335f5ef2013-10-16 11:02:35 +0200432
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500433 return (ret, fetched)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700434
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500435 def _CheckoutOne(self, detach_head, force_sync, project):
Xin Li745be2e2019-06-03 11:24:30 -0700436 """Checkout work tree for one project
437
438 Args:
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500439 detach_head: Whether to leave a detached HEAD.
440 force_sync: Force checking out of the repo.
Xin Li745be2e2019-06-03 11:24:30 -0700441 project: Project object for the project to checkout.
Xin Li745be2e2019-06-03 11:24:30 -0700442
443 Returns:
444 Whether the fetch was successful.
445 """
Xin Li745be2e2019-06-03 11:24:30 -0700446 start = time.time()
447 syncbuf = SyncBuffer(self.manifest.manifestProject.config,
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500448 detach_head=detach_head)
Xin Li745be2e2019-06-03 11:24:30 -0700449 success = False
450 try:
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500451 project.Sync_LocalHalf(syncbuf, force_sync=force_sync)
Mike Frysingerebf04a42021-02-23 20:48:04 -0500452 success = syncbuf.Finish()
Raman Tennetiad8aa692021-04-15 09:20:51 -0700453 except GitError as e:
454 print('error.GitError: Cannot checkout %s: %s' %
455 (project.name, str(e)), file=sys.stderr)
Mike Frysingerebf04a42021-02-23 20:48:04 -0500456 except Exception as e:
457 print('error: Cannot checkout %s: %s: %s' %
458 (project.name, type(e).__name__, str(e)),
459 file=sys.stderr)
460 raise
Xin Li745be2e2019-06-03 11:24:30 -0700461
Mike Frysingerebf04a42021-02-23 20:48:04 -0500462 if not success:
463 print('error: Cannot checkout %s' % (project.name), file=sys.stderr)
464 finish = time.time()
465 return (success, project, start, finish)
Xin Li745be2e2019-06-03 11:24:30 -0700466
Mike Frysingerebf04a42021-02-23 20:48:04 -0500467 def _Checkout(self, all_projects, opt, err_results):
Xin Li745be2e2019-06-03 11:24:30 -0700468 """Checkout projects listed in all_projects
469
470 Args:
471 all_projects: List of all projects that should be checked out.
472 opt: Program options returned from optparse. See _Options().
Mike Frysingerebf04a42021-02-23 20:48:04 -0500473 err_results: A list of strings, paths to git repos where checkout failed.
Xin Li745be2e2019-06-03 11:24:30 -0700474 """
Mike Frysingerebf04a42021-02-23 20:48:04 -0500475 # Only checkout projects with worktrees.
476 all_projects = [x for x in all_projects if x.worktree]
Xin Li745be2e2019-06-03 11:24:30 -0700477
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500478 def _ProcessResults(pool, pm, results):
479 ret = True
Mike Frysingerebf04a42021-02-23 20:48:04 -0500480 for (success, project, start, finish) in results:
481 self.event_log.AddSync(project, event_log.TASK_SYNC_LOCAL,
482 start, finish, success)
483 # Check for any errors before running any more tasks.
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500484 # ...we'll let existing jobs finish, though.
Mike Frysingerebf04a42021-02-23 20:48:04 -0500485 if not success:
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500486 ret = False
Mike Frysingerebf04a42021-02-23 20:48:04 -0500487 err_results.append(project.relpath)
488 if opt.fail_fast:
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500489 if pool:
490 pool.close()
491 return ret
Mike Frysingerebf04a42021-02-23 20:48:04 -0500492 pm.update(msg=project.name)
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500493 return ret
Xin Li745be2e2019-06-03 11:24:30 -0700494
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500495 return self.ExecuteInParallel(
496 opt.jobs_checkout if opt.jobs_checkout else self.jobs,
497 functools.partial(self._CheckoutOne, opt.detach_head, opt.force_sync),
498 all_projects,
499 callback=_ProcessResults,
500 output=Progress('Checking out', len(all_projects), quiet=opt.quiet)) and not err_results
Mike Frysingerebf04a42021-02-23 20:48:04 -0500501
Mike Frysinger5a033082019-09-23 19:21:20 -0400502 def _GCProjects(self, projects, opt, err_event):
Mike Frysinger151701e2021-04-13 15:07:21 -0400503 pm = Progress('Garbage collecting', len(projects), delay=False, quiet=opt.quiet)
Mike Frysinger65af2602021-04-08 22:47:44 -0400504 pm.update(inc=0, msg='prescan')
505
Gabe Black2ff30292014-10-09 17:54:35 -0700506 gc_gitdirs = {}
David James8d201162013-10-11 17:03:19 -0700507 for project in projects:
Mike Frysinger5f2b0452020-02-11 03:23:24 -0500508 # Make sure pruning never kicks in with shared projects.
Mike Frysinger979d5bd2020-02-09 02:28:34 -0500509 if (not project.use_git_worktrees and
David Pursehouseaa611a22020-02-20 10:47:26 +0900510 len(project.manifest.GetProjectsWithName(project.name)) > 1):
Anders Björklund2a2da802021-01-18 10:32:36 +0100511 if not opt.quiet:
Mike Frysinger65af2602021-04-08 22:47:44 -0400512 print('\r%s: Shared project %s found, disabling pruning.' %
Anders Björklund2a2da802021-01-18 10:32:36 +0100513 (project.relpath, project.name))
Mike Frysinger5f2b0452020-02-11 03:23:24 -0500514 if git_require((2, 7, 0)):
Mike Frysingerf81c72e2020-02-19 15:50:00 -0500515 project.EnableRepositoryExtension('preciousObjects')
Mike Frysinger5f2b0452020-02-11 03:23:24 -0500516 else:
517 # This isn't perfect, but it's the best we can do with old git.
Mike Frysinger65af2602021-04-08 22:47:44 -0400518 print('\r%s: WARNING: shared projects are unreliable when using old '
Mike Frysinger5f2b0452020-02-11 03:23:24 -0500519 'versions of git; please upgrade to git-2.7.0+.'
520 % (project.relpath,),
521 file=sys.stderr)
522 project.config.SetString('gc.pruneExpire', 'never')
Gabe Black2ff30292014-10-09 17:54:35 -0700523 gc_gitdirs[project.gitdir] = project.bare_git
David James8d201162013-10-11 17:03:19 -0700524
Mike Frysinger65af2602021-04-08 22:47:44 -0400525 pm.update(inc=len(projects) - len(gc_gitdirs), msg='warming up')
526
527 cpu_count = os.cpu_count()
Dave Borowitz18857212012-10-23 17:02:59 -0700528 jobs = min(self.jobs, cpu_count)
529
530 if jobs < 2:
Gabe Black2ff30292014-10-09 17:54:35 -0700531 for bare_git in gc_gitdirs.values():
Mike Frysinger65af2602021-04-08 22:47:44 -0400532 pm.update(msg=bare_git._project.name)
David James8d201162013-10-11 17:03:19 -0700533 bare_git.gc('--auto')
Mike Frysinger65af2602021-04-08 22:47:44 -0400534 pm.end()
Dave Borowitz18857212012-10-23 17:02:59 -0700535 return
536
Mike Frysinger0c0e9342019-06-13 12:42:39 -0400537 config = {'pack.threads': cpu_count // jobs if cpu_count > jobs else 1}
Dave Borowitz18857212012-10-23 17:02:59 -0700538
539 threads = set()
540 sem = _threading.Semaphore(jobs)
Dave Borowitz18857212012-10-23 17:02:59 -0700541
David James8d201162013-10-11 17:03:19 -0700542 def GC(bare_git):
Mike Frysinger65af2602021-04-08 22:47:44 -0400543 pm.start(bare_git._project.name)
Dave Borowitz18857212012-10-23 17:02:59 -0700544 try:
545 try:
David James8d201162013-10-11 17:03:19 -0700546 bare_git.gc('--auto', config=config)
Dave Borowitz18857212012-10-23 17:02:59 -0700547 except GitError:
548 err_event.set()
David Pursehouse145e35b2020-02-12 15:40:47 +0900549 except Exception:
Dave Borowitz18857212012-10-23 17:02:59 -0700550 err_event.set()
551 raise
552 finally:
Mike Frysinger65af2602021-04-08 22:47:44 -0400553 pm.finish(bare_git._project.name)
Dave Borowitz18857212012-10-23 17:02:59 -0700554 sem.release()
555
Gabe Black2ff30292014-10-09 17:54:35 -0700556 for bare_git in gc_gitdirs.values():
Mike Frysingerbe24a542021-02-23 03:24:12 -0500557 if err_event.is_set() and opt.fail_fast:
Dave Borowitz18857212012-10-23 17:02:59 -0700558 break
559 sem.acquire()
David James8d201162013-10-11 17:03:19 -0700560 t = _threading.Thread(target=GC, args=(bare_git,))
Dave Borowitz18857212012-10-23 17:02:59 -0700561 t.daemon = True
562 threads.add(t)
563 t.start()
564
565 for t in threads:
566 t.join()
Mike Frysinger65af2602021-04-08 22:47:44 -0400567 pm.end()
Dave Borowitz18857212012-10-23 17:02:59 -0700568
Raman Tennetifeb28912021-05-02 19:47:29 -0700569 def _ReloadManifest(self, manifest_name=None, load_local_manifests=True):
570 """Reload the manfiest from the file specified by the |manifest_name|.
571
572 It unloads the manifest if |manifest_name| is None.
573
574 Args:
575 manifest_name: Manifest file to be reloaded.
576 load_local_manifests: Whether to load local manifests.
577 """
Tim Kilbourn07669002013-03-08 15:02:49 -0800578 if manifest_name:
579 # Override calls _Unload already
Raman Tennetifeb28912021-05-02 19:47:29 -0700580 self.manifest.Override(manifest_name, load_local_manifests=load_local_manifests)
Tim Kilbourn07669002013-03-08 15:02:49 -0800581 else:
582 self.manifest._Unload()
583
Oleksii Okolielovd3c0f592018-12-17 19:23:44 -0500584 def UpdateProjectList(self, opt):
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700585 new_project_paths = []
Colin Cross5acde752012-03-28 20:15:45 -0700586 for project in self.GetProjects(None, missing_ok=True):
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700587 if project.relpath:
588 new_project_paths.append(project.relpath)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700589 file_name = 'project.list'
Mike Frysingere3315bb2021-02-09 23:45:28 -0500590 file_path = os.path.join(self.repodir, file_name)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700591 old_project_paths = []
592
593 if os.path.exists(file_path):
Mike Frysinger3164d402019-11-11 05:40:22 -0500594 with open(file_path, 'r') as fd:
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700595 old_project_paths = fd.read().split('\n')
Kuang-che Wu0d9b16d2019-04-06 00:49:47 +0800596 # In reversed order, so subfolders are deleted before parent folder.
597 for path in sorted(old_project_paths, reverse=True):
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700598 if not path:
599 continue
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700600 if path not in new_project_paths:
David Pursehouse8a68ff92012-09-24 12:15:13 +0900601 # If the path has already been deleted, we don't need to do it
Dan Willemsen43507912016-09-01 16:26:02 -0700602 gitdir = os.path.join(self.manifest.topdir, path, '.git')
603 if os.path.exists(gitdir):
David Pursehousec1b86a22012-11-14 11:36:51 +0900604 project = Project(
David Pursehouseabdf7502020-02-12 14:58:39 +0900605 manifest=self.manifest,
606 name=path,
607 remote=RemoteSpec('origin'),
608 gitdir=gitdir,
609 objdir=gitdir,
Mike Frysingerc0d18662020-02-19 19:19:18 -0500610 use_git_worktrees=os.path.isfile(gitdir),
David Pursehouseabdf7502020-02-12 14:58:39 +0900611 worktree=os.path.join(self.manifest.topdir, path),
612 relpath=path,
613 revisionExpr='HEAD',
614 revisionId=None,
615 groups=None)
Mike Frysingerc0d18662020-02-19 19:19:18 -0500616 if not project.DeleteWorktree(
David Pursehouseaa611a22020-02-20 10:47:26 +0900617 quiet=opt.quiet,
618 force=opt.force_remove_dirty):
Mike Frysingera850ca22019-08-07 17:19:24 -0400619 return 1
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700620
Shawn O. Pearce9fb29ce2009-06-04 20:41:02 -0700621 new_project_paths.sort()
Mike Frysinger3164d402019-11-11 05:40:22 -0500622 with open(file_path, 'w') as fd:
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700623 fd.write('\n'.join(new_project_paths))
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700624 fd.write('\n')
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700625 return 0
626
jiajia tanga590e642021-04-25 20:02:02 +0800627 def UpdateCopyLinkfileList(self):
628 """Save all dests of copyfile and linkfile, and update them if needed.
629
630 Returns:
631 Whether update was successful.
632 """
633 new_paths = {}
634 new_linkfile_paths = []
635 new_copyfile_paths = []
636 for project in self.GetProjects(None, missing_ok=True):
637 new_linkfile_paths.extend(x.dest for x in project.linkfiles)
638 new_copyfile_paths.extend(x.dest for x in project.copyfiles)
639
640 new_paths = {
641 'linkfile': new_linkfile_paths,
642 'copyfile': new_copyfile_paths,
643 }
644
645 copylinkfile_name = 'copy-link-files.json'
646 copylinkfile_path = os.path.join(self.manifest.repodir, copylinkfile_name)
647 old_copylinkfile_paths = {}
648
649 if os.path.exists(copylinkfile_path):
650 with open(copylinkfile_path, 'rb') as fp:
651 try:
652 old_copylinkfile_paths = json.load(fp)
653 except:
654 print('error: %s is not a json formatted file.' %
655 copylinkfile_path, file=sys.stderr)
656 platform_utils.remove(copylinkfile_path)
657 return False
658
659 need_remove_files = []
660 need_remove_files.extend(
661 set(old_copylinkfile_paths.get('linkfile', [])) -
662 set(new_linkfile_paths))
663 need_remove_files.extend(
664 set(old_copylinkfile_paths.get('copyfile', [])) -
665 set(new_copyfile_paths))
666
667 for need_remove_file in need_remove_files:
668 try:
669 platform_utils.remove(need_remove_file)
670 except OSError as e:
671 if e.errno == errno.ENOENT:
672 # Try to remove the updated copyfile or linkfile.
673 # So, if the file is not exist, nothing need to do.
674 pass
675
676 # Create copy-link-files.json, save dest path of "copyfile" and "linkfile".
677 with open(copylinkfile_path, 'w', encoding='utf-8') as fp:
678 json.dump(new_paths, fp)
679 return True
680
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400681 def _SmartSyncSetup(self, opt, smart_sync_manifest_path):
682 if not self.manifest.manifest_server:
683 print('error: cannot smart sync: no manifest server defined in '
684 'manifest', file=sys.stderr)
685 sys.exit(1)
686
687 manifest_server = self.manifest.manifest_server
688 if not opt.quiet:
689 print('Using manifest server %s' % manifest_server)
690
David Pursehouseeeff3532020-02-12 11:24:10 +0900691 if '@' not in manifest_server:
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400692 username = None
693 password = None
694 if opt.manifest_server_username and opt.manifest_server_password:
695 username = opt.manifest_server_username
696 password = opt.manifest_server_password
697 else:
698 try:
699 info = netrc.netrc()
700 except IOError:
701 # .netrc file does not exist or could not be opened
702 pass
703 else:
704 try:
705 parse_result = urllib.parse.urlparse(manifest_server)
706 if parse_result.hostname:
707 auth = info.authenticators(parse_result.hostname)
708 if auth:
709 username, _account, password = auth
710 else:
711 print('No credentials found for %s in .netrc'
712 % parse_result.hostname, file=sys.stderr)
713 except netrc.NetrcParseError as e:
714 print('Error parsing .netrc file: %s' % e, file=sys.stderr)
715
716 if (username and password):
717 manifest_server = manifest_server.replace('://', '://%s:%s@' %
718 (username, password),
719 1)
720
721 transport = PersistentTransport(manifest_server)
722 if manifest_server.startswith('persistent-'):
723 manifest_server = manifest_server[len('persistent-'):]
724
725 try:
726 server = xmlrpc.client.Server(manifest_server, transport=transport)
727 if opt.smart_sync:
Raman Tenneti8d43dea2021-02-07 16:30:27 -0800728 branch = self._GetBranch()
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400729
Mike Frysinger56ce3462019-12-04 19:30:48 -0500730 if 'SYNC_TARGET' in os.environ:
Mike Frysingere20da3e2020-03-07 01:53:53 -0500731 target = os.environ['SYNC_TARGET']
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400732 [success, manifest_str] = server.GetApprovedManifest(branch, target)
Mike Frysinger56ce3462019-12-04 19:30:48 -0500733 elif ('TARGET_PRODUCT' in os.environ and
734 'TARGET_BUILD_VARIANT' in os.environ):
Mike Frysingere20da3e2020-03-07 01:53:53 -0500735 target = '%s-%s' % (os.environ['TARGET_PRODUCT'],
736 os.environ['TARGET_BUILD_VARIANT'])
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400737 [success, manifest_str] = server.GetApprovedManifest(branch, target)
738 else:
739 [success, manifest_str] = server.GetApprovedManifest(branch)
740 else:
741 assert(opt.smart_tag)
742 [success, manifest_str] = server.GetManifest(opt.smart_tag)
743
744 if success:
745 manifest_name = os.path.basename(smart_sync_manifest_path)
746 try:
Mike Frysinger3164d402019-11-11 05:40:22 -0500747 with open(smart_sync_manifest_path, 'w') as f:
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400748 f.write(manifest_str)
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400749 except IOError as e:
750 print('error: cannot write manifest to %s:\n%s'
751 % (smart_sync_manifest_path, e),
752 file=sys.stderr)
753 sys.exit(1)
754 self._ReloadManifest(manifest_name)
755 else:
756 print('error: manifest server RPC call failed: %s' %
757 manifest_str, file=sys.stderr)
758 sys.exit(1)
759 except (socket.error, IOError, xmlrpc.client.Fault) as e:
760 print('error: cannot connect to manifest server %s:\n%s'
761 % (self.manifest.manifest_server, e), file=sys.stderr)
762 sys.exit(1)
763 except xmlrpc.client.ProtocolError as e:
764 print('error: cannot connect to manifest server %s:\n%d %s'
765 % (self.manifest.manifest_server, e.errcode, e.errmsg),
766 file=sys.stderr)
767 sys.exit(1)
768
769 return manifest_name
770
Mike Frysingerfb527e32019-08-27 02:34:32 -0400771 def _UpdateManifestProject(self, opt, mp, manifest_name):
772 """Fetch & update the local manifest project."""
773 if not opt.local_only:
774 start = time.time()
Mike Frysinger521d01b2020-02-17 01:51:49 -0500775 success = mp.Sync_NetworkHalf(quiet=opt.quiet, verbose=opt.verbose,
Raman Tenneti2ae44d72021-03-23 15:12:27 -0700776 current_branch_only=self._GetCurrentBranchOnly(opt),
Erwan Yvindc5c4d12019-06-18 13:49:12 +0200777 force_sync=opt.force_sync,
Mike Frysingerc58ec4d2020-02-17 14:36:08 -0500778 tags=opt.tags,
Mike Frysingerfb527e32019-08-27 02:34:32 -0400779 optimized_fetch=opt.optimized_fetch,
George Engelbrecht9bc283e2020-04-02 12:36:09 -0600780 retry_fetches=opt.retry_fetches,
Mike Frysingerfb527e32019-08-27 02:34:32 -0400781 submodules=self.manifest.HasSubmodules,
Raman Tennetif32f2432021-04-12 20:57:25 -0700782 clone_filter=self.manifest.CloneFilter,
783 partial_clone_exclude=self.manifest.PartialCloneExclude)
Mike Frysingerfb527e32019-08-27 02:34:32 -0400784 finish = time.time()
785 self.event_log.AddSync(mp, event_log.TASK_SYNC_NETWORK,
786 start, finish, success)
787
788 if mp.HasChanges:
789 syncbuf = SyncBuffer(mp.config)
790 start = time.time()
791 mp.Sync_LocalHalf(syncbuf, submodules=self.manifest.HasSubmodules)
792 clean = syncbuf.Finish()
793 self.event_log.AddSync(mp, event_log.TASK_SYNC_LOCAL,
794 start, time.time(), clean)
795 if not clean:
796 sys.exit(1)
797 self._ReloadManifest(opt.manifest_name)
798 if opt.jobs is None:
799 self.jobs = self.manifest.default.sync_j
800
Mike Frysingerae6cb082019-08-27 01:10:59 -0400801 def ValidateOptions(self, opt, args):
802 if opt.force_broken:
803 print('warning: -f/--force-broken is now the default behavior, and the '
804 'options are deprecated', file=sys.stderr)
805 if opt.network_only and opt.detach_head:
806 self.OptionParser.error('cannot combine -n and -d')
807 if opt.network_only and opt.local_only:
808 self.OptionParser.error('cannot combine -n and -l')
809 if opt.manifest_name and opt.smart_sync:
810 self.OptionParser.error('cannot combine -m and -s')
811 if opt.manifest_name and opt.smart_tag:
812 self.OptionParser.error('cannot combine -m and -t')
813 if opt.manifest_server_username or opt.manifest_server_password:
814 if not (opt.smart_sync or opt.smart_tag):
815 self.OptionParser.error('-u and -p may only be combined with -s or -t')
816 if None in [opt.manifest_server_username, opt.manifest_server_password]:
817 self.OptionParser.error('both -u and -p must be given')
818
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700819 def Execute(self, opt, args):
Roy Lee18afd7f2010-05-09 04:32:08 +0800820 if opt.jobs:
821 self.jobs = opt.jobs
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -0700822 if self.jobs > 1:
823 soft_limit, _ = _rlimit_nofile()
Mike Frysinger0c0e9342019-06-13 12:42:39 -0400824 self.jobs = min(self.jobs, (soft_limit - 5) // 3)
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -0700825
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500826 if opt.manifest_name:
827 self.manifest.Override(opt.manifest_name)
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700828
Chirayu Desaia892b102013-06-11 14:18:46 +0530829 manifest_name = opt.manifest_name
David Pursehouse59b41742015-05-07 14:36:09 +0900830 smart_sync_manifest_path = os.path.join(
David Pursehouseabdf7502020-02-12 14:58:39 +0900831 self.manifest.manifestProject.worktree, 'smart_sync_override.xml')
Chirayu Desaia892b102013-06-11 14:18:46 +0530832
Xin Lid79a4bc2020-05-20 16:03:45 -0700833 if opt.clone_bundle is None:
834 opt.clone_bundle = self.manifest.CloneBundle
835
Victor Boivie08c880d2011-04-19 10:32:52 +0200836 if opt.smart_sync or opt.smart_tag:
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400837 manifest_name = self._SmartSyncSetup(opt, smart_sync_manifest_path)
838 else:
David Pursehouse59b41742015-05-07 14:36:09 +0900839 if os.path.isfile(smart_sync_manifest_path):
840 try:
Renaud Paquay010fed72016-11-11 14:25:29 -0800841 platform_utils.remove(smart_sync_manifest_path)
David Pursehouse59b41742015-05-07 14:36:09 +0900842 except OSError as e:
843 print('error: failed to remove existing smart sync override manifest: %s' %
844 e, file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700845
Mike Frysinger5a033082019-09-23 19:21:20 -0400846 err_event = _threading.Event()
847
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700848 rp = self.manifest.repoProject
849 rp.PreSync()
Mike Frysinger23c900f2020-02-29 02:52:44 -0500850 cb = rp.CurrentBranch
851 if cb:
852 base = rp.GetBranch(cb).merge
853 if not base or not base.startswith('refs/heads/'):
854 print('warning: repo is not tracking a remote branch, so it will not '
Mike Frysinger58ac1672020-03-14 14:35:26 -0400855 'receive updates; run `repo init --repo-rev=stable` to fix.',
Mike Frysinger23c900f2020-02-29 02:52:44 -0500856 file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700857
858 mp = self.manifest.manifestProject
859 mp.PreSync()
860
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800861 if opt.repo_upgraded:
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -0700862 _PostRepoUpgrade(self.manifest, quiet=opt.quiet)
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800863
Fredrik de Grootcc960972019-11-22 09:04:31 +0100864 if not opt.mp_update:
865 print('Skipping update of local manifest project.')
866 else:
867 self._UpdateManifestProject(opt, mp, manifest_name)
Simran Basib9a1b732015-08-20 12:19:28 -0700868
Raman Tennetifeb28912021-05-02 19:47:29 -0700869 load_local_manifests = not self.manifest.HasLocalManifests
Raman Tenneti2ae44d72021-03-23 15:12:27 -0700870 if self._UseSuperproject(opt):
Raman Tennetifeb28912021-05-02 19:47:29 -0700871 manifest_name = self._UpdateProjectsRevisionId(opt, args, load_local_manifests)
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800872
Simran Basib9a1b732015-08-20 12:19:28 -0700873 if self.gitc_manifest:
874 gitc_manifest_projects = self.GetProjects(args,
Simran Basib9a1b732015-08-20 12:19:28 -0700875 missing_ok=True)
876 gitc_projects = []
877 opened_projects = []
878 for project in gitc_manifest_projects:
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700879 if project.relpath in self.gitc_manifest.paths and \
880 self.gitc_manifest.paths[project.relpath].old_revision:
881 opened_projects.append(project.relpath)
Simran Basib9a1b732015-08-20 12:19:28 -0700882 else:
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700883 gitc_projects.append(project.relpath)
Simran Basib9a1b732015-08-20 12:19:28 -0700884
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700885 if not args:
886 gitc_projects = None
887
888 if gitc_projects != [] and not opt.local_only:
Simran Basib9a1b732015-08-20 12:19:28 -0700889 print('Updating GITC client: %s' % self.gitc_manifest.gitc_client_name)
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700890 manifest = GitcManifest(self.repodir, self.gitc_manifest.gitc_client_name)
891 if manifest_name:
892 manifest.Override(manifest_name)
893 else:
894 manifest.Override(self.manifest.manifestFile)
895 gitc_utils.generate_gitc_manifest(self.gitc_manifest,
896 manifest,
Simran Basib9a1b732015-08-20 12:19:28 -0700897 gitc_projects)
898 print('GITC client successfully synced.')
899
900 # The opened projects need to be synced as normal, therefore we
901 # generate a new args list to represent the opened projects.
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700902 # TODO: make this more reliable -- if there's a project name/path overlap,
903 # this may choose the wrong project.
David Pursehouse3bcd3052017-07-10 22:42:22 +0900904 args = [os.path.relpath(self.manifest.paths[path].worktree, os.getcwd())
905 for path in opened_projects]
Simran Basib9a1b732015-08-20 12:19:28 -0700906 if not args:
907 return
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800908 all_projects = self.GetProjects(args,
909 missing_ok=True,
910 submodules_ok=opt.fetch_submodules)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700911
Mike Frysinger5a033082019-09-23 19:21:20 -0400912 err_network_sync = False
913 err_update_projects = False
Mike Frysinger5a033082019-09-23 19:21:20 -0400914
Dave Borowitz67700e92012-10-23 15:00:54 -0700915 self._fetch_times = _FetchTimes(self.manifest)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700916 if not opt.local_only:
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700917 to_fetch = []
918 now = time.time()
Dave Borowitz67700e92012-10-23 15:00:54 -0700919 if _ONE_DAY_S <= (now - rp.LastFetch):
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700920 to_fetch.append(rp)
David Pursehouse8a68ff92012-09-24 12:15:13 +0900921 to_fetch.extend(all_projects)
Dave Borowitz67700e92012-10-23 15:00:54 -0700922 to_fetch.sort(key=self._fetch_times.Get, reverse=True)
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700923
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500924 success, fetched = self._Fetch(to_fetch, opt, err_event)
925 if not success:
926 err_event.set()
Mike Frysinger5a033082019-09-23 19:21:20 -0400927
Mike Frysingerc58ec4d2020-02-17 14:36:08 -0500928 _PostRepoFetch(rp, opt.repo_verify)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700929 if opt.network_only:
930 # bail out now; the rest touches the working tree
Mike Frysingerbe24a542021-02-23 03:24:12 -0500931 if err_event.is_set():
Mike Frysinger5a033082019-09-23 19:21:20 -0400932 print('\nerror: Exited sync due to fetch errors.\n', file=sys.stderr)
933 sys.exit(1)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700934 return
935
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800936 # Iteratively fetch missing and/or nested unregistered submodules
937 previously_missing_set = set()
938 while True:
Raman Tennetifeb28912021-05-02 19:47:29 -0700939 self._ReloadManifest(manifest_name, load_local_manifests)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800940 all_projects = self.GetProjects(args,
941 missing_ok=True,
942 submodules_ok=opt.fetch_submodules)
943 missing = []
944 for project in all_projects:
945 if project.gitdir not in fetched:
946 missing.append(project)
947 if not missing:
948 break
949 # Stop us from non-stopped fetching actually-missing repos: If set of
950 # missing repos has not been changed from last fetch, we break.
951 missing_set = set(p.name for p in missing)
952 if previously_missing_set == missing_set:
953 break
954 previously_missing_set = missing_set
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500955 success, new_fetched = self._Fetch(to_fetch, opt, err_event)
956 if not success:
957 err_event.set()
958 fetched.update(new_fetched)
Mike Frysinger5a033082019-09-23 19:21:20 -0400959
960 # If we saw an error, exit with code 1 so that other scripts can check.
Mike Frysingerbe24a542021-02-23 03:24:12 -0500961 if err_event.is_set():
Mike Frysinger5a033082019-09-23 19:21:20 -0400962 err_network_sync = True
963 if opt.fail_fast:
964 print('\nerror: Exited sync due to fetch errors.\n'
965 'Local checkouts *not* updated. Resolve network issues & '
966 'retry.\n'
967 '`repo sync -l` will update some local checkouts.',
968 file=sys.stderr)
969 sys.exit(1)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800970
Julien Campergue335f5ef2013-10-16 11:02:35 +0200971 if self.manifest.IsMirror or self.manifest.IsArchive:
Shawn O. Pearcecd1d7ff2009-06-04 16:15:53 -0700972 # bail out now, we have no working tree
973 return
974
Oleksii Okolielovd3c0f592018-12-17 19:23:44 -0500975 if self.UpdateProjectList(opt):
Mike Frysinger5a033082019-09-23 19:21:20 -0400976 err_event.set()
977 err_update_projects = True
978 if opt.fail_fast:
979 print('\nerror: Local checkouts *not* updated.', file=sys.stderr)
980 sys.exit(1)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700981
jiajia tanga590e642021-04-25 20:02:02 +0800982 if not self.UpdateCopyLinkfileList():
983 err_event.set()
984 err_update_linkfiles = True
985 if opt.fail_fast:
986 print('\nerror: Local update copyfile or linkfile failed.', file=sys.stderr)
987 sys.exit(1)
988
Mike Frysinger5a033082019-09-23 19:21:20 -0400989 err_results = []
Mike Frysingerebf04a42021-02-23 20:48:04 -0500990 # NB: We don't exit here because this is the last step.
991 err_checkout = not self._Checkout(all_projects, opt, err_results)
992 if err_checkout:
993 err_event.set()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700994
Doug Anderson2b8db3c2010-11-01 15:08:06 -0700995 # If there's a notice that's supposed to print at the end of the sync, print
996 # it now...
997 if self.manifest.notice:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700998 print(self.manifest.notice)
Doug Anderson2b8db3c2010-11-01 15:08:06 -0700999
Mike Frysinger5a033082019-09-23 19:21:20 -04001000 # If we saw an error, exit with code 1 so that other scripts can check.
Mike Frysingerbe24a542021-02-23 03:24:12 -05001001 if err_event.is_set():
Mike Frysinger5a033082019-09-23 19:21:20 -04001002 print('\nerror: Unable to fully sync the tree.', file=sys.stderr)
1003 if err_network_sync:
1004 print('error: Downloading network changes failed.', file=sys.stderr)
1005 if err_update_projects:
1006 print('error: Updating local project lists failed.', file=sys.stderr)
jiajia tanga590e642021-04-25 20:02:02 +08001007 if err_update_linkfiles:
1008 print('error: Updating copyfiles or linkfiles failed.', file=sys.stderr)
Mike Frysinger5a033082019-09-23 19:21:20 -04001009 if err_checkout:
1010 print('error: Checking out local projects failed.', file=sys.stderr)
1011 if err_results:
1012 print('Failing repos:\n%s' % '\n'.join(err_results), file=sys.stderr)
1013 print('Try re-running with "-j1 --fail-fast" to exit at the first error.',
1014 file=sys.stderr)
1015 sys.exit(1)
1016
Mike Frysingere19d9e12020-02-12 11:23:32 -05001017 if not opt.quiet:
1018 print('repo sync has finished successfully.')
1019
David Pursehouse819827a2020-02-12 15:20:19 +09001020
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -07001021def _PostRepoUpgrade(manifest, quiet=False):
Conley Owens094cdbe2014-01-30 15:09:59 -08001022 wrapper = Wrapper()
Conley Owensc9129d92012-10-01 16:12:28 -07001023 if wrapper.NeedSetupGnuPG():
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -07001024 wrapper.SetupGnuPG(quiet)
Conley Owensf2fe2d92014-01-29 13:53:43 -08001025 for project in manifest.projects:
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001026 if project.Exists:
1027 project.PostRepoUpgrade()
1028
David Pursehouse819827a2020-02-12 15:20:19 +09001029
Mike Frysingerc58ec4d2020-02-17 14:36:08 -05001030def _PostRepoFetch(rp, repo_verify=True, verbose=False):
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001031 if rp.HasChanges:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001032 print('info: A new version of repo is available', file=sys.stderr)
Mike Frysinger347f9ed2021-03-15 14:58:52 -04001033 wrapper = Wrapper()
1034 try:
1035 rev = rp.bare_git.describe(rp.GetRevisionId())
1036 except GitError:
1037 rev = None
1038 _, new_rev = wrapper.check_repo_rev(rp.gitdir, rev, repo_verify=repo_verify)
1039 # See if we're held back due to missing signed tag.
1040 current_revid = rp.bare_git.rev_parse('HEAD')
1041 new_revid = rp.bare_git.rev_parse('--verify', new_rev)
1042 if current_revid != new_revid:
1043 # We want to switch to the new rev, but also not trash any uncommitted
1044 # changes. This helps with local testing/hacking.
1045 # If a local change has been made, we will throw that away.
1046 # We also have to make sure this will switch to an older commit if that's
1047 # the latest tag in order to support release rollback.
1048 try:
1049 rp.work_git.reset('--keep', new_rev)
1050 except GitError as e:
1051 sys.exit(str(e))
Sarah Owenscecd1d82012-11-01 22:59:27 -07001052 print('info: Restarting repo with latest version', file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001053 raise RepoChangedException(['--repo-upgraded'])
1054 else:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001055 print('warning: Skipped upgrade to unverified version', file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001056 else:
1057 if verbose:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001058 print('repo version %s is current' % rp.work_git.describe(HEAD),
1059 file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001060
David Pursehouse819827a2020-02-12 15:20:19 +09001061
Dave Borowitz67700e92012-10-23 15:00:54 -07001062class _FetchTimes(object):
Dave Borowitzd9478582012-10-23 16:35:39 -07001063 _ALPHA = 0.5
1064
Dave Borowitz67700e92012-10-23 15:00:54 -07001065 def __init__(self, manifest):
Anthony King85b24ac2014-05-06 15:57:48 +01001066 self._path = os.path.join(manifest.repodir, '.repo_fetchtimes.json')
Dave Borowitz67700e92012-10-23 15:00:54 -07001067 self._times = None
Dave Borowitzd9478582012-10-23 16:35:39 -07001068 self._seen = set()
Dave Borowitz67700e92012-10-23 15:00:54 -07001069
1070 def Get(self, project):
1071 self._Load()
1072 return self._times.get(project.name, _ONE_DAY_S)
1073
1074 def Set(self, project, t):
Dave Borowitzd9478582012-10-23 16:35:39 -07001075 self._Load()
1076 name = project.name
1077 old = self._times.get(name, t)
1078 self._seen.add(name)
1079 a = self._ALPHA
David Pursehouse54a4e602020-02-12 14:31:05 +09001080 self._times[name] = (a * t) + ((1 - a) * old)
Dave Borowitz67700e92012-10-23 15:00:54 -07001081
1082 def _Load(self):
1083 if self._times is None:
1084 try:
Mike Frysinger3164d402019-11-11 05:40:22 -05001085 with open(self._path) as f:
Anthony King85b24ac2014-05-06 15:57:48 +01001086 self._times = json.load(f)
Anthony King85b24ac2014-05-06 15:57:48 +01001087 except (IOError, ValueError):
1088 try:
Renaud Paquay010fed72016-11-11 14:25:29 -08001089 platform_utils.remove(self._path)
Anthony King85b24ac2014-05-06 15:57:48 +01001090 except OSError:
1091 pass
1092 self._times = {}
Dave Borowitz67700e92012-10-23 15:00:54 -07001093
1094 def Save(self):
1095 if self._times is None:
1096 return
Dave Borowitzd9478582012-10-23 16:35:39 -07001097
1098 to_delete = []
1099 for name in self._times:
1100 if name not in self._seen:
1101 to_delete.append(name)
1102 for name in to_delete:
1103 del self._times[name]
1104
Dave Borowitz67700e92012-10-23 15:00:54 -07001105 try:
Mike Frysinger3164d402019-11-11 05:40:22 -05001106 with open(self._path, 'w') as f:
Anthony King85b24ac2014-05-06 15:57:48 +01001107 json.dump(self._times, f, indent=2)
Anthony King85b24ac2014-05-06 15:57:48 +01001108 except (IOError, TypeError):
1109 try:
Renaud Paquay010fed72016-11-11 14:25:29 -08001110 platform_utils.remove(self._path)
Anthony King85b24ac2014-05-06 15:57:48 +01001111 except OSError:
1112 pass
Dan Willemsen0745bb22015-08-17 13:41:45 -07001113
1114# This is a replacement for xmlrpc.client.Transport using urllib2
1115# and supporting persistent-http[s]. It cannot change hosts from
1116# request to request like the normal transport, the real url
1117# is passed during initialization.
David Pursehouse819827a2020-02-12 15:20:19 +09001118
1119
Dan Willemsen0745bb22015-08-17 13:41:45 -07001120class PersistentTransport(xmlrpc.client.Transport):
1121 def __init__(self, orig_host):
1122 self.orig_host = orig_host
1123
1124 def request(self, host, handler, request_body, verbose=False):
1125 with GetUrlCookieFile(self.orig_host, not verbose) as (cookiefile, proxy):
1126 # Python doesn't understand cookies with the #HttpOnly_ prefix
1127 # Since we're only using them for HTTP, copy the file temporarily,
1128 # stripping those prefixes away.
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001129 if cookiefile:
Collin Fijalkoviche1191b32020-02-18 10:57:32 -08001130 tmpcookiefile = tempfile.NamedTemporaryFile(mode='w')
David Pursehouse4c5f74e2015-10-02 11:10:10 +09001131 tmpcookiefile.write("# HTTP Cookie File")
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001132 try:
1133 with open(cookiefile) as f:
1134 for line in f:
1135 if line.startswith("#HttpOnly_"):
1136 line = line[len("#HttpOnly_"):]
1137 tmpcookiefile.write(line)
1138 tmpcookiefile.flush()
Dan Willemsen0745bb22015-08-17 13:41:45 -07001139
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001140 cookiejar = cookielib.MozillaCookieJar(tmpcookiefile.name)
David Pursehouseb1ad2192015-09-30 10:35:43 +09001141 try:
1142 cookiejar.load()
1143 except cookielib.LoadError:
1144 cookiejar = cookielib.CookieJar()
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001145 finally:
1146 tmpcookiefile.close()
1147 else:
1148 cookiejar = cookielib.CookieJar()
Dan Willemsen0745bb22015-08-17 13:41:45 -07001149
1150 proxyhandler = urllib.request.ProxyHandler
1151 if proxy:
1152 proxyhandler = urllib.request.ProxyHandler({
1153 "http": proxy,
David Pursehouse54a4e602020-02-12 14:31:05 +09001154 "https": proxy})
Dan Willemsen0745bb22015-08-17 13:41:45 -07001155
1156 opener = urllib.request.build_opener(
1157 urllib.request.HTTPCookieProcessor(cookiejar),
1158 proxyhandler)
1159
1160 url = urllib.parse.urljoin(self.orig_host, handler)
1161 parse_results = urllib.parse.urlparse(url)
1162
1163 scheme = parse_results.scheme
1164 if scheme == 'persistent-http':
1165 scheme = 'http'
1166 if scheme == 'persistent-https':
1167 # If we're proxying through persistent-https, use http. The
1168 # proxy itself will do the https.
1169 if proxy:
1170 scheme = 'http'
1171 else:
1172 scheme = 'https'
1173
1174 # Parse out any authentication information using the base class
1175 host, extra_headers, _ = self.get_host_info(parse_results.netloc)
1176
1177 url = urllib.parse.urlunparse((
1178 scheme,
1179 host,
1180 parse_results.path,
1181 parse_results.params,
1182 parse_results.query,
1183 parse_results.fragment))
1184
1185 request = urllib.request.Request(url, request_body)
1186 if extra_headers is not None:
1187 for (name, header) in extra_headers:
1188 request.add_header(name, header)
1189 request.add_header('Content-Type', 'text/xml')
1190 try:
1191 response = opener.open(request)
1192 except urllib.error.HTTPError as e:
1193 if e.code == 501:
1194 # We may have been redirected through a login process
1195 # but our POST turned into a GET. Retry.
1196 response = opener.open(request)
1197 else:
1198 raise
1199
1200 p, u = xmlrpc.client.getparser()
1201 while 1:
1202 data = response.read(1024)
1203 if not data:
1204 break
1205 p.feed(data)
1206 p.close()
1207 return u.close()
1208
1209 def close(self):
1210 pass