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) |