blob: cc0b17e93cacd2e2feec363d51c5f784e3ec0682 [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):
490 if len(os.listdir(d)) == 0:
491 try:
492 os.rmdir(d)
493 except OSError:
494 print('Failed to remove %s' % os.path.join(root, d), file=sys.stderr)
495 failed = True
496 continue
497 if failed:
498 print('error: Failed to delete obsolete path %s' % path, file=sys.stderr)
499 print(' remove manually, then run sync again', file=sys.stderr)
500 return -1
501
502 # Try deleting parent dirs if they are empty
503 project_dir = path
504 while project_dir != self.manifest.topdir:
505 if len(os.listdir(project_dir)) == 0:
506 os.rmdir(project_dir)
507 else:
508 break
509 project_dir = os.path.dirname(project_dir)
510
511 return 0
512
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700513 def UpdateProjectList(self):
514 new_project_paths = []
Colin Cross5acde752012-03-28 20:15:45 -0700515 for project in self.GetProjects(None, missing_ok=True):
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700516 if project.relpath:
517 new_project_paths.append(project.relpath)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700518 file_name = 'project.list'
519 file_path = os.path.join(self.manifest.repodir, file_name)
520 old_project_paths = []
521
522 if os.path.exists(file_path):
523 fd = open(file_path, 'r')
524 try:
525 old_project_paths = fd.read().split('\n')
526 finally:
527 fd.close()
528 for path in old_project_paths:
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700529 if not path:
530 continue
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700531 if path not in new_project_paths:
David Pursehouse8a68ff92012-09-24 12:15:13 +0900532 # If the path has already been deleted, we don't need to do it
Dan Willemsen43507912016-09-01 16:26:02 -0700533 gitdir = os.path.join(self.manifest.topdir, path, '.git')
534 if os.path.exists(gitdir):
David Pursehousec1b86a22012-11-14 11:36:51 +0900535 project = Project(
536 manifest = self.manifest,
537 name = path,
538 remote = RemoteSpec('origin'),
David James8d201162013-10-11 17:03:19 -0700539 gitdir = gitdir,
540 objdir = gitdir,
David Pursehousec1b86a22012-11-14 11:36:51 +0900541 worktree = os.path.join(self.manifest.topdir, path),
542 relpath = path,
543 revisionExpr = 'HEAD',
544 revisionId = None,
545 groups = None)
Anthonyf3fdf822009-09-26 13:38:52 -0400546
David Pursehousec1b86a22012-11-14 11:36:51 +0900547 if project.IsDirty():
David Pursehouse2f9e7e42013-03-05 17:26:46 +0900548 print('error: Cannot remove project "%s": uncommitted changes '
David Pursehousec1b86a22012-11-14 11:36:51 +0900549 'are present' % project.relpath, file=sys.stderr)
550 print(' commit changes, then run sync again',
551 file=sys.stderr)
552 return -1
Dan Willemsen43507912016-09-01 16:26:02 -0700553 elif self._DeleteProject(project.worktree):
554 return -1
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700555
Shawn O. Pearce9fb29ce2009-06-04 20:41:02 -0700556 new_project_paths.sort()
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700557 fd = open(file_path, 'w')
558 try:
559 fd.write('\n'.join(new_project_paths))
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700560 fd.write('\n')
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700561 finally:
562 fd.close()
563 return 0
564
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700565 def Execute(self, opt, args):
Roy Lee18afd7f2010-05-09 04:32:08 +0800566 if opt.jobs:
567 self.jobs = opt.jobs
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -0700568 if self.jobs > 1:
569 soft_limit, _ = _rlimit_nofile()
570 self.jobs = min(self.jobs, (soft_limit - 5) / 3)
571
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700572 if opt.network_only and opt.detach_head:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700573 print('error: cannot combine -n and -d', file=sys.stderr)
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700574 sys.exit(1)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700575 if opt.network_only and opt.local_only:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700576 print('error: cannot combine -n and -l', file=sys.stderr)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700577 sys.exit(1)
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500578 if opt.manifest_name and opt.smart_sync:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700579 print('error: cannot combine -m and -s', file=sys.stderr)
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500580 sys.exit(1)
581 if opt.manifest_name and opt.smart_tag:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700582 print('error: cannot combine -m and -t', file=sys.stderr)
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500583 sys.exit(1)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900584 if opt.manifest_server_username or opt.manifest_server_password:
585 if not (opt.smart_sync or opt.smart_tag):
Sarah Owenscecd1d82012-11-01 22:59:27 -0700586 print('error: -u and -p may only be combined with -s or -t',
587 file=sys.stderr)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900588 sys.exit(1)
589 if None in [opt.manifest_server_username, opt.manifest_server_password]:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700590 print('error: both -u and -p must be given', file=sys.stderr)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900591 sys.exit(1)
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500592
593 if opt.manifest_name:
594 self.manifest.Override(opt.manifest_name)
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700595
Chirayu Desaia892b102013-06-11 14:18:46 +0530596 manifest_name = opt.manifest_name
David Pursehouse59b41742015-05-07 14:36:09 +0900597 smart_sync_manifest_name = "smart_sync_override.xml"
598 smart_sync_manifest_path = os.path.join(
599 self.manifest.manifestProject.worktree, smart_sync_manifest_name)
Chirayu Desaia892b102013-06-11 14:18:46 +0530600
Victor Boivie08c880d2011-04-19 10:32:52 +0200601 if opt.smart_sync or opt.smart_tag:
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700602 if not self.manifest.manifest_server:
David Pursehouse2f9e7e42013-03-05 17:26:46 +0900603 print('error: cannot smart sync: no manifest server defined in '
Sarah Owenscecd1d82012-11-01 22:59:27 -0700604 'manifest', file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700605 sys.exit(1)
David Pursehouse86d973d2012-08-24 10:21:02 +0900606
607 manifest_server = self.manifest.manifest_server
David Pursehousefb99c712013-09-25 11:09:34 +0900608 if not opt.quiet:
609 print('Using manifest server %s' % manifest_server)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900610
David Pursehouse86d973d2012-08-24 10:21:02 +0900611 if not '@' in manifest_server:
David Pursehousecf76b1b2012-09-14 10:31:42 +0900612 username = None
613 password = None
614 if opt.manifest_server_username and opt.manifest_server_password:
615 username = opt.manifest_server_username
616 password = opt.manifest_server_password
David Pursehouse86d973d2012-08-24 10:21:02 +0900617 else:
618 try:
David Pursehousecf76b1b2012-09-14 10:31:42 +0900619 info = netrc.netrc()
620 except IOError:
David Pursehouseba7bc732015-08-20 16:55:42 +0900621 # .netrc file does not exist or could not be opened
622 pass
David Pursehouse86d973d2012-08-24 10:21:02 +0900623 else:
David Pursehousecf76b1b2012-09-14 10:31:42 +0900624 try:
Chirayu Desaidb2ad9d2013-06-11 13:42:25 +0530625 parse_result = urllib.parse.urlparse(manifest_server)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900626 if parse_result.hostname:
David Pursehouseba7bc732015-08-20 16:55:42 +0900627 auth = info.authenticators(parse_result.hostname)
628 if auth:
629 username, _account, password = auth
630 else:
631 print('No credentials found for %s in .netrc'
632 % parse_result.hostname, file=sys.stderr)
Sarah Owensa5be53f2012-09-09 15:37:57 -0700633 except netrc.NetrcParseError as e:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700634 print('Error parsing .netrc file: %s' % e, file=sys.stderr)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900635
636 if (username and password):
637 manifest_server = manifest_server.replace('://', '://%s:%s@' %
638 (username, password),
639 1)
David Pursehouse86d973d2012-08-24 10:21:02 +0900640
Dan Willemsen0745bb22015-08-17 13:41:45 -0700641 transport = PersistentTransport(manifest_server)
642 if manifest_server.startswith('persistent-'):
643 manifest_server = manifest_server[len('persistent-'):]
644
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700645 try:
Dan Willemsen0745bb22015-08-17 13:41:45 -0700646 server = xmlrpc.client.Server(manifest_server, transport=transport)
Victor Boivie08c880d2011-04-19 10:32:52 +0200647 if opt.smart_sync:
648 p = self.manifest.manifestProject
649 b = p.GetBranch(p.CurrentBranch)
650 branch = b.merge
651 if branch.startswith(R_HEADS):
652 branch = branch[len(R_HEADS):]
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700653
Victor Boivie08c880d2011-04-19 10:32:52 +0200654 env = os.environ.copy()
Jeff Davidson5cf16602014-10-02 10:13:38 -0700655 if 'SYNC_TARGET' in env:
656 target = env['SYNC_TARGET']
657 [success, manifest_str] = server.GetApprovedManifest(branch, target)
658 elif 'TARGET_PRODUCT' in env and 'TARGET_BUILD_VARIANT' in env:
Victor Boivie08c880d2011-04-19 10:32:52 +0200659 target = '%s-%s' % (env['TARGET_PRODUCT'],
660 env['TARGET_BUILD_VARIANT'])
661 [success, manifest_str] = server.GetApprovedManifest(branch, target)
662 else:
663 [success, manifest_str] = server.GetApprovedManifest(branch)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700664 else:
Victor Boivie08c880d2011-04-19 10:32:52 +0200665 assert(opt.smart_tag)
666 [success, manifest_str] = server.GetManifest(opt.smart_tag)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700667
668 if success:
David Pursehouse59b41742015-05-07 14:36:09 +0900669 manifest_name = smart_sync_manifest_name
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700670 try:
David Pursehouse59b41742015-05-07 14:36:09 +0900671 f = open(smart_sync_manifest_path, 'w')
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700672 try:
673 f.write(manifest_str)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700674 finally:
675 f.close()
David Pursehouse727cc3e2015-05-07 14:16:49 +0900676 except IOError as e:
677 print('error: cannot write manifest to %s:\n%s'
David Pursehouse59b41742015-05-07 14:36:09 +0900678 % (smart_sync_manifest_path, e),
Sarah Owenscecd1d82012-11-01 22:59:27 -0700679 file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700680 sys.exit(1)
Victor Boivie53a6c5d2013-03-19 12:20:52 +0100681 self._ReloadManifest(manifest_name)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700682 else:
David Pursehouse351fe2c2013-09-25 17:54:26 +0900683 print('error: manifest server RPC call failed: %s' %
684 manifest_str, file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700685 sys.exit(1)
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530686 except (socket.error, IOError, xmlrpc.client.Fault) as e:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700687 print('error: cannot connect to manifest server %s:\n%s'
688 % (self.manifest.manifest_server, e), file=sys.stderr)
David Pursehousebd489c42012-08-23 10:21:26 +0900689 sys.exit(1)
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530690 except xmlrpc.client.ProtocolError as e:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700691 print('error: cannot connect to manifest server %s:\n%d %s'
692 % (self.manifest.manifest_server, e.errcode, e.errmsg),
693 file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700694 sys.exit(1)
David Pursehouse59b41742015-05-07 14:36:09 +0900695 else: # Not smart sync or smart tag mode
696 if os.path.isfile(smart_sync_manifest_path):
697 try:
698 os.remove(smart_sync_manifest_path)
699 except OSError as e:
700 print('error: failed to remove existing smart sync override manifest: %s' %
701 e, file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700702
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700703 rp = self.manifest.repoProject
704 rp.PreSync()
705
706 mp = self.manifest.manifestProject
707 mp.PreSync()
708
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800709 if opt.repo_upgraded:
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -0700710 _PostRepoUpgrade(self.manifest, quiet=opt.quiet)
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800711
Nico Sallembien9bb18162009-12-07 15:38:01 -0800712 if not opt.local_only:
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -0700713 mp.Sync_NetworkHalf(quiet=opt.quiet,
Mitchel Humpherys597868b2012-10-29 10:18:34 -0700714 current_branch_only=opt.current_branch_only,
David Pursehouseb1553542014-09-04 21:28:09 +0900715 no_tags=opt.no_tags,
716 optimized_fetch=opt.optimized_fetch)
Nico Sallembien9bb18162009-12-07 15:38:01 -0800717
718 if mp.HasChanges:
719 syncbuf = SyncBuffer(mp.config)
720 mp.Sync_LocalHalf(syncbuf)
721 if not syncbuf.Finish():
722 sys.exit(1)
Victor Boivie53a6c5d2013-03-19 12:20:52 +0100723 self._ReloadManifest(manifest_name)
Shawn O. Pearcec4657962011-09-26 09:08:01 -0700724 if opt.jobs is None:
725 self.jobs = self.manifest.default.sync_j
Simran Basib9a1b732015-08-20 12:19:28 -0700726
Simran Basib9a1b732015-08-20 12:19:28 -0700727 if self.gitc_manifest:
728 gitc_manifest_projects = self.GetProjects(args,
Simran Basib9a1b732015-08-20 12:19:28 -0700729 missing_ok=True)
730 gitc_projects = []
731 opened_projects = []
732 for project in gitc_manifest_projects:
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700733 if project.relpath in self.gitc_manifest.paths and \
734 self.gitc_manifest.paths[project.relpath].old_revision:
735 opened_projects.append(project.relpath)
Simran Basib9a1b732015-08-20 12:19:28 -0700736 else:
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700737 gitc_projects.append(project.relpath)
Simran Basib9a1b732015-08-20 12:19:28 -0700738
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700739 if not args:
740 gitc_projects = None
741
742 if gitc_projects != [] and not opt.local_only:
Simran Basib9a1b732015-08-20 12:19:28 -0700743 print('Updating GITC client: %s' % self.gitc_manifest.gitc_client_name)
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700744 manifest = GitcManifest(self.repodir, self.gitc_manifest.gitc_client_name)
745 if manifest_name:
746 manifest.Override(manifest_name)
747 else:
748 manifest.Override(self.manifest.manifestFile)
749 gitc_utils.generate_gitc_manifest(self.gitc_manifest,
750 manifest,
Simran Basib9a1b732015-08-20 12:19:28 -0700751 gitc_projects)
752 print('GITC client successfully synced.')
753
754 # The opened projects need to be synced as normal, therefore we
755 # generate a new args list to represent the opened projects.
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700756 # TODO: make this more reliable -- if there's a project name/path overlap,
757 # this may choose the wrong project.
758 args = [os.path.relpath(self.manifest.paths[p].worktree, os.getcwd())
759 for p in opened_projects]
Simran Basib9a1b732015-08-20 12:19:28 -0700760 if not args:
761 return
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800762 all_projects = self.GetProjects(args,
763 missing_ok=True,
764 submodules_ok=opt.fetch_submodules)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700765
Dave Borowitz67700e92012-10-23 15:00:54 -0700766 self._fetch_times = _FetchTimes(self.manifest)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700767 if not opt.local_only:
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700768 to_fetch = []
769 now = time.time()
Dave Borowitz67700e92012-10-23 15:00:54 -0700770 if _ONE_DAY_S <= (now - rp.LastFetch):
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700771 to_fetch.append(rp)
David Pursehouse8a68ff92012-09-24 12:15:13 +0900772 to_fetch.extend(all_projects)
Dave Borowitz67700e92012-10-23 15:00:54 -0700773 to_fetch.sort(key=self._fetch_times.Get, reverse=True)
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700774
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800775 fetched = self._Fetch(to_fetch, opt)
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700776 _PostRepoFetch(rp, opt.no_repo_verify)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700777 if opt.network_only:
778 # bail out now; the rest touches the working tree
779 return
780
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800781 # Iteratively fetch missing and/or nested unregistered submodules
782 previously_missing_set = set()
783 while True:
Victor Boivie53a6c5d2013-03-19 12:20:52 +0100784 self._ReloadManifest(manifest_name)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800785 all_projects = self.GetProjects(args,
786 missing_ok=True,
787 submodules_ok=opt.fetch_submodules)
788 missing = []
789 for project in all_projects:
790 if project.gitdir not in fetched:
791 missing.append(project)
792 if not missing:
793 break
794 # Stop us from non-stopped fetching actually-missing repos: If set of
795 # missing repos has not been changed from last fetch, we break.
796 missing_set = set(p.name for p in missing)
797 if previously_missing_set == missing_set:
798 break
799 previously_missing_set = missing_set
800 fetched.update(self._Fetch(missing, opt))
801
Julien Campergue335f5ef2013-10-16 11:02:35 +0200802 if self.manifest.IsMirror or self.manifest.IsArchive:
Shawn O. Pearcecd1d7ff2009-06-04 16:15:53 -0700803 # bail out now, we have no working tree
804 return
805
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700806 if self.UpdateProjectList():
807 sys.exit(1)
808
Shawn O. Pearce350cde42009-04-16 11:21:18 -0700809 syncbuf = SyncBuffer(mp.config,
810 detach_head = opt.detach_head)
David Pursehouse8a68ff92012-09-24 12:15:13 +0900811 pm = Progress('Syncing work tree', len(all_projects))
812 for project in all_projects:
Shawn O. Pearce68194f42009-04-10 16:48:52 -0700813 pm.update()
Shawn O. Pearcee284ad12008-11-04 07:37:10 -0800814 if project.worktree:
Kevin Degiabaa7f32014-11-12 11:27:45 -0700815 project.Sync_LocalHalf(syncbuf, force_sync=opt.force_sync)
Shawn O. Pearce68194f42009-04-10 16:48:52 -0700816 pm.end()
Sarah Owenscecd1d82012-11-01 22:59:27 -0700817 print(file=sys.stderr)
Shawn O. Pearce350cde42009-04-16 11:21:18 -0700818 if not syncbuf.Finish():
819 sys.exit(1)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700820
Doug Anderson2b8db3c2010-11-01 15:08:06 -0700821 # If there's a notice that's supposed to print at the end of the sync, print
822 # it now...
823 if self.manifest.notice:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700824 print(self.manifest.notice)
Doug Anderson2b8db3c2010-11-01 15:08:06 -0700825
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -0700826def _PostRepoUpgrade(manifest, quiet=False):
Conley Owens094cdbe2014-01-30 15:09:59 -0800827 wrapper = Wrapper()
Conley Owensc9129d92012-10-01 16:12:28 -0700828 if wrapper.NeedSetupGnuPG():
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -0700829 wrapper.SetupGnuPG(quiet)
Conley Owensf2fe2d92014-01-29 13:53:43 -0800830 for project in manifest.projects:
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700831 if project.Exists:
832 project.PostRepoUpgrade()
833
834def _PostRepoFetch(rp, no_repo_verify=False, verbose=False):
835 if rp.HasChanges:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700836 print('info: A new version of repo is available', file=sys.stderr)
837 print(file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700838 if no_repo_verify or _VerifyTag(rp):
Shawn O. Pearce350cde42009-04-16 11:21:18 -0700839 syncbuf = SyncBuffer(rp.config)
840 rp.Sync_LocalHalf(syncbuf)
841 if not syncbuf.Finish():
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700842 sys.exit(1)
Sarah Owenscecd1d82012-11-01 22:59:27 -0700843 print('info: Restarting repo with latest version', file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700844 raise RepoChangedException(['--repo-upgraded'])
845 else:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700846 print('warning: Skipped upgrade to unverified version', file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700847 else:
848 if verbose:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700849 print('repo version %s is current' % rp.work_git.describe(HEAD),
850 file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700851
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700852def _VerifyTag(project):
853 gpg_dir = os.path.expanduser('~/.repoconfig/gnupg')
854 if not os.path.exists(gpg_dir):
Sarah Owenscecd1d82012-11-01 22:59:27 -0700855 print('warning: GnuPG was not available during last "repo init"\n'
856 'warning: Cannot automatically authenticate repo."""',
857 file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700858 return True
859
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700860 try:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -0700861 cur = project.bare_git.describe(project.GetRevisionId())
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700862 except GitError:
863 cur = None
864
865 if not cur \
866 or re.compile(r'^.*-[0-9]{1,}-g[0-9a-f]{1,}$').match(cur):
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -0700867 rev = project.revisionExpr
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700868 if rev.startswith(R_HEADS):
869 rev = rev[len(R_HEADS):]
870
Sarah Owenscecd1d82012-11-01 22:59:27 -0700871 print(file=sys.stderr)
872 print("warning: project '%s' branch '%s' is not signed"
873 % (project.name, rev), file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700874 return False
875
Shawn O. Pearcef18cb762010-12-07 11:41:05 -0800876 env = os.environ.copy()
877 env['GIT_DIR'] = project.gitdir.encode()
878 env['GNUPGHOME'] = gpg_dir.encode()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700879
880 cmd = [GIT, 'tag', '-v', cur]
881 proc = subprocess.Popen(cmd,
882 stdout = subprocess.PIPE,
883 stderr = subprocess.PIPE,
884 env = env)
885 out = proc.stdout.read()
886 proc.stdout.close()
887
888 err = proc.stderr.read()
889 proc.stderr.close()
890
891 if proc.wait() != 0:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700892 print(file=sys.stderr)
893 print(out, file=sys.stderr)
894 print(err, file=sys.stderr)
895 print(file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700896 return False
897 return True
Dave Borowitz67700e92012-10-23 15:00:54 -0700898
899class _FetchTimes(object):
Dave Borowitzd9478582012-10-23 16:35:39 -0700900 _ALPHA = 0.5
901
Dave Borowitz67700e92012-10-23 15:00:54 -0700902 def __init__(self, manifest):
Anthony King85b24ac2014-05-06 15:57:48 +0100903 self._path = os.path.join(manifest.repodir, '.repo_fetchtimes.json')
Dave Borowitz67700e92012-10-23 15:00:54 -0700904 self._times = None
Dave Borowitzd9478582012-10-23 16:35:39 -0700905 self._seen = set()
Dave Borowitz67700e92012-10-23 15:00:54 -0700906
907 def Get(self, project):
908 self._Load()
909 return self._times.get(project.name, _ONE_DAY_S)
910
911 def Set(self, project, t):
Dave Borowitzd9478582012-10-23 16:35:39 -0700912 self._Load()
913 name = project.name
914 old = self._times.get(name, t)
915 self._seen.add(name)
916 a = self._ALPHA
917 self._times[name] = (a*t) + ((1-a) * old)
Dave Borowitz67700e92012-10-23 15:00:54 -0700918
919 def _Load(self):
920 if self._times is None:
921 try:
Anthony King85b24ac2014-05-06 15:57:48 +0100922 f = open(self._path)
Dave Borowitz67700e92012-10-23 15:00:54 -0700923 try:
Anthony King85b24ac2014-05-06 15:57:48 +0100924 self._times = json.load(f)
925 finally:
926 f.close()
927 except (IOError, ValueError):
928 try:
929 os.remove(self._path)
930 except OSError:
931 pass
932 self._times = {}
Dave Borowitz67700e92012-10-23 15:00:54 -0700933
934 def Save(self):
935 if self._times is None:
936 return
Dave Borowitzd9478582012-10-23 16:35:39 -0700937
938 to_delete = []
939 for name in self._times:
940 if name not in self._seen:
941 to_delete.append(name)
942 for name in to_delete:
943 del self._times[name]
944
Dave Borowitz67700e92012-10-23 15:00:54 -0700945 try:
Anthony King85b24ac2014-05-06 15:57:48 +0100946 f = open(self._path, 'w')
Dave Borowitz67700e92012-10-23 15:00:54 -0700947 try:
Anthony King85b24ac2014-05-06 15:57:48 +0100948 json.dump(self._times, f, indent=2)
949 finally:
950 f.close()
951 except (IOError, TypeError):
952 try:
953 os.remove(self._path)
954 except OSError:
955 pass
Dan Willemsen0745bb22015-08-17 13:41:45 -0700956
957# This is a replacement for xmlrpc.client.Transport using urllib2
958# and supporting persistent-http[s]. It cannot change hosts from
959# request to request like the normal transport, the real url
960# is passed during initialization.
961class PersistentTransport(xmlrpc.client.Transport):
962 def __init__(self, orig_host):
963 self.orig_host = orig_host
964
965 def request(self, host, handler, request_body, verbose=False):
966 with GetUrlCookieFile(self.orig_host, not verbose) as (cookiefile, proxy):
967 # Python doesn't understand cookies with the #HttpOnly_ prefix
968 # Since we're only using them for HTTP, copy the file temporarily,
969 # stripping those prefixes away.
Dan Willemsen3010e5b2015-08-20 10:09:20 -0700970 if cookiefile:
971 tmpcookiefile = tempfile.NamedTemporaryFile()
David Pursehouse4c5f74e2015-10-02 11:10:10 +0900972 tmpcookiefile.write("# HTTP Cookie File")
Dan Willemsen3010e5b2015-08-20 10:09:20 -0700973 try:
974 with open(cookiefile) as f:
975 for line in f:
976 if line.startswith("#HttpOnly_"):
977 line = line[len("#HttpOnly_"):]
978 tmpcookiefile.write(line)
979 tmpcookiefile.flush()
Dan Willemsen0745bb22015-08-17 13:41:45 -0700980
Dan Willemsen3010e5b2015-08-20 10:09:20 -0700981 cookiejar = cookielib.MozillaCookieJar(tmpcookiefile.name)
David Pursehouseb1ad2192015-09-30 10:35:43 +0900982 try:
983 cookiejar.load()
984 except cookielib.LoadError:
985 cookiejar = cookielib.CookieJar()
Dan Willemsen3010e5b2015-08-20 10:09:20 -0700986 finally:
987 tmpcookiefile.close()
988 else:
989 cookiejar = cookielib.CookieJar()
Dan Willemsen0745bb22015-08-17 13:41:45 -0700990
991 proxyhandler = urllib.request.ProxyHandler
992 if proxy:
993 proxyhandler = urllib.request.ProxyHandler({
994 "http": proxy,
995 "https": proxy })
996
997 opener = urllib.request.build_opener(
998 urllib.request.HTTPCookieProcessor(cookiejar),
999 proxyhandler)
1000
1001 url = urllib.parse.urljoin(self.orig_host, handler)
1002 parse_results = urllib.parse.urlparse(url)
1003
1004 scheme = parse_results.scheme
1005 if scheme == 'persistent-http':
1006 scheme = 'http'
1007 if scheme == 'persistent-https':
1008 # If we're proxying through persistent-https, use http. The
1009 # proxy itself will do the https.
1010 if proxy:
1011 scheme = 'http'
1012 else:
1013 scheme = 'https'
1014
1015 # Parse out any authentication information using the base class
1016 host, extra_headers, _ = self.get_host_info(parse_results.netloc)
1017
1018 url = urllib.parse.urlunparse((
1019 scheme,
1020 host,
1021 parse_results.path,
1022 parse_results.params,
1023 parse_results.query,
1024 parse_results.fragment))
1025
1026 request = urllib.request.Request(url, request_body)
1027 if extra_headers is not None:
1028 for (name, header) in extra_headers:
1029 request.add_header(name, header)
1030 request.add_header('Content-Type', 'text/xml')
1031 try:
1032 response = opener.open(request)
1033 except urllib.error.HTTPError as e:
1034 if e.code == 501:
1035 # We may have been redirected through a login process
1036 # but our POST turned into a GET. Retry.
1037 response = opener.open(request)
1038 else:
1039 raise
1040
1041 p, u = xmlrpc.client.getparser()
1042 while 1:
1043 data = response.read(1024)
1044 if not data:
1045 break
1046 p.feed(data)
1047 p.close()
1048 return u.close()
1049
1050 def close(self):
1051 pass
1052