sync: Try fetching a tag as a last resort before giving up

If a tagged commit is not reachable by the fetch refspec configured
for the git (usually refs/heads/*) it will not be downloaded by
'git fetch'.  The tag can however be downloaded with 'git fetch
--tags' or 'git fetch tag <tag>'.

This patch fixes the situation when a tag is not found after a
'git fetch'. Repo will issue 'git fetch tag <tag>' before giving
up completely.

Change-Id: I87796a5e1d51fcf398f346a274b7a069df37599a
Signed-off-by: Shawn O. Pearce <sop@google.com>
diff --git a/project.py b/project.py
index 1bcd959..956f45b 100644
--- a/project.py
+++ b/project.py
@@ -283,7 +283,7 @@
     return os.path.exists(os.path.join(g, 'rebase-apply')) \
         or os.path.exists(os.path.join(g, 'rebase-merge')) \
         or os.path.exists(os.path.join(w, '.dotest'))
-    
+
   def IsDirty(self, consider_untracked=True):
     """Is the working directory modified in some way?
     """
@@ -416,7 +416,7 @@
 
       try: f = df[p]
       except KeyError: f = None
- 
+
       if i: i_status = i.status.upper()
       else: i_status = '-'
 
@@ -601,6 +601,18 @@
     if not self._RemoteFetch():
       return False
 
+    #Check that the requested ref was found after fetch
+    #
+    try:
+      self.GetRevisionId()
+    except ManifestInvalidRevisionError:
+      # if the ref is a tag. We can try fetching
+      # the tag manually as a last resort
+      #
+      rev = self.revisionExpr
+      if rev.startswith(R_TAGS):
+        self._RemoteFetch(None, rev[len(R_TAGS):])
+
     if self.worktree:
       self._InitMRef()
     else:
@@ -982,7 +994,7 @@
 
 ## Direct Git Commands ##
 
-  def _RemoteFetch(self, name=None):
+  def _RemoteFetch(self, name=None, tag=None):
     if not name:
       name = self.remote.name
 
@@ -994,6 +1006,9 @@
     if not self.worktree:
       cmd.append('--update-head-ok')
     cmd.append(name)
+    if tag is not None:
+      cmd.append('tag')
+      cmd.append(tag)
     return GitCommand(self,
                       cmd,
                       bare = True,