From b0a8c4ad6c5b4ec4b619d1a1af8380669aacb22c Mon Sep 17 00:00:00 2001 From: Michael Eischer Date: Sat, 22 Aug 2020 12:22:00 +0200 Subject: [PATCH] copy: Only process each tree once This speeds up copying multiple overlapping snapshots from one repository to another, as we only have to copy the changed parts of later snapshots. --- cmd/restic/cmd_copy.go | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/cmd/restic/cmd_copy.go b/cmd/restic/cmd_copy.go index cbc29a1b3..9fbc19028 100644 --- a/cmd/restic/cmd_copy.go +++ b/cmd/restic/cmd_copy.go @@ -108,11 +108,13 @@ func runCopy(opts CopyOptions, gopts GlobalOptions, args []string) error { return err } + visitedTrees := restic.NewIDSet() + for sn := range FindFilteredSnapshots(ctx, srcRepo, opts.Hosts, opts.Tags, opts.Paths, args) { Verbosef("snapshot %s of %v at %s)\n", sn.ID().Str(), sn.Paths, sn.Time) Verbosef(" copy started, this may take a while...\n") - if err := copyTree(ctx, srcRepo, dstRepo, *sn.Tree); err != nil { + if err := copyTree(ctx, srcRepo, dstRepo, *sn.Tree, visitedTrees); err != nil { return err } debug.Log("tree copied") @@ -134,11 +136,18 @@ func runCopy(opts CopyOptions, gopts GlobalOptions, args []string) error { return nil } -func copyTree(ctx context.Context, srcRepo, dstRepo restic.Repository, treeID restic.ID) error { +func copyTree(ctx context.Context, srcRepo, dstRepo restic.Repository, treeID restic.ID, visitedTrees restic.IDSet) error { + // We have already processed this tree + if visitedTrees.Has(treeID) { + return nil + } + tree, err := srcRepo.LoadTree(ctx, treeID) if err != nil { return fmt.Errorf("LoadTree(%v) returned error %v", treeID.Str(), err) } + visitedTrees.Insert(treeID) + // Do we already have this tree blob? if !dstRepo.Index().Has(treeID, restic.TreeBlob) { newTreeID, err := dstRepo.SaveTree(ctx, tree) @@ -157,7 +166,7 @@ func copyTree(ctx context.Context, srcRepo, dstRepo restic.Repository, treeID re for _, entry := range tree.Nodes { // If it is a directory, recurse if entry.Type == "dir" && entry.Subtree != nil { - if err := copyTree(ctx, srcRepo, dstRepo, *entry.Subtree); err != nil { + if err := copyTree(ctx, srcRepo, dstRepo, *entry.Subtree, visitedTrees); err != nil { return err } }