blob: 02cd38795e578379bb4c224ea021482f2ade2b77 [file] [log] [blame]
Mike Frysingerf6013762019-06-13 02:30:51 -04001# -*- coding:utf-8 -*-
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002#
3# Copyright (C) 2008 The Android Open Source Project
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16
Sarah Owenscecd1d82012-11-01 22:59:27 -070017from __future__ import print_function
Anthony King85b24ac2014-05-06 15:57:48 +010018import json
David Pursehouse86d973d2012-08-24 10:21:02 +090019import netrc
Shawn O. Pearce2a1ccb22009-04-10 16:51:53 -070020from optparse import SUPPRESS_HELP
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070021import os
22import re
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
David Rileye0684ad2017-04-05 00:02:59 -070067import event_log
Dave Borowitze2152672012-10-31 12:24:38 -070068from git_command import GIT, git_require
David Pursehouseba7bc732015-08-20 16:55:42 +090069from git_config import GetUrlCookieFile
David Pursehoused94aaef2012-09-07 09:52:04 +090070from git_refs import R_HEADS, HEAD
Simran Basibdb52712015-08-10 13:23:23 -070071import gitc_utils
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -070072from project import Project
73from project import RemoteSpec
Shawn O. Pearcec95583b2009-03-03 17:47:06 -080074from command import Command, MirrorSafeCommand
Torne (Richard Coles)7bdbde72012-12-05 10:58:06 +000075from error import RepoChangedException, GitError, ManifestParseError
Renaud Paquaya65adf72016-11-03 10:37:53 -070076import platform_utils
Shawn O. Pearce350cde42009-04-16 11:21:18 -070077from project import SyncBuffer
Shawn O. Pearce68194f42009-04-10 16:48:52 -070078from progress import Progress
Conley Owens094cdbe2014-01-30 15:09:59 -080079from wrapper import Wrapper
Dan Willemsen5ea32d12015-09-08 13:27:20 -070080from manifest_xml import GitcManifest
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070081
Dave Borowitz67700e92012-10-23 15:00:54 -070082_ONE_DAY_S = 24 * 60 * 60
83
Doug Andersonfc06ced2011-03-16 15:49:18 -070084class _FetchError(Exception):
85 """Internal error thrown in _FetchHelper() when we don't want stack trace."""
86 pass
87
Shawn O. Pearcec95583b2009-03-03 17:47:06 -080088class Sync(Command, MirrorSafeCommand):
Roy Lee18afd7f2010-05-09 04:32:08 +080089 jobs = 1
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070090 common = True
91 helpSummary = "Update working tree to the latest revision"
92 helpUsage = """
93%prog [<project>...]
94"""
95 helpDescription = """
96The '%prog' command synchronizes local project directories
97with the remote repositories specified in the manifest. If a local
98project does not yet exist, it will clone a new local directory from
99the remote repository and set up tracking branches as specified in
100the manifest. If the local project already exists, '%prog'
101will update the remote branches and rebase any new local changes
102on top of the new remote changes.
103
104'%prog' will synchronize all projects listed at the command
105line. Projects can be specified either by name, or by a relative
106or absolute path to the project's local directory. If no projects
107are specified, '%prog' will synchronize all projects listed in
108the manifest.
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700109
110The -d/--detach option can be used to switch specified projects
111back to the manifest revision. This option is especially helpful
112if the project is currently on a topic branch, but the manifest
113revision is temporarily needed.
Shawn O. Pearceeb7af872009-04-21 08:02:04 -0700114
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700115The -s/--smart-sync option can be used to sync to a known good
116build as specified by the manifest-server element in the current
Victor Boivie08c880d2011-04-19 10:32:52 +0200117manifest. The -t/--smart-tag option is similar and allows you to
118specify a custom tag/label.
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700119
David Pursehousecf76b1b2012-09-14 10:31:42 +0900120The -u/--manifest-server-username and -p/--manifest-server-password
121options can be used to specify a username and password to authenticate
122with the manifest server when using the -s or -t option.
123
124If -u and -p are not specified when using the -s or -t option, '%prog'
125will attempt to read authentication credentials for the manifest server
126from the user's .netrc file.
127
128'%prog' will not use authentication credentials from -u/-p or .netrc
129if the manifest server specified in the manifest file already includes
130credentials.
131
Andrei Warkentin5df6de02010-07-02 17:58:31 -0500132The -f/--force-broken option can be used to proceed with syncing
133other projects if a project sync fails.
134
Kevin Degiabaa7f32014-11-12 11:27:45 -0700135The --force-sync option can be used to overwrite existing git
136directories if they have previously been linked to a different
137object direcotry. WARNING: This may cause data to be lost since
138refs may be removed when overwriting.
139
Oleksii Okolielovd3c0f592018-12-17 19:23:44 -0500140The --force-remove-dirty option can be used to remove previously used
141projects with uncommitted changes. WARNING: This may cause data to be
142lost since uncommitted changes may be removed with projects that no longer
143exist in the manifest.
144
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -0700145The --no-clone-bundle option disables any attempt to use
146$URL/clone.bundle to bootstrap a new Git repository from a
147resumeable bundle file on a content delivery network. This
148may be necessary if there are problems with the local Python
149HTTP client or proxy configuration, but the Git binary works.
150
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800151The --fetch-submodules option enables fetching Git submodules
152of a project from server.
153
David Pursehousef2fad612015-01-29 14:36:28 +0900154The -c/--current-branch option can be used to only fetch objects that
155are on the branch specified by a project's revision.
156
David Pursehouseb1553542014-09-04 21:28:09 +0900157The --optimized-fetch option can be used to only fetch projects that
158are fixed to a sha1 revision if the sha1 revision does not already
159exist locally.
160
David Pursehouse74cfd272015-10-14 10:50:15 +0900161The --prune option can be used to remove any refs that no longer
162exist on the remote.
163
Mike Frysingerb8f7bb02018-10-10 01:05:11 -0400164# SSH Connections
Shawn O. Pearceeb7af872009-04-21 08:02:04 -0700165
166If at least one project remote URL uses an SSH connection (ssh://,
167git+ssh://, or user@host:path syntax) repo will automatically
168enable the SSH ControlMaster option when connecting to that host.
169This feature permits other projects in the same '%prog' session to
170reuse the same SSH tunnel, saving connection setup overheads.
171
172To disable this behavior on UNIX platforms, set the GIT_SSH
173environment variable to 'ssh'. For example:
174
175 export GIT_SSH=ssh
176 %prog
177
Mike Frysingerb8f7bb02018-10-10 01:05:11 -0400178# Compatibility
Shawn O. Pearceeb7af872009-04-21 08:02:04 -0700179
180This feature is automatically disabled on Windows, due to the lack
181of UNIX domain socket support.
182
183This feature is not compatible with url.insteadof rewrites in the
184user's ~/.gitconfig. '%prog' is currently not able to perform the
185rewrite early enough to establish the ControlMaster tunnel.
186
187If the remote SSH daemon is Gerrit Code Review, version 2.0.10 or
188later is required to fix a server side protocol bug.
189
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700190"""
191
Nico Sallembien6623b212010-05-11 12:57:01 -0700192 def _Options(self, p, show_smart=True):
Torne (Richard Coles)7bdbde72012-12-05 10:58:06 +0000193 try:
194 self.jobs = self.manifest.default.sync_j
195 except ManifestParseError:
196 self.jobs = 1
Shawn O. Pearce6392c872011-09-22 17:44:31 -0700197
Andrei Warkentin5df6de02010-07-02 17:58:31 -0500198 p.add_option('-f', '--force-broken',
199 dest='force_broken', action='store_true',
200 help="continue sync even if a project fails to sync")
Kevin Degiabaa7f32014-11-12 11:27:45 -0700201 p.add_option('--force-sync',
202 dest='force_sync', action='store_true',
203 help="overwrite an existing git directory if it needs to "
204 "point to a different object directory. WARNING: this "
205 "may cause loss of data")
Oleksii Okolielovd3c0f592018-12-17 19:23:44 -0500206 p.add_option('--force-remove-dirty',
207 dest='force_remove_dirty', action='store_true',
208 help="force remove projects with uncommitted modifications if "
209 "projects no longer exist in the manifest. "
210 "WARNING: this may cause loss of data")
David Pursehouse8f62fb72012-11-14 12:09:38 +0900211 p.add_option('-l', '--local-only',
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700212 dest='local_only', action='store_true',
213 help="only update working tree, don't fetch")
David Pursehouse8f62fb72012-11-14 12:09:38 +0900214 p.add_option('-n', '--network-only',
Shawn O. Pearce96fdcef2009-04-10 16:29:20 -0700215 dest='network_only', action='store_true',
216 help="fetch only, don't update working tree")
David Pursehouse8f62fb72012-11-14 12:09:38 +0900217 p.add_option('-d', '--detach',
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700218 dest='detach_head', action='store_true',
219 help='detach projects back to manifest revision')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900220 p.add_option('-c', '--current-branch',
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -0700221 dest='current_branch_only', action='store_true',
222 help='fetch only current branch from server')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900223 p.add_option('-q', '--quiet',
Shawn O. Pearce16614f82010-10-29 12:05:43 -0700224 dest='quiet', action='store_true',
225 help='be more quiet')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900226 p.add_option('-j', '--jobs',
Roy Lee18afd7f2010-05-09 04:32:08 +0800227 dest='jobs', action='store', type='int',
Shawn O. Pearce6392c872011-09-22 17:44:31 -0700228 help="projects to fetch simultaneously (default %d)" % self.jobs)
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500229 p.add_option('-m', '--manifest-name',
230 dest='manifest_name',
231 help='temporary manifest to use for this sync', metavar='NAME.xml')
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -0700232 p.add_option('--no-clone-bundle',
233 dest='no_clone_bundle', action='store_true',
234 help='disable use of /clone.bundle on HTTP/HTTPS')
Conley Owens8d070cf2012-11-06 13:14:31 -0800235 p.add_option('-u', '--manifest-server-username', action='store',
236 dest='manifest_server_username',
237 help='username to authenticate with the manifest server')
238 p.add_option('-p', '--manifest-server-password', action='store',
239 dest='manifest_server_password',
240 help='password to authenticate with the manifest server')
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800241 p.add_option('--fetch-submodules',
242 dest='fetch_submodules', action='store_true',
243 help='fetch submodules from server')
Mitchel Humpherys597868b2012-10-29 10:18:34 -0700244 p.add_option('--no-tags',
245 dest='no_tags', action='store_true',
246 help="don't fetch tags")
David Pursehouseb1553542014-09-04 21:28:09 +0900247 p.add_option('--optimized-fetch',
248 dest='optimized_fetch', action='store_true',
249 help='only fetch projects fixed to sha1 if revision does not exist locally')
David Pursehouse74cfd272015-10-14 10:50:15 +0900250 p.add_option('--prune', dest='prune', action='store_true',
251 help='delete refs that no longer exist on the remote')
Nico Sallembien6623b212010-05-11 12:57:01 -0700252 if show_smart:
253 p.add_option('-s', '--smart-sync',
254 dest='smart_sync', action='store_true',
David Pursehouse79fba682016-04-13 18:03:00 +0900255 help='smart sync using manifest from the latest known good build')
Victor Boivie08c880d2011-04-19 10:32:52 +0200256 p.add_option('-t', '--smart-tag',
257 dest='smart_tag', action='store',
258 help='smart sync using manifest from a known tag')
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700259
Shawn O. Pearcefd89b672009-04-18 11:28:57 -0700260 g = p.add_option_group('repo Version options')
261 g.add_option('--no-repo-verify',
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700262 dest='no_repo_verify', action='store_true',
263 help='do not verify repo source code')
Shawn O. Pearcefd89b672009-04-18 11:28:57 -0700264 g.add_option('--repo-upgraded',
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800265 dest='repo_upgraded', action='store_true',
Shawn O. Pearce2a1ccb22009-04-10 16:51:53 -0700266 help=SUPPRESS_HELP)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700267
Andrew Wheeler7f1ccfb2016-06-17 16:51:07 -0500268 def _FetchProjectList(self, opt, projects, sem, *args, **kwargs):
David Pursehousec1b86a22012-11-14 11:36:51 +0900269 """Main function of the fetch threads when jobs are > 1.
Roy Lee18afd7f2010-05-09 04:32:08 +0800270
David James8d201162013-10-11 17:03:19 -0700271 Delegates most of the work to _FetchHelper.
272
273 Args:
274 opt: Program options returned from optparse. See _Options().
275 projects: Projects to fetch.
Andrew Wheeler7f1ccfb2016-06-17 16:51:07 -0500276 sem: We'll release() this semaphore when we exit so that another thread
277 can be started up.
David James89ece422014-01-09 18:51:58 -0800278 *args, **kwargs: Remaining arguments to pass to _FetchHelper. See the
David James8d201162013-10-11 17:03:19 -0700279 _FetchHelper docstring for details.
280 """
Andrew Wheeler7f1ccfb2016-06-17 16:51:07 -0500281 try:
282 for project in projects:
283 success = self._FetchHelper(opt, project, *args, **kwargs)
284 if not success and not opt.force_broken:
285 break
286 finally:
287 sem.release()
David James8d201162013-10-11 17:03:19 -0700288
Andrew Wheeler7f1ccfb2016-06-17 16:51:07 -0500289 def _FetchHelper(self, opt, project, lock, fetched, pm, err_event):
David James8d201162013-10-11 17:03:19 -0700290 """Fetch git objects for a single project.
291
David Pursehousec1b86a22012-11-14 11:36:51 +0900292 Args:
293 opt: Program options returned from optparse. See _Options().
294 project: Project object for the project to fetch.
295 lock: Lock for accessing objects that are shared amongst multiple
296 _FetchHelper() threads.
297 fetched: set object that we will add project.gitdir to when we're done
298 (with our lock held).
299 pm: Instance of a Project object. We will call pm.update() (with our
300 lock held).
David Pursehousec1b86a22012-11-14 11:36:51 +0900301 err_event: We'll set this event in the case of an error (after printing
302 out info about the error).
David James8d201162013-10-11 17:03:19 -0700303
304 Returns:
305 Whether the fetch was successful.
David Pursehousec1b86a22012-11-14 11:36:51 +0900306 """
307 # We'll set to true once we've locked the lock.
308 did_lock = False
Doug Andersonfc06ced2011-03-16 15:49:18 -0700309
Chirayu Desaifef4ae72013-04-12 14:54:32 +0530310 if not opt.quiet:
311 print('Fetching project %s' % project.name)
312
David Pursehousec1b86a22012-11-14 11:36:51 +0900313 # Encapsulate everything in a try/except/finally so that:
314 # - We always set err_event in the case of an exception.
315 # - We always make sure we call sem.release().
316 # - We always make sure we unlock the lock if we locked it.
David Rileye0684ad2017-04-05 00:02:59 -0700317 start = time.time()
318 success = False
David Pursehousec1b86a22012-11-14 11:36:51 +0900319 try:
Doug Andersonfc06ced2011-03-16 15:49:18 -0700320 try:
David Pursehousec1b86a22012-11-14 11:36:51 +0900321 success = project.Sync_NetworkHalf(
322 quiet=opt.quiet,
323 current_branch_only=opt.current_branch_only,
Kevin Degiabaa7f32014-11-12 11:27:45 -0700324 force_sync=opt.force_sync,
Mitchel Humpherys597868b2012-10-29 10:18:34 -0700325 clone_bundle=not opt.no_clone_bundle,
David Pursehouseb1553542014-09-04 21:28:09 +0900326 no_tags=opt.no_tags, archive=self.manifest.IsArchive,
David Pursehouse74cfd272015-10-14 10:50:15 +0900327 optimized_fetch=opt.optimized_fetch,
328 prune=opt.prune)
David Pursehousec1b86a22012-11-14 11:36:51 +0900329 self._fetch_times.Set(project, time.time() - start)
Doug Andersonfc06ced2011-03-16 15:49:18 -0700330
David Pursehousec1b86a22012-11-14 11:36:51 +0900331 # Lock around all the rest of the code, since printing, updating a set
332 # and Progress.update() are not thread safe.
333 lock.acquire()
334 did_lock = True
Doug Andersonfc06ced2011-03-16 15:49:18 -0700335
David Pursehousec1b86a22012-11-14 11:36:51 +0900336 if not success:
Hu Xiuyune9becc02015-11-25 15:52:26 +0800337 err_event.set()
Marc Herbertffb4b892017-04-04 22:03:53 -0700338 print('error: Cannot fetch %s from %s'
339 % (project.name, project.remote.url),
340 file=sys.stderr)
David Pursehousec1b86a22012-11-14 11:36:51 +0900341 if opt.force_broken:
342 print('warn: --force-broken, continuing to sync',
343 file=sys.stderr)
344 else:
345 raise _FetchError()
Doug Andersonfc06ced2011-03-16 15:49:18 -0700346
David Pursehousec1b86a22012-11-14 11:36:51 +0900347 fetched.add(project.gitdir)
348 pm.update()
349 except _FetchError:
Hu Xiuyune9becc02015-11-25 15:52:26 +0800350 pass
Dan Sandlerc5cd4332015-07-31 09:37:53 -0400351 except Exception as e:
352 print('error: Cannot fetch %s (%s: %s)' \
353 % (project.name, type(e).__name__, str(e)), file=sys.stderr)
David Pursehousec1b86a22012-11-14 11:36:51 +0900354 err_event.set()
355 raise
356 finally:
357 if did_lock:
358 lock.release()
David Rileye0684ad2017-04-05 00:02:59 -0700359 finish = time.time()
360 self.event_log.AddSync(project, event_log.TASK_SYNC_NETWORK,
361 start, finish, success)
Roy Lee18afd7f2010-05-09 04:32:08 +0800362
David James8d201162013-10-11 17:03:19 -0700363 return success
364
Shawn O. Pearce16614f82010-10-29 12:05:43 -0700365 def _Fetch(self, projects, opt):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700366 fetched = set()
David James89ece422014-01-09 18:51:58 -0800367 lock = _threading.Lock()
Tim Schumacher913327f2017-06-05 15:01:41 +0200368 pm = Progress('Fetching projects', len(projects),
Tim Schumacher7be072e2017-06-28 18:29:23 +0200369 print_newline=not(opt.quiet),
370 always_print_percentage=opt.quiet)
Roy Lee18afd7f2010-05-09 04:32:08 +0800371
David James89ece422014-01-09 18:51:58 -0800372 objdir_project_map = dict()
373 for project in projects:
374 objdir_project_map.setdefault(project.objdir, []).append(project)
David James8d201162013-10-11 17:03:19 -0700375
David James89ece422014-01-09 18:51:58 -0800376 threads = set()
377 sem = _threading.Semaphore(self.jobs)
378 err_event = _threading.Event()
379 for project_list in objdir_project_map.values():
380 # Check for any errors before running any more tasks.
381 # ...we'll let existing threads finish, though.
382 if err_event.isSet() and not opt.force_broken:
383 break
Doug Andersonfc06ced2011-03-16 15:49:18 -0700384
David James89ece422014-01-09 18:51:58 -0800385 sem.acquire()
386 kwargs = dict(opt=opt,
387 projects=project_list,
Andrew Wheeler7f1ccfb2016-06-17 16:51:07 -0500388 sem=sem,
David James89ece422014-01-09 18:51:58 -0800389 lock=lock,
390 fetched=fetched,
391 pm=pm,
David James89ece422014-01-09 18:51:58 -0800392 err_event=err_event)
393 if self.jobs > 1:
David James8d201162013-10-11 17:03:19 -0700394 t = _threading.Thread(target = self._FetchProjectList,
David James89ece422014-01-09 18:51:58 -0800395 kwargs = kwargs)
David 'Digit' Turnere2126652012-09-05 10:35:06 +0200396 # Ensure that Ctrl-C will not freeze the repo process.
397 t.daemon = True
Roy Lee18afd7f2010-05-09 04:32:08 +0800398 threads.add(t)
399 t.start()
David James89ece422014-01-09 18:51:58 -0800400 else:
401 self._FetchProjectList(**kwargs)
Roy Lee18afd7f2010-05-09 04:32:08 +0800402
David James89ece422014-01-09 18:51:58 -0800403 for t in threads:
404 t.join()
Roy Lee18afd7f2010-05-09 04:32:08 +0800405
David James89ece422014-01-09 18:51:58 -0800406 # If we saw an error, exit with code 1 so that other scripts can check.
Nicolas Cornu8419ab22017-06-16 12:09:06 +0200407 if err_event.isSet() and not opt.force_broken:
David James89ece422014-01-09 18:51:58 -0800408 print('\nerror: Exited sync due to fetch errors', file=sys.stderr)
409 sys.exit(1)
Doug Andersonfc06ced2011-03-16 15:49:18 -0700410
Shawn O. Pearce68194f42009-04-10 16:48:52 -0700411 pm.end()
Dave Borowitz67700e92012-10-23 15:00:54 -0700412 self._fetch_times.Save()
Dave Borowitz18857212012-10-23 17:02:59 -0700413
Julien Campergue335f5ef2013-10-16 11:02:35 +0200414 if not self.manifest.IsArchive:
415 self._GCProjects(projects)
416
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700417 return fetched
418
Dave Borowitz18857212012-10-23 17:02:59 -0700419 def _GCProjects(self, projects):
Gabe Black2ff30292014-10-09 17:54:35 -0700420 gc_gitdirs = {}
David James8d201162013-10-11 17:03:19 -0700421 for project in projects:
Gabe Black2ff30292014-10-09 17:54:35 -0700422 if len(project.manifest.GetProjectsWithName(project.name)) > 1:
423 print('Shared project %s found, disabling pruning.' % project.name)
424 project.bare_git.config('--replace-all', 'gc.pruneExpire', 'never')
425 gc_gitdirs[project.gitdir] = project.bare_git
David James8d201162013-10-11 17:03:19 -0700426
Dave Borowitze2152672012-10-31 12:24:38 -0700427 has_dash_c = git_require((1, 7, 2))
428 if multiprocessing and has_dash_c:
Dave Borowitz18857212012-10-23 17:02:59 -0700429 cpu_count = multiprocessing.cpu_count()
430 else:
431 cpu_count = 1
432 jobs = min(self.jobs, cpu_count)
433
434 if jobs < 2:
Gabe Black2ff30292014-10-09 17:54:35 -0700435 for bare_git in gc_gitdirs.values():
David James8d201162013-10-11 17:03:19 -0700436 bare_git.gc('--auto')
Dave Borowitz18857212012-10-23 17:02:59 -0700437 return
438
Mike Frysinger0c0e9342019-06-13 12:42:39 -0400439 config = {'pack.threads': cpu_count // jobs if cpu_count > jobs else 1}
Dave Borowitz18857212012-10-23 17:02:59 -0700440
441 threads = set()
442 sem = _threading.Semaphore(jobs)
443 err_event = _threading.Event()
444
David James8d201162013-10-11 17:03:19 -0700445 def GC(bare_git):
Dave Borowitz18857212012-10-23 17:02:59 -0700446 try:
447 try:
David James8d201162013-10-11 17:03:19 -0700448 bare_git.gc('--auto', config=config)
Dave Borowitz18857212012-10-23 17:02:59 -0700449 except GitError:
450 err_event.set()
451 except:
452 err_event.set()
453 raise
454 finally:
455 sem.release()
456
Gabe Black2ff30292014-10-09 17:54:35 -0700457 for bare_git in gc_gitdirs.values():
Dave Borowitz18857212012-10-23 17:02:59 -0700458 if err_event.isSet():
459 break
460 sem.acquire()
David James8d201162013-10-11 17:03:19 -0700461 t = _threading.Thread(target=GC, args=(bare_git,))
Dave Borowitz18857212012-10-23 17:02:59 -0700462 t.daemon = True
463 threads.add(t)
464 t.start()
465
466 for t in threads:
467 t.join()
468
469 if err_event.isSet():
Sarah Owenscecd1d82012-11-01 22:59:27 -0700470 print('\nerror: Exited sync due to gc errors', file=sys.stderr)
Dave Borowitz18857212012-10-23 17:02:59 -0700471 sys.exit(1)
472
Tim Kilbourn07669002013-03-08 15:02:49 -0800473 def _ReloadManifest(self, manifest_name=None):
474 if manifest_name:
475 # Override calls _Unload already
476 self.manifest.Override(manifest_name)
477 else:
478 self.manifest._Unload()
479
Dan Willemsen43507912016-09-01 16:26:02 -0700480 def _DeleteProject(self, path):
481 print('Deleting obsolete path %s' % path, file=sys.stderr)
482
483 # Delete the .git directory first, so we're less likely to have a partially
484 # working git repository around. There shouldn't be any git projects here,
485 # so rmtree works.
486 try:
Renaud Paquaya65adf72016-11-03 10:37:53 -0700487 platform_utils.rmtree(os.path.join(path, '.git'))
Renaud Paquaybed8b622018-09-27 10:46:58 -0700488 except OSError as e:
489 print('Failed to remove %s (%s)' % (os.path.join(path, '.git'), str(e)), file=sys.stderr)
Dan Willemsen43507912016-09-01 16:26:02 -0700490 print('error: Failed to delete obsolete path %s' % path, file=sys.stderr)
491 print(' remove manually, then run sync again', file=sys.stderr)
492 return -1
493
494 # Delete everything under the worktree, except for directories that contain
495 # another git project
496 dirs_to_remove = []
497 failed = False
Renaud Paquaybed8b622018-09-27 10:46:58 -0700498 for root, dirs, files in platform_utils.walk(path):
Dan Willemsen43507912016-09-01 16:26:02 -0700499 for f in files:
500 try:
Renaud Paquay010fed72016-11-11 14:25:29 -0800501 platform_utils.remove(os.path.join(root, f))
Renaud Paquaybed8b622018-09-27 10:46:58 -0700502 except OSError as e:
503 print('Failed to remove %s (%s)' % (os.path.join(root, f), str(e)), file=sys.stderr)
Dan Willemsen43507912016-09-01 16:26:02 -0700504 failed = True
505 dirs[:] = [d for d in dirs
506 if not os.path.lexists(os.path.join(root, d, '.git'))]
507 dirs_to_remove += [os.path.join(root, d) for d in dirs
508 if os.path.join(root, d) not in dirs_to_remove]
509 for d in reversed(dirs_to_remove):
Renaud Paquay227ad2e2016-11-01 14:37:13 -0700510 if platform_utils.islink(d):
Dan Willemseneceeb1b2016-09-25 18:24:27 -0700511 try:
Renaud Paquay010fed72016-11-11 14:25:29 -0800512 platform_utils.remove(d)
Renaud Paquaybed8b622018-09-27 10:46:58 -0700513 except OSError as e:
514 print('Failed to remove %s (%s)' % (os.path.join(root, d), str(e)), file=sys.stderr)
Dan Willemseneceeb1b2016-09-25 18:24:27 -0700515 failed = True
Renaud Paquaybed8b622018-09-27 10:46:58 -0700516 elif len(platform_utils.listdir(d)) == 0:
Dan Willemsen43507912016-09-01 16:26:02 -0700517 try:
Renaud Paquaybed8b622018-09-27 10:46:58 -0700518 platform_utils.rmdir(d)
519 except OSError as e:
520 print('Failed to remove %s (%s)' % (os.path.join(root, d), str(e)), file=sys.stderr)
Dan Willemsen43507912016-09-01 16:26:02 -0700521 failed = True
522 continue
523 if failed:
524 print('error: Failed to delete obsolete path %s' % path, file=sys.stderr)
525 print(' remove manually, then run sync again', file=sys.stderr)
526 return -1
527
528 # Try deleting parent dirs if they are empty
529 project_dir = path
530 while project_dir != self.manifest.topdir:
Renaud Paquaybed8b622018-09-27 10:46:58 -0700531 if len(platform_utils.listdir(project_dir)) == 0:
532 platform_utils.rmdir(project_dir)
Dan Willemsen43507912016-09-01 16:26:02 -0700533 else:
534 break
535 project_dir = os.path.dirname(project_dir)
536
537 return 0
538
Oleksii Okolielovd3c0f592018-12-17 19:23:44 -0500539 def UpdateProjectList(self, opt):
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700540 new_project_paths = []
Colin Cross5acde752012-03-28 20:15:45 -0700541 for project in self.GetProjects(None, missing_ok=True):
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700542 if project.relpath:
543 new_project_paths.append(project.relpath)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700544 file_name = 'project.list'
545 file_path = os.path.join(self.manifest.repodir, file_name)
546 old_project_paths = []
547
548 if os.path.exists(file_path):
549 fd = open(file_path, 'r')
550 try:
551 old_project_paths = fd.read().split('\n')
552 finally:
553 fd.close()
Kuang-che Wu0d9b16d2019-04-06 00:49:47 +0800554 # In reversed order, so subfolders are deleted before parent folder.
555 for path in sorted(old_project_paths, reverse=True):
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700556 if not path:
557 continue
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700558 if path not in new_project_paths:
David Pursehouse8a68ff92012-09-24 12:15:13 +0900559 # If the path has already been deleted, we don't need to do it
Dan Willemsen43507912016-09-01 16:26:02 -0700560 gitdir = os.path.join(self.manifest.topdir, path, '.git')
561 if os.path.exists(gitdir):
David Pursehousec1b86a22012-11-14 11:36:51 +0900562 project = Project(
563 manifest = self.manifest,
564 name = path,
565 remote = RemoteSpec('origin'),
David James8d201162013-10-11 17:03:19 -0700566 gitdir = gitdir,
567 objdir = gitdir,
David Pursehousec1b86a22012-11-14 11:36:51 +0900568 worktree = os.path.join(self.manifest.topdir, path),
569 relpath = path,
570 revisionExpr = 'HEAD',
571 revisionId = None,
572 groups = None)
Anthonyf3fdf822009-09-26 13:38:52 -0400573
Oleksii Okolielovd3c0f592018-12-17 19:23:44 -0500574 if project.IsDirty() and opt.force_remove_dirty:
575 print('WARNING: Removing dirty project "%s": uncommitted changes '
576 'erased' % project.relpath, file=sys.stderr)
577 self._DeleteProject(project.worktree)
578 elif project.IsDirty():
David Pursehouse2f9e7e42013-03-05 17:26:46 +0900579 print('error: Cannot remove project "%s": uncommitted changes '
David Pursehousec1b86a22012-11-14 11:36:51 +0900580 'are present' % project.relpath, file=sys.stderr)
581 print(' commit changes, then run sync again',
582 file=sys.stderr)
583 return -1
Dan Willemsen43507912016-09-01 16:26:02 -0700584 elif self._DeleteProject(project.worktree):
585 return -1
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700586
Shawn O. Pearce9fb29ce2009-06-04 20:41:02 -0700587 new_project_paths.sort()
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700588 fd = open(file_path, 'w')
589 try:
590 fd.write('\n'.join(new_project_paths))
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700591 fd.write('\n')
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700592 finally:
593 fd.close()
594 return 0
595
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700596 def Execute(self, opt, args):
Roy Lee18afd7f2010-05-09 04:32:08 +0800597 if opt.jobs:
598 self.jobs = opt.jobs
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -0700599 if self.jobs > 1:
600 soft_limit, _ = _rlimit_nofile()
Mike Frysinger0c0e9342019-06-13 12:42:39 -0400601 self.jobs = min(self.jobs, (soft_limit - 5) // 3)
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -0700602
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700603 if opt.network_only and opt.detach_head:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700604 print('error: cannot combine -n and -d', file=sys.stderr)
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700605 sys.exit(1)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700606 if opt.network_only and opt.local_only:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700607 print('error: cannot combine -n and -l', file=sys.stderr)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700608 sys.exit(1)
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500609 if opt.manifest_name and opt.smart_sync:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700610 print('error: cannot combine -m and -s', file=sys.stderr)
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500611 sys.exit(1)
612 if opt.manifest_name and opt.smart_tag:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700613 print('error: cannot combine -m and -t', file=sys.stderr)
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500614 sys.exit(1)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900615 if opt.manifest_server_username or opt.manifest_server_password:
616 if not (opt.smart_sync or opt.smart_tag):
Sarah Owenscecd1d82012-11-01 22:59:27 -0700617 print('error: -u and -p may only be combined with -s or -t',
618 file=sys.stderr)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900619 sys.exit(1)
620 if None in [opt.manifest_server_username, opt.manifest_server_password]:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700621 print('error: both -u and -p must be given', file=sys.stderr)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900622 sys.exit(1)
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500623
624 if opt.manifest_name:
625 self.manifest.Override(opt.manifest_name)
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700626
Chirayu Desaia892b102013-06-11 14:18:46 +0530627 manifest_name = opt.manifest_name
David Pursehouse59b41742015-05-07 14:36:09 +0900628 smart_sync_manifest_name = "smart_sync_override.xml"
629 smart_sync_manifest_path = os.path.join(
630 self.manifest.manifestProject.worktree, smart_sync_manifest_name)
Chirayu Desaia892b102013-06-11 14:18:46 +0530631
Victor Boivie08c880d2011-04-19 10:32:52 +0200632 if opt.smart_sync or opt.smart_tag:
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700633 if not self.manifest.manifest_server:
David Pursehouse2f9e7e42013-03-05 17:26:46 +0900634 print('error: cannot smart sync: no manifest server defined in '
Sarah Owenscecd1d82012-11-01 22:59:27 -0700635 'manifest', file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700636 sys.exit(1)
David Pursehouse86d973d2012-08-24 10:21:02 +0900637
638 manifest_server = self.manifest.manifest_server
David Pursehousefb99c712013-09-25 11:09:34 +0900639 if not opt.quiet:
640 print('Using manifest server %s' % manifest_server)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900641
David Pursehouse86d973d2012-08-24 10:21:02 +0900642 if not '@' in manifest_server:
David Pursehousecf76b1b2012-09-14 10:31:42 +0900643 username = None
644 password = None
645 if opt.manifest_server_username and opt.manifest_server_password:
646 username = opt.manifest_server_username
647 password = opt.manifest_server_password
David Pursehouse86d973d2012-08-24 10:21:02 +0900648 else:
649 try:
David Pursehousecf76b1b2012-09-14 10:31:42 +0900650 info = netrc.netrc()
651 except IOError:
David Pursehouseba7bc732015-08-20 16:55:42 +0900652 # .netrc file does not exist or could not be opened
653 pass
David Pursehouse86d973d2012-08-24 10:21:02 +0900654 else:
David Pursehousecf76b1b2012-09-14 10:31:42 +0900655 try:
Chirayu Desaidb2ad9d2013-06-11 13:42:25 +0530656 parse_result = urllib.parse.urlparse(manifest_server)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900657 if parse_result.hostname:
David Pursehouseba7bc732015-08-20 16:55:42 +0900658 auth = info.authenticators(parse_result.hostname)
659 if auth:
660 username, _account, password = auth
661 else:
662 print('No credentials found for %s in .netrc'
663 % parse_result.hostname, file=sys.stderr)
Sarah Owensa5be53f2012-09-09 15:37:57 -0700664 except netrc.NetrcParseError as e:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700665 print('Error parsing .netrc file: %s' % e, file=sys.stderr)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900666
667 if (username and password):
668 manifest_server = manifest_server.replace('://', '://%s:%s@' %
669 (username, password),
670 1)
David Pursehouse86d973d2012-08-24 10:21:02 +0900671
Dan Willemsen0745bb22015-08-17 13:41:45 -0700672 transport = PersistentTransport(manifest_server)
673 if manifest_server.startswith('persistent-'):
674 manifest_server = manifest_server[len('persistent-'):]
675
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700676 try:
Dan Willemsen0745bb22015-08-17 13:41:45 -0700677 server = xmlrpc.client.Server(manifest_server, transport=transport)
Victor Boivie08c880d2011-04-19 10:32:52 +0200678 if opt.smart_sync:
679 p = self.manifest.manifestProject
680 b = p.GetBranch(p.CurrentBranch)
681 branch = b.merge
682 if branch.startswith(R_HEADS):
683 branch = branch[len(R_HEADS):]
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700684
Victor Boivie08c880d2011-04-19 10:32:52 +0200685 env = os.environ.copy()
Jeff Davidson5cf16602014-10-02 10:13:38 -0700686 if 'SYNC_TARGET' in env:
687 target = env['SYNC_TARGET']
688 [success, manifest_str] = server.GetApprovedManifest(branch, target)
689 elif 'TARGET_PRODUCT' in env and 'TARGET_BUILD_VARIANT' in env:
Victor Boivie08c880d2011-04-19 10:32:52 +0200690 target = '%s-%s' % (env['TARGET_PRODUCT'],
691 env['TARGET_BUILD_VARIANT'])
692 [success, manifest_str] = server.GetApprovedManifest(branch, target)
693 else:
694 [success, manifest_str] = server.GetApprovedManifest(branch)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700695 else:
Victor Boivie08c880d2011-04-19 10:32:52 +0200696 assert(opt.smart_tag)
697 [success, manifest_str] = server.GetManifest(opt.smart_tag)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700698
699 if success:
David Pursehouse59b41742015-05-07 14:36:09 +0900700 manifest_name = smart_sync_manifest_name
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700701 try:
David Pursehouse59b41742015-05-07 14:36:09 +0900702 f = open(smart_sync_manifest_path, 'w')
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700703 try:
704 f.write(manifest_str)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700705 finally:
706 f.close()
David Pursehouse727cc3e2015-05-07 14:16:49 +0900707 except IOError as e:
708 print('error: cannot write manifest to %s:\n%s'
David Pursehouse59b41742015-05-07 14:36:09 +0900709 % (smart_sync_manifest_path, e),
Sarah Owenscecd1d82012-11-01 22:59:27 -0700710 file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700711 sys.exit(1)
Victor Boivie53a6c5d2013-03-19 12:20:52 +0100712 self._ReloadManifest(manifest_name)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700713 else:
David Pursehouse351fe2c2013-09-25 17:54:26 +0900714 print('error: manifest server RPC call failed: %s' %
715 manifest_str, file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700716 sys.exit(1)
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530717 except (socket.error, IOError, xmlrpc.client.Fault) as e:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700718 print('error: cannot connect to manifest server %s:\n%s'
719 % (self.manifest.manifest_server, e), file=sys.stderr)
David Pursehousebd489c42012-08-23 10:21:26 +0900720 sys.exit(1)
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530721 except xmlrpc.client.ProtocolError as e:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700722 print('error: cannot connect to manifest server %s:\n%d %s'
723 % (self.manifest.manifest_server, e.errcode, e.errmsg),
724 file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700725 sys.exit(1)
David Pursehouse59b41742015-05-07 14:36:09 +0900726 else: # Not smart sync or smart tag mode
727 if os.path.isfile(smart_sync_manifest_path):
728 try:
Renaud Paquay010fed72016-11-11 14:25:29 -0800729 platform_utils.remove(smart_sync_manifest_path)
David Pursehouse59b41742015-05-07 14:36:09 +0900730 except OSError as e:
731 print('error: failed to remove existing smart sync override manifest: %s' %
732 e, file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700733
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700734 rp = self.manifest.repoProject
735 rp.PreSync()
736
737 mp = self.manifest.manifestProject
738 mp.PreSync()
739
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800740 if opt.repo_upgraded:
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -0700741 _PostRepoUpgrade(self.manifest, quiet=opt.quiet)
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800742
Nico Sallembien9bb18162009-12-07 15:38:01 -0800743 if not opt.local_only:
David Rileye0684ad2017-04-05 00:02:59 -0700744 start = time.time()
745 success = mp.Sync_NetworkHalf(quiet=opt.quiet,
746 current_branch_only=opt.current_branch_only,
747 no_tags=opt.no_tags,
748 optimized_fetch=opt.optimized_fetch,
749 submodules=self.manifest.HasSubmodules)
750 finish = time.time()
751 self.event_log.AddSync(mp, event_log.TASK_SYNC_NETWORK,
752 start, finish, success)
Nico Sallembien9bb18162009-12-07 15:38:01 -0800753
754 if mp.HasChanges:
755 syncbuf = SyncBuffer(mp.config)
David Rileye0684ad2017-04-05 00:02:59 -0700756 start = time.time()
Martin Kellye4e94d22017-03-21 16:05:12 -0700757 mp.Sync_LocalHalf(syncbuf, submodules=self.manifest.HasSubmodules)
David Rileye0684ad2017-04-05 00:02:59 -0700758 clean = syncbuf.Finish()
759 self.event_log.AddSync(mp, event_log.TASK_SYNC_LOCAL,
760 start, time.time(), clean)
761 if not clean:
Nico Sallembien9bb18162009-12-07 15:38:01 -0800762 sys.exit(1)
Victor Boivie53a6c5d2013-03-19 12:20:52 +0100763 self._ReloadManifest(manifest_name)
Shawn O. Pearcec4657962011-09-26 09:08:01 -0700764 if opt.jobs is None:
765 self.jobs = self.manifest.default.sync_j
Simran Basib9a1b732015-08-20 12:19:28 -0700766
Simran Basib9a1b732015-08-20 12:19:28 -0700767 if self.gitc_manifest:
768 gitc_manifest_projects = self.GetProjects(args,
Simran Basib9a1b732015-08-20 12:19:28 -0700769 missing_ok=True)
770 gitc_projects = []
771 opened_projects = []
772 for project in gitc_manifest_projects:
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700773 if project.relpath in self.gitc_manifest.paths and \
774 self.gitc_manifest.paths[project.relpath].old_revision:
775 opened_projects.append(project.relpath)
Simran Basib9a1b732015-08-20 12:19:28 -0700776 else:
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700777 gitc_projects.append(project.relpath)
Simran Basib9a1b732015-08-20 12:19:28 -0700778
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700779 if not args:
780 gitc_projects = None
781
782 if gitc_projects != [] and not opt.local_only:
Simran Basib9a1b732015-08-20 12:19:28 -0700783 print('Updating GITC client: %s' % self.gitc_manifest.gitc_client_name)
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700784 manifest = GitcManifest(self.repodir, self.gitc_manifest.gitc_client_name)
785 if manifest_name:
786 manifest.Override(manifest_name)
787 else:
788 manifest.Override(self.manifest.manifestFile)
789 gitc_utils.generate_gitc_manifest(self.gitc_manifest,
790 manifest,
Simran Basib9a1b732015-08-20 12:19:28 -0700791 gitc_projects)
792 print('GITC client successfully synced.')
793
794 # The opened projects need to be synced as normal, therefore we
795 # generate a new args list to represent the opened projects.
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700796 # TODO: make this more reliable -- if there's a project name/path overlap,
797 # this may choose the wrong project.
David Pursehouse3bcd3052017-07-10 22:42:22 +0900798 args = [os.path.relpath(self.manifest.paths[path].worktree, os.getcwd())
799 for path in opened_projects]
Simran Basib9a1b732015-08-20 12:19:28 -0700800 if not args:
801 return
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800802 all_projects = self.GetProjects(args,
803 missing_ok=True,
804 submodules_ok=opt.fetch_submodules)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700805
Dave Borowitz67700e92012-10-23 15:00:54 -0700806 self._fetch_times = _FetchTimes(self.manifest)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700807 if not opt.local_only:
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700808 to_fetch = []
809 now = time.time()
Dave Borowitz67700e92012-10-23 15:00:54 -0700810 if _ONE_DAY_S <= (now - rp.LastFetch):
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700811 to_fetch.append(rp)
David Pursehouse8a68ff92012-09-24 12:15:13 +0900812 to_fetch.extend(all_projects)
Dave Borowitz67700e92012-10-23 15:00:54 -0700813 to_fetch.sort(key=self._fetch_times.Get, reverse=True)
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700814
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800815 fetched = self._Fetch(to_fetch, opt)
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700816 _PostRepoFetch(rp, opt.no_repo_verify)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700817 if opt.network_only:
818 # bail out now; the rest touches the working tree
819 return
820
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800821 # Iteratively fetch missing and/or nested unregistered submodules
822 previously_missing_set = set()
823 while True:
Victor Boivie53a6c5d2013-03-19 12:20:52 +0100824 self._ReloadManifest(manifest_name)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800825 all_projects = self.GetProjects(args,
826 missing_ok=True,
827 submodules_ok=opt.fetch_submodules)
828 missing = []
829 for project in all_projects:
830 if project.gitdir not in fetched:
831 missing.append(project)
832 if not missing:
833 break
834 # Stop us from non-stopped fetching actually-missing repos: If set of
835 # missing repos has not been changed from last fetch, we break.
836 missing_set = set(p.name for p in missing)
837 if previously_missing_set == missing_set:
838 break
839 previously_missing_set = missing_set
840 fetched.update(self._Fetch(missing, opt))
841
Julien Campergue335f5ef2013-10-16 11:02:35 +0200842 if self.manifest.IsMirror or self.manifest.IsArchive:
Shawn O. Pearcecd1d7ff2009-06-04 16:15:53 -0700843 # bail out now, we have no working tree
844 return
845
Oleksii Okolielovd3c0f592018-12-17 19:23:44 -0500846 if self.UpdateProjectList(opt):
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700847 sys.exit(1)
848
Shawn O. Pearce350cde42009-04-16 11:21:18 -0700849 syncbuf = SyncBuffer(mp.config,
850 detach_head = opt.detach_head)
David Pursehouse8a68ff92012-09-24 12:15:13 +0900851 pm = Progress('Syncing work tree', len(all_projects))
852 for project in all_projects:
Shawn O. Pearce68194f42009-04-10 16:48:52 -0700853 pm.update()
Shawn O. Pearcee284ad12008-11-04 07:37:10 -0800854 if project.worktree:
David Rileye0684ad2017-04-05 00:02:59 -0700855 start = time.time()
Kevin Degiabaa7f32014-11-12 11:27:45 -0700856 project.Sync_LocalHalf(syncbuf, force_sync=opt.force_sync)
David Rileye0684ad2017-04-05 00:02:59 -0700857 self.event_log.AddSync(project, event_log.TASK_SYNC_LOCAL,
858 start, time.time(), syncbuf.Recently())
Shawn O. Pearce68194f42009-04-10 16:48:52 -0700859 pm.end()
Sarah Owenscecd1d82012-11-01 22:59:27 -0700860 print(file=sys.stderr)
Shawn O. Pearce350cde42009-04-16 11:21:18 -0700861 if not syncbuf.Finish():
862 sys.exit(1)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700863
Doug Anderson2b8db3c2010-11-01 15:08:06 -0700864 # If there's a notice that's supposed to print at the end of the sync, print
865 # it now...
866 if self.manifest.notice:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700867 print(self.manifest.notice)
Doug Anderson2b8db3c2010-11-01 15:08:06 -0700868
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -0700869def _PostRepoUpgrade(manifest, quiet=False):
Conley Owens094cdbe2014-01-30 15:09:59 -0800870 wrapper = Wrapper()
Conley Owensc9129d92012-10-01 16:12:28 -0700871 if wrapper.NeedSetupGnuPG():
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -0700872 wrapper.SetupGnuPG(quiet)
Conley Owensf2fe2d92014-01-29 13:53:43 -0800873 for project in manifest.projects:
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700874 if project.Exists:
875 project.PostRepoUpgrade()
876
877def _PostRepoFetch(rp, no_repo_verify=False, verbose=False):
878 if rp.HasChanges:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700879 print('info: A new version of repo is available', file=sys.stderr)
880 print(file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700881 if no_repo_verify or _VerifyTag(rp):
Shawn O. Pearce350cde42009-04-16 11:21:18 -0700882 syncbuf = SyncBuffer(rp.config)
883 rp.Sync_LocalHalf(syncbuf)
884 if not syncbuf.Finish():
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700885 sys.exit(1)
Sarah Owenscecd1d82012-11-01 22:59:27 -0700886 print('info: Restarting repo with latest version', file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700887 raise RepoChangedException(['--repo-upgraded'])
888 else:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700889 print('warning: Skipped upgrade to unverified version', file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700890 else:
891 if verbose:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700892 print('repo version %s is current' % rp.work_git.describe(HEAD),
893 file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700894
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700895def _VerifyTag(project):
896 gpg_dir = os.path.expanduser('~/.repoconfig/gnupg')
897 if not os.path.exists(gpg_dir):
Sarah Owenscecd1d82012-11-01 22:59:27 -0700898 print('warning: GnuPG was not available during last "repo init"\n'
899 'warning: Cannot automatically authenticate repo."""',
900 file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700901 return True
902
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700903 try:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -0700904 cur = project.bare_git.describe(project.GetRevisionId())
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700905 except GitError:
906 cur = None
907
908 if not cur \
909 or re.compile(r'^.*-[0-9]{1,}-g[0-9a-f]{1,}$').match(cur):
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -0700910 rev = project.revisionExpr
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700911 if rev.startswith(R_HEADS):
912 rev = rev[len(R_HEADS):]
913
Sarah Owenscecd1d82012-11-01 22:59:27 -0700914 print(file=sys.stderr)
915 print("warning: project '%s' branch '%s' is not signed"
916 % (project.name, rev), file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700917 return False
918
Shawn O. Pearcef18cb762010-12-07 11:41:05 -0800919 env = os.environ.copy()
920 env['GIT_DIR'] = project.gitdir.encode()
921 env['GNUPGHOME'] = gpg_dir.encode()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700922
923 cmd = [GIT, 'tag', '-v', cur]
924 proc = subprocess.Popen(cmd,
925 stdout = subprocess.PIPE,
926 stderr = subprocess.PIPE,
927 env = env)
928 out = proc.stdout.read()
929 proc.stdout.close()
930
931 err = proc.stderr.read()
932 proc.stderr.close()
933
934 if proc.wait() != 0:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700935 print(file=sys.stderr)
936 print(out, file=sys.stderr)
937 print(err, file=sys.stderr)
938 print(file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700939 return False
940 return True
Dave Borowitz67700e92012-10-23 15:00:54 -0700941
David Rileye0684ad2017-04-05 00:02:59 -0700942
Dave Borowitz67700e92012-10-23 15:00:54 -0700943class _FetchTimes(object):
Dave Borowitzd9478582012-10-23 16:35:39 -0700944 _ALPHA = 0.5
945
Dave Borowitz67700e92012-10-23 15:00:54 -0700946 def __init__(self, manifest):
Anthony King85b24ac2014-05-06 15:57:48 +0100947 self._path = os.path.join(manifest.repodir, '.repo_fetchtimes.json')
Dave Borowitz67700e92012-10-23 15:00:54 -0700948 self._times = None
Dave Borowitzd9478582012-10-23 16:35:39 -0700949 self._seen = set()
Dave Borowitz67700e92012-10-23 15:00:54 -0700950
951 def Get(self, project):
952 self._Load()
953 return self._times.get(project.name, _ONE_DAY_S)
954
955 def Set(self, project, t):
Dave Borowitzd9478582012-10-23 16:35:39 -0700956 self._Load()
957 name = project.name
958 old = self._times.get(name, t)
959 self._seen.add(name)
960 a = self._ALPHA
961 self._times[name] = (a*t) + ((1-a) * old)
Dave Borowitz67700e92012-10-23 15:00:54 -0700962
963 def _Load(self):
964 if self._times is None:
965 try:
Anthony King85b24ac2014-05-06 15:57:48 +0100966 f = open(self._path)
Dave Borowitz67700e92012-10-23 15:00:54 -0700967 try:
Anthony King85b24ac2014-05-06 15:57:48 +0100968 self._times = json.load(f)
969 finally:
970 f.close()
971 except (IOError, ValueError):
972 try:
Renaud Paquay010fed72016-11-11 14:25:29 -0800973 platform_utils.remove(self._path)
Anthony King85b24ac2014-05-06 15:57:48 +0100974 except OSError:
975 pass
976 self._times = {}
Dave Borowitz67700e92012-10-23 15:00:54 -0700977
978 def Save(self):
979 if self._times is None:
980 return
Dave Borowitzd9478582012-10-23 16:35:39 -0700981
982 to_delete = []
983 for name in self._times:
984 if name not in self._seen:
985 to_delete.append(name)
986 for name in to_delete:
987 del self._times[name]
988
Dave Borowitz67700e92012-10-23 15:00:54 -0700989 try:
Anthony King85b24ac2014-05-06 15:57:48 +0100990 f = open(self._path, 'w')
Dave Borowitz67700e92012-10-23 15:00:54 -0700991 try:
Anthony King85b24ac2014-05-06 15:57:48 +0100992 json.dump(self._times, f, indent=2)
993 finally:
994 f.close()
995 except (IOError, TypeError):
996 try:
Renaud Paquay010fed72016-11-11 14:25:29 -0800997 platform_utils.remove(self._path)
Anthony King85b24ac2014-05-06 15:57:48 +0100998 except OSError:
999 pass
Dan Willemsen0745bb22015-08-17 13:41:45 -07001000
1001# This is a replacement for xmlrpc.client.Transport using urllib2
1002# and supporting persistent-http[s]. It cannot change hosts from
1003# request to request like the normal transport, the real url
1004# is passed during initialization.
1005class PersistentTransport(xmlrpc.client.Transport):
1006 def __init__(self, orig_host):
1007 self.orig_host = orig_host
1008
1009 def request(self, host, handler, request_body, verbose=False):
1010 with GetUrlCookieFile(self.orig_host, not verbose) as (cookiefile, proxy):
1011 # Python doesn't understand cookies with the #HttpOnly_ prefix
1012 # Since we're only using them for HTTP, copy the file temporarily,
1013 # stripping those prefixes away.
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001014 if cookiefile:
1015 tmpcookiefile = tempfile.NamedTemporaryFile()
David Pursehouse4c5f74e2015-10-02 11:10:10 +09001016 tmpcookiefile.write("# HTTP Cookie File")
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001017 try:
1018 with open(cookiefile) as f:
1019 for line in f:
1020 if line.startswith("#HttpOnly_"):
1021 line = line[len("#HttpOnly_"):]
1022 tmpcookiefile.write(line)
1023 tmpcookiefile.flush()
Dan Willemsen0745bb22015-08-17 13:41:45 -07001024
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001025 cookiejar = cookielib.MozillaCookieJar(tmpcookiefile.name)
David Pursehouseb1ad2192015-09-30 10:35:43 +09001026 try:
1027 cookiejar.load()
1028 except cookielib.LoadError:
1029 cookiejar = cookielib.CookieJar()
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001030 finally:
1031 tmpcookiefile.close()
1032 else:
1033 cookiejar = cookielib.CookieJar()
Dan Willemsen0745bb22015-08-17 13:41:45 -07001034
1035 proxyhandler = urllib.request.ProxyHandler
1036 if proxy:
1037 proxyhandler = urllib.request.ProxyHandler({
1038 "http": proxy,
1039 "https": proxy })
1040
1041 opener = urllib.request.build_opener(
1042 urllib.request.HTTPCookieProcessor(cookiejar),
1043 proxyhandler)
1044
1045 url = urllib.parse.urljoin(self.orig_host, handler)
1046 parse_results = urllib.parse.urlparse(url)
1047
1048 scheme = parse_results.scheme
1049 if scheme == 'persistent-http':
1050 scheme = 'http'
1051 if scheme == 'persistent-https':
1052 # If we're proxying through persistent-https, use http. The
1053 # proxy itself will do the https.
1054 if proxy:
1055 scheme = 'http'
1056 else:
1057 scheme = 'https'
1058
1059 # Parse out any authentication information using the base class
1060 host, extra_headers, _ = self.get_host_info(parse_results.netloc)
1061
1062 url = urllib.parse.urlunparse((
1063 scheme,
1064 host,
1065 parse_results.path,
1066 parse_results.params,
1067 parse_results.query,
1068 parse_results.fragment))
1069
1070 request = urllib.request.Request(url, request_body)
1071 if extra_headers is not None:
1072 for (name, header) in extra_headers:
1073 request.add_header(name, header)
1074 request.add_header('Content-Type', 'text/xml')
1075 try:
1076 response = opener.open(request)
1077 except urllib.error.HTTPError as e:
1078 if e.code == 501:
1079 # We may have been redirected through a login process
1080 # but our POST turned into a GET. Retry.
1081 response = opener.open(request)
1082 else:
1083 raise
1084
1085 p, u = xmlrpc.client.getparser()
1086 while 1:
1087 data = response.read(1024)
1088 if not data:
1089 break
1090 p.feed(data)
1091 p.close()
1092 return u.close()
1093
1094 def close(self):
1095 pass
1096