2016-11-04 01:16:01 +03:00
// Copyright 2015 The Gogs Authors. All rights reserved.
2018-11-28 00:52:20 +03:00
// Copyright 2018 The Gitea Authors. All rights reserved.
2016-11-04 01:16:01 +03:00
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package git
import (
2021-11-30 23:06:32 +03:00
"context"
2016-11-04 01:16:01 +03:00
"fmt"
"strings"
)
2016-12-22 12:30:52 +03:00
// BranchPrefix base dir of the branch information file store on git
const BranchPrefix = "refs/heads/"
2016-11-04 01:16:01 +03:00
2021-07-28 12:42:56 +03:00
// AGit Flow
// PullRequestPrefix sepcial ref to create a pull request: refs/for/<targe-branch>/<topic-branch>
// or refs/for/<targe-branch> -o topic='<topic-branch>'
const PullRequestPrefix = "refs/for/"
// TODO: /refs/for-review for suggest change interface
2016-11-04 01:16:01 +03:00
// IsReferenceExist returns true if given reference exists in the repository.
2021-11-30 23:06:32 +03:00
func IsReferenceExist ( ctx context . Context , repoPath , name string ) bool {
_ , err := NewCommandContext ( ctx , "show-ref" , "--verify" , "--" , name ) . RunInDir ( repoPath )
2016-11-04 01:16:01 +03:00
return err == nil
}
// IsBranchExist returns true if given branch exists in the repository.
2021-11-30 23:06:32 +03:00
func IsBranchExist ( ctx context . Context , repoPath , name string ) bool {
return IsReferenceExist ( ctx , repoPath , BranchPrefix + name )
2016-11-04 01:16:01 +03:00
}
// Branch represents a Git branch.
type Branch struct {
Name string
Path string
2019-04-19 15:17:27 +03:00
gitRepo * Repository
2016-11-04 01:16:01 +03:00
}
// GetHEADBranch returns corresponding branch of HEAD.
func ( repo * Repository ) GetHEADBranch ( ) ( * Branch , error ) {
2020-02-26 09:32:22 +03:00
if repo == nil {
return nil , fmt . Errorf ( "nil repo" )
}
2021-11-30 23:06:32 +03:00
stdout , err := NewCommandContext ( repo . Ctx , "symbolic-ref" , "HEAD" ) . RunInDir ( repo . Path )
2016-11-04 01:16:01 +03:00
if err != nil {
return nil , err
}
stdout = strings . TrimSpace ( stdout )
2016-12-22 12:30:52 +03:00
if ! strings . HasPrefix ( stdout , BranchPrefix ) {
2016-11-04 01:16:01 +03:00
return nil , fmt . Errorf ( "invalid HEAD branch: %v" , stdout )
}
return & Branch {
2019-04-19 15:17:27 +03:00
Name : stdout [ len ( BranchPrefix ) : ] ,
Path : stdout ,
gitRepo : repo ,
2016-11-04 01:16:01 +03:00
} , nil
}
// SetDefaultBranch sets default branch of repository.
func ( repo * Repository ) SetDefaultBranch ( name string ) error {
2021-11-30 23:06:32 +03:00
_ , err := NewCommandContext ( repo . Ctx , "symbolic-ref" , "HEAD" , BranchPrefix + name ) . RunInDir ( repo . Path )
2016-11-04 01:16:01 +03:00
return err
}
2020-09-25 07:09:23 +03:00
// GetDefaultBranch gets default branch of repository.
func ( repo * Repository ) GetDefaultBranch ( ) ( string , error ) {
2021-11-30 23:06:32 +03:00
return NewCommandContext ( repo . Ctx , "symbolic-ref" , "HEAD" ) . RunInDir ( repo . Path )
2020-09-25 07:09:23 +03:00
}
2019-04-19 15:17:27 +03:00
// 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
2021-02-03 22:06:13 +03:00
// if limit = 0 it will not limit
func GetBranchesByPath ( path string , skip , limit int ) ( [ ] * Branch , int , error ) {
2019-04-19 15:17:27 +03:00
gitRepo , err := OpenRepository ( path )
2018-11-28 00:52:20 +03:00
if err != nil {
2021-02-03 22:06:13 +03:00
return nil , 0 , err
2016-11-04 01:16:01 +03:00
}
2019-11-13 10:01:19 +03:00
defer gitRepo . Close ( )
2019-04-19 15:17:27 +03:00
2021-02-03 22:06:13 +03:00
brs , countAll , err := gitRepo . GetBranches ( skip , limit )
2019-04-19 15:17:27 +03:00
if err != nil {
2021-02-03 22:06:13 +03:00
return nil , 0 , err
2018-11-28 00:52:20 +03:00
}
2019-04-19 15:17:27 +03:00
branches := make ( [ ] * Branch , len ( brs ) )
for i := range brs {
branches [ i ] = & Branch {
Path : path ,
Name : brs [ i ] ,
gitRepo : gitRepo ,
}
}
2021-02-03 22:06:13 +03:00
return branches , countAll , nil
2016-11-04 01:16:01 +03:00
}
2016-12-22 12:30:52 +03:00
// DeleteBranchOptions Option(s) for delete branch
2016-11-04 01:16:01 +03:00
type DeleteBranchOptions struct {
Force bool
}
// DeleteBranch delete a branch by name on repository.
func ( repo * Repository ) DeleteBranch ( name string , opts DeleteBranchOptions ) error {
2021-11-30 23:06:32 +03:00
cmd := NewCommandContext ( repo . Ctx , "branch" )
2016-11-04 01:16:01 +03:00
if opts . Force {
2017-08-03 16:48:36 +03:00
cmd . AddArguments ( "-D" )
} else {
cmd . AddArguments ( "-d" )
2016-11-04 01:16:01 +03:00
}
2019-08-05 23:39:39 +03:00
cmd . AddArguments ( "--" , name )
2016-11-04 01:16:01 +03:00
_ , err := cmd . RunInDir ( repo . Path )
return err
}
2017-02-05 17:43:28 +03:00
// CreateBranch create a new branch
2019-05-11 18:29:17 +03:00
func ( repo * Repository ) CreateBranch ( branch , oldbranchOrCommit string ) error {
2021-11-30 23:06:32 +03:00
cmd := NewCommandContext ( repo . Ctx , "branch" )
2019-05-11 18:29:17 +03:00
cmd . AddArguments ( "--" , branch , oldbranchOrCommit )
2017-02-05 17:43:28 +03:00
_ , err := cmd . RunInDir ( repo . Path )
return err
}
2016-11-04 01:16:01 +03:00
// AddRemote adds a new remote to repository.
func ( repo * Repository ) AddRemote ( name , url string , fetch bool ) error {
2021-11-30 23:06:32 +03:00
cmd := NewCommandContext ( repo . Ctx , "remote" , "add" )
2016-11-04 01:16:01 +03:00
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 {
2021-11-30 23:06:32 +03:00
_ , err := NewCommandContext ( repo . Ctx , "remote" , "rm" , name ) . RunInDir ( repo . Path )
2016-11-04 01:16:01 +03:00
return err
}
2019-04-19 15:17:27 +03:00
// GetCommit returns the head commit of a branch
func ( branch * Branch ) GetCommit ( ) ( * Commit , error ) {
return branch . gitRepo . GetBranchCommit ( branch . Name )
}
2021-10-08 20:03:04 +03:00
// RenameBranch rename a branch
func ( repo * Repository ) RenameBranch ( from , to string ) error {
2021-11-30 23:06:32 +03:00
_ , err := NewCommandContext ( repo . Ctx , "branch" , "-m" , from , to ) . RunInDir ( repo . Path )
2021-10-08 20:03:04 +03:00
return err
}