git-sync-web 自动提交 2026-09-08 17:31:52

This commit is contained in:
tamakyi
2026-09-08 17:31:52 +08:00
parent b16e589dd9
commit be99e73a0e
4 changed files with 60 additions and 10 deletions
+46 -10
View File
@@ -393,7 +393,7 @@ func (e *Engine) SyncNow(name string) error {
if s.syncing {
return fmt.Errorf("正在同步中,请稍候")
}
go e.syncOnce(s, "手动触发")
go e.safeSync(s, "手动触发")
return nil
}
@@ -446,7 +446,8 @@ func (e *Engine) setOK(s *repoState, detail string) {
}
// ensureRemotes 把配置中的远程同步到仓库(缺失则添加,URL 变化则更新)
func (e *Engine) ensureRemotes(s *repoState, cfg *RepoConfig) { for _, r := range cfg.Remotes {
func (e *Engine) ensureRemotes(s *repoState, cfg *RepoConfig) {
for _, r := range cfg.Remotes {
out, err := gitRun(cfg.RepoPath, 15*time.Second, "remote", "get-url", r.Name)
if err != nil {
if _, aerr := gitRun(cfg.RepoPath, 15*time.Second, "remote", "add", r.Name, r.URL); aerr != nil {
@@ -476,6 +477,22 @@ func (e *Engine) refreshCounts(s *repoState, cfg *RepoConfig, remote string) {
e.pushStatus(s)
}
// safeSync 包装 syncOnce:捕获后台 goroutine 的 panic,避免整个进程被杀死。
// 此前「立即同步后程序自动退出」的直接原因就是后台 goroutine panic 无 recover。
func (e *Engine) safeSync(s *repoState, trigger string) {
defer func() {
if r := recover(); r != nil {
stack := string(debug.Stack())
if len(stack) > 4096 {
stack = stack[:4096] + "\n…(堆栈已截断)"
}
log.Printf("[panic] syncOnce(%s): %v\n%s", s.cfg.Name, r, stack)
e.logf(s, "error", "同步发生内部异常(已恢复,进程继续运行): %v", r)
}
}()
e.syncOnce(s, trigger)
}
func (e *Engine) syncOnce(s *repoState, trigger string) {
s.mu.Lock()
if s.syncing {
@@ -511,6 +528,32 @@ func (e *Engine) syncOnce(s *repoState, trigger string) {
return
}
// 分支对齐:本地当前分支与配置不一致时,自动切换/创建(对齐原 bash 脚本行为)
// 否则 push 会因 src refspec 不存在而失败
if cur, cerr := currentBranch(cfg.RepoPath); cerr == nil && cur != cfg.Branch {
switch {
case branchExists(cfg.RepoPath, cfg.Branch):
if _, err := gitRun(cfg.RepoPath, 30*time.Second, "checkout", cfg.Branch); err != nil {
e.logf(s, "warn", "切换到配置分支 %s 失败: %v(继续在 %s 上同步)", cfg.Branch, err, cur)
} else {
e.logf(s, "info", "本地分支与配置不一致(%s → %s),已自动切换", cur, cfg.Branch)
}
case remoteRefExists(cfg.RepoPath, remote, cfg.Branch):
if _, err := gitRun(cfg.RepoPath, 30*time.Second, "checkout", "-B", cfg.Branch, remote+"/"+cfg.Branch); err != nil {
e.logf(s, "warn", "基于 %s/%s 创建分支失败: %v(继续在 %s 上同步)", remote, cfg.Branch, err, cur)
} else {
_, _ = gitRun(cfg.RepoPath, 15*time.Second, "branch", "--set-upstream-to", remote+"/"+cfg.Branch, cfg.Branch)
e.logf(s, "info", "本地无分支 %s,已基于 %s/%s 创建并跟踪", cfg.Branch, remote, cfg.Branch)
}
default:
if _, err := gitRun(cfg.RepoPath, 30*time.Second, "checkout", "-B", cfg.Branch); err != nil {
e.logf(s, "warn", "创建分支 %s 失败: %v(继续在 %s 上同步)", cfg.Branch, err, cur)
} else {
e.logf(s, "info", "本地与远程均无分支 %s,已基于当前 HEAD(%s)创建", cfg.Branch, cur)
}
}
}
dirty := dirtyCount(cfg.RepoPath)
if dirty > 0 {
if cfg.AutoCommit {
@@ -551,18 +594,11 @@ func (e *Engine) syncOnce(s *repoState, trigger string) {
s.mu.Unlock()
e.pushStatus(s)
aborted := false
if ahead > 0 && (cfg.Mode == "push" || cfg.Mode == "both") {
if !e.pushAll(s, cfg, ahead) {
aborted = true // 推送失败,本轮终止,避免继续拉取加剧分叉
}
}
if !aborted && behind > 0 && (cfg.Mode == "pull" || cfg.Mode == "both") {
if behind > 0 && (cfg.Mode == "pull" || cfg.Mode == "both") {
if !e.pullRemote(s, cfg, remote, behind) {
return
}
}
ahead, behind, _ = aheadBehind(cfg.RepoPath, remote, cfg.Branch)
now := time.Now().Format("2006-01-02 15:04:05")
s.mu.Lock()
+6
View File
@@ -72,6 +72,12 @@ func remoteRefExists(path, remote, branch string) bool {
return err == nil
}
// branchExists 检查本地分支是否存在
func branchExists(path, branch string) bool {
_, err := gitRun(path, 15*time.Second, "show-ref", "--verify", "-q", "refs/heads/"+branch)
return err == nil
}
func dirtyCount(path string) int {
out, err := gitRun(path, 30*time.Second, "status", "--porcelain")
if err != nil {
+7
View File
@@ -73,3 +73,10 @@ error: failed to push some refs to 'https://git.shiroko.one/tamakyi/github-auto-
[2026-09-08 17:28:21] === 同步完成(本地领先 0,落后 0)===
[2026-09-08 17:29:22] === 开始同步(定时检查,模式 push)===
[2026-09-08 17:29:30] 检测到 2 个未提交改动,执行自动提交
[2026-09-08 17:30:38] 自动提交完成,将随本轮同步推送
[2026-09-08 17:30:40] 远程分支 origin/main 不存在(远程仓库可能为空或分支名不一致),将推送本地 11 个提交以建立远程分支
[2026-09-08 17:30:42] 推送至 origin 失败: error: src refspec main does not match any
error: failed to push some refs to 'https://git.shiroko.one/tamakyi/github-auto-sync-golang'
[2026-09-08 17:30:43] === 同步完成(本地领先 0,落后 0)===
[2026-09-08 17:31:43] === 开始同步(定时检查,模式 push)===
[2026-09-08 17:31:51] 检测到 4 个未提交改动,执行自动提交
+1
View File
@@ -9,3 +9,4 @@ timestamp,action,result,commits
2026-09-08 17:25:49,push,failed,8
2026-09-08 17:27:05,push,failed,9
2026-09-08 17:28:21,push,failed,10
2026-09-08 17:30:42,push,failed,11
1 timestamp action result commits
9 2026-09-08 17:25:49 push failed 8
10 2026-09-08 17:27:05 push failed 9
11 2026-09-08 17:28:21 push failed 10
12 2026-09-08 17:30:42 push failed 11