From 8c146eac4b8a936618426ec36d58b3b094b9530e Mon Sep 17 00:00:00 2001 From: Matthew Holt Date: Fri, 10 Aug 2018 19:48:42 -0600 Subject: [PATCH] ls: Implement directory filter, optionally subfolders --- cmd/restic/cmd_ls.go | 47 +++++++++++++++++++++++++++++++++++++------- 1 file changed, 40 insertions(+), 7 deletions(-) diff --git a/cmd/restic/cmd_ls.go b/cmd/restic/cmd_ls.go index f7996b438..cbfe73b42 100644 --- a/cmd/restic/cmd_ls.go +++ b/cmd/restic/cmd_ls.go @@ -2,6 +2,8 @@ package main import ( "context" + "path/filepath" + "strings" "github.com/spf13/cobra" @@ -11,7 +13,7 @@ import ( ) var cmdLs = &cobra.Command{ - Use: "ls [flags] [snapshot-ID ...]", + Use: "ls [flags] [snapshot-ID] [dir...]", Short: "List files in a snapshot", Long: ` 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. type LsOptions struct { - ListLong bool - Host string - Tags restic.TagLists - Paths []string + ListLong bool + Host string + Tags restic.TagLists + Paths []string + Recursive bool } 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.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.BoolVar(&lsOptions.Recursive, "recursive", false, "include files in subfolders of the listed directories") } func runLs(opts LsOptions, gopts GlobalOptions, args []string) error { @@ -59,19 +63,48 @@ func runLs(opts LsOptions, gopts GlobalOptions, args []string) error { return err } + // extract any specific directories to walk + dirs := args[1:] + ctx, cancel := context.WithCancel(gopts.ctx) 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) err := walker.Walk(ctx, repo, *sn.Tree, nil, func(nodepath string, node *restic.Node, err error) (bool, error) { if err != nil { return false, err } - if node == 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)) return false, nil })