@ -20,6 +20,7 @@ import (
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/queue"
"code.gitea.io/gitea/modules/queue"
repo_module "code.gitea.io/gitea/modules/repository"
repo_module "code.gitea.io/gitea/modules/repository"
"code.gitea.io/gitea/modules/timeutil"
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/modules/util"
notify_service "code.gitea.io/gitea/services/notify"
notify_service "code.gitea.io/gitea/services/notify"
files_service "code.gitea.io/gitea/services/repository/files"
files_service "code.gitea.io/gitea/services/repository/files"
@ -28,30 +29,13 @@ import (
)
)
// CreateNewBranch creates a new repository branch
// CreateNewBranch creates a new repository branch
func CreateNewBranch ( ctx context . Context , doer * user_model . User , repo * repo_model . Repository , oldBranchName , branchName string ) ( err error ) {
func CreateNewBranch ( ctx context . Context , doer * user_model . User , repo * repo_model . Repository , gitRepo * git . Repository , oldBranchName , branchName string ) ( err error ) {
// Check if branch name can be used
branch , err := git_model . GetBranch ( ctx , repo . ID , oldBranchName )
if err := checkBranchName ( ctx , repo , branchName ) ; err != nil {
if err != nil {
return err
return err
}
}
if ! git . IsBranchExist ( ctx , repo . RepoPath ( ) , oldBranchName ) {
return CreateNewBranchFromCommit ( ctx , doer , repo , gitRepo , branch . CommitID , branchName )
return git_model . ErrBranchNotExist {
BranchName : oldBranchName ,
}
}
if err := git . Push ( ctx , repo . RepoPath ( ) , git . PushOptions {
Remote : repo . RepoPath ( ) ,
Branch : fmt . Sprintf ( "%s%s:%s%s" , git . BranchPrefix , oldBranchName , git . BranchPrefix , branchName ) ,
Env : repo_module . PushingEnvironment ( doer , repo ) ,
} ) ; err != nil {
if git . IsErrPushOutOfDate ( err ) || git . IsErrPushRejected ( err ) {
return err
}
return fmt . Errorf ( "push: %w" , err )
}
return nil
}
}
// Branch contains the branch information
// Branch contains the branch information
@ -244,25 +228,81 @@ func checkBranchName(ctx context.Context, repo *repo_model.Repository, name stri
return err
return err
}
}
// syncBranchToDB sync the branch information in the database. It will try to update the branch first,
// if updated success with affect records > 0, then all are done. Because that means the branch has been in the database.
// If no record is affected, that means the branch does not exist in database. So there are two possibilities.
// One is this is a new branch, then we just need to insert the record. Another is the branches haven't been synced,
// then we need to sync all the branches into database.
func syncBranchToDB ( ctx context . Context , repoID , pusherID int64 , branchName string , commit * git . Commit ) error {
cnt , err := git_model . UpdateBranch ( ctx , repoID , pusherID , branchName , commit )
if err != nil {
return fmt . Errorf ( "git_model.UpdateBranch %d:%s failed: %v" , repoID , branchName , err )
}
if cnt > 0 { // This means branch does exist, so it's a normal update. It also means the branch has been synced.
return nil
}
// if user haven't visit UI but directly push to a branch after upgrading from 1.20 -> 1.21,
// we cannot simply insert the branch but need to check we have branches or not
hasBranch , err := db . Exist [ git_model . Branch ] ( ctx , git_model . FindBranchOptions {
RepoID : repoID ,
IsDeletedBranch : util . OptionalBoolFalse ,
} . ToConds ( ) )
if err != nil {
return err
}
if ! hasBranch {
if _ , err = repo_module . SyncRepoBranches ( ctx , repoID , pusherID ) ; err != nil {
return fmt . Errorf ( "repo_module.SyncRepoBranches %d:%s failed: %v" , repoID , branchName , err )
}
return nil
}
// if database have branches but not this branch, it means this is a new branch
return db . Insert ( ctx , & git_model . Branch {
RepoID : repoID ,
Name : branchName ,
CommitID : commit . ID . String ( ) ,
CommitMessage : commit . Summary ( ) ,
PusherID : pusherID ,
CommitTime : timeutil . TimeStamp ( commit . Committer . When . Unix ( ) ) ,
} )
}
// CreateNewBranchFromCommit creates a new repository branch
// CreateNewBranchFromCommit creates a new repository branch
func CreateNewBranchFromCommit ( ctx context . Context , doer * user_model . User , repo * repo_model . Repository , commit , branchName string ) ( err error ) {
func CreateNewBranchFromCommit ( ctx context . Context , doer * user_model . User , repo * repo_model . Repository , gitRepo * git . Repository , commitID , branchName string ) ( err error ) {
err = repo . MustNotBeArchived ( )
if err != nil {
return err
}
// Check if branch name can be used
// Check if branch name can be used
if err := checkBranchName ( ctx , repo , branchName ) ; err != nil {
if err := checkBranchName ( ctx , repo , branchName ) ; err != nil {
return err
return err
}
}
if err := git . Push ( ctx , repo . RepoPath ( ) , git . PushOptions {
return db . WithTx ( ctx , func ( ctx context . Context ) error {
Remote : repo . RepoPath ( ) ,
commit , err := gitRepo . GetCommit ( commitID )
Branch : fmt . Sprintf ( "%s:%s%s" , commit , git . BranchPrefix , branchName ) ,
if err != nil {
Env : repo_module . PushingEnvironment ( doer , repo ) ,
return err
} ) ; err != nil {
}
if git . IsErrPushOutOfDate ( err ) || git . IsErrPushRejected ( err ) {
// database operation should be done before git operation so that we can rollback if git operation failed
if err := syncBranchToDB ( ctx , repo . ID , doer . ID , branchName , commit ) ; err != nil {
return err
return err
}
}
return fmt . Errorf ( "push: %w" , err )
}
return nil
if err := git . Push ( ctx , repo . RepoPath ( ) , git . PushOptions {
Remote : repo . RepoPath ( ) ,
Branch : fmt . Sprintf ( "%s:%s%s" , commitID , git . BranchPrefix , branchName ) ,
Env : repo_module . PushingEnvironment ( doer , repo ) ,
} ) ; err != nil {
if git . IsErrPushOutOfDate ( err ) || git . IsErrPushRejected ( err ) {
return err
}
return fmt . Errorf ( "push: %w" , err )
}
return nil
} )
}
}
// RenameBranch rename a branch
// RenameBranch rename a branch