Chromium Code Reviews| Index: gclient_scm.py |
| diff --git a/gclient_scm.py b/gclient_scm.py |
| index 491345e856920023b7168a89ba8c513727d981e2..fa26ecd14c980a5b7934010ceb4b46ca550fa662 100644 |
| --- a/gclient_scm.py |
| +++ b/gclient_scm.py |
| @@ -276,16 +276,98 @@ class GitWrapper(SCMWrapper): |
| cwd=self.checkout_path, |
| filter_fn=GitDiffFilterer(self.relpath).Filter, print_func=self.Print) |
| + def _ParseRevision(self, revision): |
| + """Analyzes a revision string and determines what the repository-local |
| + refspec is for that revision. |
| + |
| + This function handles: |
| + - Standard local revision paths (refs/heads, refs/remotes) |
| + (e.g., refs/heads/master) |
| + - Remote-relative mirrored revision paths (e.g., origin/master) |
| + - Non-remote revision paths (anything else begining with 'refs/') are |
| + assumed to be refs on our remote. |
| + - Everything else (branches, commits) |
| + |
| + Params: |
| + revision: The revision string, fitting into one of the categories above, |
| + to resolve |
| + |
| + Return (dict): Refspec analysis dictionary including: |
| + - 'revision' (str): The refspec to use to reference the revision in the |
| + local repository (after it's been fetched). |
| + - 'type' (str): The type of revision, one of ('branch', 'hash') |
| + - 'mapped' (bool): If 'True', the revision is part of the default mapped |
| + revision set ('refs/heads/*'); if 'False', it needs to be |
| + explicitly fetched before it can be referenced at 'revision'. |
| + - 'fetch_refspec' (str): The parameter to use to fetch this revision from |
| + its origin. |
| + """ |
| + result = { |
| + 'revision': revision, |
| + 'type': 'branch', |
| + 'mapped': True, |
| + 'fetch_refspec': revision, |
| + } |
| + # |
| + # If 'revision' begins with some prefixes, chop them off to reference |
| + # local refs. |
| + for prefix, replacement in ( |
| + ('refs/heads/', None), |
| + ('refs/remotes/%s/' % (self.remote), None), |
| + ('%s/' % (self.remote), 'refs/heads/'), |
| + ): |
| + if revision.startswith(prefix): |
| + local_revision = revision[len(prefix):] |
| + if replacement is not None: |
| + revision = revision.replace(prefix, replacement) |
| + |
| + # Trim 'prefix' to make 'revision' relative to our standard |
| + # 'refs/heads/*' fetch mapping. |
| + result['revision'] = revision |
| + result['fetch_refspec'] = local_revision |
| + return result |
| + |
| + # If 'revision' begins with 'refs/', treat it as an unmapped remote ref |
| + if revision.startswith('refs/'): |
| + result['fetch_refspec'] = '%s:%s' % (revision, revision) |
| + result['mapped'] = False |
| + return result |
| + |
| + # Assume 'revision' is either a local branch or a commit |
| + result['type'] = 'hash' |
| + return result |
| + |
| + def _Fetch(self, options, revision=None): |
| + cfg = gclient_utils.DefaultIndexPackConfig() |
| + fetch_cmd = cfg + ['fetch', self.remote] |
| + |
| + if revision is not None: |
| + refdict = self._ParseRevision(revision) |
| + fetch_cmd.append(refdict['fetch_refspec']) |
| + |
| + if not options.verbose: |
| + fetch_cmd.append('--quiet') |
| + fetch_cmd.append('--prune') |
| + self._Run(fetch_cmd, options, retry=True) |
| + |
| + def _FetchIfRemote(self, options, revision): |
| + refdict = self._ParseRevision(revision) |
| + if not refdict['mapped']: |
| + self._Fetch(options, revision) |
| + |
| def _FetchAndReset(self, revision, file_list, options): |
| """Equivalent to git fetch; git reset.""" |
| - quiet = [] |
| - if not options.verbose: |
| - quiet = ['--quiet'] |
| self._UpdateBranchHeads(options, fetch=False) |
| - cfg = gclient_utils.DefaultIndexPackConfig(self.url) |
| - fetch_cmd = cfg + ['fetch', self.remote, '--prune'] |
| - self._Run(fetch_cmd + quiet, options, retry=True) |
| + # Fetch default fetch targets ('master'...) |
| + self._Fetch(options) |
| + # Fetch our specific revision from the remote, if it's not in the default |
| + # fetch set. |
| + self._FetchIfRemote(options, revision) |
| + |
| + quiet = [] |
| + if not options.verbose: |
| + quiet.append('--quiet') |
| self._Run(['reset', '--hard', revision] + quiet, options) |
| if file_list is not None: |
| files = self._Capture(['ls-files']).splitlines() |
| @@ -339,15 +421,9 @@ class GitWrapper(SCMWrapper): |
| url = self._CreateOrUpdateCache(url, options) |
| - if revision.startswith('refs/'): |
| - rev_type = "branch" |
| - elif revision.startswith(self.remote + '/'): |
| - # For compatibility with old naming, translate 'origin' to 'refs/heads' |
| - revision = revision.replace(self.remote + '/', 'refs/heads/') |
| - rev_type = "branch" |
| - else: |
| - # hash is also a tag, only make a distinction at checkout |
| - rev_type = "hash" |
| + refdict = self._ParseRevision(revision) |
| + revision = refdict['revision'] |
| + rev_type = refdict['type'] |
| if (not os.path.exists(self.checkout_path) or |
| (os.path.isdir(self.checkout_path) and |
| @@ -786,9 +862,13 @@ class GitWrapper(SCMWrapper): |
| traceback.print_exc(file=self.out_fh) |
| raise |
| finally: |
| - if os.listdir(tmp_dir): |
| + if os.path.exists(tmp_dir) and os.listdir(tmp_dir): |
|
dnj (Google)
2014/04/24 19:30:44
A bit unrelated, but I did hit this case when 'lis
|
| self.Print('_____ removing non-empty tmp dir %s' % tmp_dir) |
| gclient_utils.rmtree(tmp_dir) |
| + |
| + # Fetch our specific revision |
| + self._FetchIfRemote(options, revision) |
| + |
| if revision.startswith('refs/heads/'): |
| self._Run( |
| ['checkout', '--quiet', revision.replace('refs/heads/', '')], options) |
| @@ -973,11 +1053,7 @@ class GitWrapper(SCMWrapper): |
| '^\\+refs/branch-heads/\\*:.*$'] |
| self._Run(config_cmd, options) |
| if fetch: |
| - cfg = gclient_utils.DefaultIndexPackConfig(self.url) |
| - fetch_cmd = cfg + ['fetch', self.remote] |
| - if options.verbose: |
| - fetch_cmd.append('--verbose') |
| - self._Run(fetch_cmd, options, retry=True) |
| + self._Fetch(options) |
| def _Run(self, args, options, **kwargs): |
| cwd = kwargs.setdefault('cwd', self.checkout_path) |