// Copyright 2015 The Gogs Authors. All rights reserved. // Copyright 2018 The Gitea Authors. All rights reserved. // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. package git import ( "fmt" "strings" "github.com/go-git/go-git/v5/plumbing" ) // BranchPrefix base dir of the branch information file store on git const BranchPrefix = "refs/heads/" // IsReferenceExist returns true if given reference exists in the repository. func IsReferenceExist(repoPath, name string) bool { _, err := NewCommand("show-ref", "--verify", "--", name).RunInDir(repoPath) return err == nil } // IsBranchExist returns true if given branch exists in the repository. func IsBranchExist(repoPath, name string) bool { return IsReferenceExist(repoPath, BranchPrefix+name) } // IsBranchExist returns true if given branch exists in current repository. func (repo *Repository) IsBranchExist(name string) bool { if name == "" { return false } reference, err := repo.gogitRepo.Reference(plumbing.ReferenceName(BranchPrefix+name), true) if err != nil { return false } return reference.Type() != plumbing.InvalidReference } // Branch represents a Git branch. type Branch struct { Name string Path string gitRepo *Repository } // GetHEADBranch returns corresponding branch of HEAD. func (repo *Repository) GetHEADBranch() (*Branch, error) { if repo == nil { return nil, fmt.Errorf("nil repo") } stdout, err := NewCommand("symbolic-ref", "HEAD").RunInDir(repo.Path) if err != nil { return nil, err } stdout = strings.TrimSpace(stdout) if !strings.HasPrefix(stdout, BranchPrefix) { return nil, fmt.Errorf("invalid HEAD branch: %v", stdout) } return &Branch{ Name: stdout[len(BranchPrefix):], Path: stdout, gitRepo: repo, }, nil } // SetDefaultBranch sets default branch of repository. func (repo *Repository) SetDefaultBranch(name string) error { _, err := NewCommand("symbolic-ref", "HEAD", BranchPrefix+name).RunInDir(repo.Path) return err } // GetDefaultBranch gets default branch of repository. func (repo *Repository) GetDefaultBranch() (string, error) { return NewCommand("symbolic-ref", "HEAD").RunInDir(repo.Path) } // GetBranches returns all branches of the repository. func (repo *Repository) GetBranches() ([]string, error) { var branchNames []string branches, err := repo.gogitRepo.Branches() if err != nil { return nil, err } _ = branches.ForEach(func(branch *plumbing.Reference) error { branchNames = append(branchNames, strings.TrimPrefix(branch.Name().String(), BranchPrefix)) return nil }) // TODO: Sort? return branchNames, nil } // GetBranch returns a branch by it's name func (repo *Repository) GetBranch(branch string) (*Branch, error) { if !repo.IsBranchExist(branch) { return nil, ErrBranchNotExist{branch} } return &Branch{ Path: repo.Path, Name: branch, gitRepo: repo, }, nil } // GetBranchesByPath returns a branch by it's path func GetBranchesByPath(path string) ([]*Branch, error) { gitRepo, err := OpenRepository(path) if err != nil { return nil, err } defer gitRepo.Close() brs, err := gitRepo.GetBranches() if err != nil { return nil, err } branches := make([]*Branch, len(brs)) for i := range brs { branches[i] = &Branch{ Path: path, Name: brs[i], gitRepo: gitRepo, } } return branches, nil } // DeleteBranchOptions Option(s) for delete branch type DeleteBranchOptions struct { Force bool } // DeleteBranch delete a branch by name on repository. func (repo *Repository) DeleteBranch(name string, opts DeleteBranchOptions) error { cmd := NewCommand("branch") if opts.Force { cmd.AddArguments("-D") } else { cmd.AddArguments("-d") } cmd.AddArguments("--", name) _, err := cmd.RunInDir(repo.Path) return err } // CreateBranch create a new branch func (repo *Repository) CreateBranch(branch, oldbranchOrCommit string) error { cmd := NewCommand("branch") cmd.AddArguments("--", branch, oldbranchOrCommit) _, err := cmd.RunInDir(repo.Path) return err } // AddRemote adds a new remote to repository. func (repo *Repository) AddRemote(name, url string, fetch bool) error { cmd := NewCommand("remote", "add") if fetch { cmd.AddArguments("-f") } cmd.AddArguments(name, url) _, err := cmd.RunInDir(repo.Path) return err } // RemoveRemote removes a remote from repository. func (repo *Repository) RemoveRemote(name string) error { _, err := NewCommand("remote", "rm", name).RunInDir(repo.Path) return err } // GetCommit returns the head commit of a branch func (branch *Branch) GetCommit() (*Commit, error) { return branch.gitRepo.GetBranchCommit(branch.Name) }