blob: ec7337aaa8b89a8a50e58294d7f2968d43bfd9db [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()
Kuang-che Wu0d9b16d2019-04-06 00:49:47 +0800553 # In reversed order, so subfolders are deleted before parent folder.
554 for path in sorted(old_project_paths, reverse=True):
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700555 if not path:
556 continue
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700557 if path not in new_project_paths:
David Pursehouse8a68ff92012-09-24 12:15:13 +0900558 # If the path has already been deleted, we don't need to do it
Dan Willemsen43507912016-09-01 16:26:02 -0700559 gitdir = os.path.join(self.manifest.topdir, path, '.git')
560 if os.path.exists(gitdir):
David Pursehousec1b86a22012-11-14 11:36:51 +0900561 project = Project(
562 manifest = self.manifest,
563 name = path,
564 remote = RemoteSpec('origin'),
David James8d201162013-10-11 17:03:19 -0700565 gitdir = gitdir,
566 objdir = gitdir,
David Pursehousec1b86a22012-11-14 11:36:51 +0900567 worktree = os.path.join(self.manifest.topdir, path),
568 relpath = path,
569 revisionExpr = 'HEAD',
570 revisionId = None,
571 groups = None)
Anthonyf3fdf822009-09-26 13:38:52 -0400572
Oleksii Okolielovd3c0f592018-12-17 19:23:44 -0500573 if project.IsDirty() and opt.force_remove_dirty:
574 print('WARNING: Removing dirty project "%s": uncommitted changes '
575 'erased' % project.relpath, file=sys.stderr)
576 self._DeleteProject(project.worktree)
577 elif project.IsDirty():
David Pursehouse2f9e7e42013-03-05 17:26:46 +0900578 print('error: Cannot remove project "%s": uncommitted changes '
David Pursehousec1b86a22012-11-14 11:36:51 +0900579 'are present' % project.relpath, file=sys.stderr)
580 print(' commit changes, then run sync again',
581 file=sys.stderr)
582 return -1
Dan Willemsen43507912016-09-01 16:26:02 -0700583 elif self._DeleteProject(project.worktree):
584 return -1
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700585
Shawn O. Pearce9fb29ce2009-06-04 20:41:02 -0700586 new_project_paths.sort()
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700587 fd = open(file_path, 'w')
588 try:
589 fd.write('\n'.join(new_project_paths))
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700590 fd.write('\n')
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700591 finally:
592 fd.close()
593 return 0
594
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700595 def Execute(self, opt, args):
Roy Lee18afd7f2010-05-09 04:32:08 +0800596 if opt.jobs:
597 self.jobs = opt.jobs
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -0700598 if self.jobs > 1:
599 soft_limit, _ = _rlimit_nofile()
600 self.jobs = min(self.jobs, (soft_limit - 5) / 3)
601
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700602 if opt.network_only and opt.detach_head:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700603 print('error: cannot combine -n and -d', file=sys.stderr)
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700604 sys.exit(1)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700605 if opt.network_only and opt.local_only:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700606 print('error: cannot combine -n and -l', file=sys.stderr)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700607 sys.exit(1)
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500608 if opt.manifest_name and opt.smart_sync:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700609 print('error: cannot combine -m and -s', file=sys.stderr)
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500610 sys.exit(1)
611 if opt.manifest_name and opt.smart_tag:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700612 print('error: cannot combine -m and -t', file=sys.stderr)
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500613 sys.exit(1)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900614 if opt.manifest_server_username or opt.manifest_server_password:
615 if not (opt.smart_sync or opt.smart_tag):
Sarah Owenscecd1d82012-11-01 22:59:27 -0700616 print('error: -u and -p may only be combined with -s or -t',
617 file=sys.stderr)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900618 sys.exit(1)
619 if None in [opt.manifest_server_username, opt.manifest_server_password]:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700620 print('error: both -u and -p must be given', file=sys.stderr)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900621 sys.exit(1)
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500622
623 if opt.manifest_name:
624 self.manifest.Override(opt.manifest_name)
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700625
Chirayu Desaia892b102013-06-11 14:18:46 +0530626 manifest_name = opt.manifest_name
David Pursehouse59b41742015-05-07 14:36:09 +0900627 smart_sync_manifest_name = "smart_sync_override.xml"
628 smart_sync_manifest_path = os.path.join(
629 self.manifest.manifestProject.worktree, smart_sync_manifest_name)
Chirayu Desaia892b102013-06-11 14:18:46 +0530630
Victor Boivie08c880d2011-04-19 10:32:52 +0200631 if opt.smart_sync or opt.smart_tag:
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700632 if not self.manifest.manifest_server:
David Pursehouse2f9e7e42013-03-05 17:26:46 +0900633 print('error: cannot smart sync: no manifest server defined in '
Sarah Owenscecd1d82012-11-01 22:59:27 -0700634 'manifest', file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700635 sys.exit(1)
David Pursehouse86d973d2012-08-24 10:21:02 +0900636
637 manifest_server = self.manifest.manifest_server
David Pursehousefb99c712013-09-25 11:09:34 +0900638 if not opt.quiet:
639 print('Using manifest server %s' % manifest_server)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900640
David Pursehouse86d973d2012-08-24 10:21:02 +0900641 if not '@' in manifest_server:
David Pursehousecf76b1b2012-09-14 10:31:42 +0900642 username = None
643 password = None
644 if opt.manifest_server_username and opt.manifest_server_password:
645 username = opt.manifest_server_username
646 password = opt.manifest_server_password
David Pursehouse86d973d2012-08-24 10:21:02 +0900647 else:
648 try:
David Pursehousecf76b1b2012-09-14 10:31:42 +0900649 info = netrc.netrc()
650 except IOError:
David Pursehouseba7bc732015-08-20 16:55:42 +0900651 # .netrc file does not exist or could not be opened
652 pass
David Pursehouse86d973d2012-08-24 10:21:02 +0900653 else:
David Pursehousecf76b1b2012-09-14 10:31:42 +0900654 try:
Chirayu Desaidb2ad9d2013-06-11 13:42:25 +0530655 parse_result = urllib.parse.urlparse(manifest_server)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900656 if parse_result.hostname:
David Pursehouseba7bc732015-08-20 16:55:42 +0900657 auth = info.authenticators(parse_result.hostname)
658 if auth:
659 username, _account, password = auth
660 else:
661 print('No credentials found for %s in .netrc'
662 % parse_result.hostname, file=sys.stderr)
Sarah Owensa5be53f2012-09-09 15:37:57 -0700663 except netrc.NetrcParseError as e:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700664 print('Error parsing .netrc file: %s' % e, file=sys.stderr)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900665
666 if (username and password):
667 manifest_server = manifest_server.replace('://', '://%s:%s@' %
668 (username, password),
669 1)
David Pursehouse86d973d2012-08-24 10:21:02 +0900670
Dan Willemsen0745bb22015-08-17 13:41:45 -0700671 transport = PersistentTransport(manifest_server)
672 if manifest_server.startswith('persistent-'):
673 manifest_server = manifest_server[len('persistent-'):]
674
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700675 try:
Dan Willemsen0745bb22015-08-17 13:41:45 -0700676 server = xmlrpc.client.Server(manifest_server, transport=transport)
Victor Boivie08c880d2011-04-19 10:32:52 +0200677 if opt.smart_sync:
678 p = self.manifest.manifestProject
679 b = p.GetBranch(p.CurrentBranch)
680 branch = b.merge
681 if branch.startswith(R_HEADS):
682 branch = branch[len(R_HEADS):]
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700683
Victor Boivie08c880d2011-04-19 10:32:52 +0200684 env = os.environ.copy()
Jeff Davidson5cf16602014-10-02 10:13:38 -0700685 if 'SYNC_TARGET' in env:
686 target = env['SYNC_TARGET']
687 [success, manifest_str] = server.GetApprovedManifest(branch, target)
688 elif 'TARGET_PRODUCT' in env and 'TARGET_BUILD_VARIANT' in env:
Victor Boivie08c880d2011-04-19 10:32:52 +0200689 target = '%s-%s' % (env['TARGET_PRODUCT'],
690 env['TARGET_BUILD_VARIANT'])
691 [success, manifest_str] = server.GetApprovedManifest(branch, target)
692 else:
693 [success, manifest_str] = server.GetApprovedManifest(branch)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700694 else:
Victor Boivie08c880d2011-04-19 10:32:52 +0200695 assert(opt.smart_tag)
696 [success, manifest_str] = server.GetManifest(opt.smart_tag)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700697
698 if success:
David Pursehouse59b41742015-05-07 14:36:09 +0900699 manifest_name = smart_sync_manifest_name
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700700 try:
David Pursehouse59b41742015-05-07 14:36:09 +0900701 f = open(smart_sync_manifest_path, 'w')
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700702 try:
703 f.write(manifest_str)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700704 finally:
705 f.close()
David Pursehouse727cc3e2015-05-07 14:16:49 +0900706 except IOError as e:
707 print('error: cannot write manifest to %s:\n%s'
David Pursehouse59b41742015-05-07 14:36:09 +0900708 % (smart_sync_manifest_path, e),
Sarah Owenscecd1d82012-11-01 22:59:27 -0700709 file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700710 sys.exit(1)
Victor Boivie53a6c5d2013-03-19 12:20:52 +0100711 self._ReloadManifest(manifest_name)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700712 else:
David Pursehouse351fe2c2013-09-25 17:54:26 +0900713 print('error: manifest server RPC call failed: %s' %
714 manifest_str, file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700715 sys.exit(1)
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530716 except (socket.error, IOError, xmlrpc.client.Fault) as e:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700717 print('error: cannot connect to manifest server %s:\n%s'
718 % (self.manifest.manifest_server, e), file=sys.stderr)
David Pursehousebd489c42012-08-23 10:21:26 +0900719 sys.exit(1)
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530720 except xmlrpc.client.ProtocolError as e:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700721 print('error: cannot connect to manifest server %s:\n%d %s'
722 % (self.manifest.manifest_server, e.errcode, e.errmsg),
723 file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700724 sys.exit(1)
David Pursehouse59b41742015-05-07 14:36:09 +0900725 else: # Not smart sync or smart tag mode
726 if os.path.isfile(smart_sync_manifest_path):
727 try:
Renaud Paquay010fed72016-11-11 14:25:29 -0800728 platform_utils.remove(smart_sync_manifest_path)
David Pursehouse59b41742015-05-07 14:36:09 +0900729 except OSError as e:
730 print('error: failed to remove existing smart sync override manifest: %s' %
731 e, file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700732
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700733 rp = self.manifest.repoProject
734 rp.PreSync()
735
736 mp = self.manifest.manifestProject
737 mp.PreSync()
738
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800739 if opt.repo_upgraded:
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -0700740 _PostRepoUpgrade(self.manifest, quiet=opt.quiet)
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800741
Nico Sallembien9bb18162009-12-07 15:38:01 -0800742 if not opt.local_only:
David Rileye0684ad2017-04-05 00:02:59 -0700743 start = time.time()
744 success = mp.Sync_NetworkHalf(quiet=opt.quiet,
745 current_branch_only=opt.current_branch_only,
746 no_tags=opt.no_tags,
747 optimized_fetch=opt.optimized_fetch,
748 submodules=self.manifest.HasSubmodules)
749 finish = time.time()
750 self.event_log.AddSync(mp, event_log.TASK_SYNC_NETWORK,
751 start, finish, success)
Nico Sallembien9bb18162009-12-07 15:38:01 -0800752
753 if mp.HasChanges:
754 syncbuf = SyncBuffer(mp.config)
David Rileye0684ad2017-04-05 00:02:59 -0700755 start = time.time()
Martin Kellye4e94d22017-03-21 16:05:12 -0700756 mp.Sync_LocalHalf(syncbuf, submodules=self.manifest.HasSubmodules)
David Rileye0684ad2017-04-05 00:02:59 -0700757 clean = syncbuf.Finish()
758 self.event_log.AddSync(mp, event_log.TASK_SYNC_LOCAL,
759 start, time.time(), clean)
760 if not clean:
Nico Sallembien9bb18162009-12-07 15:38:01 -0800761 sys.exit(1)
Victor Boivie53a6c5d2013-03-19 12:20:52 +0100762 self._ReloadManifest(manifest_name)
Shawn O. Pearcec4657962011-09-26 09:08:01 -0700763 if opt.jobs is None:
764 self.jobs = self.manifest.default.sync_j
Simran Basib9a1b732015-08-20 12:19:28 -0700765
Simran Basib9a1b732015-08-20 12:19:28 -0700766 if self.gitc_manifest:
767 gitc_manifest_projects = self.GetProjects(args,
Simran Basib9a1b732015-08-20 12:19:28 -0700768 missing_ok=True)
769 gitc_projects = []
770 opened_projects = []
771 for project in gitc_manifest_projects:
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700772 if project.relpath in self.gitc_manifest.paths and \
773 self.gitc_manifest.paths[project.relpath].old_revision:
774 opened_projects.append(project.relpath)
Simran Basib9a1b732015-08-20 12:19:28 -0700775 else:
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700776 gitc_projects.append(project.relpath)
Simran Basib9a1b732015-08-20 12:19:28 -0700777
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700778 if not args:
779 gitc_projects = None
780
781 if gitc_projects != [] and not opt.local_only:
Simran Basib9a1b732015-08-20 12:19:28 -0700782 print('Updating GITC client: %s' % self.gitc_manifest.gitc_client_name)
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700783 manifest = GitcManifest(self.repodir, self.gitc_manifest.gitc_client_name)
784 if manifest_name:
785 manifest.Override(manifest_name)
786 else:
787 manifest.Override(self.manifest.manifestFile)
788 gitc_utils.generate_gitc_manifest(self.gitc_manifest,
789 manifest,
Simran Basib9a1b732015-08-20 12:19:28 -0700790 gitc_projects)
791 print('GITC client successfully synced.')
792
793 # The opened projects need to be synced as normal, therefore we
794 # generate a new args list to represent the opened projects.
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700795 # TODO: make this more reliable -- if there's a project name/path overlap,
796 # this may choose the wrong project.
David Pursehouse3bcd3052017-07-10 22:42:22 +0900797 args = [os.path.relpath(self.manifest.paths[path].worktree, os.getcwd())
798 for path in opened_projects]
Simran Basib9a1b732015-08-20 12:19:28 -0700799 if not args:
800 return
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800801 all_projects = self.GetProjects(args,
802 missing_ok=True,
803 submodules_ok=opt.fetch_submodules)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700804
Dave Borowitz67700e92012-10-23 15:00:54 -0700805 self._fetch_times = _FetchTimes(self.manifest)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700806 if not opt.local_only:
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700807 to_fetch = []
808 now = time.time()
Dave Borowitz67700e92012-10-23 15:00:54 -0700809 if _ONE_DAY_S <= (now - rp.LastFetch):
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700810 to_fetch.append(rp)
David Pursehouse8a68ff92012-09-24 12:15:13 +0900811 to_fetch.extend(all_projects)
Dave Borowitz67700e92012-10-23 15:00:54 -0700812 to_fetch.sort(key=self._fetch_times.Get, reverse=True)
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700813
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800814 fetched = self._Fetch(to_fetch, opt)
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700815 _PostRepoFetch(rp, opt.no_repo_verify)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700816 if opt.network_only:
817 # bail out now; the rest touches the working tree
818 return
819
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800820 # Iteratively fetch missing and/or nested unregistered submodules
821 previously_missing_set = set()
822 while True:
Victor Boivie53a6c5d2013-03-19 12:20:52 +0100823 self._ReloadManifest(manifest_name)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800824 all_projects = self.GetProjects(args,
825 missing_ok=True,
826 submodules_ok=opt.fetch_submodules)
827 missing = []
828 for project in all_projects:
829 if project.gitdir not in fetched:
830 missing.append(project)
831 if not missing:
832 break
833 # Stop us from non-stopped fetching actually-missing repos: If set of
834 # missing repos has not been changed from last fetch, we break.
835 missing_set = set(p.name for p in missing)
836 if previously_missing_set == missing_set:
837 break
838 previously_missing_set = missing_set
839 fetched.update(self._Fetch(missing, opt))
840
Julien Campergue335f5ef2013-10-16 11:02:35 +0200841 if self.manifest.IsMirror or self.manifest.IsArchive:
Shawn O. Pearcecd1d7ff2009-06-04 16:15:53 -0700842 # bail out now, we have no working tree
843 return
844
Oleksii Okolielovd3c0f592018-12-17 19:23:44 -0500845 if self.UpdateProjectList(opt):
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700846 sys.exit(1)
847
Shawn O. Pearce350cde42009-04-16 11:21:18 -0700848 syncbuf = SyncBuffer(mp.config,
849 detach_head = opt.detach_head)
David Pursehouse8a68ff92012-09-24 12:15:13 +0900850 pm = Progress('Syncing work tree', len(all_projects))
851 for project in all_projects:
Shawn O. Pearce68194f42009-04-10 16:48:52 -0700852 pm.update()
Shawn O. Pearcee284ad12008-11-04 07:37:10 -0800853 if project.worktree:
David Rileye0684ad2017-04-05 00:02:59 -0700854 start = time.time()
Kevin Degiabaa7f32014-11-12 11:27:45 -0700855 project.Sync_LocalHalf(syncbuf, force_sync=opt.force_sync)
David Rileye0684ad2017-04-05 00:02:59 -0700856 self.event_log.AddSync(project, event_log.TASK_SYNC_LOCAL,
857 start, time.time(), syncbuf.Recently())
Shawn O. Pearce68194f42009-04-10 16:48:52 -0700858 pm.end()
Sarah Owenscecd1d82012-11-01 22:59:27 -0700859 print(file=sys.stderr)
Shawn O. Pearce350cde42009-04-16 11:21:18 -0700860 if not syncbuf.Finish():
861 sys.exit(1)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700862
Doug Anderson2b8db3c2010-11-01 15:08:06 -0700863 # If there's a notice that's supposed to print at the end of the sync, print
864 # it now...
865 if self.manifest.notice:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700866 print(self.manifest.notice)
Doug Anderson2b8db3c2010-11-01 15:08:06 -0700867
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -0700868def _PostRepoUpgrade(manifest, quiet=False):
Conley Owens094cdbe2014-01-30 15:09:59 -0800869 wrapper = Wrapper()
Conley Owensc9129d92012-10-01 16:12:28 -0700870 if wrapper.NeedSetupGnuPG():
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -0700871 wrapper.SetupGnuPG(quiet)
Conley Owensf2fe2d92014-01-29 13:53:43 -0800872 for project in manifest.projects:
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700873 if project.Exists:
874 project.PostRepoUpgrade()
875
876def _PostRepoFetch(rp, no_repo_verify=False, verbose=False):
877 if rp.HasChanges:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700878 print('info: A new version of repo is available', file=sys.stderr)
879 print(file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700880 if no_repo_verify or _VerifyTag(rp):
Shawn O. Pearce350cde42009-04-16 11:21:18 -0700881 syncbuf = SyncBuffer(rp.config)
882 rp.Sync_LocalHalf(syncbuf)
883 if not syncbuf.Finish():
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700884 sys.exit(1)
Sarah Owenscecd1d82012-11-01 22:59:27 -0700885 print('info: Restarting repo with latest version', file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700886 raise RepoChangedException(['--repo-upgraded'])
887 else:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700888 print('warning: Skipped upgrade to unverified version', file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700889 else:
890 if verbose:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700891 print('repo version %s is current' % rp.work_git.describe(HEAD),
892 file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700893
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700894def _VerifyTag(project):
895 gpg_dir = os.path.expanduser('~/.repoconfig/gnupg')
896 if not os.path.exists(gpg_dir):
Sarah Owenscecd1d82012-11-01 22:59:27 -0700897 print('warning: GnuPG was not available during last "repo init"\n'
898 'warning: Cannot automatically authenticate repo."""',
899 file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700900 return True
901
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700902 try:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -0700903 cur = project.bare_git.describe(project.GetRevisionId())
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700904 except GitError:
905 cur = None
906
907 if not cur \
908 or re.compile(r'^.*-[0-9]{1,}-g[0-9a-f]{1,}$').match(cur):
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -0700909 rev = project.revisionExpr
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700910 if rev.startswith(R_HEADS):
911 rev = rev[len(R_HEADS):]
912
Sarah Owenscecd1d82012-11-01 22:59:27 -0700913 print(file=sys.stderr)
914 print("warning: project '%s' branch '%s' is not signed"
915 % (project.name, rev), file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700916 return False
917
Shawn O. Pearcef18cb762010-12-07 11:41:05 -0800918 env = os.environ.copy()
919 env['GIT_DIR'] = project.gitdir.encode()
920 env['GNUPGHOME'] = gpg_dir.encode()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700921
922 cmd = [GIT, 'tag', '-v', cur]
923 proc = subprocess.Popen(cmd,
924 stdout = subprocess.PIPE,
925 stderr = subprocess.PIPE,
926 env = env)
927 out = proc.stdout.read()
928 proc.stdout.close()
929
930 err = proc.stderr.read()
931 proc.stderr.close()
932
933 if proc.wait() != 0:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700934 print(file=sys.stderr)
935 print(out, file=sys.stderr)
936 print(err, file=sys.stderr)
937 print(file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700938 return False
939 return True
Dave Borowitz67700e92012-10-23 15:00:54 -0700940
David Rileye0684ad2017-04-05 00:02:59 -0700941
Dave Borowitz67700e92012-10-23 15:00:54 -0700942class _FetchTimes(object):
Dave Borowitzd9478582012-10-23 16:35:39 -0700943 _ALPHA = 0.5
944
Dave Borowitz67700e92012-10-23 15:00:54 -0700945 def __init__(self, manifest):
Anthony King85b24ac2014-05-06 15:57:48 +0100946 self._path = os.path.join(manifest.repodir, '.repo_fetchtimes.json')
Dave Borowitz67700e92012-10-23 15:00:54 -0700947 self._times = None
Dave Borowitzd9478582012-10-23 16:35:39 -0700948 self._seen = set()
Dave Borowitz67700e92012-10-23 15:00:54 -0700949
950 def Get(self, project):
951 self._Load()
952 return self._times.get(project.name, _ONE_DAY_S)
953
954 def Set(self, project, t):
Dave Borowitzd9478582012-10-23 16:35:39 -0700955 self._Load()
956 name = project.name
957 old = self._times.get(name, t)
958 self._seen.add(name)
959 a = self._ALPHA
960 self._times[name] = (a*t) + ((1-a) * old)
Dave Borowitz67700e92012-10-23 15:00:54 -0700961
962 def _Load(self):
963 if self._times is None:
964 try:
Anthony King85b24ac2014-05-06 15:57:48 +0100965 f = open(self._path)
Dave Borowitz67700e92012-10-23 15:00:54 -0700966 try:
Anthony King85b24ac2014-05-06 15:57:48 +0100967 self._times = json.load(f)
968 finally:
969 f.close()
970 except (IOError, ValueError):
971 try:
Renaud Paquay010fed72016-11-11 14:25:29 -0800972 platform_utils.remove(self._path)
Anthony King85b24ac2014-05-06 15:57:48 +0100973 except OSError:
974 pass
975 self._times = {}
Dave Borowitz67700e92012-10-23 15:00:54 -0700976
977 def Save(self):
978 if self._times is None:
979 return
Dave Borowitzd9478582012-10-23 16:35:39 -0700980
981 to_delete = []
982 for name in self._times:
983 if name not in self._seen:
984 to_delete.append(name)
985 for name in to_delete:
986 del self._times[name]
987
Dave Borowitz67700e92012-10-23 15:00:54 -0700988 try:
Anthony King85b24ac2014-05-06 15:57:48 +0100989 f = open(self._path, 'w')
Dave Borowitz67700e92012-10-23 15:00:54 -0700990 try:
Anthony King85b24ac2014-05-06 15:57:48 +0100991 json.dump(self._times, f, indent=2)
992 finally:
993 f.close()
994 except (IOError, TypeError):
995 try:
Renaud Paquay010fed72016-11-11 14:25:29 -0800996 platform_utils.remove(self._path)
Anthony King85b24ac2014-05-06 15:57:48 +0100997 except OSError:
998 pass
Dan Willemsen0745bb22015-08-17 13:41:45 -0700999
1000# This is a replacement for xmlrpc.client.Transport using urllib2
1001# and supporting persistent-http[s]. It cannot change hosts from
1002# request to request like the normal transport, the real url
1003# is passed during initialization.
1004class PersistentTransport(xmlrpc.client.Transport):
1005 def __init__(self, orig_host):
1006 self.orig_host = orig_host
1007
1008 def request(self, host, handler, request_body, verbose=False):
1009 with GetUrlCookieFile(self.orig_host, not verbose) as (cookiefile, proxy):
1010 # Python doesn't understand cookies with the #HttpOnly_ prefix
1011 # Since we're only using them for HTTP, copy the file temporarily,
1012 # stripping those prefixes away.
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001013 if cookiefile:
1014 tmpcookiefile = tempfile.NamedTemporaryFile()
David Pursehouse4c5f74e2015-10-02 11:10:10 +09001015 tmpcookiefile.write("# HTTP Cookie File")
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001016 try:
1017 with open(cookiefile) as f:
1018 for line in f:
1019 if line.startswith("#HttpOnly_"):
1020 line = line[len("#HttpOnly_"):]
1021 tmpcookiefile.write(line)
1022 tmpcookiefile.flush()
Dan Willemsen0745bb22015-08-17 13:41:45 -07001023
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001024 cookiejar = cookielib.MozillaCookieJar(tmpcookiefile.name)
David Pursehouseb1ad2192015-09-30 10:35:43 +09001025 try:
1026 cookiejar.load()
1027 except cookielib.LoadError:
1028 cookiejar = cookielib.CookieJar()
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001029 finally:
1030 tmpcookiefile.close()
1031 else:
1032 cookiejar = cookielib.CookieJar()
Dan Willemsen0745bb22015-08-17 13:41:45 -07001033
1034 proxyhandler = urllib.request.ProxyHandler
1035 if proxy:
1036 proxyhandler = urllib.request.ProxyHandler({
1037 "http": proxy,
1038 "https": proxy })
1039
1040 opener = urllib.request.build_opener(
1041 urllib.request.HTTPCookieProcessor(cookiejar),
1042 proxyhandler)
1043
1044 url = urllib.parse.urljoin(self.orig_host, handler)
1045 parse_results = urllib.parse.urlparse(url)
1046
1047 scheme = parse_results.scheme
1048 if scheme == 'persistent-http':
1049 scheme = 'http'
1050 if scheme == 'persistent-https':
1051 # If we're proxying through persistent-https, use http. The
1052 # proxy itself will do the https.
1053 if proxy:
1054 scheme = 'http'
1055 else:
1056 scheme = 'https'
1057
1058 # Parse out any authentication information using the base class
1059 host, extra_headers, _ = self.get_host_info(parse_results.netloc)
1060
1061 url = urllib.parse.urlunparse((
1062 scheme,
1063 host,
1064 parse_results.path,
1065 parse_results.params,
1066 parse_results.query,
1067 parse_results.fragment))
1068
1069 request = urllib.request.Request(url, request_body)
1070 if extra_headers is not None:
1071 for (name, header) in extra_headers:
1072 request.add_header(name, header)
1073 request.add_header('Content-Type', 'text/xml')
1074 try:
1075 response = opener.open(request)
1076 except urllib.error.HTTPError as e:
1077 if e.code == 501:
1078 # We may have been redirected through a login process
1079 # but our POST turned into a GET. Retry.
1080 response = opener.open(request)
1081 else:
1082 raise
1083
1084 p, u = xmlrpc.client.getparser()
1085 while 1:
1086 data = response.read(1024)
1087 if not data:
1088 break
1089 p.feed(data)
1090 p.close()
1091 return u.close()
1092
1093 def close(self):
1094 pass
1095