git-sync-web 自动提交 2026-09-09 12:04:01

This commit is contained in:
tamakyi
2026-09-09 12:04:02 +08:00
parent 05d6f1bc84
commit a832d0659a
2 changed files with 60 additions and 2 deletions
+42 -1
View File
@@ -62,6 +62,7 @@ function card(r) {
${r.auto_commit ? '<span class="badge">自动提交</span>' : ''} ${r.auto_commit ? '<span class="badge">自动提交</span>' : ''}
${r.cron ? `<span class="badge" title="${esc(r.cron)}">cron</span>` : ''} ${r.cron ? `<span class="badge" title="${esc(r.cron)}">cron</span>` : ''}
${r.conflicts && r.conflicts.length ? `<span class="badge b-conflict">冲突 ${r.conflicts.length}</span>` : ''} ${r.conflicts && r.conflicts.length ? `<span class="badge b-conflict">冲突 ${r.conflicts.length}</span>` : ''}
${r.push_conflict ? `<span class="badge b-conflict">推送冲突</span>` : ''}
</div> </div>
<div class="last">上次同步:${esc(last)}</div> <div class="last">上次同步:${esc(last)}</div>
${r.last_sync && r.last_sync.detail ? `<div class="last-detail" title="${esc(r.last_sync.detail)}">${esc(r.last_sync.detail)}</div>` : ''} ${r.last_sync && r.last_sync.detail ? `<div class="last-detail" title="${esc(r.last_sync.detail)}">${esc(r.last_sync.detail)}</div>` : ''}
@@ -106,6 +107,7 @@ function connect() {
const i = repos.findIndex(x => x.name === msg.repo); const i = repos.findIndex(x => x.name === msg.repo);
if (i >= 0) repos[i] = msg.data; else repos.push(msg.data); if (i >= 0) repos[i] = msg.data; else repos.push(msg.data);
render(); render();
updateConflictActions();
} else if (msg.type === 'log') { } else if (msg.type === 'log') {
appendLiveLine(msg); appendLiveLine(msg);
if (current === msg.repo && !$('#tab-log').hidden) appendLogLine(`[${msg.time}] ${msg.msg}`); if (current === msg.repo && !$('#tab-log').hidden) appendLogLine(`[${msg.time}] ${msg.msg}`);
@@ -204,7 +206,7 @@ function showTab(t) {
document.querySelectorAll('.tab-pane').forEach(p => { p.hidden = p.id !== 'tab-' + t; }); document.querySelectorAll('.tab-pane').forEach(p => { p.hidden = p.id !== 'tab-' + t; });
if (t === 'log' && current) loadLog(current); if (t === 'log' && current) loadLog(current);
if (t === 'history' && current) loadHistory(current); if (t === 'history' && current) loadHistory(current);
if (t === 'conflict' && current) loadConflicts(current); if (t === 'conflict' && current) { loadConflicts(current); updateConflictActions(); }
if (t === 'stats' && current) loadStats(current); if (t === 'stats' && current) loadStats(current);
} }
@@ -328,6 +330,43 @@ $('#btn-abort').addEventListener('click', () => {
}); });
$('#btn-resolved').addEventListener('click', () => doAction('resolve', '冲突处理流程完成')); $('#btn-resolved').addEventListener('click', () => doAction('resolve', '冲突处理流程完成'));
// 手动拉取 / 推送 / 获取(B1:后端 ManualGit 已支持)
$('#btn-fetch').addEventListener('click', () => doAction('fetch', '获取完成'));
$('#btn-pull').addEventListener('click', () => doAction('pull', '拉取完成'));
$('#btn-push').addEventListener('click', () => doAction('push', '推送完成'));
// 推送冲突(non-fast-forward)的解决入口(B3)
$('#btn-force-push').addEventListener('click', async () => {
if (!current) return;
try {
await post(`/api/repos/${current}/force-push`);
alert('已发起强制推送(--force-with-lease)');
updateConflictActions();
} catch (err) { alert(err.message); }
});
$('#btn-abort-push').addEventListener('click', async () => {
if (!current) return;
try {
await post(`/api/repos/${current}/abort-push`);
alert('已放弃强制推送');
updateConflictActions();
} catch (err) { alert(err.message); }
});
// 根据当前仓库快照切换推送冲突操作区显隐(B3)
function updateConflictActions() {
const r = repos.find(x => x.name === current);
const box = $('#push-conflict-actions');
const hint = $('#push-conflict-hint');
if (!box) return;
if (r && r.push_conflict) {
box.hidden = false;
if (hint) hint.textContent = `远程 ${r.push_conflict_remote || ''} 分支 ${r.push_conflict_branch || ''} 有新提交,推送被拒绝`;
} else {
box.hidden = true;
}
}
// ---------- 统计 ---------- // ---------- 统计 ----------
function dateStr(d) { function dateStr(d) {
return d.getFullYear() + '-' + String(d.getMonth() + 1).padStart(2, '0') + '-' + String(d.getDate()).padStart(2, '0'); return d.getFullYear() + '-' + String(d.getMonth() + 1).padStart(2, '0') + '-' + String(d.getDate()).padStart(2, '0');
@@ -385,6 +424,7 @@ function fillForm(r) {
$('#f-interval').value = r ? r.interval : 60; $('#f-interval').value = r ? r.interval : 60;
$('#f-auth').value = r ? r.auth_method : 'ssh'; $('#f-auth').value = r ? r.auth_method : 'ssh';
$('#f-conflict').value = r ? r.conflict_strategy : 'merge'; $('#f-conflict').value = r ? r.conflict_strategy : 'merge';
$('#f-push').value = r ? r.push_strategy : 'manual';
$('#f-retry').value = r ? r.retry_count : 3; $('#f-retry').value = r ? r.retry_count : 3;
$('#f-retry-interval').value = r ? r.retry_interval : 5; $('#f-retry-interval').value = r ? r.retry_interval : 5;
$('#f-autocommit').checked = r ? !!r.auto_commit : false; $('#f-autocommit').checked = r ? !!r.auto_commit : false;
@@ -411,6 +451,7 @@ function readForm() {
retry_count: parseInt($('#f-retry').value, 10) || 0, retry_count: parseInt($('#f-retry').value, 10) || 0,
retry_interval: parseInt($('#f-retry-interval').value, 10) || 0, retry_interval: parseInt($('#f-retry-interval').value, 10) || 0,
conflict_strategy: $('#f-conflict').value, conflict_strategy: $('#f-conflict').value,
push_strategy: $('#f-push').value,
auto_commit: $('#f-autocommit').checked, auto_commit: $('#f-autocommit').checked,
cron: $('#f-cron').value.trim(), cron: $('#f-cron').value.trim(),
log_file: $('#f-log').value.trim(), log_file: $('#f-log').value.trim(),
+18 -1
View File
@@ -57,10 +57,20 @@
<div id="commit-list" class="commit-list">加载中…</div> <div id="commit-list" class="commit-list">加载中…</div>
</div> </div>
<div id="tab-conflict" class="tab-pane" hidden> <div id="tab-conflict" class="tab-pane" hidden>
<div class="conflict-actions"> <div class="manual-actions">
<button class="btn btn-sm" id="btn-fetch">获取</button>
<button class="btn btn-sm" id="btn-pull">拉取</button>
<button class="btn btn-sm" id="btn-push">推送</button>
</div>
<div class="conflict-actions" id="pull-conflict-actions">
<button class="btn btn-danger" id="btn-abort">放弃合并</button> <button class="btn btn-danger" id="btn-abort">放弃合并</button>
<button class="btn btn-primary" id="btn-resolved">我已在本地解决,继续</button> <button class="btn btn-primary" id="btn-resolved">我已在本地解决,继续</button>
</div> </div>
<div class="conflict-actions" id="push-conflict-actions" hidden>
<span class="hint" id="push-conflict-hint"></span>
<button class="btn btn-danger" id="btn-force-push">强制推送(--force-with-lease)</button>
<button class="btn" id="btn-abort-push">放弃强制推送</button>
</div>
<div id="conflict-list"></div> <div id="conflict-list"></div>
</div> </div>
<div id="tab-stats" class="tab-pane" hidden> <div id="tab-stats" class="tab-pane" hidden>
@@ -105,6 +115,13 @@
<option value="abort">abort(冲突后自动放弃)</option> <option value="abort">abort(冲突后自动放弃)</option>
</select> </select>
</label> </label>
<label>推送策略(non-fast-forward)
<select name="push_strategy" id="f-push">
<option value="manual">manual(冲突后等待人工决策)</option>
<option value="rebase">rebase(变基到远程后重试)</option>
<option value="force">force(--force-with-lease 强制推送)</option>
</select>
</label>
<label>重试次数(HTTPS) <label>重试次数(HTTPS)
<input name="retry_count" id="f-retry" type="number" min="0" value="3"> <input name="retry_count" id="f-retry" type="number" min="0" value="3">
</label> </label>