diff --git a/docs/content/docs.md b/docs/content/docs.md index 3b3a50a63..375da2b08 100644 --- a/docs/content/docs.md +++ b/docs/content/docs.md @@ -1809,6 +1809,11 @@ files if they are incorrect as it would normally. This can be used if the remote is being synced with another tool also (e.g. the Google Drive client). +### --no-update-dir-modtime ### + +When using this flag, rclone won't update modification times of remote +directories if they are incorrect as it would normally. + ### --order-by string ### The `--order-by` flag controls the order in which files in the backlog diff --git a/fs/config.go b/fs/config.go index 5deb921fd..435fbf8e7 100644 --- a/fs/config.go +++ b/fs/config.go @@ -89,6 +89,7 @@ type ConfigInfo struct { NoCheckDest bool NoUnicodeNormalization bool NoUpdateModTime bool + NoUpdateDirModTime bool DataRateUnit string CompareDest []string CopyDest []string diff --git a/fs/config/configflags/configflags.go b/fs/config/configflags/configflags.go index 206888fc3..ce7425343 100644 --- a/fs/config/configflags/configflags.go +++ b/fs/config/configflags/configflags.go @@ -91,6 +91,7 @@ func AddFlags(ci *fs.ConfigInfo, flagSet *pflag.FlagSet) { flags.BoolVarP(flagSet, &ci.NoCheckDest, "no-check-dest", "", ci.NoCheckDest, "Don't check the destination, copy regardless", "Copy") flags.BoolVarP(flagSet, &ci.NoUnicodeNormalization, "no-unicode-normalization", "", ci.NoUnicodeNormalization, "Don't normalize unicode characters in filenames", "Config") flags.BoolVarP(flagSet, &ci.NoUpdateModTime, "no-update-modtime", "", ci.NoUpdateModTime, "Don't update destination modtime if files identical", "Copy") + flags.BoolVarP(flagSet, &ci.NoUpdateDirModTime, "no-update-dir-modtime", "", ci.NoUpdateModTime, "Don't update directory modification times", "Copy") flags.StringArrayVarP(flagSet, &ci.CompareDest, "compare-dest", "", nil, "Include additional comma separated server-side paths during comparison", "Copy") flags.StringArrayVarP(flagSet, &ci.CopyDest, "copy-dest", "", nil, "Implies --compare-dest but also copies files from paths into destination", "Copy") flags.StringVarP(flagSet, &ci.BackupDir, "backup-dir", "", ci.BackupDir, "Make backups into hierarchy based in DIR", "Sync") diff --git a/fs/operations/operations.go b/fs/operations/operations.go index 1d950e40c..13c3794b3 100644 --- a/fs/operations/operations.go +++ b/fs/operations/operations.go @@ -2612,6 +2612,11 @@ func CopyDirMetadata(ctx context.Context, f fs.Fs, dst fs.Directory, dir string, // It does not create the directory. func SetDirModTime(ctx context.Context, f fs.Fs, dst fs.Directory, dir string, modTime time.Time) (newDst fs.Directory, err error) { logName := dirName(f, dst, dir) + ci := fs.GetConfig(ctx) + if ci.NoUpdateDirModTime { + fs.Debugf(logName, "Skipping set directory modification time as --no-update-dir-modtime is set") + return nil, nil + } if SkipDestructive(ctx, logName, "set directory modification time") { return nil, nil } diff --git a/fs/operations/operations_test.go b/fs/operations/operations_test.go index 98676ae26..9bba6ffff 100644 --- a/fs/operations/operations_test.go +++ b/fs/operations/operations_test.go @@ -1755,14 +1755,21 @@ func TestCopyDirMetadata(t *testing.T) { func TestSetDirModTime(t *testing.T) { const name = "set modtime on existing directory" - ctx := context.Background() + ctx, ci := fs.AddConfig(context.Background()) r := fstest.NewRun(t) if r.Fremote.Features().DirSetModTime == nil && !r.Fremote.Features().WriteDirSetModTime { t.Skip("Skipping test as remote does not support DirSetModTime or WriteDirSetModTime") } - // First try with the directory not existing - should return an error + // Check that we obey --no-update-dir-modtime - this should return nil, nil + ci.NoUpdateDirModTime = true newDst, err := operations.SetDirModTime(ctx, r.Fremote, nil, "set modtime on non existent directory", t2) + require.NoError(t, err) + require.Nil(t, newDst) + ci.NoUpdateDirModTime = false + + // First try with the directory not existing - should return an error + newDst, err = operations.SetDirModTime(ctx, r.Fremote, nil, "set modtime on non existent directory", t2) require.Error(t, err) require.Nil(t, newDst) diff --git a/fs/sync/sync.go b/fs/sync/sync.go index 9dac7c694..0c75415f4 100644 --- a/fs/sync/sync.go +++ b/fs/sync/sync.go @@ -154,8 +154,8 @@ func newSyncCopyMove(ctx context.Context, fdst, fsrc fs.Fs, deleteMode fs.Delete trackRenamesCh: make(chan fs.Object, ci.Checkers), checkFirst: ci.CheckFirst, setDirMetadata: ci.Metadata && fsrc.Features().ReadDirMetadata && fdst.Features().WriteDirMetadata, - setDirModTime: fdst.Features().WriteDirSetModTime || fdst.Features().MkdirMetadata != nil || fdst.Features().DirSetModTime != nil, - setDirModTimeAfter: fdst.Features().DirModTimeUpdatesOnWrite, + setDirModTime: !ci.NoUpdateDirModTime && (fdst.Features().WriteDirSetModTime || fdst.Features().MkdirMetadata != nil || fdst.Features().DirSetModTime != nil), + setDirModTimeAfter: !ci.NoUpdateDirModTime && fdst.Features().DirModTimeUpdatesOnWrite, modifiedDirs: make(map[string]struct{}), } diff --git a/fs/sync/sync_test.go b/fs/sync/sync_test.go index 150051062..6636812f4 100644 --- a/fs/sync/sync_test.go +++ b/fs/sync/sync_test.go @@ -344,6 +344,45 @@ func TestMoveEmptyDirectories(t *testing.T) { fstest.CheckDirModTime(ctx, t, r.Fremote, got, subDirT) } +// Test that --no-update-dir-modtime is working +func TestSyncNoUpdateDirModtime(t *testing.T) { + r := fstest.NewRun(t) + if r.Fremote.Features().DirSetModTime == nil { + t.Skip("Skipping test as backend does not support DirSetModTime") + } + + ctx, ci := fs.AddConfig(context.Background()) + ci.NoUpdateDirModTime = true + const name = "sub dir no update dir modtime" + + // Set the modtime on name to something specific + _, err := operations.MkdirModTime(ctx, r.Flocal, name, t1) + require.NoError(t, err) + + // Create the remote directory with the current time + require.NoError(t, r.Fremote.Mkdir(ctx, name)) + + // Read its modification time + wantT := fstest.NewDirectory(ctx, t, r.Fremote, name).ModTime(ctx) + + ctx = predictDstFromLogger(ctx) + err = Sync(ctx, r.Fremote, r.Flocal, true) + require.NoError(t, err) + testLoggerVsLsf(ctx, r.Fremote, operations.GetLoggerOpt(ctx).JSON, t) + + r.CheckRemoteListing( + t, + []fstest.Item{}, + []string{ + name, + }, + ) + + // Read the new directory modification time - it should not have changed + gotT := fstest.NewDirectory(ctx, t, r.Fremote, name).ModTime(ctx) + fstest.AssertTimeEqualWithPrecision(t, name, wantT, gotT, r.Fremote.Precision()) +} + // Test sync empty directories func TestSyncEmptyDirectories(t *testing.T) { ctx := context.Background()