go-git是golang上纯go实现的git客户端,可用来同步文件到git仓库。
为什么不用gitee官方openapi,因为我需要强制推送覆盖,官方api不支持。
下面是一个通过xml.gz文件到gitee的代码示例
package client
import (
"fmt"
"gin-epg/internal/app/common/util"
"io"
"os"
"path/filepath"
"strings"
"time"
"gin-epg/internal/app/store"
"github.com/go-git/go-git/v5"
"github.com/go-git/go-git/v5/config"
"github.com/go-git/go-git/v5/plumbing/object"
"github.com/go-git/go-git/v5/plumbing/transport/http"
)
// SyncXmlGzToGitee 同步多个 XML.gz 文件到 Gitee,每次同步时强制覆盖历史提交记录
func SyncXmlGzToGitee() error {
url := ""
userName := ""
password := ""
// 获取配置值
repoUrl, err := util.GetConfigValue("sync.repoUrl")
repoUserName, err := util.GetConfigValue("sync.repoUserName")
repoPassword, err := util.GetConfigValue("sync.repoPassword")
if repoUrl != nil {
url = repoUrl.(string)
}
if repoUserName != nil {
userName = repoUserName.(string)
}
if repoPassword != nil {
password = repoPassword.(string)
}
if url == "" || userName == "" || password == "" {
return fmt.Errorf("配置错误,请检查 sync.repoUrl、sync.repoUserName 和 sync.repoPassword 是否正确")
}
fileNames := []interface{}{
"e9.xml.gz",
"e7.xml.gz",
"e.xml.gz",
}
// 创建 gitee同步目录
tempDirPath := filepath.Join(os.TempDir(), "gitee-sync")
if err := os.MkdirAll(tempDirPath, os.ModePerm); err != nil {
return fmt.Errorf("创建临时目录失败: %v", err)
}
fmt.Printf("临时目录路径: %s\n", tempDirPath)
// 创建存储工具下载目录
tempDir := filepath.Join(os.TempDir(), "xmltvsync")
if err := os.MkdirAll(tempDir, os.ModePerm); err != nil {
return fmt.Errorf("创建临时目录失败: %v", err)
}
fmt.Printf("存储工具下载目录路径: %s\n", tempDir)
// 初始化 OSS 存储服务
ossService := store.GlobalStoreService
// 初始化或打开本地临时仓库
repoPath := filepath.Join(tempDirPath, "repo")
fmt.Printf("Git 仓库路径: %s\n", repoPath)
// 清理旧的仓库目录(如果存在)
if _, err := os.Stat(repoPath); !os.IsNotExist(err) {
if err := os.RemoveAll(repoPath); err != nil {
return fmt.Errorf("清理旧的仓库目录失败: %v", err)
}
fmt.Printf("旧的仓库目录已清理: %s\n", repoPath)
}
repo, err := git.PlainOpen(repoPath)
if err != nil {
if strings.Contains(err.Error(), "repository does not exist") {
// 如果仓库不存在,则初始化一个新的仓库
repo, err = git.PlainInit(repoPath, false)
if err != nil {
return fmt.Errorf("初始化 Git 仓库失败: %v", err)
}
fmt.Printf("Git 仓库初始化成功: %s\n", repoPath)
} else {
return fmt.Errorf("打开 Git 仓库失败: %v", err)
}
}
// 获取工作树
worktree, err := repo.Worktree()
if err != nil {
return fmt.Errorf("获取工作树失败: %v", err)
}
fmt.Printf("工作树获取成功\n")
// 遍历需要同步的文件列表
for _, fileInterface := range fileNames {
fileName, ok := fileInterface.(string)
if !ok {
fmt.Printf("配置 sync.filesToSync 中包含非字符串项,跳过该项: %v\n", fileInterface)
continue
}
localFilePath := filepath.Join(tempDir, fileName)
tempFilePath := filepath.Join(repoPath, fileName)
// 下载文件
if err := ossService.DownloadToFile(fileName, localFilePath); err != nil {
fmt.Printf("下载文件 %s 失败: %v\n", fileName, err)
continue
}
fmt.Printf("文件下载成功: %s\n", localFilePath)
// 复制文件到 Git 仓库目录
if err := copyFile(localFilePath, tempFilePath); err != nil {
fmt.Printf("复制文件 %s 失败: %v\n", fileName, err)
continue
}
fmt.Printf("文件复制成功: %s\n", tempFilePath)
// 添加文件到索引
if _, err := worktree.Add(fileName); err != nil {
fmt.Printf("添加文件 %s 到 Git 失败: %v\n", fileName, err)
continue
}
fmt.Printf("文件 %s 添加到索引成功\n", fileName)
}
// 获取当前时间并格式化
currentTime := time.Now().Format("2006-01-02 15:04:05")
// 提交更改
commitHash, err := worktree.Commit(fmt.Sprintf("同步 XML.gz 文件 - %s", currentTime), &git.CommitOptions{
Author: &object.Signature{
Name: "sync",
Email: "sync@example.com",
When: time.Now(),
},
})
if err != nil {
return fmt.Errorf("提交更改失败: %v", err)
}
fmt.Printf("提交成功,提交哈希: %s\n", commitHash)
// 添加远程仓库(如果尚未添加)
remoteName := "origin"
if _, err := repo.Remote(remoteName); err != nil {
if err == git.ErrRemoteNotFound {
// 添加远程仓库
_, err := repo.CreateRemote(&config.RemoteConfig{
Name: remoteName,
URLs: []string{url},
})
if err != nil {
return fmt.Errorf("添加远程仓库失败: %v", err)
}
fmt.Printf("远程仓库添加成功\n")
} else {
return fmt.Errorf("检查远程仓库失败: %v", err)
}
}
// 强制推送到远程仓库
if err := repo.Push(&git.PushOptions{
RemoteName: remoteName,
RefSpecs: []config.RefSpec{
config.RefSpec("refs/heads/master:refs/heads/master"),
},
Auth: &http.BasicAuth{
Username: userName,
Password: password,
},
Force: true,
}); err != nil {
return fmt.Errorf("强制推送失败: %v", err)
}
fmt.Printf("强制推送成功\n")
return nil
}
// copyFile 复制文件
func copyFile(src, dst string) error {
in, err := os.Open(src)
if err != nil {
return err
}
defer in.Close()
out, err := os.Create(dst)
if err != nil {
return err
}
defer out.Close()
_, err = io.Copy(out, in)
return err
}
这里需要强制推送,没有git历史可以减少仓库体积。不能用go-git v4版,v4版不支持强制推送。
若需要兼容win7 golang1.20 需要降低go-git版本至github.com/go-git/go-git/v5 v5.9.0
可在go.mod文件中修改版本后执行 go mod tidy