Discovery win: Fix detection of case-only renames
authorChristian Kamm <mail@ckamm.de>
Thu, 14 Feb 2019 13:52:22 +0000 (14:52 +0100)
committerKevin Ottens <kevin.ottens@nextcloud.com>
Tue, 15 Dec 2020 09:58:42 +0000 (10:58 +0100)
Previously they were detected as DELETE+NEW because if "a" is renamed to
"A" then QFile::exists("a") will still return true on Windows.

src/libsync/discovery.cpp
test/testsyncmove.cpp

index c2c1db09986216ca8bd3eadd12688e7d35b3d56f..a84dd94c05e09e8968646ca498ac581f9ca7a194 100644 (file)
@@ -807,9 +807,14 @@ void ProcessDirectoryJob::processFileAnalyzeLocalInfo(
                // Directories and virtual files don't need size/mtime equality
                || localEntry.isDirectory || localEntry.isVirtualFile);
 
+    auto originalPath = QString::fromUtf8(base._path);
     if (isMove) {
-        //  The old file must have been deleted.
-        isMove = !QFile::exists(_discoveryData->_localDir + base._path);
+        // The old file must have been deleted.
+        isMove = !QFile::exists(_discoveryData->_localDir + base._path)
+                // Exception: If the rename changes case only (like "foo" -> "Foo") the
+                // old filename might still point to the same file.
+                || (Utility::fsCasePreserving()
+                    && originalPath.compare(path._local, Qt::CaseInsensitive) == 0);
     }
 
     // Verify the checksum where possible
@@ -819,7 +824,6 @@ void ProcessDirectoryJob::processFileAnalyzeLocalInfo(
             isMove = item->_checksumHeader == base._checksumHeader;
         }
     }
-    auto originalPath = QString::fromUtf8(base._path);
     if (isMove && _discoveryData->isRenamed(originalPath))
         isMove = false;
 
index e78f15cc37c3c303d43d97d792b8ea22c9a7ac4a..d7e1f7017e4cccee745f2849a511372fb3d91e48 100644 (file)
@@ -585,6 +585,28 @@ private slots:
         QCOMPARE(counter.nDELETE, 0);
     }
 
+    // These renames can be troublesome on windows
+    void testRenameCaseOnly()
+    {
+        FakeFolder fakeFolder{ FileInfo::A12_B12_C12_S12() };
+        auto &local = fakeFolder.localModifier();
+        auto &remote = fakeFolder.remoteModifier();
+
+        OperationCounter counter;
+        fakeFolder.setServerOverride(counter.functor());
+
+        local.rename("A/a1", "A/A1");
+        remote.rename("A/a2", "A/A2");
+
+        QVERIFY(fakeFolder.syncOnce());
+        QCOMPARE(fakeFolder.currentLocalState(), remote);
+        QCOMPARE(printDbData(fakeFolder.dbState()), printDbData(fakeFolder.currentRemoteState()));
+        QCOMPARE(counter.nGET, 0);
+        QCOMPARE(counter.nPUT, 0);
+        QCOMPARE(counter.nMOVE, 1);
+        QCOMPARE(counter.nDELETE, 0);
+    }
+
     // Check interaction of moves with file type changes
     void testMoveAndTypeChange()
     {