概要
项目集成使用 github.com/go-git/go-git
go-git 介绍
一个用 Go 语言编写的 git 实现库:它的官方仓库地址:go-git
为什么我们需要它?
举个例子:
如果我们需要获取 git log 的信息,需要通过 Go 调用 cmd 命令来获取:获取 git 的 last commit hash 的话需要耗费的时间大概在 50ms 左右(不同运行环境可能不一样)
它的慢原因是什么呢 ?
Go 调用 cmd 命令本身存在着性能原因
其次,如果想要获取其它仓库的 git log 信息,还存在着跨文件调用的消耗
所以这里 go-git 的作用就体现出来了!由于它是 go 编写的 git 实现库,所以对 cmd 命令调用的消耗就可以避免了。测试之后,使用时间在 500μs 左右,巨大的提升!
技术细节,上代码
package gogit
import (
"context"
"fmt"
"os"
"time"
"github.com/go-git/go-billy/v5/osfs" // 使用 osfs 作为文件系统抽象
"github.com/go-git/go-git/v5"
"github.com/go-git/go-git/v5/config"
"github.com/go-git/go-git/v5/plumbing"
"github.com/go-git/go-git/v5/plumbing/object"
"github.com/go-git/go-git/v5/plumbing/transport/http"
"github.com/go-git/go-git/v5/storage/memory"
)
// git fetch | localPath:本地代码实际路径
func Fetch(gitLabUserName, gitLabPassW, localPath, gitUrl string) error {
// 创建一个基于操作系统文件系统的 worktree
worktree := osfs.New(localPath)
if localPath == "" {
worktree = nil
}
r, err := git.Init(memory.NewStorage(), worktree)
if err != nil {
return err
}
_, err = r.CreateRemote(&config.RemoteConfig{Name: "origin", Mirror: false, URLs: []string{gitUrl}})
if err != nil {
return err
}
opts := &git.FetchOptions{
RemoteName: "origin",
// 指定分支
// ReferenceName: plumbing.NewBranchReferenceName(),
RemoteURL: gitUrl,
InsecureSkipTLS: true,
}
opts.Auth = &http.BasicAuth{Username: gitLabUserName, Password: gitLabPassW}
return r.FetchContext(context.Background(), opts)
}
// git clone | localPath := "/path/to/your/local/repo" // 替换为您想要克隆到的实际路径
func Clone(gitLabUserName, gitLabPassW, localPath, gitUrl string) (*git.Repository, error) {
// 创建一个基于操作系统文件系统的 worktree
worktree := osfs.New(localPath)
if localPath == "" {
worktree = nil
}
// r.CreateRemote(c)
opts := &git.CloneOptions{
// 指定分支
// ReferenceName: plumbing.NewBranchReferenceName(),
URL: gitUrl,
InsecureSkipTLS: true,
}
opts.Auth = &http.BasicAuth{Username: gitLabUserName, Password: gitLabPassW}
// 打开仓库
return git.Clone(memory.NewStorage(), worktree, opts)
}
// git branch
func GetBranch(gitLabUserName, gitLabPassW, gitUrl string) ([]string, error) {
brans := make([]string, 0)
repo, err := Clone(gitLabUserName, gitLabPassW, "", gitUrl)
if err != nil {
return brans, err
}
// 获取所有分支
branches, err := repo.References()
if err != nil {
return brans, err
}
// 遍历分支并打印
err = branches.ForEach(func(ref *plumbing.Reference) error {
if ref.Name().IsRemote() {
fmt.Println(ref.Name().Short())
brans = append(brans, ref.Name().Short())
}
return nil
})
return brans, err
}
// git push repoPath := "/path/to/your/repo" 替换为本地仓库路径 gitUrl: git 仓库地址
func Push(gitLabUserName, gitLabPassW, repoPath, gitUrl string) error {
// 克隆仓库到本地
_, err := git.PlainClone(repoPath, false, &git.CloneOptions{
Auth: &http.BasicAuth{
Username: gitLabUserName,
Password: gitLabPassW,
},
URL: gitUrl,
Progress: os.Stdout,
InsecureSkipTLS: true,
})
if err != nil {
fmt.Println("Failed to clone repository:", err)
return err
}
// 打开已存在的Git仓库
r, err := git.PlainOpen(repoPath)
if err != nil {
fmt.Println(err)
return err
}
// 获取工作树
w, err := r.Worktree()
if err != nil {
fmt.Println(err)
return err
}
// 添加所有更改到暂存区
_, err = w.Add(".")
if err != nil {
fmt.Println(err)
return err
}
// 提交更改
commitMsg := "init commit"
commit, err := w.Commit(commitMsg, &git.CommitOptions{
Author: &object.Signature{
Name: gitLabUserName,
Email: gitLabPassW,
When: time.Now(),
},
})
if err != nil {
fmt.Println(err)
return err
}
fmt.Println("Commit:", commit)
// 获取远程仓库配置
remotes, err := r.Remotes()
if err != nil {
fmt.Println(err)
return err
}
// 假设我们使用的是第一个远程仓库(通常是"origin")
remote := remotes[0]
// 创建一个推送选项对象
pushOptions := &git.PushOptions{
RemoteName: remote.Config().Name,
RefSpecs: []config.RefSpec{config.RefSpec("+refs/heads/master:refs/heads/master")},
Auth: &http.BasicAuth{
Username: gitLabUserName,
Password: gitLabPassW,
},
InsecureSkipTLS: true,
}
// 执行推送操作
if err = r.Push(pushOptions); err != nil {
fmt.Println(err)
return err
}
fmt.Println("Changes have been pushed to the remote repository.")
return nil
}
小结
直接将此文件复制到项目中即可使用,封装api接口不太完善,可按需引入其他接口。