From 88e516adee46f439cd0f53ccbbf3b7a86272e538 Mon Sep 17 00:00:00 2001
From: nielash <nielronash@gmail.com>
Date: Tue, 10 Oct 2023 07:21:56 -0400
Subject: [PATCH] moveOrCopyFile: avoid panic on --dry-run

Before this change, changing the case of a file on a case insensitive remote
would fatally panic when `--dry-run` was set, due to `moveOrCopyFile`
attempting to access the non-existent `tmpObj` it (would normally have)
created. After this change, the panic is avoided by skipping this step during
a `--dry-run` (with the usual "skipped as --dry-run is set" log message.)
---
 fs/operations/operations.go      |  4 ++++
 fs/operations/operations_test.go | 17 +++++++++++++++++
 2 files changed, 21 insertions(+)

diff --git a/fs/operations/operations.go b/fs/operations/operations.go
index efa3d4bab..b71e6adc5 100644
--- a/fs/operations/operations.go
+++ b/fs/operations/operations.go
@@ -1770,6 +1770,10 @@ func moveOrCopyFile(ctx context.Context, fdst fs.Fs, fsrc fs.Fs, dstFileName str
 	// move it back to the intended destination. This is required
 	// to avoid issues with certain remotes and avoid file deletion.
 	if !cp && fdst.Name() == fsrc.Name() && fdst.Features().CaseInsensitive && dstFileName != srcFileName && strings.EqualFold(dstFilePath, srcFilePath) {
+		if SkipDestructive(ctx, srcFileName, "rename to "+dstFileName) {
+			// avoid fatalpanic on --dry-run (trying to access non-existent tmpObj)
+			return nil
+		}
 		// Create random name to temporarily move file to
 		tmpObjName := dstFileName + "-rclone-move-" + random.String(8)
 		tmpObjFail, err := fdst.NewObject(ctx, tmpObjName)
diff --git a/fs/operations/operations_test.go b/fs/operations/operations_test.go
index 19d58ea30..325db1748 100644
--- a/fs/operations/operations_test.go
+++ b/fs/operations/operations_test.go
@@ -997,6 +997,23 @@ func TestCaseInsensitiveMoveFile(t *testing.T) {
 	r.CheckRemoteItems(t, file2Capitalized)
 }
 
+func TestCaseInsensitiveMoveFileDryRun(t *testing.T) {
+	ctx := context.Background()
+	ctx, ci := fs.AddConfig(ctx)
+	r := fstest.NewRun(t)
+	if !r.Fremote.Features().CaseInsensitive {
+		return
+	}
+
+	ci.DryRun = true
+	file1 := r.WriteObject(ctx, "hello", "world", t1)
+	r.CheckRemoteItems(t, file1)
+
+	err := operations.MoveFile(ctx, r.Fremote, r.Fremote, "HELLO", file1.Path)
+	require.NoError(t, err)
+	r.CheckRemoteItems(t, file1)
+}
+
 func TestMoveFileBackupDir(t *testing.T) {
 	ctx := context.Background()
 	ctx, ci := fs.AddConfig(ctx)