code.gitea.io/gitea@v1.19.3/modules/git/repo_branch.go (about) 1 // Copyright 2015 The Gogs Authors. All rights reserved. 2 // Copyright 2018 The Gitea Authors. All rights reserved. 3 // SPDX-License-Identifier: MIT 4 5 package git 6 7 import ( 8 "context" 9 "errors" 10 "fmt" 11 "strings" 12 ) 13 14 // BranchPrefix base dir of the branch information file store on git 15 const BranchPrefix = "refs/heads/" 16 17 // AGit Flow 18 19 // PullRequestPrefix special ref to create a pull request: refs/for/<targe-branch>/<topic-branch> 20 // or refs/for/<targe-branch> -o topic='<topic-branch>' 21 const PullRequestPrefix = "refs/for/" 22 23 // TODO: /refs/for-review for suggest change interface 24 25 // IsReferenceExist returns true if given reference exists in the repository. 26 func IsReferenceExist(ctx context.Context, repoPath, name string) bool { 27 _, _, err := NewCommand(ctx, "show-ref", "--verify").AddDashesAndList(name).RunStdString(&RunOpts{Dir: repoPath}) 28 return err == nil 29 } 30 31 // IsBranchExist returns true if given branch exists in the repository. 32 func IsBranchExist(ctx context.Context, repoPath, name string) bool { 33 return IsReferenceExist(ctx, repoPath, BranchPrefix+name) 34 } 35 36 // Branch represents a Git branch. 37 type Branch struct { 38 Name string 39 Path string 40 41 gitRepo *Repository 42 } 43 44 // GetHEADBranch returns corresponding branch of HEAD. 45 func (repo *Repository) GetHEADBranch() (*Branch, error) { 46 if repo == nil { 47 return nil, fmt.Errorf("nil repo") 48 } 49 stdout, _, err := NewCommand(repo.Ctx, "symbolic-ref", "HEAD").RunStdString(&RunOpts{Dir: repo.Path}) 50 if err != nil { 51 return nil, err 52 } 53 stdout = strings.TrimSpace(stdout) 54 55 if !strings.HasPrefix(stdout, BranchPrefix) { 56 return nil, fmt.Errorf("invalid HEAD branch: %v", stdout) 57 } 58 59 return &Branch{ 60 Name: stdout[len(BranchPrefix):], 61 Path: stdout, 62 gitRepo: repo, 63 }, nil 64 } 65 66 // SetDefaultBranch sets default branch of repository. 67 func (repo *Repository) SetDefaultBranch(name string) error { 68 _, _, err := NewCommand(repo.Ctx, "symbolic-ref", "HEAD").AddDynamicArguments(BranchPrefix + name).RunStdString(&RunOpts{Dir: repo.Path}) 69 return err 70 } 71 72 // GetDefaultBranch gets default branch of repository. 73 func (repo *Repository) GetDefaultBranch() (string, error) { 74 stdout, _, err := NewCommand(repo.Ctx, "symbolic-ref", "HEAD").RunStdString(&RunOpts{Dir: repo.Path}) 75 if err != nil { 76 return "", err 77 } 78 stdout = strings.TrimSpace(stdout) 79 if !strings.HasPrefix(stdout, BranchPrefix) { 80 return "", errors.New("the HEAD is not a branch: " + stdout) 81 } 82 return strings.TrimPrefix(stdout, BranchPrefix), nil 83 } 84 85 // GetBranch returns a branch by it's name 86 func (repo *Repository) GetBranch(branch string) (*Branch, error) { 87 if !repo.IsBranchExist(branch) { 88 return nil, ErrBranchNotExist{branch} 89 } 90 return &Branch{ 91 Path: repo.Path, 92 Name: branch, 93 gitRepo: repo, 94 }, nil 95 } 96 97 // GetBranchesByPath returns a branch by it's path 98 // if limit = 0 it will not limit 99 func GetBranchesByPath(ctx context.Context, path string, skip, limit int) ([]*Branch, int, error) { 100 gitRepo, err := OpenRepository(ctx, path) 101 if err != nil { 102 return nil, 0, err 103 } 104 defer gitRepo.Close() 105 106 return gitRepo.GetBranches(skip, limit) 107 } 108 109 // GetBranches returns a slice of *git.Branch 110 func (repo *Repository) GetBranches(skip, limit int) ([]*Branch, int, error) { 111 brs, countAll, err := repo.GetBranchNames(skip, limit) 112 if err != nil { 113 return nil, 0, err 114 } 115 116 branches := make([]*Branch, len(brs)) 117 for i := range brs { 118 branches[i] = &Branch{ 119 Path: repo.Path, 120 Name: brs[i], 121 gitRepo: repo, 122 } 123 } 124 125 return branches, countAll, nil 126 } 127 128 // DeleteBranchOptions Option(s) for delete branch 129 type DeleteBranchOptions struct { 130 Force bool 131 } 132 133 // DeleteBranch delete a branch by name on repository. 134 func (repo *Repository) DeleteBranch(name string, opts DeleteBranchOptions) error { 135 cmd := NewCommand(repo.Ctx, "branch") 136 137 if opts.Force { 138 cmd.AddArguments("-D") 139 } else { 140 cmd.AddArguments("-d") 141 } 142 143 cmd.AddDashesAndList(name) 144 _, _, err := cmd.RunStdString(&RunOpts{Dir: repo.Path}) 145 146 return err 147 } 148 149 // CreateBranch create a new branch 150 func (repo *Repository) CreateBranch(branch, oldbranchOrCommit string) error { 151 cmd := NewCommand(repo.Ctx, "branch") 152 cmd.AddDashesAndList(branch, oldbranchOrCommit) 153 154 _, _, err := cmd.RunStdString(&RunOpts{Dir: repo.Path}) 155 156 return err 157 } 158 159 // AddRemote adds a new remote to repository. 160 func (repo *Repository) AddRemote(name, url string, fetch bool) error { 161 cmd := NewCommand(repo.Ctx, "remote", "add") 162 if fetch { 163 cmd.AddArguments("-f") 164 } 165 cmd.AddDynamicArguments(name, url) 166 167 _, _, err := cmd.RunStdString(&RunOpts{Dir: repo.Path}) 168 return err 169 } 170 171 // RemoveRemote removes a remote from repository. 172 func (repo *Repository) RemoveRemote(name string) error { 173 _, _, err := NewCommand(repo.Ctx, "remote", "rm").AddDynamicArguments(name).RunStdString(&RunOpts{Dir: repo.Path}) 174 return err 175 } 176 177 // GetCommit returns the head commit of a branch 178 func (branch *Branch) GetCommit() (*Commit, error) { 179 return branch.gitRepo.GetBranchCommit(branch.Name) 180 } 181 182 // RenameBranch rename a branch 183 func (repo *Repository) RenameBranch(from, to string) error { 184 _, _, err := NewCommand(repo.Ctx, "branch", "-m").AddDynamicArguments(from, to).RunStdString(&RunOpts{Dir: repo.Path}) 185 return err 186 }