forked from TrueCloudLab/restic
Merge pull request #4911 from MichaelEischer/fix-filtered-ls-ncdu
ls: Fix --ncdu output with path filters and fix disk size
This commit is contained in:
commit
5e1d0ffd99
4 changed files with 43 additions and 21 deletions
|
@ -9,3 +9,4 @@ You can use it as follows: `restic ls latest --ncdu | ncdu -f -`
|
||||||
|
|
||||||
https://github.com/restic/restic/issues/4549
|
https://github.com/restic/restic/issues/4549
|
||||||
https://github.com/restic/restic/pull/4550
|
https://github.com/restic/restic/pull/4550
|
||||||
|
https://github.com/restic/restic/pull/4911
|
||||||
|
|
|
@ -71,7 +71,7 @@ func init() {
|
||||||
|
|
||||||
type lsPrinter interface {
|
type lsPrinter interface {
|
||||||
Snapshot(sn *restic.Snapshot)
|
Snapshot(sn *restic.Snapshot)
|
||||||
Node(path string, node *restic.Node)
|
Node(path string, node *restic.Node, isPrefixDirectory bool)
|
||||||
LeaveDir(path string)
|
LeaveDir(path string)
|
||||||
Close()
|
Close()
|
||||||
}
|
}
|
||||||
|
@ -102,7 +102,10 @@ func (p *jsonLsPrinter) Snapshot(sn *restic.Snapshot) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Print node in our custom JSON format, followed by a newline.
|
// Print node in our custom JSON format, followed by a newline.
|
||||||
func (p *jsonLsPrinter) Node(path string, node *restic.Node) {
|
func (p *jsonLsPrinter) Node(path string, node *restic.Node, isPrefixDirectory bool) {
|
||||||
|
if isPrefixDirectory {
|
||||||
|
return
|
||||||
|
}
|
||||||
err := lsNodeJSON(p.enc, path, node)
|
err := lsNodeJSON(p.enc, path, node)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
Warnf("JSON encode failed: %v\n", err)
|
Warnf("JSON encode failed: %v\n", err)
|
||||||
|
@ -190,10 +193,13 @@ func lsNcduNode(_ string, node *restic.Node) ([]byte, error) {
|
||||||
Mtime int64 `json:"mtime"`
|
Mtime int64 `json:"mtime"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const blockSize = 512
|
||||||
|
|
||||||
outNode := NcduNode{
|
outNode := NcduNode{
|
||||||
Name: node.Name,
|
Name: node.Name,
|
||||||
Asize: node.Size,
|
Asize: node.Size,
|
||||||
Dsize: node.Size,
|
// round up to nearest full blocksize
|
||||||
|
Dsize: (node.Size + blockSize - 1) / blockSize * blockSize,
|
||||||
Dev: node.DeviceID,
|
Dev: node.DeviceID,
|
||||||
Ino: node.Inode,
|
Ino: node.Inode,
|
||||||
NLink: node.Links,
|
NLink: node.Links,
|
||||||
|
@ -217,7 +223,7 @@ func lsNcduNode(_ string, node *restic.Node) ([]byte, error) {
|
||||||
return json.Marshal(outNode)
|
return json.Marshal(outNode)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *ncduLsPrinter) Node(path string, node *restic.Node) {
|
func (p *ncduLsPrinter) Node(path string, node *restic.Node, _ bool) {
|
||||||
out, err := lsNcduNode(path, node)
|
out, err := lsNcduNode(path, node)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
Warnf("JSON encode failed: %v\n", err)
|
Warnf("JSON encode failed: %v\n", err)
|
||||||
|
@ -249,8 +255,10 @@ type textLsPrinter struct {
|
||||||
func (p *textLsPrinter) Snapshot(sn *restic.Snapshot) {
|
func (p *textLsPrinter) Snapshot(sn *restic.Snapshot) {
|
||||||
Verbosef("%v filtered by %v:\n", sn, p.dirs)
|
Verbosef("%v filtered by %v:\n", sn, p.dirs)
|
||||||
}
|
}
|
||||||
func (p *textLsPrinter) Node(path string, node *restic.Node) {
|
func (p *textLsPrinter) Node(path string, node *restic.Node, isPrefixDirectory bool) {
|
||||||
|
if !isPrefixDirectory {
|
||||||
Printf("%s\n", formatNode(path, node, p.ListLong, p.HumanReadable))
|
Printf("%s\n", formatNode(path, node, p.ListLong, p.HumanReadable))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *textLsPrinter) LeaveDir(_ string) {}
|
func (p *textLsPrinter) LeaveDir(_ string) {}
|
||||||
|
@ -367,9 +375,11 @@ func runLs(ctx context.Context, opts LsOptions, gopts GlobalOptions, args []stri
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
printedDir := false
|
||||||
if withinDir(nodepath) {
|
if withinDir(nodepath) {
|
||||||
// if we're within a dir, print the node
|
// if we're within a target path, print the node
|
||||||
printer.Node(nodepath, node)
|
printer.Node(nodepath, node, false)
|
||||||
|
printedDir = true
|
||||||
|
|
||||||
// if recursive listing is requested, signal the walker that it
|
// if recursive listing is requested, signal the walker that it
|
||||||
// should continue walking recursively
|
// should continue walking recursively
|
||||||
|
@ -381,12 +391,20 @@ func runLs(ctx context.Context, opts LsOptions, gopts GlobalOptions, args []stri
|
||||||
// if there's an upcoming match deeper in the tree (but we're not
|
// if there's an upcoming match deeper in the tree (but we're not
|
||||||
// there yet), signal the walker to descend into any subdirs
|
// there yet), signal the walker to descend into any subdirs
|
||||||
if approachingMatchingTree(nodepath) {
|
if approachingMatchingTree(nodepath) {
|
||||||
|
// print node leading up to the target paths
|
||||||
|
if !printedDir {
|
||||||
|
printer.Node(nodepath, node, true)
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// otherwise, signal the walker to not walk recursively into any
|
// otherwise, signal the walker to not walk recursively into any
|
||||||
// subdirs
|
// subdirs
|
||||||
if node.Type == "dir" {
|
if node.Type == "dir" {
|
||||||
|
// immediately generate leaveDir if the directory is skipped
|
||||||
|
if printedDir {
|
||||||
|
printer.LeaveDir(nodepath)
|
||||||
|
}
|
||||||
return walker.ErrSkipNode
|
return walker.ErrSkipNode
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
@ -396,7 +414,7 @@ func runLs(ctx context.Context, opts LsOptions, gopts GlobalOptions, args []stri
|
||||||
ProcessNode: processNode,
|
ProcessNode: processNode,
|
||||||
LeaveDir: func(path string) {
|
LeaveDir: func(path string) {
|
||||||
// the root path `/` has no corresponding node and is thus also skipped by processNode
|
// the root path `/` has no corresponding node and is thus also skipped by processNode
|
||||||
if withinDir(path) && path != "/" {
|
if path != "/" {
|
||||||
printer.LeaveDir(path)
|
printer.LeaveDir(path)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -35,13 +35,16 @@ func TestRunLsNcdu(t *testing.T) {
|
||||||
env, cleanup := withTestEnvironment(t)
|
env, cleanup := withTestEnvironment(t)
|
||||||
defer cleanup()
|
defer cleanup()
|
||||||
|
|
||||||
testRunInit(t, env.gopts)
|
testSetupBackupData(t, env)
|
||||||
opts := BackupOptions{}
|
opts := BackupOptions{}
|
||||||
testRunBackup(t, filepath.Dir(env.testdata), []string{"testdata"}, opts, env.gopts)
|
testRunBackup(t, filepath.Dir(env.testdata), []string{"testdata"}, opts, env.gopts)
|
||||||
|
|
||||||
ncdu := testRunLsWithOpts(t, env.gopts, LsOptions{Ncdu: true}, []string{"latest"})
|
for _, paths := range [][]string{
|
||||||
assertIsValidJSON(t, ncdu)
|
{"latest"},
|
||||||
|
{"latest", "/testdata"},
|
||||||
ncdu = testRunLsWithOpts(t, env.gopts, LsOptions{Ncdu: true}, []string{"latest", "/testdata"})
|
{"latest", "/testdata/0", "/testdata/0/tests"},
|
||||||
|
} {
|
||||||
|
ncdu := testRunLsWithOpts(t, env.gopts, LsOptions{Ncdu: true}, paths)
|
||||||
assertIsValidJSON(t, ncdu)
|
assertIsValidJSON(t, ncdu)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -109,7 +109,7 @@ func TestLsNodeJSON(t *testing.T) {
|
||||||
|
|
||||||
func TestLsNcduNode(t *testing.T) {
|
func TestLsNcduNode(t *testing.T) {
|
||||||
for i, expect := range []string{
|
for i, expect := range []string{
|
||||||
`{"name":"baz","asize":12345,"dsize":12345,"dev":0,"ino":0,"nlink":1,"notreg":false,"uid":10000000,"gid":20000000,"mode":0,"mtime":-62135596800}`,
|
`{"name":"baz","asize":12345,"dsize":12800,"dev":0,"ino":0,"nlink":1,"notreg":false,"uid":10000000,"gid":20000000,"mode":0,"mtime":-62135596800}`,
|
||||||
`{"name":"empty","asize":0,"dsize":0,"dev":0,"ino":0,"nlink":3840,"notreg":false,"uid":1001,"gid":1001,"mode":0,"mtime":-62135596800}`,
|
`{"name":"empty","asize":0,"dsize":0,"dev":0,"ino":0,"nlink":3840,"notreg":false,"uid":1001,"gid":1001,"mode":0,"mtime":-62135596800}`,
|
||||||
`{"name":"link","asize":0,"dsize":0,"dev":0,"ino":0,"nlink":0,"notreg":true,"uid":0,"gid":0,"mode":511,"mtime":-62135596800}`,
|
`{"name":"link","asize":0,"dsize":0,"dev":0,"ino":0,"nlink":0,"notreg":true,"uid":0,"gid":0,"mode":511,"mtime":-62135596800}`,
|
||||||
`{"name":"directory","asize":0,"dsize":0,"dev":0,"ino":0,"nlink":0,"notreg":false,"uid":0,"gid":0,"mode":493,"mtime":1577934245}`,
|
`{"name":"directory","asize":0,"dsize":0,"dev":0,"ino":0,"nlink":0,"notreg":false,"uid":0,"gid":0,"mode":493,"mtime":1577934245}`,
|
||||||
|
@ -140,19 +140,19 @@ func TestLsNcdu(t *testing.T) {
|
||||||
printer.Node("/directory", &restic.Node{
|
printer.Node("/directory", &restic.Node{
|
||||||
Type: "dir",
|
Type: "dir",
|
||||||
Name: "directory",
|
Name: "directory",
|
||||||
})
|
}, false)
|
||||||
printer.Node("/directory/data", &restic.Node{
|
printer.Node("/directory/data", &restic.Node{
|
||||||
Type: "file",
|
Type: "file",
|
||||||
Name: "data",
|
Name: "data",
|
||||||
Size: 42,
|
Size: 42,
|
||||||
})
|
}, false)
|
||||||
printer.LeaveDir("/directory")
|
printer.LeaveDir("/directory")
|
||||||
printer.Close()
|
printer.Close()
|
||||||
|
|
||||||
rtest.Equals(t, `[1, 2, {"time":"0001-01-01T00:00:00Z","tree":null,"paths":["/example"],"hostname":"host"},
|
rtest.Equals(t, `[1, 2, {"time":"0001-01-01T00:00:00Z","tree":null,"paths":["/example"],"hostname":"host"},
|
||||||
[
|
[
|
||||||
{"name":"directory","asize":0,"dsize":0,"dev":0,"ino":0,"nlink":0,"notreg":false,"uid":0,"gid":0,"mode":0,"mtime":-62135596800},
|
{"name":"directory","asize":0,"dsize":0,"dev":0,"ino":0,"nlink":0,"notreg":false,"uid":0,"gid":0,"mode":0,"mtime":-62135596800},
|
||||||
{"name":"data","asize":42,"dsize":42,"dev":0,"ino":0,"nlink":0,"notreg":false,"uid":0,"gid":0,"mode":0,"mtime":-62135596800}
|
{"name":"data","asize":42,"dsize":512,"dev":0,"ino":0,"nlink":0,"notreg":false,"uid":0,"gid":0,"mode":0,"mtime":-62135596800}
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
`, buf.String())
|
`, buf.String())
|
||||||
|
|
Loading…
Reference in a new issue