Add 'repo init --mirror' to download a complete forrest

The mirror option downloads a complete forrest (as described by the
manifest) and creates a replica of the remote repositories rather
than a client working directory.  This permits other clients to
sync off the mirror site.

A mirror can be positioned in a "DMZ", where the mirror executes
"repo sync" to obtain changes from the external upstream and
clients inside the protected zone operate off the mirror only,
and therefore do not require direct git:// access to the external
upstream repositories.

Signed-off-by: Shawn O. Pearce <sop@google.com>
diff --git a/project.py b/project.py
index 0637f4b..1cfaaae 100644
--- a/project.py
+++ b/project.py
@@ -211,7 +211,10 @@
                     gitdir = self.gitdir,
                     defaults =  self.manifest.globalConfig)
 
-    self.work_git = self._GitGetByExec(self, bare=False)
+    if self.worktree:
+      self.work_git = self._GitGetByExec(self, bare=False)
+    else:
+      self.work_git = None
     self.bare_git = self._GitGetByExec(self, bare=True)
 
   @property
@@ -489,14 +492,23 @@
       print >>sys.stderr
       print >>sys.stderr, 'Initializing project %s ...' % self.name
       self._InitGitDir()
+
     self._InitRemote()
     for r in self.extraRemotes.values():
       if not self._RemoteFetch(r.name):
         return False
     if not self._RemoteFetch():
       return False
-    self._RepairAndroidImportErrors()
-    self._InitMRef()
+
+    if self.worktree:
+      self._RepairAndroidImportErrors()
+      self._InitMRef()
+    else:
+      self._InitMirrorHead()
+      try:
+        os.remove(os.path.join(self.gitdir, 'FETCH_HEAD'))
+      except OSError:
+        pass
     return True
 
   def PostRepoUpgrade(self):
@@ -792,9 +804,11 @@
   def _RemoteFetch(self, name=None):
     if not name:
       name = self.remote.name
-    return GitCommand(self,
-                      ['fetch', name],
-                      bare = True).Wait() == 0
+    cmd = ['fetch']
+    if not self.worktree:
+      cmd.append('--update-head-ok')
+    cmd.append(name)
+    return GitCommand(self, cmd, bare = True).Wait() == 0
 
   def _Checkout(self, rev, quiet=False):
     cmd = ['checkout']
@@ -874,7 +888,10 @@
       remote.url = url
       remote.review = self.remote.reviewUrl
 
-      remote.ResetFetch()
+      if self.worktree:
+        remote.ResetFetch(mirror=False)
+      else:
+        remote.ResetFetch(mirror=True)
       remote.Save()
 
     for r in self.extraRemotes.values():
@@ -897,6 +914,11 @@
         dst = remote.ToLocal(self.revision)
         self.bare_git.symbolic_ref('-m', msg, ref, dst)
 
+  def _InitMirrorHead(self):
+    dst = self.GetRemote(self.remote.name).ToLocal(self.revision)
+    msg = 'manifest set to %s' % self.revision
+    self.bare_git.SetHead(dst, message=msg)
+
   def _InitWorkTree(self):
     dotgit = os.path.join(self.worktree, '.git')
     if not os.path.exists(dotgit):