aboutsummaryrefslogtreecommitdiffstats
path: root/services/repository/branch.go
diff options
context:
space:
mode:
Diffstat (limited to 'services/repository/branch.go')
-rw-r--r--services/repository/branch.go123
1 files changed, 114 insertions, 9 deletions
diff --git a/services/repository/branch.go b/services/repository/branch.go
index fc476298ca..6e0065b277 100644
--- a/services/repository/branch.go
+++ b/services/repository/branch.go
@@ -26,9 +26,11 @@ import (
"code.gitea.io/gitea/modules/optional"
"code.gitea.io/gitea/modules/queue"
repo_module "code.gitea.io/gitea/modules/repository"
+ "code.gitea.io/gitea/modules/reqctx"
"code.gitea.io/gitea/modules/timeutil"
"code.gitea.io/gitea/modules/util"
webhook_module "code.gitea.io/gitea/modules/webhook"
+ actions_service "code.gitea.io/gitea/services/actions"
notify_service "code.gitea.io/gitea/services/notify"
release_service "code.gitea.io/gitea/services/release"
files_service "code.gitea.io/gitea/services/repository/files"
@@ -231,7 +233,7 @@ func loadOneBranch(ctx context.Context, repo *repo_model.Repository, dbBranch *g
defer baseGitRepo.Close()
repoIDToGitRepo[pr.BaseRepoID] = baseGitRepo
}
- pullCommit, err := baseGitRepo.GetRefCommitID(pr.GetGitRefName())
+ pullCommit, err := baseGitRepo.GetRefCommitID(pr.GetGitHeadRefName())
if err != nil && !git.IsErrNotExist(err) {
return nil, fmt.Errorf("GetBranchCommitID: %v", err)
}
@@ -301,7 +303,7 @@ func SyncBranchesToDB(ctx context.Context, repoID, pusherID int64, branchNames,
// For other batches, it will hit optimization 4.
if len(branchNames) != len(commitIDs) {
- return fmt.Errorf("branchNames and commitIDs length not match")
+ return errors.New("branchNames and commitIDs length not match")
}
return db.WithTx(ctx, func(ctx context.Context) error {
@@ -408,14 +410,37 @@ func RenameBranch(ctx context.Context, repo *repo_model.Repository, doer *user_m
return "target_exist", nil
}
- if gitRepo.IsBranchExist(to) {
+ if gitrepo.IsBranchExist(ctx, repo, to) {
return "target_exist", nil
}
- if !gitRepo.IsBranchExist(from) {
+ if !gitrepo.IsBranchExist(ctx, repo, from) {
return "from_not_exist", nil
}
+ perm, err := access_model.GetUserRepoPermission(ctx, repo, doer)
+ if err != nil {
+ return "", err
+ }
+
+ isDefault := from == repo.DefaultBranch
+ if isDefault && !perm.IsAdmin() {
+ return "", repo_model.ErrUserDoesNotHaveAccessToRepo{
+ UserID: doer.ID,
+ RepoName: repo.LowerName,
+ }
+ }
+
+ // If from == rule name, admins are allowed to modify them.
+ if protectedBranch, err := git_model.GetProtectedBranchRuleByName(ctx, repo.ID, from); err != nil {
+ return "", err
+ } else if protectedBranch != nil && !perm.IsAdmin() {
+ return "", repo_model.ErrUserDoesNotHaveAccessToRepo{
+ UserID: doer.ID,
+ RepoName: repo.LowerName,
+ }
+ }
+
if err := git_model.RenameBranch(ctx, repo, from, to, func(ctx context.Context, isDefault bool) error {
err2 := gitRepo.RenameBranch(from, to)
if err2 != nil {
@@ -428,7 +453,7 @@ func RenameBranch(ctx context.Context, repo *repo_model.Repository, doer *user_m
log.Error("DeleteCronTaskByRepo: %v", err)
}
// cancel running cron jobs of this repository and delete old schedules
- if err := actions_model.CancelPreviousJobs(
+ if err := actions_service.CancelPreviousJobs(
ctx,
repo.ID,
from,
@@ -489,7 +514,7 @@ func CanDeleteBranch(ctx context.Context, repo *repo_model.Repository, branchNam
}
// DeleteBranch delete branch
-func DeleteBranch(ctx context.Context, doer *user_model.User, repo *repo_model.Repository, gitRepo *git.Repository, branchName string) error {
+func DeleteBranch(ctx context.Context, doer *user_model.User, repo *repo_model.Repository, gitRepo *git.Repository, branchName string, pr *issues_model.PullRequest) error {
err := repo.MustNotBeArchived()
if err != nil {
return err
@@ -519,6 +544,12 @@ func DeleteBranch(ctx context.Context, doer *user_model.User, repo *repo_model.R
}
}
+ if pr != nil {
+ if err := issues_model.AddDeletePRBranchComment(ctx, doer, pr.BaseRepo, pr.Issue.ID, pr.HeadBranch); err != nil {
+ return fmt.Errorf("DeleteBranch: %v", err)
+ }
+ }
+
return gitRepo.DeleteBranch(branchName, git.DeleteBranchOptions{
Force: true,
})
@@ -587,12 +618,12 @@ func AddAllRepoBranchesToSyncQueue(ctx context.Context) error {
return nil
}
-func SetRepoDefaultBranch(ctx context.Context, repo *repo_model.Repository, gitRepo *git.Repository, newBranchName string) error {
+func SetRepoDefaultBranch(ctx context.Context, repo *repo_model.Repository, newBranchName string) error {
if repo.DefaultBranch == newBranchName {
return nil
}
- if !gitRepo.IsBranchExist(newBranchName) {
+ if !gitrepo.IsBranchExist(ctx, repo, newBranchName) {
return git_model.ErrBranchNotExist{
BranchName: newBranchName,
}
@@ -609,7 +640,7 @@ func SetRepoDefaultBranch(ctx context.Context, repo *repo_model.Repository, gitR
log.Error("DeleteCronTaskByRepo: %v", err)
}
// cancel running cron jobs of this repository and delete old schedules
- if err := actions_model.CancelPreviousJobs(
+ if err := actions_service.CancelPreviousJobs(
ctx,
repo.ID,
oldDefaultBranchName,
@@ -632,7 +663,81 @@ func SetRepoDefaultBranch(ctx context.Context, repo *repo_model.Repository, gitR
}
}
+ // clear divergence cache
+ if err := DelRepoDivergenceFromCache(ctx, repo.ID); err != nil {
+ log.Error("DelRepoDivergenceFromCache: %v", err)
+ }
+
notify_service.ChangeDefaultBranch(ctx, repo)
return nil
}
+
+// BranchDivergingInfo contains the information about the divergence of a head branch to the base branch.
+type BranchDivergingInfo struct {
+ // whether the base branch contains new commits which are not in the head branch
+ BaseHasNewCommits bool
+
+ // behind/after are number of commits that the head branch is behind/after the base branch, it's 0 if it's unable to calculate.
+ // there could be a case that BaseHasNewCommits=true while the behind/after are both 0 (unable to calculate).
+ HeadCommitsBehind int
+ HeadCommitsAhead int
+}
+
+// GetBranchDivergingInfo returns the information about the divergence of a patch branch to the base branch.
+func GetBranchDivergingInfo(ctx reqctx.RequestContext, baseRepo *repo_model.Repository, baseBranch string, headRepo *repo_model.Repository, headBranch string) (*BranchDivergingInfo, error) {
+ headGitBranch, err := git_model.GetBranch(ctx, headRepo.ID, headBranch)
+ if err != nil {
+ return nil, err
+ }
+ if headGitBranch.IsDeleted {
+ return nil, git_model.ErrBranchNotExist{
+ BranchName: headBranch,
+ }
+ }
+ baseGitBranch, err := git_model.GetBranch(ctx, baseRepo.ID, baseBranch)
+ if err != nil {
+ return nil, err
+ }
+ if baseGitBranch.IsDeleted {
+ return nil, git_model.ErrBranchNotExist{
+ BranchName: baseBranch,
+ }
+ }
+
+ info := &BranchDivergingInfo{}
+ if headGitBranch.CommitID == baseGitBranch.CommitID {
+ return info, nil
+ }
+
+ // if the fork repo has new commits, this call will fail because they are not in the base repo
+ // exit status 128 - fatal: Invalid symmetric difference expression aaaaaaaaaaaa...bbbbbbbbbbbb
+ // so at the moment, we first check the update time, then check whether the fork branch has base's head
+ diff, err := git.GetDivergingCommits(ctx, baseRepo.RepoPath(), baseGitBranch.CommitID, headGitBranch.CommitID)
+ if err != nil {
+ info.BaseHasNewCommits = baseGitBranch.UpdatedUnix > headGitBranch.UpdatedUnix
+ if headRepo.IsFork && info.BaseHasNewCommits {
+ return info, nil
+ }
+ // if the base's update time is before the fork, check whether the base's head is in the fork
+ headGitRepo, err := gitrepo.RepositoryFromRequestContextOrOpen(ctx, headRepo)
+ if err != nil {
+ return nil, err
+ }
+ headCommit, err := headGitRepo.GetCommit(headGitBranch.CommitID)
+ if err != nil {
+ return nil, err
+ }
+ baseCommitID, err := git.NewIDFromString(baseGitBranch.CommitID)
+ if err != nil {
+ return nil, err
+ }
+ hasPreviousCommit, _ := headCommit.HasPreviousCommit(baseCommitID)
+ info.BaseHasNewCommits = !hasPreviousCommit
+ return info, nil
+ }
+
+ info.HeadCommitsBehind, info.HeadCommitsAhead = diff.Behind, diff.Ahead
+ info.BaseHasNewCommits = info.HeadCommitsBehind > 0
+ return info, nil
+}