1
0
Fork 0
mirror of https://github.com/restic/restic.git synced 2024-12-23 16:26:11 +00:00

ls: Implement directory filter, optionally subfolders

This commit is contained in:
Matthew Holt 2018-08-10 19:48:42 -06:00
parent beb208e159
commit 8c146eac4b

View file

@ -2,6 +2,8 @@ package main
import ( import (
"context" "context"
"path/filepath"
"strings"
"github.com/spf13/cobra" "github.com/spf13/cobra"
@ -11,7 +13,7 @@ import (
) )
var cmdLs = &cobra.Command{ var cmdLs = &cobra.Command{
Use: "ls [flags] [snapshot-ID ...]", Use: "ls [flags] [snapshot-ID] [dir...]",
Short: "List files in a snapshot", Short: "List files in a snapshot",
Long: ` Long: `
The "ls" command allows listing files and directories in a snapshot. The "ls" command allows listing files and directories in a snapshot.
@ -26,10 +28,11 @@ The special snapshot-ID "latest" can be used to list files and directories of th
// LsOptions collects all options for the ls command. // LsOptions collects all options for the ls command.
type LsOptions struct { type LsOptions struct {
ListLong bool ListLong bool
Host string Host string
Tags restic.TagLists Tags restic.TagLists
Paths []string Paths []string
Recursive bool
} }
var lsOptions LsOptions var lsOptions LsOptions
@ -43,6 +46,7 @@ func init() {
flags.StringVarP(&lsOptions.Host, "host", "H", "", "only consider snapshots for this `host`, when no snapshot ID is given") flags.StringVarP(&lsOptions.Host, "host", "H", "", "only consider snapshots for this `host`, when no snapshot ID is given")
flags.Var(&lsOptions.Tags, "tag", "only consider snapshots which include this `taglist`, when no snapshot ID is given") flags.Var(&lsOptions.Tags, "tag", "only consider snapshots which include this `taglist`, when no snapshot ID is given")
flags.StringArrayVar(&lsOptions.Paths, "path", nil, "only consider snapshots which include this (absolute) `path`, when no snapshot ID is given") flags.StringArrayVar(&lsOptions.Paths, "path", nil, "only consider snapshots which include this (absolute) `path`, when no snapshot ID is given")
flags.BoolVar(&lsOptions.Recursive, "recursive", false, "include files in subfolders of the listed directories")
} }
func runLs(opts LsOptions, gopts GlobalOptions, args []string) error { func runLs(opts LsOptions, gopts GlobalOptions, args []string) error {
@ -59,19 +63,48 @@ func runLs(opts LsOptions, gopts GlobalOptions, args []string) error {
return err return err
} }
// extract any specific directories to walk
dirs := args[1:]
ctx, cancel := context.WithCancel(gopts.ctx) ctx, cancel := context.WithCancel(gopts.ctx)
defer cancel() defer cancel()
for sn := range FindFilteredSnapshots(ctx, repo, opts.Host, opts.Tags, opts.Paths, args) { for sn := range FindFilteredSnapshots(ctx, repo, opts.Host, opts.Tags, opts.Paths, args[:1]) {
Verbosef("snapshot %s of %v at %s):\n", sn.ID().Str(), sn.Paths, sn.Time) Verbosef("snapshot %s of %v at %s):\n", sn.ID().Str(), sn.Paths, sn.Time)
err := walker.Walk(ctx, repo, *sn.Tree, nil, func(nodepath string, node *restic.Node, err error) (bool, error) { err := walker.Walk(ctx, repo, *sn.Tree, nil, func(nodepath string, node *restic.Node, err error) (bool, error) {
if err != nil { if err != nil {
return false, err return false, err
} }
if node == nil { if node == nil {
return false, nil return false, nil
} }
// apply any directory filters
if len(dirs) > 0 {
var nodeDir string
if !opts.Recursive {
// only needed for exact directory match; i.e. no subfolders
nodeDir = filepath.Dir(nodepath)
}
var match bool
for _, dir := range dirs {
if opts.Recursive {
if strings.HasPrefix(nodepath, dir) {
match = true
break
}
} else {
if nodeDir == dir {
match = true
break
}
}
}
if !match {
return true, nil
}
}
Printf("%s\n", formatNode(nodepath, node, lsOptions.ListLong)) Printf("%s\n", formatNode(nodepath, node, lsOptions.ListLong))
return false, nil return false, nil
}) })