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:
@@ -32,6 +32,9 @@ Dialog {
|
||||
property string currentContent: ""
|
||||
property string emptyMessage: qsTr("Нет версии для просмотра")
|
||||
property bool busy: false
|
||||
// Пагинация: подгружаем порциями, чтобы revwalk не бежал по всей истории сразу
|
||||
property int historyLimit: 60
|
||||
property bool hasMore: false
|
||||
|
||||
function fileDirOf(p) {
|
||||
const i = p.lastIndexOf("/")
|
||||
@@ -66,13 +69,23 @@ Dialog {
|
||||
return
|
||||
}
|
||||
busy = true
|
||||
// берём историю
|
||||
const hist = gitManager.fileHistory(vaultPath, filePath)
|
||||
historyModel = hist
|
||||
listView.model = hist
|
||||
if (hist.length>0) {
|
||||
selectedIndex = 0
|
||||
selectedHash = hist[0].hash
|
||||
// берём историю порцией limit+1: лишний элемент — признак «есть ещё»
|
||||
const keepHash = selectedHash
|
||||
const hist = gitManager.fileHistory(vaultPath, filePath, historyLimit + 1)
|
||||
hasMore = hist.length > historyLimit
|
||||
const shown = hasMore ? hist.slice(0, historyLimit) : hist
|
||||
historyModel = shown
|
||||
listView.model = shown
|
||||
if (shown.length>0) {
|
||||
// сохраняем выбор при «Показать ещё»/обновлении
|
||||
let idx = 0
|
||||
if (keepHash.length > 0) {
|
||||
for (let i = 0; i < shown.length; ++i) {
|
||||
if (shown[i].hash === keepHash) { idx = i; break }
|
||||
}
|
||||
}
|
||||
selectedIndex = idx
|
||||
selectedHash = shown[idx].hash
|
||||
updateDiff()
|
||||
} else {
|
||||
selectedIndex = -1
|
||||
@@ -93,13 +106,15 @@ Dialog {
|
||||
if (selectedHash.length===0) {
|
||||
versionContent = ""
|
||||
currentContent = ""
|
||||
busy = false
|
||||
return
|
||||
}
|
||||
// свежее содержимое с диска — diff всегда актуален, даже если файл правили при открытом диалоге
|
||||
currentContent = gitManager.workdirContent(filePath)
|
||||
// содержимое версии — сырой markdown, отрисуется через MarkdownView как в «Просмотре»,
|
||||
// с подсветкой отличий от текущего файла прямо там
|
||||
// с подсветкой отличий от текущего файла прямо там (считается в рабочем потоке)
|
||||
versionContent = gitManager.fileContentAt(vaultPath, filePath, selectedHash)
|
||||
busy = currentContent.length > 0 && versionContent.length > 0
|
||||
}
|
||||
|
||||
onOpened: reload()
|
||||
@@ -280,6 +295,12 @@ Dialog {
|
||||
font.family: "Menlo, monospace"
|
||||
}
|
||||
}
|
||||
Button {
|
||||
visible: root.hasMore
|
||||
Layout.fillWidth: true
|
||||
text: qsTr("Показать ещё")
|
||||
onClicked: { root.historyLimit += 60; root.reload() }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -302,16 +323,17 @@ Dialog {
|
||||
ScrollBar.vertical: ScrollBar {}
|
||||
Column {
|
||||
width: versionScroll.availableWidth
|
||||
MarkdownView {
|
||||
id: versionPreview
|
||||
width: parent.width
|
||||
markdown: root.versionContent
|
||||
compareMarkdown: root.currentContent
|
||||
vaultRoot: root.vaultPath
|
||||
fileDir: root.fileDirOf(root.filePath)
|
||||
emptyText: root.emptyMessage
|
||||
onLinkActivated: (href) => root.handleHistoryLink(href)
|
||||
}
|
||||
MarkdownView {
|
||||
id: versionPreview
|
||||
width: parent.width
|
||||
markdown: root.versionContent
|
||||
compareMarkdown: root.currentContent
|
||||
vaultRoot: root.vaultPath
|
||||
fileDir: root.fileDirOf(root.filePath)
|
||||
emptyText: root.emptyMessage
|
||||
onLinkActivated: (href) => root.handleHistoryLink(href)
|
||||
onDiffApplied: root.busy = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -355,7 +377,10 @@ Dialog {
|
||||
text: qsTr("Восстановить эту версию")
|
||||
enabled: root.selectedHash.length>0
|
||||
highlighted: true
|
||||
onClicked: confirmRestore.open()
|
||||
onClicked: {
|
||||
confirmRestore.willDelete = !gitManager.fileExistedAt(root.vaultPath, root.filePath, root.selectedHash)
|
||||
confirmRestore.open()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -363,6 +388,7 @@ Dialog {
|
||||
// Подтверждение восстановления
|
||||
Dialog {
|
||||
id: confirmRestore
|
||||
property bool willDelete: false
|
||||
title: qsTr("Восстановить версию?")
|
||||
modal: true
|
||||
anchors.centerIn: parent
|
||||
@@ -373,10 +399,12 @@ Dialog {
|
||||
anchors.fill: parent
|
||||
spacing: 12
|
||||
Label {
|
||||
text: qsTr("Файл будет заменён на выбранную версию и сохранён как новая версия. Вы сможете снова вернуться к текущей версии через историю.")
|
||||
text: confirmRestore.willDelete
|
||||
? qsTr("ВНИМАНИЕ: в выбранной версии этого файла не было — файл будет УДАЛЁН (это тоже сохранится как новая версия, отменить можно через историю).")
|
||||
: qsTr("Файл будет заменён на выбранную версию и сохранён как новая версия. Вы сможете снова вернуться к текущей версии через историю.")
|
||||
wrapMode: Text.Wrap
|
||||
Layout.fillWidth: true
|
||||
color: Theme.text
|
||||
color: confirmRestore.willDelete ? Theme.errorText : Theme.text
|
||||
}
|
||||
Label {
|
||||
text: qsTr("Версия: %1 (%2)").arg(root.selectedHash.substring(0,7)).arg(root.selectedIndex>=0 && historyModel.length>root.selectedIndex ? historyModel[root.selectedIndex].timeStr : "")
|
||||
@@ -389,7 +417,7 @@ Dialog {
|
||||
Item { Layout.fillWidth: true }
|
||||
Button { text: qsTr("Отмена"); onClicked: confirmRestore.close() }
|
||||
Button {
|
||||
text: qsTr("Восстановить")
|
||||
text: confirmRestore.willDelete ? qsTr("Удалить файл") : qsTr("Восстановить")
|
||||
highlighted: true
|
||||
onClicked: {
|
||||
confirmRestore.close()
|
||||
|
||||
Reference in New Issue
Block a user