Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
52cafd1cd3 |
@@ -836,9 +836,8 @@ Each display is one line holding:
|
||||
directories or groups. It covers the items completed over the last 5 seconds,
|
||||
or, when stderr is not a terminal, since the line before, so it falls to 0
|
||||
while nothing completes, and a burst of items after a wait shows as a burst
|
||||
rather than as a steady rate.
|
||||
- once no item has completed for 10 seconds, how long that has been and what the
|
||||
phase is doing (see below)
|
||||
rather than as a steady rate. Once no item has completed for 10 seconds, the
|
||||
word `stalled` and what the phase is doing take the rate's place (see below).
|
||||
- elapsed time
|
||||
- estimated time remaining, or `unknown`. It comes from the phase's average rate
|
||||
since it started counting, so that it does not swing with each burst.
|
||||
@@ -848,17 +847,19 @@ Example shape (exact layout is flexible, content is not):
|
||||
```
|
||||
[3/5] hash: [12345/98765] 12% |████ | 92 files/s elapsed 2:32 eta 17:54
|
||||
[2/5] walk: [1234567/~1200000] 103%, 5120 files/s, elapsed 4m1s, eta unknown
|
||||
[3/5] hash: [12345/98765] 12%, 0 files/s, no files finished for 42s, reading files, elapsed 4m1s, eta 28m7s
|
||||
[3/5] hash: [12345/45678] 27%, stalled reading, elapsed 4m30s, eta 12m9s
|
||||
```
|
||||
|
||||
When no item has completed for 10 seconds, as while one large file is read, a
|
||||
disk is slow or the database is busy, the line says so and what the phase is
|
||||
doing: `walk` is listing directories; `hash` and the content phase's reads are
|
||||
reading files; `update` is writing to the database; `load` and the `read` phases
|
||||
of `trees` and `report` are reading the database; the content phase's checks are
|
||||
searching the database; `trees`' `group` is grouping directories, and its
|
||||
`write` is writing the report. A phase still waiting for its total already says
|
||||
what it waits for, and says nothing more.
|
||||
disk is slow or the database is busy, the line says `stalled` and what the phase
|
||||
is doing in place of the rate, which would read 0: `walk` is `listing`
|
||||
directories; `hash` and the content phase's reads are `reading` files; `update`
|
||||
is `writing` to the database; `load` and the `read` phase of `trees` are
|
||||
`reading` the database; the content phase's checks are `searching` the database;
|
||||
`trees`' `group` is `grouping` directories, and its `write` is `writing` the
|
||||
report. The `read` phase of `report` writes the report while it reads the
|
||||
records and may be waiting on either, so it says `read/write`. A phase still
|
||||
waiting for its total already says what it waits for, and says nothing more.
|
||||
|
||||
Additional requirements:
|
||||
|
||||
@@ -871,16 +872,18 @@ Additional requirements:
|
||||
times a second, so its elapsed time keeps moving while a phase waits for its
|
||||
next item.
|
||||
- On a terminal no line reaches the terminal's last column, however narrow the
|
||||
terminal is. The phase and its place, the progress, the elapsed time and the
|
||||
ETA are kept whole: a line too wide for the terminal leaves out its rate first
|
||||
(or what the phase is waiting for), then what it says of 10 seconds with no
|
||||
item completed, and a bar with no room beside those four gives way to the
|
||||
spinner. Only when those four alone are too wide, as on a terminal narrower
|
||||
than about 70 columns hours into a scan of millions of files, is the line cut
|
||||
short at the terminal's edge, so the end of the ETA is lost first. A terminal
|
||||
made narrower while a phase runs gets the same: the next draw erases the line
|
||||
within the new width and fits the line to it, and the display goes on
|
||||
redrawing in place.
|
||||
terminal is. The phase and its place, the progress, what a stalled phase is
|
||||
doing, the elapsed time and the ETA are kept whole: a line too wide for the
|
||||
terminal leaves out its rate (or what the phase is waiting for), and a bar
|
||||
with no room beside the parts kept whole gives way to the spinner. The words a
|
||||
stalled phase shows are short, so that a line like the stalled one in the
|
||||
example above, with tens of thousands of files and times in minutes, fits on
|
||||
an 80-column terminal. Only when the parts kept whole are too wide, as on a
|
||||
terminal narrower than about 70 columns hours into a scan of millions of
|
||||
files, or about 90 once the phase has stalled, is the line cut short at the
|
||||
terminal's edge, so the end of the ETA is lost first. A terminal made narrower
|
||||
while a phase runs gets the same: the next draw erases the line within the new
|
||||
width and fits the line to it, and the display goes on redrawing in place.
|
||||
- When stderr is not a terminal (a pipe, a file, `/dev/null`), do not emit ANSI
|
||||
redraws: print the line the moment each phase starts, every 5 seconds while it
|
||||
runs (whether or not an item has completed, and never more often), and once
|
||||
|
||||
@@ -33,11 +33,13 @@
|
||||
seconds, or since the line before when stderr is not a terminal, so it falls
|
||||
to 0 while nothing completes, and a burst after a wait no longer reads as a
|
||||
steady rate of over 1,000 files a second; the ETA still comes from the phase's
|
||||
average. Once no item has completed for 10 seconds, the line says how long and
|
||||
what the phase is doing, as `no files finished for 42s, reading files`, each
|
||||
phase naming its own work where it is defined. A line too wide for the
|
||||
terminal leaves that out after the rate, keeping the phase, progress, elapsed
|
||||
time and ETA whole (2026-10-16, https://git.eeqj.de/sneak/sfdupes/issues/125)
|
||||
average. Once no item has completed for 10 seconds, the word `stalled` and
|
||||
what the phase is doing take the rate's place, as `stalled reading`, each
|
||||
phase naming its own work where it is defined; `report`'s `read` says
|
||||
`read/write`, as it writes the report while it reads. Those words are kept
|
||||
whole with the phase, progress, elapsed time and ETA, so only the bar gives
|
||||
way before them, and they fit on an 80-column terminal in an ordinary scan
|
||||
(2026-10-16, https://git.eeqj.de/sneak/sfdupes/issues/125)
|
||||
|
||||
- new subcommand `info` prints the database's details on stdout, one per line as
|
||||
a name, a tab and the value: its path and where it came from, the sizes of the
|
||||
|
||||
+27
-30
@@ -28,7 +28,7 @@ const redrawInterval = 100 * time.Millisecond
|
||||
const rateWindow = 5 * time.Second
|
||||
|
||||
// stallTime is how long a phase goes with no item completed before its
|
||||
// line says so, and what the phase is doing.
|
||||
// line says it has stalled, and what it is doing, in place of the rate.
|
||||
const stallTime = 10 * time.Second
|
||||
|
||||
// spinnerType selects the progressbar library's spinner style.
|
||||
@@ -68,9 +68,9 @@ const (
|
||||
openingDatabase = "opening the database"
|
||||
)
|
||||
|
||||
// readingDatabase is what the display of a phase that reads the records
|
||||
// says it is doing once no record has been read for stallTime.
|
||||
const readingDatabase = "reading the database"
|
||||
// reading is what the display of a phase that reads files or records
|
||||
// says it is doing once it has stalled; see stallTime.
|
||||
const reading = "reading"
|
||||
|
||||
// stderrIsTTY reports whether stderr is attached to a terminal.
|
||||
func stderrIsTTY() bool {
|
||||
@@ -102,7 +102,7 @@ type progressParams struct {
|
||||
total int64 // the items the phase will complete; -1 when not known
|
||||
estimate bool // total is an estimate, which the count may pass
|
||||
waitingFor string // what the phase waits for until setTotal is called
|
||||
doing string // what the phase is doing, as "reading files"; see stallTime
|
||||
doing string // what the phase is doing, as "reading"; see stallTime
|
||||
}
|
||||
|
||||
// drawnCount is the count of items completed as of one draw, and when
|
||||
@@ -448,24 +448,23 @@ func (p *progress) finish() {
|
||||
// with no total shows the items done instead, or what it waits for. The
|
||||
// rate is that since the first draw in drawn, and the ETA comes from
|
||||
// the rate since the count started. Once no item has been completed for
|
||||
// stallTime, the line says so before the elapsed time, and what the
|
||||
// phase is doing, as "no files finished for 42s, reading files".
|
||||
// stallTime, the rate, which would read 0, gives way to the word
|
||||
// "stalled" and what the phase is doing, as "stalled reading".
|
||||
//
|
||||
// The phase and its place, the progress, the elapsed time and the ETA
|
||||
// are kept whole. A line wider than room leaves out the rate, or what
|
||||
// the phase waits for, then what it says of a stall; only if it is
|
||||
// still wider is it cut at room, and line reports that it does not fit.
|
||||
// Every part is ASCII, so its length is its width in columns. p.mu must
|
||||
// be held.
|
||||
// The phase and its place, the progress, what a stalled phase is doing,
|
||||
// the elapsed time and the ETA are kept whole. A line wider than room
|
||||
// leaves out the rate, or what the phase waits for; only if it is still
|
||||
// wider is it cut at room, and line reports that it does not fit. Every
|
||||
// part is ASCII, so its length is its width in columns. p.mu must be
|
||||
// held.
|
||||
func (p *progress) line(now time.Time, room int,
|
||||
percent bool,
|
||||
) (string, bool) {
|
||||
elapsed := now.Sub(p.start).Round(time.Second)
|
||||
|
||||
// head and tail hold the parts kept whole. extra, after head, is the
|
||||
// part left out first, and stall, before tail, the part left out
|
||||
// next.
|
||||
var head, extra, stall, tail string
|
||||
// head and tail hold the parts kept whole; extra, between them, is
|
||||
// the part left out.
|
||||
var head, extra, tail string
|
||||
|
||||
if p.total < 0 && p.waitingFor != "" {
|
||||
head, extra = p.label+": ", p.waitingFor+", "
|
||||
@@ -477,24 +476,22 @@ func (p *progress) line(now time.Time, room int,
|
||||
|
||||
head = p.label + ": " + p.itemsDone(percent) + ", "
|
||||
extra = fmt.Sprintf("%.0f %s/s, ", rate, p.unit)
|
||||
|
||||
if idle := now.Sub(p.lastItem); idle >= stallTime {
|
||||
stall = fmt.Sprintf("no %s finished for %s, %s, ",
|
||||
p.unit, idle.Round(time.Second), p.doing)
|
||||
}
|
||||
|
||||
tail = fmt.Sprintf("elapsed %s, eta %s", elapsed, p.eta(average))
|
||||
}
|
||||
|
||||
for _, text := range []string{
|
||||
head + extra + stall + tail, head + stall + tail, head + tail,
|
||||
} {
|
||||
if len(text) <= room {
|
||||
return text, true
|
||||
if now.Sub(p.lastItem) >= stallTime {
|
||||
head += "stalled " + p.doing + ", "
|
||||
extra = ""
|
||||
}
|
||||
}
|
||||
|
||||
kept := head + tail
|
||||
full, kept := head+extra+tail, head+tail
|
||||
|
||||
switch {
|
||||
case len(full) <= room:
|
||||
return full, true
|
||||
case len(kept) <= room:
|
||||
return kept, true
|
||||
}
|
||||
|
||||
return kept[:max(room, 0)], false
|
||||
}
|
||||
|
||||
+77
-28
@@ -167,9 +167,9 @@ func TestProgressLine(t *testing.T) {
|
||||
|
||||
// TestLineFitsRoom checks how a line is fitted to the room it has, ten
|
||||
// seconds into the phase, and fifty into a phase with no file read for
|
||||
// the last 42: it leaves out the rate, or what the phase waits for,
|
||||
// then what it says of the stall, and is cut only when what is left is
|
||||
// still too wide.
|
||||
// the last 42: it leaves out the rate, or what the phase waits for, but
|
||||
// not what a stalled phase is doing, and is cut only when what is left
|
||||
// is still too wide.
|
||||
func TestLineFitsRoom(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
@@ -182,13 +182,13 @@ func TestLineFitsRoom(t *testing.T) {
|
||||
}
|
||||
content := &progress{
|
||||
label: scanPhase("content"), total: -1,
|
||||
waitingFor: searchingDatabase, start: ago, rateStart: ago,
|
||||
waitingFor: "searching the database", start: ago, rateStart: ago,
|
||||
}
|
||||
|
||||
started := now.Add(-50 * time.Second)
|
||||
stalled := &progress{
|
||||
label: scanPhase("hash"), unit: scanUnit, total: 100, count: 20,
|
||||
doing: readingFiles, start: started, rateStart: started,
|
||||
doing: reading, start: started, rateStart: started,
|
||||
lastItem: now.Add(-42 * time.Second),
|
||||
drawn: []drawnCount{{at: now.Add(-rateWindow), count: 20}},
|
||||
}
|
||||
@@ -221,19 +221,15 @@ func TestLineFitsRoom(t *testing.T) {
|
||||
want: "[5/5] content: elapsed 10s, eta unknown",
|
||||
fits: true,
|
||||
}, {
|
||||
p: stalled, room: 101, percent: true,
|
||||
want: "[3/5] hash: [20/100] 20%, 0 files/s, no files finished for " +
|
||||
"42s, reading files, elapsed 50s, eta 3m20s",
|
||||
p: stalled, room: 65, percent: true,
|
||||
want: "[3/5] hash: [20/100] 20%, stalled reading, elapsed 50s, " +
|
||||
"eta 3m20s",
|
||||
fits: true,
|
||||
}, {
|
||||
p: stalled, room: 100, percent: true,
|
||||
want: "[3/5] hash: [20/100] 20%, no files finished for 42s, " +
|
||||
"reading files, elapsed 50s, eta 3m20s",
|
||||
fits: true,
|
||||
}, {
|
||||
p: stalled, room: 89, percent: true,
|
||||
want: "[3/5] hash: [20/100] 20%, elapsed 50s, eta 3m20s",
|
||||
fits: true,
|
||||
p: stalled, room: 64, percent: true,
|
||||
want: "[3/5] hash: [20/100] 20%, stalled reading, elapsed 50s, " +
|
||||
"eta 3m20",
|
||||
fits: false,
|
||||
}}
|
||||
|
||||
for _, tc := range cases {
|
||||
@@ -255,7 +251,7 @@ func TestSetTotalStartsRate(t *testing.T) {
|
||||
waited := clock.Now().Add(-20 * time.Second)
|
||||
p := &progress{
|
||||
now: clock.Now, label: scanPhase("load"), unit: scanUnit, total: -1,
|
||||
doing: readingDatabase, start: waited, rateStart: waited,
|
||||
doing: reading, start: waited, rateStart: waited,
|
||||
lastItem: waited, drawn: []drawnCount{{at: waited}},
|
||||
}
|
||||
|
||||
@@ -264,8 +260,8 @@ func TestSetTotalStartsRate(t *testing.T) {
|
||||
// Ten seconds after the total came, no record has been read yet.
|
||||
clock.Add(10 * time.Second)
|
||||
|
||||
want := "[1/5] load: [0/100] 0%, 0 files/s, no files finished for 10s, " +
|
||||
"reading the database, elapsed 30s, eta unknown"
|
||||
want := "[1/5] load: [0/100] 0%, stalled reading, elapsed 30s, " +
|
||||
"eta unknown"
|
||||
if got, _ := p.line(clock.Now(), math.MaxInt, true); got != want {
|
||||
t.Errorf("line = %q, want %q", got, want)
|
||||
}
|
||||
@@ -347,8 +343,8 @@ func TestPlainProgressPrintsWhileWaiting(t *testing.T) {
|
||||
// coming, as when one large file is being read or a disk is slow, goes
|
||||
// on printing its line every interval: the rate falls to 0 once no file
|
||||
// has been read for rateWindow, and once none has for stallTime the
|
||||
// line says so, and what the phase is doing. The test moves the
|
||||
// display's clock by hand.
|
||||
// line says the phase has stalled, and what it is doing, in place of
|
||||
// the rate. The test moves the display's clock by hand.
|
||||
//
|
||||
//nolint:paralleltest // captureStderr replaces the process-wide os.Stderr
|
||||
func TestPlainProgressStalledProducer(t *testing.T) {
|
||||
@@ -356,7 +352,7 @@ func TestPlainProgressStalledProducer(t *testing.T) {
|
||||
clock := &testClock{now: time.Now()}
|
||||
p := startProgress(progressParams{
|
||||
label: scanPhase("hash"), unit: scanUnit, total: 100,
|
||||
doing: readingFiles,
|
||||
doing: reading,
|
||||
}, nil, testRedraw, clock.Now)
|
||||
|
||||
// Pass or fail, the display ends before stderr is put back.
|
||||
@@ -377,8 +373,8 @@ func TestPlainProgressStalledProducer(t *testing.T) {
|
||||
|
||||
clock.Add(4 * time.Second)
|
||||
awaitOutput(t, stderr,
|
||||
"[3/5] hash: [20/100] 20%, 0 files/s, no files finished for 10s, "+
|
||||
"reading files, elapsed 10s, eta 40s\n", 3)
|
||||
"[3/5] hash: [20/100] 20%, stalled reading, elapsed 10s, eta 40s\n",
|
||||
3)
|
||||
}
|
||||
|
||||
// TestPlainProgressRateAfterStall draws a phase's plain line by hand, a
|
||||
@@ -395,7 +391,7 @@ func TestPlainProgressRateAfterStall(t *testing.T) {
|
||||
// No timed draws: the test draws.
|
||||
p := startProgress(progressParams{
|
||||
label: scanPhase("hash"), unit: scanUnit, total: 1000,
|
||||
doing: readingFiles,
|
||||
doing: reading,
|
||||
}, nil, time.Hour, clock.Now)
|
||||
|
||||
late := plainInterval + time.Millisecond
|
||||
@@ -415,8 +411,7 @@ func TestPlainProgressRateAfterStall(t *testing.T) {
|
||||
want := []string{
|
||||
"[3/5] hash: [0/1000] 0%, 0 files/s, elapsed 0s, eta unknown",
|
||||
"[3/5] hash: [0/1000] 0%, 0 files/s, elapsed 5s, eta unknown",
|
||||
"[3/5] hash: [0/1000] 0%, 0 files/s, no files finished for 10s, " +
|
||||
"reading files, elapsed 10s, eta unknown",
|
||||
"[3/5] hash: [0/1000] 0%, stalled reading, elapsed 10s, eta unknown",
|
||||
"[3/5] hash: [100/1000] 10%, 20 files/s, elapsed 15s, eta 2m15s",
|
||||
}
|
||||
|
||||
@@ -441,7 +436,7 @@ func TestProgressRateAfterStallRedrawn(t *testing.T) {
|
||||
// No timed draws: the test draws.
|
||||
p := startProgress(progressParams{
|
||||
label: scanPhase("hash"), unit: scanUnit, total: 1000,
|
||||
doing: readingFiles,
|
||||
doing: reading,
|
||||
}, nil, time.Hour, clock.Now)
|
||||
|
||||
// redraw draws the display every redrawInterval for d.
|
||||
@@ -604,6 +599,60 @@ func TestTerminalLinesKeepETA(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestTerminalStalledLineKeepsWords draws a stalled phase on an
|
||||
// 80-column terminal at an ordinary scan's counts and times, 12345 of
|
||||
// 45678 files read four and a half minutes in, the last 42 seconds ago,
|
||||
// and report's read phase at the same counts: the bar gives way, and
|
||||
// what the phase is doing shows whole in place of the rate, beside the
|
||||
// phase, progress, elapsed time and ETA.
|
||||
//
|
||||
//nolint:paralleltest // captureStderr replaces the process-wide os.Stderr
|
||||
func TestTerminalStalledLineKeepsWords(t *testing.T) {
|
||||
stderr := captureStderr(t)
|
||||
|
||||
cases := []struct {
|
||||
params progressParams
|
||||
want string
|
||||
}{{
|
||||
params: progressParams{
|
||||
label: scanPhase("hash"), unit: scanUnit, total: 45678,
|
||||
doing: reading,
|
||||
},
|
||||
want: "[3/5] hash: [12345/45678] 27%, stalled reading, " +
|
||||
"elapsed 4m30s, eta 12m9s",
|
||||
}, {
|
||||
params: progressParams{
|
||||
label: reportPhase("read"), total: 45678, doing: "read/write",
|
||||
},
|
||||
want: "[2/2] read: [12345/45678] 27%, stalled read/write, " +
|
||||
"elapsed 4m30s, eta 12m9s",
|
||||
}}
|
||||
|
||||
for _, tc := range cases {
|
||||
before := len(stderr())
|
||||
clock := &testClock{now: time.Now()}
|
||||
|
||||
// No timed draws: the display is drawn as it starts and as it
|
||||
// ends.
|
||||
p := startProgress(tc.params, func() int { return 80 }, time.Hour,
|
||||
clock.Now)
|
||||
|
||||
clock.Add(4*time.Minute + 30*time.Second - 42*time.Second)
|
||||
|
||||
p.mu.Lock()
|
||||
p.count, p.lastItem = 12345, clock.Now()
|
||||
p.mu.Unlock()
|
||||
|
||||
clock.Add(42 * time.Second)
|
||||
p.finish()
|
||||
|
||||
shown := lastFrame(stderr()[before:])
|
||||
if !strings.Contains(shown, tc.want) {
|
||||
t.Errorf("terminal shows %q, want %q", shown, tc.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestTerminalLinesFitWidth draws a large scan's bar display and a small
|
||||
// scan's, two and a half hours into the phase, on terminals from 120
|
||||
// columns down to 40, and checks that every line stays clear of the
|
||||
|
||||
@@ -74,9 +74,11 @@ func runReport(ctx context.Context, stdout io.Writer) error {
|
||||
|
||||
defer func() { _ = db.Close() }()
|
||||
|
||||
// The read phase writes the report while it reads the records, so a
|
||||
// stall may be in either.
|
||||
prog = newProgress(progressParams{
|
||||
label: reportPhase("read"), unit: "records", total: -1,
|
||||
waitingFor: countingRecords, doing: readingDatabase,
|
||||
waitingFor: countingRecords, doing: "read/write",
|
||||
})
|
||||
report, err := readReport(ctx, db, dbPath, stdout, prog)
|
||||
|
||||
|
||||
@@ -88,15 +88,6 @@ type fileMeta struct {
|
||||
// names it.
|
||||
const scanUnit = "files"
|
||||
|
||||
// readingFiles is what the display of a phase that reads files says it
|
||||
// is doing once no file has been read for stallTime.
|
||||
const readingFiles = "reading files"
|
||||
|
||||
// searchingDatabase is what the display of the content phase's checks
|
||||
// says it waits for until the search returns its first record, and what
|
||||
// it says it is doing once no record has been checked for stallTime.
|
||||
const searchingDatabase = "searching the database"
|
||||
|
||||
// scanPhase names one phase of a scan with its place among all of them,
|
||||
// as "[2/5] walk", for the phase's progress display. The list here is
|
||||
// the one place the phases are counted.
|
||||
@@ -455,7 +446,7 @@ func (s *scanState) loadIndex(ctx context.Context, roots []string) error {
|
||||
// first gives the display its total.
|
||||
prog := newProgress(progressParams{
|
||||
label: scanPhase("load"), unit: scanUnit, total: -1,
|
||||
waitingFor: countingRecords, doing: readingDatabase,
|
||||
waitingFor: countingRecords, doing: reading,
|
||||
})
|
||||
defer prog.finish()
|
||||
|
||||
@@ -506,7 +497,7 @@ func (s *scanState) walkPhase(
|
||||
|
||||
prog := newProgress(progressParams{
|
||||
label: scanPhase("walk"), unit: scanUnit, total: estimate,
|
||||
estimate: true, doing: "listing directories",
|
||||
estimate: true, doing: "listing",
|
||||
})
|
||||
|
||||
var (
|
||||
@@ -698,7 +689,7 @@ func (s *scanState) readRuns(ctx context.Context, workers int,
|
||||
|
||||
prog := newProgress(progressParams{
|
||||
label: scanPhase(label), unit: scanUnit, total: int64(len(runs)),
|
||||
doing: readingFiles,
|
||||
doing: reading,
|
||||
})
|
||||
defer prog.finish()
|
||||
|
||||
@@ -788,7 +779,7 @@ func (s *scanState) updatePhase(ctx context.Context) error {
|
||||
total := len(s.batch) + len(s.sentinels) + len(deletes)
|
||||
prog := newProgress(progressParams{
|
||||
label: scanPhase("update"), unit: scanUnit, total: int64(total),
|
||||
doing: "writing to the database",
|
||||
doing: "writing",
|
||||
})
|
||||
|
||||
defer prog.finish()
|
||||
@@ -877,7 +868,7 @@ func (s *scanState) contentCandidates(
|
||||
// the search returns, and so do their rate and ETA.
|
||||
prog := newProgress(progressParams{
|
||||
label: scanPhase("content"), unit: scanUnit, total: -1,
|
||||
waitingFor: searchingDatabase, doing: searchingDatabase,
|
||||
waitingFor: "searching the database", doing: "searching",
|
||||
})
|
||||
defer prog.finish()
|
||||
|
||||
|
||||
@@ -72,7 +72,7 @@ func runTrees(ctx context.Context, stdout io.Writer) error {
|
||||
|
||||
prog = newProgress(progressParams{
|
||||
label: treesPhase("read"), unit: "records", total: -1,
|
||||
waitingFor: countingRecords, doing: readingDatabase,
|
||||
waitingFor: countingRecords, doing: reading,
|
||||
})
|
||||
records, super, allDirs, err := readTree(ctx, db, prog)
|
||||
|
||||
@@ -84,7 +84,7 @@ func runTrees(ctx context.Context, stdout io.Writer) error {
|
||||
|
||||
prog = newProgress(progressParams{
|
||||
label: treesPhase("group"), unit: "directories",
|
||||
total: int64(len(allDirs)), doing: "grouping directories",
|
||||
total: int64(len(allDirs)), doing: "grouping",
|
||||
})
|
||||
dupes := collectTreeGroups(allDirs, super, prog)
|
||||
|
||||
@@ -92,7 +92,7 @@ func runTrees(ctx context.Context, stdout io.Writer) error {
|
||||
|
||||
prog = newProgress(progressParams{
|
||||
label: treesPhase("write"), unit: "groups", total: int64(len(dupes)),
|
||||
doing: "writing the report",
|
||||
doing: "writing",
|
||||
})
|
||||
dupeTrees, reclaimable, err := writeTreeGroups(stdout, dupes, prog)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user