blob: bbb166c0d893093096ce80e93bc48f4bac6e9348 [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
29from pyversion import is_python3
30if is_python3():
Dan Willemsen0745bb22015-08-17 13:41:45 -070031 import http.cookiejar as cookielib
32 import urllib.error
Chirayu Desai217ea7d2013-03-01 19:14:38 +053033 import urllib.parse
Dan Willemsen0745bb22015-08-17 13:41:45 -070034 import urllib.request
David Pursehouse59bbb582013-05-17 10:49:33 +090035 import xmlrpc.client
36else:
Dan Willemsen0745bb22015-08-17 13:41:45 -070037 import cookielib
Chirayu Desai217ea7d2013-03-01 19:14:38 +053038 import imp
Dan Willemsen0745bb22015-08-17 13:41:45 -070039 import urllib2
Chirayu Desai217ea7d2013-03-01 19:14:38 +053040 import urlparse
David Pursehouse59bbb582013-05-17 10:49:33 +090041 import xmlrpclib
Chirayu Desai217ea7d2013-03-01 19:14:38 +053042 urllib = imp.new_module('urllib')
Dan Willemsen0745bb22015-08-17 13:41:45 -070043 urllib.error = urllib2
Chirayu Desaidb2ad9d2013-06-11 13:42:25 +053044 urllib.parse = urlparse
Dan Willemsen0745bb22015-08-17 13:41:45 -070045 urllib.request = urllib2
Chirayu Desai217ea7d2013-03-01 19:14:38 +053046 xmlrpc = imp.new_module('xmlrpc')
47 xmlrpc.client = xmlrpclib
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070048
Roy Lee18afd7f2010-05-09 04:32:08 +080049try:
50 import threading as _threading
51except ImportError:
52 import dummy_threading as _threading
53
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -070054try:
55 import resource
56 def _rlimit_nofile():
57 return resource.getrlimit(resource.RLIMIT_NOFILE)
58except ImportError:
59 def _rlimit_nofile():
60 return (256, 256)
61
Dave Borowitz18857212012-10-23 17:02:59 -070062try:
63 import multiprocessing
64except ImportError:
65 multiprocessing = None
66
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
Shawn O. Pearce350cde42009-04-16 11:21:18 -070075from project import SyncBuffer
Shawn O. Pearce68194f42009-04-10 16:48:52 -070076from progress import Progress
Conley Owens094cdbe2014-01-30 15:09:59 -080077from wrapper import Wrapper
Dan Willemsen5ea32d12015-09-08 13:27:20 -070078from manifest_xml import GitcManifest
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070079
Dave Borowitz67700e92012-10-23 15:00:54 -070080_ONE_DAY_S = 24 * 60 * 60
81
Doug Andersonfc06ced2011-03-16 15:49:18 -070082class _FetchError(Exception):
83 """Internal error thrown in _FetchHelper() when we don't want stack trace."""
84 pass
85
Shawn O. Pearcec95583b2009-03-03 17:47:06 -080086class Sync(Command, MirrorSafeCommand):
Roy Lee18afd7f2010-05-09 04:32:08 +080087 jobs = 1
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070088 common = True
89 helpSummary = "Update working tree to the latest revision"
90 helpUsage = """
91%prog [<project>...]
92"""
93 helpDescription = """
94The '%prog' command synchronizes local project directories
95with the remote repositories specified in the manifest. If a local
96project does not yet exist, it will clone a new local directory from
97the remote repository and set up tracking branches as specified in
98the manifest. If the local project already exists, '%prog'
99will update the remote branches and rebase any new local changes
100on top of the new remote changes.
101
102'%prog' will synchronize all projects listed at the command
103line. Projects can be specified either by name, or by a relative
104or absolute path to the project's local directory. If no projects
105are specified, '%prog' will synchronize all projects listed in
106the manifest.
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700107
108The -d/--detach option can be used to switch specified projects
109back to the manifest revision. This option is especially helpful
110if the project is currently on a topic branch, but the manifest
111revision is temporarily needed.
Shawn O. Pearceeb7af872009-04-21 08:02:04 -0700112
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700113The -s/--smart-sync option can be used to sync to a known good
114build as specified by the manifest-server element in the current
Victor Boivie08c880d2011-04-19 10:32:52 +0200115manifest. The -t/--smart-tag option is similar and allows you to
116specify a custom tag/label.
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700117
David Pursehousecf76b1b2012-09-14 10:31:42 +0900118The -u/--manifest-server-username and -p/--manifest-server-password
119options can be used to specify a username and password to authenticate
120with the manifest server when using the -s or -t option.
121
122If -u and -p are not specified when using the -s or -t option, '%prog'
123will attempt to read authentication credentials for the manifest server
124from the user's .netrc file.
125
126'%prog' will not use authentication credentials from -u/-p or .netrc
127if the manifest server specified in the manifest file already includes
128credentials.
129
Andrei Warkentin5df6de02010-07-02 17:58:31 -0500130The -f/--force-broken option can be used to proceed with syncing
131other projects if a project sync fails.
132
Kevin Degiabaa7f32014-11-12 11:27:45 -0700133The --force-sync option can be used to overwrite existing git
134directories if they have previously been linked to a different
135object direcotry. WARNING: This may cause data to be lost since
136refs may be removed when overwriting.
137
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -0700138The --no-clone-bundle option disables any attempt to use
139$URL/clone.bundle to bootstrap a new Git repository from a
140resumeable bundle file on a content delivery network. This
141may be necessary if there are problems with the local Python
142HTTP client or proxy configuration, but the Git binary works.
143
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800144The --fetch-submodules option enables fetching Git submodules
145of a project from server.
146
David Pursehousef2fad612015-01-29 14:36:28 +0900147The -c/--current-branch option can be used to only fetch objects that
148are on the branch specified by a project's revision.
149
David Pursehouseb1553542014-09-04 21:28:09 +0900150The --optimized-fetch option can be used to only fetch projects that
151are fixed to a sha1 revision if the sha1 revision does not already
152exist locally.
153
David Pursehouse74cfd272015-10-14 10:50:15 +0900154The --prune option can be used to remove any refs that no longer
155exist on the remote.
156
Shawn O. Pearceeb7af872009-04-21 08:02:04 -0700157SSH Connections
158---------------
159
160If at least one project remote URL uses an SSH connection (ssh://,
161git+ssh://, or user@host:path syntax) repo will automatically
162enable the SSH ControlMaster option when connecting to that host.
163This feature permits other projects in the same '%prog' session to
164reuse the same SSH tunnel, saving connection setup overheads.
165
166To disable this behavior on UNIX platforms, set the GIT_SSH
167environment variable to 'ssh'. For example:
168
169 export GIT_SSH=ssh
170 %prog
171
172Compatibility
173~~~~~~~~~~~~~
174
175This feature is automatically disabled on Windows, due to the lack
176of UNIX domain socket support.
177
178This feature is not compatible with url.insteadof rewrites in the
179user's ~/.gitconfig. '%prog' is currently not able to perform the
180rewrite early enough to establish the ControlMaster tunnel.
181
182If the remote SSH daemon is Gerrit Code Review, version 2.0.10 or
183later is required to fix a server side protocol bug.
184
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700185"""
186
Nico Sallembien6623b212010-05-11 12:57:01 -0700187 def _Options(self, p, show_smart=True):
Torne (Richard Coles)7bdbde72012-12-05 10:58:06 +0000188 try:
189 self.jobs = self.manifest.default.sync_j
190 except ManifestParseError:
191 self.jobs = 1
Shawn O. Pearce6392c872011-09-22 17:44:31 -0700192
Andrei Warkentin5df6de02010-07-02 17:58:31 -0500193 p.add_option('-f', '--force-broken',
194 dest='force_broken', action='store_true',
195 help="continue sync even if a project fails to sync")
Kevin Degiabaa7f32014-11-12 11:27:45 -0700196 p.add_option('--force-sync',
197 dest='force_sync', action='store_true',
198 help="overwrite an existing git directory if it needs to "
199 "point to a different object directory. WARNING: this "
200 "may cause loss of data")
David Pursehouse8f62fb72012-11-14 12:09:38 +0900201 p.add_option('-l', '--local-only',
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700202 dest='local_only', action='store_true',
203 help="only update working tree, don't fetch")
David Pursehouse8f62fb72012-11-14 12:09:38 +0900204 p.add_option('-n', '--network-only',
Shawn O. Pearce96fdcef2009-04-10 16:29:20 -0700205 dest='network_only', action='store_true',
206 help="fetch only, don't update working tree")
David Pursehouse8f62fb72012-11-14 12:09:38 +0900207 p.add_option('-d', '--detach',
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700208 dest='detach_head', action='store_true',
209 help='detach projects back to manifest revision')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900210 p.add_option('-c', '--current-branch',
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -0700211 dest='current_branch_only', action='store_true',
212 help='fetch only current branch from server')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900213 p.add_option('-q', '--quiet',
Shawn O. Pearce16614f82010-10-29 12:05:43 -0700214 dest='quiet', action='store_true',
215 help='be more quiet')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900216 p.add_option('-j', '--jobs',
Roy Lee18afd7f2010-05-09 04:32:08 +0800217 dest='jobs', action='store', type='int',
Shawn O. Pearce6392c872011-09-22 17:44:31 -0700218 help="projects to fetch simultaneously (default %d)" % self.jobs)
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500219 p.add_option('-m', '--manifest-name',
220 dest='manifest_name',
221 help='temporary manifest to use for this sync', metavar='NAME.xml')
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -0700222 p.add_option('--no-clone-bundle',
223 dest='no_clone_bundle', action='store_true',
224 help='disable use of /clone.bundle on HTTP/HTTPS')
Conley Owens8d070cf2012-11-06 13:14:31 -0800225 p.add_option('-u', '--manifest-server-username', action='store',
226 dest='manifest_server_username',
227 help='username to authenticate with the manifest server')
228 p.add_option('-p', '--manifest-server-password', action='store',
229 dest='manifest_server_password',
230 help='password to authenticate with the manifest server')
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800231 p.add_option('--fetch-submodules',
232 dest='fetch_submodules', action='store_true',
233 help='fetch submodules from server')
Mitchel Humpherys597868b2012-10-29 10:18:34 -0700234 p.add_option('--no-tags',
235 dest='no_tags', action='store_true',
236 help="don't fetch tags")
David Pursehouseb1553542014-09-04 21:28:09 +0900237 p.add_option('--optimized-fetch',
238 dest='optimized_fetch', action='store_true',
239 help='only fetch projects fixed to sha1 if revision does not exist locally')
David Pursehouse74cfd272015-10-14 10:50:15 +0900240 p.add_option('--prune', dest='prune', action='store_true',
241 help='delete refs that no longer exist on the remote')
Nico Sallembien6623b212010-05-11 12:57:01 -0700242 if show_smart:
243 p.add_option('-s', '--smart-sync',
244 dest='smart_sync', action='store_true',
David Pursehouse79fba682016-04-13 18:03:00 +0900245 help='smart sync using manifest from the latest known good build')
Victor Boivie08c880d2011-04-19 10:32:52 +0200246 p.add_option('-t', '--smart-tag',
247 dest='smart_tag', action='store',
248 help='smart sync using manifest from a known tag')
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700249
Shawn O. Pearcefd89b672009-04-18 11:28:57 -0700250 g = p.add_option_group('repo Version options')
251 g.add_option('--no-repo-verify',
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700252 dest='no_repo_verify', action='store_true',
253 help='do not verify repo source code')
Shawn O. Pearcefd89b672009-04-18 11:28:57 -0700254 g.add_option('--repo-upgraded',
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800255 dest='repo_upgraded', action='store_true',
Shawn O. Pearce2a1ccb22009-04-10 16:51:53 -0700256 help=SUPPRESS_HELP)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700257
Andrew Wheeler7f1ccfb2016-06-17 16:51:07 -0500258 def _FetchProjectList(self, opt, projects, sem, *args, **kwargs):
David Pursehousec1b86a22012-11-14 11:36:51 +0900259 """Main function of the fetch threads when jobs are > 1.
Roy Lee18afd7f2010-05-09 04:32:08 +0800260
David James8d201162013-10-11 17:03:19 -0700261 Delegates most of the work to _FetchHelper.
262
263 Args:
264 opt: Program options returned from optparse. See _Options().
265 projects: Projects to fetch.
Andrew Wheeler7f1ccfb2016-06-17 16:51:07 -0500266 sem: We'll release() this semaphore when we exit so that another thread
267 can be started up.
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 """
Andrew Wheeler7f1ccfb2016-06-17 16:51:07 -0500271 try:
272 for project in projects:
273 success = self._FetchHelper(opt, project, *args, **kwargs)
274 if not success and not opt.force_broken:
275 break
276 finally:
277 sem.release()
David James8d201162013-10-11 17:03:19 -0700278
Andrew Wheeler7f1ccfb2016-06-17 16:51:07 -0500279 def _FetchHelper(self, opt, project, lock, fetched, pm, err_event):
David James8d201162013-10-11 17:03:19 -0700280 """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).
David Pursehousec1b86a22012-11-14 11:36:51 +0900291 err_event: We'll set this event in the case of an error (after printing
292 out info about the error).
David James8d201162013-10-11 17:03:19 -0700293
294 Returns:
295 Whether the fetch was successful.
David Pursehousec1b86a22012-11-14 11:36:51 +0900296 """
297 # We'll set to true once we've locked the lock.
298 did_lock = False
Doug Andersonfc06ced2011-03-16 15:49:18 -0700299
Chirayu Desaifef4ae72013-04-12 14:54:32 +0530300 if not opt.quiet:
301 print('Fetching project %s' % project.name)
302
David Pursehousec1b86a22012-11-14 11:36:51 +0900303 # Encapsulate everything in a try/except/finally so that:
304 # - We always set err_event in the case of an exception.
305 # - We always make sure we call sem.release().
306 # - We always make sure we unlock the lock if we locked it.
307 try:
Doug Andersonfc06ced2011-03-16 15:49:18 -0700308 try:
David Pursehousec1b86a22012-11-14 11:36:51 +0900309 start = time.time()
310 success = project.Sync_NetworkHalf(
311 quiet=opt.quiet,
312 current_branch_only=opt.current_branch_only,
Kevin Degiabaa7f32014-11-12 11:27:45 -0700313 force_sync=opt.force_sync,
Mitchel Humpherys597868b2012-10-29 10:18:34 -0700314 clone_bundle=not opt.no_clone_bundle,
David Pursehouseb1553542014-09-04 21:28:09 +0900315 no_tags=opt.no_tags, archive=self.manifest.IsArchive,
David Pursehouse74cfd272015-10-14 10:50:15 +0900316 optimized_fetch=opt.optimized_fetch,
317 prune=opt.prune)
David Pursehousec1b86a22012-11-14 11:36:51 +0900318 self._fetch_times.Set(project, time.time() - start)
Doug Andersonfc06ced2011-03-16 15:49:18 -0700319
David Pursehousec1b86a22012-11-14 11:36:51 +0900320 # Lock around all the rest of the code, since printing, updating a set
321 # and Progress.update() are not thread safe.
322 lock.acquire()
323 did_lock = True
Doug Andersonfc06ced2011-03-16 15:49:18 -0700324
David Pursehousec1b86a22012-11-14 11:36:51 +0900325 if not success:
Hu Xiuyune9becc02015-11-25 15:52:26 +0800326 err_event.set()
David Pursehousec1b86a22012-11-14 11:36:51 +0900327 print('error: Cannot fetch %s' % project.name, file=sys.stderr)
328 if opt.force_broken:
329 print('warn: --force-broken, continuing to sync',
330 file=sys.stderr)
331 else:
332 raise _FetchError()
Doug Andersonfc06ced2011-03-16 15:49:18 -0700333
David Pursehousec1b86a22012-11-14 11:36:51 +0900334 fetched.add(project.gitdir)
335 pm.update()
336 except _FetchError:
Hu Xiuyune9becc02015-11-25 15:52:26 +0800337 pass
Dan Sandlerc5cd4332015-07-31 09:37:53 -0400338 except Exception as e:
339 print('error: Cannot fetch %s (%s: %s)' \
340 % (project.name, type(e).__name__, str(e)), file=sys.stderr)
David Pursehousec1b86a22012-11-14 11:36:51 +0900341 err_event.set()
342 raise
343 finally:
344 if did_lock:
345 lock.release()
Roy Lee18afd7f2010-05-09 04:32:08 +0800346
David James8d201162013-10-11 17:03:19 -0700347 return success
348
Shawn O. Pearce16614f82010-10-29 12:05:43 -0700349 def _Fetch(self, projects, opt):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700350 fetched = set()
David James89ece422014-01-09 18:51:58 -0800351 lock = _threading.Lock()
Shawn O. Pearce68194f42009-04-10 16:48:52 -0700352 pm = Progress('Fetching projects', len(projects))
Roy Lee18afd7f2010-05-09 04:32:08 +0800353
David James89ece422014-01-09 18:51:58 -0800354 objdir_project_map = dict()
355 for project in projects:
356 objdir_project_map.setdefault(project.objdir, []).append(project)
David James8d201162013-10-11 17:03:19 -0700357
David James89ece422014-01-09 18:51:58 -0800358 threads = set()
359 sem = _threading.Semaphore(self.jobs)
360 err_event = _threading.Event()
361 for project_list in objdir_project_map.values():
362 # Check for any errors before running any more tasks.
363 # ...we'll let existing threads finish, though.
364 if err_event.isSet() and not opt.force_broken:
365 break
Doug Andersonfc06ced2011-03-16 15:49:18 -0700366
David James89ece422014-01-09 18:51:58 -0800367 sem.acquire()
368 kwargs = dict(opt=opt,
369 projects=project_list,
Andrew Wheeler7f1ccfb2016-06-17 16:51:07 -0500370 sem=sem,
David James89ece422014-01-09 18:51:58 -0800371 lock=lock,
372 fetched=fetched,
373 pm=pm,
David James89ece422014-01-09 18:51:58 -0800374 err_event=err_event)
375 if self.jobs > 1:
David James8d201162013-10-11 17:03:19 -0700376 t = _threading.Thread(target = self._FetchProjectList,
David James89ece422014-01-09 18:51:58 -0800377 kwargs = kwargs)
David 'Digit' Turnere2126652012-09-05 10:35:06 +0200378 # Ensure that Ctrl-C will not freeze the repo process.
379 t.daemon = True
Roy Lee18afd7f2010-05-09 04:32:08 +0800380 threads.add(t)
381 t.start()
David James89ece422014-01-09 18:51:58 -0800382 else:
383 self._FetchProjectList(**kwargs)
Roy Lee18afd7f2010-05-09 04:32:08 +0800384
David James89ece422014-01-09 18:51:58 -0800385 for t in threads:
386 t.join()
Roy Lee18afd7f2010-05-09 04:32:08 +0800387
David James89ece422014-01-09 18:51:58 -0800388 # If we saw an error, exit with code 1 so that other scripts can check.
389 if err_event.isSet():
390 print('\nerror: Exited sync due to fetch errors', file=sys.stderr)
391 sys.exit(1)
Doug Andersonfc06ced2011-03-16 15:49:18 -0700392
Shawn O. Pearce68194f42009-04-10 16:48:52 -0700393 pm.end()
Dave Borowitz67700e92012-10-23 15:00:54 -0700394 self._fetch_times.Save()
Dave Borowitz18857212012-10-23 17:02:59 -0700395
Julien Campergue335f5ef2013-10-16 11:02:35 +0200396 if not self.manifest.IsArchive:
397 self._GCProjects(projects)
398
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700399 return fetched
400
Dave Borowitz18857212012-10-23 17:02:59 -0700401 def _GCProjects(self, projects):
Gabe Black2ff30292014-10-09 17:54:35 -0700402 gc_gitdirs = {}
David James8d201162013-10-11 17:03:19 -0700403 for project in projects:
Gabe Black2ff30292014-10-09 17:54:35 -0700404 if len(project.manifest.GetProjectsWithName(project.name)) > 1:
405 print('Shared project %s found, disabling pruning.' % project.name)
406 project.bare_git.config('--replace-all', 'gc.pruneExpire', 'never')
407 gc_gitdirs[project.gitdir] = project.bare_git
David James8d201162013-10-11 17:03:19 -0700408
Dave Borowitze2152672012-10-31 12:24:38 -0700409 has_dash_c = git_require((1, 7, 2))
410 if multiprocessing and has_dash_c:
Dave Borowitz18857212012-10-23 17:02:59 -0700411 cpu_count = multiprocessing.cpu_count()
412 else:
413 cpu_count = 1
414 jobs = min(self.jobs, cpu_count)
415
416 if jobs < 2:
Gabe Black2ff30292014-10-09 17:54:35 -0700417 for bare_git in gc_gitdirs.values():
David James8d201162013-10-11 17:03:19 -0700418 bare_git.gc('--auto')
Dave Borowitz18857212012-10-23 17:02:59 -0700419 return
420
421 config = {'pack.threads': cpu_count / jobs if cpu_count > jobs else 1}
422
423 threads = set()
424 sem = _threading.Semaphore(jobs)
425 err_event = _threading.Event()
426
David James8d201162013-10-11 17:03:19 -0700427 def GC(bare_git):
Dave Borowitz18857212012-10-23 17:02:59 -0700428 try:
429 try:
David James8d201162013-10-11 17:03:19 -0700430 bare_git.gc('--auto', config=config)
Dave Borowitz18857212012-10-23 17:02:59 -0700431 except GitError:
432 err_event.set()
433 except:
434 err_event.set()
435 raise
436 finally:
437 sem.release()
438
Gabe Black2ff30292014-10-09 17:54:35 -0700439 for bare_git in gc_gitdirs.values():
Dave Borowitz18857212012-10-23 17:02:59 -0700440 if err_event.isSet():
441 break
442 sem.acquire()
David James8d201162013-10-11 17:03:19 -0700443 t = _threading.Thread(target=GC, args=(bare_git,))
Dave Borowitz18857212012-10-23 17:02:59 -0700444 t.daemon = True
445 threads.add(t)
446 t.start()
447
448 for t in threads:
449 t.join()
450
451 if err_event.isSet():
Sarah Owenscecd1d82012-11-01 22:59:27 -0700452 print('\nerror: Exited sync due to gc errors', file=sys.stderr)
Dave Borowitz18857212012-10-23 17:02:59 -0700453 sys.exit(1)
454
Tim Kilbourn07669002013-03-08 15:02:49 -0800455 def _ReloadManifest(self, manifest_name=None):
456 if manifest_name:
457 # Override calls _Unload already
458 self.manifest.Override(manifest_name)
459 else:
460 self.manifest._Unload()
461
Dan Willemsen43507912016-09-01 16:26:02 -0700462 def _DeleteProject(self, path):
463 print('Deleting obsolete path %s' % path, file=sys.stderr)
464
465 # Delete the .git directory first, so we're less likely to have a partially
466 # working git repository around. There shouldn't be any git projects here,
467 # so rmtree works.
468 try:
469 shutil.rmtree(os.path.join(path, '.git'))
470 except OSError:
471 print('Failed to remove %s' % os.path.join(path, '.git'), file=sys.stderr)
472 print('error: Failed to delete obsolete path %s' % path, file=sys.stderr)
473 print(' remove manually, then run sync again', file=sys.stderr)
474 return -1
475
476 # Delete everything under the worktree, except for directories that contain
477 # another git project
478 dirs_to_remove = []
479 failed = False
480 for root, dirs, files in os.walk(path):
481 for f in files:
482 try:
483 os.remove(os.path.join(root, f))
484 except OSError:
485 print('Failed to remove %s' % os.path.join(root, f), file=sys.stderr)
486 failed = True
487 dirs[:] = [d for d in dirs
488 if not os.path.lexists(os.path.join(root, d, '.git'))]
489 dirs_to_remove += [os.path.join(root, d) for d in dirs
490 if os.path.join(root, d) not in dirs_to_remove]
491 for d in reversed(dirs_to_remove):
Dan Willemseneceeb1b2016-09-25 18:24:27 -0700492 if os.path.islink(d):
493 try:
494 os.remove(d)
495 except OSError:
496 print('Failed to remove %s' % os.path.join(root, d), file=sys.stderr)
497 failed = True
498 elif len(os.listdir(d)) == 0:
Dan Willemsen43507912016-09-01 16:26:02 -0700499 try:
500 os.rmdir(d)
501 except OSError:
502 print('Failed to remove %s' % os.path.join(root, d), file=sys.stderr)
503 failed = True
504 continue
505 if failed:
506 print('error: Failed to delete obsolete path %s' % path, file=sys.stderr)
507 print(' remove manually, then run sync again', file=sys.stderr)
508 return -1
509
510 # Try deleting parent dirs if they are empty
511 project_dir = path
512 while project_dir != self.manifest.topdir:
513 if len(os.listdir(project_dir)) == 0:
514 os.rmdir(project_dir)
515 else:
516 break
517 project_dir = os.path.dirname(project_dir)
518
519 return 0
520
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700521 def UpdateProjectList(self):
522 new_project_paths = []
Colin Cross5acde752012-03-28 20:15:45 -0700523 for project in self.GetProjects(None, missing_ok=True):
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700524 if project.relpath:
525 new_project_paths.append(project.relpath)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700526 file_name = 'project.list'
527 file_path = os.path.join(self.manifest.repodir, file_name)
528 old_project_paths = []
529
530 if os.path.exists(file_path):
531 fd = open(file_path, 'r')
532 try:
533 old_project_paths = fd.read().split('\n')
534 finally:
535 fd.close()
536 for path in old_project_paths:
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700537 if not path:
538 continue
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700539 if path not in new_project_paths:
David Pursehouse8a68ff92012-09-24 12:15:13 +0900540 # If the path has already been deleted, we don't need to do it
Dan Willemsen43507912016-09-01 16:26:02 -0700541 gitdir = os.path.join(self.manifest.topdir, path, '.git')
542 if os.path.exists(gitdir):
David Pursehousec1b86a22012-11-14 11:36:51 +0900543 project = Project(
544 manifest = self.manifest,
545 name = path,
546 remote = RemoteSpec('origin'),
David James8d201162013-10-11 17:03:19 -0700547 gitdir = gitdir,
548 objdir = gitdir,
David Pursehousec1b86a22012-11-14 11:36:51 +0900549 worktree = os.path.join(self.manifest.topdir, path),
550 relpath = path,
551 revisionExpr = 'HEAD',
552 revisionId = None,
553 groups = None)
Anthonyf3fdf822009-09-26 13:38:52 -0400554
David Pursehousec1b86a22012-11-14 11:36:51 +0900555 if project.IsDirty():
David Pursehouse2f9e7e42013-03-05 17:26:46 +0900556 print('error: Cannot remove project "%s": uncommitted changes '
David Pursehousec1b86a22012-11-14 11:36:51 +0900557 'are present' % project.relpath, file=sys.stderr)
558 print(' commit changes, then run sync again',
559 file=sys.stderr)
560 return -1
Dan Willemsen43507912016-09-01 16:26:02 -0700561 elif self._DeleteProject(project.worktree):
562 return -1
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700563
Shawn O. Pearce9fb29ce2009-06-04 20:41:02 -0700564 new_project_paths.sort()
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700565 fd = open(file_path, 'w')
566 try:
567 fd.write('\n'.join(new_project_paths))
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700568 fd.write('\n')
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700569 finally:
570 fd.close()
571 return 0
572
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700573 def Execute(self, opt, args):
Roy Lee18afd7f2010-05-09 04:32:08 +0800574 if opt.jobs:
575 self.jobs = opt.jobs
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -0700576 if self.jobs > 1:
577 soft_limit, _ = _rlimit_nofile()
578 self.jobs = min(self.jobs, (soft_limit - 5) / 3)
579
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700580 if opt.network_only and opt.detach_head:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700581 print('error: cannot combine -n and -d', file=sys.stderr)
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700582 sys.exit(1)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700583 if opt.network_only and opt.local_only:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700584 print('error: cannot combine -n and -l', file=sys.stderr)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700585 sys.exit(1)
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500586 if opt.manifest_name and opt.smart_sync:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700587 print('error: cannot combine -m and -s', file=sys.stderr)
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500588 sys.exit(1)
589 if opt.manifest_name and opt.smart_tag:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700590 print('error: cannot combine -m and -t', file=sys.stderr)
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500591 sys.exit(1)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900592 if opt.manifest_server_username or opt.manifest_server_password:
593 if not (opt.smart_sync or opt.smart_tag):
Sarah Owenscecd1d82012-11-01 22:59:27 -0700594 print('error: -u and -p may only be combined with -s or -t',
595 file=sys.stderr)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900596 sys.exit(1)
597 if None in [opt.manifest_server_username, opt.manifest_server_password]:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700598 print('error: both -u and -p must be given', file=sys.stderr)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900599 sys.exit(1)
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500600
601 if opt.manifest_name:
602 self.manifest.Override(opt.manifest_name)
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700603
Chirayu Desaia892b102013-06-11 14:18:46 +0530604 manifest_name = opt.manifest_name
David Pursehouse59b41742015-05-07 14:36:09 +0900605 smart_sync_manifest_name = "smart_sync_override.xml"
606 smart_sync_manifest_path = os.path.join(
607 self.manifest.manifestProject.worktree, smart_sync_manifest_name)
Chirayu Desaia892b102013-06-11 14:18:46 +0530608
Victor Boivie08c880d2011-04-19 10:32:52 +0200609 if opt.smart_sync or opt.smart_tag:
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700610 if not self.manifest.manifest_server:
David Pursehouse2f9e7e42013-03-05 17:26:46 +0900611 print('error: cannot smart sync: no manifest server defined in '
Sarah Owenscecd1d82012-11-01 22:59:27 -0700612 'manifest', file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700613 sys.exit(1)
David Pursehouse86d973d2012-08-24 10:21:02 +0900614
615 manifest_server = self.manifest.manifest_server
David Pursehousefb99c712013-09-25 11:09:34 +0900616 if not opt.quiet:
617 print('Using manifest server %s' % manifest_server)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900618
David Pursehouse86d973d2012-08-24 10:21:02 +0900619 if not '@' in manifest_server:
David Pursehousecf76b1b2012-09-14 10:31:42 +0900620 username = None
621 password = None
622 if opt.manifest_server_username and opt.manifest_server_password:
623 username = opt.manifest_server_username
624 password = opt.manifest_server_password
David Pursehouse86d973d2012-08-24 10:21:02 +0900625 else:
626 try:
David Pursehousecf76b1b2012-09-14 10:31:42 +0900627 info = netrc.netrc()
628 except IOError:
David Pursehouseba7bc732015-08-20 16:55:42 +0900629 # .netrc file does not exist or could not be opened
630 pass
David Pursehouse86d973d2012-08-24 10:21:02 +0900631 else:
David Pursehousecf76b1b2012-09-14 10:31:42 +0900632 try:
Chirayu Desaidb2ad9d2013-06-11 13:42:25 +0530633 parse_result = urllib.parse.urlparse(manifest_server)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900634 if parse_result.hostname:
David Pursehouseba7bc732015-08-20 16:55:42 +0900635 auth = info.authenticators(parse_result.hostname)
636 if auth:
637 username, _account, password = auth
638 else:
639 print('No credentials found for %s in .netrc'
640 % parse_result.hostname, file=sys.stderr)
Sarah Owensa5be53f2012-09-09 15:37:57 -0700641 except netrc.NetrcParseError as e:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700642 print('Error parsing .netrc file: %s' % e, file=sys.stderr)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900643
644 if (username and password):
645 manifest_server = manifest_server.replace('://', '://%s:%s@' %
646 (username, password),
647 1)
David Pursehouse86d973d2012-08-24 10:21:02 +0900648
Dan Willemsen0745bb22015-08-17 13:41:45 -0700649 transport = PersistentTransport(manifest_server)
650 if manifest_server.startswith('persistent-'):
651 manifest_server = manifest_server[len('persistent-'):]
652
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700653 try:
Dan Willemsen0745bb22015-08-17 13:41:45 -0700654 server = xmlrpc.client.Server(manifest_server, transport=transport)
Victor Boivie08c880d2011-04-19 10:32:52 +0200655 if opt.smart_sync:
656 p = self.manifest.manifestProject
657 b = p.GetBranch(p.CurrentBranch)
658 branch = b.merge
659 if branch.startswith(R_HEADS):
660 branch = branch[len(R_HEADS):]
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700661
Victor Boivie08c880d2011-04-19 10:32:52 +0200662 env = os.environ.copy()
Jeff Davidson5cf16602014-10-02 10:13:38 -0700663 if 'SYNC_TARGET' in env:
664 target = env['SYNC_TARGET']
665 [success, manifest_str] = server.GetApprovedManifest(branch, target)
666 elif 'TARGET_PRODUCT' in env and 'TARGET_BUILD_VARIANT' in env:
Victor Boivie08c880d2011-04-19 10:32:52 +0200667 target = '%s-%s' % (env['TARGET_PRODUCT'],
668 env['TARGET_BUILD_VARIANT'])
669 [success, manifest_str] = server.GetApprovedManifest(branch, target)
670 else:
671 [success, manifest_str] = server.GetApprovedManifest(branch)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700672 else:
Victor Boivie08c880d2011-04-19 10:32:52 +0200673 assert(opt.smart_tag)
674 [success, manifest_str] = server.GetManifest(opt.smart_tag)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700675
676 if success:
David Pursehouse59b41742015-05-07 14:36:09 +0900677 manifest_name = smart_sync_manifest_name
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700678 try:
David Pursehouse59b41742015-05-07 14:36:09 +0900679 f = open(smart_sync_manifest_path, 'w')
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700680 try:
681 f.write(manifest_str)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700682 finally:
683 f.close()
David Pursehouse727cc3e2015-05-07 14:16:49 +0900684 except IOError as e:
685 print('error: cannot write manifest to %s:\n%s'
David Pursehouse59b41742015-05-07 14:36:09 +0900686 % (smart_sync_manifest_path, e),
Sarah Owenscecd1d82012-11-01 22:59:27 -0700687 file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700688 sys.exit(1)
Victor Boivie53a6c5d2013-03-19 12:20:52 +0100689 self._ReloadManifest(manifest_name)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700690 else:
David Pursehouse351fe2c2013-09-25 17:54:26 +0900691 print('error: manifest server RPC call failed: %s' %
692 manifest_str, file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700693 sys.exit(1)
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530694 except (socket.error, IOError, xmlrpc.client.Fault) as e:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700695 print('error: cannot connect to manifest server %s:\n%s'
696 % (self.manifest.manifest_server, e), file=sys.stderr)
David Pursehousebd489c42012-08-23 10:21:26 +0900697 sys.exit(1)
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530698 except xmlrpc.client.ProtocolError as e:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700699 print('error: cannot connect to manifest server %s:\n%d %s'
700 % (self.manifest.manifest_server, e.errcode, e.errmsg),
701 file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700702 sys.exit(1)
David Pursehouse59b41742015-05-07 14:36:09 +0900703 else: # Not smart sync or smart tag mode
704 if os.path.isfile(smart_sync_manifest_path):
705 try:
706 os.remove(smart_sync_manifest_path)
707 except OSError as e:
708 print('error: failed to remove existing smart sync override manifest: %s' %
709 e, file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700710
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700711 rp = self.manifest.repoProject
712 rp.PreSync()
713
714 mp = self.manifest.manifestProject
715 mp.PreSync()
716
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800717 if opt.repo_upgraded:
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -0700718 _PostRepoUpgrade(self.manifest, quiet=opt.quiet)
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800719
Nico Sallembien9bb18162009-12-07 15:38:01 -0800720 if not opt.local_only:
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -0700721 mp.Sync_NetworkHalf(quiet=opt.quiet,
Mitchel Humpherys597868b2012-10-29 10:18:34 -0700722 current_branch_only=opt.current_branch_only,
David Pursehouseb1553542014-09-04 21:28:09 +0900723 no_tags=opt.no_tags,
724 optimized_fetch=opt.optimized_fetch)
Nico Sallembien9bb18162009-12-07 15:38:01 -0800725
726 if mp.HasChanges:
727 syncbuf = SyncBuffer(mp.config)
728 mp.Sync_LocalHalf(syncbuf)
729 if not syncbuf.Finish():
730 sys.exit(1)
Victor Boivie53a6c5d2013-03-19 12:20:52 +0100731 self._ReloadManifest(manifest_name)
Shawn O. Pearcec4657962011-09-26 09:08:01 -0700732 if opt.jobs is None:
733 self.jobs = self.manifest.default.sync_j
Simran Basib9a1b732015-08-20 12:19:28 -0700734
Simran Basib9a1b732015-08-20 12:19:28 -0700735 if self.gitc_manifest:
736 gitc_manifest_projects = self.GetProjects(args,
Simran Basib9a1b732015-08-20 12:19:28 -0700737 missing_ok=True)
738 gitc_projects = []
739 opened_projects = []
740 for project in gitc_manifest_projects:
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700741 if project.relpath in self.gitc_manifest.paths and \
742 self.gitc_manifest.paths[project.relpath].old_revision:
743 opened_projects.append(project.relpath)
Simran Basib9a1b732015-08-20 12:19:28 -0700744 else:
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700745 gitc_projects.append(project.relpath)
Simran Basib9a1b732015-08-20 12:19:28 -0700746
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700747 if not args:
748 gitc_projects = None
749
750 if gitc_projects != [] and not opt.local_only:
Simran Basib9a1b732015-08-20 12:19:28 -0700751 print('Updating GITC client: %s' % self.gitc_manifest.gitc_client_name)
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700752 manifest = GitcManifest(self.repodir, self.gitc_manifest.gitc_client_name)
753 if manifest_name:
754 manifest.Override(manifest_name)
755 else:
756 manifest.Override(self.manifest.manifestFile)
757 gitc_utils.generate_gitc_manifest(self.gitc_manifest,
758 manifest,
Simran Basib9a1b732015-08-20 12:19:28 -0700759 gitc_projects)
760 print('GITC client successfully synced.')
761
762 # The opened projects need to be synced as normal, therefore we
763 # generate a new args list to represent the opened projects.
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700764 # TODO: make this more reliable -- if there's a project name/path overlap,
765 # this may choose the wrong project.
766 args = [os.path.relpath(self.manifest.paths[p].worktree, os.getcwd())
767 for p in opened_projects]
Simran Basib9a1b732015-08-20 12:19:28 -0700768 if not args:
769 return
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800770 all_projects = self.GetProjects(args,
771 missing_ok=True,
772 submodules_ok=opt.fetch_submodules)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700773
Dave Borowitz67700e92012-10-23 15:00:54 -0700774 self._fetch_times = _FetchTimes(self.manifest)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700775 if not opt.local_only:
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700776 to_fetch = []
777 now = time.time()
Dave Borowitz67700e92012-10-23 15:00:54 -0700778 if _ONE_DAY_S <= (now - rp.LastFetch):
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700779 to_fetch.append(rp)
David Pursehouse8a68ff92012-09-24 12:15:13 +0900780 to_fetch.extend(all_projects)
Dave Borowitz67700e92012-10-23 15:00:54 -0700781 to_fetch.sort(key=self._fetch_times.Get, reverse=True)
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700782
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800783 fetched = self._Fetch(to_fetch, opt)
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700784 _PostRepoFetch(rp, opt.no_repo_verify)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700785 if opt.network_only:
786 # bail out now; the rest touches the working tree
787 return
788
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800789 # Iteratively fetch missing and/or nested unregistered submodules
790 previously_missing_set = set()
791 while True:
Victor Boivie53a6c5d2013-03-19 12:20:52 +0100792 self._ReloadManifest(manifest_name)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800793 all_projects = self.GetProjects(args,
794 missing_ok=True,
795 submodules_ok=opt.fetch_submodules)
796 missing = []
797 for project in all_projects:
798 if project.gitdir not in fetched:
799 missing.append(project)
800 if not missing:
801 break
802 # Stop us from non-stopped fetching actually-missing repos: If set of
803 # missing repos has not been changed from last fetch, we break.
804 missing_set = set(p.name for p in missing)
805 if previously_missing_set == missing_set:
806 break
807 previously_missing_set = missing_set
808 fetched.update(self._Fetch(missing, opt))
809
Julien Campergue335f5ef2013-10-16 11:02:35 +0200810 if self.manifest.IsMirror or self.manifest.IsArchive:
Shawn O. Pearcecd1d7ff2009-06-04 16:15:53 -0700811 # bail out now, we have no working tree
812 return
813
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700814 if self.UpdateProjectList():
815 sys.exit(1)
816
Shawn O. Pearce350cde42009-04-16 11:21:18 -0700817 syncbuf = SyncBuffer(mp.config,
818 detach_head = opt.detach_head)
David Pursehouse8a68ff92012-09-24 12:15:13 +0900819 pm = Progress('Syncing work tree', len(all_projects))
820 for project in all_projects:
Shawn O. Pearce68194f42009-04-10 16:48:52 -0700821 pm.update()
Shawn O. Pearcee284ad12008-11-04 07:37:10 -0800822 if project.worktree:
Kevin Degiabaa7f32014-11-12 11:27:45 -0700823 project.Sync_LocalHalf(syncbuf, force_sync=opt.force_sync)
Shawn O. Pearce68194f42009-04-10 16:48:52 -0700824 pm.end()
Sarah Owenscecd1d82012-11-01 22:59:27 -0700825 print(file=sys.stderr)
Shawn O. Pearce350cde42009-04-16 11:21:18 -0700826 if not syncbuf.Finish():
827 sys.exit(1)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700828
Doug Anderson2b8db3c2010-11-01 15:08:06 -0700829 # If there's a notice that's supposed to print at the end of the sync, print
830 # it now...
831 if self.manifest.notice:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700832 print(self.manifest.notice)
Doug Anderson2b8db3c2010-11-01 15:08:06 -0700833
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -0700834def _PostRepoUpgrade(manifest, quiet=False):
Conley Owens094cdbe2014-01-30 15:09:59 -0800835 wrapper = Wrapper()
Conley Owensc9129d92012-10-01 16:12:28 -0700836 if wrapper.NeedSetupGnuPG():
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -0700837 wrapper.SetupGnuPG(quiet)
Conley Owensf2fe2d92014-01-29 13:53:43 -0800838 for project in manifest.projects:
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700839 if project.Exists:
840 project.PostRepoUpgrade()
841
842def _PostRepoFetch(rp, no_repo_verify=False, verbose=False):
843 if rp.HasChanges:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700844 print('info: A new version of repo is available', file=sys.stderr)
845 print(file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700846 if no_repo_verify or _VerifyTag(rp):
Shawn O. Pearce350cde42009-04-16 11:21:18 -0700847 syncbuf = SyncBuffer(rp.config)
848 rp.Sync_LocalHalf(syncbuf)
849 if not syncbuf.Finish():
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700850 sys.exit(1)
Sarah Owenscecd1d82012-11-01 22:59:27 -0700851 print('info: Restarting repo with latest version', file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700852 raise RepoChangedException(['--repo-upgraded'])
853 else:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700854 print('warning: Skipped upgrade to unverified version', file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700855 else:
856 if verbose:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700857 print('repo version %s is current' % rp.work_git.describe(HEAD),
858 file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700859
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700860def _VerifyTag(project):
861 gpg_dir = os.path.expanduser('~/.repoconfig/gnupg')
862 if not os.path.exists(gpg_dir):
Sarah Owenscecd1d82012-11-01 22:59:27 -0700863 print('warning: GnuPG was not available during last "repo init"\n'
864 'warning: Cannot automatically authenticate repo."""',
865 file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700866 return True
867
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700868 try:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -0700869 cur = project.bare_git.describe(project.GetRevisionId())
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700870 except GitError:
871 cur = None
872
873 if not cur \
874 or re.compile(r'^.*-[0-9]{1,}-g[0-9a-f]{1,}$').match(cur):
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -0700875 rev = project.revisionExpr
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700876 if rev.startswith(R_HEADS):
877 rev = rev[len(R_HEADS):]
878
Sarah Owenscecd1d82012-11-01 22:59:27 -0700879 print(file=sys.stderr)
880 print("warning: project '%s' branch '%s' is not signed"
881 % (project.name, rev), file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700882 return False
883
Shawn O. Pearcef18cb762010-12-07 11:41:05 -0800884 env = os.environ.copy()
885 env['GIT_DIR'] = project.gitdir.encode()
886 env['GNUPGHOME'] = gpg_dir.encode()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700887
888 cmd = [GIT, 'tag', '-v', cur]
889 proc = subprocess.Popen(cmd,
890 stdout = subprocess.PIPE,
891 stderr = subprocess.PIPE,
892 env = env)
893 out = proc.stdout.read()
894 proc.stdout.close()
895
896 err = proc.stderr.read()
897 proc.stderr.close()
898
899 if proc.wait() != 0:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700900 print(file=sys.stderr)
901 print(out, file=sys.stderr)
902 print(err, file=sys.stderr)
903 print(file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700904 return False
905 return True
Dave Borowitz67700e92012-10-23 15:00:54 -0700906
907class _FetchTimes(object):
Dave Borowitzd9478582012-10-23 16:35:39 -0700908 _ALPHA = 0.5
909
Dave Borowitz67700e92012-10-23 15:00:54 -0700910 def __init__(self, manifest):
Anthony King85b24ac2014-05-06 15:57:48 +0100911 self._path = os.path.join(manifest.repodir, '.repo_fetchtimes.json')
Dave Borowitz67700e92012-10-23 15:00:54 -0700912 self._times = None
Dave Borowitzd9478582012-10-23 16:35:39 -0700913 self._seen = set()
Dave Borowitz67700e92012-10-23 15:00:54 -0700914
915 def Get(self, project):
916 self._Load()
917 return self._times.get(project.name, _ONE_DAY_S)
918
919 def Set(self, project, t):
Dave Borowitzd9478582012-10-23 16:35:39 -0700920 self._Load()
921 name = project.name
922 old = self._times.get(name, t)
923 self._seen.add(name)
924 a = self._ALPHA
925 self._times[name] = (a*t) + ((1-a) * old)
Dave Borowitz67700e92012-10-23 15:00:54 -0700926
927 def _Load(self):
928 if self._times is None:
929 try:
Anthony King85b24ac2014-05-06 15:57:48 +0100930 f = open(self._path)
Dave Borowitz67700e92012-10-23 15:00:54 -0700931 try:
Anthony King85b24ac2014-05-06 15:57:48 +0100932 self._times = json.load(f)
933 finally:
934 f.close()
935 except (IOError, ValueError):
936 try:
937 os.remove(self._path)
938 except OSError:
939 pass
940 self._times = {}
Dave Borowitz67700e92012-10-23 15:00:54 -0700941
942 def Save(self):
943 if self._times is None:
944 return
Dave Borowitzd9478582012-10-23 16:35:39 -0700945
946 to_delete = []
947 for name in self._times:
948 if name not in self._seen:
949 to_delete.append(name)
950 for name in to_delete:
951 del self._times[name]
952
Dave Borowitz67700e92012-10-23 15:00:54 -0700953 try:
Anthony King85b24ac2014-05-06 15:57:48 +0100954 f = open(self._path, 'w')
Dave Borowitz67700e92012-10-23 15:00:54 -0700955 try:
Anthony King85b24ac2014-05-06 15:57:48 +0100956 json.dump(self._times, f, indent=2)
957 finally:
958 f.close()
959 except (IOError, TypeError):
960 try:
961 os.remove(self._path)
962 except OSError:
963 pass
Dan Willemsen0745bb22015-08-17 13:41:45 -0700964
965# This is a replacement for xmlrpc.client.Transport using urllib2
966# and supporting persistent-http[s]. It cannot change hosts from
967# request to request like the normal transport, the real url
968# is passed during initialization.
969class PersistentTransport(xmlrpc.client.Transport):
970 def __init__(self, orig_host):
971 self.orig_host = orig_host
972
973 def request(self, host, handler, request_body, verbose=False):
974 with GetUrlCookieFile(self.orig_host, not verbose) as (cookiefile, proxy):
975 # Python doesn't understand cookies with the #HttpOnly_ prefix
976 # Since we're only using them for HTTP, copy the file temporarily,
977 # stripping those prefixes away.
Dan Willemsen3010e5b2015-08-20 10:09:20 -0700978 if cookiefile:
979 tmpcookiefile = tempfile.NamedTemporaryFile()
David Pursehouse4c5f74e2015-10-02 11:10:10 +0900980 tmpcookiefile.write("# HTTP Cookie File")
Dan Willemsen3010e5b2015-08-20 10:09:20 -0700981 try:
982 with open(cookiefile) as f:
983 for line in f:
984 if line.startswith("#HttpOnly_"):
985 line = line[len("#HttpOnly_"):]
986 tmpcookiefile.write(line)
987 tmpcookiefile.flush()
Dan Willemsen0745bb22015-08-17 13:41:45 -0700988
Dan Willemsen3010e5b2015-08-20 10:09:20 -0700989 cookiejar = cookielib.MozillaCookieJar(tmpcookiefile.name)
David Pursehouseb1ad2192015-09-30 10:35:43 +0900990 try:
991 cookiejar.load()
992 except cookielib.LoadError:
993 cookiejar = cookielib.CookieJar()
Dan Willemsen3010e5b2015-08-20 10:09:20 -0700994 finally:
995 tmpcookiefile.close()
996 else:
997 cookiejar = cookielib.CookieJar()
Dan Willemsen0745bb22015-08-17 13:41:45 -0700998
999 proxyhandler = urllib.request.ProxyHandler
1000 if proxy:
1001 proxyhandler = urllib.request.ProxyHandler({
1002 "http": proxy,
1003 "https": proxy })
1004
1005 opener = urllib.request.build_opener(
1006 urllib.request.HTTPCookieProcessor(cookiejar),
1007 proxyhandler)
1008
1009 url = urllib.parse.urljoin(self.orig_host, handler)
1010 parse_results = urllib.parse.urlparse(url)
1011
1012 scheme = parse_results.scheme
1013 if scheme == 'persistent-http':
1014 scheme = 'http'
1015 if scheme == 'persistent-https':
1016 # If we're proxying through persistent-https, use http. The
1017 # proxy itself will do the https.
1018 if proxy:
1019 scheme = 'http'
1020 else:
1021 scheme = 'https'
1022
1023 # Parse out any authentication information using the base class
1024 host, extra_headers, _ = self.get_host_info(parse_results.netloc)
1025
1026 url = urllib.parse.urlunparse((
1027 scheme,
1028 host,
1029 parse_results.path,
1030 parse_results.params,
1031 parse_results.query,
1032 parse_results.fragment))
1033
1034 request = urllib.request.Request(url, request_body)
1035 if extra_headers is not None:
1036 for (name, header) in extra_headers:
1037 request.add_header(name, header)
1038 request.add_header('Content-Type', 'text/xml')
1039 try:
1040 response = opener.open(request)
1041 except urllib.error.HTTPError as e:
1042 if e.code == 501:
1043 # We may have been redirected through a login process
1044 # but our POST turned into a GET. Retry.
1045 response = opener.open(request)
1046 else:
1047 raise
1048
1049 p, u = xmlrpc.client.getparser()
1050 while 1:
1051 data = response.read(1024)
1052 if not data:
1053 break
1054 p.feed(data)
1055 p.close()
1056 return u.close()
1057
1058 def close(self):
1059 pass
1060