git-sync-web 自动提交 2026-09-08 17:31:52
This commit is contained in:
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user