blob: 381e9e7749795dac49e2b95e50e09849a9086f55 [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 [...]
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):
Mike Frysingerc177f942021-05-04 08:06:36 -0400172 if self.manifest:
173 try:
174 self.PARALLEL_JOBS = self.manifest.default.sync_j
175 except ManifestParseError:
176 pass
Mike Frysinger9180a072021-04-13 14:57:40 -0400177 super()._CommonOptions(p)
Shawn O. Pearce6392c872011-09-22 17:44:31 -0700178
Mike Frysinger9180a072021-04-13 14:57:40 -0400179 def _Options(self, p, show_smart=True):
Mike Frysinger49de8ef2021-04-09 00:21:02 -0400180 p.add_option('--jobs-network', default=None, type=int, metavar='JOBS',
181 help='number of network jobs to run in parallel (defaults to --jobs)')
182 p.add_option('--jobs-checkout', default=None, type=int, metavar='JOBS',
183 help='number of local checkout jobs to run in parallel (defaults to --jobs)')
184
Andrei Warkentin5df6de02010-07-02 17:58:31 -0500185 p.add_option('-f', '--force-broken',
Stefan Müller-Klieser46702ed2019-08-30 10:20:15 +0200186 dest='force_broken', action='store_true',
Mike Frysingerd9e5cf02019-08-26 03:12:55 -0400187 help='obsolete option (to be deleted in the future)')
188 p.add_option('--fail-fast',
189 dest='fail_fast', action='store_true',
190 help='stop syncing after first error is hit')
Kevin Degiabaa7f32014-11-12 11:27:45 -0700191 p.add_option('--force-sync',
192 dest='force_sync', action='store_true',
193 help="overwrite an existing git directory if it needs to "
194 "point to a different object directory. WARNING: this "
195 "may cause loss of data")
Oleksii Okolielovd3c0f592018-12-17 19:23:44 -0500196 p.add_option('--force-remove-dirty',
197 dest='force_remove_dirty', action='store_true',
198 help="force remove projects with uncommitted modifications if "
199 "projects no longer exist in the manifest. "
200 "WARNING: this may cause loss of data")
David Pursehouse8f62fb72012-11-14 12:09:38 +0900201 p.add_option('-l', '--local-only',
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700202 dest='local_only', action='store_true',
203 help="only update working tree, don't fetch")
David Pursehouse54a4e602020-02-12 14:31:05 +0900204 p.add_option('--no-manifest-update', '--nmu',
Fredrik de Grootcc960972019-11-22 09:04:31 +0100205 dest='mp_update', action='store_false', default='true',
206 help='use the existing manifest checkout as-is. '
207 '(do not update to the latest revision)')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900208 p.add_option('-n', '--network-only',
Shawn O. Pearce96fdcef2009-04-10 16:29:20 -0700209 dest='network_only', action='store_true',
210 help="fetch only, don't update working tree")
David Pursehouse8f62fb72012-11-14 12:09:38 +0900211 p.add_option('-d', '--detach',
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700212 dest='detach_head', action='store_true',
213 help='detach projects back to manifest revision')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900214 p.add_option('-c', '--current-branch',
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -0700215 dest='current_branch_only', action='store_true',
216 help='fetch only current branch from server')
Mike Frysinger73561142021-05-03 01:10:09 -0400217 p.add_option('--no-current-branch',
218 dest='current_branch_only', action='store_false',
219 help='fetch all branches from server')
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500220 p.add_option('-m', '--manifest-name',
221 dest='manifest_name',
222 help='temporary manifest to use for this sync', metavar='NAME.xml')
Xin Lid79a4bc2020-05-20 16:03:45 -0700223 p.add_option('--clone-bundle', action='store_true',
224 help='enable use of /clone.bundle on HTTP/HTTPS')
225 p.add_option('--no-clone-bundle', dest='clone_bundle', action='store_false',
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -0700226 help='disable use of /clone.bundle on HTTP/HTTPS')
Conley Owens8d070cf2012-11-06 13:14:31 -0800227 p.add_option('-u', '--manifest-server-username', action='store',
228 dest='manifest_server_username',
229 help='username to authenticate with the manifest server')
230 p.add_option('-p', '--manifest-server-password', action='store',
231 dest='manifest_server_password',
232 help='password to authenticate with the manifest server')
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800233 p.add_option('--fetch-submodules',
234 dest='fetch_submodules', action='store_true',
235 help='fetch submodules from server')
Raman Tenneti6a872c92021-01-14 19:17:50 -0800236 p.add_option('--use-superproject', action='store_true',
237 help='use the manifest superproject to sync projects')
Mike Frysingerd68ed632021-05-03 01:21:35 -0400238 p.add_option('--tags',
239 action='store_false',
240 help='fetch tags')
Mitchel Humpherys597868b2012-10-29 10:18:34 -0700241 p.add_option('--no-tags',
Mike Frysingerd68ed632021-05-03 01:21:35 -0400242 dest='tags', action='store_false',
Mitchel Humpherys597868b2012-10-29 10:18:34 -0700243 help="don't fetch tags")
David Pursehouseb1553542014-09-04 21:28:09 +0900244 p.add_option('--optimized-fetch',
245 dest='optimized_fetch', action='store_true',
246 help='only fetch projects fixed to sha1 if revision does not exist locally')
George Engelbrecht9bc283e2020-04-02 12:36:09 -0600247 p.add_option('--retry-fetches',
248 default=0, action='store', type='int',
249 help='number of times to retry fetches on transient errors')
David Pursehouse74cfd272015-10-14 10:50:15 +0900250 p.add_option('--prune', dest='prune', action='store_true',
251 help='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
Raman Tenneti8d43dea2021-02-07 16:30:27 -0800268 def _GetBranch(self):
269 """Returns the branch name for getting the approved manifest."""
270 p = self.manifest.manifestProject
271 b = p.GetBranch(p.CurrentBranch)
272 branch = b.merge
273 if branch.startswith(R_HEADS):
274 branch = branch[len(R_HEADS):]
275 return branch
276
Raman Tenneti2ae44d72021-03-23 15:12:27 -0700277 def _UseSuperproject(self, opt):
278 """Returns True if use-superproject option is enabled"""
279 return (opt.use_superproject or
280 self.manifest.manifestProject.config.GetBoolean(
281 'repo.superproject'))
282
283 def _GetCurrentBranchOnly(self, opt):
284 """Returns True if current-branch or use-superproject options are enabled."""
285 return opt.current_branch_only or self._UseSuperproject(opt)
286
Raman Tennetifeb28912021-05-02 19:47:29 -0700287 def _UpdateProjectsRevisionId(self, opt, args, load_local_manifests):
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800288 """Update revisionId of every project with the SHA from superproject.
289
290 This function updates each project's revisionId with SHA from superproject.
291 It writes the updated manifest into a file and reloads the manifest from it.
292
293 Args:
294 opt: Program options returned from optparse. See _Options().
295 args: Arguments to pass to GetProjects. See the GetProjects
296 docstring for details.
Raman Tennetifeb28912021-05-02 19:47:29 -0700297 load_local_manifests: Whether to load local manifests.
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800298
299 Returns:
300 Returns path to the overriding manifest file.
301 """
Raman Tenneti21dce3d2021-02-09 00:26:31 -0800302 superproject = git_superproject.Superproject(self.manifest,
Raman Tennetief99ec02021-03-04 10:29:40 -0800303 self.repodir,
304 quiet=opt.quiet)
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800305 all_projects = self.GetProjects(args,
306 missing_ok=True,
307 submodules_ok=opt.fetch_submodules)
Raman Tenneti21dce3d2021-02-09 00:26:31 -0800308 manifest_path = superproject.UpdateProjectsRevisionId(all_projects)
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800309 if not manifest_path:
310 print('error: Update of revsionId from superproject has failed',
311 file=sys.stderr)
312 sys.exit(1)
Raman Tennetifeb28912021-05-02 19:47:29 -0700313 self._ReloadManifest(manifest_path, load_local_manifests)
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800314 return manifest_path
315
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500316 def _FetchProjectList(self, opt, projects):
317 """Main function of the fetch worker.
318
319 The projects we're given share the same underlying git object store, so we
320 have to fetch them in serial.
Roy Lee18afd7f2010-05-09 04:32:08 +0800321
David James8d201162013-10-11 17:03:19 -0700322 Delegates most of the work to _FetchHelper.
323
324 Args:
325 opt: Program options returned from optparse. See _Options().
326 projects: Projects to fetch.
David James8d201162013-10-11 17:03:19 -0700327 """
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500328 return [self._FetchOne(opt, x) for x in projects]
David James8d201162013-10-11 17:03:19 -0700329
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500330 def _FetchOne(self, opt, project):
David James8d201162013-10-11 17:03:19 -0700331 """Fetch git objects for a single project.
332
David Pursehousec1b86a22012-11-14 11:36:51 +0900333 Args:
334 opt: Program options returned from optparse. See _Options().
335 project: Project object for the project to fetch.
David James8d201162013-10-11 17:03:19 -0700336
337 Returns:
338 Whether the fetch was successful.
David Pursehousec1b86a22012-11-14 11:36:51 +0900339 """
David Rileye0684ad2017-04-05 00:02:59 -0700340 start = time.time()
341 success = False
Mike Frysinger7b586f22021-02-23 18:38:39 -0500342 buf = io.StringIO()
David Pursehousec1b86a22012-11-14 11:36:51 +0900343 try:
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500344 success = project.Sync_NetworkHalf(
345 quiet=opt.quiet,
346 verbose=opt.verbose,
347 output_redir=buf,
348 current_branch_only=self._GetCurrentBranchOnly(opt),
349 force_sync=opt.force_sync,
350 clone_bundle=opt.clone_bundle,
351 tags=opt.tags, archive=self.manifest.IsArchive,
352 optimized_fetch=opt.optimized_fetch,
353 retry_fetches=opt.retry_fetches,
354 prune=opt.prune,
Raman Tennetif32f2432021-04-12 20:57:25 -0700355 clone_filter=self.manifest.CloneFilter,
356 partial_clone_exclude=self.manifest.PartialCloneExclude)
Doug Andersonfc06ced2011-03-16 15:49:18 -0700357
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500358 output = buf.getvalue()
359 if opt.verbose and output:
360 print('\n' + output.rstrip())
Doug Andersonfc06ced2011-03-16 15:49:18 -0700361
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500362 if not success:
363 print('error: Cannot fetch %s from %s'
364 % (project.name, project.remote.url),
365 file=sys.stderr)
Raman Tennetiad8aa692021-04-15 09:20:51 -0700366 except GitError as e:
367 print('error.GitError: Cannot fetch %s' % str(e), file=sys.stderr)
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500368 except Exception as e:
369 print('error: Cannot fetch %s (%s: %s)'
370 % (project.name, type(e).__name__, str(e)), file=sys.stderr)
371 raise
Mike Frysinger7b586f22021-02-23 18:38:39 -0500372
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500373 finish = time.time()
374 return (success, project, start, finish)
David James8d201162013-10-11 17:03:19 -0700375
Mike Frysinger5a033082019-09-23 19:21:20 -0400376 def _Fetch(self, projects, opt, err_event):
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500377 ret = True
378
Mike Frysinger49de8ef2021-04-09 00:21:02 -0400379 jobs = opt.jobs_network if opt.jobs_network else self.jobs
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700380 fetched = set()
Mike Frysinger151701e2021-04-13 15:07:21 -0400381 pm = Progress('Fetching', len(projects), delay=False, quiet=opt.quiet)
Roy Lee18afd7f2010-05-09 04:32:08 +0800382
David James89ece422014-01-09 18:51:58 -0800383 objdir_project_map = dict()
384 for project in projects:
385 objdir_project_map.setdefault(project.objdir, []).append(project)
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500386 projects_list = list(objdir_project_map.values())
David James8d201162013-10-11 17:03:19 -0700387
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500388 def _ProcessResults(results_sets):
389 ret = True
390 for results in results_sets:
391 for (success, project, start, finish) in results:
392 self._fetch_times.Set(project, finish - start)
393 self.event_log.AddSync(project, event_log.TASK_SYNC_NETWORK,
394 start, finish, success)
395 # Check for any errors before running any more tasks.
396 # ...we'll let existing jobs finish, though.
397 if not success:
398 ret = False
399 else:
400 fetched.add(project.gitdir)
401 pm.update(msg=project.name)
402 if not ret and opt.fail_fast:
403 break
404 return ret
Doug Andersonfc06ced2011-03-16 15:49:18 -0700405
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500406 # NB: Multiprocessing is heavy, so don't spin it up for one job.
Mike Frysinger49de8ef2021-04-09 00:21:02 -0400407 if len(projects_list) == 1 or jobs == 1:
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500408 if not _ProcessResults(self._FetchProjectList(opt, x) for x in projects_list):
409 ret = False
410 else:
411 # Favor throughput over responsiveness when quiet. It seems that imap()
412 # will yield results in batches relative to chunksize, so even as the
413 # children finish a sync, we won't see the result until one child finishes
414 # ~chunksize jobs. When using a large --jobs with large chunksize, this
415 # can be jarring as there will be a large initial delay where repo looks
416 # like it isn't doing anything and sits at 0%, but then suddenly completes
417 # a lot of jobs all at once. Since this code is more network bound, we
418 # can accept a bit more CPU overhead with a smaller chunksize so that the
419 # user sees more immediate & continuous feedback.
420 if opt.quiet:
421 chunksize = WORKER_BATCH_SIZE
David James89ece422014-01-09 18:51:58 -0800422 else:
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500423 pm.update(inc=0, msg='warming up')
424 chunksize = 4
Mike Frysinger49de8ef2021-04-09 00:21:02 -0400425 with multiprocessing.Pool(jobs) as pool:
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500426 results = pool.imap_unordered(
427 functools.partial(self._FetchProjectList, opt),
428 projects_list,
429 chunksize=chunksize)
430 if not _ProcessResults(results):
431 ret = False
432 pool.close()
Roy Lee18afd7f2010-05-09 04:32:08 +0800433
Shawn O. Pearce68194f42009-04-10 16:48:52 -0700434 pm.end()
Dave Borowitz67700e92012-10-23 15:00:54 -0700435 self._fetch_times.Save()
Dave Borowitz18857212012-10-23 17:02:59 -0700436
Julien Campergue335f5ef2013-10-16 11:02:35 +0200437 if not self.manifest.IsArchive:
Mike Frysinger5a033082019-09-23 19:21:20 -0400438 self._GCProjects(projects, opt, err_event)
Julien Campergue335f5ef2013-10-16 11:02:35 +0200439
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500440 return (ret, fetched)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700441
Mike Frysingerb4429432021-05-05 20:03:26 -0400442 def _FetchMain(self, opt, args, all_projects, err_event, manifest_name,
443 load_local_manifests):
444 """The main network fetch loop.
445
446 Args:
447 opt: Program options returned from optparse. See _Options().
448 args: Command line args used to filter out projects.
449 all_projects: List of all projects that should be checked out.
450 err_event: Whether an error was hit while processing.
451 manifest_name: Manifest file to be reloaded.
452 load_local_manifests: Whether to load local manifests.
453 """
454 rp = self.manifest.repoProject
455
456 to_fetch = []
457 now = time.time()
458 if _ONE_DAY_S <= (now - rp.LastFetch):
459 to_fetch.append(rp)
460 to_fetch.extend(all_projects)
461 to_fetch.sort(key=self._fetch_times.Get, reverse=True)
462
463 success, fetched = self._Fetch(to_fetch, opt, err_event)
464 if not success:
465 err_event.set()
466
467 _PostRepoFetch(rp, opt.repo_verify)
468 if opt.network_only:
469 # bail out now; the rest touches the working tree
470 if err_event.is_set():
471 print('\nerror: Exited sync due to fetch errors.\n', file=sys.stderr)
472 sys.exit(1)
473 return
474
475 # Iteratively fetch missing and/or nested unregistered submodules
476 previously_missing_set = set()
477 while True:
478 self._ReloadManifest(manifest_name, load_local_manifests)
479 all_projects = self.GetProjects(args,
480 missing_ok=True,
481 submodules_ok=opt.fetch_submodules)
482 missing = []
483 for project in all_projects:
484 if project.gitdir not in fetched:
485 missing.append(project)
486 if not missing:
487 break
488 # Stop us from non-stopped fetching actually-missing repos: If set of
489 # missing repos has not been changed from last fetch, we break.
490 missing_set = set(p.name for p in missing)
491 if previously_missing_set == missing_set:
492 break
493 previously_missing_set = missing_set
494 success, new_fetched = self._Fetch(missing, opt, err_event)
495 if not success:
496 err_event.set()
497 fetched.update(new_fetched)
498
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500499 def _CheckoutOne(self, detach_head, force_sync, project):
Xin Li745be2e2019-06-03 11:24:30 -0700500 """Checkout work tree for one project
501
502 Args:
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500503 detach_head: Whether to leave a detached HEAD.
504 force_sync: Force checking out of the repo.
Xin Li745be2e2019-06-03 11:24:30 -0700505 project: Project object for the project to checkout.
Xin Li745be2e2019-06-03 11:24:30 -0700506
507 Returns:
508 Whether the fetch was successful.
509 """
Xin Li745be2e2019-06-03 11:24:30 -0700510 start = time.time()
511 syncbuf = SyncBuffer(self.manifest.manifestProject.config,
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500512 detach_head=detach_head)
Xin Li745be2e2019-06-03 11:24:30 -0700513 success = False
514 try:
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500515 project.Sync_LocalHalf(syncbuf, force_sync=force_sync)
Mike Frysingerebf04a42021-02-23 20:48:04 -0500516 success = syncbuf.Finish()
Raman Tennetiad8aa692021-04-15 09:20:51 -0700517 except GitError as e:
518 print('error.GitError: Cannot checkout %s: %s' %
519 (project.name, str(e)), file=sys.stderr)
Mike Frysingerebf04a42021-02-23 20:48:04 -0500520 except Exception as e:
521 print('error: Cannot checkout %s: %s: %s' %
522 (project.name, type(e).__name__, str(e)),
523 file=sys.stderr)
524 raise
Xin Li745be2e2019-06-03 11:24:30 -0700525
Mike Frysingerebf04a42021-02-23 20:48:04 -0500526 if not success:
527 print('error: Cannot checkout %s' % (project.name), file=sys.stderr)
528 finish = time.time()
529 return (success, project, start, finish)
Xin Li745be2e2019-06-03 11:24:30 -0700530
Mike Frysingerebf04a42021-02-23 20:48:04 -0500531 def _Checkout(self, all_projects, opt, err_results):
Xin Li745be2e2019-06-03 11:24:30 -0700532 """Checkout projects listed in all_projects
533
534 Args:
535 all_projects: List of all projects that should be checked out.
536 opt: Program options returned from optparse. See _Options().
Mike Frysingerebf04a42021-02-23 20:48:04 -0500537 err_results: A list of strings, paths to git repos where checkout failed.
Xin Li745be2e2019-06-03 11:24:30 -0700538 """
Mike Frysingerebf04a42021-02-23 20:48:04 -0500539 # Only checkout projects with worktrees.
540 all_projects = [x for x in all_projects if x.worktree]
Xin Li745be2e2019-06-03 11:24:30 -0700541
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500542 def _ProcessResults(pool, pm, results):
543 ret = True
Mike Frysingerebf04a42021-02-23 20:48:04 -0500544 for (success, project, start, finish) in results:
545 self.event_log.AddSync(project, event_log.TASK_SYNC_LOCAL,
546 start, finish, success)
547 # Check for any errors before running any more tasks.
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500548 # ...we'll let existing jobs finish, though.
Mike Frysingerebf04a42021-02-23 20:48:04 -0500549 if not success:
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500550 ret = False
Mike Frysingerebf04a42021-02-23 20:48:04 -0500551 err_results.append(project.relpath)
552 if opt.fail_fast:
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500553 if pool:
554 pool.close()
555 return ret
Mike Frysingerebf04a42021-02-23 20:48:04 -0500556 pm.update(msg=project.name)
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500557 return ret
Xin Li745be2e2019-06-03 11:24:30 -0700558
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500559 return self.ExecuteInParallel(
560 opt.jobs_checkout if opt.jobs_checkout else self.jobs,
561 functools.partial(self._CheckoutOne, opt.detach_head, opt.force_sync),
562 all_projects,
563 callback=_ProcessResults,
564 output=Progress('Checking out', len(all_projects), quiet=opt.quiet)) and not err_results
Mike Frysingerebf04a42021-02-23 20:48:04 -0500565
Mike Frysinger5a033082019-09-23 19:21:20 -0400566 def _GCProjects(self, projects, opt, err_event):
Mike Frysinger151701e2021-04-13 15:07:21 -0400567 pm = Progress('Garbage collecting', len(projects), delay=False, quiet=opt.quiet)
Mike Frysinger65af2602021-04-08 22:47:44 -0400568 pm.update(inc=0, msg='prescan')
569
Gabe Black2ff30292014-10-09 17:54:35 -0700570 gc_gitdirs = {}
David James8d201162013-10-11 17:03:19 -0700571 for project in projects:
Mike Frysinger5f2b0452020-02-11 03:23:24 -0500572 # Make sure pruning never kicks in with shared projects.
Mike Frysinger979d5bd2020-02-09 02:28:34 -0500573 if (not project.use_git_worktrees and
David Pursehouseaa611a22020-02-20 10:47:26 +0900574 len(project.manifest.GetProjectsWithName(project.name)) > 1):
Anders Björklund2a2da802021-01-18 10:32:36 +0100575 if not opt.quiet:
Mike Frysinger65af2602021-04-08 22:47:44 -0400576 print('\r%s: Shared project %s found, disabling pruning.' %
Anders Björklund2a2da802021-01-18 10:32:36 +0100577 (project.relpath, project.name))
Mike Frysinger5f2b0452020-02-11 03:23:24 -0500578 if git_require((2, 7, 0)):
Mike Frysingerf81c72e2020-02-19 15:50:00 -0500579 project.EnableRepositoryExtension('preciousObjects')
Mike Frysinger5f2b0452020-02-11 03:23:24 -0500580 else:
581 # This isn't perfect, but it's the best we can do with old git.
Mike Frysinger65af2602021-04-08 22:47:44 -0400582 print('\r%s: WARNING: shared projects are unreliable when using old '
Mike Frysinger5f2b0452020-02-11 03:23:24 -0500583 'versions of git; please upgrade to git-2.7.0+.'
584 % (project.relpath,),
585 file=sys.stderr)
586 project.config.SetString('gc.pruneExpire', 'never')
Gabe Black2ff30292014-10-09 17:54:35 -0700587 gc_gitdirs[project.gitdir] = project.bare_git
David James8d201162013-10-11 17:03:19 -0700588
Mike Frysinger65af2602021-04-08 22:47:44 -0400589 pm.update(inc=len(projects) - len(gc_gitdirs), msg='warming up')
590
591 cpu_count = os.cpu_count()
Dave Borowitz18857212012-10-23 17:02:59 -0700592 jobs = min(self.jobs, cpu_count)
593
594 if jobs < 2:
Gabe Black2ff30292014-10-09 17:54:35 -0700595 for bare_git in gc_gitdirs.values():
Mike Frysinger65af2602021-04-08 22:47:44 -0400596 pm.update(msg=bare_git._project.name)
David James8d201162013-10-11 17:03:19 -0700597 bare_git.gc('--auto')
Mike Frysinger65af2602021-04-08 22:47:44 -0400598 pm.end()
Dave Borowitz18857212012-10-23 17:02:59 -0700599 return
600
Mike Frysinger0c0e9342019-06-13 12:42:39 -0400601 config = {'pack.threads': cpu_count // jobs if cpu_count > jobs else 1}
Dave Borowitz18857212012-10-23 17:02:59 -0700602
603 threads = set()
604 sem = _threading.Semaphore(jobs)
Dave Borowitz18857212012-10-23 17:02:59 -0700605
David James8d201162013-10-11 17:03:19 -0700606 def GC(bare_git):
Mike Frysinger65af2602021-04-08 22:47:44 -0400607 pm.start(bare_git._project.name)
Dave Borowitz18857212012-10-23 17:02:59 -0700608 try:
609 try:
David James8d201162013-10-11 17:03:19 -0700610 bare_git.gc('--auto', config=config)
Dave Borowitz18857212012-10-23 17:02:59 -0700611 except GitError:
612 err_event.set()
David Pursehouse145e35b2020-02-12 15:40:47 +0900613 except Exception:
Dave Borowitz18857212012-10-23 17:02:59 -0700614 err_event.set()
615 raise
616 finally:
Mike Frysinger65af2602021-04-08 22:47:44 -0400617 pm.finish(bare_git._project.name)
Dave Borowitz18857212012-10-23 17:02:59 -0700618 sem.release()
619
Gabe Black2ff30292014-10-09 17:54:35 -0700620 for bare_git in gc_gitdirs.values():
Mike Frysingerbe24a542021-02-23 03:24:12 -0500621 if err_event.is_set() and opt.fail_fast:
Dave Borowitz18857212012-10-23 17:02:59 -0700622 break
623 sem.acquire()
David James8d201162013-10-11 17:03:19 -0700624 t = _threading.Thread(target=GC, args=(bare_git,))
Dave Borowitz18857212012-10-23 17:02:59 -0700625 t.daemon = True
626 threads.add(t)
627 t.start()
628
629 for t in threads:
630 t.join()
Mike Frysinger65af2602021-04-08 22:47:44 -0400631 pm.end()
Dave Borowitz18857212012-10-23 17:02:59 -0700632
Raman Tennetifeb28912021-05-02 19:47:29 -0700633 def _ReloadManifest(self, manifest_name=None, load_local_manifests=True):
634 """Reload the manfiest from the file specified by the |manifest_name|.
635
636 It unloads the manifest if |manifest_name| is None.
637
638 Args:
639 manifest_name: Manifest file to be reloaded.
640 load_local_manifests: Whether to load local manifests.
641 """
Tim Kilbourn07669002013-03-08 15:02:49 -0800642 if manifest_name:
643 # Override calls _Unload already
Raman Tennetifeb28912021-05-02 19:47:29 -0700644 self.manifest.Override(manifest_name, load_local_manifests=load_local_manifests)
Tim Kilbourn07669002013-03-08 15:02:49 -0800645 else:
646 self.manifest._Unload()
647
Oleksii Okolielovd3c0f592018-12-17 19:23:44 -0500648 def UpdateProjectList(self, opt):
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700649 new_project_paths = []
Colin Cross5acde752012-03-28 20:15:45 -0700650 for project in self.GetProjects(None, missing_ok=True):
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700651 if project.relpath:
652 new_project_paths.append(project.relpath)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700653 file_name = 'project.list'
Mike Frysingere3315bb2021-02-09 23:45:28 -0500654 file_path = os.path.join(self.repodir, file_name)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700655 old_project_paths = []
656
657 if os.path.exists(file_path):
Mike Frysinger3164d402019-11-11 05:40:22 -0500658 with open(file_path, 'r') as fd:
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700659 old_project_paths = fd.read().split('\n')
Kuang-che Wu0d9b16d2019-04-06 00:49:47 +0800660 # In reversed order, so subfolders are deleted before parent folder.
661 for path in sorted(old_project_paths, reverse=True):
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700662 if not path:
663 continue
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700664 if path not in new_project_paths:
David Pursehouse8a68ff92012-09-24 12:15:13 +0900665 # If the path has already been deleted, we don't need to do it
Dan Willemsen43507912016-09-01 16:26:02 -0700666 gitdir = os.path.join(self.manifest.topdir, path, '.git')
667 if os.path.exists(gitdir):
David Pursehousec1b86a22012-11-14 11:36:51 +0900668 project = Project(
David Pursehouseabdf7502020-02-12 14:58:39 +0900669 manifest=self.manifest,
670 name=path,
671 remote=RemoteSpec('origin'),
672 gitdir=gitdir,
673 objdir=gitdir,
Mike Frysingerc0d18662020-02-19 19:19:18 -0500674 use_git_worktrees=os.path.isfile(gitdir),
David Pursehouseabdf7502020-02-12 14:58:39 +0900675 worktree=os.path.join(self.manifest.topdir, path),
676 relpath=path,
677 revisionExpr='HEAD',
678 revisionId=None,
679 groups=None)
Mike Frysingerc0d18662020-02-19 19:19:18 -0500680 if not project.DeleteWorktree(
David Pursehouseaa611a22020-02-20 10:47:26 +0900681 quiet=opt.quiet,
682 force=opt.force_remove_dirty):
Mike Frysingera850ca22019-08-07 17:19:24 -0400683 return 1
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700684
Shawn O. Pearce9fb29ce2009-06-04 20:41:02 -0700685 new_project_paths.sort()
Mike Frysinger3164d402019-11-11 05:40:22 -0500686 with open(file_path, 'w') as fd:
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700687 fd.write('\n'.join(new_project_paths))
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700688 fd.write('\n')
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700689 return 0
690
jiajia tanga590e642021-04-25 20:02:02 +0800691 def UpdateCopyLinkfileList(self):
692 """Save all dests of copyfile and linkfile, and update them if needed.
693
694 Returns:
695 Whether update was successful.
696 """
697 new_paths = {}
698 new_linkfile_paths = []
699 new_copyfile_paths = []
700 for project in self.GetProjects(None, missing_ok=True):
701 new_linkfile_paths.extend(x.dest for x in project.linkfiles)
702 new_copyfile_paths.extend(x.dest for x in project.copyfiles)
703
704 new_paths = {
705 'linkfile': new_linkfile_paths,
706 'copyfile': new_copyfile_paths,
707 }
708
709 copylinkfile_name = 'copy-link-files.json'
710 copylinkfile_path = os.path.join(self.manifest.repodir, copylinkfile_name)
711 old_copylinkfile_paths = {}
712
713 if os.path.exists(copylinkfile_path):
714 with open(copylinkfile_path, 'rb') as fp:
715 try:
716 old_copylinkfile_paths = json.load(fp)
717 except:
718 print('error: %s is not a json formatted file.' %
719 copylinkfile_path, file=sys.stderr)
720 platform_utils.remove(copylinkfile_path)
721 return False
722
723 need_remove_files = []
724 need_remove_files.extend(
725 set(old_copylinkfile_paths.get('linkfile', [])) -
726 set(new_linkfile_paths))
727 need_remove_files.extend(
728 set(old_copylinkfile_paths.get('copyfile', [])) -
729 set(new_copyfile_paths))
730
731 for need_remove_file in need_remove_files:
732 try:
733 platform_utils.remove(need_remove_file)
734 except OSError as e:
735 if e.errno == errno.ENOENT:
736 # Try to remove the updated copyfile or linkfile.
737 # So, if the file is not exist, nothing need to do.
738 pass
739
740 # Create copy-link-files.json, save dest path of "copyfile" and "linkfile".
741 with open(copylinkfile_path, 'w', encoding='utf-8') as fp:
742 json.dump(new_paths, fp)
743 return True
744
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400745 def _SmartSyncSetup(self, opt, smart_sync_manifest_path):
746 if not self.manifest.manifest_server:
747 print('error: cannot smart sync: no manifest server defined in '
748 'manifest', file=sys.stderr)
749 sys.exit(1)
750
751 manifest_server = self.manifest.manifest_server
752 if not opt.quiet:
753 print('Using manifest server %s' % manifest_server)
754
David Pursehouseeeff3532020-02-12 11:24:10 +0900755 if '@' not in manifest_server:
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400756 username = None
757 password = None
758 if opt.manifest_server_username and opt.manifest_server_password:
759 username = opt.manifest_server_username
760 password = opt.manifest_server_password
761 else:
762 try:
763 info = netrc.netrc()
764 except IOError:
765 # .netrc file does not exist or could not be opened
766 pass
767 else:
768 try:
769 parse_result = urllib.parse.urlparse(manifest_server)
770 if parse_result.hostname:
771 auth = info.authenticators(parse_result.hostname)
772 if auth:
773 username, _account, password = auth
774 else:
775 print('No credentials found for %s in .netrc'
776 % parse_result.hostname, file=sys.stderr)
777 except netrc.NetrcParseError as e:
778 print('Error parsing .netrc file: %s' % e, file=sys.stderr)
779
780 if (username and password):
781 manifest_server = manifest_server.replace('://', '://%s:%s@' %
782 (username, password),
783 1)
784
785 transport = PersistentTransport(manifest_server)
786 if manifest_server.startswith('persistent-'):
787 manifest_server = manifest_server[len('persistent-'):]
788
789 try:
790 server = xmlrpc.client.Server(manifest_server, transport=transport)
791 if opt.smart_sync:
Raman Tenneti8d43dea2021-02-07 16:30:27 -0800792 branch = self._GetBranch()
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400793
Mike Frysinger56ce3462019-12-04 19:30:48 -0500794 if 'SYNC_TARGET' in os.environ:
Mike Frysingere20da3e2020-03-07 01:53:53 -0500795 target = os.environ['SYNC_TARGET']
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400796 [success, manifest_str] = server.GetApprovedManifest(branch, target)
Mike Frysinger56ce3462019-12-04 19:30:48 -0500797 elif ('TARGET_PRODUCT' in os.environ and
798 'TARGET_BUILD_VARIANT' in os.environ):
Mike Frysingere20da3e2020-03-07 01:53:53 -0500799 target = '%s-%s' % (os.environ['TARGET_PRODUCT'],
800 os.environ['TARGET_BUILD_VARIANT'])
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400801 [success, manifest_str] = server.GetApprovedManifest(branch, target)
802 else:
803 [success, manifest_str] = server.GetApprovedManifest(branch)
804 else:
805 assert(opt.smart_tag)
806 [success, manifest_str] = server.GetManifest(opt.smart_tag)
807
808 if success:
809 manifest_name = os.path.basename(smart_sync_manifest_path)
810 try:
Mike Frysinger3164d402019-11-11 05:40:22 -0500811 with open(smart_sync_manifest_path, 'w') as f:
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400812 f.write(manifest_str)
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400813 except IOError as e:
814 print('error: cannot write manifest to %s:\n%s'
815 % (smart_sync_manifest_path, e),
816 file=sys.stderr)
817 sys.exit(1)
818 self._ReloadManifest(manifest_name)
819 else:
820 print('error: manifest server RPC call failed: %s' %
821 manifest_str, file=sys.stderr)
822 sys.exit(1)
823 except (socket.error, IOError, xmlrpc.client.Fault) as e:
824 print('error: cannot connect to manifest server %s:\n%s'
825 % (self.manifest.manifest_server, e), file=sys.stderr)
826 sys.exit(1)
827 except xmlrpc.client.ProtocolError as e:
828 print('error: cannot connect to manifest server %s:\n%d %s'
829 % (self.manifest.manifest_server, e.errcode, e.errmsg),
830 file=sys.stderr)
831 sys.exit(1)
832
833 return manifest_name
834
Mike Frysingerfb527e32019-08-27 02:34:32 -0400835 def _UpdateManifestProject(self, opt, mp, manifest_name):
836 """Fetch & update the local manifest project."""
837 if not opt.local_only:
838 start = time.time()
Mike Frysinger521d01b2020-02-17 01:51:49 -0500839 success = mp.Sync_NetworkHalf(quiet=opt.quiet, verbose=opt.verbose,
Raman Tenneti2ae44d72021-03-23 15:12:27 -0700840 current_branch_only=self._GetCurrentBranchOnly(opt),
Erwan Yvindc5c4d12019-06-18 13:49:12 +0200841 force_sync=opt.force_sync,
Mike Frysingerc58ec4d2020-02-17 14:36:08 -0500842 tags=opt.tags,
Mike Frysingerfb527e32019-08-27 02:34:32 -0400843 optimized_fetch=opt.optimized_fetch,
George Engelbrecht9bc283e2020-04-02 12:36:09 -0600844 retry_fetches=opt.retry_fetches,
Mike Frysingerfb527e32019-08-27 02:34:32 -0400845 submodules=self.manifest.HasSubmodules,
Raman Tennetif32f2432021-04-12 20:57:25 -0700846 clone_filter=self.manifest.CloneFilter,
847 partial_clone_exclude=self.manifest.PartialCloneExclude)
Mike Frysingerfb527e32019-08-27 02:34:32 -0400848 finish = time.time()
849 self.event_log.AddSync(mp, event_log.TASK_SYNC_NETWORK,
850 start, finish, success)
851
852 if mp.HasChanges:
853 syncbuf = SyncBuffer(mp.config)
854 start = time.time()
855 mp.Sync_LocalHalf(syncbuf, submodules=self.manifest.HasSubmodules)
856 clean = syncbuf.Finish()
857 self.event_log.AddSync(mp, event_log.TASK_SYNC_LOCAL,
858 start, time.time(), clean)
859 if not clean:
860 sys.exit(1)
Mike Frysinger05638bf2021-05-04 15:33:31 -0400861 self._ReloadManifest(manifest_name)
Mike Frysingerfb527e32019-08-27 02:34:32 -0400862 if opt.jobs is None:
863 self.jobs = self.manifest.default.sync_j
864
Mike Frysingerae6cb082019-08-27 01:10:59 -0400865 def ValidateOptions(self, opt, args):
866 if opt.force_broken:
867 print('warning: -f/--force-broken is now the default behavior, and the '
868 'options are deprecated', file=sys.stderr)
869 if opt.network_only and opt.detach_head:
870 self.OptionParser.error('cannot combine -n and -d')
871 if opt.network_only and opt.local_only:
872 self.OptionParser.error('cannot combine -n and -l')
873 if opt.manifest_name and opt.smart_sync:
874 self.OptionParser.error('cannot combine -m and -s')
875 if opt.manifest_name and opt.smart_tag:
876 self.OptionParser.error('cannot combine -m and -t')
877 if opt.manifest_server_username or opt.manifest_server_password:
878 if not (opt.smart_sync or opt.smart_tag):
879 self.OptionParser.error('-u and -p may only be combined with -s or -t')
880 if None in [opt.manifest_server_username, opt.manifest_server_password]:
881 self.OptionParser.error('both -u and -p must be given')
882
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700883 def Execute(self, opt, args):
Roy Lee18afd7f2010-05-09 04:32:08 +0800884 if opt.jobs:
885 self.jobs = opt.jobs
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -0700886 if self.jobs > 1:
887 soft_limit, _ = _rlimit_nofile()
Mike Frysinger0c0e9342019-06-13 12:42:39 -0400888 self.jobs = min(self.jobs, (soft_limit - 5) // 3)
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -0700889
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500890 if opt.manifest_name:
891 self.manifest.Override(opt.manifest_name)
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700892
Chirayu Desaia892b102013-06-11 14:18:46 +0530893 manifest_name = opt.manifest_name
David Pursehouse59b41742015-05-07 14:36:09 +0900894 smart_sync_manifest_path = os.path.join(
David Pursehouseabdf7502020-02-12 14:58:39 +0900895 self.manifest.manifestProject.worktree, 'smart_sync_override.xml')
Chirayu Desaia892b102013-06-11 14:18:46 +0530896
Xin Lid79a4bc2020-05-20 16:03:45 -0700897 if opt.clone_bundle is None:
898 opt.clone_bundle = self.manifest.CloneBundle
899
Victor Boivie08c880d2011-04-19 10:32:52 +0200900 if opt.smart_sync or opt.smart_tag:
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400901 manifest_name = self._SmartSyncSetup(opt, smart_sync_manifest_path)
902 else:
David Pursehouse59b41742015-05-07 14:36:09 +0900903 if os.path.isfile(smart_sync_manifest_path):
904 try:
Renaud Paquay010fed72016-11-11 14:25:29 -0800905 platform_utils.remove(smart_sync_manifest_path)
David Pursehouse59b41742015-05-07 14:36:09 +0900906 except OSError as e:
907 print('error: failed to remove existing smart sync override manifest: %s' %
908 e, file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700909
Mike Frysingerc99322a2021-05-04 15:32:43 -0400910 err_event = multiprocessing.Event()
Mike Frysinger5a033082019-09-23 19:21:20 -0400911
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700912 rp = self.manifest.repoProject
913 rp.PreSync()
Mike Frysinger23c900f2020-02-29 02:52:44 -0500914 cb = rp.CurrentBranch
915 if cb:
916 base = rp.GetBranch(cb).merge
917 if not base or not base.startswith('refs/heads/'):
918 print('warning: repo is not tracking a remote branch, so it will not '
Mike Frysinger58ac1672020-03-14 14:35:26 -0400919 'receive updates; run `repo init --repo-rev=stable` to fix.',
Mike Frysinger23c900f2020-02-29 02:52:44 -0500920 file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700921
922 mp = self.manifest.manifestProject
923 mp.PreSync()
924
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800925 if opt.repo_upgraded:
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -0700926 _PostRepoUpgrade(self.manifest, quiet=opt.quiet)
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800927
Fredrik de Grootcc960972019-11-22 09:04:31 +0100928 if not opt.mp_update:
929 print('Skipping update of local manifest project.')
930 else:
931 self._UpdateManifestProject(opt, mp, manifest_name)
Simran Basib9a1b732015-08-20 12:19:28 -0700932
Raman Tennetifeb28912021-05-02 19:47:29 -0700933 load_local_manifests = not self.manifest.HasLocalManifests
Raman Tenneti2ae44d72021-03-23 15:12:27 -0700934 if self._UseSuperproject(opt):
Raman Tennetifeb28912021-05-02 19:47:29 -0700935 manifest_name = self._UpdateProjectsRevisionId(opt, args, load_local_manifests)
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800936
Simran Basib9a1b732015-08-20 12:19:28 -0700937 if self.gitc_manifest:
938 gitc_manifest_projects = self.GetProjects(args,
Simran Basib9a1b732015-08-20 12:19:28 -0700939 missing_ok=True)
940 gitc_projects = []
941 opened_projects = []
942 for project in gitc_manifest_projects:
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700943 if project.relpath in self.gitc_manifest.paths and \
944 self.gitc_manifest.paths[project.relpath].old_revision:
945 opened_projects.append(project.relpath)
Simran Basib9a1b732015-08-20 12:19:28 -0700946 else:
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700947 gitc_projects.append(project.relpath)
Simran Basib9a1b732015-08-20 12:19:28 -0700948
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700949 if not args:
950 gitc_projects = None
951
952 if gitc_projects != [] and not opt.local_only:
Simran Basib9a1b732015-08-20 12:19:28 -0700953 print('Updating GITC client: %s' % self.gitc_manifest.gitc_client_name)
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700954 manifest = GitcManifest(self.repodir, self.gitc_manifest.gitc_client_name)
955 if manifest_name:
956 manifest.Override(manifest_name)
957 else:
958 manifest.Override(self.manifest.manifestFile)
959 gitc_utils.generate_gitc_manifest(self.gitc_manifest,
960 manifest,
Simran Basib9a1b732015-08-20 12:19:28 -0700961 gitc_projects)
962 print('GITC client successfully synced.')
963
964 # The opened projects need to be synced as normal, therefore we
965 # generate a new args list to represent the opened projects.
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700966 # TODO: make this more reliable -- if there's a project name/path overlap,
967 # this may choose the wrong project.
David Pursehouse3bcd3052017-07-10 22:42:22 +0900968 args = [os.path.relpath(self.manifest.paths[path].worktree, os.getcwd())
969 for path in opened_projects]
Simran Basib9a1b732015-08-20 12:19:28 -0700970 if not args:
971 return
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800972 all_projects = self.GetProjects(args,
973 missing_ok=True,
974 submodules_ok=opt.fetch_submodules)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700975
Mike Frysinger5a033082019-09-23 19:21:20 -0400976 err_network_sync = False
977 err_update_projects = False
Mike Frysinger5a033082019-09-23 19:21:20 -0400978
Dave Borowitz67700e92012-10-23 15:00:54 -0700979 self._fetch_times = _FetchTimes(self.manifest)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700980 if not opt.local_only:
Mike Frysingerb4429432021-05-05 20:03:26 -0400981 self._FetchMain(opt, args, all_projects, err_event, manifest_name,
982 load_local_manifests)
Mike Frysinger5a033082019-09-23 19:21:20 -0400983
984 # If we saw an error, exit with code 1 so that other scripts can check.
Mike Frysingerbe24a542021-02-23 03:24:12 -0500985 if err_event.is_set():
Mike Frysinger5a033082019-09-23 19:21:20 -0400986 err_network_sync = True
987 if opt.fail_fast:
988 print('\nerror: Exited sync due to fetch errors.\n'
989 'Local checkouts *not* updated. Resolve network issues & '
990 'retry.\n'
991 '`repo sync -l` will update some local checkouts.',
992 file=sys.stderr)
993 sys.exit(1)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800994
Julien Campergue335f5ef2013-10-16 11:02:35 +0200995 if self.manifest.IsMirror or self.manifest.IsArchive:
Shawn O. Pearcecd1d7ff2009-06-04 16:15:53 -0700996 # bail out now, we have no working tree
997 return
998
Oleksii Okolielovd3c0f592018-12-17 19:23:44 -0500999 if self.UpdateProjectList(opt):
Mike Frysinger5a033082019-09-23 19:21:20 -04001000 err_event.set()
1001 err_update_projects = True
1002 if opt.fail_fast:
1003 print('\nerror: Local checkouts *not* updated.', file=sys.stderr)
1004 sys.exit(1)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -07001005
Mike Frysinger14208f42021-05-04 15:31:51 -04001006 err_update_linkfiles = not self.UpdateCopyLinkfileList()
1007 if err_update_linkfiles:
jiajia tanga590e642021-04-25 20:02:02 +08001008 err_event.set()
jiajia tanga590e642021-04-25 20:02:02 +08001009 if opt.fail_fast:
1010 print('\nerror: Local update copyfile or linkfile failed.', file=sys.stderr)
1011 sys.exit(1)
1012
Mike Frysinger5a033082019-09-23 19:21:20 -04001013 err_results = []
Mike Frysingerebf04a42021-02-23 20:48:04 -05001014 # NB: We don't exit here because this is the last step.
1015 err_checkout = not self._Checkout(all_projects, opt, err_results)
1016 if err_checkout:
1017 err_event.set()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001018
Doug Anderson2b8db3c2010-11-01 15:08:06 -07001019 # If there's a notice that's supposed to print at the end of the sync, print
1020 # it now...
1021 if self.manifest.notice:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001022 print(self.manifest.notice)
Doug Anderson2b8db3c2010-11-01 15:08:06 -07001023
Mike Frysinger5a033082019-09-23 19:21:20 -04001024 # If we saw an error, exit with code 1 so that other scripts can check.
Mike Frysingerbe24a542021-02-23 03:24:12 -05001025 if err_event.is_set():
Mike Frysinger5a033082019-09-23 19:21:20 -04001026 print('\nerror: Unable to fully sync the tree.', file=sys.stderr)
1027 if err_network_sync:
1028 print('error: Downloading network changes failed.', file=sys.stderr)
1029 if err_update_projects:
1030 print('error: Updating local project lists failed.', file=sys.stderr)
jiajia tanga590e642021-04-25 20:02:02 +08001031 if err_update_linkfiles:
1032 print('error: Updating copyfiles or linkfiles failed.', file=sys.stderr)
Mike Frysinger5a033082019-09-23 19:21:20 -04001033 if err_checkout:
1034 print('error: Checking out local projects failed.', file=sys.stderr)
1035 if err_results:
1036 print('Failing repos:\n%s' % '\n'.join(err_results), file=sys.stderr)
1037 print('Try re-running with "-j1 --fail-fast" to exit at the first error.',
1038 file=sys.stderr)
1039 sys.exit(1)
1040
Mike Frysingere19d9e12020-02-12 11:23:32 -05001041 if not opt.quiet:
1042 print('repo sync has finished successfully.')
1043
David Pursehouse819827a2020-02-12 15:20:19 +09001044
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -07001045def _PostRepoUpgrade(manifest, quiet=False):
Conley Owens094cdbe2014-01-30 15:09:59 -08001046 wrapper = Wrapper()
Conley Owensc9129d92012-10-01 16:12:28 -07001047 if wrapper.NeedSetupGnuPG():
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -07001048 wrapper.SetupGnuPG(quiet)
Conley Owensf2fe2d92014-01-29 13:53:43 -08001049 for project in manifest.projects:
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001050 if project.Exists:
1051 project.PostRepoUpgrade()
1052
David Pursehouse819827a2020-02-12 15:20:19 +09001053
Mike Frysingerc58ec4d2020-02-17 14:36:08 -05001054def _PostRepoFetch(rp, repo_verify=True, verbose=False):
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001055 if rp.HasChanges:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001056 print('info: A new version of repo is available', file=sys.stderr)
Mike Frysinger347f9ed2021-03-15 14:58:52 -04001057 wrapper = Wrapper()
1058 try:
1059 rev = rp.bare_git.describe(rp.GetRevisionId())
1060 except GitError:
1061 rev = None
1062 _, new_rev = wrapper.check_repo_rev(rp.gitdir, rev, repo_verify=repo_verify)
1063 # See if we're held back due to missing signed tag.
1064 current_revid = rp.bare_git.rev_parse('HEAD')
1065 new_revid = rp.bare_git.rev_parse('--verify', new_rev)
1066 if current_revid != new_revid:
1067 # We want to switch to the new rev, but also not trash any uncommitted
1068 # changes. This helps with local testing/hacking.
1069 # If a local change has been made, we will throw that away.
1070 # We also have to make sure this will switch to an older commit if that's
1071 # the latest tag in order to support release rollback.
1072 try:
1073 rp.work_git.reset('--keep', new_rev)
1074 except GitError as e:
1075 sys.exit(str(e))
Sarah Owenscecd1d82012-11-01 22:59:27 -07001076 print('info: Restarting repo with latest version', file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001077 raise RepoChangedException(['--repo-upgraded'])
1078 else:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001079 print('warning: Skipped upgrade to unverified version', file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001080 else:
1081 if verbose:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001082 print('repo version %s is current' % rp.work_git.describe(HEAD),
1083 file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001084
David Pursehouse819827a2020-02-12 15:20:19 +09001085
Dave Borowitz67700e92012-10-23 15:00:54 -07001086class _FetchTimes(object):
Dave Borowitzd9478582012-10-23 16:35:39 -07001087 _ALPHA = 0.5
1088
Dave Borowitz67700e92012-10-23 15:00:54 -07001089 def __init__(self, manifest):
Anthony King85b24ac2014-05-06 15:57:48 +01001090 self._path = os.path.join(manifest.repodir, '.repo_fetchtimes.json')
Dave Borowitz67700e92012-10-23 15:00:54 -07001091 self._times = None
Dave Borowitzd9478582012-10-23 16:35:39 -07001092 self._seen = set()
Dave Borowitz67700e92012-10-23 15:00:54 -07001093
1094 def Get(self, project):
1095 self._Load()
1096 return self._times.get(project.name, _ONE_DAY_S)
1097
1098 def Set(self, project, t):
Dave Borowitzd9478582012-10-23 16:35:39 -07001099 self._Load()
1100 name = project.name
1101 old = self._times.get(name, t)
1102 self._seen.add(name)
1103 a = self._ALPHA
David Pursehouse54a4e602020-02-12 14:31:05 +09001104 self._times[name] = (a * t) + ((1 - a) * old)
Dave Borowitz67700e92012-10-23 15:00:54 -07001105
1106 def _Load(self):
1107 if self._times is None:
1108 try:
Mike Frysinger3164d402019-11-11 05:40:22 -05001109 with open(self._path) as f:
Anthony King85b24ac2014-05-06 15:57:48 +01001110 self._times = json.load(f)
Anthony King85b24ac2014-05-06 15:57:48 +01001111 except (IOError, ValueError):
1112 try:
Renaud Paquay010fed72016-11-11 14:25:29 -08001113 platform_utils.remove(self._path)
Anthony King85b24ac2014-05-06 15:57:48 +01001114 except OSError:
1115 pass
1116 self._times = {}
Dave Borowitz67700e92012-10-23 15:00:54 -07001117
1118 def Save(self):
1119 if self._times is None:
1120 return
Dave Borowitzd9478582012-10-23 16:35:39 -07001121
1122 to_delete = []
1123 for name in self._times:
1124 if name not in self._seen:
1125 to_delete.append(name)
1126 for name in to_delete:
1127 del self._times[name]
1128
Dave Borowitz67700e92012-10-23 15:00:54 -07001129 try:
Mike Frysinger3164d402019-11-11 05:40:22 -05001130 with open(self._path, 'w') as f:
Anthony King85b24ac2014-05-06 15:57:48 +01001131 json.dump(self._times, f, indent=2)
Anthony King85b24ac2014-05-06 15:57:48 +01001132 except (IOError, TypeError):
1133 try:
Renaud Paquay010fed72016-11-11 14:25:29 -08001134 platform_utils.remove(self._path)
Anthony King85b24ac2014-05-06 15:57:48 +01001135 except OSError:
1136 pass
Dan Willemsen0745bb22015-08-17 13:41:45 -07001137
1138# This is a replacement for xmlrpc.client.Transport using urllib2
1139# and supporting persistent-http[s]. It cannot change hosts from
1140# request to request like the normal transport, the real url
1141# is passed during initialization.
David Pursehouse819827a2020-02-12 15:20:19 +09001142
1143
Dan Willemsen0745bb22015-08-17 13:41:45 -07001144class PersistentTransport(xmlrpc.client.Transport):
1145 def __init__(self, orig_host):
1146 self.orig_host = orig_host
1147
1148 def request(self, host, handler, request_body, verbose=False):
1149 with GetUrlCookieFile(self.orig_host, not verbose) as (cookiefile, proxy):
1150 # Python doesn't understand cookies with the #HttpOnly_ prefix
1151 # Since we're only using them for HTTP, copy the file temporarily,
1152 # stripping those prefixes away.
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001153 if cookiefile:
Collin Fijalkoviche1191b32020-02-18 10:57:32 -08001154 tmpcookiefile = tempfile.NamedTemporaryFile(mode='w')
David Pursehouse4c5f74e2015-10-02 11:10:10 +09001155 tmpcookiefile.write("# HTTP Cookie File")
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001156 try:
1157 with open(cookiefile) as f:
1158 for line in f:
1159 if line.startswith("#HttpOnly_"):
1160 line = line[len("#HttpOnly_"):]
1161 tmpcookiefile.write(line)
1162 tmpcookiefile.flush()
Dan Willemsen0745bb22015-08-17 13:41:45 -07001163
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001164 cookiejar = cookielib.MozillaCookieJar(tmpcookiefile.name)
David Pursehouseb1ad2192015-09-30 10:35:43 +09001165 try:
1166 cookiejar.load()
1167 except cookielib.LoadError:
1168 cookiejar = cookielib.CookieJar()
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001169 finally:
1170 tmpcookiefile.close()
1171 else:
1172 cookiejar = cookielib.CookieJar()
Dan Willemsen0745bb22015-08-17 13:41:45 -07001173
1174 proxyhandler = urllib.request.ProxyHandler
1175 if proxy:
1176 proxyhandler = urllib.request.ProxyHandler({
1177 "http": proxy,
David Pursehouse54a4e602020-02-12 14:31:05 +09001178 "https": proxy})
Dan Willemsen0745bb22015-08-17 13:41:45 -07001179
1180 opener = urllib.request.build_opener(
1181 urllib.request.HTTPCookieProcessor(cookiejar),
1182 proxyhandler)
1183
1184 url = urllib.parse.urljoin(self.orig_host, handler)
1185 parse_results = urllib.parse.urlparse(url)
1186
1187 scheme = parse_results.scheme
1188 if scheme == 'persistent-http':
1189 scheme = 'http'
1190 if scheme == 'persistent-https':
1191 # If we're proxying through persistent-https, use http. The
1192 # proxy itself will do the https.
1193 if proxy:
1194 scheme = 'http'
1195 else:
1196 scheme = 'https'
1197
1198 # Parse out any authentication information using the base class
1199 host, extra_headers, _ = self.get_host_info(parse_results.netloc)
1200
1201 url = urllib.parse.urlunparse((
1202 scheme,
1203 host,
1204 parse_results.path,
1205 parse_results.params,
1206 parse_results.query,
1207 parse_results.fragment))
1208
1209 request = urllib.request.Request(url, request_body)
1210 if extra_headers is not None:
1211 for (name, header) in extra_headers:
1212 request.add_header(name, header)
1213 request.add_header('Content-Type', 'text/xml')
1214 try:
1215 response = opener.open(request)
1216 except urllib.error.HTTPError as e:
1217 if e.code == 501:
1218 # We may have been redirected through a login process
1219 # but our POST turned into a GET. Retry.
1220 response = opener.open(request)
1221 else:
1222 raise
1223
1224 p, u = xmlrpc.client.getparser()
1225 while 1:
1226 data = response.read(1024)
1227 if not data:
1228 break
1229 p.feed(data)
1230 p.close()
1231 return u.close()
1232
1233 def close(self):
1234 pass