github.com/gitbundle/modules@v0.0.0-20231025071548-85b91c5c3b01/git/repo_branch.go (about) 1 // Copyright 2023 The GitBundle Inc. All rights reserved. 2 // Copyright 2017 The Gitea Authors. All rights reserved. 3 // Use of this source code is governed by a MIT-style 4 // license that can be found in the LICENSE file. 5 6 package git 7 8 import ( 9 "context" 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", "--", 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", 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 return stdout, err 76 } 77 78 // GetBranch returns a branch by it's name 79 func (repo *Repository) GetBranch(branch string) (*Branch, error) { 80 if !repo.IsBranchExist(branch) { 81 return nil, ErrBranchNotExist{branch} 82 } 83 return &Branch{ 84 Path: repo.Path, 85 Name: branch, 86 gitRepo: repo, 87 }, nil 88 } 89 90 // GetBranchesByPath returns a branch by it's path 91 // if limit = 0 it will not limit 92 func GetBranchesByPath(ctx context.Context, path string, skip, limit int) ([]*Branch, int, error) { 93 gitRepo, err := OpenRepository(ctx, path) 94 if err != nil { 95 return nil, 0, err 96 } 97 defer gitRepo.Close() 98 99 return gitRepo.GetBranches(skip, limit) 100 } 101 102 // GetBranches returns a slice of *git.Branch 103 func (repo *Repository) GetBranches(skip, limit int) ([]*Branch, int, error) { 104 brs, countAll, err := repo.GetBranchNames(skip, limit) 105 if err != nil { 106 return nil, 0, err 107 } 108 109 branches := make([]*Branch, len(brs)) 110 for i := range brs { 111 branches[i] = &Branch{ 112 Path: repo.Path, 113 Name: brs[i], 114 gitRepo: repo, 115 } 116 } 117 118 return branches, countAll, nil 119 } 120 121 // DeleteBranchOptions Option(s) for delete branch 122 type DeleteBranchOptions struct { 123 Force bool 124 } 125 126 // DeleteBranch delete a branch by name on repository. 127 func (repo *Repository) DeleteBranch(name string, opts DeleteBranchOptions) error { 128 cmd := NewCommand(repo.Ctx, "branch") 129 130 if opts.Force { 131 cmd.AddArguments("-D") 132 } else { 133 cmd.AddArguments("-d") 134 } 135 136 cmd.AddArguments("--", name) 137 _, _, err := cmd.RunStdString(&RunOpts{Dir: repo.Path}) 138 139 return err 140 } 141 142 // CreateBranch create a new branch 143 func (repo *Repository) CreateBranch(branch, oldbranchOrCommit string) error { 144 cmd := NewCommand(repo.Ctx, "branch") 145 cmd.AddArguments("--", branch, oldbranchOrCommit) 146 147 _, _, err := cmd.RunStdString(&RunOpts{Dir: repo.Path}) 148 149 return err 150 } 151 152 // AddRemote adds a new remote to repository. 153 func (repo *Repository) AddRemote(name, url string, fetch bool) error { 154 cmd := NewCommand(repo.Ctx, "remote", "add") 155 if fetch { 156 cmd.AddArguments("-f") 157 } 158 cmd.AddArguments(name, url) 159 160 _, _, err := cmd.RunStdString(&RunOpts{Dir: repo.Path}) 161 return err 162 } 163 164 // RemoveRemote removes a remote from repository. 165 func (repo *Repository) RemoveRemote(name string) error { 166 _, _, err := NewCommand(repo.Ctx, "remote", "rm", name).RunStdString(&RunOpts{Dir: repo.Path}) 167 return err 168 } 169 170 // GetCommit returns the head commit of a branch 171 func (branch *Branch) GetCommit() (*Commit, error) { 172 return branch.gitRepo.GetBranchCommit(branch.Name) 173 } 174 175 // RenameBranch rename a branch 176 func (repo *Repository) RenameBranch(from, to string) error { 177 _, _, err := NewCommand(repo.Ctx, "branch", "-m", from, to).RunStdString(&RunOpts{Dir: repo.Path}) 178 return err 179 }