diff --git a/fs/operations/operations.go b/fs/operations/operations.go index 116f291e8..1d950e40c 100644 --- a/fs/operations/operations.go +++ b/fs/operations/operations.go @@ -2310,7 +2310,10 @@ func DirMove(ctx context.Context, f fs.Fs, srcRemote, dstRemote string) (err err if err == nil { accounting.Stats(ctx).Renames(1) } - return err + if err != fs.ErrorCantDirMove && err != fs.ErrorDirExists { + return err + } + fs.Infof(f, "Can't DirMove - falling back to file moves: %v", err) } // Load the directory tree into memory diff --git a/fs/operations/operations_test.go b/fs/operations/operations_test.go index 4f4cf4cfd..98676ae26 100644 --- a/fs/operations/operations_test.go +++ b/fs/operations/operations_test.go @@ -1405,6 +1405,33 @@ func TestDirMove(t *testing.T) { fs.GetModifyWindow(ctx, r.Fremote), ) + // Try with a DirMove method that exists but returns fs.ErrorCantDirMove (ex. combine moving across upstreams) + // Should fall back to manual move (copy + delete) + + features.DirMove = func(ctx context.Context, src fs.Fs, srcRemote string, dstRemote string) error { + return fs.ErrorCantDirMove + } + + assert.NoError(t, operations.DirMove(ctx, r.Fremote, "A3", "A4")) + + for i := range files { + files[i].Path = strings.ReplaceAll(files[i].Path, "A3/", "A4/") + } + + fstest.CheckListingWithPrecision( + t, + r.Fremote, + files, + []string{ + "A4", + "A4/B1", + "A4/B2", + "A4/B1/C1", + "A4/B1/C2", + "A4/B1/C3", + }, + fs.GetModifyWindow(ctx, r.Fremote), + ) } func TestGetFsInfo(t *testing.T) {