Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
| 2 # Copyright 2014 The Chromium Authors. All rights reserved. | 2 # Copyright 2014 The Chromium Authors. All rights reserved. |
| 3 # Use of this source code is governed by a BSD-style license that can be | 3 # Use of this source code is governed by a BSD-style license that can be |
| 4 # found in the LICENSE file. | 4 # found in the LICENSE file. |
| 5 | 5 |
| 6 """A git command for managing a local cache of git repositories.""" | 6 """A git command for managing a local cache of git repositories.""" |
| 7 | 7 |
| 8 from __future__ import print_function | 8 from __future__ import print_function |
| 9 import errno | 9 import errno |
| 10 import logging | 10 import logging |
| 11 import optparse | 11 import optparse |
| 12 import os | 12 import os |
| 13 import re | 13 import re |
| 14 import tempfile | 14 import tempfile |
| 15 import time | 15 import time |
| 16 import subprocess | 16 import subprocess |
| 17 import sys | 17 import sys |
| 18 import urlparse | 18 import urlparse |
| 19 import zipfile | 19 import zipfile |
| 20 | 20 |
| 21 from download_from_google_storage import Gsutil | 21 from download_from_google_storage import Gsutil |
| 22 import gclient_utils | 22 import gclient_utils |
| 23 import subcommand | 23 import subcommand |
| 24 | 24 |
| 25 # Analogous to gc.autopacklimit git config. | |
| 26 GC_AUTOPACKLIMIT = 50 | |
| 27 | |
| 25 try: | 28 try: |
| 26 # pylint: disable=E0602 | 29 # pylint: disable=E0602 |
| 27 WinErr = WindowsError | 30 WinErr = WindowsError |
| 28 except NameError: | 31 except NameError: |
| 29 class WinErr(Exception): | 32 class WinErr(Exception): |
| 30 pass | 33 pass |
| 31 | 34 |
| 32 class LockError(Exception): | 35 class LockError(Exception): |
| 33 pass | 36 pass |
| 34 | 37 |
| (...skipping 184 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 219 kwargs.setdefault('filter_fn', self.print) | 222 kwargs.setdefault('filter_fn', self.print) |
| 220 env = kwargs.get('env') or kwargs.setdefault('env', os.environ.copy()) | 223 env = kwargs.get('env') or kwargs.setdefault('env', os.environ.copy()) |
| 221 env.setdefault('GIT_ASKPASS', 'true') | 224 env.setdefault('GIT_ASKPASS', 'true') |
| 222 env.setdefault('SSH_ASKPASS', 'true') | 225 env.setdefault('SSH_ASKPASS', 'true') |
| 223 self.print('running "git %s" in "%s"' % (' '.join(cmd), cwd)) | 226 self.print('running "git %s" in "%s"' % (' '.join(cmd), cwd)) |
| 224 gclient_utils.CheckCallAndFilter([self.git_exe] + cmd, **kwargs) | 227 gclient_utils.CheckCallAndFilter([self.git_exe] + cmd, **kwargs) |
| 225 | 228 |
| 226 def config(self, cwd=None): | 229 def config(self, cwd=None): |
| 227 if cwd is None: | 230 if cwd is None: |
| 228 cwd = self.mirror_path | 231 cwd = self.mirror_path |
| 232 | |
| 233 # Don't run git-gc in a daemon. Bad things can happen if it gets killed. | |
| 234 self.RunGit(['config', 'gc.autodetach', '0'], cwd=cwd) | |
| 235 | |
| 236 # Don't combine pack files into one big pack file. It's really slow for | |
| 237 # repositories, and there's no way to track progress and make sure it's | |
| 238 # not stuck. | |
| 239 self.RunGit(['config', 'gc.autopacklimit', '0'], cwd=cwd) | |
| 240 | |
| 241 # Allocate more RAM for cache-ing delta chains, for better performance | |
| 242 # of "Resolving deltas". | |
| 229 self.RunGit(['config', 'core.deltaBaseCacheLimit', | 243 self.RunGit(['config', 'core.deltaBaseCacheLimit', |
| 230 gclient_utils.DefaultDeltaBaseCacheLimit()], cwd=cwd) | 244 gclient_utils.DefaultDeltaBaseCacheLimit()], cwd=cwd) |
| 245 | |
| 231 self.RunGit(['config', 'remote.origin.url', self.url], cwd=cwd) | 246 self.RunGit(['config', 'remote.origin.url', self.url], cwd=cwd) |
| 232 self.RunGit(['config', '--replace-all', 'remote.origin.fetch', | 247 self.RunGit(['config', '--replace-all', 'remote.origin.fetch', |
| 233 '+refs/heads/*:refs/heads/*'], cwd=cwd) | 248 '+refs/heads/*:refs/heads/*'], cwd=cwd) |
| 234 for ref in self.refs: | 249 for ref in self.refs: |
| 235 ref = ref.lstrip('+').rstrip('/') | 250 ref = ref.lstrip('+').rstrip('/') |
| 236 if ref.startswith('refs/'): | 251 if ref.startswith('refs/'): |
| 237 refspec = '+%s:%s' % (ref, ref) | 252 refspec = '+%s:%s' % (ref, ref) |
| 238 else: | 253 else: |
| 239 refspec = '+refs/%s/*:refs/%s/*' % (ref, ref) | 254 refspec = '+refs/%s/*:refs/%s/*' % (ref, ref) |
| 240 self.RunGit(['config', '--add', 'remote.origin.fetch', refspec], cwd=cwd) | 255 self.RunGit(['config', '--add', 'remote.origin.fetch', refspec], cwd=cwd) |
| 241 | 256 |
| 242 def bootstrap_repo(self, directory): | 257 def bootstrap_repo(self, directory): |
| 243 """Bootstrap the repo from Google Stroage if possible.""" | 258 """Bootstrap the repo from Google Stroage if possible.""" |
| 244 | 259 |
| 245 python_fallback = False | 260 python_fallback = False |
| 246 if sys.platform.startswith('win') and not self.FindExecutable('7z'): | 261 if sys.platform.startswith('win') and not self.FindExecutable('7z'): |
| 247 python_fallback = True | 262 python_fallback = True |
| 248 elif sys.platform.startswith('darwin'): | 263 elif sys.platform.startswith('darwin'): |
| 249 # The OSX version of unzip doesn't support zip64. | 264 # The OSX version of unzip doesn't support zip64. |
| 250 python_fallback = True | 265 python_fallback = True |
| 251 elif not self.FindExecutable('unzip'): | 266 elif not self.FindExecutable('unzip'): |
| 252 python_fallback = True | 267 python_fallback = True |
| 253 | 268 |
| 254 gs_folder = 'gs://%s/%s' % (self.bootstrap_bucket, self.basedir) | 269 gs_folder = 'gs://%s/%s' % (self.bootstrap_bucket, self.basedir) |
| 255 gsutil = Gsutil(self.gsutil_exe, boto_path=None, bypass_prodaccess=True) | 270 gsutil = Gsutil(self.gsutil_exe, boto_path=None, bypass_prodaccess=True) |
| 256 # Get the most recent version of the zipfile. | 271 # Get the most recent version of the zipfile. |
| 257 _, ls_out, _ = gsutil.check_call('ls', gs_folder) | 272 _, ls_out, _ = gsutil.check_call('ls', gs_folder) |
| 258 ls_out_sorted = sorted(ls_out.splitlines()) | 273 ls_out_sorted = sorted(ls_out.splitlines(), |
| 274 key=lambda x: x.split('/')[-1]) | |
|
Ryan Tseng
2014/06/13 23:04:53
What was broken?
szager1
2014/06/16 04:46:26
Whoops, nothing was broken. I started thinking ab
Ryan Tseng
2014/06/16 17:28:22
Ah. fwiw the bootstrap zip file uses the git numbe
| |
| 259 if not ls_out_sorted: | 275 if not ls_out_sorted: |
| 260 # This repo is not on Google Storage. | 276 # This repo is not on Google Storage. |
| 261 return False | 277 return False |
| 262 latest_checkout = ls_out_sorted[-1] | 278 latest_checkout = ls_out_sorted[-1] |
| 263 | 279 |
| 264 # Download zip file to a temporary directory. | 280 # Download zip file to a temporary directory. |
| 265 try: | 281 try: |
| 266 tempdir = tempfile.mkdtemp() | 282 tempdir = tempfile.mkdtemp() |
| 267 self.print('Downloading %s' % latest_checkout) | 283 self.print('Downloading %s' % latest_checkout) |
| 268 code = gsutil.call('cp', latest_checkout, tempdir) | 284 code = gsutil.call('cp', latest_checkout, tempdir) |
| (...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 312 v = ['-v', '--progress'] | 328 v = ['-v', '--progress'] |
| 313 | 329 |
| 314 d = [] | 330 d = [] |
| 315 if depth: | 331 if depth: |
| 316 d = ['--depth', str(depth)] | 332 d = ['--depth', str(depth)] |
| 317 | 333 |
| 318 | 334 |
| 319 with Lockfile(self.mirror_path): | 335 with Lockfile(self.mirror_path): |
| 320 # Setup from scratch if the repo is new or is in a bad state. | 336 # Setup from scratch if the repo is new or is in a bad state. |
| 321 tempdir = None | 337 tempdir = None |
| 322 if not os.path.exists(os.path.join(self.mirror_path, 'config')): | 338 config_file = os.path.join(self.mirror_path, 'config') |
| 323 gclient_utils.rmtree(self.mirror_path) | 339 pack_dir = os.path.join(self.mirror_path, 'objects', 'pack') |
| 340 pack_files = [] | |
| 341 if os.path.isdir(pack_dir): | |
| 342 pack_files = [f for f in os.listdir(pack_dir) if f.endswith('.pack')] | |
| 343 | |
| 344 should_bootstrap = (not os.path.exists(config_file) or | |
| 345 len(pack_files) > GC_AUTOPACKLIMIT) | |
| 346 if should_bootstrap: | |
| 324 tempdir = tempfile.mkdtemp( | 347 tempdir = tempfile.mkdtemp( |
| 325 suffix=self.basedir, dir=self.GetCachePath()) | 348 suffix=self.basedir, dir=self.GetCachePath()) |
| 326 bootstrapped = not depth and bootstrap and self.bootstrap_repo(tempdir) | 349 bootstrapped = not depth and bootstrap and self.bootstrap_repo(tempdir) |
| 327 if not bootstrapped: | 350 if bootstrapped: |
| 351 # Bootstrap succeeded; delete previous cache, if any. | |
| 352 gclient_utils.rmtree(self.mirror_path) | |
| 353 elif not os.path.exists(config_file): | |
| 354 # Bootstrap failed, no previous cache; start with a bare git dir. | |
| 328 self.RunGit(['init', '--bare'], cwd=tempdir) | 355 self.RunGit(['init', '--bare'], cwd=tempdir) |
| 356 else: | |
| 357 # Bootstrap failed, previous cache exists; warn and continue. | |
| 358 logging.warn( | |
| 359 'Git cache has a lot of pack files (%d). Tried to re-bootstrap ' | |
| 360 'but failed. Continuing with non-optimized repository.' | |
| 361 % len(pack_files)) | |
| 362 gclient_utils.rmtree(tempdir) | |
| 363 tempdir = None | |
| 329 else: | 364 else: |
| 330 if depth and os.path.exists(os.path.join(self.mirror_path, 'shallow')): | 365 if depth and os.path.exists(os.path.join(self.mirror_path, 'shallow')): |
| 331 logging.warn( | 366 logging.warn( |
| 332 'Shallow fetch requested, but repo cache already exists.') | 367 'Shallow fetch requested, but repo cache already exists.') |
| 333 d = [] | 368 d = [] |
| 334 | 369 |
| 335 rundir = tempdir or self.mirror_path | 370 rundir = tempdir or self.mirror_path |
| 336 self.config(rundir) | 371 self.config(rundir) |
| 337 fetch_cmd = ['fetch'] + v + d + ['origin'] | 372 fetch_cmd = ['fetch'] + v + d + ['origin'] |
| 338 fetch_specs = subprocess.check_output( | 373 fetch_specs = subprocess.check_output( |
| (...skipping 200 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 539 return options, args | 574 return options, args |
| 540 | 575 |
| 541 | 576 |
| 542 def main(argv): | 577 def main(argv): |
| 543 dispatcher = subcommand.CommandDispatcher(__name__) | 578 dispatcher = subcommand.CommandDispatcher(__name__) |
| 544 return dispatcher.execute(OptionParser(), argv) | 579 return dispatcher.execute(OptionParser(), argv) |
| 545 | 580 |
| 546 | 581 |
| 547 if __name__ == '__main__': | 582 if __name__ == '__main__': |
| 548 sys.exit(main(sys.argv[1:])) | 583 sys.exit(main(sys.argv[1:])) |
| OLD | NEW |