init: add multi-manifest support

This moves more of the manifest project handling into ManifestProject.

Change-Id: Iecdafbec18cccdfd8e625753c3bd1bcddf2b227f
Reviewed-on: https://gerrit-review.googlesource.com/c/git-repo/+/334520
Tested-by: LaMont Jones <lamontjones@google.com>
Reviewed-by: Mike Frysinger <vapier@google.com>
diff --git a/project.py b/project.py
index 2989e09..6d8784e 100644
--- a/project.py
+++ b/project.py
@@ -34,7 +34,8 @@
     ID_RE
 from error import GitError, UploadError, DownloadError
 from error import ManifestInvalidRevisionError, ManifestInvalidPathError
-from error import NoManifestException
+from error import NoManifestException, ManifestParseError
+import git_superproject
 import platform_utils
 import progress
 from repo_trace import IsTrace, Trace
@@ -3443,7 +3444,8 @@
            partial_clone=None, depth=None, clone_filter='blob:none',
            partial_clone_exclude=None, clone_bundle=None, git_lfs=None,
            use_superproject=None, verbose=False, current_branch_only=False,
-           platform='', tags=''):
+           platform='', tags='', manifest_name='default.xml',
+           this_manifest_only=False, outer_manifest=True):
     """Sync the manifest and all submanifests.
 
     Args:
@@ -3477,12 +3479,45 @@
       platform: a string, restrict the checkout to projects with the specified
           platform group.
       tags: a boolean, whether to fetch tags.,
+      manifest_name: a string, the name of the manifest file to use.
+      this_manifest_only: a boolean, whether to only operate on the current sub
+          manifest.
+      outer_manifest: a boolean, whether to start at the outermost manifest.
 
     Returns:
       a boolean, whether the sync was successful.
     """
     assert _kwargs_only == (), 'Sync only accepts keyword arguments.'
 
+    if outer_manifest and self.manifest.is_submanifest:
+      # In a multi-manifest checkout, use the outer manifest unless we are told
+      # not to.
+      return self.client.outer_manifest.manifestProject.Sync(
+          manifest_url=manifest_url,
+          manifest_branch=manifest_branch,
+          standalone_manifest=standalone_manifest,
+          groups=groups,
+          platform=platform,
+          mirror=mirror,
+          dissociate=dissociate,
+          reference=reference,
+          worktree=worktree,
+          submodules=submodules,
+          archive=archive,
+          partial_clone=partial_clone,
+          clone_filter=clone_filter,
+          partial_clone_exclude=partial_clone_exclude,
+          clone_bundle=clone_bundle,
+          git_lfs=git_lfs,
+          use_superproject=use_superproject,
+          verbose=verbose,
+          current_branch_only=current_branch_only,
+          tags=tags,
+          depth=depth,
+          manifest_name=manifest_name,
+          this_manifest_only=this_manifest_only,
+          outer_manifest=False)
+
     # If repo has already been initialized, we take -u with the absence of
     # --standalone-manifest to mean "transition to a standard repo set up",
     # which necessitates starting fresh.
@@ -3508,7 +3543,7 @@
         print('fatal: manifest url is required.', file=sys.stderr)
         return False
 
-      if not quiet:
+      if verbose:
         print('Downloading manifest from %s' %
               (GitConfig.ForUser().UrlInsteadOf(manifest_url),),
               file=sys.stderr)
@@ -3700,6 +3735,65 @@
         print('fatal: cannot create default in manifest', file=sys.stderr)
         return False
 
+    if not manifest_name:
+      print('fatal: manifest name (-m) is required.', file=sys.stderr)
+      return False
+
+    try:
+      self.manifest.Link(manifest_name)
+    except ManifestParseError as e:
+      print("fatal: manifest '%s' not available" % manifest_name,
+            file=sys.stderr)
+      print('fatal: %s' % str(e), file=sys.stderr)
+      return False
+
+    # Lastly, clone the superproject.
+    superproject = git_superproject.Superproject(self.manifest,
+                                                 self.repodir,
+                                                 self.git_event_log,
+                                                 quiet=not verbose)
+    sync_result = superproject.Sync()
+    if not sync_result.success:
+      print('warning: git update of superproject failed, repo sync will not '
+            'use superproject to fetch source; while this error is not fatal, '
+            'and you can continue to run repo sync, please run repo init with '
+            'the --no-use-superproject option to stop seeing this warning',
+            file=sys.stderr)
+      if sync_result.fatal and use_superproject is not None:
+        return False
+
+    if this_manifest_only:
+      return True
+
+    for submanifest in self.manifest.submanifests.values():
+      spec = submanifest.ToSubmanifestSpec(root=self.manifest.outer_client)
+      submanifest.repo_client.manifestProject.Sync(
+          manifest_url=spec.manifestUrl,
+          manifest_branch=spec.revision,
+          standalone_manifest=standalone_manifest,
+          groups=self.manifest_groups,
+          platform=platform,
+          mirror=mirror,
+          dissociate=dissociate,
+          reference=reference,
+          worktree=worktree,
+          submodules=submodules,
+          archive=archive,
+          partial_clone=partial_clone,
+          clone_filter=clone_filter,
+          partial_clone_exclude=partial_clone_exclude,
+          clone_bundle=clone_bundle,
+          git_lfs=git_lfs,
+          use_superproject=use_superproject,
+          verbose=verbose,
+          current_branch_only=current_branch_only,
+          tags=tags,
+          depth=depth,
+          manifest_name=spec.manifestName,
+          this_manifest_only=False,
+          outer_manifest=False,
+      )
+
     return True
 
   def _ConfigureDepth(self, depth):
diff --git a/subcmds/init.py b/subcmds/init.py
index 13085fa..2cb3ff3 100644
--- a/subcmds/init.py
+++ b/subcmds/init.py
@@ -32,7 +32,7 @@
 
 class Init(InteractiveCommand, MirrorSafeCommand):
   COMMON = True
-  MULTI_MANIFEST_SUPPORT = False
+  MULTI_MANIFEST_SUPPORT = True
   helpSummary = "Initialize a repo client checkout in the current directory"
   helpUsage = """
 %prog [options] [manifest url]
@@ -107,26 +107,6 @@
     return {'REPO_MANIFEST_URL': 'manifest_url',
             'REPO_MIRROR_LOCATION': 'reference'}
 
-  def _CloneSuperproject(self, opt):
-    """Clone the superproject based on the superproject's url and branch.
-
-    Args:
-      opt: Program options returned from optparse.  See _Options().
-    """
-    superproject = git_superproject.Superproject(self.manifest,
-                                                 self.repodir,
-                                                 self.git_event_log,
-                                                 quiet=opt.quiet)
-    sync_result = superproject.Sync()
-    if not sync_result.success:
-      print('warning: git update of superproject failed, repo sync will not '
-            'use superproject to fetch source; while this error is not fatal, '
-            'and you can continue to run repo sync, please run repo init with '
-            'the --no-use-superproject option to stop seeing this warning',
-            file=sys.stderr)
-      if sync_result.fatal and opt.use_superproject is not None:
-        sys.exit(1)
-
   def _SyncManifest(self, opt):
     """Call manifestProject.Sync with arguments from opt.
 
@@ -154,19 +134,8 @@
         verbose=opt.verbose,
         current_branch_only=opt.current_branch_only,
         tags=opt.tags,
-        depth=opt.depth):
-      sys.exit(1)
-
-  def _LinkManifest(self, name):
-    if not name:
-      print('fatal: manifest name (-m) is required.', file=sys.stderr)
-      sys.exit(1)
-
-    try:
-      self.manifest.Link(name)
-    except ManifestParseError as e:
-      print("fatal: manifest '%s' not available" % name, file=sys.stderr)
-      print('fatal: %s' % str(e), file=sys.stderr)
+        depth=opt.depth,
+        manifest_name=opt.manifest_name):
       sys.exit(1)
 
   def _Prompt(self, prompt, value):
@@ -343,10 +312,6 @@
       git_require((2, 15, 0), fail=True, msg='git gc worktree corruption')
 
     self._SyncManifest(opt)
-    self._LinkManifest(opt.manifest_name)
-
-    if self.manifest.manifestProject.use_superproject:
-      self._CloneSuperproject(opt)
 
     if os.isatty(0) and os.isatty(1) and not self.manifest.IsMirror:
       if opt.config_name or self._ShouldConfigureUser(opt):