From d758e1908ee679948f0503cb6b961a4d67bc8491 Mon Sep 17 00:00:00 2001 From: ishuah Date: Sun, 3 Jun 2018 10:21:25 +0200 Subject: [PATCH] copy: create (pseudo copy) empty source directories to destination - fixes #1837 --- fs/sync/sync.go | 44 ++++++++++++++++++++++++++++++++++++++++---- fs/sync/sync_test.go | 26 ++++++++++++++++++++++++++ 2 files changed, 66 insertions(+), 4 deletions(-) diff --git a/fs/sync/sync.go b/fs/sync/sync.go index ebf905728..6df8791b9 100644 --- a/fs/sync/sync.go +++ b/fs/sync/sync.go @@ -4,8 +4,8 @@ package sync import ( "context" "fmt" + "path" "sort" - "strings" "sync" "github.com/ncw/rclone/fs" @@ -489,10 +489,44 @@ func deleteEmptyDirectories(f fs.Fs, entriesMap map[string]fs.DirEntry) error { return nil } +// This copies the empty directories in the slice passed in and logs +// any errors copying the directories +func copyEmptyDirectories(f fs.Fs, entries map[string]fs.DirEntry) error { + if len(entries) == 0 { + return nil + } + + var okCount int + for _, entry := range entries { + dir, ok := entry.(fs.Directory) + if ok { + err := f.Mkdir(dir.Remote()) + if err != nil { + fs.Errorf(fs.LogDirName(f, dir.Remote()), "Failed to Mkdir: %v", err) + accounting.Stats.Error(err) + } else { + okCount++ + } + } else { + fs.Errorf(f, "Not a directory: %v", entry) + } + } + + if accounting.Stats.Errored() { + fs.Debugf(f, "failed to copy %d directories", accounting.Stats.GetErrors()) + } + + if okCount > 0 { + fs.Debugf(f, "copied %d directories", okCount) + } + return nil +} + func parentDirCheck(entries map[string]fs.DirEntry, entry fs.DirEntry) { - path := strings.Split(entry.Remote(), "/") - path = path[:len(path)-1] - parentDir := strings.Join(path, "/") + parentDir := path.Dir(entry.Remote()) + if parentDir == "." { + parentDir = "" + } if _, ok := entries[parentDir]; ok { delete(entries, parentDir) } @@ -660,6 +694,8 @@ func (s *syncCopyMove) run() error { s.stopTransfers() s.stopDeleters() + s.processError(copyEmptyDirectories(s.fdst, s.srcEmptyDirs)) + // Delete files after if s.deleteMode == fs.DeleteModeAfter { if s.currentError() != nil && !fs.Config.IgnoreErrors { diff --git a/fs/sync/sync_test.go b/fs/sync/sync_test.go index c8d81129b..f7c741e13 100644 --- a/fs/sync/sync_test.go +++ b/fs/sync/sync_test.go @@ -79,6 +79,32 @@ func TestCopyWithDepth(t *testing.T) { fstest.CheckItems(t, r.Fremote, file2) } +// Test copy empty directories +func TestCopyEmptyDirectories(t *testing.T) { + r := fstest.NewRun(t) + defer r.Finalise() + file1 := r.WriteFile("sub dir/hello world", "hello world", t1) + err := operations.Mkdir(r.Flocal, "sub dir2") + require.NoError(t, err) + r.Mkdir(r.Fremote) + + err = CopyDir(r.Fremote, r.Flocal) + require.NoError(t, err) + + fstest.CheckListingWithPrecision( + t, + r.Fremote, + []fstest.Item{ + file1, + }, + []string{ + "sub dir", + "sub dir2", + }, + fs.Config.ModifyWindow, + ) +} + // Test a server side copy if possible, or the backup path if not func TestServerSideCopy(t *testing.T) { r := fstest.NewRun(t)