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())) }