blob: fafc1e5c732c152ab3b62fbf6c6e4fe1f6b4b27d [file] [log] [blame]
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001#
2# Copyright (C) 2008 The Android Open Source Project
3#
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8# http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
15
Sarah Owenscecd1d82012-11-01 22:59:27 -070016from __future__ import print_function
Anthony King85b24ac2014-05-06 15:57:48 +010017import json
David Pursehouse86d973d2012-08-24 10:21:02 +090018import netrc
Shawn O. Pearce2a1ccb22009-04-10 16:51:53 -070019from optparse import SUPPRESS_HELP
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070020import os
21import re
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -070022import socket
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070023import subprocess
24import sys
Dan Willemsen0745bb22015-08-17 13:41:45 -070025import tempfile
Shawn O. Pearcef6906872009-04-18 10:49:00 -070026import time
David Pursehouse59bbb582013-05-17 10:49:33 +090027
28from pyversion import is_python3
29if is_python3():
Dan Willemsen0745bb22015-08-17 13:41:45 -070030 import http.cookiejar as cookielib
31 import urllib.error
Chirayu Desai217ea7d2013-03-01 19:14:38 +053032 import urllib.parse
Dan Willemsen0745bb22015-08-17 13:41:45 -070033 import urllib.request
David Pursehouse59bbb582013-05-17 10:49:33 +090034 import xmlrpc.client
35else:
Dan Willemsen0745bb22015-08-17 13:41:45 -070036 import cookielib
Chirayu Desai217ea7d2013-03-01 19:14:38 +053037 import imp
Dan Willemsen0745bb22015-08-17 13:41:45 -070038 import urllib2
Chirayu Desai217ea7d2013-03-01 19:14:38 +053039 import urlparse
David Pursehouse59bbb582013-05-17 10:49:33 +090040 import xmlrpclib
Chirayu Desai217ea7d2013-03-01 19:14:38 +053041 urllib = imp.new_module('urllib')
Dan Willemsen0745bb22015-08-17 13:41:45 -070042 urllib.error = urllib2
Chirayu Desaidb2ad9d2013-06-11 13:42:25 +053043 urllib.parse = urlparse
Dan Willemsen0745bb22015-08-17 13:41:45 -070044 urllib.request = urllib2
Chirayu Desai217ea7d2013-03-01 19:14:38 +053045 xmlrpc = imp.new_module('xmlrpc')
46 xmlrpc.client = xmlrpclib
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070047
Roy Lee18afd7f2010-05-09 04:32:08 +080048try:
49 import threading as _threading
50except ImportError:
51 import dummy_threading as _threading
52
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -070053try:
54 import resource
55 def _rlimit_nofile():
56 return resource.getrlimit(resource.RLIMIT_NOFILE)
57except ImportError:
58 def _rlimit_nofile():
59 return (256, 256)
60
Dave Borowitz18857212012-10-23 17:02:59 -070061try:
62 import multiprocessing
63except ImportError:
64 multiprocessing = None
65
David Rileye0684ad2017-04-05 00:02:59 -070066import event_log
Dave Borowitze2152672012-10-31 12:24:38 -070067from git_command import GIT, git_require
David Pursehouseba7bc732015-08-20 16:55:42 +090068from git_config import GetUrlCookieFile
David Pursehoused94aaef2012-09-07 09:52:04 +090069from git_refs import R_HEADS, HEAD
Simran Basibdb52712015-08-10 13:23:23 -070070import gitc_utils
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -070071from project import Project
72from project import RemoteSpec
Shawn O. Pearcec95583b2009-03-03 17:47:06 -080073from command import Command, MirrorSafeCommand
Torne (Richard Coles)7bdbde72012-12-05 10:58:06 +000074from error import RepoChangedException, GitError, ManifestParseError
Renaud Paquaya65adf72016-11-03 10:37:53 -070075import platform_utils
Shawn O. Pearce350cde42009-04-16 11:21:18 -070076from project import SyncBuffer
Shawn O. Pearce68194f42009-04-10 16:48:52 -070077from progress import Progress
Conley Owens094cdbe2014-01-30 15:09:59 -080078from wrapper import Wrapper
Dan Willemsen5ea32d12015-09-08 13:27:20 -070079from manifest_xml import GitcManifest
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070080
Dave Borowitz67700e92012-10-23 15:00:54 -070081_ONE_DAY_S = 24 * 60 * 60
82
Doug Andersonfc06ced2011-03-16 15:49:18 -070083class _FetchError(Exception):
84 """Internal error thrown in _FetchHelper() when we don't want stack trace."""
85 pass
86
Shawn O. Pearcec95583b2009-03-03 17:47:06 -080087class Sync(Command, MirrorSafeCommand):
Roy Lee18afd7f2010-05-09 04:32:08 +080088 jobs = 1
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070089 common = True
90 helpSummary = "Update working tree to the latest revision"
91 helpUsage = """
92%prog [<project>...]
93"""
94 helpDescription = """
95The '%prog' command synchronizes local project directories
96with the remote repositories specified in the manifest. If a local
97project does not yet exist, it will clone a new local directory from
98the remote repository and set up tracking branches as specified in
99the manifest. If the local project already exists, '%prog'
100will update the remote branches and rebase any new local changes
101on top of the new remote changes.
102
103'%prog' will synchronize all projects listed at the command
104line. Projects can be specified either by name, or by a relative
105or absolute path to the project's local directory. If no projects
106are specified, '%prog' will synchronize all projects listed in
107the manifest.
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700108
109The -d/--detach option can be used to switch specified projects
110back to the manifest revision. This option is especially helpful
111if the project is currently on a topic branch, but the manifest
112revision is temporarily needed.
Shawn O. Pearceeb7af872009-04-21 08:02:04 -0700113
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700114The -s/--smart-sync option can be used to sync to a known good
115build as specified by the manifest-server element in the current
Victor Boivie08c880d2011-04-19 10:32:52 +0200116manifest. The -t/--smart-tag option is similar and allows you to
117specify a custom tag/label.
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700118
David Pursehousecf76b1b2012-09-14 10:31:42 +0900119The -u/--manifest-server-username and -p/--manifest-server-password
120options can be used to specify a username and password to authenticate
121with the manifest server when using the -s or -t option.
122
123If -u and -p are not specified when using the -s or -t option, '%prog'
124will attempt to read authentication credentials for the manifest server
125from the user's .netrc file.
126
127'%prog' will not use authentication credentials from -u/-p or .netrc
128if the manifest server specified in the manifest file already includes
129credentials.
130
Andrei Warkentin5df6de02010-07-02 17:58:31 -0500131The -f/--force-broken option can be used to proceed with syncing
132other projects if a project sync fails.
133
Kevin Degiabaa7f32014-11-12 11:27:45 -0700134The --force-sync option can be used to overwrite existing git
135directories if they have previously been linked to a different
136object direcotry. WARNING: This may cause data to be lost since
137refs may be removed when overwriting.
138
Oleksii Okolielovd3c0f592018-12-17 19:23:44 -0500139The --force-remove-dirty option can be used to remove previously used
140projects with uncommitted changes. WARNING: This may cause data to be
141lost since uncommitted changes may be removed with projects that no longer
142exist in the manifest.
143
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -0700144The --no-clone-bundle option disables any attempt to use
145$URL/clone.bundle to bootstrap a new Git repository from a
146resumeable bundle file on a content delivery network. This
147may be necessary if there are problems with the local Python
148HTTP client or proxy configuration, but the Git binary works.
149
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800150The --fetch-submodules option enables fetching Git submodules
151of a project from server.
152
David Pursehousef2fad612015-01-29 14:36:28 +0900153The -c/--current-branch option can be used to only fetch objects that
154are on the branch specified by a project's revision.
155
David Pursehouseb1553542014-09-04 21:28:09 +0900156The --optimized-fetch option can be used to only fetch projects that
157are fixed to a sha1 revision if the sha1 revision does not already
158exist locally.
159
David Pursehouse74cfd272015-10-14 10:50:15 +0900160The --prune option can be used to remove any refs that no longer
161exist on the remote.
162
Mike Frysingerb8f7bb02018-10-10 01:05:11 -0400163# SSH Connections
Shawn O. Pearceeb7af872009-04-21 08:02:04 -0700164
165If at least one project remote URL uses an SSH connection (ssh://,
166git+ssh://, or user@host:path syntax) repo will automatically
167enable the SSH ControlMaster option when connecting to that host.
168This feature permits other projects in the same '%prog' session to
169reuse the same SSH tunnel, saving connection setup overheads.
170
171To disable this behavior on UNIX platforms, set the GIT_SSH
172environment variable to 'ssh'. For example:
173
174 export GIT_SSH=ssh
175 %prog
176
Mike Frysingerb8f7bb02018-10-10 01:05:11 -0400177# Compatibility
Shawn O. Pearceeb7af872009-04-21 08:02:04 -0700178
179This feature is automatically disabled on Windows, due to the lack
180of UNIX domain socket support.
181
182This feature is not compatible with url.insteadof rewrites in the
183user's ~/.gitconfig. '%prog' is currently not able to perform the
184rewrite early enough to establish the ControlMaster tunnel.
185
186If the remote SSH daemon is Gerrit Code Review, version 2.0.10 or
187later is required to fix a server side protocol bug.
188
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700189"""
190
Nico Sallembien6623b212010-05-11 12:57:01 -0700191 def _Options(self, p, show_smart=True):
Torne (Richard Coles)7bdbde72012-12-05 10:58:06 +0000192 try:
193 self.jobs = self.manifest.default.sync_j
194 except ManifestParseError:
195 self.jobs = 1
Shawn O. Pearce6392c872011-09-22 17:44:31 -0700196
Andrei Warkentin5df6de02010-07-02 17:58:31 -0500197 p.add_option('-f', '--force-broken',
198 dest='force_broken', action='store_true',
199 help="continue sync even if a project fails to sync")
Kevin Degiabaa7f32014-11-12 11:27:45 -0700200 p.add_option('--force-sync',
201 dest='force_sync', action='store_true',
202 help="overwrite an existing git directory if it needs to "
203 "point to a different object directory. WARNING: this "
204 "may cause loss of data")
Oleksii Okolielovd3c0f592018-12-17 19:23:44 -0500205 p.add_option('--force-remove-dirty',
206 dest='force_remove_dirty', action='store_true',
207 help="force remove projects with uncommitted modifications if "
208 "projects no longer exist in the manifest. "
209 "WARNING: this may cause loss of data")
David Pursehouse8f62fb72012-11-14 12:09:38 +0900210 p.add_option('-l', '--local-only',
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700211 dest='local_only', action='store_true',
212 help="only update working tree, don't fetch")
David Pursehouse8f62fb72012-11-14 12:09:38 +0900213 p.add_option('-n', '--network-only',
Shawn O. Pearce96fdcef2009-04-10 16:29:20 -0700214 dest='network_only', action='store_true',
215 help="fetch only, don't update working tree")
David Pursehouse8f62fb72012-11-14 12:09:38 +0900216 p.add_option('-d', '--detach',
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700217 dest='detach_head', action='store_true',
218 help='detach projects back to manifest revision')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900219 p.add_option('-c', '--current-branch',
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -0700220 dest='current_branch_only', action='store_true',
221 help='fetch only current branch from server')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900222 p.add_option('-q', '--quiet',
Shawn O. Pearce16614f82010-10-29 12:05:43 -0700223 dest='quiet', action='store_true',
224 help='be more quiet')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900225 p.add_option('-j', '--jobs',
Roy Lee18afd7f2010-05-09 04:32:08 +0800226 dest='jobs', action='store', type='int',
Shawn O. Pearce6392c872011-09-22 17:44:31 -0700227 help="projects to fetch simultaneously (default %d)" % self.jobs)
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500228 p.add_option('-m', '--manifest-name',
229 dest='manifest_name',
230 help='temporary manifest to use for this sync', metavar='NAME.xml')
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -0700231 p.add_option('--no-clone-bundle',
232 dest='no_clone_bundle', action='store_true',
233 help='disable use of /clone.bundle on HTTP/HTTPS')
Conley Owens8d070cf2012-11-06 13:14:31 -0800234 p.add_option('-u', '--manifest-server-username', action='store',
235 dest='manifest_server_username',
236 help='username to authenticate with the manifest server')
237 p.add_option('-p', '--manifest-server-password', action='store',
238 dest='manifest_server_password',
239 help='password to authenticate with the manifest server')
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800240 p.add_option('--fetch-submodules',
241 dest='fetch_submodules', action='store_true',
242 help='fetch submodules from server')
Mitchel Humpherys597868b2012-10-29 10:18:34 -0700243 p.add_option('--no-tags',
244 dest='no_tags', action='store_true',
245 help="don't fetch tags")
David Pursehouseb1553542014-09-04 21:28:09 +0900246 p.add_option('--optimized-fetch',
247 dest='optimized_fetch', action='store_true',
248 help='only fetch projects fixed to sha1 if revision does not exist locally')
David Pursehouse74cfd272015-10-14 10:50:15 +0900249 p.add_option('--prune', dest='prune', action='store_true',
250 help='delete refs that no longer exist on the remote')
Nico Sallembien6623b212010-05-11 12:57:01 -0700251 if show_smart:
252 p.add_option('-s', '--smart-sync',
253 dest='smart_sync', action='store_true',
David Pursehouse79fba682016-04-13 18:03:00 +0900254 help='smart sync using manifest from the latest known good build')
Victor Boivie08c880d2011-04-19 10:32:52 +0200255 p.add_option('-t', '--smart-tag',
256 dest='smart_tag', action='store',
257 help='smart sync using manifest from a known tag')
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700258
Shawn O. Pearcefd89b672009-04-18 11:28:57 -0700259 g = p.add_option_group('repo Version options')
260 g.add_option('--no-repo-verify',
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700261 dest='no_repo_verify', action='store_true',
262 help='do not verify repo source code')
Shawn O. Pearcefd89b672009-04-18 11:28:57 -0700263 g.add_option('--repo-upgraded',
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800264 dest='repo_upgraded', action='store_true',
Shawn O. Pearce2a1ccb22009-04-10 16:51:53 -0700265 help=SUPPRESS_HELP)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700266
Andrew Wheeler7f1ccfb2016-06-17 16:51:07 -0500267 def _FetchProjectList(self, opt, projects, sem, *args, **kwargs):
David Pursehousec1b86a22012-11-14 11:36:51 +0900268 """Main function of the fetch threads when jobs are > 1.
Doug Andersonfc06ced2011-03-16 15:49:18 -0700269
David James8d201162013-10-11 17:03:19 -0700270 Delegates most of the work to _FetchHelper.
271
272 Args:
273 opt: Program options returned from optparse. See _Options().
274 projects: Projects to fetch.
Andrew Wheeler7f1ccfb2016-06-17 16:51:07 -0500275 sem: We'll release() this semaphore when we exit so that another thread
276 can be started up.
David James89ece422014-01-09 18:51:58 -0800277 *args, **kwargs: Remaining arguments to pass to _FetchHelper. See the
David James8d201162013-10-11 17:03:19 -0700278 _FetchHelper docstring for details.
279 """
Andrew Wheeler7f1ccfb2016-06-17 16:51:07 -0500280 try:
281 for project in projects:
282 success = self._FetchHelper(opt, project, *args, **kwargs)
283 if not success and not opt.force_broken:
284 break
285 finally:
286 sem.release()
David James8d201162013-10-11 17:03:19 -0700287
Andrew Wheeler7f1ccfb2016-06-17 16:51:07 -0500288 def _FetchHelper(self, opt, project, lock, fetched, pm, err_event):
David James8d201162013-10-11 17:03:19 -0700289 """Fetch git objects for a single project.
290
David Pursehousec1b86a22012-11-14 11:36:51 +0900291 Args:
292 opt: Program options returned from optparse. See _Options().
293 project: Project object for the project to fetch.
294 lock: Lock for accessing objects that are shared amongst multiple
295 _FetchHelper() threads.
296 fetched: set object that we will add project.gitdir to when we're done
297 (with our lock held).
298 pm: Instance of a Project object. We will call pm.update() (with our
299 lock held).
David Pursehousec1b86a22012-11-14 11:36:51 +0900300 err_event: We'll set this event in the case of an error (after printing
301 out info about the error).
David James8d201162013-10-11 17:03:19 -0700302
303 Returns:
304 Whether the fetch was successful.
David Pursehousec1b86a22012-11-14 11:36:51 +0900305 """
306 # We'll set to true once we've locked the lock.
307 did_lock = False
Doug Andersonfc06ced2011-03-16 15:49:18 -0700308
Chirayu Desaifef4ae72013-04-12 14:54:32 +0530309 if not opt.quiet:
310 print('Fetching project %s' % project.name)
311
David Pursehousec1b86a22012-11-14 11:36:51 +0900312 # Encapsulate everything in a try/except/finally so that:
313 # - We always set err_event in the case of an exception.
314 # - We always make sure we call sem.release().
315 # - We always make sure we unlock the lock if we locked it.
David Rileye0684ad2017-04-05 00:02:59 -0700316 start = time.time()
317 success = False
David Pursehousec1b86a22012-11-14 11:36:51 +0900318 try:
Doug Andersonfc06ced2011-03-16 15:49:18 -0700319 try:
David Pursehousec1b86a22012-11-14 11:36:51 +0900320 success = project.Sync_NetworkHalf(
321 quiet=opt.quiet,
322 current_branch_only=opt.current_branch_only,
Kevin Degiabaa7f32014-11-12 11:27:45 -0700323 force_sync=opt.force_sync,
Mitchel Humpherys597868b2012-10-29 10:18:34 -0700324 clone_bundle=not opt.no_clone_bundle,
David Pursehouseb1553542014-09-04 21:28:09 +0900325 no_tags=opt.no_tags, archive=self.manifest.IsArchive,
David Pursehouse74cfd272015-10-14 10:50:15 +0900326 optimized_fetch=opt.optimized_fetch,
327 prune=opt.prune)
David Pursehousec1b86a22012-11-14 11:36:51 +0900328 self._fetch_times.Set(project, time.time() - start)
Doug Andersonfc06ced2011-03-16 15:49:18 -0700329
David Pursehousec1b86a22012-11-14 11:36:51 +0900330 # Lock around all the rest of the code, since printing, updating a set
331 # and Progress.update() are not thread safe.
332 lock.acquire()
333 did_lock = True
Doug Andersonfc06ced2011-03-16 15:49:18 -0700334
David Pursehousec1b86a22012-11-14 11:36:51 +0900335 if not success:
Hu Xiuyune9becc02015-11-25 15:52:26 +0800336 err_event.set()
Marc Herbertffb4b892017-04-04 22:03:53 -0700337 print('error: Cannot fetch %s from %s'
338 % (project.name, project.remote.url),
339 file=sys.stderr)
David Pursehousec1b86a22012-11-14 11:36:51 +0900340 if opt.force_broken:
341 print('warn: --force-broken, continuing to sync',
342 file=sys.stderr)
343 else:
344 raise _FetchError()
Roy Lee18afd7f2010-05-09 04:32:08 +0800345
David Pursehousec1b86a22012-11-14 11:36:51 +0900346 fetched.add(project.gitdir)
347 pm.update()
348 except _FetchError:
Hu Xiuyune9becc02015-11-25 15:52:26 +0800349 pass
Dan Sandlerc5cd4332015-07-31 09:37:53 -0400350 except Exception as e:
351 print('error: Cannot fetch %s (%s: %s)' \
352 % (project.name, type(e).__name__, str(e)), file=sys.stderr)
David Pursehousec1b86a22012-11-14 11:36:51 +0900353 err_event.set()
354 raise
355 finally:
356 if did_lock:
357 lock.release()
David Rileye0684ad2017-04-05 00:02:59 -0700358 finish = time.time()
359 self.event_log.AddSync(project, event_log.TASK_SYNC_NETWORK,
360 start, finish, success)
Roy Lee18afd7f2010-05-09 04:32:08 +0800361
David James8d201162013-10-11 17:03:19 -0700362 return success
363
Shawn O. Pearce16614f82010-10-29 12:05:43 -0700364 def _Fetch(self, projects, opt):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700365 fetched = set()
David James89ece422014-01-09 18:51:58 -0800366 lock = _threading.Lock()
Tim Schumacher913327f2017-06-05 15:01:41 +0200367 pm = Progress('Fetching projects', len(projects),
Tim Schumacher7be072e2017-06-28 18:29:23 +0200368 print_newline=not(opt.quiet),
369 always_print_percentage=opt.quiet)
Roy Lee18afd7f2010-05-09 04:32:08 +0800370
David James89ece422014-01-09 18:51:58 -0800371 objdir_project_map = dict()
372 for project in projects:
373 objdir_project_map.setdefault(project.objdir, []).append(project)
David James8d201162013-10-11 17:03:19 -0700374
David James89ece422014-01-09 18:51:58 -0800375 threads = set()
376 sem = _threading.Semaphore(self.jobs)
377 err_event = _threading.Event()
378 for project_list in objdir_project_map.values():
379 # Check for any errors before running any more tasks.
380 # ...we'll let existing threads finish, though.
381 if err_event.isSet() and not opt.force_broken:
382 break
Doug Andersonfc06ced2011-03-16 15:49:18 -0700383
David James89ece422014-01-09 18:51:58 -0800384 sem.acquire()
385 kwargs = dict(opt=opt,
386 projects=project_list,
Andrew Wheeler7f1ccfb2016-06-17 16:51:07 -0500387 sem=sem,
David James89ece422014-01-09 18:51:58 -0800388 lock=lock,
389 fetched=fetched,
390 pm=pm,
David James89ece422014-01-09 18:51:58 -0800391 err_event=err_event)
392 if self.jobs > 1:
David James8d201162013-10-11 17:03:19 -0700393 t = _threading.Thread(target = self._FetchProjectList,
David James89ece422014-01-09 18:51:58 -0800394 kwargs = kwargs)
David 'Digit' Turnere2126652012-09-05 10:35:06 +0200395 # Ensure that Ctrl-C will not freeze the repo process.
396 t.daemon = True
Roy Lee18afd7f2010-05-09 04:32:08 +0800397 threads.add(t)
398 t.start()
David James89ece422014-01-09 18:51:58 -0800399 else:
400 self._FetchProjectList(**kwargs)
Roy Lee18afd7f2010-05-09 04:32:08 +0800401
David James89ece422014-01-09 18:51:58 -0800402 for t in threads:
403 t.join()
Roy Lee18afd7f2010-05-09 04:32:08 +0800404
David James89ece422014-01-09 18:51:58 -0800405 # If we saw an error, exit with code 1 so that other scripts can check.
Nicolas Cornu8419ab22017-06-16 12:09:06 +0200406 if err_event.isSet() and not opt.force_broken:
David James89ece422014-01-09 18:51:58 -0800407 print('\nerror: Exited sync due to fetch errors', file=sys.stderr)
408 sys.exit(1)
Doug Andersonfc06ced2011-03-16 15:49:18 -0700409
Shawn O. Pearce68194f42009-04-10 16:48:52 -0700410 pm.end()
Dave Borowitz67700e92012-10-23 15:00:54 -0700411 self._fetch_times.Save()
Dave Borowitz18857212012-10-23 17:02:59 -0700412
Julien Campergue335f5ef2013-10-16 11:02:35 +0200413 if not self.manifest.IsArchive:
414 self._GCProjects(projects)
415
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700416 return fetched
417
Dave Borowitz18857212012-10-23 17:02:59 -0700418 def _GCProjects(self, projects):
Gabe Black2ff30292014-10-09 17:54:35 -0700419 gc_gitdirs = {}
David James8d201162013-10-11 17:03:19 -0700420 for project in projects:
Gabe Black2ff30292014-10-09 17:54:35 -0700421 if len(project.manifest.GetProjectsWithName(project.name)) > 1:
422 print('Shared project %s found, disabling pruning.' % project.name)
423 project.bare_git.config('--replace-all', 'gc.pruneExpire', 'never')
424 gc_gitdirs[project.gitdir] = project.bare_git
David James8d201162013-10-11 17:03:19 -0700425
Dave Borowitze2152672012-10-31 12:24:38 -0700426 has_dash_c = git_require((1, 7, 2))
427 if multiprocessing and has_dash_c:
Dave Borowitz18857212012-10-23 17:02:59 -0700428 cpu_count = multiprocessing.cpu_count()
429 else:
430 cpu_count = 1
431 jobs = min(self.jobs, cpu_count)
432
433 if jobs < 2:
Gabe Black2ff30292014-10-09 17:54:35 -0700434 for bare_git in gc_gitdirs.values():
David James8d201162013-10-11 17:03:19 -0700435 bare_git.gc('--auto')
Dave Borowitz18857212012-10-23 17:02:59 -0700436 return
437
438 config = {'pack.threads': cpu_count / jobs if cpu_count > jobs else 1}
439
440 threads = set()
441 sem = _threading.Semaphore(jobs)
442 err_event = _threading.Event()
443
David James8d201162013-10-11 17:03:19 -0700444 def GC(bare_git):
Dave Borowitz18857212012-10-23 17:02:59 -0700445 try:
446 try:
David James8d201162013-10-11 17:03:19 -0700447 bare_git.gc('--auto', config=config)
Dave Borowitz18857212012-10-23 17:02:59 -0700448 except GitError:
449 err_event.set()
450 except:
451 err_event.set()
452 raise
453 finally:
454 sem.release()
455
Gabe Black2ff30292014-10-09 17:54:35 -0700456 for bare_git in gc_gitdirs.values():
Dave Borowitz18857212012-10-23 17:02:59 -0700457 if err_event.isSet():
458 break
459 sem.acquire()
David James8d201162013-10-11 17:03:19 -0700460 t = _threading.Thread(target=GC, args=(bare_git,))
Dave Borowitz18857212012-10-23 17:02:59 -0700461 t.daemon = True
462 threads.add(t)
463 t.start()
464
465 for t in threads:
466 t.join()
467
468 if err_event.isSet():
Sarah Owenscecd1d82012-11-01 22:59:27 -0700469 print('\nerror: Exited sync due to gc errors', file=sys.stderr)
Dave Borowitz18857212012-10-23 17:02:59 -0700470 sys.exit(1)
471
Tim Kilbourn07669002013-03-08 15:02:49 -0800472 def _ReloadManifest(self, manifest_name=None):
473 if manifest_name:
474 # Override calls _Unload already
475 self.manifest.Override(manifest_name)
476 else:
477 self.manifest._Unload()
478
Dan Willemsen43507912016-09-01 16:26:02 -0700479 def _DeleteProject(self, path):
480 print('Deleting obsolete path %s' % path, file=sys.stderr)
481
482 # Delete the .git directory first, so we're less likely to have a partially
483 # working git repository around. There shouldn't be any git projects here,
484 # so rmtree works.
485 try:
Renaud Paquaya65adf72016-11-03 10:37:53 -0700486 platform_utils.rmtree(os.path.join(path, '.git'))
Renaud Paquaybed8b622018-09-27 10:46:58 -0700487 except OSError as e:
488 print('Failed to remove %s (%s)' % (os.path.join(path, '.git'), str(e)), file=sys.stderr)
Dan Willemsen43507912016-09-01 16:26:02 -0700489 print('error: Failed to delete obsolete path %s' % path, file=sys.stderr)
490 print(' remove manually, then run sync again', file=sys.stderr)
491 return -1
492
493 # Delete everything under the worktree, except for directories that contain
494 # another git project
495 dirs_to_remove = []
496 failed = False
Renaud Paquaybed8b622018-09-27 10:46:58 -0700497 for root, dirs, files in platform_utils.walk(path):
Dan Willemsen43507912016-09-01 16:26:02 -0700498 for f in files:
499 try:
Renaud Paquay010fed72016-11-11 14:25:29 -0800500 platform_utils.remove(os.path.join(root, f))
Renaud Paquaybed8b622018-09-27 10:46:58 -0700501 except OSError as e:
502 print('Failed to remove %s (%s)' % (os.path.join(root, f), str(e)), file=sys.stderr)
Dan Willemsen43507912016-09-01 16:26:02 -0700503 failed = True
504 dirs[:] = [d for d in dirs
505 if not os.path.lexists(os.path.join(root, d, '.git'))]
506 dirs_to_remove += [os.path.join(root, d) for d in dirs
507 if os.path.join(root, d) not in dirs_to_remove]
508 for d in reversed(dirs_to_remove):
Renaud Paquay227ad2e2016-11-01 14:37:13 -0700509 if platform_utils.islink(d):
Dan Willemseneceeb1b2016-09-25 18:24:27 -0700510 try:
Renaud Paquay010fed72016-11-11 14:25:29 -0800511 platform_utils.remove(d)
Renaud Paquaybed8b622018-09-27 10:46:58 -0700512 except OSError as e:
513 print('Failed to remove %s (%s)' % (os.path.join(root, d), str(e)), file=sys.stderr)
Dan Willemseneceeb1b2016-09-25 18:24:27 -0700514 failed = True
Renaud Paquaybed8b622018-09-27 10:46:58 -0700515 elif len(platform_utils.listdir(d)) == 0:
Dan Willemsen43507912016-09-01 16:26:02 -0700516 try:
Renaud Paquaybed8b622018-09-27 10:46:58 -0700517 platform_utils.rmdir(d)
518 except OSError as e:
519 print('Failed to remove %s (%s)' % (os.path.join(root, d), str(e)), file=sys.stderr)
Dan Willemsen43507912016-09-01 16:26:02 -0700520 failed = True
521 continue
522 if failed:
523 print('error: Failed to delete obsolete path %s' % path, file=sys.stderr)
524 print(' remove manually, then run sync again', file=sys.stderr)
525 return -1
526
527 # Try deleting parent dirs if they are empty
528 project_dir = path
529 while project_dir != self.manifest.topdir:
Renaud Paquaybed8b622018-09-27 10:46:58 -0700530 if len(platform_utils.listdir(project_dir)) == 0:
531 platform_utils.rmdir(project_dir)
Dan Willemsen43507912016-09-01 16:26:02 -0700532 else:
533 break
534 project_dir = os.path.dirname(project_dir)
535
536 return 0
537
Oleksii Okolielovd3c0f592018-12-17 19:23:44 -0500538 def UpdateProjectList(self, opt):
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700539 new_project_paths = []
Colin Cross5acde752012-03-28 20:15:45 -0700540 for project in self.GetProjects(None, missing_ok=True):
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700541 if project.relpath:
542 new_project_paths.append(project.relpath)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700543 file_name = 'project.list'
544 file_path = os.path.join(self.manifest.repodir, file_name)
545 old_project_paths = []
546
547 if os.path.exists(file_path):
548 fd = open(file_path, 'r')
549 try:
550 old_project_paths = fd.read().split('\n')
551 finally:
552 fd.close()
553 for path in old_project_paths:
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700554 if not path:
555 continue
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700556 if path not in new_project_paths:
David Pursehouse8a68ff92012-09-24 12:15:13 +0900557 # If the path has already been deleted, we don't need to do it
Dan Willemsen43507912016-09-01 16:26:02 -0700558 gitdir = os.path.join(self.manifest.topdir, path, '.git')
559 if os.path.exists(gitdir):
David Pursehousec1b86a22012-11-14 11:36:51 +0900560 project = Project(
561 manifest = self.manifest,
562 name = path,
563 remote = RemoteSpec('origin'),
David James8d201162013-10-11 17:03:19 -0700564 gitdir = gitdir,
565 objdir = gitdir,
David Pursehousec1b86a22012-11-14 11:36:51 +0900566 worktree = os.path.join(self.manifest.topdir, path),
567 relpath = path,
568 revisionExpr = 'HEAD',
569 revisionId = None,
570 groups = None)
Anthonyf3fdf822009-09-26 13:38:52 -0400571
Oleksii Okolielovd3c0f592018-12-17 19:23:44 -0500572 if project.IsDirty() and opt.force_remove_dirty:
573 print('WARNING: Removing dirty project "%s": uncommitted changes '
574 'erased' % project.relpath, file=sys.stderr)
575 self._DeleteProject(project.worktree)
576 elif project.IsDirty():
David Pursehouse2f9e7e42013-03-05 17:26:46 +0900577 print('error: Cannot remove project "%s": uncommitted changes '
David Pursehousec1b86a22012-11-14 11:36:51 +0900578 'are present' % project.relpath, file=sys.stderr)
579 print(' commit changes, then run sync again',
580 file=sys.stderr)
581 return -1
Dan Willemsen43507912016-09-01 16:26:02 -0700582 elif self._DeleteProject(project.worktree):
583 return -1
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700584
Shawn O. Pearce9fb29ce2009-06-04 20:41:02 -0700585 new_project_paths.sort()
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700586 fd = open(file_path, 'w')
587 try:
588 fd.write('\n'.join(new_project_paths))
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700589 fd.write('\n')
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700590 finally:
591 fd.close()
592 return 0
593
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700594 def Execute(self, opt, args):
Roy Lee18afd7f2010-05-09 04:32:08 +0800595 if opt.jobs:
596 self.jobs = opt.jobs
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -0700597 if self.jobs > 1:
598 soft_limit, _ = _rlimit_nofile()
599 self.jobs = min(self.jobs, (soft_limit - 5) / 3)
600
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700601 if opt.network_only and opt.detach_head:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700602 print('error: cannot combine -n and -d', file=sys.stderr)
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700603 sys.exit(1)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700604 if opt.network_only and opt.local_only:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700605 print('error: cannot combine -n and -l', file=sys.stderr)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700606 sys.exit(1)
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500607 if opt.manifest_name and opt.smart_sync:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700608 print('error: cannot combine -m and -s', file=sys.stderr)
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500609 sys.exit(1)
610 if opt.manifest_name and opt.smart_tag:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700611 print('error: cannot combine -m and -t', file=sys.stderr)
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500612 sys.exit(1)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900613 if opt.manifest_server_username or opt.manifest_server_password:
614 if not (opt.smart_sync or opt.smart_tag):
Sarah Owenscecd1d82012-11-01 22:59:27 -0700615 print('error: -u and -p may only be combined with -s or -t',
616 file=sys.stderr)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900617 sys.exit(1)
618 if None in [opt.manifest_server_username, opt.manifest_server_password]:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700619 print('error: both -u and -p must be given', file=sys.stderr)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900620 sys.exit(1)
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500621
622 if opt.manifest_name:
623 self.manifest.Override(opt.manifest_name)
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700624
Chirayu Desaia892b102013-06-11 14:18:46 +0530625 manifest_name = opt.manifest_name
David Pursehouse59b41742015-05-07 14:36:09 +0900626 smart_sync_manifest_name = "smart_sync_override.xml"
627 smart_sync_manifest_path = os.path.join(
628 self.manifest.manifestProject.worktree, smart_sync_manifest_name)
Chirayu Desaia892b102013-06-11 14:18:46 +0530629
Victor Boivie08c880d2011-04-19 10:32:52 +0200630 if opt.smart_sync or opt.smart_tag:
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700631 if not self.manifest.manifest_server:
David Pursehouse2f9e7e42013-03-05 17:26:46 +0900632 print('error: cannot smart sync: no manifest server defined in '
Sarah Owenscecd1d82012-11-01 22:59:27 -0700633 'manifest', file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700634 sys.exit(1)
David Pursehouse86d973d2012-08-24 10:21:02 +0900635
636 manifest_server = self.manifest.manifest_server
David Pursehousefb99c712013-09-25 11:09:34 +0900637 if not opt.quiet:
638 print('Using manifest server %s' % manifest_server)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900639
David Pursehouse86d973d2012-08-24 10:21:02 +0900640 if not '@' in manifest_server:
David Pursehousecf76b1b2012-09-14 10:31:42 +0900641 username = None
642 password = None
643 if opt.manifest_server_username and opt.manifest_server_password:
644 username = opt.manifest_server_username
645 password = opt.manifest_server_password
David Pursehouse86d973d2012-08-24 10:21:02 +0900646 else:
647 try:
David Pursehousecf76b1b2012-09-14 10:31:42 +0900648 info = netrc.netrc()
649 except IOError:
David Pursehouseba7bc732015-08-20 16:55:42 +0900650 # .netrc file does not exist or could not be opened
651 pass
David Pursehouse86d973d2012-08-24 10:21:02 +0900652 else:
David Pursehousecf76b1b2012-09-14 10:31:42 +0900653 try:
Chirayu Desaidb2ad9d2013-06-11 13:42:25 +0530654 parse_result = urllib.parse.urlparse(manifest_server)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900655 if parse_result.hostname:
David Pursehouseba7bc732015-08-20 16:55:42 +0900656 auth = info.authenticators(parse_result.hostname)
657 if auth:
658 username, _account, password = auth
659 else:
660 print('No credentials found for %s in .netrc'
661 % parse_result.hostname, file=sys.stderr)
Sarah Owensa5be53f2012-09-09 15:37:57 -0700662 except netrc.NetrcParseError as e:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700663 print('Error parsing .netrc file: %s' % e, file=sys.stderr)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900664
665 if (username and password):
666 manifest_server = manifest_server.replace('://', '://%s:%s@' %
667 (username, password),
668 1)
David Pursehouse86d973d2012-08-24 10:21:02 +0900669
Dan Willemsen0745bb22015-08-17 13:41:45 -0700670 transport = PersistentTransport(manifest_server)
671 if manifest_server.startswith('persistent-'):
672 manifest_server = manifest_server[len('persistent-'):]
673
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700674 try:
Dan Willemsen0745bb22015-08-17 13:41:45 -0700675 server = xmlrpc.client.Server(manifest_server, transport=transport)
Victor Boivie08c880d2011-04-19 10:32:52 +0200676 if opt.smart_sync:
677 p = self.manifest.manifestProject
678 b = p.GetBranch(p.CurrentBranch)
679 branch = b.merge
680 if branch.startswith(R_HEADS):
681 branch = branch[len(R_HEADS):]
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700682
Victor Boivie08c880d2011-04-19 10:32:52 +0200683 env = os.environ.copy()
Jeff Davidson5cf16602014-10-02 10:13:38 -0700684 if 'SYNC_TARGET' in env:
685 target = env['SYNC_TARGET']
686 [success, manifest_str] = server.GetApprovedManifest(branch, target)
687 elif 'TARGET_PRODUCT' in env and 'TARGET_BUILD_VARIANT' in env:
Victor Boivie08c880d2011-04-19 10:32:52 +0200688 target = '%s-%s' % (env['TARGET_PRODUCT'],
689 env['TARGET_BUILD_VARIANT'])
690 [success, manifest_str] = server.GetApprovedManifest(branch, target)
691 else:
692 [success, manifest_str] = server.GetApprovedManifest(branch)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700693 else:
Victor Boivie08c880d2011-04-19 10:32:52 +0200694 assert(opt.smart_tag)
695 [success, manifest_str] = server.GetManifest(opt.smart_tag)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700696
697 if success:
David Pursehouse59b41742015-05-07 14:36:09 +0900698 manifest_name = smart_sync_manifest_name
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700699 try:
David Pursehouse59b41742015-05-07 14:36:09 +0900700 f = open(smart_sync_manifest_path, 'w')
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700701 try:
702 f.write(manifest_str)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700703 finally:
704 f.close()
David Pursehouse727cc3e2015-05-07 14:16:49 +0900705 except IOError as e:
706 print('error: cannot write manifest to %s:\n%s'
David Pursehouse59b41742015-05-07 14:36:09 +0900707 % (smart_sync_manifest_path, e),
Sarah Owenscecd1d82012-11-01 22:59:27 -0700708 file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700709 sys.exit(1)
Victor Boivie53a6c5d2013-03-19 12:20:52 +0100710 self._ReloadManifest(manifest_name)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700711 else:
David Pursehouse351fe2c2013-09-25 17:54:26 +0900712 print('error: manifest server RPC call failed: %s' %
713 manifest_str, file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700714 sys.exit(1)
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530715 except (socket.error, IOError, xmlrpc.client.Fault) as e:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700716 print('error: cannot connect to manifest server %s:\n%s'
717 % (self.manifest.manifest_server, e), file=sys.stderr)
David Pursehousebd489c42012-08-23 10:21:26 +0900718 sys.exit(1)
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530719 except xmlrpc.client.ProtocolError as e:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700720 print('error: cannot connect to manifest server %s:\n%d %s'
721 % (self.manifest.manifest_server, e.errcode, e.errmsg),
722 file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700723 sys.exit(1)
David Pursehouse59b41742015-05-07 14:36:09 +0900724 else: # Not smart sync or smart tag mode
725 if os.path.isfile(smart_sync_manifest_path):
726 try:
Renaud Paquay010fed72016-11-11 14:25:29 -0800727 platform_utils.remove(smart_sync_manifest_path)
David Pursehouse59b41742015-05-07 14:36:09 +0900728 except OSError as e:
729 print('error: failed to remove existing smart sync override manifest: %s' %
730 e, file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700731
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700732 rp = self.manifest.repoProject
733 rp.PreSync()
734
735 mp = self.manifest.manifestProject
736 mp.PreSync()
737
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800738 if opt.repo_upgraded:
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -0700739 _PostRepoUpgrade(self.manifest, quiet=opt.quiet)
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800740
Nico Sallembien9bb18162009-12-07 15:38:01 -0800741 if not opt.local_only:
David Rileye0684ad2017-04-05 00:02:59 -0700742 start = time.time()
743 success = mp.Sync_NetworkHalf(quiet=opt.quiet,
744 current_branch_only=opt.current_branch_only,
745 no_tags=opt.no_tags,
746 optimized_fetch=opt.optimized_fetch,
747 submodules=self.manifest.HasSubmodules)
748 finish = time.time()
749 self.event_log.AddSync(mp, event_log.TASK_SYNC_NETWORK,
750 start, finish, success)
Nico Sallembien9bb18162009-12-07 15:38:01 -0800751
752 if mp.HasChanges:
753 syncbuf = SyncBuffer(mp.config)
David Rileye0684ad2017-04-05 00:02:59 -0700754 start = time.time()
Martin Kellye4e94d22017-03-21 16:05:12 -0700755 mp.Sync_LocalHalf(syncbuf, submodules=self.manifest.HasSubmodules)
David Rileye0684ad2017-04-05 00:02:59 -0700756 clean = syncbuf.Finish()
757 self.event_log.AddSync(mp, event_log.TASK_SYNC_LOCAL,
758 start, time.time(), clean)
759 if not clean:
Nico Sallembien9bb18162009-12-07 15:38:01 -0800760 sys.exit(1)
Victor Boivie53a6c5d2013-03-19 12:20:52 +0100761 self._ReloadManifest(manifest_name)
Shawn O. Pearcec4657962011-09-26 09:08:01 -0700762 if opt.jobs is None:
763 self.jobs = self.manifest.default.sync_j
Simran Basib9a1b732015-08-20 12:19:28 -0700764
Simran Basib9a1b732015-08-20 12:19:28 -0700765 if self.gitc_manifest:
766 gitc_manifest_projects = self.GetProjects(args,
Simran Basib9a1b732015-08-20 12:19:28 -0700767 missing_ok=True)
768 gitc_projects = []
769 opened_projects = []
770 for project in gitc_manifest_projects:
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700771 if project.relpath in self.gitc_manifest.paths and \
772 self.gitc_manifest.paths[project.relpath].old_revision:
773 opened_projects.append(project.relpath)
Simran Basib9a1b732015-08-20 12:19:28 -0700774 else:
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700775 gitc_projects.append(project.relpath)
Simran Basib9a1b732015-08-20 12:19:28 -0700776
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700777 if not args:
778 gitc_projects = None
779
780 if gitc_projects != [] and not opt.local_only:
Simran Basib9a1b732015-08-20 12:19:28 -0700781 print('Updating GITC client: %s' % self.gitc_manifest.gitc_client_name)
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700782 manifest = GitcManifest(self.repodir, self.gitc_manifest.gitc_client_name)
783 if manifest_name:
784 manifest.Override(manifest_name)
785 else:
786 manifest.Override(self.manifest.manifestFile)
787 gitc_utils.generate_gitc_manifest(self.gitc_manifest,
788 manifest,
Simran Basib9a1b732015-08-20 12:19:28 -0700789 gitc_projects)
790 print('GITC client successfully synced.')
791
792 # The opened projects need to be synced as normal, therefore we
793 # generate a new args list to represent the opened projects.
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700794 # TODO: make this more reliable -- if there's a project name/path overlap,
795 # this may choose the wrong project.
David Pursehouse3bcd3052017-07-10 22:42:22 +0900796 args = [os.path.relpath(self.manifest.paths[path].worktree, os.getcwd())
797 for path in opened_projects]
Simran Basib9a1b732015-08-20 12:19:28 -0700798 if not args:
799 return
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800800 all_projects = self.GetProjects(args,
801 missing_ok=True,
802 submodules_ok=opt.fetch_submodules)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700803
Dave Borowitz67700e92012-10-23 15:00:54 -0700804 self._fetch_times = _FetchTimes(self.manifest)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700805 if not opt.local_only:
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700806 to_fetch = []
807 now = time.time()
Dave Borowitz67700e92012-10-23 15:00:54 -0700808 if _ONE_DAY_S <= (now - rp.LastFetch):
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700809 to_fetch.append(rp)
David Pursehouse8a68ff92012-09-24 12:15:13 +0900810 to_fetch.extend(all_projects)
Dave Borowitz67700e92012-10-23 15:00:54 -0700811 to_fetch.sort(key=self._fetch_times.Get, reverse=True)
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700812
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800813 fetched = self._Fetch(to_fetch, opt)
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700814 _PostRepoFetch(rp, opt.no_repo_verify)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700815 if opt.network_only:
816 # bail out now; the rest touches the working tree
817 return
818
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800819 # Iteratively fetch missing and/or nested unregistered submodules
820 previously_missing_set = set()
821 while True:
Victor Boivie53a6c5d2013-03-19 12:20:52 +0100822 self._ReloadManifest(manifest_name)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800823 all_projects = self.GetProjects(args,
824 missing_ok=True,
825 submodules_ok=opt.fetch_submodules)
826 missing = []
827 for project in all_projects:
828 if project.gitdir not in fetched:
829 missing.append(project)
830 if not missing:
831 break
832 # Stop us from non-stopped fetching actually-missing repos: If set of
833 # missing repos has not been changed from last fetch, we break.
834 missing_set = set(p.name for p in missing)
835 if previously_missing_set == missing_set:
836 break
837 previously_missing_set = missing_set
838 fetched.update(self._Fetch(missing, opt))
839
Julien Campergue335f5ef2013-10-16 11:02:35 +0200840 if self.manifest.IsMirror or self.manifest.IsArchive:
Shawn O. Pearcecd1d7ff2009-06-04 16:15:53 -0700841 # bail out now, we have no working tree
842 return
843
Oleksii Okolielovd3c0f592018-12-17 19:23:44 -0500844 if self.UpdateProjectList(opt):
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700845 sys.exit(1)
846
Shawn O. Pearce350cde42009-04-16 11:21:18 -0700847 syncbuf = SyncBuffer(mp.config,
848 detach_head = opt.detach_head)
David Pursehouse8a68ff92012-09-24 12:15:13 +0900849 pm = Progress('Syncing work tree', len(all_projects))
850 for project in all_projects:
Shawn O. Pearce68194f42009-04-10 16:48:52 -0700851 pm.update()
Shawn O. Pearcee284ad12008-11-04 07:37:10 -0800852 if project.worktree:
David Rileye0684ad2017-04-05 00:02:59 -0700853 start = time.time()
Kevin Degiabaa7f32014-11-12 11:27:45 -0700854 project.Sync_LocalHalf(syncbuf, force_sync=opt.force_sync)
David Rileye0684ad2017-04-05 00:02:59 -0700855 self.event_log.AddSync(project, event_log.TASK_SYNC_LOCAL,
856 start, time.time(), syncbuf.Recently())
Shawn O. Pearce68194f42009-04-10 16:48:52 -0700857 pm.end()
Sarah Owenscecd1d82012-11-01 22:59:27 -0700858 print(file=sys.stderr)
Shawn O. Pearce350cde42009-04-16 11:21:18 -0700859 if not syncbuf.Finish():
860 sys.exit(1)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700861
Doug Anderson2b8db3c2010-11-01 15:08:06 -0700862 # If there's a notice that's supposed to print at the end of the sync, print
863 # it now...
864 if self.manifest.notice:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700865 print(self.manifest.notice)
Doug Anderson2b8db3c2010-11-01 15:08:06 -0700866
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -0700867def _PostRepoUpgrade(manifest, quiet=False):
Conley Owens094cdbe2014-01-30 15:09:59 -0800868 wrapper = Wrapper()
Conley Owensc9129d92012-10-01 16:12:28 -0700869 if wrapper.NeedSetupGnuPG():
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -0700870 wrapper.SetupGnuPG(quiet)
Conley Owensf2fe2d92014-01-29 13:53:43 -0800871 for project in manifest.projects:
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700872 if project.Exists:
873 project.PostRepoUpgrade()
874
875def _PostRepoFetch(rp, no_repo_verify=False, verbose=False):
876 if rp.HasChanges:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700877 print('info: A new version of repo is available', file=sys.stderr)
878 print(file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700879 if no_repo_verify or _VerifyTag(rp):
Shawn O. Pearce350cde42009-04-16 11:21:18 -0700880 syncbuf = SyncBuffer(rp.config)
881 rp.Sync_LocalHalf(syncbuf)
882 if not syncbuf.Finish():
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700883 sys.exit(1)
Sarah Owenscecd1d82012-11-01 22:59:27 -0700884 print('info: Restarting repo with latest version', file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700885 raise RepoChangedException(['--repo-upgraded'])
886 else:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700887 print('warning: Skipped upgrade to unverified version', file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700888 else:
889 if verbose:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700890 print('repo version %s is current' % rp.work_git.describe(HEAD),
891 file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700892
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700893def _VerifyTag(project):
894 gpg_dir = os.path.expanduser('~/.repoconfig/gnupg')
895 if not os.path.exists(gpg_dir):
Sarah Owenscecd1d82012-11-01 22:59:27 -0700896 print('warning: GnuPG was not available during last "repo init"\n'
897 'warning: Cannot automatically authenticate repo."""',
898 file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700899 return True
900
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700901 try:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -0700902 cur = project.bare_git.describe(project.GetRevisionId())
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700903 except GitError:
904 cur = None
905
906 if not cur \
907 or re.compile(r'^.*-[0-9]{1,}-g[0-9a-f]{1,}$').match(cur):
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -0700908 rev = project.revisionExpr
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700909 if rev.startswith(R_HEADS):
910 rev = rev[len(R_HEADS):]
911
Sarah Owenscecd1d82012-11-01 22:59:27 -0700912 print(file=sys.stderr)
913 print("warning: project '%s' branch '%s' is not signed"
914 % (project.name, rev), file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700915 return False
916
Shawn O. Pearcef18cb762010-12-07 11:41:05 -0800917 env = os.environ.copy()
918 env['GIT_DIR'] = project.gitdir.encode()
919 env['GNUPGHOME'] = gpg_dir.encode()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700920
921 cmd = [GIT, 'tag', '-v', cur]
922 proc = subprocess.Popen(cmd,
923 stdout = subprocess.PIPE,
924 stderr = subprocess.PIPE,
925 env = env)
926 out = proc.stdout.read()
927 proc.stdout.close()
928
929 err = proc.stderr.read()
930 proc.stderr.close()
931
932 if proc.wait() != 0:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700933 print(file=sys.stderr)
934 print(out, file=sys.stderr)
935 print(err, file=sys.stderr)
936 print(file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700937 return False
938 return True
Dave Borowitz67700e92012-10-23 15:00:54 -0700939
David Rileye0684ad2017-04-05 00:02:59 -0700940
Dave Borowitz67700e92012-10-23 15:00:54 -0700941class _FetchTimes(object):
Dave Borowitzd9478582012-10-23 16:35:39 -0700942 _ALPHA = 0.5
943
Dave Borowitz67700e92012-10-23 15:00:54 -0700944 def __init__(self, manifest):
Anthony King85b24ac2014-05-06 15:57:48 +0100945 self._path = os.path.join(manifest.repodir, '.repo_fetchtimes.json')
Dave Borowitz67700e92012-10-23 15:00:54 -0700946 self._times = None
Dave Borowitzd9478582012-10-23 16:35:39 -0700947 self._seen = set()
Dave Borowitz67700e92012-10-23 15:00:54 -0700948
949 def Get(self, project):
950 self._Load()
951 return self._times.get(project.name, _ONE_DAY_S)
952
953 def Set(self, project, t):
Dave Borowitzd9478582012-10-23 16:35:39 -0700954 self._Load()
955 name = project.name
956 old = self._times.get(name, t)
957 self._seen.add(name)
958 a = self._ALPHA
959 self._times[name] = (a*t) + ((1-a) * old)
Dave Borowitz67700e92012-10-23 15:00:54 -0700960
961 def _Load(self):
962 if self._times is None:
963 try:
Anthony King85b24ac2014-05-06 15:57:48 +0100964 f = open(self._path)
Dave Borowitz67700e92012-10-23 15:00:54 -0700965 try:
Anthony King85b24ac2014-05-06 15:57:48 +0100966 self._times = json.load(f)
967 finally:
968 f.close()
969 except (IOError, ValueError):
970 try:
Renaud Paquay010fed72016-11-11 14:25:29 -0800971 platform_utils.remove(self._path)
Anthony King85b24ac2014-05-06 15:57:48 +0100972 except OSError:
973 pass
974 self._times = {}
Dave Borowitz67700e92012-10-23 15:00:54 -0700975
976 def Save(self):
977 if self._times is None:
978 return
Dave Borowitzd9478582012-10-23 16:35:39 -0700979
980 to_delete = []
981 for name in self._times:
982 if name not in self._seen:
983 to_delete.append(name)
984 for name in to_delete:
985 del self._times[name]
986
Dave Borowitz67700e92012-10-23 15:00:54 -0700987 try:
Anthony King85b24ac2014-05-06 15:57:48 +0100988 f = open(self._path, 'w')
Dave Borowitz67700e92012-10-23 15:00:54 -0700989 try:
Anthony King85b24ac2014-05-06 15:57:48 +0100990 json.dump(self._times, f, indent=2)
991 finally:
992 f.close()
993 except (IOError, TypeError):
994 try:
Renaud Paquay010fed72016-11-11 14:25:29 -0800995 platform_utils.remove(self._path)
Anthony King85b24ac2014-05-06 15:57:48 +0100996 except OSError:
997 pass
Dan Willemsen0745bb22015-08-17 13:41:45 -0700998
999# This is a replacement for xmlrpc.client.Transport using urllib2
1000# and supporting persistent-http[s]. It cannot change hosts from
1001# request to request like the normal transport, the real url
1002# is passed during initialization.
1003class PersistentTransport(xmlrpc.client.Transport):
1004 def __init__(self, orig_host):
1005 self.orig_host = orig_host
1006
1007 def request(self, host, handler, request_body, verbose=False):
1008 with GetUrlCookieFile(self.orig_host, not verbose) as (cookiefile, proxy):
1009 # Python doesn't understand cookies with the #HttpOnly_ prefix
1010 # Since we're only using them for HTTP, copy the file temporarily,
1011 # stripping those prefixes away.
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001012 if cookiefile:
1013 tmpcookiefile = tempfile.NamedTemporaryFile()
David Pursehouse4c5f74e2015-10-02 11:10:10 +09001014 tmpcookiefile.write("# HTTP Cookie File")
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001015 try:
1016 with open(cookiefile) as f:
1017 for line in f:
1018 if line.startswith("#HttpOnly_"):
1019 line = line[len("#HttpOnly_"):]
1020 tmpcookiefile.write(line)
1021 tmpcookiefile.flush()
Dan Willemsen0745bb22015-08-17 13:41:45 -07001022
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001023 cookiejar = cookielib.MozillaCookieJar(tmpcookiefile.name)
David Pursehouseb1ad2192015-09-30 10:35:43 +09001024 try:
1025 cookiejar.load()
1026 except cookielib.LoadError:
1027 cookiejar = cookielib.CookieJar()
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001028 finally:
1029 tmpcookiefile.close()
1030 else:
1031 cookiejar = cookielib.CookieJar()
Dan Willemsen0745bb22015-08-17 13:41:45 -07001032
1033 proxyhandler = urllib.request.ProxyHandler
1034 if proxy:
1035 proxyhandler = urllib.request.ProxyHandler({
1036 "http": proxy,
1037 "https": proxy })
1038
1039 opener = urllib.request.build_opener(
1040 urllib.request.HTTPCookieProcessor(cookiejar),
1041 proxyhandler)
1042
1043 url = urllib.parse.urljoin(self.orig_host, handler)
1044 parse_results = urllib.parse.urlparse(url)
1045
1046 scheme = parse_results.scheme
1047 if scheme == 'persistent-http':
1048 scheme = 'http'
1049 if scheme == 'persistent-https':
1050 # If we're proxying through persistent-https, use http. The
1051 # proxy itself will do the https.
1052 if proxy:
1053 scheme = 'http'
1054 else:
1055 scheme = 'https'
1056
1057 # Parse out any authentication information using the base class
1058 host, extra_headers, _ = self.get_host_info(parse_results.netloc)
1059
1060 url = urllib.parse.urlunparse((
1061 scheme,
1062 host,
1063 parse_results.path,
1064 parse_results.params,
1065 parse_results.query,
1066 parse_results.fragment))
1067
1068 request = urllib.request.Request(url, request_body)
1069 if extra_headers is not None:
1070 for (name, header) in extra_headers:
1071 request.add_header(name, header)
1072 request.add_header('Content-Type', 'text/xml')
1073 try:
1074 response = opener.open(request)
1075 except urllib.error.HTTPError as e:
1076 if e.code == 501:
1077 # We may have been redirected through a login process
1078 # but our POST turned into a GET. Retry.
1079 response = opener.open(request)
1080 else:
1081 raise
1082
1083 p, u = xmlrpc.client.getparser()
1084 while 1:
1085 data = response.read(1024)
1086 if not data:
1087 break
1088 p.feed(data)
1089 p.close()
1090 return u.close()
1091
1092 def close(self):
1093 pass
1094