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 20 matching lines...) Expand all Loading... |
31 try: | 31 try: |
32 # pylint: disable=E0602 | 32 # pylint: disable=E0602 |
33 WinErr = WindowsError | 33 WinErr = WindowsError |
34 except NameError: | 34 except NameError: |
35 class WinErr(Exception): | 35 class WinErr(Exception): |
36 pass | 36 pass |
37 | 37 |
38 class LockError(Exception): | 38 class LockError(Exception): |
39 pass | 39 pass |
40 | 40 |
41 class ClobberNeeded(Exception): | 41 class RefsHeadsFailedToFetch(Exception): |
42 pass | 42 pass |
43 | 43 |
44 class Lockfile(object): | 44 class Lockfile(object): |
45 """Class to represent a cross-platform process-specific lockfile.""" | 45 """Class to represent a cross-platform process-specific lockfile.""" |
46 | 46 |
47 def __init__(self, path, timeout=0): | 47 def __init__(self, path, timeout=0): |
48 self.path = os.path.abspath(path) | 48 self.path = os.path.abspath(path) |
49 self.timeout = timeout | 49 self.timeout = timeout |
50 self.lockfile = self.path + ".lock" | 50 self.lockfile = self.path + ".lock" |
51 self.pid = os.getpid() | 51 self.pid = os.getpid() |
(...skipping 187 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
239 env.setdefault('GIT_ASKPASS', 'true') | 239 env.setdefault('GIT_ASKPASS', 'true') |
240 env.setdefault('SSH_ASKPASS', 'true') | 240 env.setdefault('SSH_ASKPASS', 'true') |
241 self.print('running "git %s" in "%s"' % (' '.join(cmd), cwd)) | 241 self.print('running "git %s" in "%s"' % (' '.join(cmd), cwd)) |
242 gclient_utils.CheckCallAndFilter([self.git_exe] + cmd, **kwargs) | 242 gclient_utils.CheckCallAndFilter([self.git_exe] + cmd, **kwargs) |
243 | 243 |
244 def config(self, cwd=None): | 244 def config(self, cwd=None): |
245 if cwd is None: | 245 if cwd is None: |
246 cwd = self.mirror_path | 246 cwd = self.mirror_path |
247 | 247 |
248 # Don't run git-gc in a daemon. Bad things can happen if it gets killed. | 248 # Don't run git-gc in a daemon. Bad things can happen if it gets killed. |
249 try: | 249 self.RunGit(['config', 'gc.autodetach', '0'], cwd=cwd) |
250 self.RunGit(['config', 'gc.autodetach', '0'], cwd=cwd) | |
251 except subprocess.CalledProcessError: | |
252 # Hard error, need to clobber. | |
253 raise ClobberNeeded() | |
254 | 250 |
255 # Don't combine pack files into one big pack file. It's really slow for | 251 # Don't combine pack files into one big pack file. It's really slow for |
256 # repositories, and there's no way to track progress and make sure it's | 252 # repositories, and there's no way to track progress and make sure it's |
257 # not stuck. | 253 # not stuck. |
258 self.RunGit(['config', 'gc.autopacklimit', '0'], cwd=cwd) | 254 self.RunGit(['config', 'gc.autopacklimit', '0'], cwd=cwd) |
259 | 255 |
260 # Allocate more RAM for cache-ing delta chains, for better performance | 256 # Allocate more RAM for cache-ing delta chains, for better performance |
261 # of "Resolving deltas". | 257 # of "Resolving deltas". |
262 self.RunGit(['config', 'core.deltaBaseCacheLimit', | 258 self.RunGit(['config', 'core.deltaBaseCacheLimit', |
263 gclient_utils.DefaultDeltaBaseCacheLimit()], cwd=cwd) | 259 gclient_utils.DefaultDeltaBaseCacheLimit()], cwd=cwd) |
(...skipping 140 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
404 fetch_cmd = ['fetch'] + v + d + ['origin'] | 400 fetch_cmd = ['fetch'] + v + d + ['origin'] |
405 fetch_specs = subprocess.check_output( | 401 fetch_specs = subprocess.check_output( |
406 [self.git_exe, 'config', '--get-all', 'remote.origin.fetch'], | 402 [self.git_exe, 'config', '--get-all', 'remote.origin.fetch'], |
407 cwd=rundir).strip().splitlines() | 403 cwd=rundir).strip().splitlines() |
408 for spec in fetch_specs: | 404 for spec in fetch_specs: |
409 try: | 405 try: |
410 self.print('Fetching %s' % spec) | 406 self.print('Fetching %s' % spec) |
411 self.RunGit(fetch_cmd + [spec], cwd=rundir, retry=True) | 407 self.RunGit(fetch_cmd + [spec], cwd=rundir, retry=True) |
412 except subprocess.CalledProcessError: | 408 except subprocess.CalledProcessError: |
413 if spec == '+refs/heads/*:refs/heads/*': | 409 if spec == '+refs/heads/*:refs/heads/*': |
414 raise ClobberNeeded() # Corrupted cache. | 410 raise RefsHeadsFailedToFetch |
415 logging.warn('Fetch of %s failed' % spec) | 411 logging.warn('Fetch of %s failed' % spec) |
416 | 412 |
417 def populate(self, depth=None, shallow=False, bootstrap=False, | 413 def populate(self, depth=None, shallow=False, bootstrap=False, |
418 verbose=False, ignore_lock=False, lock_timeout=0): | 414 verbose=False, ignore_lock=False, lock_timeout=0): |
419 assert self.GetCachePath() | 415 assert self.GetCachePath() |
420 if shallow and not depth: | 416 if shallow and not depth: |
421 depth = 10000 | 417 depth = 10000 |
422 gclient_utils.safe_makedirs(self.GetCachePath()) | 418 gclient_utils.safe_makedirs(self.GetCachePath()) |
423 | 419 |
424 lockfile = Lockfile(self.mirror_path, lock_timeout) | 420 lockfile = Lockfile(self.mirror_path, lock_timeout) |
425 if not ignore_lock: | 421 if not ignore_lock: |
426 lockfile.lock() | 422 lockfile.lock() |
427 | 423 |
428 tempdir = None | 424 tempdir = None |
429 try: | 425 try: |
430 tempdir = self._ensure_bootstrapped(depth, bootstrap) | 426 tempdir = self._ensure_bootstrapped(depth, bootstrap) |
431 rundir = tempdir or self.mirror_path | 427 rundir = tempdir or self.mirror_path |
432 self._fetch(rundir, verbose, depth) | 428 self._fetch(rundir, verbose, depth) |
433 except ClobberNeeded: | 429 except RefsHeadsFailedToFetch: |
434 # This is a major failure, we need to clean and force a bootstrap. | 430 # This is a major failure, we need to clean and force a bootstrap. |
435 gclient_utils.rmtree(rundir) | 431 gclient_utils.rmtree(rundir) |
436 self.print(GIT_CACHE_CORRUPT_MESSAGE) | 432 self.print(GIT_CACHE_CORRUPT_MESSAGE) |
437 tempdir = self._ensure_bootstrapped(depth, bootstrap, force=True) | 433 tempdir = self._ensure_bootstrapped(depth, bootstrap, force=True) |
438 assert tempdir | 434 assert tempdir |
439 self._fetch(tempdir or self.mirror_path, verbose, depth) | 435 self._fetch(tempdir or self.mirror_path, verbose, depth) |
440 finally: | 436 finally: |
441 if tempdir: | 437 if tempdir: |
442 try: | 438 try: |
443 if os.path.exists(self.mirror_path): | 439 if os.path.exists(self.mirror_path): |
(...skipping 285 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
729 dispatcher = subcommand.CommandDispatcher(__name__) | 725 dispatcher = subcommand.CommandDispatcher(__name__) |
730 return dispatcher.execute(OptionParser(), argv) | 726 return dispatcher.execute(OptionParser(), argv) |
731 | 727 |
732 | 728 |
733 if __name__ == '__main__': | 729 if __name__ == '__main__': |
734 try: | 730 try: |
735 sys.exit(main(sys.argv[1:])) | 731 sys.exit(main(sys.argv[1:])) |
736 except KeyboardInterrupt: | 732 except KeyboardInterrupt: |
737 sys.stderr.write('interrupted\n') | 733 sys.stderr.write('interrupted\n') |
738 sys.exit(1) | 734 sys.exit(1) |
OLD | NEW |