blob: 7ba9ebfc83f7234af80b20a99d3780767ad99d15 [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
David James89ece422014-01-09 18:51:58 -0800258 def _FetchProjectList(self, opt, projects, *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.
David James89ece422014-01-09 18:51:58 -0800266 *args, **kwargs: Remaining arguments to pass to _FetchHelper. See the
David James8d201162013-10-11 17:03:19 -0700267 _FetchHelper docstring for details.
268 """
269 for project in projects:
David James89ece422014-01-09 18:51:58 -0800270 success = self._FetchHelper(opt, project, *args, **kwargs)
David James8d201162013-10-11 17:03:19 -0700271 if not success and not opt.force_broken:
272 break
273
274 def _FetchHelper(self, opt, project, lock, fetched, pm, sem, err_event):
275 """Fetch git objects for a single project.
276
David Pursehousec1b86a22012-11-14 11:36:51 +0900277 Args:
278 opt: Program options returned from optparse. See _Options().
279 project: Project object for the project to fetch.
280 lock: Lock for accessing objects that are shared amongst multiple
281 _FetchHelper() threads.
282 fetched: set object that we will add project.gitdir to when we're done
283 (with our lock held).
284 pm: Instance of a Project object. We will call pm.update() (with our
285 lock held).
286 sem: We'll release() this semaphore when we exit so that another thread
287 can be started up.
288 err_event: We'll set this event in the case of an error (after printing
289 out info about the error).
David James8d201162013-10-11 17:03:19 -0700290
291 Returns:
292 Whether the fetch was successful.
David Pursehousec1b86a22012-11-14 11:36:51 +0900293 """
294 # We'll set to true once we've locked the lock.
295 did_lock = False
Doug Andersonfc06ced2011-03-16 15:49:18 -0700296
Chirayu Desaifef4ae72013-04-12 14:54:32 +0530297 if not opt.quiet:
298 print('Fetching project %s' % project.name)
299
David Pursehousec1b86a22012-11-14 11:36:51 +0900300 # Encapsulate everything in a try/except/finally so that:
301 # - We always set err_event in the case of an exception.
302 # - We always make sure we call sem.release().
303 # - We always make sure we unlock the lock if we locked it.
304 try:
Doug Andersonfc06ced2011-03-16 15:49:18 -0700305 try:
David Pursehousec1b86a22012-11-14 11:36:51 +0900306 start = time.time()
307 success = project.Sync_NetworkHalf(
308 quiet=opt.quiet,
309 current_branch_only=opt.current_branch_only,
Kevin Degiabaa7f32014-11-12 11:27:45 -0700310 force_sync=opt.force_sync,
Mitchel Humpherys597868b2012-10-29 10:18:34 -0700311 clone_bundle=not opt.no_clone_bundle,
David Pursehouseb1553542014-09-04 21:28:09 +0900312 no_tags=opt.no_tags, archive=self.manifest.IsArchive,
David Pursehouse74cfd272015-10-14 10:50:15 +0900313 optimized_fetch=opt.optimized_fetch,
314 prune=opt.prune)
David Pursehousec1b86a22012-11-14 11:36:51 +0900315 self._fetch_times.Set(project, time.time() - start)
Doug Andersonfc06ced2011-03-16 15:49:18 -0700316
David Pursehousec1b86a22012-11-14 11:36:51 +0900317 # Lock around all the rest of the code, since printing, updating a set
318 # and Progress.update() are not thread safe.
319 lock.acquire()
320 did_lock = True
Doug Andersonfc06ced2011-03-16 15:49:18 -0700321
David Pursehousec1b86a22012-11-14 11:36:51 +0900322 if not success:
Hu Xiuyune9becc02015-11-25 15:52:26 +0800323 err_event.set()
David Pursehousec1b86a22012-11-14 11:36:51 +0900324 print('error: Cannot fetch %s' % project.name, file=sys.stderr)
325 if opt.force_broken:
326 print('warn: --force-broken, continuing to sync',
327 file=sys.stderr)
328 else:
329 raise _FetchError()
Doug Andersonfc06ced2011-03-16 15:49:18 -0700330
David Pursehousec1b86a22012-11-14 11:36:51 +0900331 fetched.add(project.gitdir)
332 pm.update()
333 except _FetchError:
Hu Xiuyune9becc02015-11-25 15:52:26 +0800334 pass
Dan Sandlerc5cd4332015-07-31 09:37:53 -0400335 except Exception as e:
336 print('error: Cannot fetch %s (%s: %s)' \
337 % (project.name, type(e).__name__, str(e)), file=sys.stderr)
David Pursehousec1b86a22012-11-14 11:36:51 +0900338 err_event.set()
339 raise
340 finally:
341 if did_lock:
342 lock.release()
343 sem.release()
Roy Lee18afd7f2010-05-09 04:32:08 +0800344
David James8d201162013-10-11 17:03:19 -0700345 return success
346
Shawn O. Pearce16614f82010-10-29 12:05:43 -0700347 def _Fetch(self, projects, opt):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700348 fetched = set()
David James89ece422014-01-09 18:51:58 -0800349 lock = _threading.Lock()
Shawn O. Pearce68194f42009-04-10 16:48:52 -0700350 pm = Progress('Fetching projects', len(projects))
Roy Lee18afd7f2010-05-09 04:32:08 +0800351
David James89ece422014-01-09 18:51:58 -0800352 objdir_project_map = dict()
353 for project in projects:
354 objdir_project_map.setdefault(project.objdir, []).append(project)
David James8d201162013-10-11 17:03:19 -0700355
David James89ece422014-01-09 18:51:58 -0800356 threads = set()
357 sem = _threading.Semaphore(self.jobs)
358 err_event = _threading.Event()
359 for project_list in objdir_project_map.values():
360 # Check for any errors before running any more tasks.
361 # ...we'll let existing threads finish, though.
362 if err_event.isSet() and not opt.force_broken:
363 break
Doug Andersonfc06ced2011-03-16 15:49:18 -0700364
David James89ece422014-01-09 18:51:58 -0800365 sem.acquire()
366 kwargs = dict(opt=opt,
367 projects=project_list,
368 lock=lock,
369 fetched=fetched,
370 pm=pm,
371 sem=sem,
372 err_event=err_event)
373 if self.jobs > 1:
David James8d201162013-10-11 17:03:19 -0700374 t = _threading.Thread(target = self._FetchProjectList,
David James89ece422014-01-09 18:51:58 -0800375 kwargs = kwargs)
David 'Digit' Turnere2126652012-09-05 10:35:06 +0200376 # Ensure that Ctrl-C will not freeze the repo process.
377 t.daemon = True
Roy Lee18afd7f2010-05-09 04:32:08 +0800378 threads.add(t)
379 t.start()
David James89ece422014-01-09 18:51:58 -0800380 else:
381 self._FetchProjectList(**kwargs)
Roy Lee18afd7f2010-05-09 04:32:08 +0800382
David James89ece422014-01-09 18:51:58 -0800383 for t in threads:
384 t.join()
Roy Lee18afd7f2010-05-09 04:32:08 +0800385
David James89ece422014-01-09 18:51:58 -0800386 # If we saw an error, exit with code 1 so that other scripts can check.
387 if err_event.isSet():
388 print('\nerror: Exited sync due to fetch errors', file=sys.stderr)
389 sys.exit(1)
Doug Andersonfc06ced2011-03-16 15:49:18 -0700390
Shawn O. Pearce68194f42009-04-10 16:48:52 -0700391 pm.end()
Dave Borowitz67700e92012-10-23 15:00:54 -0700392 self._fetch_times.Save()
Dave Borowitz18857212012-10-23 17:02:59 -0700393
Julien Campergue335f5ef2013-10-16 11:02:35 +0200394 if not self.manifest.IsArchive:
395 self._GCProjects(projects)
396
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700397 return fetched
398
Dave Borowitz18857212012-10-23 17:02:59 -0700399 def _GCProjects(self, projects):
Gabe Black2ff30292014-10-09 17:54:35 -0700400 gc_gitdirs = {}
David James8d201162013-10-11 17:03:19 -0700401 for project in projects:
Gabe Black2ff30292014-10-09 17:54:35 -0700402 if len(project.manifest.GetProjectsWithName(project.name)) > 1:
403 print('Shared project %s found, disabling pruning.' % project.name)
404 project.bare_git.config('--replace-all', 'gc.pruneExpire', 'never')
405 gc_gitdirs[project.gitdir] = project.bare_git
David James8d201162013-10-11 17:03:19 -0700406
Dave Borowitze2152672012-10-31 12:24:38 -0700407 has_dash_c = git_require((1, 7, 2))
408 if multiprocessing and has_dash_c:
Dave Borowitz18857212012-10-23 17:02:59 -0700409 cpu_count = multiprocessing.cpu_count()
410 else:
411 cpu_count = 1
412 jobs = min(self.jobs, cpu_count)
413
414 if jobs < 2:
Gabe Black2ff30292014-10-09 17:54:35 -0700415 for bare_git in gc_gitdirs.values():
David James8d201162013-10-11 17:03:19 -0700416 bare_git.gc('--auto')
Dave Borowitz18857212012-10-23 17:02:59 -0700417 return
418
419 config = {'pack.threads': cpu_count / jobs if cpu_count > jobs else 1}
420
421 threads = set()
422 sem = _threading.Semaphore(jobs)
423 err_event = _threading.Event()
424
David James8d201162013-10-11 17:03:19 -0700425 def GC(bare_git):
Dave Borowitz18857212012-10-23 17:02:59 -0700426 try:
427 try:
David James8d201162013-10-11 17:03:19 -0700428 bare_git.gc('--auto', config=config)
Dave Borowitz18857212012-10-23 17:02:59 -0700429 except GitError:
430 err_event.set()
431 except:
432 err_event.set()
433 raise
434 finally:
435 sem.release()
436
Gabe Black2ff30292014-10-09 17:54:35 -0700437 for bare_git in gc_gitdirs.values():
Dave Borowitz18857212012-10-23 17:02:59 -0700438 if err_event.isSet():
439 break
440 sem.acquire()
David James8d201162013-10-11 17:03:19 -0700441 t = _threading.Thread(target=GC, args=(bare_git,))
Dave Borowitz18857212012-10-23 17:02:59 -0700442 t.daemon = True
443 threads.add(t)
444 t.start()
445
446 for t in threads:
447 t.join()
448
449 if err_event.isSet():
Sarah Owenscecd1d82012-11-01 22:59:27 -0700450 print('\nerror: Exited sync due to gc errors', file=sys.stderr)
Dave Borowitz18857212012-10-23 17:02:59 -0700451 sys.exit(1)
452
Tim Kilbourn07669002013-03-08 15:02:49 -0800453 def _ReloadManifest(self, manifest_name=None):
454 if manifest_name:
455 # Override calls _Unload already
456 self.manifest.Override(manifest_name)
457 else:
458 self.manifest._Unload()
459
Dan Willemsen43507912016-09-01 16:26:02 -0700460 def _DeleteProject(self, path):
461 print('Deleting obsolete path %s' % path, file=sys.stderr)
462
463 # Delete the .git directory first, so we're less likely to have a partially
464 # working git repository around. There shouldn't be any git projects here,
465 # so rmtree works.
466 try:
467 shutil.rmtree(os.path.join(path, '.git'))
468 except OSError:
469 print('Failed to remove %s' % os.path.join(path, '.git'), file=sys.stderr)
470 print('error: Failed to delete obsolete path %s' % path, file=sys.stderr)
471 print(' remove manually, then run sync again', file=sys.stderr)
472 return -1
473
474 # Delete everything under the worktree, except for directories that contain
475 # another git project
476 dirs_to_remove = []
477 failed = False
478 for root, dirs, files in os.walk(path):
479 for f in files:
480 try:
481 os.remove(os.path.join(root, f))
482 except OSError:
483 print('Failed to remove %s' % os.path.join(root, f), file=sys.stderr)
484 failed = True
485 dirs[:] = [d for d in dirs
486 if not os.path.lexists(os.path.join(root, d, '.git'))]
487 dirs_to_remove += [os.path.join(root, d) for d in dirs
488 if os.path.join(root, d) not in dirs_to_remove]
489 for d in reversed(dirs_to_remove):
Dan Willemseneceeb1b2016-09-25 18:24:27 -0700490 if os.path.islink(d):
491 try:
492 os.remove(d)
493 except OSError:
494 print('Failed to remove %s' % os.path.join(root, d), file=sys.stderr)
495 failed = True
496 elif len(os.listdir(d)) == 0:
Dan Willemsen43507912016-09-01 16:26:02 -0700497 try:
498 os.rmdir(d)
499 except OSError:
500 print('Failed to remove %s' % os.path.join(root, d), file=sys.stderr)
501 failed = True
502 continue
503 if failed:
504 print('error: Failed to delete obsolete path %s' % path, file=sys.stderr)
505 print(' remove manually, then run sync again', file=sys.stderr)
506 return -1
507
508 # Try deleting parent dirs if they are empty
509 project_dir = path
510 while project_dir != self.manifest.topdir:
511 if len(os.listdir(project_dir)) == 0:
512 os.rmdir(project_dir)
513 else:
514 break
515 project_dir = os.path.dirname(project_dir)
516
517 return 0
518
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700519 def UpdateProjectList(self):
520 new_project_paths = []
Colin Cross5acde752012-03-28 20:15:45 -0700521 for project in self.GetProjects(None, missing_ok=True):
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700522 if project.relpath:
523 new_project_paths.append(project.relpath)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700524 file_name = 'project.list'
525 file_path = os.path.join(self.manifest.repodir, file_name)
526 old_project_paths = []
527
528 if os.path.exists(file_path):
529 fd = open(file_path, 'r')
530 try:
531 old_project_paths = fd.read().split('\n')
532 finally:
533 fd.close()
534 for path in old_project_paths:
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700535 if not path:
536 continue
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700537 if path not in new_project_paths:
David Pursehouse8a68ff92012-09-24 12:15:13 +0900538 # If the path has already been deleted, we don't need to do it
Dan Willemsen43507912016-09-01 16:26:02 -0700539 gitdir = os.path.join(self.manifest.topdir, path, '.git')
540 if os.path.exists(gitdir):
David Pursehousec1b86a22012-11-14 11:36:51 +0900541 project = Project(
542 manifest = self.manifest,
543 name = path,
544 remote = RemoteSpec('origin'),
David James8d201162013-10-11 17:03:19 -0700545 gitdir = gitdir,
546 objdir = gitdir,
David Pursehousec1b86a22012-11-14 11:36:51 +0900547 worktree = os.path.join(self.manifest.topdir, path),
548 relpath = path,
549 revisionExpr = 'HEAD',
550 revisionId = None,
551 groups = None)
Anthonyf3fdf822009-09-26 13:38:52 -0400552
David Pursehousec1b86a22012-11-14 11:36:51 +0900553 if project.IsDirty():
David Pursehouse2f9e7e42013-03-05 17:26:46 +0900554 print('error: Cannot remove project "%s": uncommitted changes '
David Pursehousec1b86a22012-11-14 11:36:51 +0900555 'are present' % project.relpath, file=sys.stderr)
556 print(' commit changes, then run sync again',
557 file=sys.stderr)
558 return -1
Dan Willemsen43507912016-09-01 16:26:02 -0700559 elif self._DeleteProject(project.worktree):
560 return -1
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700561
Shawn O. Pearce9fb29ce2009-06-04 20:41:02 -0700562 new_project_paths.sort()
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700563 fd = open(file_path, 'w')
564 try:
565 fd.write('\n'.join(new_project_paths))
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700566 fd.write('\n')
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700567 finally:
568 fd.close()
569 return 0
570
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700571 def Execute(self, opt, args):
Roy Lee18afd7f2010-05-09 04:32:08 +0800572 if opt.jobs:
573 self.jobs = opt.jobs
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -0700574 if self.jobs > 1:
575 soft_limit, _ = _rlimit_nofile()
576 self.jobs = min(self.jobs, (soft_limit - 5) / 3)
577
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700578 if opt.network_only and opt.detach_head:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700579 print('error: cannot combine -n and -d', file=sys.stderr)
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700580 sys.exit(1)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700581 if opt.network_only and opt.local_only:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700582 print('error: cannot combine -n and -l', file=sys.stderr)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700583 sys.exit(1)
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500584 if opt.manifest_name and opt.smart_sync:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700585 print('error: cannot combine -m and -s', file=sys.stderr)
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500586 sys.exit(1)
587 if opt.manifest_name and opt.smart_tag:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700588 print('error: cannot combine -m and -t', file=sys.stderr)
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500589 sys.exit(1)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900590 if opt.manifest_server_username or opt.manifest_server_password:
591 if not (opt.smart_sync or opt.smart_tag):
Sarah Owenscecd1d82012-11-01 22:59:27 -0700592 print('error: -u and -p may only be combined with -s or -t',
593 file=sys.stderr)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900594 sys.exit(1)
595 if None in [opt.manifest_server_username, opt.manifest_server_password]:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700596 print('error: both -u and -p must be given', file=sys.stderr)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900597 sys.exit(1)
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500598
599 if opt.manifest_name:
600 self.manifest.Override(opt.manifest_name)
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700601
Chirayu Desaia892b102013-06-11 14:18:46 +0530602 manifest_name = opt.manifest_name
David Pursehouse59b41742015-05-07 14:36:09 +0900603 smart_sync_manifest_name = "smart_sync_override.xml"
604 smart_sync_manifest_path = os.path.join(
605 self.manifest.manifestProject.worktree, smart_sync_manifest_name)
Chirayu Desaia892b102013-06-11 14:18:46 +0530606
Victor Boivie08c880d2011-04-19 10:32:52 +0200607 if opt.smart_sync or opt.smart_tag:
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700608 if not self.manifest.manifest_server:
David Pursehouse2f9e7e42013-03-05 17:26:46 +0900609 print('error: cannot smart sync: no manifest server defined in '
Sarah Owenscecd1d82012-11-01 22:59:27 -0700610 'manifest', file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700611 sys.exit(1)
David Pursehouse86d973d2012-08-24 10:21:02 +0900612
613 manifest_server = self.manifest.manifest_server
David Pursehousefb99c712013-09-25 11:09:34 +0900614 if not opt.quiet:
615 print('Using manifest server %s' % manifest_server)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900616
David Pursehouse86d973d2012-08-24 10:21:02 +0900617 if not '@' in manifest_server:
David Pursehousecf76b1b2012-09-14 10:31:42 +0900618 username = None
619 password = None
620 if opt.manifest_server_username and opt.manifest_server_password:
621 username = opt.manifest_server_username
622 password = opt.manifest_server_password
David Pursehouse86d973d2012-08-24 10:21:02 +0900623 else:
624 try:
David Pursehousecf76b1b2012-09-14 10:31:42 +0900625 info = netrc.netrc()
626 except IOError:
David Pursehouseba7bc732015-08-20 16:55:42 +0900627 # .netrc file does not exist or could not be opened
628 pass
David Pursehouse86d973d2012-08-24 10:21:02 +0900629 else:
David Pursehousecf76b1b2012-09-14 10:31:42 +0900630 try:
Chirayu Desaidb2ad9d2013-06-11 13:42:25 +0530631 parse_result = urllib.parse.urlparse(manifest_server)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900632 if parse_result.hostname:
David Pursehouseba7bc732015-08-20 16:55:42 +0900633 auth = info.authenticators(parse_result.hostname)
634 if auth:
635 username, _account, password = auth
636 else:
637 print('No credentials found for %s in .netrc'
638 % parse_result.hostname, file=sys.stderr)
Sarah Owensa5be53f2012-09-09 15:37:57 -0700639 except netrc.NetrcParseError as e:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700640 print('Error parsing .netrc file: %s' % e, file=sys.stderr)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900641
642 if (username and password):
643 manifest_server = manifest_server.replace('://', '://%s:%s@' %
644 (username, password),
645 1)
David Pursehouse86d973d2012-08-24 10:21:02 +0900646
Dan Willemsen0745bb22015-08-17 13:41:45 -0700647 transport = PersistentTransport(manifest_server)
648 if manifest_server.startswith('persistent-'):
649 manifest_server = manifest_server[len('persistent-'):]
650
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700651 try:
Dan Willemsen0745bb22015-08-17 13:41:45 -0700652 server = xmlrpc.client.Server(manifest_server, transport=transport)
Victor Boivie08c880d2011-04-19 10:32:52 +0200653 if opt.smart_sync:
654 p = self.manifest.manifestProject
655 b = p.GetBranch(p.CurrentBranch)
656 branch = b.merge
657 if branch.startswith(R_HEADS):
658 branch = branch[len(R_HEADS):]
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700659
Victor Boivie08c880d2011-04-19 10:32:52 +0200660 env = os.environ.copy()
Jeff Davidson5cf16602014-10-02 10:13:38 -0700661 if 'SYNC_TARGET' in env:
662 target = env['SYNC_TARGET']
663 [success, manifest_str] = server.GetApprovedManifest(branch, target)
664 elif 'TARGET_PRODUCT' in env and 'TARGET_BUILD_VARIANT' in env:
Victor Boivie08c880d2011-04-19 10:32:52 +0200665 target = '%s-%s' % (env['TARGET_PRODUCT'],
666 env['TARGET_BUILD_VARIANT'])
667 [success, manifest_str] = server.GetApprovedManifest(branch, target)
668 else:
669 [success, manifest_str] = server.GetApprovedManifest(branch)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700670 else:
Victor Boivie08c880d2011-04-19 10:32:52 +0200671 assert(opt.smart_tag)
672 [success, manifest_str] = server.GetManifest(opt.smart_tag)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700673
674 if success:
David Pursehouse59b41742015-05-07 14:36:09 +0900675 manifest_name = smart_sync_manifest_name
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700676 try:
David Pursehouse59b41742015-05-07 14:36:09 +0900677 f = open(smart_sync_manifest_path, 'w')
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700678 try:
679 f.write(manifest_str)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700680 finally:
681 f.close()
David Pursehouse727cc3e2015-05-07 14:16:49 +0900682 except IOError as e:
683 print('error: cannot write manifest to %s:\n%s'
David Pursehouse59b41742015-05-07 14:36:09 +0900684 % (smart_sync_manifest_path, e),
Sarah Owenscecd1d82012-11-01 22:59:27 -0700685 file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700686 sys.exit(1)
Victor Boivie53a6c5d2013-03-19 12:20:52 +0100687 self._ReloadManifest(manifest_name)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700688 else:
David Pursehouse351fe2c2013-09-25 17:54:26 +0900689 print('error: manifest server RPC call failed: %s' %
690 manifest_str, file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700691 sys.exit(1)
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530692 except (socket.error, IOError, xmlrpc.client.Fault) as e:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700693 print('error: cannot connect to manifest server %s:\n%s'
694 % (self.manifest.manifest_server, e), file=sys.stderr)
David Pursehousebd489c42012-08-23 10:21:26 +0900695 sys.exit(1)
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530696 except xmlrpc.client.ProtocolError as e:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700697 print('error: cannot connect to manifest server %s:\n%d %s'
698 % (self.manifest.manifest_server, e.errcode, e.errmsg),
699 file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700700 sys.exit(1)
David Pursehouse59b41742015-05-07 14:36:09 +0900701 else: # Not smart sync or smart tag mode
702 if os.path.isfile(smart_sync_manifest_path):
703 try:
704 os.remove(smart_sync_manifest_path)
705 except OSError as e:
706 print('error: failed to remove existing smart sync override manifest: %s' %
707 e, file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700708
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700709 rp = self.manifest.repoProject
710 rp.PreSync()
711
712 mp = self.manifest.manifestProject
713 mp.PreSync()
714
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800715 if opt.repo_upgraded:
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -0700716 _PostRepoUpgrade(self.manifest, quiet=opt.quiet)
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800717
Nico Sallembien9bb18162009-12-07 15:38:01 -0800718 if not opt.local_only:
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -0700719 mp.Sync_NetworkHalf(quiet=opt.quiet,
Mitchel Humpherys597868b2012-10-29 10:18:34 -0700720 current_branch_only=opt.current_branch_only,
David Pursehouseb1553542014-09-04 21:28:09 +0900721 no_tags=opt.no_tags,
722 optimized_fetch=opt.optimized_fetch)
Nico Sallembien9bb18162009-12-07 15:38:01 -0800723
724 if mp.HasChanges:
725 syncbuf = SyncBuffer(mp.config)
726 mp.Sync_LocalHalf(syncbuf)
727 if not syncbuf.Finish():
728 sys.exit(1)
Victor Boivie53a6c5d2013-03-19 12:20:52 +0100729 self._ReloadManifest(manifest_name)
Shawn O. Pearcec4657962011-09-26 09:08:01 -0700730 if opt.jobs is None:
731 self.jobs = self.manifest.default.sync_j
Simran Basib9a1b732015-08-20 12:19:28 -0700732
Simran Basib9a1b732015-08-20 12:19:28 -0700733 if self.gitc_manifest:
734 gitc_manifest_projects = self.GetProjects(args,
Simran Basib9a1b732015-08-20 12:19:28 -0700735 missing_ok=True)
736 gitc_projects = []
737 opened_projects = []
738 for project in gitc_manifest_projects:
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700739 if project.relpath in self.gitc_manifest.paths and \
740 self.gitc_manifest.paths[project.relpath].old_revision:
741 opened_projects.append(project.relpath)
Simran Basib9a1b732015-08-20 12:19:28 -0700742 else:
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700743 gitc_projects.append(project.relpath)
Simran Basib9a1b732015-08-20 12:19:28 -0700744
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700745 if not args:
746 gitc_projects = None
747
748 if gitc_projects != [] and not opt.local_only:
Simran Basib9a1b732015-08-20 12:19:28 -0700749 print('Updating GITC client: %s' % self.gitc_manifest.gitc_client_name)
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700750 manifest = GitcManifest(self.repodir, self.gitc_manifest.gitc_client_name)
751 if manifest_name:
752 manifest.Override(manifest_name)
753 else:
754 manifest.Override(self.manifest.manifestFile)
755 gitc_utils.generate_gitc_manifest(self.gitc_manifest,
756 manifest,
Simran Basib9a1b732015-08-20 12:19:28 -0700757 gitc_projects)
758 print('GITC client successfully synced.')
759
760 # The opened projects need to be synced as normal, therefore we
761 # generate a new args list to represent the opened projects.
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700762 # TODO: make this more reliable -- if there's a project name/path overlap,
763 # this may choose the wrong project.
764 args = [os.path.relpath(self.manifest.paths[p].worktree, os.getcwd())
765 for p in opened_projects]
Simran Basib9a1b732015-08-20 12:19:28 -0700766 if not args:
767 return
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800768 all_projects = self.GetProjects(args,
769 missing_ok=True,
770 submodules_ok=opt.fetch_submodules)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700771
Dave Borowitz67700e92012-10-23 15:00:54 -0700772 self._fetch_times = _FetchTimes(self.manifest)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700773 if not opt.local_only:
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700774 to_fetch = []
775 now = time.time()
Dave Borowitz67700e92012-10-23 15:00:54 -0700776 if _ONE_DAY_S <= (now - rp.LastFetch):
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700777 to_fetch.append(rp)
David Pursehouse8a68ff92012-09-24 12:15:13 +0900778 to_fetch.extend(all_projects)
Dave Borowitz67700e92012-10-23 15:00:54 -0700779 to_fetch.sort(key=self._fetch_times.Get, reverse=True)
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700780
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800781 fetched = self._Fetch(to_fetch, opt)
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700782 _PostRepoFetch(rp, opt.no_repo_verify)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700783 if opt.network_only:
784 # bail out now; the rest touches the working tree
785 return
786
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800787 # Iteratively fetch missing and/or nested unregistered submodules
788 previously_missing_set = set()
789 while True:
Victor Boivie53a6c5d2013-03-19 12:20:52 +0100790 self._ReloadManifest(manifest_name)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800791 all_projects = self.GetProjects(args,
792 missing_ok=True,
793 submodules_ok=opt.fetch_submodules)
794 missing = []
795 for project in all_projects:
796 if project.gitdir not in fetched:
797 missing.append(project)
798 if not missing:
799 break
800 # Stop us from non-stopped fetching actually-missing repos: If set of
801 # missing repos has not been changed from last fetch, we break.
802 missing_set = set(p.name for p in missing)
803 if previously_missing_set == missing_set:
804 break
805 previously_missing_set = missing_set
806 fetched.update(self._Fetch(missing, opt))
807
Julien Campergue335f5ef2013-10-16 11:02:35 +0200808 if self.manifest.IsMirror or self.manifest.IsArchive:
Shawn O. Pearcecd1d7ff2009-06-04 16:15:53 -0700809 # bail out now, we have no working tree
810 return
811
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700812 if self.UpdateProjectList():
813 sys.exit(1)
814
Shawn O. Pearce350cde42009-04-16 11:21:18 -0700815 syncbuf = SyncBuffer(mp.config,
816 detach_head = opt.detach_head)
David Pursehouse8a68ff92012-09-24 12:15:13 +0900817 pm = Progress('Syncing work tree', len(all_projects))
818 for project in all_projects:
Shawn O. Pearce68194f42009-04-10 16:48:52 -0700819 pm.update()
Shawn O. Pearcee284ad12008-11-04 07:37:10 -0800820 if project.worktree:
Kevin Degiabaa7f32014-11-12 11:27:45 -0700821 project.Sync_LocalHalf(syncbuf, force_sync=opt.force_sync)
Shawn O. Pearce68194f42009-04-10 16:48:52 -0700822 pm.end()
Sarah Owenscecd1d82012-11-01 22:59:27 -0700823 print(file=sys.stderr)
Shawn O. Pearce350cde42009-04-16 11:21:18 -0700824 if not syncbuf.Finish():
825 sys.exit(1)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700826
Doug Anderson2b8db3c2010-11-01 15:08:06 -0700827 # If there's a notice that's supposed to print at the end of the sync, print
828 # it now...
829 if self.manifest.notice:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700830 print(self.manifest.notice)
Doug Anderson2b8db3c2010-11-01 15:08:06 -0700831
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -0700832def _PostRepoUpgrade(manifest, quiet=False):
Conley Owens094cdbe2014-01-30 15:09:59 -0800833 wrapper = Wrapper()
Conley Owensc9129d92012-10-01 16:12:28 -0700834 if wrapper.NeedSetupGnuPG():
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -0700835 wrapper.SetupGnuPG(quiet)
Conley Owensf2fe2d92014-01-29 13:53:43 -0800836 for project in manifest.projects:
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700837 if project.Exists:
838 project.PostRepoUpgrade()
839
840def _PostRepoFetch(rp, no_repo_verify=False, verbose=False):
841 if rp.HasChanges:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700842 print('info: A new version of repo is available', file=sys.stderr)
843 print(file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700844 if no_repo_verify or _VerifyTag(rp):
Shawn O. Pearce350cde42009-04-16 11:21:18 -0700845 syncbuf = SyncBuffer(rp.config)
846 rp.Sync_LocalHalf(syncbuf)
847 if not syncbuf.Finish():
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700848 sys.exit(1)
Sarah Owenscecd1d82012-11-01 22:59:27 -0700849 print('info: Restarting repo with latest version', file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700850 raise RepoChangedException(['--repo-upgraded'])
851 else:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700852 print('warning: Skipped upgrade to unverified version', file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700853 else:
854 if verbose:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700855 print('repo version %s is current' % rp.work_git.describe(HEAD),
856 file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700857
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700858def _VerifyTag(project):
859 gpg_dir = os.path.expanduser('~/.repoconfig/gnupg')
860 if not os.path.exists(gpg_dir):
Sarah Owenscecd1d82012-11-01 22:59:27 -0700861 print('warning: GnuPG was not available during last "repo init"\n'
862 'warning: Cannot automatically authenticate repo."""',
863 file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700864 return True
865
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700866 try:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -0700867 cur = project.bare_git.describe(project.GetRevisionId())
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700868 except GitError:
869 cur = None
870
871 if not cur \
872 or re.compile(r'^.*-[0-9]{1,}-g[0-9a-f]{1,}$').match(cur):
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -0700873 rev = project.revisionExpr
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700874 if rev.startswith(R_HEADS):
875 rev = rev[len(R_HEADS):]
876
Sarah Owenscecd1d82012-11-01 22:59:27 -0700877 print(file=sys.stderr)
878 print("warning: project '%s' branch '%s' is not signed"
879 % (project.name, rev), file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700880 return False
881
Shawn O. Pearcef18cb762010-12-07 11:41:05 -0800882 env = os.environ.copy()
883 env['GIT_DIR'] = project.gitdir.encode()
884 env['GNUPGHOME'] = gpg_dir.encode()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700885
886 cmd = [GIT, 'tag', '-v', cur]
887 proc = subprocess.Popen(cmd,
888 stdout = subprocess.PIPE,
889 stderr = subprocess.PIPE,
890 env = env)
891 out = proc.stdout.read()
892 proc.stdout.close()
893
894 err = proc.stderr.read()
895 proc.stderr.close()
896
897 if proc.wait() != 0:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700898 print(file=sys.stderr)
899 print(out, file=sys.stderr)
900 print(err, file=sys.stderr)
901 print(file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700902 return False
903 return True
Dave Borowitz67700e92012-10-23 15:00:54 -0700904
905class _FetchTimes(object):
Dave Borowitzd9478582012-10-23 16:35:39 -0700906 _ALPHA = 0.5
907
Dave Borowitz67700e92012-10-23 15:00:54 -0700908 def __init__(self, manifest):
Anthony King85b24ac2014-05-06 15:57:48 +0100909 self._path = os.path.join(manifest.repodir, '.repo_fetchtimes.json')
Dave Borowitz67700e92012-10-23 15:00:54 -0700910 self._times = None
Dave Borowitzd9478582012-10-23 16:35:39 -0700911 self._seen = set()
Dave Borowitz67700e92012-10-23 15:00:54 -0700912
913 def Get(self, project):
914 self._Load()
915 return self._times.get(project.name, _ONE_DAY_S)
916
917 def Set(self, project, t):
Dave Borowitzd9478582012-10-23 16:35:39 -0700918 self._Load()
919 name = project.name
920 old = self._times.get(name, t)
921 self._seen.add(name)
922 a = self._ALPHA
923 self._times[name] = (a*t) + ((1-a) * old)
Dave Borowitz67700e92012-10-23 15:00:54 -0700924
925 def _Load(self):
926 if self._times is None:
927 try:
Anthony King85b24ac2014-05-06 15:57:48 +0100928 f = open(self._path)
Dave Borowitz67700e92012-10-23 15:00:54 -0700929 try:
Anthony King85b24ac2014-05-06 15:57:48 +0100930 self._times = json.load(f)
931 finally:
932 f.close()
933 except (IOError, ValueError):
934 try:
935 os.remove(self._path)
936 except OSError:
937 pass
938 self._times = {}
Dave Borowitz67700e92012-10-23 15:00:54 -0700939
940 def Save(self):
941 if self._times is None:
942 return
Dave Borowitzd9478582012-10-23 16:35:39 -0700943
944 to_delete = []
945 for name in self._times:
946 if name not in self._seen:
947 to_delete.append(name)
948 for name in to_delete:
949 del self._times[name]
950
Dave Borowitz67700e92012-10-23 15:00:54 -0700951 try:
Anthony King85b24ac2014-05-06 15:57:48 +0100952 f = open(self._path, 'w')
Dave Borowitz67700e92012-10-23 15:00:54 -0700953 try:
Anthony King85b24ac2014-05-06 15:57:48 +0100954 json.dump(self._times, f, indent=2)
955 finally:
956 f.close()
957 except (IOError, TypeError):
958 try:
959 os.remove(self._path)
960 except OSError:
961 pass
Dan Willemsen0745bb22015-08-17 13:41:45 -0700962
963# This is a replacement for xmlrpc.client.Transport using urllib2
964# and supporting persistent-http[s]. It cannot change hosts from
965# request to request like the normal transport, the real url
966# is passed during initialization.
967class PersistentTransport(xmlrpc.client.Transport):
968 def __init__(self, orig_host):
969 self.orig_host = orig_host
970
971 def request(self, host, handler, request_body, verbose=False):
972 with GetUrlCookieFile(self.orig_host, not verbose) as (cookiefile, proxy):
973 # Python doesn't understand cookies with the #HttpOnly_ prefix
974 # Since we're only using them for HTTP, copy the file temporarily,
975 # stripping those prefixes away.
Dan Willemsen3010e5b2015-08-20 10:09:20 -0700976 if cookiefile:
977 tmpcookiefile = tempfile.NamedTemporaryFile()
David Pursehouse4c5f74e2015-10-02 11:10:10 +0900978 tmpcookiefile.write("# HTTP Cookie File")
Dan Willemsen3010e5b2015-08-20 10:09:20 -0700979 try:
980 with open(cookiefile) as f:
981 for line in f:
982 if line.startswith("#HttpOnly_"):
983 line = line[len("#HttpOnly_"):]
984 tmpcookiefile.write(line)
985 tmpcookiefile.flush()
Dan Willemsen0745bb22015-08-17 13:41:45 -0700986
Dan Willemsen3010e5b2015-08-20 10:09:20 -0700987 cookiejar = cookielib.MozillaCookieJar(tmpcookiefile.name)
David Pursehouseb1ad2192015-09-30 10:35:43 +0900988 try:
989 cookiejar.load()
990 except cookielib.LoadError:
991 cookiejar = cookielib.CookieJar()
Dan Willemsen3010e5b2015-08-20 10:09:20 -0700992 finally:
993 tmpcookiefile.close()
994 else:
995 cookiejar = cookielib.CookieJar()
Dan Willemsen0745bb22015-08-17 13:41:45 -0700996
997 proxyhandler = urllib.request.ProxyHandler
998 if proxy:
999 proxyhandler = urllib.request.ProxyHandler({
1000 "http": proxy,
1001 "https": proxy })
1002
1003 opener = urllib.request.build_opener(
1004 urllib.request.HTTPCookieProcessor(cookiejar),
1005 proxyhandler)
1006
1007 url = urllib.parse.urljoin(self.orig_host, handler)
1008 parse_results = urllib.parse.urlparse(url)
1009
1010 scheme = parse_results.scheme
1011 if scheme == 'persistent-http':
1012 scheme = 'http'
1013 if scheme == 'persistent-https':
1014 # If we're proxying through persistent-https, use http. The
1015 # proxy itself will do the https.
1016 if proxy:
1017 scheme = 'http'
1018 else:
1019 scheme = 'https'
1020
1021 # Parse out any authentication information using the base class
1022 host, extra_headers, _ = self.get_host_info(parse_results.netloc)
1023
1024 url = urllib.parse.urlunparse((
1025 scheme,
1026 host,
1027 parse_results.path,
1028 parse_results.params,
1029 parse_results.query,
1030 parse_results.fragment))
1031
1032 request = urllib.request.Request(url, request_body)
1033 if extra_headers is not None:
1034 for (name, header) in extra_headers:
1035 request.add_header(name, header)
1036 request.add_header('Content-Type', 'text/xml')
1037 try:
1038 response = opener.open(request)
1039 except urllib.error.HTTPError as e:
1040 if e.code == 501:
1041 # We may have been redirected through a login process
1042 # but our POST turned into a GET. Retry.
1043 response = opener.open(request)
1044 else:
1045 raise
1046
1047 p, u = xmlrpc.client.getparser()
1048 while 1:
1049 data = response.read(1024)
1050 if not data:
1051 break
1052 p.feed(data)
1053 p.close()
1054 return u.close()
1055
1056 def close(self):
1057 pass
1058