Git: кэш+пагинация истории, diff в потоке, точечный коммит, честное удаление

- История: кэш по vault|rel|headOid, fileHistory(vault,file,limit), в диалоге Показать ещё по 60 с сохранением выбора
- Diff считается в QtConcurrent-пуле (parseBlocksWithDiffAsync+blocksReady, stale-guard), спиннер busy; MarkdownView.diffApplied
- commitFile точечно вместо всего хранилища (общий commitFromIndex), autoCommit честный; fileExistedAt + willDelete в подтверждении
- DocumentManager.readTextFile общий (GitManager.workdirContent через него); fileGitStatus через relativePath
- Тесты: пагинация/кэш, existed, commitFile не трогает B, async==sync
This commit is contained in:
2026-09-06 21:17:25 +03:00
parent f6074df0fc
commit 0846605c2b
12 changed files with 474 additions and 138 deletions
+33
View File
@@ -5,6 +5,8 @@
#include <QTemporaryDir>
#include <QDir>
#include <QFile>
#include <QEventLoop>
#include <QTimer>
#include <cstdio>
#include "markdown/MdParser.h"
@@ -201,6 +203,37 @@ int main(int argc, char *argv[])
return fail("text add без зелёного");
}
// parseBlocksWithDiffAsync: тот же результат, что sync, id совпадает
{
const QString oldMd = "# T\n\n```cpp\nint a = 1;\n```\n";
const QString newMd = "# T\n\n```cpp\nint a = 2;\n```\n";
QVariantList expect = p.parseBlocksWithDiff(oldMd, newMd, vault, fileDir);
const int reqId = p.parseBlocksWithDiffAsync(oldMd, newMd, vault, fileDir);
QVariantList got;
int gotId = -1;
QObject::connect(&p, &MdParser::blocksReady,
[&](int id, const QVariantList &b) { gotId = id; got = b; });
QEventLoop loop;
QTimer timer;
timer.setSingleShot(true);
QObject::connect(&p, &MdParser::blocksReady, &loop, &QEventLoop::quit);
QObject::connect(&timer, &QTimer::timeout, &loop, &QEventLoop::quit);
timer.start(10000);
loop.exec();
if (gotId != reqId) return fail("async id", QString::number(gotId));
if (got.size() != expect.size()) return fail("async размер", QString::number(got.size()));
for (int i = 0; i < got.size(); ++i) {
const QVariantMap g = got[i].toMap(), e = expect[i].toMap();
if (g.value("type") != e.value("type") || g.value("change") != e.value("change"))
return fail("async блок", QString::number(i));
if (g.value("type").toString() == "code") {
// копирование работает и после async (id свежие, код — новый)
if (!p.getCodeBlock(g.value("codeId").toString()).contains("int a = 2;"))
return fail("async copy", p.getCodeBlock(g.value("codeId").toString()));
}
}
}
// parseBlocksWithDiff: правка внутри кода — ОДИН блок с построчным diff внутри
{
const QString oldMd = "# T\n\n```cpp\nint a = 1;\nint b = 2;\n```\n";