Files
kopia/cli/command_backups.go
2017-08-01 08:29:41 +02:00

135 lines
2.9 KiB
Go

package cli
import (
"fmt"
"log"
"path/filepath"
"sort"
"strings"
"github.com/kopia/kopia/internal/units"
"github.com/kopia/kopia/snapshot"
kingpin "gopkg.in/alecthomas/kingpin.v2"
)
var (
backupsCommand = app.Command("backups", "List history of file or directory backups.")
backupsPath = backupsCommand.Arg("source", "File or directory to show history of.").String()
maxResultsPerPath = backupsCommand.Flag("maxresults", "Maximum number of results.").Default("100").Int()
)
func findBackups(mgr *snapshot.Manager, sourceInfo snapshot.SourceInfo) (manifestIDs []string, relPath string, err error) {
for len(sourceInfo.Path) > 0 {
list, err := mgr.ListSnapshotManifests(&sourceInfo, -1)
if err != nil {
return nil, "", err
}
if len(list) > 0 {
return list, relPath, nil
}
if len(relPath) > 0 {
relPath = filepath.Base(sourceInfo.Path) + "/" + relPath
} else {
relPath = filepath.Base(sourceInfo.Path)
}
log.Printf("No backups of %v@%v:%v", sourceInfo.UserName, sourceInfo.Host, sourceInfo.Path)
parentPath := filepath.Dir(sourceInfo.Path)
if parentPath == sourceInfo.Path {
break
}
sourceInfo.Path = parentPath
}
return nil, "", nil
}
func runBackupsCommand(context *kingpin.ParseContext) error {
conn := mustOpenConnection()
defer conn.Close()
mgr := snapshot.NewManager(conn)
var previous []string
var relPath string
var err error
if *backupsPath != "" {
si, err := snapshot.ParseSourceInfo(*backupsPath, getHostName(), getUserName())
if err != nil {
return fmt.Errorf("invalid directory: '%s': %s", *backupsPath, err)
}
previous, relPath, err = findBackups(mgr, si)
if relPath != "" {
relPath = "/" + relPath
}
} else {
previous, err = mgr.ListSnapshotManifests(nil, -1)
}
if err != nil {
return fmt.Errorf("cannot list backups: %v", err)
}
var lastSource snapshot.SourceInfo
var count int
manifests, err := mgr.LoadSnapshots(previous)
if err != nil {
return err
}
sort.Sort(manifestSorter(manifests))
for _, m := range manifests {
if m.Source != lastSource {
fmt.Printf("\n%v\n", m.Source)
lastSource = m.Source
count = 0
}
if count < *maxResultsPerPath {
fmt.Printf(
" %v%v %v %10v %v\n",
m.RootObjectID,
relPath,
m.StartTime.Format("2006-01-02 15:04:05 MST"),
units.BytesString(m.Stats.TotalFileSize),
deltaBytes(m.Stats.Repository.WrittenBytes),
)
count++
}
}
return nil
}
type manifestSorter []*snapshot.Manifest
func (b manifestSorter) Len() int { return len(b) }
func (b manifestSorter) Less(i, j int) bool {
if c := strings.Compare(b[i].Source.String(), b[j].Source.String()); c != 0 {
return c < 0
}
return b[i].StartTime.UnixNano() < b[j].StartTime.UnixNano()
}
func (b manifestSorter) Swap(i, j int) { b[i], b[j] = b[j], b[i] }
func deltaBytes(b int64) string {
if b > 0 {
return "(+" + units.BytesString(b) + ")"
}
return "(no change)"
}
func init() {
backupsCommand.Action(runBackupsCommand)
}