mirror of
https://github.com/restic/restic.git
synced 2024-12-24 00:37:28 +00:00
ls: Implement directory filter, optionally subfolders
This commit is contained in:
parent
beb208e159
commit
8c146eac4b
1 changed files with 40 additions and 7 deletions
|
@ -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
|
||||||
})
|
})
|
||||||
|
|
Loading…
Reference in a new issue