aedead2823
To clear the status lines, they should be set to an empty array to prevent future updates of those lines. Setting the status lines to an array containing an empty string is wrong as this causes the output to continuously add that empty status line after each message.
156 lines
4.7 KiB
Go
156 lines
4.7 KiB
Go
package backup
|
|
|
|
import (
|
|
"fmt"
|
|
"sort"
|
|
"time"
|
|
|
|
"github.com/restic/restic/internal/archiver"
|
|
"github.com/restic/restic/internal/restic"
|
|
"github.com/restic/restic/internal/ui"
|
|
"github.com/restic/restic/internal/ui/termstatus"
|
|
)
|
|
|
|
// TextProgress reports progress for the `backup` command.
|
|
type TextProgress struct {
|
|
*ui.Message
|
|
|
|
term *termstatus.Terminal
|
|
}
|
|
|
|
// assert that Backup implements the ProgressPrinter interface
|
|
var _ ProgressPrinter = &TextProgress{}
|
|
|
|
// NewTextProgress returns a new backup progress reporter.
|
|
func NewTextProgress(term *termstatus.Terminal, verbosity uint) *TextProgress {
|
|
return &TextProgress{
|
|
Message: ui.NewMessage(term, verbosity),
|
|
term: term,
|
|
}
|
|
}
|
|
|
|
// Update updates the status lines.
|
|
func (b *TextProgress) Update(total, processed Counter, errors uint, currentFiles map[string]struct{}, start time.Time, secs uint64) {
|
|
var status string
|
|
if total.Files == 0 && total.Dirs == 0 {
|
|
// no total count available yet
|
|
status = fmt.Sprintf("[%s] %v files, %s, %d errors",
|
|
ui.FormatDuration(time.Since(start)),
|
|
processed.Files, ui.FormatBytes(processed.Bytes), errors,
|
|
)
|
|
} else {
|
|
var eta, percent string
|
|
|
|
if secs > 0 && processed.Bytes < total.Bytes {
|
|
eta = fmt.Sprintf(" ETA %s", ui.FormatSeconds(secs))
|
|
percent = ui.FormatPercent(processed.Bytes, total.Bytes)
|
|
percent += " "
|
|
}
|
|
|
|
// include totals
|
|
status = fmt.Sprintf("[%s] %s%v files %s, total %v files %v, %d errors%s",
|
|
ui.FormatDuration(time.Since(start)),
|
|
percent,
|
|
processed.Files,
|
|
ui.FormatBytes(processed.Bytes),
|
|
total.Files,
|
|
ui.FormatBytes(total.Bytes),
|
|
errors,
|
|
eta,
|
|
)
|
|
}
|
|
|
|
lines := make([]string, 0, len(currentFiles)+1)
|
|
for filename := range currentFiles {
|
|
lines = append(lines, filename)
|
|
}
|
|
sort.Strings(lines)
|
|
lines = append([]string{status}, lines...)
|
|
|
|
b.term.SetStatus(lines)
|
|
}
|
|
|
|
// ScannerError is the error callback function for the scanner, it prints the
|
|
// error in verbose mode and returns nil.
|
|
func (b *TextProgress) ScannerError(_ string, err error) error {
|
|
b.V("scan: %v\n", err)
|
|
return nil
|
|
}
|
|
|
|
// Error is the error callback function for the archiver, it prints the error and returns nil.
|
|
func (b *TextProgress) Error(_ string, err error) error {
|
|
b.E("error: %v\n", err)
|
|
return nil
|
|
}
|
|
|
|
// CompleteItem is the status callback function for the archiver when a
|
|
// file/dir has been saved successfully.
|
|
func (b *TextProgress) CompleteItem(messageType, item string, s archiver.ItemStats, d time.Duration) {
|
|
item = termstatus.Quote(item)
|
|
|
|
switch messageType {
|
|
case "dir new":
|
|
b.VV("new %v, saved in %.3fs (%v added, %v stored, %v metadata)",
|
|
item, d.Seconds(), ui.FormatBytes(s.DataSize),
|
|
ui.FormatBytes(s.DataSizeInRepo), ui.FormatBytes(s.TreeSizeInRepo))
|
|
case "dir unchanged":
|
|
b.VV("unchanged %v", item)
|
|
case "dir modified":
|
|
b.VV("modified %v, saved in %.3fs (%v added, %v stored, %v metadata)",
|
|
item, d.Seconds(), ui.FormatBytes(s.DataSize),
|
|
ui.FormatBytes(s.DataSizeInRepo), ui.FormatBytes(s.TreeSizeInRepo))
|
|
case "file new":
|
|
b.VV("new %v, saved in %.3fs (%v added)", item,
|
|
d.Seconds(), ui.FormatBytes(s.DataSize))
|
|
case "file unchanged":
|
|
b.VV("unchanged %v", item)
|
|
case "file modified":
|
|
b.VV("modified %v, saved in %.3fs (%v added, %v stored)", item,
|
|
d.Seconds(), ui.FormatBytes(s.DataSize), ui.FormatBytes(s.DataSizeInRepo))
|
|
}
|
|
}
|
|
|
|
// ReportTotal sets the total stats up to now
|
|
func (b *TextProgress) ReportTotal(start time.Time, s archiver.ScanStats) {
|
|
b.V("scan finished in %.3fs: %v files, %s",
|
|
time.Since(start).Seconds(),
|
|
s.Files, ui.FormatBytes(s.Bytes),
|
|
)
|
|
}
|
|
|
|
// Reset status
|
|
func (b *TextProgress) Reset() {
|
|
if b.term.CanUpdateStatus() {
|
|
b.term.SetStatus(nil)
|
|
}
|
|
}
|
|
|
|
// Finish prints the finishing messages.
|
|
func (b *TextProgress) Finish(id restic.ID, start time.Time, summary *archiver.Summary, dryRun bool) {
|
|
b.P("\n")
|
|
b.P("Files: %5d new, %5d changed, %5d unmodified\n", summary.Files.New, summary.Files.Changed, summary.Files.Unchanged)
|
|
b.P("Dirs: %5d new, %5d changed, %5d unmodified\n", summary.Dirs.New, summary.Dirs.Changed, summary.Dirs.Unchanged)
|
|
b.V("Data Blobs: %5d new\n", summary.ItemStats.DataBlobs)
|
|
b.V("Tree Blobs: %5d new\n", summary.ItemStats.TreeBlobs)
|
|
verb := "Added"
|
|
if dryRun {
|
|
verb = "Would add"
|
|
}
|
|
b.P("%s to the repository: %-5s (%-5s stored)\n", verb,
|
|
ui.FormatBytes(summary.ItemStats.DataSize+summary.ItemStats.TreeSize),
|
|
ui.FormatBytes(summary.ItemStats.DataSizeInRepo+summary.ItemStats.TreeSizeInRepo))
|
|
b.P("\n")
|
|
b.P("processed %v files, %v in %s",
|
|
summary.Files.New+summary.Files.Changed+summary.Files.Unchanged,
|
|
ui.FormatBytes(summary.ProcessedBytes),
|
|
ui.FormatDuration(time.Since(start)),
|
|
)
|
|
|
|
if !dryRun {
|
|
if id.IsNull() {
|
|
b.P("skipped creating snapshot\n")
|
|
} else {
|
|
b.P("snapshot %s saved\n", id.Str())
|
|
}
|
|
}
|
|
}
|