Merge "Fix checkout error when depth passed to repo init and revision is a sha1"
diff --git a/manifest_xml.py b/manifest_xml.py
index 9c882af..0859e1f 100644
--- a/manifest_xml.py
+++ b/manifest_xml.py
@@ -40,8 +40,18 @@
 LOCAL_MANIFESTS_DIR_NAME = 'local_manifests'
 
 # urljoin gets confused if the scheme is not known.
-urllib.parse.uses_relative.extend(['ssh', 'git', 'persistent-https', 'rpc'])
-urllib.parse.uses_netloc.extend(['ssh', 'git', 'persistent-https', 'rpc'])
+urllib.parse.uses_relative.extend([
+    'ssh',
+    'git',
+    'persistent-https',
+    'sso',
+    'rpc'])
+urllib.parse.uses_netloc.extend([
+    'ssh',
+    'git',
+    'persistent-https',
+    'sso',
+    'rpc'])
 
 class _Default(object):
   """Project defaults within the manifest."""
diff --git a/project.py b/project.py
index cdb61c2..633ae07 100644
--- a/project.py
+++ b/project.py
@@ -911,11 +911,13 @@
     else:
       return False
 
-  def PrintWorkTreeStatus(self, output_redir=None):
+  def PrintWorkTreeStatus(self, output_redir=None, quiet=False):
     """Prints the status of the repository to stdout.
 
     Args:
       output: If specified, redirect the output to this object.
+      quiet:  If True then only print the project name.  Do not print
+              the modified files, branch name, etc.
     """
     if not os.path.isdir(self.worktree):
       if output_redir is None:
@@ -941,6 +943,10 @@
       out.redirect(output_redir)
     out.project('project %-40s', self.relpath + '/ ')
 
+    if quiet:
+      out.nl()
+      return 'DIRTY'
+
     branch = self.CurrentBranch
     if branch is None:
       out.nobranch('(*** NO BRANCH ***)')
@@ -2394,6 +2400,7 @@
         src = os.path.realpath(os.path.join(srcdir, name))
         # Fail if the links are pointing to the wrong place
         if src != dst:
+          _error('%s is different in %s vs %s', name, destdir, srcdir)
           raise GitError('--force-sync not enabled; cannot overwrite a local '
                          'work tree. If you\'re comfortable with the '
                          'possibility of losing the work tree\'s git metadata,'
diff --git a/subcmds/abandon.py b/subcmds/abandon.py
index b94ccdd..6f78da7 100644
--- a/subcmds/abandon.py
+++ b/subcmds/abandon.py
@@ -16,6 +16,7 @@
 from __future__ import print_function
 import sys
 from command import Command
+from collections import defaultdict
 from git_command import git
 from progress import Progress
 
@@ -23,49 +24,75 @@
   common = True
   helpSummary = "Permanently abandon a development branch"
   helpUsage = """
-%prog <branchname> [<project>...]
+%prog [--all | <branchname>] [<project>...]
 
 This subcommand permanently abandons a development branch by
 deleting it (and all its history) from your local repository.
 
 It is equivalent to "git branch -D <branchname>".
 """
+  def _Options(self, p):
+    p.add_option('--all',
+                 dest='all', action='store_true',
+                 help='delete all branches in all projects')
 
   def Execute(self, opt, args):
-    if not args:
+    if not opt.all and not args:
       self.Usage()
 
-    nb = args[0]
-    if not git.check_ref_format('heads/%s' % nb):
-      print("error: '%s' is not a valid name" % nb, file=sys.stderr)
-      sys.exit(1)
+    if not opt.all:
+      nb = args[0]
+      if not git.check_ref_format('heads/%s' % nb):
+        print("error: '%s' is not a valid name" % nb, file=sys.stderr)
+        sys.exit(1)
+    else:
+      args.insert(0,None)
+      nb = "'All local branches'"
 
-    nb = args[0]
-    err = []
-    success = []
+    err = defaultdict(list)
+    success = defaultdict(list)
     all_projects = self.GetProjects(args[1:])
 
     pm = Progress('Abandon %s' % nb, len(all_projects))
     for project in all_projects:
       pm.update()
 
-      status = project.AbandonBranch(nb)
-      if status is not None:
-        if status:
-          success.append(project)
-        else:
-          err.append(project)
+      if opt.all:
+        branches = project.GetBranches().keys()
+      else:
+        branches = [nb]
+
+      for name in branches:
+        status = project.AbandonBranch(name)
+        if status is not None:
+          if status:
+            success[name].append(project)
+          else:
+            err[name].append(project)
     pm.end()
 
+    width = 25
+    for name in branches:
+      if width < len(name):
+        width = len(name)
+
     if err:
-      for p in err:
-        print("error: %s/: cannot abandon %s" % (p.relpath, nb),
-              file=sys.stderr)
+      for br in err.keys():
+        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)
       sys.exit(1)
     elif not success:
-      print('error: no project has branch %s' % nb, file=sys.stderr)
+      print('error: no project has local branch(es) : %s' % nb,
+            file=sys.stderr)
       sys.exit(1)
     else:
-      print('Abandoned in %d project(s):\n  %s'
-            % (len(success), '\n  '.join(p.relpath for p in success)),
-            file=sys.stderr)
+      print('Abandoned branches:', file=sys.stderr)
+      for br in success.keys():
+        if len(all_projects) > 1 and len(all_projects) == len(success[br]):
+          result = "all project"
+        else:
+          result = "%s" % (
+            ('\n'+' '*width + '| ').join(p.relpath for p in success[br]))
+        print("%s%s| %s\n" % (br,' '*(width-len(br)), result),file=sys.stderr)
diff --git a/subcmds/status.py b/subcmds/status.py
index 38c229b..60e26ff 100644
--- a/subcmds/status.py
+++ b/subcmds/status.py
@@ -89,8 +89,10 @@
     p.add_option('-o', '--orphans',
                  dest='orphans', action='store_true',
                  help="include objects in working directory outside of repo projects")
+    p.add_option('-q', '--quiet', action='store_true',
+                 help="only print the name of modified projects")
 
-  def _StatusHelper(self, project, clean_counter, sem):
+  def _StatusHelper(self, project, clean_counter, sem, quiet):
     """Obtains the status for a specific project.
 
     Obtains the status for a project, redirecting the output to
@@ -104,7 +106,7 @@
       output: Where to output the status.
     """
     try:
-      state = project.PrintWorkTreeStatus()
+      state = project.PrintWorkTreeStatus(quiet=quiet)
       if state == 'CLEAN':
         next(clean_counter)
     finally:
@@ -132,7 +134,7 @@
 
     if opt.jobs == 1:
       for project in all_projects:
-        state = project.PrintWorkTreeStatus()
+        state = project.PrintWorkTreeStatus(quiet=opt.quiet)
         if state == 'CLEAN':
           next(counter)
     else:
@@ -142,13 +144,13 @@
         sem.acquire()
 
         t = _threading.Thread(target=self._StatusHelper,
-                              args=(project, counter, sem))
+                              args=(project, counter, sem, opt.quiet))
         threads.append(t)
         t.daemon = True
         t.start()
       for t in threads:
         t.join()
-    if len(all_projects) == next(counter):
+    if not opt.quiet and len(all_projects) == next(counter):
       print('nothing to commit (working directory clean)')
 
     if opt.orphans:
diff --git a/subcmds/sync.py b/subcmds/sync.py
index 7ba9ebf..bbb166c 100644
--- a/subcmds/sync.py
+++ b/subcmds/sync.py
@@ -255,7 +255,7 @@
                  dest='repo_upgraded', action='store_true',
                  help=SUPPRESS_HELP)
 
-  def _FetchProjectList(self, opt, projects, *args, **kwargs):
+  def _FetchProjectList(self, opt, projects, sem, *args, **kwargs):
     """Main function of the fetch threads when jobs are > 1.
 
     Delegates most of the work to _FetchHelper.
@@ -263,15 +263,20 @@
     Args:
       opt: Program options returned from optparse.  See _Options().
       projects: Projects to fetch.
+      sem: We'll release() this semaphore when we exit so that another thread
+          can be started up.
       *args, **kwargs: Remaining arguments to pass to _FetchHelper. See the
           _FetchHelper docstring for details.
     """
-    for project in projects:
-      success = self._FetchHelper(opt, project, *args, **kwargs)
-      if not success and not opt.force_broken:
-        break
+    try:
+        for project in projects:
+          success = self._FetchHelper(opt, project, *args, **kwargs)
+          if not success and not opt.force_broken:
+            break
+    finally:
+        sem.release()
 
-  def _FetchHelper(self, opt, project, lock, fetched, pm, sem, err_event):
+  def _FetchHelper(self, opt, project, lock, fetched, pm, err_event):
     """Fetch git objects for a single project.
 
     Args:
@@ -283,8 +288,6 @@
           (with our lock held).
       pm: Instance of a Project object.  We will call pm.update() (with our
           lock held).
-      sem: We'll release() this semaphore when we exit so that another thread
-          can be started up.
       err_event: We'll set this event in the case of an error (after printing
           out info about the error).
 
@@ -340,7 +343,6 @@
     finally:
       if did_lock:
         lock.release()
-      sem.release()
 
     return success
 
@@ -365,10 +367,10 @@
       sem.acquire()
       kwargs = dict(opt=opt,
                     projects=project_list,
+                    sem=sem,
                     lock=lock,
                     fetched=fetched,
                     pm=pm,
-                    sem=sem,
                     err_event=err_event)
       if self.jobs > 1:
         t = _threading.Thread(target = self._FetchProjectList,