blob: 138eaf2afb523a490546747d31343bf0c47fe6a0 [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
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -070022import shutil
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -070023import socket
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070024import subprocess
25import sys
Dan Willemsen0745bb22015-08-17 13:41:45 -070026import tempfile
Shawn O. Pearcef6906872009-04-18 10:49:00 -070027import time
David Pursehouse59bbb582013-05-17 10:49:33 +090028
natalie.chen3e01e3a2016-07-20 10:45:57 +080029from urllib import quote as url_quote
30
David Pursehouse59bbb582013-05-17 10:49:33 +090031from pyversion import is_python3
32if is_python3():
Dan Willemsen0745bb22015-08-17 13:41:45 -070033 import http.cookiejar as cookielib
34 import urllib.error
Chirayu Desai217ea7d2013-03-01 19:14:38 +053035 import urllib.parse
Dan Willemsen0745bb22015-08-17 13:41:45 -070036 import urllib.request
David Pursehouse59bbb582013-05-17 10:49:33 +090037 import xmlrpc.client
38else:
Dan Willemsen0745bb22015-08-17 13:41:45 -070039 import cookielib
Chirayu Desai217ea7d2013-03-01 19:14:38 +053040 import imp
Dan Willemsen0745bb22015-08-17 13:41:45 -070041 import urllib2
Chirayu Desai217ea7d2013-03-01 19:14:38 +053042 import urlparse
David Pursehouse59bbb582013-05-17 10:49:33 +090043 import xmlrpclib
Chirayu Desai217ea7d2013-03-01 19:14:38 +053044 urllib = imp.new_module('urllib')
Dan Willemsen0745bb22015-08-17 13:41:45 -070045 urllib.error = urllib2
Chirayu Desaidb2ad9d2013-06-11 13:42:25 +053046 urllib.parse = urlparse
Dan Willemsen0745bb22015-08-17 13:41:45 -070047 urllib.request = urllib2
Chirayu Desai217ea7d2013-03-01 19:14:38 +053048 xmlrpc = imp.new_module('xmlrpc')
49 xmlrpc.client = xmlrpclib
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070050
Roy Lee18afd7f2010-05-09 04:32:08 +080051try:
52 import threading as _threading
53except ImportError:
54 import dummy_threading as _threading
55
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -070056try:
57 import resource
58 def _rlimit_nofile():
59 return resource.getrlimit(resource.RLIMIT_NOFILE)
60except ImportError:
61 def _rlimit_nofile():
62 return (256, 256)
63
Dave Borowitz18857212012-10-23 17:02:59 -070064try:
65 import multiprocessing
66except ImportError:
67 multiprocessing = None
68
Dave Borowitze2152672012-10-31 12:24:38 -070069from git_command import GIT, git_require
David Pursehouseba7bc732015-08-20 16:55:42 +090070from git_config import GetUrlCookieFile
David Pursehoused94aaef2012-09-07 09:52:04 +090071from git_refs import R_HEADS, HEAD
Simran Basibdb52712015-08-10 13:23:23 -070072import gitc_utils
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -070073from project import Project
74from project import RemoteSpec
Shawn O. Pearcec95583b2009-03-03 17:47:06 -080075from command import Command, MirrorSafeCommand
Torne (Richard Coles)7bdbde72012-12-05 10:58:06 +000076from error import RepoChangedException, GitError, ManifestParseError
Shawn O. Pearce350cde42009-04-16 11:21:18 -070077from project import SyncBuffer
Shawn O. Pearce68194f42009-04-10 16:48:52 -070078from progress import Progress
Conley Owens094cdbe2014-01-30 15:09:59 -080079from wrapper import Wrapper
Dan Willemsen5ea32d12015-09-08 13:27:20 -070080from manifest_xml import GitcManifest
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070081
Dave Borowitz67700e92012-10-23 15:00:54 -070082_ONE_DAY_S = 24 * 60 * 60
83
Doug Andersonfc06ced2011-03-16 15:49:18 -070084class _FetchError(Exception):
85 """Internal error thrown in _FetchHelper() when we don't want stack trace."""
86 pass
87
Shawn O. Pearcec95583b2009-03-03 17:47:06 -080088class Sync(Command, MirrorSafeCommand):
Roy Lee18afd7f2010-05-09 04:32:08 +080089 jobs = 1
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070090 common = True
91 helpSummary = "Update working tree to the latest revision"
92 helpUsage = """
93%prog [<project>...]
94"""
95 helpDescription = """
96The '%prog' command synchronizes local project directories
97with the remote repositories specified in the manifest. If a local
98project does not yet exist, it will clone a new local directory from
99the remote repository and set up tracking branches as specified in
100the manifest. If the local project already exists, '%prog'
101will update the remote branches and rebase any new local changes
102on top of the new remote changes.
103
104'%prog' will synchronize all projects listed at the command
105line. Projects can be specified either by name, or by a relative
106or absolute path to the project's local directory. If no projects
107are specified, '%prog' will synchronize all projects listed in
108the manifest.
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700109
110The -d/--detach option can be used to switch specified projects
111back to the manifest revision. This option is especially helpful
112if the project is currently on a topic branch, but the manifest
113revision is temporarily needed.
Shawn O. Pearceeb7af872009-04-21 08:02:04 -0700114
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700115The -s/--smart-sync option can be used to sync to a known good
116build as specified by the manifest-server element in the current
Victor Boivie08c880d2011-04-19 10:32:52 +0200117manifest. The -t/--smart-tag option is similar and allows you to
118specify a custom tag/label.
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700119
David Pursehousecf76b1b2012-09-14 10:31:42 +0900120The -u/--manifest-server-username and -p/--manifest-server-password
121options can be used to specify a username and password to authenticate
122with the manifest server when using the -s or -t option.
123
124If -u and -p are not specified when using the -s or -t option, '%prog'
125will attempt to read authentication credentials for the manifest server
126from the user's .netrc file.
127
128'%prog' will not use authentication credentials from -u/-p or .netrc
129if the manifest server specified in the manifest file already includes
130credentials.
131
Andrei Warkentin5df6de02010-07-02 17:58:31 -0500132The -f/--force-broken option can be used to proceed with syncing
133other projects if a project sync fails.
134
Kevin Degiabaa7f32014-11-12 11:27:45 -0700135The --force-sync option can be used to overwrite existing git
136directories if they have previously been linked to a different
137object direcotry. WARNING: This may cause data to be lost since
138refs may be removed when overwriting.
139
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -0700140The --no-clone-bundle option disables any attempt to use
141$URL/clone.bundle to bootstrap a new Git repository from a
142resumeable bundle file on a content delivery network. This
143may be necessary if there are problems with the local Python
144HTTP client or proxy configuration, but the Git binary works.
145
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800146The --fetch-submodules option enables fetching Git submodules
147of a project from server.
148
David Pursehousef2fad612015-01-29 14:36:28 +0900149The -c/--current-branch option can be used to only fetch objects that
150are on the branch specified by a project's revision.
151
David Pursehouseb1553542014-09-04 21:28:09 +0900152The --optimized-fetch option can be used to only fetch projects that
153are fixed to a sha1 revision if the sha1 revision does not already
154exist locally.
155
David Pursehouse74cfd272015-10-14 10:50:15 +0900156The --prune option can be used to remove any refs that no longer
157exist on the remote.
158
Shawn O. Pearceeb7af872009-04-21 08:02:04 -0700159SSH Connections
160---------------
161
162If at least one project remote URL uses an SSH connection (ssh://,
163git+ssh://, or user@host:path syntax) repo will automatically
164enable the SSH ControlMaster option when connecting to that host.
165This feature permits other projects in the same '%prog' session to
166reuse the same SSH tunnel, saving connection setup overheads.
167
168To disable this behavior on UNIX platforms, set the GIT_SSH
169environment variable to 'ssh'. For example:
170
171 export GIT_SSH=ssh
172 %prog
173
174Compatibility
175~~~~~~~~~~~~~
176
177This feature is automatically disabled on Windows, due to the lack
178of UNIX domain socket support.
179
180This feature is not compatible with url.insteadof rewrites in the
181user's ~/.gitconfig. '%prog' is currently not able to perform the
182rewrite early enough to establish the ControlMaster tunnel.
183
184If the remote SSH daemon is Gerrit Code Review, version 2.0.10 or
185later is required to fix a server side protocol bug.
186
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700187"""
188
Nico Sallembien6623b212010-05-11 12:57:01 -0700189 def _Options(self, p, show_smart=True):
Torne (Richard Coles)7bdbde72012-12-05 10:58:06 +0000190 try:
191 self.jobs = self.manifest.default.sync_j
192 except ManifestParseError:
193 self.jobs = 1
Shawn O. Pearce6392c872011-09-22 17:44:31 -0700194
Andrei Warkentin5df6de02010-07-02 17:58:31 -0500195 p.add_option('-f', '--force-broken',
196 dest='force_broken', action='store_true',
197 help="continue sync even if a project fails to sync")
Kevin Degiabaa7f32014-11-12 11:27:45 -0700198 p.add_option('--force-sync',
199 dest='force_sync', action='store_true',
200 help="overwrite an existing git directory if it needs to "
201 "point to a different object directory. WARNING: this "
202 "may cause loss of data")
David Pursehouse8f62fb72012-11-14 12:09:38 +0900203 p.add_option('-l', '--local-only',
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700204 dest='local_only', action='store_true',
205 help="only update working tree, don't fetch")
David Pursehouse8f62fb72012-11-14 12:09:38 +0900206 p.add_option('-n', '--network-only',
Shawn O. Pearce96fdcef2009-04-10 16:29:20 -0700207 dest='network_only', action='store_true',
208 help="fetch only, don't update working tree")
David Pursehouse8f62fb72012-11-14 12:09:38 +0900209 p.add_option('-d', '--detach',
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700210 dest='detach_head', action='store_true',
211 help='detach projects back to manifest revision')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900212 p.add_option('-c', '--current-branch',
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -0700213 dest='current_branch_only', action='store_true',
214 help='fetch only current branch from server')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900215 p.add_option('-q', '--quiet',
Shawn O. Pearce16614f82010-10-29 12:05:43 -0700216 dest='quiet', action='store_true',
217 help='be more quiet')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900218 p.add_option('-j', '--jobs',
Roy Lee18afd7f2010-05-09 04:32:08 +0800219 dest='jobs', action='store', type='int',
Shawn O. Pearce6392c872011-09-22 17:44:31 -0700220 help="projects to fetch simultaneously (default %d)" % self.jobs)
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500221 p.add_option('-m', '--manifest-name',
222 dest='manifest_name',
223 help='temporary manifest to use for this sync', metavar='NAME.xml')
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -0700224 p.add_option('--no-clone-bundle',
225 dest='no_clone_bundle', action='store_true',
226 help='disable use of /clone.bundle on HTTP/HTTPS')
Conley Owens8d070cf2012-11-06 13:14:31 -0800227 p.add_option('-u', '--manifest-server-username', action='store',
228 dest='manifest_server_username',
229 help='username to authenticate with the manifest server')
230 p.add_option('-p', '--manifest-server-password', action='store',
231 dest='manifest_server_password',
232 help='password to authenticate with the manifest server')
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800233 p.add_option('--fetch-submodules',
234 dest='fetch_submodules', action='store_true',
235 help='fetch submodules from server')
Mitchel Humpherys597868b2012-10-29 10:18:34 -0700236 p.add_option('--no-tags',
237 dest='no_tags', action='store_true',
238 help="don't fetch tags")
David Pursehouseb1553542014-09-04 21:28:09 +0900239 p.add_option('--optimized-fetch',
240 dest='optimized_fetch', action='store_true',
241 help='only fetch projects fixed to sha1 if revision does not exist locally')
David Pursehouse74cfd272015-10-14 10:50:15 +0900242 p.add_option('--prune', dest='prune', action='store_true',
243 help='delete refs that no longer exist on the remote')
Nico Sallembien6623b212010-05-11 12:57:01 -0700244 if show_smart:
245 p.add_option('-s', '--smart-sync',
246 dest='smart_sync', action='store_true',
David Pursehouse79fba682016-04-13 18:03:00 +0900247 help='smart sync using manifest from the latest known good build')
Victor Boivie08c880d2011-04-19 10:32:52 +0200248 p.add_option('-t', '--smart-tag',
249 dest='smart_tag', action='store',
250 help='smart sync using manifest from a known tag')
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700251
Shawn O. Pearcefd89b672009-04-18 11:28:57 -0700252 g = p.add_option_group('repo Version options')
253 g.add_option('--no-repo-verify',
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700254 dest='no_repo_verify', action='store_true',
255 help='do not verify repo source code')
Shawn O. Pearcefd89b672009-04-18 11:28:57 -0700256 g.add_option('--repo-upgraded',
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800257 dest='repo_upgraded', action='store_true',
Shawn O. Pearce2a1ccb22009-04-10 16:51:53 -0700258 help=SUPPRESS_HELP)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700259
natalie.chen451dca82015-12-09 21:24:16 +0800260 def _FetchProjectList(self, opt, projects, lock, fetched, pm, sem, err_event):
David Pursehousec1b86a22012-11-14 11:36:51 +0900261 """Main function of the fetch threads when jobs are > 1.
Doug Andersonfc06ced2011-03-16 15:49:18 -0700262
David James8d201162013-10-11 17:03:19 -0700263 Delegates most of the work to _FetchHelper.
264
265 Args:
266 opt: Program options returned from optparse. See _Options().
267 projects: Projects to fetch.
David James89ece422014-01-09 18:51:58 -0800268 *args, **kwargs: Remaining arguments to pass to _FetchHelper. See the
David James8d201162013-10-11 17:03:19 -0700269 _FetchHelper docstring for details.
270 """
natalie.chen451dca82015-12-09 21:24:16 +0800271 try:
272 for project in projects:
273 success = self._FetchHelper(opt, project, lock, fetched, pm, sem, err_event)
274 if not success and not opt.force_broken:
275 break
276 finally:
277 sem.release()
David James8d201162013-10-11 17:03:19 -0700278
279 def _FetchHelper(self, opt, project, lock, fetched, pm, sem, err_event):
280 """Fetch git objects for a single project.
281
David Pursehousec1b86a22012-11-14 11:36:51 +0900282 Args:
283 opt: Program options returned from optparse. See _Options().
284 project: Project object for the project to fetch.
285 lock: Lock for accessing objects that are shared amongst multiple
286 _FetchHelper() threads.
287 fetched: set object that we will add project.gitdir to when we're done
288 (with our lock held).
289 pm: Instance of a Project object. We will call pm.update() (with our
290 lock held).
291 sem: We'll release() this semaphore when we exit so that another thread
292 can be started up.
293 err_event: We'll set this event in the case of an error (after printing
294 out info about the error).
David James8d201162013-10-11 17:03:19 -0700295
296 Returns:
297 Whether the fetch was successful.
David Pursehousec1b86a22012-11-14 11:36:51 +0900298 """
299 # We'll set to true once we've locked the lock.
300 did_lock = False
Doug Andersonfc06ced2011-03-16 15:49:18 -0700301
Chirayu Desaifef4ae72013-04-12 14:54:32 +0530302 if not opt.quiet:
303 print('Fetching project %s' % project.name)
304
David Pursehousec1b86a22012-11-14 11:36:51 +0900305 # Encapsulate everything in a try/except/finally so that:
306 # - We always set err_event in the case of an exception.
307 # - We always make sure we call sem.release().
308 # - We always make sure we unlock the lock if we locked it.
309 try:
Doug Andersonfc06ced2011-03-16 15:49:18 -0700310 try:
David Pursehousec1b86a22012-11-14 11:36:51 +0900311 start = time.time()
312 success = project.Sync_NetworkHalf(
313 quiet=opt.quiet,
314 current_branch_only=opt.current_branch_only,
Kevin Degiabaa7f32014-11-12 11:27:45 -0700315 force_sync=opt.force_sync,
Mitchel Humpherys597868b2012-10-29 10:18:34 -0700316 clone_bundle=not opt.no_clone_bundle,
David Pursehouseb1553542014-09-04 21:28:09 +0900317 no_tags=opt.no_tags, archive=self.manifest.IsArchive,
David Pursehouse74cfd272015-10-14 10:50:15 +0900318 optimized_fetch=opt.optimized_fetch,
319 prune=opt.prune)
David Pursehousec1b86a22012-11-14 11:36:51 +0900320 self._fetch_times.Set(project, time.time() - start)
Doug Andersonfc06ced2011-03-16 15:49:18 -0700321
David Pursehousec1b86a22012-11-14 11:36:51 +0900322 # Lock around all the rest of the code, since printing, updating a set
323 # and Progress.update() are not thread safe.
324 lock.acquire()
325 did_lock = True
Doug Andersonfc06ced2011-03-16 15:49:18 -0700326
David Pursehousec1b86a22012-11-14 11:36:51 +0900327 if not success:
Hu Xiuyune9becc02015-11-25 15:52:26 +0800328 err_event.set()
David Pursehousec1b86a22012-11-14 11:36:51 +0900329 print('error: Cannot fetch %s' % project.name, file=sys.stderr)
330 if opt.force_broken:
331 print('warn: --force-broken, continuing to sync',
332 file=sys.stderr)
333 else:
334 raise _FetchError()
Roy Lee18afd7f2010-05-09 04:32:08 +0800335
David Pursehousec1b86a22012-11-14 11:36:51 +0900336 fetched.add(project.gitdir)
337 pm.update()
338 except _FetchError:
Hu Xiuyune9becc02015-11-25 15:52:26 +0800339 pass
Dan Sandlerc5cd4332015-07-31 09:37:53 -0400340 except Exception as e:
341 print('error: Cannot fetch %s (%s: %s)' \
342 % (project.name, type(e).__name__, str(e)), file=sys.stderr)
David Pursehousec1b86a22012-11-14 11:36:51 +0900343 err_event.set()
344 raise
345 finally:
346 if did_lock:
347 lock.release()
natalie.chen451dca82015-12-09 21:24:16 +0800348 #sem.release()
Roy Lee18afd7f2010-05-09 04:32:08 +0800349
David James8d201162013-10-11 17:03:19 -0700350 return success
351
Shawn O. Pearce16614f82010-10-29 12:05:43 -0700352 def _Fetch(self, projects, opt):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700353 fetched = set()
David James89ece422014-01-09 18:51:58 -0800354 lock = _threading.Lock()
Shawn O. Pearce68194f42009-04-10 16:48:52 -0700355 pm = Progress('Fetching projects', len(projects))
Roy Lee18afd7f2010-05-09 04:32:08 +0800356
David James89ece422014-01-09 18:51:58 -0800357 objdir_project_map = dict()
358 for project in projects:
359 objdir_project_map.setdefault(project.objdir, []).append(project)
David James8d201162013-10-11 17:03:19 -0700360
David James89ece422014-01-09 18:51:58 -0800361 threads = set()
362 sem = _threading.Semaphore(self.jobs)
363 err_event = _threading.Event()
364 for project_list in objdir_project_map.values():
365 # Check for any errors before running any more tasks.
366 # ...we'll let existing threads finish, though.
367 if err_event.isSet() and not opt.force_broken:
368 break
Doug Andersonfc06ced2011-03-16 15:49:18 -0700369
David James89ece422014-01-09 18:51:58 -0800370 sem.acquire()
371 kwargs = dict(opt=opt,
372 projects=project_list,
373 lock=lock,
374 fetched=fetched,
375 pm=pm,
376 sem=sem,
377 err_event=err_event)
378 if self.jobs > 1:
David James8d201162013-10-11 17:03:19 -0700379 t = _threading.Thread(target = self._FetchProjectList,
David James89ece422014-01-09 18:51:58 -0800380 kwargs = kwargs)
David 'Digit' Turnere2126652012-09-05 10:35:06 +0200381 # Ensure that Ctrl-C will not freeze the repo process.
382 t.daemon = True
Roy Lee18afd7f2010-05-09 04:32:08 +0800383 threads.add(t)
384 t.start()
David James89ece422014-01-09 18:51:58 -0800385 else:
386 self._FetchProjectList(**kwargs)
Roy Lee18afd7f2010-05-09 04:32:08 +0800387
David James89ece422014-01-09 18:51:58 -0800388 for t in threads:
389 t.join()
Roy Lee18afd7f2010-05-09 04:32:08 +0800390
David James89ece422014-01-09 18:51:58 -0800391 # If we saw an error, exit with code 1 so that other scripts can check.
392 if err_event.isSet():
393 print('\nerror: Exited sync due to fetch errors', file=sys.stderr)
394 sys.exit(1)
Doug Andersonfc06ced2011-03-16 15:49:18 -0700395
Shawn O. Pearce68194f42009-04-10 16:48:52 -0700396 pm.end()
Dave Borowitz67700e92012-10-23 15:00:54 -0700397 self._fetch_times.Save()
Dave Borowitz18857212012-10-23 17:02:59 -0700398
Julien Campergue335f5ef2013-10-16 11:02:35 +0200399 if not self.manifest.IsArchive:
400 self._GCProjects(projects)
401
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700402 return fetched
403
Dave Borowitz18857212012-10-23 17:02:59 -0700404 def _GCProjects(self, projects):
Gabe Black2ff30292014-10-09 17:54:35 -0700405 gc_gitdirs = {}
David James8d201162013-10-11 17:03:19 -0700406 for project in projects:
Gabe Black2ff30292014-10-09 17:54:35 -0700407 if len(project.manifest.GetProjectsWithName(project.name)) > 1:
408 print('Shared project %s found, disabling pruning.' % project.name)
409 project.bare_git.config('--replace-all', 'gc.pruneExpire', 'never')
410 gc_gitdirs[project.gitdir] = project.bare_git
David James8d201162013-10-11 17:03:19 -0700411
Dave Borowitze2152672012-10-31 12:24:38 -0700412 has_dash_c = git_require((1, 7, 2))
413 if multiprocessing and has_dash_c:
Dave Borowitz18857212012-10-23 17:02:59 -0700414 cpu_count = multiprocessing.cpu_count()
415 else:
416 cpu_count = 1
417 jobs = min(self.jobs, cpu_count)
418
419 if jobs < 2:
Gabe Black2ff30292014-10-09 17:54:35 -0700420 for bare_git in gc_gitdirs.values():
David James8d201162013-10-11 17:03:19 -0700421 bare_git.gc('--auto')
Dave Borowitz18857212012-10-23 17:02:59 -0700422 return
423
424 config = {'pack.threads': cpu_count / jobs if cpu_count > jobs else 1}
425
426 threads = set()
427 sem = _threading.Semaphore(jobs)
428 err_event = _threading.Event()
429
David James8d201162013-10-11 17:03:19 -0700430 def GC(bare_git):
Dave Borowitz18857212012-10-23 17:02:59 -0700431 try:
432 try:
David James8d201162013-10-11 17:03:19 -0700433 bare_git.gc('--auto', config=config)
Dave Borowitz18857212012-10-23 17:02:59 -0700434 except GitError:
435 err_event.set()
436 except:
437 err_event.set()
438 raise
439 finally:
440 sem.release()
441
Gabe Black2ff30292014-10-09 17:54:35 -0700442 for bare_git in gc_gitdirs.values():
Dave Borowitz18857212012-10-23 17:02:59 -0700443 if err_event.isSet():
444 break
445 sem.acquire()
David James8d201162013-10-11 17:03:19 -0700446 t = _threading.Thread(target=GC, args=(bare_git,))
Dave Borowitz18857212012-10-23 17:02:59 -0700447 t.daemon = True
448 threads.add(t)
449 t.start()
450
451 for t in threads:
452 t.join()
453
454 if err_event.isSet():
Sarah Owenscecd1d82012-11-01 22:59:27 -0700455 print('\nerror: Exited sync due to gc errors', file=sys.stderr)
Dave Borowitz18857212012-10-23 17:02:59 -0700456 sys.exit(1)
457
Tim Kilbourn07669002013-03-08 15:02:49 -0800458 def _ReloadManifest(self, manifest_name=None):
459 if manifest_name:
460 # Override calls _Unload already
461 self.manifest.Override(manifest_name)
462 else:
463 self.manifest._Unload()
464
Dan Willemsen43507912016-09-01 16:26:02 -0700465 def _DeleteProject(self, path):
466 print('Deleting obsolete path %s' % path, file=sys.stderr)
467
468 # Delete the .git directory first, so we're less likely to have a partially
469 # working git repository around. There shouldn't be any git projects here,
470 # so rmtree works.
471 try:
472 shutil.rmtree(os.path.join(path, '.git'))
473 except OSError:
474 print('Failed to remove %s' % os.path.join(path, '.git'), file=sys.stderr)
475 print('error: Failed to delete obsolete path %s' % path, file=sys.stderr)
476 print(' remove manually, then run sync again', file=sys.stderr)
477 return -1
478
479 # Delete everything under the worktree, except for directories that contain
480 # another git project
481 dirs_to_remove = []
482 failed = False
483 for root, dirs, files in os.walk(path):
484 for f in files:
485 try:
486 os.remove(os.path.join(root, f))
487 except OSError:
488 print('Failed to remove %s' % os.path.join(root, f), file=sys.stderr)
489 failed = True
490 dirs[:] = [d for d in dirs
491 if not os.path.lexists(os.path.join(root, d, '.git'))]
492 dirs_to_remove += [os.path.join(root, d) for d in dirs
493 if os.path.join(root, d) not in dirs_to_remove]
494 for d in reversed(dirs_to_remove):
Dan Willemseneceeb1b2016-09-25 18:24:27 -0700495 if os.path.islink(d):
496 try:
497 os.remove(d)
498 except OSError:
499 print('Failed to remove %s' % os.path.join(root, d), file=sys.stderr)
500 failed = True
501 elif len(os.listdir(d)) == 0:
Dan Willemsen43507912016-09-01 16:26:02 -0700502 try:
503 os.rmdir(d)
504 except OSError:
505 print('Failed to remove %s' % os.path.join(root, d), file=sys.stderr)
506 failed = True
507 continue
508 if failed:
509 print('error: Failed to delete obsolete path %s' % path, file=sys.stderr)
510 print(' remove manually, then run sync again', file=sys.stderr)
511 return -1
512
513 # Try deleting parent dirs if they are empty
514 project_dir = path
515 while project_dir != self.manifest.topdir:
516 if len(os.listdir(project_dir)) == 0:
517 os.rmdir(project_dir)
518 else:
519 break
520 project_dir = os.path.dirname(project_dir)
521
522 return 0
523
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700524 def UpdateProjectList(self):
525 new_project_paths = []
Colin Cross5acde752012-03-28 20:15:45 -0700526 for project in self.GetProjects(None, missing_ok=True):
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700527 if project.relpath:
528 new_project_paths.append(project.relpath)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700529 file_name = 'project.list'
530 file_path = os.path.join(self.manifest.repodir, file_name)
531 old_project_paths = []
532
533 if os.path.exists(file_path):
534 fd = open(file_path, 'r')
535 try:
536 old_project_paths = fd.read().split('\n')
537 finally:
538 fd.close()
539 for path in old_project_paths:
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700540 if not path:
541 continue
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700542 if path not in new_project_paths:
David Pursehouse8a68ff92012-09-24 12:15:13 +0900543 # If the path has already been deleted, we don't need to do it
Dan Willemsen43507912016-09-01 16:26:02 -0700544 gitdir = os.path.join(self.manifest.topdir, path, '.git')
545 if os.path.exists(gitdir):
David Pursehousec1b86a22012-11-14 11:36:51 +0900546 project = Project(
547 manifest = self.manifest,
548 name = path,
549 remote = RemoteSpec('origin'),
David James8d201162013-10-11 17:03:19 -0700550 gitdir = gitdir,
551 objdir = gitdir,
David Pursehousec1b86a22012-11-14 11:36:51 +0900552 worktree = os.path.join(self.manifest.topdir, path),
553 relpath = path,
554 revisionExpr = 'HEAD',
555 revisionId = None,
556 groups = None)
Anthonyf3fdf822009-09-26 13:38:52 -0400557
David Pursehousec1b86a22012-11-14 11:36:51 +0900558 if project.IsDirty():
David Pursehouse2f9e7e42013-03-05 17:26:46 +0900559 print('error: Cannot remove project "%s": uncommitted changes '
David Pursehousec1b86a22012-11-14 11:36:51 +0900560 'are present' % project.relpath, file=sys.stderr)
561 print(' commit changes, then run sync again',
562 file=sys.stderr)
563 return -1
564 else:
natalie.chen507a9362018-02-14 17:52:40 +0800565 if self._DeleteProject(project.worktree) == 0:
566 project.RemoveOldCopyAndLinkFiles(os.path.join(self.manifest.repodir, 'projects', '%s.git' % path))
567 else:
568 return -1
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700569
Shawn O. Pearce9fb29ce2009-06-04 20:41:02 -0700570 new_project_paths.sort()
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700571 fd = open(file_path, 'w')
572 try:
573 fd.write('\n'.join(new_project_paths))
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700574 fd.write('\n')
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700575 finally:
576 fd.close()
577 return 0
578
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700579 def Execute(self, opt, args):
Roy Lee18afd7f2010-05-09 04:32:08 +0800580 if opt.jobs:
581 self.jobs = opt.jobs
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -0700582 if self.jobs > 1:
583 soft_limit, _ = _rlimit_nofile()
584 self.jobs = min(self.jobs, (soft_limit - 5) / 3)
585
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700586 if opt.network_only and opt.detach_head:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700587 print('error: cannot combine -n and -d', file=sys.stderr)
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700588 sys.exit(1)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700589 if opt.network_only and opt.local_only:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700590 print('error: cannot combine -n and -l', file=sys.stderr)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700591 sys.exit(1)
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500592 if opt.manifest_name and opt.smart_sync:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700593 print('error: cannot combine -m and -s', file=sys.stderr)
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500594 sys.exit(1)
595 if opt.manifest_name and opt.smart_tag:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700596 print('error: cannot combine -m and -t', file=sys.stderr)
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500597 sys.exit(1)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900598 if opt.manifest_server_username or opt.manifest_server_password:
599 if not (opt.smart_sync or opt.smart_tag):
Sarah Owenscecd1d82012-11-01 22:59:27 -0700600 print('error: -u and -p may only be combined with -s or -t',
601 file=sys.stderr)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900602 sys.exit(1)
603 if None in [opt.manifest_server_username, opt.manifest_server_password]:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700604 print('error: both -u and -p must be given', file=sys.stderr)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900605 sys.exit(1)
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500606
607 if opt.manifest_name:
608 self.manifest.Override(opt.manifest_name)
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700609
Chirayu Desaia892b102013-06-11 14:18:46 +0530610 manifest_name = opt.manifest_name
David Pursehouse59b41742015-05-07 14:36:09 +0900611 smart_sync_manifest_name = "smart_sync_override.xml"
612 smart_sync_manifest_path = os.path.join(
613 self.manifest.manifestProject.worktree, smart_sync_manifest_name)
Chirayu Desaia892b102013-06-11 14:18:46 +0530614
Victor Boivie08c880d2011-04-19 10:32:52 +0200615 if opt.smart_sync or opt.smart_tag:
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700616 if not self.manifest.manifest_server:
David Pursehouse2f9e7e42013-03-05 17:26:46 +0900617 print('error: cannot smart sync: no manifest server defined in '
Sarah Owenscecd1d82012-11-01 22:59:27 -0700618 'manifest', file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700619 sys.exit(1)
David Pursehouse86d973d2012-08-24 10:21:02 +0900620
621 manifest_server = self.manifest.manifest_server
David Pursehousefb99c712013-09-25 11:09:34 +0900622 if not opt.quiet:
623 print('Using manifest server %s' % manifest_server)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900624
David Pursehouse86d973d2012-08-24 10:21:02 +0900625 if not '@' in manifest_server:
David Pursehousecf76b1b2012-09-14 10:31:42 +0900626 username = None
627 password = None
628 if opt.manifest_server_username and opt.manifest_server_password:
629 username = opt.manifest_server_username
630 password = opt.manifest_server_password
David Pursehouse86d973d2012-08-24 10:21:02 +0900631 else:
632 try:
David Pursehousecf76b1b2012-09-14 10:31:42 +0900633 info = netrc.netrc()
634 except IOError:
David Pursehouseba7bc732015-08-20 16:55:42 +0900635 # .netrc file does not exist or could not be opened
636 pass
David Pursehouse86d973d2012-08-24 10:21:02 +0900637 else:
David Pursehousecf76b1b2012-09-14 10:31:42 +0900638 try:
Chirayu Desaidb2ad9d2013-06-11 13:42:25 +0530639 parse_result = urllib.parse.urlparse(manifest_server)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900640 if parse_result.hostname:
David Pursehouseba7bc732015-08-20 16:55:42 +0900641 auth = info.authenticators(parse_result.hostname)
642 if auth:
643 username, _account, password = auth
644 else:
645 print('No credentials found for %s in .netrc'
646 % parse_result.hostname, file=sys.stderr)
Sarah Owensa5be53f2012-09-09 15:37:57 -0700647 except netrc.NetrcParseError as e:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700648 print('Error parsing .netrc file: %s' % e, file=sys.stderr)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900649
650 if (username and password):
651 manifest_server = manifest_server.replace('://', '://%s:%s@' %
natalie.chen3e01e3a2016-07-20 10:45:57 +0800652 (username, url_quote(password, safe='')),
David Pursehousecf76b1b2012-09-14 10:31:42 +0900653 1)
David Pursehouse86d973d2012-08-24 10:21:02 +0900654
Dan Willemsen0745bb22015-08-17 13:41:45 -0700655 transport = PersistentTransport(manifest_server)
656 if manifest_server.startswith('persistent-'):
657 manifest_server = manifest_server[len('persistent-'):]
658
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700659 try:
Dan Willemsen0745bb22015-08-17 13:41:45 -0700660 server = xmlrpc.client.Server(manifest_server, transport=transport)
Victor Boivie08c880d2011-04-19 10:32:52 +0200661 if opt.smart_sync:
662 p = self.manifest.manifestProject
663 b = p.GetBranch(p.CurrentBranch)
664 branch = b.merge
665 if branch.startswith(R_HEADS):
666 branch = branch[len(R_HEADS):]
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700667
Victor Boivie08c880d2011-04-19 10:32:52 +0200668 env = os.environ.copy()
Jeff Davidson5cf16602014-10-02 10:13:38 -0700669 if 'SYNC_TARGET' in env:
670 target = env['SYNC_TARGET']
671 [success, manifest_str] = server.GetApprovedManifest(branch, target)
672 elif 'TARGET_PRODUCT' in env and 'TARGET_BUILD_VARIANT' in env:
Victor Boivie08c880d2011-04-19 10:32:52 +0200673 target = '%s-%s' % (env['TARGET_PRODUCT'],
674 env['TARGET_BUILD_VARIANT'])
675 [success, manifest_str] = server.GetApprovedManifest(branch, target)
676 else:
677 [success, manifest_str] = server.GetApprovedManifest(branch)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700678 else:
Victor Boivie08c880d2011-04-19 10:32:52 +0200679 assert(opt.smart_tag)
680 [success, manifest_str] = server.GetManifest(opt.smart_tag)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700681
682 if success:
David Pursehouse59b41742015-05-07 14:36:09 +0900683 manifest_name = smart_sync_manifest_name
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700684 try:
David Pursehouse59b41742015-05-07 14:36:09 +0900685 f = open(smart_sync_manifest_path, 'w')
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700686 try:
687 f.write(manifest_str)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700688 finally:
689 f.close()
David Pursehouse727cc3e2015-05-07 14:16:49 +0900690 except IOError as e:
691 print('error: cannot write manifest to %s:\n%s'
David Pursehouse59b41742015-05-07 14:36:09 +0900692 % (smart_sync_manifest_path, e),
Sarah Owenscecd1d82012-11-01 22:59:27 -0700693 file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700694 sys.exit(1)
Victor Boivie53a6c5d2013-03-19 12:20:52 +0100695 self._ReloadManifest(manifest_name)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700696 else:
David Pursehouse351fe2c2013-09-25 17:54:26 +0900697 print('error: manifest server RPC call failed: %s' %
698 manifest_str, file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700699 sys.exit(1)
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530700 except (socket.error, IOError, xmlrpc.client.Fault) as e:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700701 print('error: cannot connect to manifest server %s:\n%s'
702 % (self.manifest.manifest_server, e), file=sys.stderr)
David Pursehousebd489c42012-08-23 10:21:26 +0900703 sys.exit(1)
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530704 except xmlrpc.client.ProtocolError as e:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700705 print('error: cannot connect to manifest server %s:\n%d %s'
706 % (self.manifest.manifest_server, e.errcode, e.errmsg),
707 file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700708 sys.exit(1)
David Pursehouse59b41742015-05-07 14:36:09 +0900709 else: # Not smart sync or smart tag mode
710 if os.path.isfile(smart_sync_manifest_path):
711 try:
712 os.remove(smart_sync_manifest_path)
713 except OSError as e:
714 print('error: failed to remove existing smart sync override manifest: %s' %
715 e, file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700716
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700717 rp = self.manifest.repoProject
718 rp.PreSync()
719
720 mp = self.manifest.manifestProject
721 mp.PreSync()
722
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800723 if opt.repo_upgraded:
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -0700724 _PostRepoUpgrade(self.manifest, quiet=opt.quiet)
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800725
Nico Sallembien9bb18162009-12-07 15:38:01 -0800726 if not opt.local_only:
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -0700727 mp.Sync_NetworkHalf(quiet=opt.quiet,
Mitchel Humpherys597868b2012-10-29 10:18:34 -0700728 current_branch_only=opt.current_branch_only,
David Pursehouseb1553542014-09-04 21:28:09 +0900729 no_tags=opt.no_tags,
730 optimized_fetch=opt.optimized_fetch)
Nico Sallembien9bb18162009-12-07 15:38:01 -0800731
732 if mp.HasChanges:
733 syncbuf = SyncBuffer(mp.config)
734 mp.Sync_LocalHalf(syncbuf)
735 if not syncbuf.Finish():
736 sys.exit(1)
Victor Boivie53a6c5d2013-03-19 12:20:52 +0100737 self._ReloadManifest(manifest_name)
Shawn O. Pearcec4657962011-09-26 09:08:01 -0700738 if opt.jobs is None:
739 self.jobs = self.manifest.default.sync_j
Simran Basib9a1b732015-08-20 12:19:28 -0700740
Simran Basib9a1b732015-08-20 12:19:28 -0700741 if self.gitc_manifest:
742 gitc_manifest_projects = self.GetProjects(args,
Simran Basib9a1b732015-08-20 12:19:28 -0700743 missing_ok=True)
744 gitc_projects = []
745 opened_projects = []
746 for project in gitc_manifest_projects:
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700747 if project.relpath in self.gitc_manifest.paths and \
748 self.gitc_manifest.paths[project.relpath].old_revision:
749 opened_projects.append(project.relpath)
Simran Basib9a1b732015-08-20 12:19:28 -0700750 else:
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700751 gitc_projects.append(project.relpath)
Simran Basib9a1b732015-08-20 12:19:28 -0700752
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700753 if not args:
754 gitc_projects = None
755
756 if gitc_projects != [] and not opt.local_only:
Simran Basib9a1b732015-08-20 12:19:28 -0700757 print('Updating GITC client: %s' % self.gitc_manifest.gitc_client_name)
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700758 manifest = GitcManifest(self.repodir, self.gitc_manifest.gitc_client_name)
759 if manifest_name:
760 manifest.Override(manifest_name)
761 else:
762 manifest.Override(self.manifest.manifestFile)
763 gitc_utils.generate_gitc_manifest(self.gitc_manifest,
764 manifest,
Simran Basib9a1b732015-08-20 12:19:28 -0700765 gitc_projects)
766 print('GITC client successfully synced.')
767
768 # The opened projects need to be synced as normal, therefore we
769 # generate a new args list to represent the opened projects.
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700770 # TODO: make this more reliable -- if there's a project name/path overlap,
771 # this may choose the wrong project.
772 args = [os.path.relpath(self.manifest.paths[p].worktree, os.getcwd())
773 for p in opened_projects]
Simran Basib9a1b732015-08-20 12:19:28 -0700774 if not args:
775 return
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800776 all_projects = self.GetProjects(args,
777 missing_ok=True,
778 submodules_ok=opt.fetch_submodules)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700779
Dave Borowitz67700e92012-10-23 15:00:54 -0700780 self._fetch_times = _FetchTimes(self.manifest)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700781 if not opt.local_only:
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700782 to_fetch = []
783 now = time.time()
Dave Borowitz67700e92012-10-23 15:00:54 -0700784 if _ONE_DAY_S <= (now - rp.LastFetch):
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700785 to_fetch.append(rp)
David Pursehouse8a68ff92012-09-24 12:15:13 +0900786 to_fetch.extend(all_projects)
Dave Borowitz67700e92012-10-23 15:00:54 -0700787 to_fetch.sort(key=self._fetch_times.Get, reverse=True)
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700788
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800789 fetched = self._Fetch(to_fetch, opt)
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700790 _PostRepoFetch(rp, opt.no_repo_verify)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700791 if opt.network_only:
792 # bail out now; the rest touches the working tree
793 return
794
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800795 # Iteratively fetch missing and/or nested unregistered submodules
796 previously_missing_set = set()
797 while True:
Victor Boivie53a6c5d2013-03-19 12:20:52 +0100798 self._ReloadManifest(manifest_name)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800799 all_projects = self.GetProjects(args,
800 missing_ok=True,
801 submodules_ok=opt.fetch_submodules)
802 missing = []
803 for project in all_projects:
804 if project.gitdir not in fetched:
805 missing.append(project)
806 if not missing:
807 break
808 # Stop us from non-stopped fetching actually-missing repos: If set of
809 # missing repos has not been changed from last fetch, we break.
810 missing_set = set(p.name for p in missing)
811 if previously_missing_set == missing_set:
812 break
813 previously_missing_set = missing_set
814 fetched.update(self._Fetch(missing, opt))
815
Julien Campergue335f5ef2013-10-16 11:02:35 +0200816 if self.manifest.IsMirror or self.manifest.IsArchive:
Shawn O. Pearcecd1d7ff2009-06-04 16:15:53 -0700817 # bail out now, we have no working tree
818 return
819
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700820 if self.UpdateProjectList():
821 sys.exit(1)
822
Shawn O. Pearce350cde42009-04-16 11:21:18 -0700823 syncbuf = SyncBuffer(mp.config,
824 detach_head = opt.detach_head)
David Pursehouse8a68ff92012-09-24 12:15:13 +0900825 pm = Progress('Syncing work tree', len(all_projects))
826 for project in all_projects:
Shawn O. Pearce68194f42009-04-10 16:48:52 -0700827 pm.update()
Shawn O. Pearcee284ad12008-11-04 07:37:10 -0800828 if project.worktree:
Kevin Degiabaa7f32014-11-12 11:27:45 -0700829 project.Sync_LocalHalf(syncbuf, force_sync=opt.force_sync)
Shawn O. Pearce68194f42009-04-10 16:48:52 -0700830 pm.end()
Sarah Owenscecd1d82012-11-01 22:59:27 -0700831 print(file=sys.stderr)
Shawn O. Pearce350cde42009-04-16 11:21:18 -0700832 if not syncbuf.Finish():
833 sys.exit(1)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700834
Doug Anderson2b8db3c2010-11-01 15:08:06 -0700835 # If there's a notice that's supposed to print at the end of the sync, print
836 # it now...
837 if self.manifest.notice:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700838 print(self.manifest.notice)
Doug Anderson2b8db3c2010-11-01 15:08:06 -0700839
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -0700840def _PostRepoUpgrade(manifest, quiet=False):
Conley Owens094cdbe2014-01-30 15:09:59 -0800841 wrapper = Wrapper()
Conley Owensc9129d92012-10-01 16:12:28 -0700842 if wrapper.NeedSetupGnuPG():
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -0700843 wrapper.SetupGnuPG(quiet)
Conley Owensf2fe2d92014-01-29 13:53:43 -0800844 for project in manifest.projects:
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700845 if project.Exists:
846 project.PostRepoUpgrade()
847
848def _PostRepoFetch(rp, no_repo_verify=False, verbose=False):
849 if rp.HasChanges:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700850 print('info: A new version of repo is available', file=sys.stderr)
851 print(file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700852 if no_repo_verify or _VerifyTag(rp):
Shawn O. Pearce350cde42009-04-16 11:21:18 -0700853 syncbuf = SyncBuffer(rp.config)
854 rp.Sync_LocalHalf(syncbuf)
855 if not syncbuf.Finish():
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700856 sys.exit(1)
Sarah Owenscecd1d82012-11-01 22:59:27 -0700857 print('info: Restarting repo with latest version', file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700858 raise RepoChangedException(['--repo-upgraded'])
859 else:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700860 print('warning: Skipped upgrade to unverified version', file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700861 else:
862 if verbose:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700863 print('repo version %s is current' % rp.work_git.describe(HEAD),
864 file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700865
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700866def _VerifyTag(project):
867 gpg_dir = os.path.expanduser('~/.repoconfig/gnupg')
868 if not os.path.exists(gpg_dir):
Sarah Owenscecd1d82012-11-01 22:59:27 -0700869 print('warning: GnuPG was not available during last "repo init"\n'
870 'warning: Cannot automatically authenticate repo."""',
871 file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700872 return True
873
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700874 try:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -0700875 cur = project.bare_git.describe(project.GetRevisionId())
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700876 except GitError:
877 cur = None
878
879 if not cur \
880 or re.compile(r'^.*-[0-9]{1,}-g[0-9a-f]{1,}$').match(cur):
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -0700881 rev = project.revisionExpr
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700882 if rev.startswith(R_HEADS):
883 rev = rev[len(R_HEADS):]
884
Sarah Owenscecd1d82012-11-01 22:59:27 -0700885 print(file=sys.stderr)
886 print("warning: project '%s' branch '%s' is not signed"
887 % (project.name, rev), file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700888 return False
889
Shawn O. Pearcef18cb762010-12-07 11:41:05 -0800890 env = os.environ.copy()
891 env['GIT_DIR'] = project.gitdir.encode()
892 env['GNUPGHOME'] = gpg_dir.encode()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700893
894 cmd = [GIT, 'tag', '-v', cur]
895 proc = subprocess.Popen(cmd,
896 stdout = subprocess.PIPE,
897 stderr = subprocess.PIPE,
898 env = env)
899 out = proc.stdout.read()
900 proc.stdout.close()
901
902 err = proc.stderr.read()
903 proc.stderr.close()
904
905 if proc.wait() != 0:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700906 print(file=sys.stderr)
907 print(out, file=sys.stderr)
908 print(err, file=sys.stderr)
909 print(file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700910 return False
911 return True
Dave Borowitz67700e92012-10-23 15:00:54 -0700912
913class _FetchTimes(object):
Dave Borowitzd9478582012-10-23 16:35:39 -0700914 _ALPHA = 0.5
915
Dave Borowitz67700e92012-10-23 15:00:54 -0700916 def __init__(self, manifest):
Anthony King85b24ac2014-05-06 15:57:48 +0100917 self._path = os.path.join(manifest.repodir, '.repo_fetchtimes.json')
Dave Borowitz67700e92012-10-23 15:00:54 -0700918 self._times = None
Dave Borowitzd9478582012-10-23 16:35:39 -0700919 self._seen = set()
Dave Borowitz67700e92012-10-23 15:00:54 -0700920
921 def Get(self, project):
922 self._Load()
923 return self._times.get(project.name, _ONE_DAY_S)
924
925 def Set(self, project, t):
Dave Borowitzd9478582012-10-23 16:35:39 -0700926 self._Load()
927 name = project.name
928 old = self._times.get(name, t)
929 self._seen.add(name)
930 a = self._ALPHA
931 self._times[name] = (a*t) + ((1-a) * old)
Dave Borowitz67700e92012-10-23 15:00:54 -0700932
933 def _Load(self):
934 if self._times is None:
935 try:
Anthony King85b24ac2014-05-06 15:57:48 +0100936 f = open(self._path)
Dave Borowitz67700e92012-10-23 15:00:54 -0700937 try:
Anthony King85b24ac2014-05-06 15:57:48 +0100938 self._times = json.load(f)
939 finally:
940 f.close()
941 except (IOError, ValueError):
942 try:
943 os.remove(self._path)
944 except OSError:
945 pass
946 self._times = {}
Dave Borowitz67700e92012-10-23 15:00:54 -0700947
948 def Save(self):
949 if self._times is None:
950 return
Dave Borowitzd9478582012-10-23 16:35:39 -0700951
952 to_delete = []
953 for name in self._times:
954 if name not in self._seen:
955 to_delete.append(name)
956 for name in to_delete:
957 del self._times[name]
958
Dave Borowitz67700e92012-10-23 15:00:54 -0700959 try:
Anthony King85b24ac2014-05-06 15:57:48 +0100960 f = open(self._path, 'w')
Dave Borowitz67700e92012-10-23 15:00:54 -0700961 try:
Anthony King85b24ac2014-05-06 15:57:48 +0100962 json.dump(self._times, f, indent=2)
963 finally:
964 f.close()
965 except (IOError, TypeError):
966 try:
967 os.remove(self._path)
968 except OSError:
969 pass
Dan Willemsen0745bb22015-08-17 13:41:45 -0700970
971# This is a replacement for xmlrpc.client.Transport using urllib2
972# and supporting persistent-http[s]. It cannot change hosts from
973# request to request like the normal transport, the real url
974# is passed during initialization.
975class PersistentTransport(xmlrpc.client.Transport):
976 def __init__(self, orig_host):
977 self.orig_host = orig_host
978
979 def request(self, host, handler, request_body, verbose=False):
980 with GetUrlCookieFile(self.orig_host, not verbose) as (cookiefile, proxy):
981 # Python doesn't understand cookies with the #HttpOnly_ prefix
982 # Since we're only using them for HTTP, copy the file temporarily,
983 # stripping those prefixes away.
Dan Willemsen3010e5b2015-08-20 10:09:20 -0700984 if cookiefile:
985 tmpcookiefile = tempfile.NamedTemporaryFile()
David Pursehouse4c5f74e2015-10-02 11:10:10 +0900986 tmpcookiefile.write("# HTTP Cookie File")
Dan Willemsen3010e5b2015-08-20 10:09:20 -0700987 try:
988 with open(cookiefile) as f:
989 for line in f:
990 if line.startswith("#HttpOnly_"):
991 line = line[len("#HttpOnly_"):]
992 tmpcookiefile.write(line)
993 tmpcookiefile.flush()
Dan Willemsen0745bb22015-08-17 13:41:45 -0700994
Dan Willemsen3010e5b2015-08-20 10:09:20 -0700995 cookiejar = cookielib.MozillaCookieJar(tmpcookiefile.name)
David Pursehouseb1ad2192015-09-30 10:35:43 +0900996 try:
997 cookiejar.load()
998 except cookielib.LoadError:
999 cookiejar = cookielib.CookieJar()
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001000 finally:
1001 tmpcookiefile.close()
1002 else:
1003 cookiejar = cookielib.CookieJar()
Dan Willemsen0745bb22015-08-17 13:41:45 -07001004
1005 proxyhandler = urllib.request.ProxyHandler
1006 if proxy:
1007 proxyhandler = urllib.request.ProxyHandler({
1008 "http": proxy,
1009 "https": proxy })
1010
1011 opener = urllib.request.build_opener(
1012 urllib.request.HTTPCookieProcessor(cookiejar),
1013 proxyhandler)
1014
1015 url = urllib.parse.urljoin(self.orig_host, handler)
1016 parse_results = urllib.parse.urlparse(url)
1017
1018 scheme = parse_results.scheme
1019 if scheme == 'persistent-http':
1020 scheme = 'http'
1021 if scheme == 'persistent-https':
1022 # If we're proxying through persistent-https, use http. The
1023 # proxy itself will do the https.
1024 if proxy:
1025 scheme = 'http'
1026 else:
1027 scheme = 'https'
1028
1029 # Parse out any authentication information using the base class
1030 host, extra_headers, _ = self.get_host_info(parse_results.netloc)
1031
1032 url = urllib.parse.urlunparse((
1033 scheme,
1034 host,
1035 parse_results.path,
1036 parse_results.params,
1037 parse_results.query,
1038 parse_results.fragment))
1039
1040 request = urllib.request.Request(url, request_body)
1041 if extra_headers is not None:
1042 for (name, header) in extra_headers:
1043 request.add_header(name, header)
1044 request.add_header('Content-Type', 'text/xml')
1045 try:
1046 response = opener.open(request)
1047 except urllib.error.HTTPError as e:
1048 if e.code == 501:
1049 # We may have been redirected through a login process
1050 # but our POST turned into a GET. Retry.
1051 response = opener.open(request)
1052 else:
1053 raise
1054
1055 p, u = xmlrpc.client.getparser()
1056 while 1:
1057 data = response.read(1024)
1058 if not data:
1059 break
1060 p.feed(data)
1061 p.close()
1062 return u.close()
1063
1064 def close(self):
1065 pass
1066