blob: 82056f3379a3112d338c884f87c14574b62ccf8e [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()
Marc Herbertffb4b892017-04-04 22:03:53 -0700327 print('error: Cannot fetch %s from %s'
328 % (project.name, project.remote.url),
329 file=sys.stderr)
David Pursehousec1b86a22012-11-14 11:36:51 +0900330 if opt.force_broken:
331 print('warn: --force-broken, continuing to sync',
332 file=sys.stderr)
333 else:
334 raise _FetchError()
Doug Andersonfc06ced2011-03-16 15:49:18 -0700335
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()
Roy Lee18afd7f2010-05-09 04:32:08 +0800348
David James8d201162013-10-11 17:03:19 -0700349 return success
350
Shawn O. Pearce16614f82010-10-29 12:05:43 -0700351 def _Fetch(self, projects, opt):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700352 fetched = set()
David James89ece422014-01-09 18:51:58 -0800353 lock = _threading.Lock()
Shawn O. Pearce68194f42009-04-10 16:48:52 -0700354 pm = Progress('Fetching projects', len(projects))
Roy Lee18afd7f2010-05-09 04:32:08 +0800355
David James89ece422014-01-09 18:51:58 -0800356 objdir_project_map = dict()
357 for project in projects:
358 objdir_project_map.setdefault(project.objdir, []).append(project)
David James8d201162013-10-11 17:03:19 -0700359
David James89ece422014-01-09 18:51:58 -0800360 threads = set()
361 sem = _threading.Semaphore(self.jobs)
362 err_event = _threading.Event()
363 for project_list in objdir_project_map.values():
364 # Check for any errors before running any more tasks.
365 # ...we'll let existing threads finish, though.
366 if err_event.isSet() and not opt.force_broken:
367 break
Doug Andersonfc06ced2011-03-16 15:49:18 -0700368
David James89ece422014-01-09 18:51:58 -0800369 sem.acquire()
370 kwargs = dict(opt=opt,
371 projects=project_list,
Andrew Wheeler7f1ccfb2016-06-17 16:51:07 -0500372 sem=sem,
David James89ece422014-01-09 18:51:58 -0800373 lock=lock,
374 fetched=fetched,
375 pm=pm,
David James89ece422014-01-09 18:51:58 -0800376 err_event=err_event)
377 if self.jobs > 1:
David James8d201162013-10-11 17:03:19 -0700378 t = _threading.Thread(target = self._FetchProjectList,
David James89ece422014-01-09 18:51:58 -0800379 kwargs = kwargs)
David 'Digit' Turnere2126652012-09-05 10:35:06 +0200380 # Ensure that Ctrl-C will not freeze the repo process.
381 t.daemon = True
Roy Lee18afd7f2010-05-09 04:32:08 +0800382 threads.add(t)
383 t.start()
David James89ece422014-01-09 18:51:58 -0800384 else:
385 self._FetchProjectList(**kwargs)
Roy Lee18afd7f2010-05-09 04:32:08 +0800386
David James89ece422014-01-09 18:51:58 -0800387 for t in threads:
388 t.join()
Roy Lee18afd7f2010-05-09 04:32:08 +0800389
David James89ece422014-01-09 18:51:58 -0800390 # If we saw an error, exit with code 1 so that other scripts can check.
391 if err_event.isSet():
392 print('\nerror: Exited sync due to fetch errors', file=sys.stderr)
393 sys.exit(1)
Doug Andersonfc06ced2011-03-16 15:49:18 -0700394
Shawn O. Pearce68194f42009-04-10 16:48:52 -0700395 pm.end()
Dave Borowitz67700e92012-10-23 15:00:54 -0700396 self._fetch_times.Save()
Dave Borowitz18857212012-10-23 17:02:59 -0700397
Julien Campergue335f5ef2013-10-16 11:02:35 +0200398 if not self.manifest.IsArchive:
399 self._GCProjects(projects)
400
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700401 return fetched
402
Dave Borowitz18857212012-10-23 17:02:59 -0700403 def _GCProjects(self, projects):
Gabe Black2ff30292014-10-09 17:54:35 -0700404 gc_gitdirs = {}
David James8d201162013-10-11 17:03:19 -0700405 for project in projects:
Gabe Black2ff30292014-10-09 17:54:35 -0700406 if len(project.manifest.GetProjectsWithName(project.name)) > 1:
407 print('Shared project %s found, disabling pruning.' % project.name)
408 project.bare_git.config('--replace-all', 'gc.pruneExpire', 'never')
409 gc_gitdirs[project.gitdir] = project.bare_git
David James8d201162013-10-11 17:03:19 -0700410
Dave Borowitze2152672012-10-31 12:24:38 -0700411 has_dash_c = git_require((1, 7, 2))
412 if multiprocessing and has_dash_c:
Dave Borowitz18857212012-10-23 17:02:59 -0700413 cpu_count = multiprocessing.cpu_count()
414 else:
415 cpu_count = 1
416 jobs = min(self.jobs, cpu_count)
417
418 if jobs < 2:
Gabe Black2ff30292014-10-09 17:54:35 -0700419 for bare_git in gc_gitdirs.values():
David James8d201162013-10-11 17:03:19 -0700420 bare_git.gc('--auto')
Dave Borowitz18857212012-10-23 17:02:59 -0700421 return
422
423 config = {'pack.threads': cpu_count / jobs if cpu_count > jobs else 1}
424
425 threads = set()
426 sem = _threading.Semaphore(jobs)
427 err_event = _threading.Event()
428
David James8d201162013-10-11 17:03:19 -0700429 def GC(bare_git):
Dave Borowitz18857212012-10-23 17:02:59 -0700430 try:
431 try:
David James8d201162013-10-11 17:03:19 -0700432 bare_git.gc('--auto', config=config)
Dave Borowitz18857212012-10-23 17:02:59 -0700433 except GitError:
434 err_event.set()
435 except:
436 err_event.set()
437 raise
438 finally:
439 sem.release()
440
Gabe Black2ff30292014-10-09 17:54:35 -0700441 for bare_git in gc_gitdirs.values():
Dave Borowitz18857212012-10-23 17:02:59 -0700442 if err_event.isSet():
443 break
444 sem.acquire()
David James8d201162013-10-11 17:03:19 -0700445 t = _threading.Thread(target=GC, args=(bare_git,))
Dave Borowitz18857212012-10-23 17:02:59 -0700446 t.daemon = True
447 threads.add(t)
448 t.start()
449
450 for t in threads:
451 t.join()
452
453 if err_event.isSet():
Sarah Owenscecd1d82012-11-01 22:59:27 -0700454 print('\nerror: Exited sync due to gc errors', file=sys.stderr)
Dave Borowitz18857212012-10-23 17:02:59 -0700455 sys.exit(1)
456
Tim Kilbourn07669002013-03-08 15:02:49 -0800457 def _ReloadManifest(self, manifest_name=None):
458 if manifest_name:
459 # Override calls _Unload already
460 self.manifest.Override(manifest_name)
461 else:
462 self.manifest._Unload()
463
Dan Willemsen43507912016-09-01 16:26:02 -0700464 def _DeleteProject(self, path):
465 print('Deleting obsolete path %s' % path, file=sys.stderr)
466
467 # Delete the .git directory first, so we're less likely to have a partially
468 # working git repository around. There shouldn't be any git projects here,
469 # so rmtree works.
470 try:
471 shutil.rmtree(os.path.join(path, '.git'))
472 except OSError:
473 print('Failed to remove %s' % os.path.join(path, '.git'), file=sys.stderr)
474 print('error: Failed to delete obsolete path %s' % path, file=sys.stderr)
475 print(' remove manually, then run sync again', file=sys.stderr)
476 return -1
477
478 # Delete everything under the worktree, except for directories that contain
479 # another git project
480 dirs_to_remove = []
481 failed = False
482 for root, dirs, files in os.walk(path):
483 for f in files:
484 try:
485 os.remove(os.path.join(root, f))
486 except OSError:
487 print('Failed to remove %s' % os.path.join(root, f), file=sys.stderr)
488 failed = True
489 dirs[:] = [d for d in dirs
490 if not os.path.lexists(os.path.join(root, d, '.git'))]
491 dirs_to_remove += [os.path.join(root, d) for d in dirs
492 if os.path.join(root, d) not in dirs_to_remove]
493 for d in reversed(dirs_to_remove):
Dan Willemseneceeb1b2016-09-25 18:24:27 -0700494 if os.path.islink(d):
495 try:
496 os.remove(d)
497 except OSError:
498 print('Failed to remove %s' % os.path.join(root, d), file=sys.stderr)
499 failed = True
500 elif len(os.listdir(d)) == 0:
Dan Willemsen43507912016-09-01 16:26:02 -0700501 try:
502 os.rmdir(d)
503 except OSError:
504 print('Failed to remove %s' % os.path.join(root, d), file=sys.stderr)
505 failed = True
506 continue
507 if failed:
508 print('error: Failed to delete obsolete path %s' % path, file=sys.stderr)
509 print(' remove manually, then run sync again', file=sys.stderr)
510 return -1
511
512 # Try deleting parent dirs if they are empty
513 project_dir = path
514 while project_dir != self.manifest.topdir:
515 if len(os.listdir(project_dir)) == 0:
516 os.rmdir(project_dir)
517 else:
518 break
519 project_dir = os.path.dirname(project_dir)
520
521 return 0
522
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700523 def UpdateProjectList(self):
524 new_project_paths = []
Colin Cross5acde752012-03-28 20:15:45 -0700525 for project in self.GetProjects(None, missing_ok=True):
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700526 if project.relpath:
527 new_project_paths.append(project.relpath)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700528 file_name = 'project.list'
529 file_path = os.path.join(self.manifest.repodir, file_name)
530 old_project_paths = []
531
532 if os.path.exists(file_path):
533 fd = open(file_path, 'r')
534 try:
535 old_project_paths = fd.read().split('\n')
536 finally:
537 fd.close()
538 for path in old_project_paths:
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700539 if not path:
540 continue
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700541 if path not in new_project_paths:
David Pursehouse8a68ff92012-09-24 12:15:13 +0900542 # If the path has already been deleted, we don't need to do it
Dan Willemsen43507912016-09-01 16:26:02 -0700543 gitdir = os.path.join(self.manifest.topdir, path, '.git')
544 if os.path.exists(gitdir):
David Pursehousec1b86a22012-11-14 11:36:51 +0900545 project = Project(
546 manifest = self.manifest,
547 name = path,
548 remote = RemoteSpec('origin'),
David James8d201162013-10-11 17:03:19 -0700549 gitdir = gitdir,
550 objdir = gitdir,
David Pursehousec1b86a22012-11-14 11:36:51 +0900551 worktree = os.path.join(self.manifest.topdir, path),
552 relpath = path,
553 revisionExpr = 'HEAD',
554 revisionId = None,
555 groups = None)
Anthonyf3fdf822009-09-26 13:38:52 -0400556
David Pursehousec1b86a22012-11-14 11:36:51 +0900557 if project.IsDirty():
David Pursehouse2f9e7e42013-03-05 17:26:46 +0900558 print('error: Cannot remove project "%s": uncommitted changes '
David Pursehousec1b86a22012-11-14 11:36:51 +0900559 'are present' % project.relpath, file=sys.stderr)
560 print(' commit changes, then run sync again',
561 file=sys.stderr)
562 return -1
Dan Willemsen43507912016-09-01 16:26:02 -0700563 elif self._DeleteProject(project.worktree):
564 return -1
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700565
Shawn O. Pearce9fb29ce2009-06-04 20:41:02 -0700566 new_project_paths.sort()
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700567 fd = open(file_path, 'w')
568 try:
569 fd.write('\n'.join(new_project_paths))
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700570 fd.write('\n')
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700571 finally:
572 fd.close()
573 return 0
574
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700575 def Execute(self, opt, args):
Roy Lee18afd7f2010-05-09 04:32:08 +0800576 if opt.jobs:
577 self.jobs = opt.jobs
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -0700578 if self.jobs > 1:
579 soft_limit, _ = _rlimit_nofile()
580 self.jobs = min(self.jobs, (soft_limit - 5) / 3)
581
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700582 if opt.network_only and opt.detach_head:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700583 print('error: cannot combine -n and -d', file=sys.stderr)
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700584 sys.exit(1)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700585 if opt.network_only and opt.local_only:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700586 print('error: cannot combine -n and -l', file=sys.stderr)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700587 sys.exit(1)
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500588 if opt.manifest_name and opt.smart_sync:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700589 print('error: cannot combine -m and -s', file=sys.stderr)
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500590 sys.exit(1)
591 if opt.manifest_name and opt.smart_tag:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700592 print('error: cannot combine -m and -t', file=sys.stderr)
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500593 sys.exit(1)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900594 if opt.manifest_server_username or opt.manifest_server_password:
595 if not (opt.smart_sync or opt.smart_tag):
Sarah Owenscecd1d82012-11-01 22:59:27 -0700596 print('error: -u and -p may only be combined with -s or -t',
597 file=sys.stderr)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900598 sys.exit(1)
599 if None in [opt.manifest_server_username, opt.manifest_server_password]:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700600 print('error: both -u and -p must be given', file=sys.stderr)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900601 sys.exit(1)
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500602
603 if opt.manifest_name:
604 self.manifest.Override(opt.manifest_name)
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700605
Chirayu Desaia892b102013-06-11 14:18:46 +0530606 manifest_name = opt.manifest_name
David Pursehouse59b41742015-05-07 14:36:09 +0900607 smart_sync_manifest_name = "smart_sync_override.xml"
608 smart_sync_manifest_path = os.path.join(
609 self.manifest.manifestProject.worktree, smart_sync_manifest_name)
Chirayu Desaia892b102013-06-11 14:18:46 +0530610
Victor Boivie08c880d2011-04-19 10:32:52 +0200611 if opt.smart_sync or opt.smart_tag:
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700612 if not self.manifest.manifest_server:
David Pursehouse2f9e7e42013-03-05 17:26:46 +0900613 print('error: cannot smart sync: no manifest server defined in '
Sarah Owenscecd1d82012-11-01 22:59:27 -0700614 'manifest', file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700615 sys.exit(1)
David Pursehouse86d973d2012-08-24 10:21:02 +0900616
617 manifest_server = self.manifest.manifest_server
David Pursehousefb99c712013-09-25 11:09:34 +0900618 if not opt.quiet:
619 print('Using manifest server %s' % manifest_server)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900620
David Pursehouse86d973d2012-08-24 10:21:02 +0900621 if not '@' in manifest_server:
David Pursehousecf76b1b2012-09-14 10:31:42 +0900622 username = None
623 password = None
624 if opt.manifest_server_username and opt.manifest_server_password:
625 username = opt.manifest_server_username
626 password = opt.manifest_server_password
David Pursehouse86d973d2012-08-24 10:21:02 +0900627 else:
628 try:
David Pursehousecf76b1b2012-09-14 10:31:42 +0900629 info = netrc.netrc()
630 except IOError:
David Pursehouseba7bc732015-08-20 16:55:42 +0900631 # .netrc file does not exist or could not be opened
632 pass
David Pursehouse86d973d2012-08-24 10:21:02 +0900633 else:
David Pursehousecf76b1b2012-09-14 10:31:42 +0900634 try:
Chirayu Desaidb2ad9d2013-06-11 13:42:25 +0530635 parse_result = urllib.parse.urlparse(manifest_server)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900636 if parse_result.hostname:
David Pursehouseba7bc732015-08-20 16:55:42 +0900637 auth = info.authenticators(parse_result.hostname)
638 if auth:
639 username, _account, password = auth
640 else:
641 print('No credentials found for %s in .netrc'
642 % parse_result.hostname, file=sys.stderr)
Sarah Owensa5be53f2012-09-09 15:37:57 -0700643 except netrc.NetrcParseError as e:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700644 print('Error parsing .netrc file: %s' % e, file=sys.stderr)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900645
646 if (username and password):
647 manifest_server = manifest_server.replace('://', '://%s:%s@' %
648 (username, password),
649 1)
David Pursehouse86d973d2012-08-24 10:21:02 +0900650
Dan Willemsen0745bb22015-08-17 13:41:45 -0700651 transport = PersistentTransport(manifest_server)
652 if manifest_server.startswith('persistent-'):
653 manifest_server = manifest_server[len('persistent-'):]
654
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700655 try:
Dan Willemsen0745bb22015-08-17 13:41:45 -0700656 server = xmlrpc.client.Server(manifest_server, transport=transport)
Victor Boivie08c880d2011-04-19 10:32:52 +0200657 if opt.smart_sync:
658 p = self.manifest.manifestProject
659 b = p.GetBranch(p.CurrentBranch)
660 branch = b.merge
661 if branch.startswith(R_HEADS):
662 branch = branch[len(R_HEADS):]
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700663
Victor Boivie08c880d2011-04-19 10:32:52 +0200664 env = os.environ.copy()
Jeff Davidson5cf16602014-10-02 10:13:38 -0700665 if 'SYNC_TARGET' in env:
666 target = env['SYNC_TARGET']
667 [success, manifest_str] = server.GetApprovedManifest(branch, target)
668 elif 'TARGET_PRODUCT' in env and 'TARGET_BUILD_VARIANT' in env:
Victor Boivie08c880d2011-04-19 10:32:52 +0200669 target = '%s-%s' % (env['TARGET_PRODUCT'],
670 env['TARGET_BUILD_VARIANT'])
671 [success, manifest_str] = server.GetApprovedManifest(branch, target)
672 else:
673 [success, manifest_str] = server.GetApprovedManifest(branch)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700674 else:
Victor Boivie08c880d2011-04-19 10:32:52 +0200675 assert(opt.smart_tag)
676 [success, manifest_str] = server.GetManifest(opt.smart_tag)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700677
678 if success:
David Pursehouse59b41742015-05-07 14:36:09 +0900679 manifest_name = smart_sync_manifest_name
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700680 try:
David Pursehouse59b41742015-05-07 14:36:09 +0900681 f = open(smart_sync_manifest_path, 'w')
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700682 try:
683 f.write(manifest_str)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700684 finally:
685 f.close()
David Pursehouse727cc3e2015-05-07 14:16:49 +0900686 except IOError as e:
687 print('error: cannot write manifest to %s:\n%s'
David Pursehouse59b41742015-05-07 14:36:09 +0900688 % (smart_sync_manifest_path, e),
Sarah Owenscecd1d82012-11-01 22:59:27 -0700689 file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700690 sys.exit(1)
Victor Boivie53a6c5d2013-03-19 12:20:52 +0100691 self._ReloadManifest(manifest_name)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700692 else:
David Pursehouse351fe2c2013-09-25 17:54:26 +0900693 print('error: manifest server RPC call failed: %s' %
694 manifest_str, file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700695 sys.exit(1)
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530696 except (socket.error, IOError, xmlrpc.client.Fault) as e:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700697 print('error: cannot connect to manifest server %s:\n%s'
698 % (self.manifest.manifest_server, e), file=sys.stderr)
David Pursehousebd489c42012-08-23 10:21:26 +0900699 sys.exit(1)
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530700 except xmlrpc.client.ProtocolError as e:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700701 print('error: cannot connect to manifest server %s:\n%d %s'
702 % (self.manifest.manifest_server, e.errcode, e.errmsg),
703 file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700704 sys.exit(1)
David Pursehouse59b41742015-05-07 14:36:09 +0900705 else: # Not smart sync or smart tag mode
706 if os.path.isfile(smart_sync_manifest_path):
707 try:
708 os.remove(smart_sync_manifest_path)
709 except OSError as e:
710 print('error: failed to remove existing smart sync override manifest: %s' %
711 e, file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700712
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700713 rp = self.manifest.repoProject
714 rp.PreSync()
715
716 mp = self.manifest.manifestProject
717 mp.PreSync()
718
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800719 if opt.repo_upgraded:
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -0700720 _PostRepoUpgrade(self.manifest, quiet=opt.quiet)
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800721
Nico Sallembien9bb18162009-12-07 15:38:01 -0800722 if not opt.local_only:
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -0700723 mp.Sync_NetworkHalf(quiet=opt.quiet,
Mitchel Humpherys597868b2012-10-29 10:18:34 -0700724 current_branch_only=opt.current_branch_only,
David Pursehouseb1553542014-09-04 21:28:09 +0900725 no_tags=opt.no_tags,
Martin Kellye4e94d22017-03-21 16:05:12 -0700726 optimized_fetch=opt.optimized_fetch,
727 submodules=self.manifest.HasSubmodules)
Nico Sallembien9bb18162009-12-07 15:38:01 -0800728
729 if mp.HasChanges:
730 syncbuf = SyncBuffer(mp.config)
Martin Kellye4e94d22017-03-21 16:05:12 -0700731 mp.Sync_LocalHalf(syncbuf, submodules=self.manifest.HasSubmodules)
Nico Sallembien9bb18162009-12-07 15:38:01 -0800732 if not syncbuf.Finish():
733 sys.exit(1)
Victor Boivie53a6c5d2013-03-19 12:20:52 +0100734 self._ReloadManifest(manifest_name)
Shawn O. Pearcec4657962011-09-26 09:08:01 -0700735 if opt.jobs is None:
736 self.jobs = self.manifest.default.sync_j
Simran Basib9a1b732015-08-20 12:19:28 -0700737
Simran Basib9a1b732015-08-20 12:19:28 -0700738 if self.gitc_manifest:
739 gitc_manifest_projects = self.GetProjects(args,
Simran Basib9a1b732015-08-20 12:19:28 -0700740 missing_ok=True)
741 gitc_projects = []
742 opened_projects = []
743 for project in gitc_manifest_projects:
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700744 if project.relpath in self.gitc_manifest.paths and \
745 self.gitc_manifest.paths[project.relpath].old_revision:
746 opened_projects.append(project.relpath)
Simran Basib9a1b732015-08-20 12:19:28 -0700747 else:
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700748 gitc_projects.append(project.relpath)
Simran Basib9a1b732015-08-20 12:19:28 -0700749
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700750 if not args:
751 gitc_projects = None
752
753 if gitc_projects != [] and not opt.local_only:
Simran Basib9a1b732015-08-20 12:19:28 -0700754 print('Updating GITC client: %s' % self.gitc_manifest.gitc_client_name)
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700755 manifest = GitcManifest(self.repodir, self.gitc_manifest.gitc_client_name)
756 if manifest_name:
757 manifest.Override(manifest_name)
758 else:
759 manifest.Override(self.manifest.manifestFile)
760 gitc_utils.generate_gitc_manifest(self.gitc_manifest,
761 manifest,
Simran Basib9a1b732015-08-20 12:19:28 -0700762 gitc_projects)
763 print('GITC client successfully synced.')
764
765 # The opened projects need to be synced as normal, therefore we
766 # generate a new args list to represent the opened projects.
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700767 # TODO: make this more reliable -- if there's a project name/path overlap,
768 # this may choose the wrong project.
769 args = [os.path.relpath(self.manifest.paths[p].worktree, os.getcwd())
770 for p in opened_projects]
Simran Basib9a1b732015-08-20 12:19:28 -0700771 if not args:
772 return
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800773 all_projects = self.GetProjects(args,
774 missing_ok=True,
775 submodules_ok=opt.fetch_submodules)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700776
Dave Borowitz67700e92012-10-23 15:00:54 -0700777 self._fetch_times = _FetchTimes(self.manifest)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700778 if not opt.local_only:
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700779 to_fetch = []
780 now = time.time()
Dave Borowitz67700e92012-10-23 15:00:54 -0700781 if _ONE_DAY_S <= (now - rp.LastFetch):
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700782 to_fetch.append(rp)
David Pursehouse8a68ff92012-09-24 12:15:13 +0900783 to_fetch.extend(all_projects)
Dave Borowitz67700e92012-10-23 15:00:54 -0700784 to_fetch.sort(key=self._fetch_times.Get, reverse=True)
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700785
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800786 fetched = self._Fetch(to_fetch, opt)
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700787 _PostRepoFetch(rp, opt.no_repo_verify)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700788 if opt.network_only:
789 # bail out now; the rest touches the working tree
790 return
791
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800792 # Iteratively fetch missing and/or nested unregistered submodules
793 previously_missing_set = set()
794 while True:
Victor Boivie53a6c5d2013-03-19 12:20:52 +0100795 self._ReloadManifest(manifest_name)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800796 all_projects = self.GetProjects(args,
797 missing_ok=True,
798 submodules_ok=opt.fetch_submodules)
799 missing = []
800 for project in all_projects:
801 if project.gitdir not in fetched:
802 missing.append(project)
803 if not missing:
804 break
805 # Stop us from non-stopped fetching actually-missing repos: If set of
806 # missing repos has not been changed from last fetch, we break.
807 missing_set = set(p.name for p in missing)
808 if previously_missing_set == missing_set:
809 break
810 previously_missing_set = missing_set
811 fetched.update(self._Fetch(missing, opt))
812
Julien Campergue335f5ef2013-10-16 11:02:35 +0200813 if self.manifest.IsMirror or self.manifest.IsArchive:
Shawn O. Pearcecd1d7ff2009-06-04 16:15:53 -0700814 # bail out now, we have no working tree
815 return
816
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700817 if self.UpdateProjectList():
818 sys.exit(1)
819
Shawn O. Pearce350cde42009-04-16 11:21:18 -0700820 syncbuf = SyncBuffer(mp.config,
821 detach_head = opt.detach_head)
David Pursehouse8a68ff92012-09-24 12:15:13 +0900822 pm = Progress('Syncing work tree', len(all_projects))
823 for project in all_projects:
Shawn O. Pearce68194f42009-04-10 16:48:52 -0700824 pm.update()
Shawn O. Pearcee284ad12008-11-04 07:37:10 -0800825 if project.worktree:
Kevin Degiabaa7f32014-11-12 11:27:45 -0700826 project.Sync_LocalHalf(syncbuf, force_sync=opt.force_sync)
Shawn O. Pearce68194f42009-04-10 16:48:52 -0700827 pm.end()
Sarah Owenscecd1d82012-11-01 22:59:27 -0700828 print(file=sys.stderr)
Shawn O. Pearce350cde42009-04-16 11:21:18 -0700829 if not syncbuf.Finish():
830 sys.exit(1)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700831
Doug Anderson2b8db3c2010-11-01 15:08:06 -0700832 # If there's a notice that's supposed to print at the end of the sync, print
833 # it now...
834 if self.manifest.notice:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700835 print(self.manifest.notice)
Doug Anderson2b8db3c2010-11-01 15:08:06 -0700836
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -0700837def _PostRepoUpgrade(manifest, quiet=False):
Conley Owens094cdbe2014-01-30 15:09:59 -0800838 wrapper = Wrapper()
Conley Owensc9129d92012-10-01 16:12:28 -0700839 if wrapper.NeedSetupGnuPG():
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -0700840 wrapper.SetupGnuPG(quiet)
Conley Owensf2fe2d92014-01-29 13:53:43 -0800841 for project in manifest.projects:
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700842 if project.Exists:
843 project.PostRepoUpgrade()
844
845def _PostRepoFetch(rp, no_repo_verify=False, verbose=False):
846 if rp.HasChanges:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700847 print('info: A new version of repo is available', file=sys.stderr)
848 print(file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700849 if no_repo_verify or _VerifyTag(rp):
Shawn O. Pearce350cde42009-04-16 11:21:18 -0700850 syncbuf = SyncBuffer(rp.config)
851 rp.Sync_LocalHalf(syncbuf)
852 if not syncbuf.Finish():
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700853 sys.exit(1)
Sarah Owenscecd1d82012-11-01 22:59:27 -0700854 print('info: Restarting repo with latest version', file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700855 raise RepoChangedException(['--repo-upgraded'])
856 else:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700857 print('warning: Skipped upgrade to unverified version', file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700858 else:
859 if verbose:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700860 print('repo version %s is current' % rp.work_git.describe(HEAD),
861 file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700862
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700863def _VerifyTag(project):
864 gpg_dir = os.path.expanduser('~/.repoconfig/gnupg')
865 if not os.path.exists(gpg_dir):
Sarah Owenscecd1d82012-11-01 22:59:27 -0700866 print('warning: GnuPG was not available during last "repo init"\n'
867 'warning: Cannot automatically authenticate repo."""',
868 file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700869 return True
870
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700871 try:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -0700872 cur = project.bare_git.describe(project.GetRevisionId())
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700873 except GitError:
874 cur = None
875
876 if not cur \
877 or re.compile(r'^.*-[0-9]{1,}-g[0-9a-f]{1,}$').match(cur):
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -0700878 rev = project.revisionExpr
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700879 if rev.startswith(R_HEADS):
880 rev = rev[len(R_HEADS):]
881
Sarah Owenscecd1d82012-11-01 22:59:27 -0700882 print(file=sys.stderr)
883 print("warning: project '%s' branch '%s' is not signed"
884 % (project.name, rev), file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700885 return False
886
Shawn O. Pearcef18cb762010-12-07 11:41:05 -0800887 env = os.environ.copy()
888 env['GIT_DIR'] = project.gitdir.encode()
889 env['GNUPGHOME'] = gpg_dir.encode()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700890
891 cmd = [GIT, 'tag', '-v', cur]
892 proc = subprocess.Popen(cmd,
893 stdout = subprocess.PIPE,
894 stderr = subprocess.PIPE,
895 env = env)
896 out = proc.stdout.read()
897 proc.stdout.close()
898
899 err = proc.stderr.read()
900 proc.stderr.close()
901
902 if proc.wait() != 0:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700903 print(file=sys.stderr)
904 print(out, file=sys.stderr)
905 print(err, file=sys.stderr)
906 print(file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700907 return False
908 return True
Dave Borowitz67700e92012-10-23 15:00:54 -0700909
910class _FetchTimes(object):
Dave Borowitzd9478582012-10-23 16:35:39 -0700911 _ALPHA = 0.5
912
Dave Borowitz67700e92012-10-23 15:00:54 -0700913 def __init__(self, manifest):
Anthony King85b24ac2014-05-06 15:57:48 +0100914 self._path = os.path.join(manifest.repodir, '.repo_fetchtimes.json')
Dave Borowitz67700e92012-10-23 15:00:54 -0700915 self._times = None
Dave Borowitzd9478582012-10-23 16:35:39 -0700916 self._seen = set()
Dave Borowitz67700e92012-10-23 15:00:54 -0700917
918 def Get(self, project):
919 self._Load()
920 return self._times.get(project.name, _ONE_DAY_S)
921
922 def Set(self, project, t):
Dave Borowitzd9478582012-10-23 16:35:39 -0700923 self._Load()
924 name = project.name
925 old = self._times.get(name, t)
926 self._seen.add(name)
927 a = self._ALPHA
928 self._times[name] = (a*t) + ((1-a) * old)
Dave Borowitz67700e92012-10-23 15:00:54 -0700929
930 def _Load(self):
931 if self._times is None:
932 try:
Anthony King85b24ac2014-05-06 15:57:48 +0100933 f = open(self._path)
Dave Borowitz67700e92012-10-23 15:00:54 -0700934 try:
Anthony King85b24ac2014-05-06 15:57:48 +0100935 self._times = json.load(f)
936 finally:
937 f.close()
938 except (IOError, ValueError):
939 try:
940 os.remove(self._path)
941 except OSError:
942 pass
943 self._times = {}
Dave Borowitz67700e92012-10-23 15:00:54 -0700944
945 def Save(self):
946 if self._times is None:
947 return
Dave Borowitzd9478582012-10-23 16:35:39 -0700948
949 to_delete = []
950 for name in self._times:
951 if name not in self._seen:
952 to_delete.append(name)
953 for name in to_delete:
954 del self._times[name]
955
Dave Borowitz67700e92012-10-23 15:00:54 -0700956 try:
Anthony King85b24ac2014-05-06 15:57:48 +0100957 f = open(self._path, 'w')
Dave Borowitz67700e92012-10-23 15:00:54 -0700958 try:
Anthony King85b24ac2014-05-06 15:57:48 +0100959 json.dump(self._times, f, indent=2)
960 finally:
961 f.close()
962 except (IOError, TypeError):
963 try:
964 os.remove(self._path)
965 except OSError:
966 pass
Dan Willemsen0745bb22015-08-17 13:41:45 -0700967
968# This is a replacement for xmlrpc.client.Transport using urllib2
969# and supporting persistent-http[s]. It cannot change hosts from
970# request to request like the normal transport, the real url
971# is passed during initialization.
972class PersistentTransport(xmlrpc.client.Transport):
973 def __init__(self, orig_host):
974 self.orig_host = orig_host
975
976 def request(self, host, handler, request_body, verbose=False):
977 with GetUrlCookieFile(self.orig_host, not verbose) as (cookiefile, proxy):
978 # Python doesn't understand cookies with the #HttpOnly_ prefix
979 # Since we're only using them for HTTP, copy the file temporarily,
980 # stripping those prefixes away.
Dan Willemsen3010e5b2015-08-20 10:09:20 -0700981 if cookiefile:
982 tmpcookiefile = tempfile.NamedTemporaryFile()
David Pursehouse4c5f74e2015-10-02 11:10:10 +0900983 tmpcookiefile.write("# HTTP Cookie File")
Dan Willemsen3010e5b2015-08-20 10:09:20 -0700984 try:
985 with open(cookiefile) as f:
986 for line in f:
987 if line.startswith("#HttpOnly_"):
988 line = line[len("#HttpOnly_"):]
989 tmpcookiefile.write(line)
990 tmpcookiefile.flush()
Dan Willemsen0745bb22015-08-17 13:41:45 -0700991
Dan Willemsen3010e5b2015-08-20 10:09:20 -0700992 cookiejar = cookielib.MozillaCookieJar(tmpcookiefile.name)
David Pursehouseb1ad2192015-09-30 10:35:43 +0900993 try:
994 cookiejar.load()
995 except cookielib.LoadError:
996 cookiejar = cookielib.CookieJar()
Dan Willemsen3010e5b2015-08-20 10:09:20 -0700997 finally:
998 tmpcookiefile.close()
999 else:
1000 cookiejar = cookielib.CookieJar()
Dan Willemsen0745bb22015-08-17 13:41:45 -07001001
1002 proxyhandler = urllib.request.ProxyHandler
1003 if proxy:
1004 proxyhandler = urllib.request.ProxyHandler({
1005 "http": proxy,
1006 "https": proxy })
1007
1008 opener = urllib.request.build_opener(
1009 urllib.request.HTTPCookieProcessor(cookiejar),
1010 proxyhandler)
1011
1012 url = urllib.parse.urljoin(self.orig_host, handler)
1013 parse_results = urllib.parse.urlparse(url)
1014
1015 scheme = parse_results.scheme
1016 if scheme == 'persistent-http':
1017 scheme = 'http'
1018 if scheme == 'persistent-https':
1019 # If we're proxying through persistent-https, use http. The
1020 # proxy itself will do the https.
1021 if proxy:
1022 scheme = 'http'
1023 else:
1024 scheme = 'https'
1025
1026 # Parse out any authentication information using the base class
1027 host, extra_headers, _ = self.get_host_info(parse_results.netloc)
1028
1029 url = urllib.parse.urlunparse((
1030 scheme,
1031 host,
1032 parse_results.path,
1033 parse_results.params,
1034 parse_results.query,
1035 parse_results.fragment))
1036
1037 request = urllib.request.Request(url, request_body)
1038 if extra_headers is not None:
1039 for (name, header) in extra_headers:
1040 request.add_header(name, header)
1041 request.add_header('Content-Type', 'text/xml')
1042 try:
1043 response = opener.open(request)
1044 except urllib.error.HTTPError as e:
1045 if e.code == 501:
1046 # We may have been redirected through a login process
1047 # but our POST turned into a GET. Retry.
1048 response = opener.open(request)
1049 else:
1050 raise
1051
1052 p, u = xmlrpc.client.getparser()
1053 while 1:
1054 data = response.read(1024)
1055 if not data:
1056 break
1057 p.feed(data)
1058 p.close()
1059 return u.close()
1060
1061 def close(self):
1062 pass
1063