Database merge confirmation dialog (#10173)

* Add Entry::calculateDifference()

This new function contains the logic that was previously in
EntryHistoryModel::calculateHistoryModifications().
It allows the re-use to display the differences in case of a merge.

* Introduce Database Merge Confirmation Dialog

Adds a dialog allowing a user to review the changes of a merge operation.
This dialog displays the changes and allows the user to abort the merge
without modifying the database.

Fixes #1152

* Added dry run option to Merger
* Changed behavior when actual merge differs from dry run to just output a warning to console
* Fixed KeeShare conflicting with merge operations in the middle of a merge

---------

Co-authored-by: Jonathan White <support@dmapps.us>
This commit is contained in:
Tamino Bauknecht
2025-09-14 18:02:22 +02:00
committed by GitHub
parent 9a40182a62
commit c0ea6f65f9
20 changed files with 952 additions and 276 deletions

View File

@@ -1682,8 +1682,9 @@ void TestCli::testMerge()
m_stderr->readLine(); // Skip password prompt
QCOMPARE(m_stderr->readAll(), QByteArray());
QList<QByteArray> outLines1 = m_stdout->readAll().split('\n');
QVERIFY(outLines1.at(0).contains("Overwriting Internet"));
QVERIFY(outLines1.at(1).contains("Creating missing Some Website"));
QVERIFY(outLines1.at(0).contains("Modified"));
QVERIFY(outLines1.at(0).contains("Modification time"));
QVERIFY(outLines1.at(1).contains("Added"));
QCOMPARE(outLines1.at(2),
QString("Successfully merged %1 into %2.").arg(sourceFile.fileName(), targetFile1.fileName()).toUtf8());
@@ -1699,8 +1700,9 @@ void TestCli::testMerge()
setInput("a");
execCmd(mergeCmd, {"merge", "--dry-run", "-s", targetFile2.fileName(), sourceFile.fileName()});
QList<QByteArray> outLines2 = m_stdout->readAll().split('\n');
QVERIFY(outLines2.at(0).contains("Overwriting Internet"));
QVERIFY(outLines2.at(1).contains("Creating missing Some Website"));
QVERIFY(outLines1.at(0).contains("Modified"));
QVERIFY(outLines1.at(0).contains("Modification time"));
QVERIFY(outLines2.at(1).contains("Added"));
QCOMPARE(outLines2.at(2), QByteArray("Database was not modified by merge operation."));
mergedDb = QSharedPointer<Database>::create();

View File

@@ -1146,7 +1146,7 @@ void TestMerge::testCustomData()
m_clock->advanceSecond(1);
Merger merger(dbSource.data(), dbDestination.data());
QStringList changes = merger.merge();
auto changes = merger.merge();
QVERIFY(!changes.isEmpty());
@@ -1167,7 +1167,7 @@ void TestMerge::testCustomData()
dbSource->metadata()->customData()->set("key3", "oldValue");
dbSource->metadata()->customData()->set("key3", "newValue");
Merger merger2(dbSource.data(), dbDestination.data());
QStringList changes2 = merger2.merge();
auto changes2 = merger2.merge();
QVERIFY(changes2.isEmpty());
Merger merger3(dbSource2.data(), dbDestination2.data());

View File

@@ -365,6 +365,10 @@ void TestGui::testMergeDatabase()
QTest::keyClicks(editPasswordMerge, "a");
QTest::keyClick(editPasswordMerge, Qt::Key_Enter);
// confirm merge in confirmation dialog
QTRY_VERIFY(QApplication::focusWindow()->title().contains("Merge"));
QTest::keyClick(QApplication::focusWidget(), Qt::Key_Enter);
QTRY_COMPARE(dbMergeSpy.count(), 1);
QTRY_VERIFY(m_tabWidget->tabText(m_tabWidget->currentIndex()).contains("*"));