Merge "pre-auto-gc: Add support for Windows"
diff --git a/.mailmap b/.mailmap
index eb64bd2..905139d 100644
--- a/.mailmap
+++ b/.mailmap
@@ -1,4 +1,5 @@
 Anthony Newnam <anthony.newnam@garmin.com>    Anthony <anthony@bnovc.com>
+He Ping <tdihp@hotmail.com>                   heping <tdihp@hotmail.com>
 Hu Xiuyun <xiuyun.hu@hisilicon.com>           Hu xiuyun <xiuyun.hu@hisilicon.com>
 Hu Xiuyun <xiuyun.hu@hisilicon.com>           Hu Xiuyun <clouds08@qq.com>
 Jelly Chen <chenguodong@huawei.com>           chenguodong <chenguodong@huawei.com>
diff --git a/docs/manifest-format.txt b/docs/manifest-format.txt
index 4484d80..7778409 100644
--- a/docs/manifest-format.txt
+++ b/docs/manifest-format.txt
@@ -27,7 +27,8 @@
                         remove-project*,
                         project*,
                         extend-project*,
-                        repo-hooks?)>
+                        repo-hooks?,
+                        include*)>
 
     <!ELEMENT notice (#PCDATA)>
 
diff --git a/git_config.py b/git_config.py
index e223678..e00f6be 100644
--- a/git_config.py
+++ b/git_config.py
@@ -631,6 +631,9 @@
       elif u.startswith('sso:'):
         self._review_url = u  # Assume it's right
         REVIEW_CACHE[u] = self._review_url
+      elif 'REPO_IGNORE_SSH_INFO' in os.environ:
+        self._review_url = http_url
+        REVIEW_CACHE[u] = self._review_url
       else:
         try:
           info_url = u + 'ssh_info'
diff --git a/main.py b/main.py
index c5f1e9c..f965d7e 100755
--- a/main.py
+++ b/main.py
@@ -198,6 +198,10 @@
       else:
         print('error: project group must be enabled for the project in the current directory', file=sys.stderr)
       result = 1
+    except SystemExit as e:
+      if e.code:
+        result = e.code
+      raise
     finally:
       elapsed = time.time() - start
       hours, remainder = divmod(elapsed, 3600)
diff --git a/manifest_xml.py b/manifest_xml.py
index 0859e1f..73e3496 100644
--- a/manifest_xml.py
+++ b/manifest_xml.py
@@ -393,6 +393,10 @@
   def IsArchive(self):
     return self.manifestProject.config.GetBoolean('repo.archive')
 
+  @property
+  def HasSubmodules(self):
+    return self.manifestProject.config.GetBoolean('repo.submodules')
+
   def _Unload(self):
     self._loaded = False
     self._projects = {}
diff --git a/project.py b/project.py
index 0d60fc6..ed5b0e6 100644
--- a/project.py
+++ b/project.py
@@ -1198,7 +1198,8 @@
                        no_tags=False,
                        archive=False,
                        optimized_fetch=False,
-                       prune=False):
+                       prune=False,
+                       submodules=False):
     """Perform only the network IO portion of the sync process.
        Local working directory/branch state is not affected.
     """
@@ -1275,7 +1276,8 @@
     if (need_to_fetch and
         not self._RemoteFetch(initial=is_new, quiet=quiet, alt_dir=alt_dir,
                               current_branch_only=current_branch_only,
-                              no_tags=no_tags, prune=prune, depth=depth)):
+                              no_tags=no_tags, prune=prune, depth=depth,
+                              submodules=submodules)):
       return False
 
     if self.worktree:
@@ -1331,11 +1333,11 @@
       raise ManifestInvalidRevisionError('revision %s in %s not found' %
                                          (self.revisionExpr, self.name))
 
-  def Sync_LocalHalf(self, syncbuf, force_sync=False):
+  def Sync_LocalHalf(self, syncbuf, force_sync=False, submodules=False):
     """Perform only the local IO portion of the sync process.
        Network access is not required.
     """
-    self._InitWorkTree(force_sync=force_sync)
+    self._InitWorkTree(force_sync=force_sync, submodules=submodules)
     all_refs = self.bare_ref.all
     self.CleanPublishedCache(all_refs)
     revid = self.GetRevisionId(all_refs)
@@ -1344,6 +1346,9 @@
       self._FastForward(revid)
       self._CopyAndLinkFiles()
 
+    def _dosubmodules():
+      self._SyncSubmodules(quiet=True)
+
     head = self.work_git.GetHead()
     if head.startswith(R_HEADS):
       branch = head[len(R_HEADS):]
@@ -1377,6 +1382,8 @@
 
       try:
         self._Checkout(revid, quiet=True)
+        if submodules:
+          self._SyncSubmodules(quiet=True)
       except GitError as e:
         syncbuf.fail(self, e)
         return
@@ -1401,6 +1408,8 @@
                    branch.name)
       try:
         self._Checkout(revid, quiet=True)
+        if submodules:
+          self._SyncSubmodules(quiet=True)
       except GitError as e:
         syncbuf.fail(self, e)
         return
@@ -1426,6 +1435,8 @@
         # strict subset.  We can fast-forward safely.
         #
         syncbuf.later1(self, _doff)
+        if submodules:
+          syncbuf.later1(self, _dosubmodules)
         return
 
     # Examine the local commits not in the remote.  Find the
@@ -1477,19 +1488,28 @@
     branch.Save()
 
     if cnt_mine > 0 and self.rebase:
+      def _docopyandlink():
+        self._CopyAndLinkFiles()
+
       def _dorebase():
         self._Rebase(upstream='%s^1' % last_mine, onto=revid)
-        self._CopyAndLinkFiles()
       syncbuf.later2(self, _dorebase)
+      if submodules:
+        syncbuf.later2(self, _dosubmodules)
+      syncbuf.later2(self, _docopyandlink)
     elif local_changes:
       try:
         self._ResetHard(revid)
+        if submodules:
+          self._SyncSubmodules(quiet=True)
         self._CopyAndLinkFiles()
       except GitError as e:
         syncbuf.fail(self, e)
         return
     else:
       syncbuf.later1(self, _doff)
+      if submodules:
+        syncbuf.later1(self, _dosubmodules)
 
   def AddCopyFile(self, src, dest, absdest):
     # dest should already be an absolute path, but src is project relative
@@ -1892,7 +1912,8 @@
                    alt_dir=None,
                    no_tags=False,
                    prune=False,
-                   depth=None):
+                   depth=None,
+                   submodules=False):
 
     is_sha1 = False
     tag_name = None
@@ -1963,15 +1984,17 @@
           ids.add(ref_id)
           tmp.add(r)
 
-        tmp_packed = ''
-        old_packed = ''
+        tmp_packed_lines = []
+        old_packed_lines = []
 
         for r in sorted(all_refs):
           line = '%s %s\n' % (all_refs[r], r)
-          tmp_packed += line
+          tmp_packed_lines.append(line)
           if r not in tmp:
-            old_packed += line
+            old_packed_lines.append(line)
 
+        tmp_packed = ''.join(tmp_packed_lines)
+        old_packed = ''.join(old_packed_lines)
         _lwrite(packed_refs, tmp_packed)
       else:
         alt_dir = None
@@ -2004,6 +2027,9 @@
     if prune:
       cmd.append('--prune')
 
+    if submodules:
+      cmd.append('--recurse-submodules=on-demand')
+
     spec = []
     if not current_branch_only:
       # Fetch whole repo
@@ -2224,6 +2250,13 @@
     if GitCommand(self, cmd).Wait() != 0:
       raise GitError('%s reset --hard %s ' % (self.name, rev))
 
+  def _SyncSubmodules(self, quiet=True):
+    cmd = ['submodule', 'update', '--init', '--recursive']
+    if quiet:
+      cmd.append('-q')
+    if GitCommand(self, cmd).Wait() != 0:
+      raise GitError('%s submodule update --init --recursive %s ' % self.name)
+
   def _Rebase(self, upstream, onto=None):
     cmd = ['rebase']
     if onto is not None:
@@ -2464,7 +2497,7 @@
         else:
           raise
 
-  def _InitWorkTree(self, force_sync=False):
+  def _InitWorkTree(self, force_sync=False, submodules=False):
     dotgit = os.path.join(self.worktree, '.git')
     init_dotgit = not os.path.exists(dotgit)
     try:
@@ -2479,7 +2512,7 @@
         if force_sync:
           try:
             shutil.rmtree(dotgit)
-            return self._InitWorkTree(force_sync=False)
+            return self._InitWorkTree(force_sync=False, submodules=submodules)
           except:
             raise e
         raise e
@@ -2493,6 +2526,8 @@
         if GitCommand(self, cmd).Wait() != 0:
           raise GitError("cannot initialize work tree")
 
+        if submodules:
+          self._SyncSubmodules(quiet=True)
         self._CopyAndLinkFiles()
     except Exception:
       if init_dotgit:
diff --git a/repo b/repo
index dcd48e0..c1d8619 100755
--- a/repo
+++ b/repo
@@ -192,6 +192,9 @@
                  dest='archive', action='store_true',
                  help='checkout an archive instead of a git repository for '
                       'each project. See git archive.')
+group.add_option('--submodules',
+                 dest='submodules', action='store_true',
+                 help='sync any submodules associated with the manifest repo')
 group.add_option('-g', '--groups',
                  dest='groups', default='default',
                  help='restrict manifest projects to ones with specified '
diff --git a/subcmds/abandon.py b/subcmds/abandon.py
index 6f78da7..be32dc5 100644
--- a/subcmds/abandon.py
+++ b/subcmds/abandon.py
@@ -81,7 +81,7 @@
         err_msg = "error: cannot abandon %s" %br
         print(err_msg, file=sys.stderr)
         for proj in err[br]:
-          print(' '*len(err_msg) + " | %s" % p.relpath, file=sys.stderr)
+          print(' '*len(err_msg) + " | %s" % proj.relpath, file=sys.stderr)
       sys.exit(1)
     elif not success:
       print('error: no project has local branch(es) : %s' % nb,
diff --git a/subcmds/gitc_delete.py b/subcmds/gitc_delete.py
index 7380c35..19caac5 100644
--- a/subcmds/gitc_delete.py
+++ b/subcmds/gitc_delete.py
@@ -14,12 +14,10 @@
 # limitations under the License.
 
 from __future__ import print_function
-import os
 import shutil
 import sys
 
 from command import Command, GitcClientCommand
-import gitc_utils
 
 from pyversion import is_python3
 if not is_python3():
diff --git a/subcmds/init.py b/subcmds/init.py
index bb7187d..b260ec0 100644
--- a/subcmds/init.py
+++ b/subcmds/init.py
@@ -111,6 +111,9 @@
                  dest='archive', action='store_true',
                  help='checkout an archive instead of a git repository for '
                       'each project. See git archive.')
+    g.add_option('--submodules',
+                 dest='submodules', action='store_true',
+                 help='sync any submodules associated with the manifest repo')
     g.add_option('-g', '--groups',
                  dest='groups', default='default',
                  help='restrict manifest projects to ones with specified '
@@ -236,10 +239,13 @@
               'in another location.', file=sys.stderr)
         sys.exit(1)
 
+    if opt.submodules:
+      m.config.SetString('repo.submodules', 'true')
+
     if not m.Sync_NetworkHalf(is_new=is_new, quiet=opt.quiet,
         clone_bundle=not opt.no_clone_bundle,
         current_branch_only=opt.current_branch_only,
-        no_tags=opt.no_tags):
+        no_tags=opt.no_tags, submodules=opt.submodules):
       r = m.GetRemote(m.remote.name)
       print('fatal: cannot obtain manifest %s' % r.url, file=sys.stderr)
 
@@ -253,7 +259,7 @@
       m.MetaBranchSwitch()
 
     syncbuf = SyncBuffer(m.config)
-    m.Sync_LocalHalf(syncbuf)
+    m.Sync_LocalHalf(syncbuf, submodules=opt.submodules)
     syncbuf.Finish()
 
     if is_new or m.CurrentBranch is None:
diff --git a/subcmds/sync.py b/subcmds/sync.py
index 8e8529e..82056f3 100644
--- a/subcmds/sync.py
+++ b/subcmds/sync.py
@@ -723,11 +723,12 @@
       mp.Sync_NetworkHalf(quiet=opt.quiet,
                           current_branch_only=opt.current_branch_only,
                           no_tags=opt.no_tags,
-                          optimized_fetch=opt.optimized_fetch)
+                          optimized_fetch=opt.optimized_fetch,
+                          submodules=self.manifest.HasSubmodules)
 
     if mp.HasChanges:
       syncbuf = SyncBuffer(mp.config)
-      mp.Sync_LocalHalf(syncbuf)
+      mp.Sync_LocalHalf(syncbuf, submodules=self.manifest.HasSubmodules)
       if not syncbuf.Finish():
         sys.exit(1)
       self._ReloadManifest(manifest_name)