123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214 |
- // Copyright 2021 The Gitea Authors. All rights reserved.
- // Use of this source code is governed by a MIT-style
- // license that can be found in the LICENSE file.
-
- package repository
-
- import (
- "context"
- "errors"
- "fmt"
- "strings"
-
- "code.gitea.io/gitea/models"
- repo_model "code.gitea.io/gitea/models/repo"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/notification"
- repo_module "code.gitea.io/gitea/modules/repository"
- pull_service "code.gitea.io/gitea/services/pull"
- )
-
- // CreateNewBranch creates a new repository branch
- func CreateNewBranch(doer *user_model.User, repo *repo_model.Repository, oldBranchName, branchName string) (err error) {
- // Check if branch name can be used
- if err := checkBranchName(git.DefaultContext, repo, branchName); err != nil {
- return err
- }
-
- if !git.IsBranchExist(git.DefaultContext, repo.RepoPath(), oldBranchName) {
- return models.ErrBranchDoesNotExist{
- BranchName: oldBranchName,
- }
- }
-
- if err := git.Push(git.DefaultContext, repo.RepoPath(), git.PushOptions{
- Remote: repo.RepoPath(),
- Branch: fmt.Sprintf("%s:%s%s", oldBranchName, git.BranchPrefix, branchName),
- Env: models.PushingEnvironment(doer, repo),
- }); err != nil {
- if git.IsErrPushOutOfDate(err) || git.IsErrPushRejected(err) {
- return err
- }
- return fmt.Errorf("Push: %v", err)
- }
-
- return nil
- }
-
- // GetBranch returns a branch by its name
- func GetBranch(repo *repo_model.Repository, branch string) (*git.Branch, error) {
- if len(branch) == 0 {
- return nil, fmt.Errorf("GetBranch: empty string for branch")
- }
- gitRepo, err := git.OpenRepository(repo.RepoPath())
- if err != nil {
- return nil, err
- }
- defer gitRepo.Close()
-
- return gitRepo.GetBranch(branch)
- }
-
- // GetBranches returns branches from the repository, skipping skip initial branches and
- // returning at most limit branches, or all branches if limit is 0.
- func GetBranches(repo *repo_model.Repository, skip, limit int) ([]*git.Branch, int, error) {
- return git.GetBranchesByPath(repo.RepoPath(), skip, limit)
- }
-
- // checkBranchName validates branch name with existing repository branches
- func checkBranchName(ctx context.Context, repo *repo_model.Repository, name string) error {
- _, err := git.WalkReferences(ctx, repo.RepoPath(), func(refName string) error {
- branchRefName := strings.TrimPrefix(refName, git.BranchPrefix)
- switch {
- case branchRefName == name:
- return models.ErrBranchAlreadyExists{
- BranchName: name,
- }
- // If branchRefName like a/b but we want to create a branch named a then we have a conflict
- case strings.HasPrefix(branchRefName, name+"/"):
- return models.ErrBranchNameConflict{
- BranchName: branchRefName,
- }
- // Conversely if branchRefName like a but we want to create a branch named a/b then we also have a conflict
- case strings.HasPrefix(name, branchRefName+"/"):
- return models.ErrBranchNameConflict{
- BranchName: branchRefName,
- }
- case refName == git.TagPrefix+name:
- return models.ErrTagAlreadyExists{
- TagName: name,
- }
- }
- return nil
- })
-
- return err
- }
-
- // CreateNewBranchFromCommit creates a new repository branch
- func CreateNewBranchFromCommit(doer *user_model.User, repo *repo_model.Repository, commit, branchName string) (err error) {
- // Check if branch name can be used
- if err := checkBranchName(git.DefaultContext, repo, branchName); err != nil {
- return err
- }
-
- if err := git.Push(git.DefaultContext, repo.RepoPath(), git.PushOptions{
- Remote: repo.RepoPath(),
- Branch: fmt.Sprintf("%s:%s%s", commit, git.BranchPrefix, branchName),
- Env: models.PushingEnvironment(doer, repo),
- }); err != nil {
- if git.IsErrPushOutOfDate(err) || git.IsErrPushRejected(err) {
- return err
- }
- return fmt.Errorf("Push: %v", err)
- }
-
- return nil
- }
-
- // RenameBranch rename a branch
- func RenameBranch(repo *repo_model.Repository, doer *user_model.User, gitRepo *git.Repository, from, to string) (string, error) {
- if from == to {
- return "target_exist", nil
- }
-
- if gitRepo.IsBranchExist(to) {
- return "target_exist", nil
- }
-
- if !gitRepo.IsBranchExist(from) {
- return "from_not_exist", nil
- }
-
- if err := models.RenameBranch(repo, from, to, func(isDefault bool) error {
- err2 := gitRepo.RenameBranch(from, to)
- if err2 != nil {
- return err2
- }
-
- if isDefault {
- err2 = gitRepo.SetDefaultBranch(to)
- if err2 != nil {
- return err2
- }
- }
-
- return nil
- }); err != nil {
- return "", err
- }
-
- notification.NotifyDeleteRef(doer, repo, "branch", git.BranchPrefix+from)
- notification.NotifyCreateRef(doer, repo, "branch", git.BranchPrefix+to)
-
- return "", nil
- }
-
- // enmuerates all branch related errors
- var (
- ErrBranchIsDefault = errors.New("branch is default")
- ErrBranchIsProtected = errors.New("branch is protected")
- )
-
- // DeleteBranch delete branch
- func DeleteBranch(doer *user_model.User, repo *repo_model.Repository, gitRepo *git.Repository, branchName string) error {
- if branchName == repo.DefaultBranch {
- return ErrBranchIsDefault
- }
-
- isProtected, err := models.IsProtectedBranch(repo.ID, branchName)
- if err != nil {
- return err
- }
-
- if isProtected {
- return ErrBranchIsProtected
- }
-
- commit, err := gitRepo.GetBranchCommit(branchName)
- if err != nil {
- return err
- }
-
- if err := gitRepo.DeleteBranch(branchName, git.DeleteBranchOptions{
- Force: true,
- }); err != nil {
- return err
- }
-
- if err := pull_service.CloseBranchPulls(doer, repo.ID, branchName); err != nil {
- return err
- }
-
- // Don't return error below this
- if err := PushUpdate(
- &repo_module.PushUpdateOptions{
- RefFullName: git.BranchPrefix + branchName,
- OldCommitID: commit.ID.String(),
- NewCommitID: git.EmptySHA,
- PusherID: doer.ID,
- PusherName: doer.Name,
- RepoUserName: repo.OwnerName,
- RepoName: repo.Name,
- }); err != nil {
- log.Error("Update: %v", err)
- }
-
- if err := models.AddDeletedBranch(repo.ID, branchName, commit.ID.String(), doer.ID); err != nil {
- log.Warn("AddDeletedBranch: %v", err)
- }
-
- return nil
- }
|