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.go100
1 files changed, 65 insertions, 35 deletions
diff --git a/services/repository/branch.go b/services/repository/branch.go
index 735fa1a756..6dc286675f 100644
--- a/services/repository/branch.go
+++ b/services/repository/branch.go
@@ -20,6 +20,7 @@ import (
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/queue"
repo_module "code.gitea.io/gitea/modules/repository"
+ "code.gitea.io/gitea/modules/timeutil"
"code.gitea.io/gitea/modules/util"
notify_service "code.gitea.io/gitea/services/notify"
files_service "code.gitea.io/gitea/services/repository/files"
@@ -28,35 +29,13 @@ import (
)
// CreateNewBranch creates a new repository branch
-func CreateNewBranch(ctx context.Context, doer *user_model.User, repo *repo_model.Repository, oldBranchName, branchName string) (err error) {
- err = repo.MustNotBeArchived()
+func CreateNewBranch(ctx context.Context, doer *user_model.User, repo *repo_model.Repository, gitRepo *git.Repository, oldBranchName, branchName string) (err error) {
+ branch, err := git_model.GetBranch(ctx, repo.ID, oldBranchName)
if err != nil {
return err
}
- // Check if branch name can be used
- if err := checkBranchName(ctx, repo, branchName); err != nil {
- return err
- }
-
- if !git.IsBranchExist(ctx, repo.RepoPath(), oldBranchName) {
- 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
+ return CreateNewBranchFromCommit(ctx, doer, repo, gitRepo, branch.CommitID, branchName)
}
// Branch contains the branch information
@@ -249,8 +228,49 @@ func checkBranchName(ctx context.Context, repo *repo_model.Repository, name stri
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
-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
@@ -261,18 +281,28 @@ func CreateNewBranchFromCommit(ctx context.Context, doer *user_model.User, repo
return err
}
- if err := git.Push(ctx, repo.RepoPath(), git.PushOptions{
- Remote: repo.RepoPath(),
- Branch: fmt.Sprintf("%s:%s%s", commit, git.BranchPrefix, branchName),
- Env: repo_module.PushingEnvironment(doer, repo),
- }); err != nil {
- if git.IsErrPushOutOfDate(err) || git.IsErrPushRejected(err) {
+ return db.WithTx(ctx, func(ctx context.Context) error {
+ commit, err := gitRepo.GetCommit(commitID)
+ if err != nil {
+ return 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 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