diff --git a/cmd/restic/cmd_rewrite.go b/cmd/restic/cmd_rewrite.go index 726a19b8f..9dfcf1336 100644 --- a/cmd/restic/cmd_rewrite.go +++ b/cmd/restic/cmd_rewrite.go @@ -145,6 +145,11 @@ func rewriteSnapshot(ctx context.Context, repo *repository.Repository, sn *resti return false, err } + includeByNameFuncs, err := opts.IncludePatternOptions.CollectPatterns(Warnf) + if err != nil { + return false, err + } + metadata, err := opts.Metadata.convert() if err != nil { @@ -354,3 +359,53 @@ func runRewrite(ctx context.Context, opts RewriteOptions, gopts GlobalOptions, a return nil } + +// gatherFilters defines the method for walker.NodeRewrite +func gatherFilters(rejectByNameFuncs []filter.RejectByNameFunc, includeByNameFuncs []filter.IncludeByNameFunc) (rewriteNode walker.NodeRewriteFunc) { + + if len(includeByNameFuncs) > 0 { + inSelectByName := func(nodepath string, node *restic.Node) bool { + for _, include := range includeByNameFuncs { + if node.Type == restic.NodeTypeDir { + // always include directories + return true + } + flag1, flag2 := include(nodepath) + if flag1 && flag2 { + return flag1 && flag2 + } + } + return false + } + + rewriteNode = func(node *restic.Node, path string) *restic.Node { + if inSelectByName(path, node) { + if node.Type != restic.NodeTypeDir { + Verboseff("including %s\n", path) + } + return node + } + return nil + } + } else { + exSelectByName := func(nodepath string) bool { + for _, reject := range rejectByNameFuncs { + if reject(nodepath) { + return false + } + } + return true + } + + rewriteNode = func(node *restic.Node, path string) *restic.Node { + if exSelectByName(path) { + return node + } + + Verboseff("excluding %s\n", path) + return nil + } + } + + return rewriteNode +} diff --git a/cmd/restic/cmd_rewrite_integration_test.go b/cmd/restic/cmd_rewrite_integration_test.go index e89c974fb..26fcace41 100644 --- a/cmd/restic/cmd_rewrite_integration_test.go +++ b/cmd/restic/cmd_rewrite_integration_test.go @@ -3,6 +3,7 @@ package main import ( "context" "path/filepath" + "strings" "testing" "github.com/restic/restic/internal/filter" diff --git a/internal/walker/rewriter.go b/internal/walker/rewriter.go index f82423f13..852571995 100644 --- a/internal/walker/rewriter.go +++ b/internal/walker/rewriter.go @@ -26,7 +26,7 @@ type RewriteOpts struct { AllowUnstableSerialization bool DisableNodeCache bool - RemoveEmptyDirectoryGlobal bool + KeepEmptyDirecoryGlobal bool } type idMap map[restic.ID]restic.ID @@ -41,6 +41,8 @@ func NewTreeRewriter(opts RewriteOpts) *TreeRewriter { rw := &TreeRewriter{ opts: opts, } + rw.opts.KeepEmptyDirecoryGlobal = true + if !opts.DisableNodeCache { rw.replaces = make(idMap) } @@ -59,7 +61,7 @@ func NewTreeRewriter(opts RewriteOpts) *TreeRewriter { return rw } -func NewSnapshotSizeRewriter(rewriteNode NodeRewriteFunc, removeEmptyDirectoryGlobal bool) (*TreeRewriter, QueryRewrittenSizeFunc) { +func NewSnapshotSizeRewriter(rewriteNode NodeRewriteFunc, keepEmptyDirecory bool) (*TreeRewriter, QueryRewrittenSizeFunc) { var count uint var size uint64 @@ -73,8 +75,8 @@ func NewSnapshotSizeRewriter(rewriteNode NodeRewriteFunc, removeEmptyDirectoryGl return node }, DisableNodeCache: true, - // RemoveEmptyDirectoryGlobal = false will force old behaviour for --exclude variants - RemoveEmptyDirectoryGlobal: removeEmptyDirectoryGlobal, + // KeepEmptyDirecoryGlobal will force old behaviour for --exclude variants + KeepEmptyDirecoryGlobal: keepEmptyDirecory, }) ss := func() SnapshotSize { @@ -118,16 +120,14 @@ func (t *TreeRewriter) RewriteTree(ctx context.Context, repo BlobLoadSaver, node debug.Log("filterTree: %s, nodeId: %s\n", nodepath, nodeID.Str()) tb := restic.NewTreeJSONBuilder() - for _, node := range curTree.Nodes { - if ctx.Err() != nil { - return restic.ID{}, ctx.Err() - } - path := path.Join(nodepath, node.Name) - node = t.opts.RewriteNode(node, path) - if node == nil { - continue - } + // explicitely exclude empty directory - so it will be saved + if len(curTree.Nodes) > 0 { + countInserts := 0 + for _, node := range curTree.Nodes { + if ctx.Err() != nil { + return restic.ID{}, ctx.Err() + } path := path.Join(nodepath, node.Name) node = t.opts.RewriteNode(node, path) @@ -154,7 +154,7 @@ func (t *TreeRewriter) RewriteTree(ctx context.Context, repo BlobLoadSaver, node } // check for empty subtree condition here - if t.opts.RemoveEmptyDirectoryGlobal && err == nil && newID.IsNull() { + if !t.opts.KeepEmptyDirecoryGlobal && err == nil && newID.IsNull() { continue } @@ -163,24 +163,11 @@ func (t *TreeRewriter) RewriteTree(ctx context.Context, repo BlobLoadSaver, node if err != nil { return restic.ID{}, err } - continue + countInserts++ } - // treat nil as null id - var subtree restic.ID - if node.Subtree != nil { - subtree = *node.Subtree - } - newID, err := t.RewriteTree(ctx, repo, path, subtree) - if err != nil { - return restic.ID{}, err - } - node.Subtree = &newID - err = tb.AddNode(node) - if err != nil { - return restic.ID{}, err // check for empty node list - if t.opts.RemoveEmptyDirectoryGlobal && countInserts == 0 { + if !t.opts.KeepEmptyDirecoryGlobal && countInserts == 0 { // current subdirectory is empty - due to no includes: create condition here return restic.ID{}, nil } diff --git a/internal/walker/rewriter_test.go b/internal/walker/rewriter_test.go index 58dd25cd0..cc5ebd4a4 100644 --- a/internal/walker/rewriter_test.go +++ b/internal/walker/rewriter_test.go @@ -334,7 +334,7 @@ func TestSnapshotSizeQuery(t *testing.T) { } return node } - rewriter, querySize := NewSnapshotSizeRewriter(rewriteNode) + rewriter, querySize := NewSnapshotSizeRewriter(rewriteNode, false) newRoot, err := rewriter.RewriteTree(ctx, modrepo, "/", root) if err != nil { t.Error(err)