Files
codit/backend/internal/git/branches.go

227 lines
5.6 KiB
Go

package git
import "errors"
import "strings"
import git "github.com/go-git/go-git/v5"
import "github.com/go-git/go-git/v5/plumbing"
import "github.com/go-git/go-git/v5/plumbing/object"
import "github.com/go-git/go-git/v5/plumbing/storer"
func GetDefaultBranch(repoPath string) (string, error) {
var repo *git.Repository
var err error
var head *plumbing.Reference
var target plumbing.ReferenceName
var targetRef *plumbing.Reference
repo, err = git.PlainOpen(repoPath)
if err != nil {
return "", err
}
_ = EnsureHead(repoPath)
head, err = repo.Reference(plumbing.HEAD, false)
if err != nil {
return "", err
}
if head.Type() == plumbing.SymbolicReference {
target = head.Target()
targetRef, err = repo.Reference(target, true)
if err != nil {
if errors.Is(err, plumbing.ErrReferenceNotFound) {
return "", nil
}
return "", err
}
if targetRef.Hash().IsZero() {
return "", nil
}
return target.Short(), nil
}
return head.Name().Short(), nil
}
type BranchInfo struct {
Name string `json:"name"`
LastHash string `json:"last_hash"`
LastAuthor string `json:"last_author"`
LastWhen string `json:"last_when"`
LastMessage string `json:"last_message"`
}
func ListBranchInfos(repoPath string) ([]BranchInfo, error) {
var repo *git.Repository
var err error
var iter storer.ReferenceIter
var branches []BranchInfo
var ref *plumbing.Reference
var commit *object.Commit
repo, err = git.PlainOpen(repoPath)
if err != nil {
return nil, err
}
iter, err = repo.Branches()
if err != nil {
return nil, err
}
defer iter.Close()
err = iter.ForEach(func(r *plumbing.Reference) error {
ref = r
if ref.Hash().IsZero() {
return nil
}
commit, err = repo.CommitObject(ref.Hash())
if err != nil {
branches = append(branches, BranchInfo{Name: ref.Name().Short()})
return nil
}
branches = append(branches, BranchInfo{
Name: ref.Name().Short(),
LastHash: commit.Hash.String(),
LastAuthor: commit.Author.Name,
LastWhen: commit.Author.When.UTC().Format(timeFormat),
LastMessage: strings.TrimSpace(commit.Message),
})
return nil
})
if err != nil {
return nil, err
}
return branches, nil
}
func SetDefaultBranch(repoPath string, branch string) error {
var repo *git.Repository
var err error
var refName plumbing.ReferenceName
repo, err = git.PlainOpen(repoPath)
if err != nil {
return err
}
refName = plumbing.NewBranchReferenceName(branch)
_, err = repo.Reference(refName, true)
if err != nil {
return err
}
err = repo.Storer.SetReference(plumbing.NewSymbolicReference(plumbing.HEAD, refName))
return err
}
func CreateBranch(repoPath string, name string, from string) error {
var repo *git.Repository
var err error
var commit *object.Commit
var refName plumbing.ReferenceName
repo, err = git.PlainOpen(repoPath)
if err != nil {
return err
}
refName = plumbing.NewBranchReferenceName(name)
_, err = repo.Reference(refName, true)
if err == nil {
return err
}
commit, err = resolveCommit(repo, from)
if err != nil {
return err
}
err = repo.Storer.SetReference(plumbing.NewHashReference(refName, commit.Hash))
return err
}
func DeleteBranch(repoPath string, branch string) error {
var repo *git.Repository
var err error
var refName plumbing.ReferenceName
repo, err = git.PlainOpen(repoPath)
if err != nil {
return err
}
refName = plumbing.NewBranchReferenceName(branch)
err = repo.Storer.RemoveReference(refName)
return err
}
func RenameBranch(repoPath string, from string, to string) error {
var repo *git.Repository
var err error
var fromRef *plumbing.Reference
var toRefName plumbing.ReferenceName
var fromRefName plumbing.ReferenceName
var head *plumbing.Reference
if from == to {
return nil
}
repo, err = git.PlainOpen(repoPath)
if err != nil {
return err
}
fromRefName = plumbing.NewBranchReferenceName(from)
toRefName = plumbing.NewBranchReferenceName(to)
fromRef, err = repo.Reference(fromRefName, true)
if err != nil {
return err
}
err = repo.Storer.SetReference(plumbing.NewHashReference(toRefName, fromRef.Hash()))
if err != nil {
return err
}
head, err = repo.Reference(plumbing.HEAD, false)
if err == nil && head.Type() == plumbing.SymbolicReference && head.Target() == fromRefName {
_ = repo.Storer.SetReference(plumbing.NewSymbolicReference(plumbing.HEAD, toRefName))
}
err = repo.Storer.RemoveReference(fromRefName)
return err
}
func EnsureHead(repoPath string) error {
var repo *git.Repository
var err error
var head *plumbing.Reference
var target plumbing.ReferenceName
var refName plumbing.ReferenceName
var iter storer.ReferenceIter
var first *plumbing.Reference
repo, err = git.PlainOpen(repoPath)
if err != nil {
return err
}
head, err = repo.Reference(plumbing.HEAD, false)
if err == nil {
if head.Type() == plumbing.SymbolicReference {
target = head.Target()
_, err = repo.Reference(target, true)
if err == nil {
return nil
}
} else {
return nil
}
}
refName = plumbing.NewBranchReferenceName("main")
_, err = repo.Reference(refName, true)
if err == nil {
return repo.Storer.SetReference(plumbing.NewSymbolicReference(plumbing.HEAD, refName))
}
refName = plumbing.NewBranchReferenceName("master")
_, err = repo.Reference(refName, true)
if err == nil {
return repo.Storer.SetReference(plumbing.NewSymbolicReference(plumbing.HEAD, refName))
}
iter, err = repo.Branches()
if err != nil {
return err
}
defer iter.Close()
err = iter.ForEach(func(r *plumbing.Reference) error {
first = r
return storer.ErrStop
})
if err != nil && err != storer.ErrStop {
return err
}
if first == nil {
return nil
}
return repo.Storer.SetReference(plumbing.NewSymbolicReference(plumbing.HEAD, first.Name()))
}