From 0a46471cc69fac3a6978dff6b1673edb83c15420 Mon Sep 17 00:00:00 2001 From: grendervill Date: Mon, 7 Sep 2026 01:06:21 +0300 Subject: [PATCH] =?UTF-8?q?=D0=98=D0=BD=D0=BA=D1=80=D0=B5=D0=BC=D0=B5?= =?UTF-8?q?=D0=BD=D1=82=D0=B0=D0=BB=D1=8C=D0=BD=D1=8B=D0=B9=20=D0=B8=D0=BD?= =?UTF-8?q?=D0=B4=D0=B5=D0=BA=D1=81,=20=D0=BF=D1=80=D0=B5=D1=81=D0=B5?= =?UTF-8?q?=D1=82=D1=8B=20=D0=B0=D0=B2=D1=82=D0=BE=D1=81=D0=B5=D0=B9=D0=B2?= =?UTF-8?q?=D0=B0,=20=D1=83=D0=BF=D1=80=D0=BE=D1=89=D0=B5=D0=BD=D0=B8?= =?UTF-8?q?=D0=B5=20git-=D1=81=D0=BB=D0=BE=D1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - VaultIndex: updateFile/removeFile/syncIncremental по mtime+size, полный рескан только на старте/смене/внешних правках; relink висячих ссылок; FileStore.externalChanged - QML: точечные обновления индекса вместо rebuild на каждый сейв - Настройки: пресеты safe/balanced/eco/manual + диалог с +/- и шкалами; коммит после паузы, в manual — по Ctrl+S - GitManager: кэш истории, пагинация, diff в потоке, точечный commitFile, fileExistedAt, топологический порядок истории - Удалены highlightedCode, словный merge, fileDiffText/Html, 2-arg fileHistory --- src/git/GitManager.cpp | 15 ++++++++++----- src/git/GitManager.h | 2 +- tests/git_history_test.cpp | 13 +++++++++++++ 3 files changed, 24 insertions(+), 6 deletions(-) diff --git a/src/git/GitManager.cpp b/src/git/GitManager.cpp index 6fa1312..ef23334 100644 --- a/src/git/GitManager.cpp +++ b/src/git/GitManager.cpp @@ -407,14 +407,19 @@ QVariantList GitManager::fileHistory(const QString &vaultPath, const QString &fi git_repository_free(repo); return out; } - git_revwalk_sorting(walk, GIT_SORT_TIME); + // TIME|TOPOLOGICAL: время + гарантия «потомки раньше родителей». + // Один TIME при коммитах в одну секунду тасует порядок (баг: «новой» могла + // оказаться старая версия); история хранилища линейная, топология даёт точный порядок. + git_revwalk_sorting(walk, GIT_SORT_TIME | GIT_SORT_TOPOLOGICAL); git_revwalk_push_head(walk); + // Собираем до limit КАСАНИЙ файла (а не коммитов): иначе на активной истории + // «Показать ещё» врало бы при разреженных касаниях. Перебор коммитов ограничен. git_oid oid; - int iter = 0; - const int maxIter = limit; // ограничение для скорости - while (git_revwalk_next(&oid, walk) == 0 && iter < maxIter) { - ++iter; + int scanned = 0; + const int scanCap = 2000; + while (git_revwalk_next(&oid, walk) == 0 && (int)out.size() < limit && scanned < scanCap) { + ++scanned; git_commit *commit = nullptr; if (git_commit_lookup(&commit, repo, &oid) < 0) continue; diff --git a/src/git/GitManager.h b/src/git/GitManager.h index 76276eb..515bb07 100644 --- a/src/git/GitManager.h +++ b/src/git/GitManager.h @@ -37,7 +37,7 @@ public: // История файла: список коммитов где файл менялся. Сортировка новые сверху. // Каждый элемент: {hash, shortHash, message, author, time (QDateTime), timeStr, timeAgo}. - // Не больше limit свежих (limit<=0 — до 500). + // Не больше limit КАСАНИЙ (limit<=0 — до 500); перебор коммитов ограничен изнутри. // Для кнопки «Показать ещё»: запросить limit+1, hasMore = (размер > limit). Q_INVOKABLE QVariantList fileHistory(const QString &vaultPath, const QString &filePath, int limit) const; diff --git a/tests/git_history_test.cpp b/tests/git_history_test.cpp index 11e89fd..107a53e 100644 --- a/tests/git_history_test.cpp +++ b/tests/git_history_test.cpp @@ -167,6 +167,19 @@ int main(int argc, char *argv[]) return fail("paged cache"); // limit<=0 — как раньше (всё) if(git.fileHistory(vault, file, 0).size() != full.size()) return fail("limit 0"); + // разреженная история: 30 чужих коммитов — касания файла всё равно находятся, + // hasMore-логика (limit+1) при этом не врёт + for (int i = 0; i < 30; ++i) { + QFile fn(vault+"/noise.md"); + if(!fn.open(QIODevice::WriteOnly | QIODevice::Append)) return fail("noise open"); + fn.write(QString("n%1\n").arg(i).toUtf8()); + fn.close(); + if(!git.commitAll(vault, QString("noise %1").arg(i))) return fail("noise commit", git.lastError()); + } + QVariantList sparse = git.fileHistory(vault, file, 3); + if(sparse.size() != 3) return fail("sparse size", QString::number(sparse.size())); + if(sparse.first().toMap().value("hash") != full.first().toMap().value("hash")) + return fail("sparse order"); } // fileExistedAt: отличает «пустой файл» от «файла не было»