diff --git a/fs/operations/operations.go b/fs/operations/operations.go index 820737841..a142140a2 100644 --- a/fs/operations/operations.go +++ b/fs/operations/operations.go @@ -19,6 +19,7 @@ import ( "github.com/ncw/rclone/fs" "github.com/ncw/rclone/fs/accounting" + "github.com/ncw/rclone/fs/cache" "github.com/ncw/rclone/fs/fserrors" "github.com/ncw/rclone/fs/fshttp" "github.com/ncw/rclone/fs/hash" @@ -1475,6 +1476,22 @@ func moveOrCopyFile(fdst fs.Fs, fsrc fs.Fs, dstFileName string, srcFileName stri } if NeedTransfer(dstObj, srcObj) { + // If destination already exists, then we must move it into --backup-dir if required + if dstObj != nil && fs.Config.BackupDir != "" { + backupDir, err := cache.Get(fs.Config.BackupDir) + if err != nil { + return errors.Wrap(err, "creating Fs for --backup-dir failed") + } + remoteWithSuffix := SuffixName(dstObj.Remote()) + overwritten, _ := backupDir.NewObject(remoteWithSuffix) + _, err = Move(backupDir, overwritten, remoteWithSuffix, dstObj) + if err != nil { + return errors.Wrap(err, "moving to --backup-dir failed") + } + // If successful zero out the dstObj as it is no longer there + dstObj = nil + } + _, err = Op(fdst, dstObj, dstFileName, srcObj) } else { accounting.Stats.Checking(srcFileName) diff --git a/fs/operations/operations_test.go b/fs/operations/operations_test.go index 42cbd8945..09cc60c64 100644 --- a/fs/operations/operations_test.go +++ b/fs/operations/operations_test.go @@ -739,6 +739,29 @@ func TestMoveFile(t *testing.T) { fstest.CheckItems(t, r.Fremote, file2) } +func TestMoveFileBackupDir(t *testing.T) { + r := fstest.NewRun(t) + defer r.Finalise() + + oldBackupDir := fs.Config.BackupDir + fs.Config.BackupDir = r.FremoteName + "/backup" + defer func() { + fs.Config.BackupDir = oldBackupDir + }() + + file1 := r.WriteFile("dst/file1", "file1 contents", t1) + fstest.CheckItems(t, r.Flocal, file1) + + file1old := r.WriteObject("dst/file1", "file1 contents old", t1) + fstest.CheckItems(t, r.Fremote, file1old) + + err := operations.MoveFile(r.Fremote, r.Flocal, file1.Path, file1.Path) + require.NoError(t, err) + fstest.CheckItems(t, r.Flocal) + file1old.Path = "backup/dst/file1" + fstest.CheckItems(t, r.Fremote, file1old, file1) +} + func TestCopyFile(t *testing.T) { r := fstest.NewRun(t) defer r.Finalise() @@ -765,6 +788,29 @@ func TestCopyFile(t *testing.T) { fstest.CheckItems(t, r.Fremote, file2) } +func TestCopyFileBackupDir(t *testing.T) { + r := fstest.NewRun(t) + defer r.Finalise() + + oldBackupDir := fs.Config.BackupDir + fs.Config.BackupDir = r.FremoteName + "/backup" + defer func() { + fs.Config.BackupDir = oldBackupDir + }() + + file1 := r.WriteFile("dst/file1", "file1 contents", t1) + fstest.CheckItems(t, r.Flocal, file1) + + file1old := r.WriteObject("dst/file1", "file1 contents old", t1) + fstest.CheckItems(t, r.Fremote, file1old) + + err := operations.CopyFile(r.Fremote, r.Flocal, file1.Path, file1.Path) + require.NoError(t, err) + fstest.CheckItems(t, r.Flocal, file1) + file1old.Path = "backup/dst/file1" + fstest.CheckItems(t, r.Fremote, file1old, file1) +} + // testFsInfo is for unit testing fs.Info type testFsInfo struct { name string