sync: improve output with intermingled progress bars and status

When displaying progress bars, we use \r to reset the cursor to the
start of the line before showing the new update.  This assumes the
new line will fully erase whatever was displayed there previously.
The "done" codepath tries to handle this by including a few extra
spaces at the end of the message to "white out" what was there.

Lets replace that hack with the standard ECMA escape sequence that
clears the current line completely.  This is the CSI "erase in line"
sequence that the terminal will use to delete all content.  The \r
is still needed to move the cursor to the start of the line.  Using
this sequence should be OK since we're already assuming the terminal
is ECMA compliant with our use of coloring sequences.  We also put
the \r after the CSI sequence on the off chance the terminal can't
process it and displays a few bytes of garbage.

The other improvement is to the syncbuffer API.  When it dumps its
status information, it almost always comes after a progress bar
update which leads to confusing comingled output.  Something like:
  Fetching projects: 100% (2/2) error: src/platform2/: branch ...
Since the progress bar is "throw away", have the syncbuffer reset
the current output to the start of the line before showing whatever
messages it has queued.

Bug: https://crbug.com/gerrit/11293
Change-Id: I6544d073fe993d98ee7e91fca5e501ba5fecfe4c
Reviewed-on: https://gerrit-review.googlesource.com/c/git-repo/+/236615
Reviewed-by: David Pursehouse <dpursehouse@collab.net>
Tested-by: Mike Frysinger <vapier@google.com>
This commit is contained in:
Mike Frysinger 2019-08-26 15:22:36 -04:00
parent 9100f7fadd
commit 70d861fa29
2 changed files with 19 additions and 4 deletions

View File

@ -21,6 +21,11 @@ from repo_trace import IsTrace
_NOT_TTY = not os.isatty(2) _NOT_TTY = not os.isatty(2)
# This will erase all content in the current line (wherever the cursor is).
# It does not move the cursor, so this is usually followed by \r to move to
# column 0.
CSI_ERASE_LINE = '\x1b[2K'
class Progress(object): class Progress(object):
def __init__(self, title, total=0, units='', print_newline=False, def __init__(self, title, total=0, units='', print_newline=False,
always_print_percentage=False): always_print_percentage=False):
@ -47,7 +52,8 @@ class Progress(object):
return return
if self._total <= 0: if self._total <= 0:
sys.stderr.write('\r%s: %d, ' % ( sys.stderr.write('%s\r%s: %d,' % (
CSI_ERASE_LINE,
self._title, self._title,
self._done)) self._done))
sys.stderr.flush() sys.stderr.flush()
@ -56,7 +62,8 @@ class Progress(object):
if self._lastp != p or self._always_print_percentage: if self._lastp != p or self._always_print_percentage:
self._lastp = p self._lastp = p
sys.stderr.write('\r%s: %3d%% (%d%s/%d%s)%s' % ( sys.stderr.write('%s\r%s: %3d%% (%d%s/%d%s)%s' % (
CSI_ERASE_LINE,
self._title, self._title,
p, p,
self._done, self._units, self._done, self._units,
@ -69,13 +76,15 @@ class Progress(object):
return return
if self._total <= 0: if self._total <= 0:
sys.stderr.write('\r%s: %d, done. \n' % ( sys.stderr.write('%s\r%s: %d, done.\n' % (
CSI_ERASE_LINE,
self._title, self._title,
self._done)) self._done))
sys.stderr.flush() sys.stderr.flush()
else: else:
p = (100 * self._done) / self._total p = (100 * self._done) / self._total
sys.stderr.write('\r%s: %3d%% (%d%s/%d%s), done. \n' % ( sys.stderr.write('%s\r%s: %3d%% (%d%s/%d%s), done.\n' % (
CSI_ERASE_LINE,
self._title, self._title,
p, p,
self._done, self._units, self._done, self._units,

View File

@ -39,6 +39,7 @@ from error import GitError, HookError, UploadError, DownloadError
from error import ManifestInvalidRevisionError from error import ManifestInvalidRevisionError
from error import NoManifestException from error import NoManifestException
import platform_utils import platform_utils
import progress
from repo_trace import IsTrace, Trace from repo_trace import IsTrace, Trace
from git_refs import GitRefs, HEAD, R_HEADS, R_TAGS, R_PUB, R_M from git_refs import GitRefs, HEAD, R_HEADS, R_TAGS, R_PUB, R_M
@ -3113,6 +3114,11 @@ class SyncBuffer(object):
return True return True
def _PrintMessages(self): def _PrintMessages(self):
if self._messages or self._failures:
if os.isatty(2):
self.out.write(progress.CSI_ERASE_LINE)
self.out.write('\r')
for m in self._messages: for m in self._messages:
m.Print(self) m.Print(self)
for m in self._failures: for m in self._failures: