Keep the progress bar estimate when the last file count is exceeded

When a backup has more files than the last one, estimate the total from the files rsync has found so far,
capped at 99% until the scan ends, instead of falling back to "(scanning)".
This commit is contained in:
Fuxino
2026-11-14 11:08:21 +01:00
parent 68b6d77f30
commit 2c7233ce45
3 changed files with 33 additions and 10 deletions
+3 -2
View File
@@ -117,8 +117,9 @@ Don't use systemd journal for logging.
Don't show a progress bar while rsync copies the files. The progress bar is only shown when
the output is a terminal, so it never appears in the systemd journal or in redirected output.
While rsync is still looking for files, the percentage and estimated time (marked with
\(aq~\(aq) are based on the number of files in the last backup, or the bar shows
\(aq(scanning)\(aq if that is unknown or already exceeded.
\(aq~\(aq) are based on the number of files in the last backup (or on the files found so far,
once this backup has more), and stay below 100%. Without a previous count the bar shows
\(aq(scanning)\(aq instead.
.TP
.B \-\-rsync\-options OPTIONS [OPTION...]
By default, the following rsync options are used:
+8 -5
View File
@@ -135,13 +135,16 @@ class _ProgressBar:
percent: int | None
if self.total is None:
# Estimate from the last backup, unless this one already has more files
if 0 < self._checked < self._estimate:
percent = self._checked * 100 // self._estimate
text = f'{f"~{percent}":>3}% {self._checked:,}/~{self._estimate:,} files {size} copied'
# Estimate from the last backup; past it, from the files found so far, as the scan is then nearly done
estimate = max(self._estimate, self._total)
if self._estimate > 0 and self._checked > 0:
# The real total is still unknown, so never claim 100%
percent = min(self._checked * 100 // estimate, 99)
text = f'{f"~{percent}":>3}% {self._checked:,}/~{estimate:,} files {size} copied'
if percent > 0:
text += f' ETA ~{self._eta(self._estimate)}'
text += f' ETA ~{self._eta(estimate)}'
else:
text = f'{self._checked:,} files checked {size} copied (scanning)'
percent = None
+22 -3
View File
@@ -303,12 +303,31 @@ class TestProgressBar:
assert capsys.readouterr().err == \
'\r[#######-----------------------] ~25% 1,000/~4,000 files 2.00M copied ETA ~0:01:30\033[K'
def test_estimate_exceeded(self, capsys: pytest.CaptureFixture[str]) -> None:
def test_estimate_exceeded(self, monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture[str]) -> None:
monkeypatch.setattr(sb, 'get_terminal_size', lambda: os.terminal_size((100, 24)))
bar = sb._ProgressBar(estimate=1000)
# More files than in the last backup: back to plain scanning
# More files than in the last backup: estimate from the files found so far
assert bar.update(' 2.00M 25% 1.47MB/s 0:00:02 (xfr#1, ir-chk=500/2000)')
assert capsys.readouterr().err == '\r1,500 files checked 2.00M copied (scanning)\033[K'
assert capsys.readouterr().err == \
'\r[######################--------] ~75% 1,500/~2,000 files 2.00M copied ETA ~0:00:10\033[K'
def test_estimate_capped_while_scanning(self, monkeypatch: pytest.MonkeyPatch,
capsys: pytest.CaptureFixture[str]) -> None:
monkeypatch.setattr(sb, 'get_terminal_size', lambda: os.terminal_size((100, 24)))
bar = sb._ProgressBar(estimate=1000)
# All files found so far are checked, but rsync may still find more
assert bar.update(' 2.00M 25% 1.47MB/s 0:00:02 (xfr#1, ir-chk=0/2000)')
assert bar.total is None
assert ' ~99% 2,000/~2,000 files 2.00M copied ETA ~0:00:00\033[K' in capsys.readouterr().err
def test_no_estimate_without_last_count(self, capsys: pytest.CaptureFixture[str]) -> None:
bar = sb._ProgressBar()
# The files found so far are only a guess at the total near the end of the scan
assert bar.update(' 2.00M 25% 1.47MB/s 0:00:02 (xfr#1, ir-chk=0/2000)')
assert capsys.readouterr().err == '\r2,000 files checked 2.00M copied (scanning)\033[K'
def test_real_total_replaces_estimate(self, capsys: pytest.CaptureFixture[str]) -> None:
bar = sb._ProgressBar(estimate=4000)