Merge commits up to v2.14

* tag 'v2.14':
  sync: cleanup sleep+retry logic a bit
  sync: only print error.GitError, don't raise that exception.
  command: add a helper for the parallel execution boilerplate
  tests: Make ReviewableBranchTests.test_smoke work with git < 2.28.0
  list: fix help grammar
  help: switch from formatter module to textwrap
  list: add option to show non-checkedout projects too
  progress: hide progress bar when --quiet
  command: make --verbose/--quiet available to all subcommands
  init: Added --partial-clone-exclude option.
  sync: add separate --jobs options for different steps
  init: organize command line options a bit
  sync: add progress bar to garbage collection phase
  sync: rework selfupdate logic
  init: merge subcmd & wrapper parsers
  repo init: Added --no-partial-clone and made it persist. Bumped version to 2.14.

Change-Id: I5cadfa206ba2cd67a0972970910a50fee9c8dcd7
diff --git a/project.py b/project.py
index 7f6e236..85be200 100644
--- a/project.py
+++ b/project.py
@@ -1113,8 +1113,9 @@
                        retry_fetches=0,
                        prune=False,
                        submodules=False,
+                       cache_dir=None,
                        clone_filter=None,
-                       cache_dir=None):
+                       partial_clone_exclude=None):
     """Perform only the network IO portion of the sync process.
        Local working directory/branch state is not affected.
     """
@@ -1151,6 +1152,10 @@
     if clone_bundle and os.path.exists(self.objdir):
       clone_bundle = False
 
+    if self.name in partial_clone_exclude:
+      clone_bundle = True
+      clone_filter = None
+
     if is_new is None:
       is_new = not self.Exists
     if is_new:
@@ -2249,19 +2254,13 @@
       elif (gitcmd.stdout and
             'error:' in gitcmd.stdout and
             'HTTP 429' in gitcmd.stdout):
-        if not quiet:
-          print('429 received, sleeping: %s sec' % retry_cur_sleep,
-                file=sys.stderr)
-        time.sleep(retry_cur_sleep)
-        retry_cur_sleep = min(retry_exp_factor * retry_cur_sleep,
-                              MAXIMUM_RETRY_SLEEP_SEC)
-        retry_cur_sleep *= (1 - random.uniform(-RETRY_JITTER_PERCENT,
-                                               RETRY_JITTER_PERCENT))
-        continue
+        # Fallthru to sleep+retry logic at the bottom.
+        pass
 
-      # If this is not last attempt, try 'git remote prune'.
-      elif (try_n < retry_fetches - 1 and
-            gitcmd.stdout and
+      # Try to prune remote branches once in case there are conflicts.
+      # For example, if the remote had refs/heads/upstream, but deleted that and
+      # now has refs/heads/upstream/foo.
+      elif (gitcmd.stdout and
             'error:' in gitcmd.stdout and
             'git remote prune' in gitcmd.stdout and
             not prune_tried):
@@ -2271,6 +2270,8 @@
         ret = prunecmd.Wait()
         if ret:
           break
+        output_redir.write('retrying fetch after pruning remote branches')
+        # Continue right away so we don't sleep as we shouldn't need to.
         continue
       elif current_branch_only and is_sha1 and ret == 128:
         # Exit code 128 means "couldn't find the ref you asked for"; if we're
@@ -2280,9 +2281,17 @@
       elif ret < 0:
         # Git died with a signal, exit immediately
         break
+
+      # Figure out how long to sleep before the next attempt, if there is one.
       if not verbose:
-        print('\n%s:\n%s' % (self.name, gitcmd.stdout), file=sys.stderr)
-      time.sleep(random.randint(30, 45))
+        output_redir.write('\n%s:\n%s' % (self.name, gitcmd.stdout), file=sys.stderr)
+      if try_n < retry_fetches - 1:
+        output_redir.write('sleeping %s seconds before retrying' % retry_cur_sleep)
+        time.sleep(retry_cur_sleep)
+        retry_cur_sleep = min(retry_exp_factor * retry_cur_sleep,
+                              MAXIMUM_RETRY_SLEEP_SEC)
+        retry_cur_sleep *= (1 - random.uniform(-RETRY_JITTER_PERCENT,
+                                               RETRY_JITTER_PERCENT))
 
     if initial:
       if alt_dir: