sync: introduce --verbose option

This allows us to control sync output better by having three levels
of output: quiet (only errors), default (progress bars), verbose (all
the things).  For now, we just put the chatty "already have persistent
ref" message behind the verbose level.

Bug: https://crbug.com/gerrit/11293
Change-Id: Ia61333fd8085719f3e99edb7b466cdb04031b67f
Reviewed-on: https://gerrit-review.googlesource.com/c/git-repo/+/255414
Reviewed-by: David Pursehouse <dpursehouse@collab.net>
Tested-by: Mike Frysinger <vapier@google.com>
diff --git a/project.py b/project.py
index 376af7a..57a5832 100644
--- a/project.py
+++ b/project.py
@@ -1427,6 +1427,7 @@
 
   def Sync_NetworkHalf(self,
                        quiet=False,
+                       verbose=False,
                        is_new=None,
                        current_branch_only=False,
                        force_sync=False,
@@ -1509,16 +1510,17 @@
     else:
       depth = self.manifest.manifestProject.config.GetString('repo.depth')
 
-    need_to_fetch = not (optimized_fetch and
-                         (ID_RE.match(self.revisionExpr) and
-                          self._CheckForImmutableRevision()))
-    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,
-                              submodules=submodules, force_sync=force_sync,
-                              clone_filter=clone_filter)):
-      return False
+    # See if we can skip the network fetch entirely.
+    if not (optimized_fetch and
+            (ID_RE.match(self.revisionExpr) and
+             self._CheckForImmutableRevision())):
+      if not self._RemoteFetch(
+          initial=is_new, quiet=quiet, verbose=verbose, alt_dir=alt_dir,
+          current_branch_only=current_branch_only,
+          no_tags=no_tags, prune=prune, depth=depth,
+          submodules=submodules, force_sync=force_sync,
+          clone_filter=clone_filter):
+        return False
 
     mp = self.manifest.manifestProject
     dissociate = mp.config.GetBoolean('repo.dissociate')
@@ -2193,6 +2195,7 @@
                    current_branch_only=False,
                    initial=False,
                    quiet=False,
+                   verbose=False,
                    alt_dir=None,
                    no_tags=False,
                    prune=False,
@@ -2223,7 +2226,7 @@
 
       if is_sha1 or tag_name is not None:
         if self._CheckForImmutableRevision():
-          if not quiet:
+          if verbose:
             print('Skipped fetching project %s (already have persistent ref)'
                   % self.name)
           return True
@@ -2400,17 +2403,13 @@
       # got what we wanted, else trigger a second run of all
       # refs.
       if not self._CheckForImmutableRevision():
-        if current_branch_only and depth:
-          # Sync the current branch only with depth set to None
-          return self._RemoteFetch(name=name,
-                                   current_branch_only=current_branch_only,
-                                   initial=False, quiet=quiet, alt_dir=alt_dir,
-                                   depth=None, clone_filter=clone_filter)
-        else:
-          # Avoid infinite recursion: sync all branches with depth set to None
-          return self._RemoteFetch(name=name, current_branch_only=False,
-                                   initial=False, quiet=quiet, alt_dir=alt_dir,
-                                   depth=None, clone_filter=clone_filter)
+        # Sync the current branch only with depth set to None.
+        # We always pass depth=None down to avoid infinite recursion.
+        return self._RemoteFetch(
+            name=name, quiet=quiet, verbose=verbose,
+            current_branch_only=current_branch_only and depth,
+            initial=False, alt_dir=alt_dir,
+            depth=None, clone_filter=clone_filter)
 
     return ok
 
diff --git a/subcmds/sync.py b/subcmds/sync.py
index 3d42a0a..cb28529 100644
--- a/subcmds/sync.py
+++ b/subcmds/sync.py
@@ -234,9 +234,12 @@
     p.add_option('-c', '--current-branch',
                  dest='current_branch_only', action='store_true',
                  help='fetch only current branch from server')
+    p.add_option('-v', '--verbose',
+                 dest='output_mode', action='store_true',
+                 help='show all sync output')
     p.add_option('-q', '--quiet',
-                 dest='quiet', action='store_true',
-                 help='be more quiet')
+                 dest='output_mode', action='store_false',
+                 help='only show errors')
     p.add_option('-j', '--jobs',
                  dest='jobs', action='store', type='int',
                  help="projects to fetch simultaneously (default %d)" % self.jobs)
@@ -332,6 +335,7 @@
       try:
         success = project.Sync_NetworkHalf(
             quiet=opt.quiet,
+            verbose=opt.verbose,
             current_branch_only=opt.current_branch_only,
             force_sync=opt.force_sync,
             clone_bundle=not opt.no_clone_bundle,
@@ -835,7 +839,7 @@
     """Fetch & update the local manifest project."""
     if not opt.local_only:
       start = time.time()
-      success = mp.Sync_NetworkHalf(quiet=opt.quiet,
+      success = mp.Sync_NetworkHalf(quiet=opt.quiet, verbose=opt.verbose,
                                     current_branch_only=opt.current_branch_only,
                                     no_tags=opt.no_tags,
                                     optimized_fetch=opt.optimized_fetch,
@@ -883,6 +887,9 @@
       soft_limit, _ = _rlimit_nofile()
       self.jobs = min(self.jobs, (soft_limit - 5) // 3)
 
+    opt.quiet = opt.output_mode is False
+    opt.verbose = opt.output_mode is True
+
     if opt.manifest_name:
       self.manifest.Override(opt.manifest_name)