| Index: git_cache.py
 | 
| diff --git a/git_cache.py b/git_cache.py
 | 
| index e80923cdbb074a0c27355bd51041341fd473ed27..9f835b5ffc144d8c6753c70585c09cea2250002b 100755
 | 
| --- a/git_cache.py
 | 
| +++ b/git_cache.py
 | 
| @@ -145,9 +145,24 @@ class Mirror(object):
 | 
|      os.path.dirname(os.path.abspath(__file__)), 'gsutil.py')
 | 
|    cachepath_lock = threading.Lock()
 | 
|  
 | 
| +  @staticmethod
 | 
| +  def parse_fetch_spec(spec):
 | 
| +    """Parses and canonicalizes a fetch spec.
 | 
| +
 | 
| +    Returns (fetchspec, value_regex), where value_regex can be used
 | 
| +    with 'git config --replace-all'.
 | 
| +    """
 | 
| +    parts = spec.split(':', 1)
 | 
| +    src = parts[0].lstrip('+').rstrip('/')
 | 
| +    if not src.startswith('refs/'):
 | 
| +      src = 'refs/heads/%s' % src
 | 
| +    dest = parts[1].rstrip('/') if len(parts) > 1 else src
 | 
| +    regex = r'\+%s:.*' % src.replace('*', r'\*')
 | 
| +    return ('+%s:%s' % (src, dest), regex)
 | 
| +
 | 
|    def __init__(self, url, refs=None, print_func=None):
 | 
|      self.url = url
 | 
| -    self.refs = refs or []
 | 
| +    self.fetch_specs = set([self.parse_fetch_spec(ref) for ref in (refs or [])])
 | 
|      self.basedir = self.UrlToCacheDir(url)
 | 
|      self.mirror_path = os.path.join(self.GetCachePath(), self.basedir)
 | 
|      if print_func:
 | 
| @@ -236,16 +251,9 @@ class Mirror(object):
 | 
|      self.RunGit(['config', 'remote.origin.url', self.url], cwd=cwd)
 | 
|      self.RunGit(['config', '--replace-all', 'remote.origin.fetch',
 | 
|                   '+refs/heads/*:refs/heads/*', r'\+refs/heads/\*:.*'], cwd=cwd)
 | 
| -    for ref in self.refs:
 | 
| -      ref = ref.lstrip('+').rstrip('/')
 | 
| -      if ref.startswith('refs/'):
 | 
| -        refspec = '+%s:%s' % (ref, ref)
 | 
| -        regex = r'\+%s:.*' % ref.replace('*', r'\*')
 | 
| -      else:
 | 
| -        refspec = '+refs/%s/*:refs/%s/*' % (ref, ref)
 | 
| -        regex = r'\+refs/heads/%s:.*' % ref.replace('*', r'\*')
 | 
| +    for spec, value_regex in self.fetch_specs:
 | 
|        self.RunGit(
 | 
| -          ['config', '--replace-all', 'remote.origin.fetch', refspec, regex],
 | 
| +          ['config', '--replace-all', 'remote.origin.fetch', spec, value_regex],
 | 
|            cwd=cwd)
 | 
|  
 | 
|    def bootstrap_repo(self, directory):
 | 
| @@ -314,9 +322,27 @@ class Mirror(object):
 | 
|    def exists(self):
 | 
|      return os.path.isfile(os.path.join(self.mirror_path, 'config'))
 | 
|  
 | 
| +  def _preserve_fetchspec(self):
 | 
| +    """Read and preserve remote.origin.fetch from an existing mirror.
 | 
| +
 | 
| +    This modifies self.fetch_specs.
 | 
| +    """
 | 
| +    if not self.exists():
 | 
| +      return
 | 
| +    try:
 | 
| +      config_fetchspecs = subprocess.check_output(
 | 
| +          [self.git_exe, 'config', '--get-all', 'remote.origin.fetch'],
 | 
| +          cwd=self.mirror_path)
 | 
| +      for fetchspec in config_fetchspecs.splitlines():
 | 
| +        self.fetch_specs.add(self.parse_fetch_spec(fetchspec))
 | 
| +    except subprocess.CalledProcessError:
 | 
| +      logging.warn('Tried and failed to preserve remote.origin.fetch from the '
 | 
| +                   'existing cache directory.  You may need to manually edit '
 | 
| +                   '%s and "git cache fetch" again.'
 | 
| +                   % os.path.join(self.mirror_path, 'config'))
 | 
| +
 | 
|    def _ensure_bootstrapped(self, depth, bootstrap, force=False):
 | 
|      tempdir = None
 | 
| -    config_file = os.path.join(self.mirror_path, 'config')
 | 
|      pack_dir = os.path.join(self.mirror_path, 'objects', 'pack')
 | 
|      pack_files = []
 | 
|  
 | 
| @@ -324,16 +350,19 @@ class Mirror(object):
 | 
|        pack_files = [f for f in os.listdir(pack_dir) if f.endswith('.pack')]
 | 
|  
 | 
|      should_bootstrap = (force or
 | 
| -                        not os.path.exists(config_file) or
 | 
| +                        not self.exists() or
 | 
|                          len(pack_files) > GC_AUTOPACKLIMIT)
 | 
|      if should_bootstrap:
 | 
| +      if self.exists():
 | 
| +        # Re-bootstrapping an existing mirror; preserve existing fetch spec.
 | 
| +        self._preserve_fetchspec()
 | 
|        tempdir = tempfile.mkdtemp(
 | 
|            prefix='_cache_tmp', suffix=self.basedir, dir=self.GetCachePath())
 | 
|        bootstrapped = not depth and bootstrap and self.bootstrap_repo(tempdir)
 | 
|        if bootstrapped:
 | 
|          # Bootstrap succeeded; delete previous cache, if any.
 | 
|          gclient_utils.rmtree(self.mirror_path)
 | 
| -      elif not os.path.exists(config_file):
 | 
| +      elif not self.exists():
 | 
|          # Bootstrap failed, no previous cache; start with a bare git dir.
 | 
|          self.RunGit(['init', '--bare'], cwd=tempdir)
 | 
|        else:
 | 
| @@ -563,6 +592,9 @@ def CMDpopulate(parser, args):
 | 
|  def CMDfetch(parser, args):
 | 
|    """Update mirror, and fetch in cwd."""
 | 
|    parser.add_option('--all', action='store_true', help='Fetch all remotes')
 | 
| +  parser.add_option('--no_bootstrap', '--no-bootstrap',
 | 
| +                    action='store_true',
 | 
| +                    help='Don\'t (re)bootstrap from Google Storage')
 | 
|    options, args = parser.parse_args(args)
 | 
|  
 | 
|    # Figure out which remotes to fetch.  This mimics the behavior of regular
 | 
| @@ -593,7 +625,7 @@ def CMDfetch(parser, args):
 | 
|    git_dir = os.path.abspath(git_dir)
 | 
|    if git_dir.startswith(cachepath):
 | 
|      mirror = Mirror.FromPath(git_dir)
 | 
| -    mirror.populate()
 | 
| +    mirror.populate(bootstrap=not options.no_bootstrap)
 | 
|      return 0
 | 
|    for remote in remotes:
 | 
|      remote_url = subprocess.check_output(
 | 
| @@ -602,7 +634,7 @@ def CMDfetch(parser, args):
 | 
|        mirror = Mirror.FromPath(remote_url)
 | 
|        mirror.print = lambda *args: None
 | 
|        print('Updating git cache...')
 | 
| -      mirror.populate()
 | 
| +      mirror.populate(bootstrap=not options.no_bootstrap)
 | 
|      subprocess.check_call([Mirror.git_exe, 'fetch', remote])
 | 
|    return 0
 | 
|  
 | 
| 
 |