2021-09-12 14:15:40 +00:00
|
|
|
package backup
|
2018-04-22 09:57:20 +00:00
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"sort"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/restic/restic/internal/archiver"
|
|
|
|
"github.com/restic/restic/internal/restic"
|
2021-09-12 14:15:40 +00:00
|
|
|
"github.com/restic/restic/internal/ui"
|
2018-04-22 09:57:20 +00:00
|
|
|
"github.com/restic/restic/internal/ui/termstatus"
|
|
|
|
)
|
|
|
|
|
2021-09-12 14:15:40 +00:00
|
|
|
// TextProgress reports progress for the `backup` command.
|
|
|
|
type TextProgress struct {
|
|
|
|
*ui.Message
|
2018-04-22 09:57:20 +00:00
|
|
|
|
2021-01-26 19:52:00 +00:00
|
|
|
term *termstatus.Terminal
|
2018-04-22 09:57:20 +00:00
|
|
|
}
|
|
|
|
|
2021-08-18 11:07:53 +00:00
|
|
|
// assert that Backup implements the ProgressPrinter interface
|
2021-09-12 14:15:40 +00:00
|
|
|
var _ ProgressPrinter = &TextProgress{}
|
2021-08-18 11:07:53 +00:00
|
|
|
|
2021-09-12 14:15:40 +00:00
|
|
|
// NewTextProgress returns a new backup progress reporter.
|
|
|
|
func NewTextProgress(term *termstatus.Terminal, verbosity uint) *TextProgress {
|
|
|
|
return &TextProgress{
|
2022-12-28 20:55:02 +00:00
|
|
|
Message: ui.NewMessage(term, verbosity),
|
|
|
|
term: term,
|
2018-04-22 09:57:20 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-09-12 14:15:40 +00:00
|
|
|
// Update updates the status lines.
|
|
|
|
func (b *TextProgress) Update(total, processed Counter, errors uint, currentFiles map[string]struct{}, start time.Time, secs uint64) {
|
2018-04-22 09:57:20 +00:00
|
|
|
var status string
|
|
|
|
if total.Files == 0 && total.Dirs == 0 {
|
|
|
|
// no total count available yet
|
|
|
|
status = fmt.Sprintf("[%s] %v files, %s, %d errors",
|
2022-10-21 15:34:14 +00:00
|
|
|
ui.FormatDuration(time.Since(start)),
|
|
|
|
processed.Files, ui.FormatBytes(processed.Bytes), errors,
|
2018-04-22 09:57:20 +00:00
|
|
|
)
|
|
|
|
} else {
|
2018-05-06 18:20:10 +00:00
|
|
|
var eta, percent string
|
2018-04-22 09:57:20 +00:00
|
|
|
|
2018-04-29 12:40:49 +00:00
|
|
|
if secs > 0 && processed.Bytes < total.Bytes {
|
2022-10-21 15:34:14 +00:00
|
|
|
eta = fmt.Sprintf(" ETA %s", ui.FormatSeconds(secs))
|
|
|
|
percent = ui.FormatPercent(processed.Bytes, total.Bytes)
|
2018-05-06 18:20:10 +00:00
|
|
|
percent += " "
|
2018-04-22 09:57:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// include totals
|
2018-05-06 18:20:10 +00:00
|
|
|
status = fmt.Sprintf("[%s] %s%v files %s, total %v files %v, %d errors%s",
|
2022-10-21 15:34:14 +00:00
|
|
|
ui.FormatDuration(time.Since(start)),
|
2018-05-06 18:20:10 +00:00
|
|
|
percent,
|
2018-04-22 09:57:20 +00:00
|
|
|
processed.Files,
|
2022-10-21 15:34:14 +00:00
|
|
|
ui.FormatBytes(processed.Bytes),
|
2018-04-22 09:57:20 +00:00
|
|
|
total.Files,
|
2022-10-21 15:34:14 +00:00
|
|
|
ui.FormatBytes(total.Bytes),
|
2018-04-22 09:57:20 +00:00
|
|
|
errors,
|
|
|
|
eta,
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
lines := make([]string, 0, len(currentFiles)+1)
|
|
|
|
for filename := range currentFiles {
|
|
|
|
lines = append(lines, filename)
|
|
|
|
}
|
2019-06-30 20:20:32 +00:00
|
|
|
sort.Strings(lines)
|
2018-04-22 09:57:20 +00:00
|
|
|
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.
|
2023-05-18 17:27:38 +00:00
|
|
|
func (b *TextProgress) ScannerError(_ string, err error) error {
|
2018-04-22 09:57:20 +00:00
|
|
|
b.V("scan: %v\n", err)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Error is the error callback function for the archiver, it prints the error and returns nil.
|
2023-05-18 17:27:38 +00:00
|
|
|
func (b *TextProgress) Error(_ string, err error) error {
|
2018-04-22 09:57:20 +00:00
|
|
|
b.E("error: %v\n", err)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2018-08-11 05:34:37 +00:00
|
|
|
// CompleteItem is the status callback function for the archiver when a
|
2018-04-22 09:57:20 +00:00
|
|
|
// file/dir has been saved successfully.
|
2023-05-18 17:29:50 +00:00
|
|
|
func (b *TextProgress) CompleteItem(messageType, item string, s archiver.ItemStats, d time.Duration) {
|
2023-02-11 13:51:58 +00:00
|
|
|
item = termstatus.Quote(item)
|
|
|
|
|
2021-01-26 19:52:00 +00:00
|
|
|
switch messageType {
|
|
|
|
case "dir new":
|
2022-10-21 15:34:14 +00:00
|
|
|
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))
|
2021-01-26 19:52:00 +00:00
|
|
|
case "dir unchanged":
|
|
|
|
b.VV("unchanged %v", item)
|
|
|
|
case "dir modified":
|
2022-10-21 15:34:14 +00:00
|
|
|
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))
|
2021-01-26 19:52:00 +00:00
|
|
|
case "file new":
|
2022-10-21 15:34:14 +00:00
|
|
|
b.VV("new %v, saved in %.3fs (%v added)", item,
|
|
|
|
d.Seconds(), ui.FormatBytes(s.DataSize))
|
2021-01-26 19:52:00 +00:00
|
|
|
case "file unchanged":
|
|
|
|
b.VV("unchanged %v", item)
|
|
|
|
case "file modified":
|
2022-10-21 15:34:14 +00:00
|
|
|
b.VV("modified %v, saved in %.3fs (%v added, %v stored)", item,
|
|
|
|
d.Seconds(), ui.FormatBytes(s.DataSize), ui.FormatBytes(s.DataSizeInRepo))
|
2018-04-22 09:57:20 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// ReportTotal sets the total stats up to now
|
2023-05-18 17:29:50 +00:00
|
|
|
func (b *TextProgress) ReportTotal(start time.Time, s archiver.ScanStats) {
|
2021-08-17 23:25:34 +00:00
|
|
|
b.V("scan finished in %.3fs: %v files, %s",
|
|
|
|
time.Since(start).Seconds(),
|
2022-10-21 15:34:14 +00:00
|
|
|
s.Files, ui.FormatBytes(s.Bytes),
|
2021-08-17 23:25:34 +00:00
|
|
|
)
|
2018-04-22 09:57:20 +00:00
|
|
|
}
|
|
|
|
|
2021-01-26 19:52:00 +00:00
|
|
|
// Reset status
|
2021-09-12 14:15:40 +00:00
|
|
|
func (b *TextProgress) Reset() {
|
2021-01-26 19:52:00 +00:00
|
|
|
if b.term.CanUpdateStatus() {
|
|
|
|
b.term.SetStatus([]string{""})
|
|
|
|
}
|
|
|
|
}
|
2018-04-29 13:01:21 +00:00
|
|
|
|
2021-01-26 19:52:00 +00:00
|
|
|
// Finish prints the finishing messages.
|
2024-02-22 21:14:48 +00:00
|
|
|
func (b *TextProgress) Finish(_ restic.ID, start time.Time, summary *archiver.Summary, dryRun bool) {
|
2018-05-01 20:02:48 +00:00
|
|
|
b.P("\n")
|
2021-01-26 19:52:00 +00:00
|
|
|
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)
|
backup: add --dry-run/-n flag to show what would happen.
This can be used to check how large a backup is or validate exclusions.
It does not actually write any data to the underlying backend. This is
implemented as a simple overlay backend that accepts writes without
forwarding them, passes through reads, and generally does the minimal
necessary to pretend that progress is actually happening.
Fixes #1542
Example usage:
$ restic -vv --dry-run . | grep add
new /changelog/unreleased/issue-1542, saved in 0.000s (350 B added)
modified /cmd/restic/cmd_backup.go, saved in 0.000s (16.543 KiB added)
modified /cmd/restic/global.go, saved in 0.000s (0 B added)
new /internal/backend/dry/dry_backend_test.go, saved in 0.000s (3.866 KiB added)
new /internal/backend/dry/dry_backend.go, saved in 0.000s (3.744 KiB added)
modified /internal/backend/test/tests.go, saved in 0.000s (0 B added)
modified /internal/repository/repository.go, saved in 0.000s (20.707 KiB added)
modified /internal/ui/backup.go, saved in 0.000s (9.110 KiB added)
modified /internal/ui/jsonstatus/status.go, saved in 0.001s (11.055 KiB added)
modified /restic, saved in 0.131s (25.542 MiB added)
Would add to the repo: 25.892 MiB
2019-06-13 03:39:13 +00:00
|
|
|
verb := "Added"
|
2021-08-18 11:03:08 +00:00
|
|
|
if dryRun {
|
backup: add --dry-run/-n flag to show what would happen.
This can be used to check how large a backup is or validate exclusions.
It does not actually write any data to the underlying backend. This is
implemented as a simple overlay backend that accepts writes without
forwarding them, passes through reads, and generally does the minimal
necessary to pretend that progress is actually happening.
Fixes #1542
Example usage:
$ restic -vv --dry-run . | grep add
new /changelog/unreleased/issue-1542, saved in 0.000s (350 B added)
modified /cmd/restic/cmd_backup.go, saved in 0.000s (16.543 KiB added)
modified /cmd/restic/global.go, saved in 0.000s (0 B added)
new /internal/backend/dry/dry_backend_test.go, saved in 0.000s (3.866 KiB added)
new /internal/backend/dry/dry_backend.go, saved in 0.000s (3.744 KiB added)
modified /internal/backend/test/tests.go, saved in 0.000s (0 B added)
modified /internal/repository/repository.go, saved in 0.000s (20.707 KiB added)
modified /internal/ui/backup.go, saved in 0.000s (9.110 KiB added)
modified /internal/ui/jsonstatus/status.go, saved in 0.001s (11.055 KiB added)
modified /restic, saved in 0.131s (25.542 MiB added)
Would add to the repo: 25.892 MiB
2019-06-13 03:39:13 +00:00
|
|
|
verb = "Would add"
|
|
|
|
}
|
2022-10-21 15:34:14 +00:00
|
|
|
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))
|
2018-05-01 20:02:48 +00:00
|
|
|
b.P("\n")
|
|
|
|
b.P("processed %v files, %v in %s",
|
2021-01-26 19:52:00 +00:00
|
|
|
summary.Files.New+summary.Files.Changed+summary.Files.Unchanged,
|
2022-10-21 15:34:14 +00:00
|
|
|
ui.FormatBytes(summary.ProcessedBytes),
|
|
|
|
ui.FormatDuration(time.Since(start)),
|
2018-05-01 20:02:48 +00:00
|
|
|
)
|
2018-04-22 09:57:20 +00:00
|
|
|
}
|