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 |
| (...skipping 110 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 121 | 121 |
| 122 Note: This method is potentially racy. By the time it returns the lockfile | 122 Note: This method is potentially racy. By the time it returns the lockfile |
| 123 may have been unlocked, removed, or stolen by some other process. | 123 may have been unlocked, removed, or stolen by some other process. |
| 124 """ | 124 """ |
| 125 return os.path.exists(self.lockfile) | 125 return os.path.exists(self.lockfile) |
| 126 | 126 |
| 127 def i_am_locking(self): | 127 def i_am_locking(self): |
| 128 """Test if the file is locked by this process.""" | 128 """Test if the file is locked by this process.""" |
| 129 return self.is_locked() and self.pid == self._read_pid() | 129 return self.is_locked() and self.pid == self._read_pid() |
| 130 | 130 |
| 131 def __enter__(self): | 131 def __enter__(self): |
|
Ryan Tseng
2014/06/18 23:54:39
No longer needed?
szager1
2014/06/19 20:35:33
Done.
| |
| 132 self.lock() | 132 self.lock() |
| 133 return self | 133 return self |
| 134 | 134 |
| 135 def __exit__(self, *_exc): | 135 def __exit__(self, *_exc): |
|
Ryan Tseng
2014/06/18 23:54:39
No longer needed?
szager1
2014/06/19 20:35:33
Done.
| |
| 136 # Windows is unreliable when it comes to file locking. YMMV. | 136 # Windows is unreliable when it comes to file locking. YMMV. |
| 137 try: | 137 try: |
| 138 self.unlock() | 138 self.unlock() |
| 139 except WinErr: | 139 except WinErr: |
| 140 pass | 140 pass |
| 141 | 141 |
| 142 | 142 |
| 143 class Mirror(object): | 143 class Mirror(object): |
| 144 | 144 |
| 145 git_exe = 'git.bat' if sys.platform.startswith('win') else 'git' | 145 git_exe = 'git.bat' if sys.platform.startswith('win') else 'git' |
| (...skipping 164 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 310 self.print( | 310 self.print( |
| 311 'Extracting bootstrap zipfile %s failed.\n' | 311 'Extracting bootstrap zipfile %s failed.\n' |
| 312 'Resuming normal operations.' % filename) | 312 'Resuming normal operations.' % filename) |
| 313 return False | 313 return False |
| 314 return True | 314 return True |
| 315 | 315 |
| 316 def exists(self): | 316 def exists(self): |
| 317 return os.path.isfile(os.path.join(self.mirror_path, 'config')) | 317 return os.path.isfile(os.path.join(self.mirror_path, 'config')) |
| 318 | 318 |
| 319 def populate(self, depth=None, shallow=False, bootstrap=False, | 319 def populate(self, depth=None, shallow=False, bootstrap=False, |
| 320 verbose=False): | 320 verbose=False, ignore_lock=False): |
| 321 assert self.GetCachePath() | 321 assert self.GetCachePath() |
| 322 if shallow and not depth: | 322 if shallow and not depth: |
| 323 depth = 10000 | 323 depth = 10000 |
| 324 gclient_utils.safe_makedirs(self.GetCachePath()) | 324 gclient_utils.safe_makedirs(self.GetCachePath()) |
| 325 | 325 |
| 326 v = [] | 326 v = [] |
| 327 if verbose: | 327 if verbose: |
| 328 v = ['-v', '--progress'] | 328 v = ['-v', '--progress'] |
| 329 | 329 |
| 330 d = [] | 330 d = [] |
| 331 if depth: | 331 if depth: |
| 332 d = ['--depth', str(depth)] | 332 d = ['--depth', str(depth)] |
| 333 | 333 |
| 334 | 334 |
| 335 with Lockfile(self.mirror_path): | 335 lockfile = Lockfile(self.mirror_path) |
| 336 if not ignore_lock: | |
| 337 lockfile.lock() | |
| 338 | |
| 339 try: | |
| 336 # Setup from scratch if the repo is new or is in a bad state. | 340 # Setup from scratch if the repo is new or is in a bad state. |
| 337 tempdir = None | 341 tempdir = None |
| 338 config_file = os.path.join(self.mirror_path, 'config') | 342 config_file = os.path.join(self.mirror_path, 'config') |
| 339 pack_dir = os.path.join(self.mirror_path, 'objects', 'pack') | 343 pack_dir = os.path.join(self.mirror_path, 'objects', 'pack') |
| 340 pack_files = [] | 344 pack_files = [] |
| 341 if os.path.isdir(pack_dir): | 345 if os.path.isdir(pack_dir): |
| 342 pack_files = [f for f in os.listdir(pack_dir) if f.endswith('.pack')] | 346 pack_files = [f for f in os.listdir(pack_dir) if f.endswith('.pack')] |
| 343 | 347 |
| 344 should_bootstrap = (not os.path.exists(config_file) or | 348 should_bootstrap = (not os.path.exists(config_file) or |
| 345 len(pack_files) > GC_AUTOPACKLIMIT) | 349 len(pack_files) > GC_AUTOPACKLIMIT) |
| (...skipping 27 matching lines...) Expand all Loading... | |
| 373 fetch_specs = subprocess.check_output( | 377 fetch_specs = subprocess.check_output( |
| 374 [self.git_exe, 'config', '--get-all', 'remote.origin.fetch'], | 378 [self.git_exe, 'config', '--get-all', 'remote.origin.fetch'], |
| 375 cwd=rundir).strip().splitlines() | 379 cwd=rundir).strip().splitlines() |
| 376 for spec in fetch_specs: | 380 for spec in fetch_specs: |
| 377 try: | 381 try: |
| 378 self.RunGit(fetch_cmd + [spec], cwd=rundir, retry=True) | 382 self.RunGit(fetch_cmd + [spec], cwd=rundir, retry=True) |
| 379 except subprocess.CalledProcessError: | 383 except subprocess.CalledProcessError: |
| 380 logging.warn('Fetch of %s failed' % spec) | 384 logging.warn('Fetch of %s failed' % spec) |
| 381 if tempdir: | 385 if tempdir: |
| 382 os.rename(tempdir, self.mirror_path) | 386 os.rename(tempdir, self.mirror_path) |
| 387 finally: | |
| 388 if not ignore_lock: | |
| 389 lockfile.unlock() | |
| 383 | 390 |
| 384 def update_bootstrap(self): | 391 def update_bootstrap(self): |
| 385 # The files are named <git number>.zip | 392 # The files are named <git number>.zip |
| 386 gen_number = subprocess.check_output( | 393 gen_number = subprocess.check_output( |
| 387 [self.git_exe, 'number', 'master'], cwd=self.mirror_path).strip() | 394 [self.git_exe, 'number', 'master'], cwd=self.mirror_path).strip() |
| 388 self.RunGit(['gc']) # Run Garbage Collect to compress packfile. | 395 self.RunGit(['gc']) # Run Garbage Collect to compress packfile. |
| 389 # Creating a temp file and then deleting it ensures we can use this name. | 396 # Creating a temp file and then deleting it ensures we can use this name. |
| 390 _, tmp_zipfile = tempfile.mkstemp(suffix='.zip') | 397 _, tmp_zipfile = tempfile.mkstemp(suffix='.zip') |
| 391 os.remove(tmp_zipfile) | 398 os.remove(tmp_zipfile) |
| 392 subprocess.call(['zip', '-r', tmp_zipfile, '.'], cwd=self.mirror_path) | 399 subprocess.call(['zip', '-r', tmp_zipfile, '.'], cwd=self.mirror_path) |
| (...skipping 97 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 490 def CMDpopulate(parser, args): | 497 def CMDpopulate(parser, args): |
| 491 """Ensure that the cache has all up-to-date objects for the given repo.""" | 498 """Ensure that the cache has all up-to-date objects for the given repo.""" |
| 492 parser.add_option('--depth', type='int', | 499 parser.add_option('--depth', type='int', |
| 493 help='Only cache DEPTH commits of history') | 500 help='Only cache DEPTH commits of history') |
| 494 parser.add_option('--shallow', '-s', action='store_true', | 501 parser.add_option('--shallow', '-s', action='store_true', |
| 495 help='Only cache 10000 commits of history') | 502 help='Only cache 10000 commits of history') |
| 496 parser.add_option('--ref', action='append', | 503 parser.add_option('--ref', action='append', |
| 497 help='Specify additional refs to be fetched') | 504 help='Specify additional refs to be fetched') |
| 498 parser.add_option('--no_bootstrap', action='store_true', | 505 parser.add_option('--no_bootstrap', action='store_true', |
| 499 help='Don\'t bootstrap from Google Storage') | 506 help='Don\'t bootstrap from Google Storage') |
| 507 parser.add_option('--ignore-locks', action='store_true', | |
|
Ryan Tseng
2014/06/18 23:54:39
--ignore_locks (matches no_bootstrap)
szager1
2014/06/19 20:35:33
Done.
| |
| 508 help='Don\'t try to lock repository') | |
| 500 | 509 |
| 501 options, args = parser.parse_args(args) | 510 options, args = parser.parse_args(args) |
| 502 if not len(args) == 1: | 511 if not len(args) == 1: |
| 503 parser.error('git cache populate only takes exactly one repo url.') | 512 parser.error('git cache populate only takes exactly one repo url.') |
| 504 url = args[0] | 513 url = args[0] |
| 505 | 514 |
| 506 mirror = Mirror(url, refs=options.ref) | 515 mirror = Mirror(url, refs=options.ref) |
| 507 kwargs = { | 516 kwargs = { |
| 508 'verbose': options.verbose, | 517 'verbose': options.verbose, |
| 509 'shallow': options.shallow, | 518 'shallow': options.shallow, |
| 510 'bootstrap': not options.no_bootstrap, | 519 'bootstrap': not options.no_bootstrap, |
| 520 'ignore_lock': options.ignore_locks, | |
| 511 } | 521 } |
| 512 if options.depth: | 522 if options.depth: |
| 513 kwargs['depth'] = options.depth | 523 kwargs['depth'] = options.depth |
| 514 mirror.populate(**kwargs) | 524 mirror.populate(**kwargs) |
| 515 | 525 |
| 516 | 526 |
| 517 @subcommand.usage('[url of repo to unlock, or -a|--all]') | 527 @subcommand.usage('[url of repo to unlock, or -a|--all]') |
| 518 def CMDunlock(parser, args): | 528 def CMDunlock(parser, args): |
| 519 """Unlock one or all repos if their lock files are still around.""" | 529 """Unlock one or all repos if their lock files are still around.""" |
| 520 parser.add_option('--force', '-f', action='store_true', | 530 parser.add_option('--force', '-f', action='store_true', |
| (...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 581 return options, args | 591 return options, args |
| 582 | 592 |
| 583 | 593 |
| 584 def main(argv): | 594 def main(argv): |
| 585 dispatcher = subcommand.CommandDispatcher(__name__) | 595 dispatcher = subcommand.CommandDispatcher(__name__) |
| 586 return dispatcher.execute(OptionParser(), argv) | 596 return dispatcher.execute(OptionParser(), argv) |
| 587 | 597 |
| 588 | 598 |
| 589 if __name__ == '__main__': | 599 if __name__ == '__main__': |
| 590 sys.exit(main(sys.argv[1:])) | 600 sys.exit(main(sys.argv[1:])) |
| OLD | NEW |