From db6ab9a701e864a6e7bf91a9ecfef42be35f5a9b Mon Sep 17 00:00:00 2001 From: shop Date: Sun, 17 May 2026 14:46:36 +0300 Subject: [PATCH] =?UTF-8?q?fix:=20=D1=81=D1=80=D0=B0=D0=B2=D0=BD=D0=B5?= =?UTF-8?q?=D0=BD=D0=B8=D0=B5=20=D1=81=20origin=20=D1=87=D0=B5=D1=80=D0=B5?= =?UTF-8?q?=D0=B7=20merge-base=20(=D1=80=D0=B0=D1=81=D1=85=D0=BE=D0=B6?= =?UTF-8?q?=D0=B4=D0=B5=D0=BD=D0=B8=D0=B5=20=D0=B2=D0=B5=D1=82=D0=BE=D0=BA?= =?UTF-8?q?)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Cursor --- src/services/git-deploy.js | 59 ++++++++++++++++++++++++++++++++++---- src/views/admin/system.ejs | 12 +++++++- 2 files changed, 64 insertions(+), 7 deletions(-) diff --git a/src/services/git-deploy.js b/src/services/git-deploy.js index f01e883..5f1bb74 100644 --- a/src/services/git-deploy.js +++ b/src/services/git-deploy.js @@ -117,17 +117,57 @@ async function gitCmd(args, cwd, { needsWrite = false } = {}) { } } -/** Сколько коммитов на origin/main без записи в .git (без fetch) */ -async function getCommitsBehind(root) { +/** + * Сравнение с origin/main через ls-remote + merge-base (без fetch, без записи в .git). + */ +async function getRemoteSyncStatus(root) { const localHead = (await gitCmd(['rev-parse', 'HEAD'], root)).split('\n')[0].trim(); const remoteOut = await gitCmd(['ls-remote', 'origin', 'refs/heads/main'], root); const remoteSha = remoteOut.split(/\s+/)[0]?.trim(); if (!remoteSha) { throw new Error('Не найден refs/heads/main на origin'); } - if (remoteSha === localHead) return 0; - const count = await gitCmd(['rev-list', '--count', `${localHead}..${remoteSha}`], root); - return parseInt(count, 10) || 0; + + const remoteShort = ( + await gitCmd(['rev-parse', '--short', remoteSha], root) + ).split('\n')[0].trim(); + + if (remoteSha === localHead) { + return { behind: 0, ahead: 0, diverged: false, remoteShort, remoteSha }; + } + + let mergeBase; + try { + mergeBase = (await gitCmd(['merge-base', localHead, remoteSha], root)).split('\n')[0].trim(); + } catch { + throw new Error( + 'Не удалось сравнить с origin/main (нет общего предка). Выполните на сервере: git fetch && git reset --hard origin/main' + ); + } + + if (!mergeBase) { + throw new Error('Нет общего предка с origin/main'); + } + + let behind = 0; + let ahead = 0; + + if (mergeBase !== remoteSha) { + const behindStr = await gitCmd(['rev-list', '--count', `${mergeBase}..${remoteSha}`], root); + behind = parseInt(behindStr, 10) || 0; + } + if (mergeBase !== localHead) { + const aheadStr = await gitCmd(['rev-list', '--count', `${mergeBase}..${localHead}`], root); + ahead = parseInt(aheadStr, 10) || 0; + } + + return { + behind, + ahead, + diverged: behind > 0 && ahead > 0, + remoteShort, + remoteSha, + }; } async function getGitInfo({ fetchRemote = false } = {}) { @@ -157,6 +197,9 @@ async function getGitInfo({ fetchRemote = false } = {}) { dirty: false, dirtyHint: null, behind: null, + ahead: null, + diverged: false, + remoteShort: null, updateEnabled: isUpdateEnabled(), platform: process.platform, }; @@ -191,7 +234,11 @@ async function getGitInfo({ fetchRemote = false } = {}) { if (fetchRemote) { try { - info.behind = await getCommitsBehind(root); + const sync = await getRemoteSyncStatus(root); + info.behind = sync.behind; + info.ahead = sync.ahead; + info.diverged = sync.diverged; + info.remoteShort = sync.remoteShort; } catch (err) { info.fetchError = err.message; if (repoOwner) { diff --git a/src/views/admin/system.ejs b/src/views/admin/system.ejs index ac0f99a..83245e9 100644 --- a/src/views/admin/system.ejs +++ b/src/views/admin/system.ejs @@ -50,8 +50,18 @@ <% if (git.behind != null) { %>
На origin/main
- <% if (git.behind > 0) { %> + <% if (git.remoteShort) { %> + Удалённо: <%= git.remoteShort %>
+ <% } %> + <% if (git.diverged) { %> + Истории разошлись + Можно подтянуть: <%= git.behind %> комм. + Локально впереди на <%= git.ahead %> комм. +

Обновление из админки сделает git pull (как на origin). Локальные коммиты могут быть сброшены.

+ <% } else if (git.behind > 0) { %> Доступно обновлений: <%= git.behind %> + <% } else if (git.ahead > 0) { %> + Локально впереди origin на <%= git.ahead %> комм. <% } else { %> Актуально <% } %>