From 976a14dc9705dbcec1402c89290ae5a4917d12b8 Mon Sep 17 00:00:00 2001 From: "iannucci@chromium.org" Date: Wed, 17 Jul 2013 21:56:49 +0000 Subject: [PATCH] When switching to/from a cache git dir, try to reuse as much data as possible. R=szager@chromium.org BUG= Review URL: https://chromiumcodereview.appspot.com/18768004 git-svn-id: svn://svn.chromium.org/chrome/trunk/tools/depot_tools@212140 0039d316-1c4b-4281-b951-d872f2087c98 --- gclient_scm.py | 75 ++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 73 insertions(+), 2 deletions(-) diff --git a/gclient_scm.py b/gclient_scm.py index 5a5dc075b3..fd7e3eed1c 100644 --- a/gclient_scm.py +++ b/gclient_scm.py @@ -382,6 +382,7 @@ class GitWrapper(SCMWrapper): # See if the url has changed (the unittests use git://foo for the url, let # that through). current_url = self._Capture(['config', 'remote.origin.url']) + return_early = False # TODO(maruel): Delete url != 'git://foo' since it's just to make the # unit test pass. (and update the comment above) # Skip url auto-correction if remote.origin.gclient-auto-fix-url is set. @@ -398,6 +399,13 @@ class GitWrapper(SCMWrapper): # Switch over to the new upstream self._Run(['remote', 'set-url', 'origin', url], options) self._FetchAndReset(revision, file_list, options) + return_early = True + + # Need to do this in the normal path as well as in the post-remote-switch + # path. + self._PossiblySwitchCache(url, options) + + if return_early: return if not self._IsValidGitRepo(): @@ -735,6 +743,44 @@ class GitWrapper(SCMWrapper): url += '.git' return url + def _PossiblySwitchCache(self, url, options): + """Handles switching a repo from with-cache to direct, or vice versa. + + When we go from direct to with-cache, the remote url changes from the + 'real' url to the local file url (in cache_dir). Therefore, this function + assumes that |url| points to the correctly-switched-over local file url, if + we're in cache_mode. + + When we go from with-cache to direct, assume that the normal url-switching + code already flipped the remote over, and we just need to repack and break + the dependency to the cache. + """ + + altfile = os.path.join( + self.checkout_path, '.git', 'objects', 'info', 'alternates') + if self.cache_dir: + if not os.path.exists(altfile): + try: + with open(altfile, 'wa') as f: + f.write(os.path.join(url, 'objects')) + # pylint: disable=C0301 + # This dance is necessary according to emperical evidence, also at: + # http://lists-archives.com/git/713652-retrospectively-add-alternates-to-a-repository.html + self._Run(['repack', '-ad'], options) + self._Run(['repack', '-adl'], options) + except Exception: + # If something goes wrong, try to remove the altfile so we'll go down + # this path again next time. + try: + os.remove(altfile) + except Exception: + pass + raise + else: + if os.path.exists(altfile): + self._Run(['repack', '-a'], options) + os.remove(altfile) + def _CreateOrUpdateCache(self, url, options): """Make a new git mirror or update existing mirror for |url|, and return the mirror URI to clone from. @@ -748,6 +794,16 @@ class GitWrapper(SCMWrapper): folder = os.path.join( self.cache_dir, self._NormalizeGitURL(url).replace('-', '--').replace('/', '-')) + altfile = os.path.join(folder, 'objects', 'info', 'alternates') + + # If we're bringing an old cache up to date or cloning a new cache, and the + # existing repo is currently a direct clone, use its objects to help out + # the fetch here. + checkout_objects = os.path.join(self.checkout_path, '.git', 'objects') + checkout_altfile = os.path.join(checkout_objects, 'info', 'alternates') + use_reference = ( + os.path.exists(checkout_objects) and + not os.path.exists(checkout_altfile)) v = ['-v'] if options.verbose else [] filter_fn = lambda l: '[up to date]' not in l @@ -755,8 +811,13 @@ class GitWrapper(SCMWrapper): gclient_utils.safe_makedirs(self.cache_dir) if not os.path.exists(os.path.join(folder, 'config')): gclient_utils.rmtree(folder) - self._Run(['clone'] + v + ['-c', 'core.deltaBaseCacheLimit=2g', - '--progress', '--mirror', url, folder], + cmd = ['clone'] + v + ['-c', 'core.deltaBaseCacheLimit=2g', + '--progress', '--mirror'] + + if use_reference: + cmd += ['--reference', os.path.abspath(self.checkout_path)] + + self._Run(cmd + [url, folder], options, git_filter=True, filter_fn=filter_fn, cwd=self.cache_dir) else: @@ -768,10 +829,20 @@ class GitWrapper(SCMWrapper): cwd=folder) assert self._NormalizeGitURL(existing_url) == self._NormalizeGitURL(url) + if use_reference: + with open(altfile, 'w') as f: + f.write(os.path.abspath(checkout_objects)) + # Would normally use `git remote update`, but it doesn't support # --progress, so use fetch instead. self._Run(['fetch'] + v + ['--multiple', '--progress', '--all'], options, git_filter=True, filter_fn=filter_fn, cwd=folder) + + # If the clone has an object dependency on the existing repo, break it + # with repack and remove the linkage. + if os.path.exists(altfile): + self._Run(['repack', '-a'], options, cwd=folder) + os.remove(altfile) return folder def _Clone(self, revision, url, options):