文章

用go实现一个git自动同步服务

用go实现一个git自动同步服务

[TOC]

本文项目见 git :

https://gitcode.com/qq_34649825/git-sync-service

一、服务功能与背景

1. 核心功能

  • 定时将 Windows 本地的 Git 仓库与远端仓库同步(定期执行 git pull,检测并提交本地变更并推送到到远程)
  • 日志自动管理(临时写入释放、大文件轮转、过期清理)
  • 支持作为 Windows 服务或通过任务计划程序运行,实现后台常驻运行,无需手动执行

2. 背景与适用场景

在多设备协作开发、服务器端自动备份等场景中,手动执行 git pull/push 操作不仅繁琐,还可能因遗漏导致代码版本不一致。本工具通过 Go 语言开发,专为 Windows 环境设计,旨在实现 Git 仓库的全自动化同步,解决以下问题

  • 多设备间代码同步不及时导致的协作冲突
  • 手动操作易遗忘、效率低的问题
  • 无人值守场景下的代码自动备份需求
  • 需长期稳定运行的后台同步任务管理

二、服务直接使用方法

1. 先决条件

  • Windows 操作系统(Windows Server 2016+ / Windows 10+)
  • 已安装并可在 PATH 中访问的 git
  • 已配置 SSH 密钥实现与远程仓库的免密连接(见后续详细步骤)

2. 快速获取与编译

2.1 编译可执行文件

在项目目录运行:

1
go build -o git-sync-service.exe main.go

或使用仓库自带脚本:

1
.\build.bat

若修改依赖后请运行:

1
go mod tidy

核心依赖:gopkg.in/ini.v1(配置解析)、github.com/robfig/cron/v3(定时任务)、github.com/kardianos/service(Windows 服务化)。

2.2 配置文件设置

编辑同目录下的 config.ini,设置必要参数:

1
2
3
4
5
6
7
8
9
[Git]
# 本地Git仓库路径(必须是已初始化的Git仓库,含.git目录)
RepoPath = F:/AllMyMarkdowns/gitcode
# 日志文件路径(服务运行日志输出位置)
LogPath  = F:/AllMyMarkdowns/autoPushGit_log/git_sync_new.log
# 定时同步表达式(Cron格式,5字段:分 时 日 月 周)
CronExpr = */1 * * * *
# 可选:SSH私钥路径(用于免密连接远程仓库)
PrivateKey = C:/Users/Administrator/.ssh/id_rsa
配置项 格式要求 示例 默认值
RepoPath 本地绝对路径,需存在.git目录 D:\Projects\test-repo 无(必须配置)
LogPath 绝对路径(目录不存在会自动创建) D:\Logs\git-sync.log 无(必须配置)
CronExpr Cron标准表达式 每5分钟:*/5 * * * *;每天23点:0 23 * * * 0 * * * *(每小时)
PrivateKey SSH私钥绝对路径 C:\Users\user\.ssh\id_rsa 无(可选)

3. 运行方式选择

3.1 作为 Windows 服务运行

安装(需管理员权限):

1
2
3
.\git-sync-service.exe install
# 或使用脚本:
.\install_service.bat

启动服务:

1
net start GitAutoSyncService

卸载:

1
2
3
.\git-sync-service.exe remove
# 或使用脚本:
.\uninstall_service.bat

注意事项

  • 服务默认以 LocalSystem 身份运行,可能无法访问用户级 SSH agent
  • 若需使用用户凭证,需以指定用户身份安装服务
  • 可通过 sc query GitAutoSyncService 查看服务状态

3.2 使用任务计划程序(推荐)

仓库中提供了 install_task.bat 脚本(在「任务计划程序」中创建任务 GitAutoSyncTask,触发条件:系统启动与用户登录,运行身份:SYSTEM)。

安装(需管理员权限):

1
.\install_task.bat

删除任务:

1
.\uninstall_task.bat

手动创建任务

在「任务计划程序」中创建任务,选择以当前用户运行(可使用用户 SSH agent,但需用户登录后才运行)。

4. 日志与排查

  • 日志位置:config.iniLogPath 指定的路径
  • 服务启动失败:查看 Windows 事件查看器和日志文件
  • Git 凭证问题:以目标账户手动运行 git pull/git push 复现并排查错误

5. 两种运行方式对比

特性 服务方式 任务计划方式
无人登录运行 支持 不支持(依赖用户登录)
账号管理 复杂(需配置服务运行身份) 灵活(可选择当前用户)
安装难度 稍高 较低
适用场景 服务器无人值守 个人电脑脑或需用户凭证场景

三、服务详细搭建过程(开发实现)

1. 前置准备

1.1 环境要求

依赖项 要求 说明
Go环境 1.18+ 需配置GOROOT、GOPATH环境变量
Git客户端 任意稳定版本 需添加至系统PATH(支持命令行直接执行git命令)
Windows系统 Windows Server 2016+ / Windows 10+ 服务化依赖Windows API
SSH密钥 已配置 用于免密连接Git远程仓库(如GitHub、GitLab)

1.2 SSH 密钥配置(关键步骤)

1.2.1 生成 SSH 密钥

打开命令行(管理员身份),执行以下命令生成密钥(默认路径:C:\Users\用户名\.ssh\):

1
ssh-keygen -t rsa -b 4096 -C "my-email@example.com"
  • 执行过程中全部回车(默认无密码,避免服务运行时需要输入密钥密码)
1.2.2 配置远程仓库公钥
  1. 打开生成的公钥文件:C:\Users\用户名\.ssh\id_rsa.pub
  2. 复制文件内容(完整字符串,不含换行)
  3. 登录远程 Git 仓库(如 GitHub),进入「Settings → SSH and GPG keys → New SSH key」
  4. 粘贴公钥内容,保存即可(实现免密拉取/推送)

1.3 Go 环境配置

1.3.1 环境变量验证

执行以下命令,确保 Go 环境配置正常:

1
2
go version  # 输出Go版本(需1.18+)
go env      # 查看GOROOT、GOPATH配置
1.3.2 切换 Go 第三方源(解决依赖下载慢/失败)

因默认 Go 模块源(proxy.golang.org)国内访问不稳定,需切换至国内源:

1
2
3
4
# 永久设置国内源(阿里云)
go env -w GOPROXY=https://goproxy.cn,direct
# 允许访问私有仓库(如公司内部Git仓库,可选)
go env -w GOPRIVATE=*.your-company-domain.com

2. 项目结构与文件清单

1
2
3
4
5
6
7
8
9
git-sync-service/
├── main.go           # 服务核心代码
├── config.ini        # 配置文件
├── install_service.bat  # 服务安装脚本
├── uninstall_service.bat # 服务卸载脚本
├── install_task.bat    # 任务计划安装脚本
├── uninstall_task.bat  # 任务计划卸载脚本
├── build.bat          # 编译脚本
└── go.mod             # 依赖管理文件
文件名 作用 备注
main.go 服务核心代码 包含同步逻辑、服务化、日志管理等
config.ini 配置文件 需手动创建,配置仓库路径、日志路径等
install_service.bat 服务安装脚本 一键安装服务(需管理员身份运行)
uninstall_service.bat 服务卸载脚本 一键卸载服务(需管理员身份运行)
install_task.bat/uninstall_task.bat 任务计划管理脚本 用于创建/删除定时任务
git-sync-service.exe 编译后的可执行文件 服务运行核心程序

3. 核心代码实现思路

如下所有代码以 git 上的为准

3.1 配置加载模块(基于gopkg.in/ini.v1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 配置结构体定义
type Config {
    RepoPath   本地仓库路径
    LogPath    日志文件路径
    CronExpr   定时任务表达式
    PrivateKey SSH私钥路径
    ExeDir     程序运行目录
}

// 加载配置文件
func loadConfig(exeDir string) (Config, error) {
    1. 检查config.ini是否存在
    2. 使用ini库读取配置文件内容
    3. 解析并验证各配置项路径有效性Cron表达式格式
    4. 处理私钥路径检查文件存在性过滤无效路径
    5. 返回结构化配置对象
}

3.2 Git 同步逻辑

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// Git同步主函数
func (s *GitSyncService) syncGit() {
    1. 记录同步开始日志
    2. 设置同步超时时间防止无限阻塞
    3. 检查仓库是否为Git仓库验证.git目录存在
    4. 执行git pull拉取远程变更调用runGitCmd
    5. 检查本地变更并提交推送调用checkAndCommit
    6. 处理同步结果成功/失败/超时日志
}

// 执行Git命令通用函数
func (s *GitSyncService) runGitCmd(ctx context.Context, cmd string, timeout time.Duration, args ...string) error {
    1. 构建完整Git命令如git pullgit add等
    2. 设置命令运行目录为本地仓库路径
    3. 配置Windows隐藏窗口参数避免闪烁
    4. 若指定私钥设置GIT_SSH_COMMAND环境变量
    5. 执行命令并捕获输出
    6. 返回命令执行结果错误信息包含输出内容
}

// 检查变更并提交推送
func (s *GitSyncService) checkAndCommit(ctx context.Context) error {
    1. 执行git status --porcelain检查本地变更
    2. 若未检测到变更刷新Git索引后再次检查
    3. 若存在变更
        a. 执行git add .暂存所有变更
        b. 生成包含时间戳的自动提交信息
        c. 执行git commit提交变更
        d. 执行git push推送到远程仓库
    4. 返回执行过程中的错误信息
}

3.3 定时任务(基于github.com/robfig/cron/v3

1
2
3
4
5
6
7
8
9
10
11
import (
    "github.com/robfig/cron/v3"
)

// 启动定时任务
func startCron(cfg *Config, log *Logger) {
    1. 初始化cron调度器
    2. 添加定时任务使用配置中的Cron表达式
    3. 任务执行逻辑调用Git同步函数并记录日志
    4. 启动调度器
}

3.4 Windows服务化(基于github.com/kardianos/service

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// 服务核心结构体
type GitSyncService struct {
    cron    定时任务实例
    ctx     上下文对象用于控制服务生命周期
    cancel  取消函数
    wg      等待组用于优雅关闭
}

// 服务启动逻辑
func (s *GitSyncService) Start(service service.Service) error {
    1. 记录服务启动日志
    2. 初始化上下文和等待组
    3. 延迟初始化定时任务避免服务启动阻塞
    4. 添加同步任务到cron调度器包含配置的Cron表达式
    5. 启动定时任务调度器
}

// 服务停止逻辑
func (s *GitSyncService) Stop(service service.Service) error {
    1. 记录服务停止日志
    2. 触发上下文取消信号
    3. 停止定时任务调度器
    4. 等待所有同步任务完成
    5. 记录服务已停止日志
}

3.5 日志轮转与过期清理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// 日志管理任务(定时执行)
func startLogManageTask() {
    1. 创建每小时执行一次的定时器
    2. 循环执行
        a. 调用rotateLog()进行日志轮转
        b. 调用deleteExpiredLogs()清理过期日志
        c. 记录管理过程中的错误信息
}

// 日志轮转函数
func rotateLog() error {
    1. 检查当前日志文件大小
    2. 若超过最大限制
        a. 将当前日志重命名为带时间戳的历史文件
        b. 创建新的空日志文件
    3. 返回执行过程中的错误
}

// 删除过期日志文件
func deleteExpiredLogs() error {
    1. 遍历日志目录下的文件
    2. 筛选出符合命名规则的历史日志文件
    3. 删除超过保留期限的日志文件
    4. 返回执行过程中的错误
}

4. 编译与部署步骤

4.1 下载依赖

进入项目目录,执行以下命令下载第三方依赖:

1
2
3
4
5
6
# 下载配置解析依赖
go get gopkg.in/ini.v1
# 下载定时任务依赖
go get github.com/robfig/cron/v3
# 下载Windows服务化依赖
go get github.com/kardianos/service

4.2 编译可执行文件

1
go build -o git-sync-service.exe main.go
  • 编译成功后,目录下会生成git-sync-service.exe文件(大小约5-10MB)
  • 若编译报错,检查:① Go版本是否达标;② 依赖是否下载完成;③ 代码无语法错误

5. 常见问题排查

5.1 服务安装失败

  • 可能原因:未以管理员身份运行脚本、服务名冲突、可执行文件缺失
  • 解决步骤
    1. 重新以管理员身份运行安装脚本
    2. 若提示“服务已存在”,先执行卸载脚本再安装
    3. 检查git-sync-service.exe是否存在于脚本同级目录

5.2 服务启动后不同步

  • 可能原因:Cron 表达式错误、仓库路径错误、SSH 密钥配置错误、网络问题
  • 解决步骤
    1. 查看日志文件(LogPath 配置路径),搜索 “sync failed” 或错误信息
    2. 验证 Git 仓库路径:cd RepoPath && git status
    3. 验证 SSH 连接:ssh -T git@github.com(GitHub 示例)
    4. 验证 Cron 表达式:使用在线工具(如 https://crontab.guru/)检查格式

5.3 Git 拉取/推送失败

  • 可能原因:远程仓库地址变更、存在冲突、SSH 密钥错误、Git 未添加至 PATH
  • 解决步骤
    1. 检查远程地址:git remote -v
    2. 手动执行git pull 解决冲突
    3. 验证SSH密钥:ssh -i 私钥路径 git@远程仓库地址
    4. 验证Git命令:git --version

6. 服务管理与维护

6.1 手动管理命令

1
2
3
4
5
6
7
8
9
10
11
# 启动服务
net start GitAutoSyncService

# 停止服务
net stop GitAutoSyncService

# 查看服务状态
sc query GitAutoSyncService

# 强制删除服务(适用于卸载脚本失败时)
sc delete GitAutoSyncService

6.2 代码更新步骤

  1. 修改main.go代码(如调整同步逻辑、日志规则)
  2. 重新编译:go build -o git-sync-service.exe main.go
  3. 停止服务:net stop GitAutoSyncService
  4. 替换旧的git-sync-service.exe文件
  5. 启动服务:net start GitAutoSyncService

6.3 配置更新步骤

  1. 编辑config.ini文件(修改仓库路径、Cron 表达式等)
  2. 停止服务:net stop GitAutoSyncService
  3. 启动服务:net start GitAutoSyncService(配置仅在服务启动时加载)

总结

本服务通过 Go 语言实现 Windows 环境下的 Git 自动同步,核心优势在于:

  • 自动化:无需手动操作,定时完成拉取/提交/推送
  • 稳定性:服务化运行,后台常驻,崩溃可自动重启
  • 灵活性:支持自定义同步频率、日志路径、仓库路径
  • 无占用:日志采用临时写入模式,可随时操作日志文件

按照文档配置后,即可实现代码的全自动化同步,适用于多设备协作、服务器自动备份等场景。若遇到问题,优先查看日志文件中的错误信息,或参考「常见问题排查」章节。

TODO

  • bug:2025年11月29日9:40 在单位电脑上服务还在后台,但是停止 git 自动同步了,日志没有内容,不知道为啥。
本文由作者按照 CC BY 4.0 进行授权