使用go-git同步文件到gitee

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

### 如何在Go项目环境中正确配置和安装Git 为了确保Go项目能够正常运行以及支持版本控制功能,需要正确配置和安装Git。以下是关于如何实现这一目标的具体说明。 #### 安装Git 首先,在本地开发环境上安装Git是非常重要的一步。可以通过以下命令来完成Git的安装: 对于Linux系统: ```bash sudo apt-get update sudo apt-get install git ``` 对于MacOS系统: ```bash brew install git ``` 确认Git已成功安装后,可以执行以下命令验证其版本号: ```bash git --version ``` 此操作有助于确保所使用Git版本是最新的或者满足特定需求[^4]。 #### 配置SSH密钥 为了让开发者能够在远程仓库(如Gitee)上安全地推送代码而无需每次都输入用户名密码,建议创建并配置SSH密钥对。具体过程如下所示: 1. 创建一个新的SSH密钥文件: ```bash ssh-keygen -t rsa -b 4096 -C "your_email@example.com" ``` 2. 将生成的公钥复制至剪贴板以便后续上传到服务器端: ```bash cat ~/.ssh/id_rsa.pub ``` 3. 登录到对应的代码托管平台(例如Gitee),找到个人设置中的SSH公钥选项,并粘贴刚才获取的内容进去。 #### 使用Git Submodules管理依赖项 当涉及到复杂的Go工程项目时,可能需要用到其他第三方库作为子模块(submodule),此时就需要利用`git submodule add`这样的指令来进行添加外部资源链接。比如这样的一条语句就可以将指定URL下的某个仓库克隆下来成为当前项目的组成部分之一: ```bash git submodule add https://gitee.com/example/repo.git path/to/submodule ``` 这一步骤完成后还需要初始化这些新增加的部分并且更新它们的状态使之同步最新改动情况[^1]。 #### 利用go vet提高代码质量 最后值得一提的是,在整个软件开发生命周期里边除了构建测试部署之外还有一个环节就是审查源码是否存在隐患之处。“vet”正是这样一个专门针对此类场景设计出来的实用程序;通过扫描所有包内的.go文件寻找可疑模式从而帮助发现那些虽然编译器允许但却很可能引起逻辑错误的地方[^3]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值