diff --git a/static/app.js b/static/app.js
index 48c4226..0a15b15 100644
--- a/static/app.js
+++ b/static/app.js
@@ -62,6 +62,7 @@ function card(r) {
${r.auto_commit ? '自动提交' : ''}
${r.cron ? `cron` : ''}
${r.conflicts && r.conflicts.length ? `冲突 ${r.conflicts.length}` : ''}
+ ${r.push_conflict ? `推送冲突` : ''}
上次同步:${esc(last)}
${r.last_sync && r.last_sync.detail ? `${esc(r.last_sync.detail)}
` : ''}
@@ -106,6 +107,7 @@ function connect() {
const i = repos.findIndex(x => x.name === msg.repo);
if (i >= 0) repos[i] = msg.data; else repos.push(msg.data);
render();
+ updateConflictActions();
} else if (msg.type === 'log') {
appendLiveLine(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; });
if (t === 'log' && current) loadLog(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);
}
@@ -328,6 +330,43 @@ $('#btn-abort').addEventListener('click', () => {
});
$('#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) {
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-auth').value = r ? r.auth_method : 'ssh';
$('#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-interval').value = r ? r.retry_interval : 5;
$('#f-autocommit').checked = r ? !!r.auto_commit : false;
@@ -411,6 +451,7 @@ function readForm() {
retry_count: parseInt($('#f-retry').value, 10) || 0,
retry_interval: parseInt($('#f-retry-interval').value, 10) || 0,
conflict_strategy: $('#f-conflict').value,
+ push_strategy: $('#f-push').value,
auto_commit: $('#f-autocommit').checked,
cron: $('#f-cron').value.trim(),
log_file: $('#f-log').value.trim(),
diff --git a/static/index.html b/static/index.html
index 132956d..adba04b 100644
--- a/static/index.html
+++ b/static/index.html
@@ -57,10 +57,20 @@
加载中…
-
+
+
+
+
+
+
+
+
+
+
+
@@ -105,6 +115,13 @@
+