mirror of
https://github.com/nextcloud/desktop.git
synced 2024-11-26 15:06:08 +03:00
Merge remote-tracking branch 'origin/2.4'
Conflicts: src/csync/csync.cpp
This commit is contained in:
commit
dd91f4a86e
8 changed files with 98 additions and 30 deletions
4
Jenkinsfile
vendored
4
Jenkinsfile
vendored
|
@ -16,7 +16,7 @@ node('CLIENT') {
|
|||
sh '''rm -rf build
|
||||
mkdir build
|
||||
cd build
|
||||
cmake -DCMAKE_BUILD_TYPE="Debug" -DUNIT_TESTING=1 -DWITH_TESTING=1 -DBUILD_WITH_QT4=OFF ..
|
||||
cmake -DCMAKE_BUILD_TYPE="Debug" -DUNIT_TESTING=1 -DWITH_TESTING=1 -DCMAKE_PREFIX_PATH=/var/lib/jenkins/qt/5.6.2 ..
|
||||
make -j4
|
||||
ctest -V --output-on-failure'''
|
||||
|
||||
|
@ -24,7 +24,7 @@ node('CLIENT') {
|
|||
sh '''rm -rf build
|
||||
mkdir build
|
||||
cd build
|
||||
cmake -DCMAKE_BUILD_TYPE="Debug" -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -DUNIT_TESTING=1 -DWITH_TESTING=1 -DBUILD_WITH_QT4=OFF ..
|
||||
cmake -DCMAKE_BUILD_TYPE="Debug" -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -DUNIT_TESTING=1 -DWITH_TESTING=1 -DCMAKE_PREFIX_PATH=/var/lib/jenkins/qt/5.6.2 ..
|
||||
make -j4
|
||||
ctest -V --output-on-failure'''
|
||||
|
||||
|
|
|
@ -53,6 +53,12 @@ X-GNOME-Autostart-Delay=3
|
|||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
Comment[oc]=@APPLICATION_NAME@ sincronizacion del client
|
||||
GenericName[oc]=Dorsièr de Sincronizacion
|
||||
|
|
|
@ -211,14 +211,14 @@ static int _csync_treewalk_visitor(csync_file_stat_t *cur, CSYNC * ctx) {
|
|||
|
||||
if (other_file_it == other_tree->cend()) {
|
||||
/* Check the renamed path as well. */
|
||||
QByteArray renamed_path = csync_rename_adjust_path(ctx, cur->path);
|
||||
QByteArray renamed_path = csync_rename_adjust_parent_path(ctx, cur->path);
|
||||
if (renamed_path != cur->path)
|
||||
other_file_it = other_tree->find(renamed_path);
|
||||
}
|
||||
|
||||
if (other_file_it == other_tree->cend()) {
|
||||
/* Check the source path as well. */
|
||||
QByteArray renamed_path = csync_rename_adjust_path_source(ctx, cur->path);
|
||||
QByteArray renamed_path = csync_rename_adjust_parent_path_source(ctx, cur->path);
|
||||
if (renamed_path != cur->path)
|
||||
other_file_it = other_tree->find(renamed_path);
|
||||
}
|
||||
|
@ -314,6 +314,9 @@ int csync_s::reinitialize() {
|
|||
local.files.clear();
|
||||
remote.files.clear();
|
||||
|
||||
renames.folder_renamed_from.clear();
|
||||
renames.folder_renamed_to.clear();
|
||||
|
||||
local_discovery_style = LocalDiscoveryStyle::FilesystemOnly;
|
||||
locally_touched_dirs.clear();
|
||||
|
||||
|
|
|
@ -113,7 +113,7 @@ static int _csync_merge_algorithm_visitor(csync_file_stat_t *cur, CSYNC * ctx) {
|
|||
|
||||
if (!other) {
|
||||
/* Check the renamed path as well. */
|
||||
other = other_tree->findFile(csync_rename_adjust_path(ctx, cur->path));
|
||||
other = other_tree->findFile(csync_rename_adjust_parent_path(ctx, cur->path));
|
||||
}
|
||||
if (!other) {
|
||||
/* Check if it is ignored */
|
||||
|
@ -147,24 +147,25 @@ static int _csync_merge_algorithm_visitor(csync_file_stat_t *cur, CSYNC * ctx) {
|
|||
cur->instruction = CSYNC_INSTRUCTION_NEW;
|
||||
|
||||
bool processedRename = false;
|
||||
auto renameCandidateProcessing = [&](const OCC::SyncJournalFileRecord &base) {
|
||||
auto renameCandidateProcessing = [&](const QByteArray &basePath) {
|
||||
if (processedRename)
|
||||
return;
|
||||
if (!base.isValid())
|
||||
if (basePath.isEmpty())
|
||||
return;
|
||||
|
||||
/* First, check that the file is NOT in our tree (another file with the same name was added) */
|
||||
if (our_tree->findFile(base._path)) {
|
||||
qCDebug(lcReconcile, "Origin found in our tree : %s", base._path.constData());
|
||||
if (our_tree->findFile(basePath)) {
|
||||
other = nullptr;
|
||||
qCDebug(lcReconcile, "Origin found in our tree : %s", basePath.constData());
|
||||
} else {
|
||||
/* Find the potential rename source file in the other tree.
|
||||
* If the renamed file could not be found in the opposite tree, that is because it
|
||||
* is not longer existing there, maybe because it was renamed or deleted.
|
||||
* The journal is cleaned up later after propagation.
|
||||
*/
|
||||
other = other_tree->findFile(base._path);
|
||||
other = other_tree->findFile(basePath);
|
||||
qCDebug(lcReconcile, "Rename origin in other tree (%s) %s",
|
||||
base._path.constData(), other ? "found" : "not found");
|
||||
basePath.constData(), other ? "found" : "not found");
|
||||
}
|
||||
|
||||
if(!other) {
|
||||
|
@ -197,7 +198,7 @@ static int _csync_merge_algorithm_visitor(csync_file_stat_t *cur, CSYNC * ctx) {
|
|||
cur->instruction = CSYNC_INSTRUCTION_NONE;
|
||||
// We have consumed 'other': exit this loop to not consume another one.
|
||||
processedRename = true;
|
||||
} else if (our_tree->findFile(csync_rename_adjust_path(ctx, other->path)) == cur) {
|
||||
} else if (our_tree->findFile(csync_rename_adjust_parent_path(ctx, other->path)) == cur) {
|
||||
// If we're here, that means that the other side's reconcile will be able
|
||||
// to work against cur: The filename itself didn't change, only a parent
|
||||
// directory was renamed! In that case it's safe to ignore the rename
|
||||
|
@ -225,12 +226,34 @@ static int _csync_merge_algorithm_visitor(csync_file_stat_t *cur, CSYNC * ctx) {
|
|||
qCDebug(lcReconcile, "Finding rename origin through inode %" PRIu64 "",
|
||||
cur->inode);
|
||||
ctx->statedb->getFileRecordByInode(cur->inode, &base);
|
||||
renameCandidateProcessing(base);
|
||||
renameCandidateProcessing(base._path);
|
||||
} else {
|
||||
ASSERT(ctx->current == REMOTE_REPLICA);
|
||||
qCDebug(lcReconcile, "Finding rename origin through file ID %s",
|
||||
cur->file_id.constData());
|
||||
ctx->statedb->getFileRecordsByFileId(cur->file_id, renameCandidateProcessing);
|
||||
|
||||
// The update phase has already mapped out all dir->dir renames, check the
|
||||
// path that is consistent with that first. Otherwise update mappings and
|
||||
// reconcile mappings might disagree, leading to odd situations down the
|
||||
// line.
|
||||
auto basePath = csync_rename_adjust_full_path_source(ctx, cur->path);
|
||||
if (basePath != cur->path) {
|
||||
qCDebug(lcReconcile, "Trying rename origin by csync_rename mapping %s",
|
||||
basePath.constData());
|
||||
// We go through getFileRecordsByFileId to ensure the basePath
|
||||
// computed in this way also has the expected fileid.
|
||||
ctx->statedb->getFileRecordsByFileId(cur->file_id,
|
||||
[&](const OCC::SyncJournalFileRecord &base) {
|
||||
if (base._path == basePath)
|
||||
renameCandidateProcessing(basePath);
|
||||
});
|
||||
}
|
||||
|
||||
// Also feed all the other files with the same fileid if necessary
|
||||
if (!processedRename) {
|
||||
qCDebug(lcReconcile, "Finding rename origin through file ID %s",
|
||||
cur->file_id.constData());
|
||||
ctx->statedb->getFileRecordsByFileId(cur->file_id,
|
||||
[&](const OCC::SyncJournalFileRecord &base) { renameCandidateProcessing(base._path); });
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
|
|
|
@ -36,7 +36,7 @@ void csync_rename_record(CSYNC* ctx, const QByteArray &from, const QByteArray &t
|
|||
ctx->renames.folder_renamed_from[to] = from;
|
||||
}
|
||||
|
||||
QByteArray csync_rename_adjust_path(CSYNC* ctx, const QByteArray &path)
|
||||
QByteArray csync_rename_adjust_parent_path(CSYNC *ctx, const QByteArray &path)
|
||||
{
|
||||
if (ctx->renames.folder_renamed_to.empty())
|
||||
return path;
|
||||
|
@ -50,11 +50,25 @@ QByteArray csync_rename_adjust_path(CSYNC* ctx, const QByteArray &path)
|
|||
return path;
|
||||
}
|
||||
|
||||
QByteArray csync_rename_adjust_path_source(CSYNC* ctx, const QByteArray &path)
|
||||
QByteArray csync_rename_adjust_parent_path_source(CSYNC *ctx, const QByteArray &path)
|
||||
{
|
||||
if (ctx->renames.folder_renamed_from.empty())
|
||||
return path;
|
||||
for (auto p = _parentDir(path); !p.isEmpty(); p = _parentDir(p)) {
|
||||
for (ByteArrayRef p = _parentDir(path); !p.isEmpty(); p = _parentDir(p)) {
|
||||
auto it = ctx->renames.folder_renamed_from.find(p);
|
||||
if (it != ctx->renames.folder_renamed_from.end()) {
|
||||
QByteArray rep = it->second + path.mid(p.length());
|
||||
return rep;
|
||||
}
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
QByteArray csync_rename_adjust_full_path_source(CSYNC *ctx, const QByteArray &path)
|
||||
{
|
||||
if (ctx->renames.folder_renamed_from.empty())
|
||||
return path;
|
||||
for (ByteArrayRef p = path; !p.isEmpty(); p = _parentDir(p)) {
|
||||
auto it = ctx->renames.folder_renamed_from.find(p);
|
||||
if (it != ctx->renames.folder_renamed_from.end()) {
|
||||
QByteArray rep = it->second + path.mid(p.length());
|
||||
|
|
|
@ -22,10 +22,19 @@
|
|||
|
||||
#include "csync.h"
|
||||
|
||||
/* Return the final destination path of a given patch in case of renames */
|
||||
QByteArray OCSYNC_EXPORT csync_rename_adjust_path(CSYNC *ctx, const QByteArray &path);
|
||||
/* Return the final destination path of a given patch in case of renames
|
||||
*
|
||||
* Does only map the parent directories. If the directory "A" is renamed to
|
||||
* "B" then this function will not map "A" to "B". Only "A/foo" -> "B/foo".
|
||||
*/
|
||||
QByteArray OCSYNC_EXPORT csync_rename_adjust_parent_path(CSYNC *ctx, const QByteArray &path);
|
||||
|
||||
/* Return the source of a given path in case of renames */
|
||||
QByteArray OCSYNC_EXPORT csync_rename_adjust_path_source(CSYNC *ctx, const QByteArray &path);
|
||||
QByteArray OCSYNC_EXPORT csync_rename_adjust_parent_path_source(CSYNC *ctx, const QByteArray &path);
|
||||
|
||||
/* like the parent_path variant, but applying to the full path */
|
||||
QByteArray OCSYNC_EXPORT csync_rename_adjust_full_path_source(CSYNC *ctx, const QByteArray &path);
|
||||
|
||||
void OCSYNC_EXPORT csync_rename_record(CSYNC *ctx, const QByteArray &from, const QByteArray &to);
|
||||
/* Return the amount of renamed item recorded */
|
||||
bool OCSYNC_EXPORT csync_rename_count(CSYNC *ctx);
|
||||
|
|
|
@ -75,7 +75,7 @@ bool DiscoveryJob::isInSelectiveSyncBlackList(const QByteArray &path) const
|
|||
|
||||
// Also try to adjust the path if there was renames
|
||||
if (csync_rename_count(_csync_ctx)) {
|
||||
QByteArray adjusted = csync_rename_adjust_path_source(_csync_ctx, path);
|
||||
QByteArray adjusted = csync_rename_adjust_parent_path_source(_csync_ctx, path);
|
||||
if (adjusted != path) {
|
||||
return findPathInList(_selectiveSyncBlackList, QString::fromUtf8(adjusted));
|
||||
}
|
||||
|
|
|
@ -231,12 +231,25 @@ private slots:
|
|||
QCOMPARE(fakeFolder.currentLocalState(), remoteInfo);
|
||||
}
|
||||
|
||||
void testDuplicateFileId_data()
|
||||
{
|
||||
QTest::addColumn<QString>("prefix");
|
||||
|
||||
// There have been bugs related to how the original
|
||||
// folder and the folder with the duplicate tree are
|
||||
// ordered. Test both cases here.
|
||||
QTest::newRow("first ordering") << "O"; // "O" > "A"
|
||||
QTest::newRow("second ordering") << "0"; // "0" < "A"
|
||||
}
|
||||
|
||||
// If the same folder is shared in two different ways with the same
|
||||
// user, the target user will see duplicate file ids. We need to make
|
||||
// sure the move detection and sync still do the right thing in that
|
||||
// case.
|
||||
void testDuplicateFileId()
|
||||
{
|
||||
QFETCH(QString, prefix);
|
||||
|
||||
FakeFolder fakeFolder{ FileInfo::A12_B12_C12_S12() };
|
||||
auto &remote = fakeFolder.remoteModifier();
|
||||
|
||||
|
@ -245,8 +258,8 @@ private slots:
|
|||
remote.mkdir("A/Q");
|
||||
|
||||
// Duplicate every entry in A under O/A
|
||||
remote.mkdir("O");
|
||||
remote.children["O"].addChild(remote.children["A"]);
|
||||
remote.mkdir(prefix);
|
||||
remote.children[prefix].addChild(remote.children["A"]);
|
||||
|
||||
// This already checks that the rename detection doesn't get
|
||||
// horribly confused if we add new files that have the same
|
||||
|
@ -263,28 +276,28 @@ private slots:
|
|||
|
||||
// Try a remote file move
|
||||
remote.rename("A/a1", "A/W/a1m");
|
||||
remote.rename("O/A/a1", "O/A/W/a1m");
|
||||
remote.rename(prefix + "/A/a1", prefix + "/A/W/a1m");
|
||||
QVERIFY(fakeFolder.syncOnce());
|
||||
QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
|
||||
QCOMPARE(nGET, 0);
|
||||
|
||||
// And a remote directory move
|
||||
remote.rename("A/W", "A/Q/W");
|
||||
remote.rename("O/A/W", "O/A/Q/W");
|
||||
remote.rename(prefix + "/A/W", prefix + "/A/Q/W");
|
||||
QVERIFY(fakeFolder.syncOnce());
|
||||
QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
|
||||
QCOMPARE(nGET, 0);
|
||||
|
||||
// Partial file removal (in practice, A/a2 may be moved to O/a2, but we don't care)
|
||||
remote.rename("O/A/a2", "O/a2");
|
||||
remote.rename(prefix + "/A/a2", prefix + "/a2");
|
||||
remote.remove("A/a2");
|
||||
QVERIFY(fakeFolder.syncOnce());
|
||||
QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
|
||||
QCOMPARE(nGET, 0);
|
||||
|
||||
// Local change plus remote move at the same time
|
||||
fakeFolder.localModifier().appendByte("O/a2");
|
||||
remote.rename("O/a2", "O/a3");
|
||||
fakeFolder.localModifier().appendByte(prefix + "/a2");
|
||||
remote.rename(prefix + "/a2", prefix + "/a3");
|
||||
QVERIFY(fakeFolder.syncOnce());
|
||||
QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
|
||||
QCOMPARE(nGET, 1);
|
||||
|
|
Loading…
Reference in a new issue