Change repo sync to be more friendly when updating the tree

We now try to sync all projects that can be done safely first, before
we start rebasing user commits over the upstream.  This has the nice
effect of making the local tree as close to the upstream as possible
before the user has to start resolving merge conflicts, as that extra
information in other projects may aid in the conflict resolution.

Informational output is buffered and delayed until calculation for
all projects has been done, so that the user gets one concise list
of notice messages, rather than it interrupting the progress meter.

Fast-forward output is now prefixed with the project header, so the
user can see which project that update is taking place in, and make
some relation of the diffstat back to the project name.

Rebase output is now prefixed with the project header, so that if
the rebase fails, the user can see which project we were operating
on and can try to address the failure themselves.

Since rebase sits on a detached HEAD, we now look for an in-progress
rebase during sync, so we can alert the user that the given project
is in a state we cannot handle.

Signed-off-by: Shawn O. Pearce <sop@google.com>
diff --git a/subcmds/init.py b/subcmds/init.py
index a32eaae..103a78d 100644
--- a/subcmds/init.py
+++ b/subcmds/init.py
@@ -20,6 +20,7 @@
 from command import InteractiveCommand, MirrorSafeCommand
 from error import ManifestParseError
 from remote import Remote
+from project import SyncBuffer
 from git_command import git, MIN_GIT_VERSION
 
 class Init(InteractiveCommand, MirrorSafeCommand):
@@ -129,7 +130,10 @@
       print >>sys.stderr, 'fatal: cannot obtain manifest %s' % r.url
       sys.exit(1)
 
-    m.Sync_LocalHalf()
+    syncbuf = SyncBuffer(m.config)
+    m.Sync_LocalHalf(syncbuf)
+    syncbuf.Finish()
+
     if is_new or m.CurrentBranch is None:
       if not m.StartBranch('default'):
         print >>sys.stderr, 'fatal: cannot create default in manifest'
diff --git a/subcmds/sync.py b/subcmds/sync.py
index f6eb2a0..ec5ada2 100644
--- a/subcmds/sync.py
+++ b/subcmds/sync.py
@@ -24,6 +24,7 @@
 from command import Command, MirrorSafeCommand
 from error import RepoChangedException, GitError
 from project import R_HEADS
+from project import SyncBuffer
 from progress import Progress
 
 class Sync(Command, MirrorSafeCommand):
@@ -112,7 +113,9 @@
         return
 
       if mp.HasChanges:
-        if not mp.Sync_LocalHalf():
+        syncbuf = SyncBuffer(mp.config)
+        mp.Sync_LocalHalf(syncbuf)
+        if not syncbuf.Finish():
           sys.exit(1)
 
         self.manifest._Unload()
@@ -123,14 +126,17 @@
             missing.append(project)
         self._Fetch(*missing)
 
+    syncbuf = SyncBuffer(mp.config,
+                         detach_head = opt.detach_head)
     pm = Progress('Syncing work tree', len(all))
     for project in all:
       pm.update()
       if project.worktree:
-        if not project.Sync_LocalHalf(
-            detach_head=opt.detach_head):
-          sys.exit(1)
+        project.Sync_LocalHalf(syncbuf)
     pm.end()
+    print >>sys.stderr
+    if not syncbuf.Finish():
+      sys.exit(1)
 
 
 def _PostRepoUpgrade(manifest):
@@ -143,7 +149,9 @@
     print >>sys.stderr, 'info: A new version of repo is available'
     print >>sys.stderr, ''
     if no_repo_verify or _VerifyTag(rp):
-      if not rp.Sync_LocalHalf():
+      syncbuf = SyncBuffer(rp.config)
+      rp.Sync_LocalHalf(syncbuf)
+      if not syncbuf.Finish():
         sys.exit(1)
       print >>sys.stderr, 'info: Restarting repo with latest version'
       raise RepoChangedException(['--repo-upgraded'])