From d2219a800af2f0f84ddbae1f5ab0ae82dd290dff Mon Sep 17 00:00:00 2001 From: Nick Craig-Wood Date: Thu, 25 Feb 2016 20:05:34 +0000 Subject: [PATCH] Fix and document the move command - fixes #334 * Don't attempt to use server side Move unless they are on the same Fs * Fix move in the presense of filters --- docs/content/docs.md | 16 ++++++++++++++++ fs/operations.go | 43 ++++++++++++++++++++++++++++++------------- fs/operations_test.go | 2 ++ 3 files changed, 48 insertions(+), 13 deletions(-) diff --git a/docs/content/docs.md b/docs/content/docs.md index 7b3c60416..3e0e30d6b 100644 --- a/docs/content/docs.md +++ b/docs/content/docs.md @@ -108,6 +108,22 @@ extended explanation in the `copy` command above if unsure. If dest:path doesn't exist, it is created and the source:path contents go there. +### move source:path dest:path ### + +Moves the source to the destination. + +If there are no filters in use this is equivalent to a copy followed +by a purge, but may using server side operations to speed it up if +possible. + +If filters are in use then it is equivalent to a copy followed by +delete, followed by an rmdir (which only removes the directory if +empty). The individual file moves will be moved with srver side +operations if possible. + +**Important**: Since this can cause data loss, test first with the +--dry-run flag. + ### rclone ls remote:path ### List all the objects in the the path with size and path. diff --git a/fs/operations.go b/fs/operations.go index 7aa602c54..564386b54 100644 --- a/fs/operations.go +++ b/fs/operations.go @@ -354,7 +354,7 @@ func PairMover(in ObjectPairChan, fdst Fs, wg *sync.WaitGroup) { Stats.Transferring(src) if Config.DryRun { Log(src, "Not moving as --dry-run") - } else if haveMover { + } else if haveMover && src.Fs().Name() == fdst.Name() { // Delete destination if it exists if pair.dst != nil { err := dst.Remove() @@ -600,8 +600,8 @@ func MoveDir(fdst, fsrc Fs) error { return nil } - // First attempt to use DirMover - if fdstDirMover, ok := fdst.(DirMover); ok && fsrc.Name() == fdst.Name() { + // First attempt to use DirMover if exists, same Fs and no filters are active + if fdstDirMover, ok := fdst.(DirMover); ok && fsrc.Name() == fdst.Name() && Config.Filter.InActive() { err := fdstDirMover.DirMove(fsrc) Debug(fdst, "Using server side directory move") switch err { @@ -623,7 +623,18 @@ func MoveDir(fdst, fsrc Fs) error { ErrorLog(fdst, "Not deleting files as there were IO errors") return err } - return Purge(fsrc) + // If no filters then purge + if Config.Filter.InActive() { + return Purge(fsrc) + } + // Otherwise remove any remaining files obeying filters + err = Delete(fsrc) + if err != nil { + return err + } + // and try to remove the directory if empty - ignoring error + _ = TryRmdir(fsrc) + return nil } // Check the files in fsrc and fdst according to Size and hash @@ -849,18 +860,24 @@ func Mkdir(f Fs) error { return nil } -// Rmdir removes a container but not if not empty -func Rmdir(f Fs) error { +// TryRmdir removes a container but not if not empty. It doesn't +// count errors but may return one. +func TryRmdir(f Fs) error { if Config.DryRun { Log(f, "Not deleting as dry run is set") - } else { - err := f.Rmdir() - if err != nil { - Stats.Error() - return err - } + return nil } - return nil + return f.Rmdir() +} + +// Rmdir removes a container but not if not empty +func Rmdir(f Fs) error { + err := TryRmdir(f) + if err != nil { + Stats.Error() + return err + } + return err } // Purge removes a container and all of its contents diff --git a/fs/operations_test.go b/fs/operations_test.go index a2dfabff6..86e086ca3 100644 --- a/fs/operations_test.go +++ b/fs/operations_test.go @@ -712,6 +712,7 @@ func TestServerSideMove(t *testing.T) { fstest.CheckItems(t, fremoteMove, file2) // Do server side move + fs.Stats.ResetCounters() err = fs.MoveDir(fremoteMove, r.fremote) if err != nil { t.Fatalf("Server Side Move failed: %v", err) @@ -721,6 +722,7 @@ func TestServerSideMove(t *testing.T) { fstest.CheckItems(t, fremoteMove, file2, file1) // Move it back again, dst does not exist this time + fs.Stats.ResetCounters() err = fs.MoveDir(r.fremote, fremoteMove) if err != nil { t.Fatalf("Server Side Move 2 failed: %v", err)