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 {
|
if s.syncing {
|
||||||
return fmt.Errorf("正在同步中,请稍候")
|
return fmt.Errorf("正在同步中,请稍候")
|
||||||
}
|
}
|
||||||
go e.syncOnce(s, "手动触发")
|
go e.safeSync(s, "手动触发")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -446,7 +446,8 @@ func (e *Engine) setOK(s *repoState, detail string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ensureRemotes 把配置中的远程同步到仓库(缺失则添加,URL 变化则更新)
|
// 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)
|
out, err := gitRun(cfg.RepoPath, 15*time.Second, "remote", "get-url", r.Name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if _, aerr := gitRun(cfg.RepoPath, 15*time.Second, "remote", "add", r.Name, r.URL); aerr != 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)
|
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) {
|
func (e *Engine) syncOnce(s *repoState, trigger string) {
|
||||||
s.mu.Lock()
|
s.mu.Lock()
|
||||||
if s.syncing {
|
if s.syncing {
|
||||||
@@ -511,6 +528,32 @@ func (e *Engine) syncOnce(s *repoState, trigger string) {
|
|||||||
return
|
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)
|
dirty := dirtyCount(cfg.RepoPath)
|
||||||
if dirty > 0 {
|
if dirty > 0 {
|
||||||
if cfg.AutoCommit {
|
if cfg.AutoCommit {
|
||||||
@@ -551,18 +594,11 @@ func (e *Engine) syncOnce(s *repoState, trigger string) {
|
|||||||
s.mu.Unlock()
|
s.mu.Unlock()
|
||||||
e.pushStatus(s)
|
e.pushStatus(s)
|
||||||
|
|
||||||
aborted := false
|
if behind > 0 && (cfg.Mode == "pull" || cfg.Mode == "both") {
|
||||||
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 !e.pullRemote(s, cfg, remote, behind) {
|
if !e.pullRemote(s, cfg, remote, behind) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ahead, behind, _ = aheadBehind(cfg.RepoPath, remote, cfg.Branch)
|
ahead, behind, _ = aheadBehind(cfg.RepoPath, remote, cfg.Branch)
|
||||||
now := time.Now().Format("2006-01-02 15:04:05")
|
now := time.Now().Format("2006-01-02 15:04:05")
|
||||||
s.mu.Lock()
|
s.mu.Lock()
|
||||||
|
|||||||
@@ -72,6 +72,12 @@ func remoteRefExists(path, remote, branch string) bool {
|
|||||||
return err == nil
|
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 {
|
func dirtyCount(path string) int {
|
||||||
out, err := gitRun(path, 30*time.Second, "status", "--porcelain")
|
out, err := gitRun(path, 30*time.Second, "status", "--porcelain")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -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:28:21] === 同步完成(本地领先 0,落后 0)===
|
||||||
[2026-09-08 17:29:22] === 开始同步(定时检查,模式 push)===
|
[2026-09-08 17:29:22] === 开始同步(定时检查,模式 push)===
|
||||||
[2026-09-08 17:29:30] 检测到 2 个未提交改动,执行自动提交
|
[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 个未提交改动,执行自动提交
|
||||||
|
|||||||
@@ -9,3 +9,4 @@ timestamp,action,result,commits
|
|||||||
2026-09-08 17:25:49,push,failed,8
|
2026-09-08 17:25:49,push,failed,8
|
||||||
2026-09-08 17:27:05,push,failed,9
|
2026-09-08 17:27:05,push,failed,9
|
||||||
2026-09-08 17:28:21,push,failed,10
|
2026-09-08 17:28:21,push,failed,10
|
||||||
|
2026-09-08 17:30:42,push,failed,11
|
||||||
|
|||||||
|
Reference in New Issue
Block a user