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
+56
View File
@@ -154,6 +154,62 @@ int main(int argc, char *argv[])
bool autoAgain = git.autoCommit(vault, file);
if(!autoAgain) return fail("auto again should be true even if no changes");
// Пагинация: limit режет сверху, порядок новые сверху, кэш консистентен
{
QVariantList full = git.fileHistory(vault, file);
QVariantList part = git.fileHistory(vault, file, 2);
if(part.size()!=2) return fail("paged size", QString::number(part.size()));
if(part.first().toMap().value("hash") != full.first().toMap().value("hash"))
return fail("paged order");
// повторный запрос с тем же лимитом — из кэша, тот же результат
QVariantList part2 = git.fileHistory(vault, file, 2);
if(part2.size()!=2 || part2.first().toMap().value("hash") != part.first().toMap().value("hash"))
return fail("paged cache");
// limit<=0 — как раньше (всё)
if(git.fileHistory(vault, file, 0).size() != full.size()) return fail("limit 0");
}
// fileExistedAt: отличает «пустой файл» от «файла не было»
// (коммит удаления в историю файла не попадает — файла нет в дереве,
// поэтому false проверяем коммитом, предшествующим созданию файла)
{
if(!git.fileExistedAt(vault, file, hash1)) return fail("existed v1");
const QString late = vault+"/late.md";
QFile lf(late);
if(!lf.open(QIODevice::WriteOnly)) return fail("late create");
lf.write("late\n");
lf.close();
if(!git.commitAll(vault, "late file")) return fail("commit late", git.lastError());
QString lateHash = git.fileHistory(vault, late).first().toMap().value("hash").toString();
if(!git.fileExistedAt(vault, late, lateHash)) return fail("late existed");
if(git.fileExistedAt(vault, late, hash1)) return fail("late should not exist at v1");
if(git.fileExistedAt(vault, late, "0000000000000000000000000000000000000000"))
return fail("bad hash existed");
}
// commitFile: точечный коммит одного файла (второй файл не трогаем)
{
QFile re(file);
if(!re.open(QIODevice::WriteOnly)) return fail("recreate");
re.write("v-final\n");
re.close();
if(!git.commitFile(vault, file, "final A")) return fail("commitFile A", git.lastError());
const int histA = git.fileHistory(vault, file).size();
// правим оба файла
QFile fa(file);
fa.open(QIODevice::WriteOnly | QIODevice::Append); fa.write("more A\n"); fa.close();
QFile fb(vault+"/other.md");
fb.open(QIODevice::WriteOnly | QIODevice::Append); fb.write("more B\n"); fb.close();
const int histBBefore = git.fileHistory(vault, vault+"/other.md").size();
if(!git.commitFile(vault, file, "final B")) return fail("commitFile B", git.lastError());
if(git.fileHistory(vault, file).size() != histA + 1) return fail("commitFile history A");
if(git.fileHistory(vault, vault+"/other.md").size() != histBBefore) return fail("commitFile затронул B");
if(!git.fileHistory(vault, file).first().toMap().value("message").toString().contains("final B"))
return fail("commitFile message");
// commitFile без изменений — false с понятной ошибкой, как commitAll
if(git.commitFile(vault, file, "noop")) return fail("commitFile noop should fail");
}
std::printf("GIT HISTORY TEST OK (forward/backward, diff, restore, time)\n");
return 0;
}