2016-01-28 22:49:05 +03:00
// Copyright 2016 The Gogs Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
2016-01-15 21:24:03 +03:00
package models
import (
2017-10-15 22:59:24 +03:00
"fmt"
"time"
2016-11-10 19:24:48 +03:00
"code.gitea.io/git"
2017-10-15 22:59:24 +03:00
"code.gitea.io/gitea/modules/setting"
"github.com/Unknwon/com"
2016-01-15 21:24:03 +03:00
)
2016-11-26 03:40:35 +03:00
// Branch holds the branch information
2016-01-15 21:24:03 +03:00
type Branch struct {
2016-02-03 01:07:40 +03:00
Path string
Name string
2016-01-15 21:24:03 +03:00
}
2016-11-26 03:40:35 +03:00
// GetBranchesByPath returns a branch by it's path
2016-01-15 21:24:03 +03:00
func GetBranchesByPath ( path string ) ( [ ] * Branch , error ) {
gitRepo , err := git . OpenRepository ( path )
if err != nil {
return nil , err
}
brs , err := gitRepo . GetBranches ( )
if err != nil {
return nil , err
}
2016-02-03 01:07:40 +03:00
branches := make ( [ ] * Branch , len ( brs ) )
2016-01-15 21:24:03 +03:00
for i := range brs {
2016-02-03 01:07:40 +03:00
branches [ i ] = & Branch {
2016-01-15 21:24:03 +03:00
Path : path ,
Name : brs [ i ] ,
}
}
2016-02-03 01:07:40 +03:00
return branches , nil
}
2017-10-15 22:59:24 +03:00
// CanCreateBranch returns true if repository meets the requirements for creating new branches.
func ( repo * Repository ) CanCreateBranch ( ) bool {
return ! repo . IsMirror
}
2016-11-26 03:40:35 +03:00
// GetBranch returns a branch by it's name
func ( repo * Repository ) GetBranch ( branch string ) ( * Branch , error ) {
if ! git . IsBranchExist ( repo . RepoPath ( ) , branch ) {
2017-06-11 05:57:28 +03:00
return nil , ErrBranchNotExist { branch }
2016-02-03 01:07:40 +03:00
}
return & Branch {
Path : repo . RepoPath ( ) ,
2016-11-26 03:40:35 +03:00
Name : branch ,
2016-02-03 01:07:40 +03:00
} , nil
}
2016-11-26 03:40:35 +03:00
// GetBranches returns all the branches of a repository
2016-02-03 01:07:40 +03:00
func ( repo * Repository ) GetBranches ( ) ( [ ] * Branch , error ) {
return GetBranchesByPath ( repo . RepoPath ( ) )
2016-01-15 21:24:03 +03:00
}
2017-10-15 22:59:24 +03:00
// CheckBranchName validates branch name with existing repository branches
func ( repo * Repository ) CheckBranchName ( name string ) error {
gitRepo , err := git . OpenRepository ( repo . RepoPath ( ) )
if err != nil {
return err
}
if _ , err := gitRepo . GetTag ( name ) ; err == nil {
return ErrTagAlreadyExists { name }
}
branches , err := repo . GetBranches ( )
if err != nil {
return err
}
for _ , branch := range branches {
if branch . Name == name {
return ErrBranchAlreadyExists { branch . Name }
} else if ( len ( branch . Name ) < len ( name ) && branch . Name + "/" == name [ 0 : len ( branch . Name ) + 1 ] ) ||
( len ( branch . Name ) > len ( name ) && name + "/" == branch . Name [ 0 : len ( name ) + 1 ] ) {
return ErrBranchNameConflict { branch . Name }
}
}
return nil
}
// CreateNewBranch creates a new repository branch
func ( repo * Repository ) CreateNewBranch ( doer * User , oldBranchName , branchName string ) ( err error ) {
repoWorkingPool . CheckIn ( com . ToStr ( repo . ID ) )
defer repoWorkingPool . CheckOut ( com . ToStr ( repo . ID ) )
// Check if branch name can be used
if err := repo . CheckBranchName ( branchName ) ; err != nil {
return err
}
localPath := repo . LocalCopyPath ( )
if err = discardLocalRepoBranchChanges ( localPath , oldBranchName ) ; err != nil {
return fmt . Errorf ( "discardLocalRepoChanges: %v" , err )
} else if err = repo . UpdateLocalCopyBranch ( oldBranchName ) ; err != nil {
return fmt . Errorf ( "UpdateLocalCopyBranch: %v" , err )
}
if err = repo . CheckoutNewBranch ( oldBranchName , branchName ) ; err != nil {
return fmt . Errorf ( "CreateNewBranch: %v" , err )
}
if err = git . Push ( localPath , git . PushOptions {
Remote : "origin" ,
Branch : branchName ,
} ) ; err != nil {
return fmt . Errorf ( "Push: %v" , err )
}
return nil
}
// updateLocalCopyToCommit pulls latest changes of given commit from repoPath to localPath.
// It creates a new clone if local copy does not exist.
// This function checks out target commit by default, it is safe to assume subsequent
// operations are operating against target commit when caller has confidence for no race condition.
func updateLocalCopyToCommit ( repoPath , localPath , commit string ) error {
if ! com . IsExist ( localPath ) {
if err := git . Clone ( repoPath , localPath , git . CloneRepoOptions {
Timeout : time . Duration ( setting . Git . Timeout . Clone ) * time . Second ,
} ) ; err != nil {
return fmt . Errorf ( "git clone: %v" , err )
}
} else {
_ , err := git . NewCommand ( "fetch" , "origin" ) . RunInDir ( localPath )
if err != nil {
return fmt . Errorf ( "git fetch origin: %v" , err )
}
if err := git . ResetHEAD ( localPath , true , "HEAD" ) ; err != nil {
return fmt . Errorf ( "git reset --hard HEAD: %v" , err )
}
}
if err := git . Checkout ( localPath , git . CheckoutOptions {
Branch : commit ,
} ) ; err != nil {
return fmt . Errorf ( "git checkout %s: %v" , commit , err )
}
return nil
}
// updateLocalCopyToCommit makes sure local copy of repository is at given commit.
func ( repo * Repository ) updateLocalCopyToCommit ( commit string ) error {
return updateLocalCopyToCommit ( repo . RepoPath ( ) , repo . LocalCopyPath ( ) , commit )
}
// CreateNewBranchFromCommit creates a new repository branch
func ( repo * Repository ) CreateNewBranchFromCommit ( doer * User , commit , branchName string ) ( err error ) {
repoWorkingPool . CheckIn ( com . ToStr ( repo . ID ) )
defer repoWorkingPool . CheckOut ( com . ToStr ( repo . ID ) )
// Check if branch name can be used
if err := repo . CheckBranchName ( branchName ) ; err != nil {
return err
}
localPath := repo . LocalCopyPath ( )
if err = repo . updateLocalCopyToCommit ( commit ) ; err != nil {
return fmt . Errorf ( "UpdateLocalCopyBranch: %v" , err )
}
if err = repo . CheckoutNewBranch ( commit , branchName ) ; err != nil {
return fmt . Errorf ( "CheckoutNewBranch: %v" , err )
}
if err = git . Push ( localPath , git . PushOptions {
Remote : "origin" ,
Branch : branchName ,
} ) ; err != nil {
return fmt . Errorf ( "Push: %v" , err )
}
return nil
}
2016-11-26 03:40:35 +03:00
// GetCommit returns all the commits of a branch
func ( branch * Branch ) GetCommit ( ) ( * git . Commit , error ) {
gitRepo , err := git . OpenRepository ( branch . Path )
2016-01-15 21:24:03 +03:00
if err != nil {
return nil , err
}
2016-11-26 03:40:35 +03:00
return gitRepo . GetBranchCommit ( branch . Name )
2016-01-15 21:24:03 +03:00
}