aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--models/git/branch.go33
-rw-r--r--models/git/branch_list.go8
-rw-r--r--models/git/branch_test.go2
-rw-r--r--routers/api/v1/repo/branch.go5
-rw-r--r--routers/web/repo/branch.go4
-rw-r--r--services/repository/branch.go100
-rw-r--r--services/repository/push.go2
-rw-r--r--tests/integration/api_repo_get_contents_list_test.go9
-rw-r--r--tests/integration/api_repo_get_contents_test.go9
9 files changed, 99 insertions, 73 deletions
diff --git a/models/git/branch.go b/models/git/branch.go
index 6d50fb9fb6..ffd1d7ed16 100644
--- a/models/git/branch.go
+++ b/models/git/branch.go
@@ -205,10 +205,9 @@ func DeleteBranches(ctx context.Context, repoID, doerID int64, branchIDs []int64
})
}
-// UpdateBranch updates the branch information in the database. If the branch exist, it will update latest commit of this branch information
-// If it doest not exist, insert a new record into database
-func UpdateBranch(ctx context.Context, repoID, pusherID int64, branchName string, commit *git.Commit) error {
- cnt, err := db.GetEngine(ctx).Where("repo_id=? AND name=?", repoID, branchName).
+// UpdateBranch updates the branch information in the database.
+func UpdateBranch(ctx context.Context, repoID, pusherID int64, branchName string, commit *git.Commit) (int64, error) {
+ return db.GetEngine(ctx).Where("repo_id=? AND name=?", repoID, branchName).
Cols("commit_id, commit_message, pusher_id, commit_time, is_deleted, updated_unix").
Update(&Branch{
CommitID: commit.ID.String(),
@@ -217,21 +216,6 @@ func UpdateBranch(ctx context.Context, repoID, pusherID int64, branchName string
CommitTime: timeutil.TimeStamp(commit.Committer.When.Unix()),
IsDeleted: false,
})
- if err != nil {
- return err
- }
- if cnt > 0 {
- return nil
- }
-
- return db.Insert(ctx, &Branch{
- RepoID: repoID,
- Name: branchName,
- CommitID: commit.ID.String(),
- CommitMessage: commit.Summary(),
- PusherID: pusherID,
- CommitTime: timeutil.TimeStamp(commit.Committer.When.Unix()),
- })
}
// AddDeletedBranch adds a deleted branch to the database
@@ -308,6 +292,17 @@ func RenameBranch(ctx context.Context, repo *repo_model.Repository, from, to str
sess := db.GetEngine(ctx)
+ var branch Branch
+ exist, err := db.GetEngine(ctx).Where("repo_id=? AND name=?", repo.ID, from).Get(&branch)
+ if err != nil {
+ return err
+ } else if !exist || branch.IsDeleted {
+ return ErrBranchNotExist{
+ RepoID: repo.ID,
+ BranchName: from,
+ }
+ }
+
// 1. update branch in database
if n, err := sess.Where("repo_id=? AND name=?", repo.ID, from).Update(&Branch{
Name: to,
diff --git a/models/git/branch_list.go b/models/git/branch_list.go
index b5c1301a1d..2efe495264 100644
--- a/models/git/branch_list.go
+++ b/models/git/branch_list.go
@@ -73,7 +73,7 @@ type FindBranchOptions struct {
Keyword string
}
-func (opts *FindBranchOptions) Cond() builder.Cond {
+func (opts FindBranchOptions) ToConds() builder.Cond {
cond := builder.NewCond()
if opts.RepoID > 0 {
cond = cond.And(builder.Eq{"repo_id": opts.RepoID})
@@ -92,7 +92,7 @@ func (opts *FindBranchOptions) Cond() builder.Cond {
}
func CountBranches(ctx context.Context, opts FindBranchOptions) (int64, error) {
- return db.GetEngine(ctx).Where(opts.Cond()).Count(&Branch{})
+ return db.GetEngine(ctx).Where(opts.ToConds()).Count(&Branch{})
}
func orderByBranches(sess *xorm.Session, opts FindBranchOptions) *xorm.Session {
@@ -108,7 +108,7 @@ func orderByBranches(sess *xorm.Session, opts FindBranchOptions) *xorm.Session {
}
func FindBranches(ctx context.Context, opts FindBranchOptions) (BranchList, error) {
- sess := db.GetEngine(ctx).Where(opts.Cond())
+ sess := db.GetEngine(ctx).Where(opts.ToConds())
if opts.PageSize > 0 && !opts.IsListAll() {
sess = db.SetSessionPagination(sess, &opts.ListOptions)
}
@@ -119,7 +119,7 @@ func FindBranches(ctx context.Context, opts FindBranchOptions) (BranchList, erro
}
func FindBranchNames(ctx context.Context, opts FindBranchOptions) ([]string, error) {
- sess := db.GetEngine(ctx).Select("name").Where(opts.Cond())
+ sess := db.GetEngine(ctx).Select("name").Where(opts.ToConds())
if opts.PageSize > 0 && !opts.IsListAll() {
sess = db.SetSessionPagination(sess, &opts.ListOptions)
}
diff --git a/models/git/branch_test.go b/models/git/branch_test.go
index ba69026927..07b243e5e6 100644
--- a/models/git/branch_test.go
+++ b/models/git/branch_test.go
@@ -37,7 +37,7 @@ func TestAddDeletedBranch(t *testing.T) {
},
}
- err := git_model.UpdateBranch(db.DefaultContext, repo.ID, secondBranch.PusherID, secondBranch.Name, commit)
+ _, err := git_model.UpdateBranch(db.DefaultContext, repo.ID, secondBranch.PusherID, secondBranch.Name, commit)
assert.NoError(t, err)
}
diff --git a/routers/api/v1/repo/branch.go b/routers/api/v1/repo/branch.go
index f2386a607e..677105bdb5 100644
--- a/routers/api/v1/repo/branch.go
+++ b/routers/api/v1/repo/branch.go
@@ -251,12 +251,11 @@ func CreateBranch(ctx *context.APIContext) {
}
}
- err = repo_service.CreateNewBranchFromCommit(ctx, ctx.Doer, ctx.Repo.Repository, oldCommit.ID.String(), opt.BranchName)
+ err = repo_service.CreateNewBranchFromCommit(ctx, ctx.Doer, ctx.Repo.Repository, ctx.Repo.GitRepo, oldCommit.ID.String(), opt.BranchName)
if err != nil {
if git_model.IsErrBranchNotExist(err) {
ctx.Error(http.StatusNotFound, "", "The old branch does not exist")
- }
- if models.IsErrTagAlreadyExists(err) {
+ } else if models.IsErrTagAlreadyExists(err) {
ctx.Error(http.StatusConflict, "", "The branch with the same tag already exists.")
} else if git_model.IsErrBranchAlreadyExists(err) || git.IsErrPushOutOfDate(err) {
ctx.Error(http.StatusConflict, "", "The branch already exists.")
diff --git a/routers/web/repo/branch.go b/routers/web/repo/branch.go
index bc72d5f2ec..a0bc1dadad 100644
--- a/routers/web/repo/branch.go
+++ b/routers/web/repo/branch.go
@@ -191,9 +191,9 @@ func CreateBranch(ctx *context.Context) {
}
err = release_service.CreateNewTag(ctx, ctx.Doer, ctx.Repo.Repository, target, form.NewBranchName, "")
} else if ctx.Repo.IsViewBranch {
- err = repo_service.CreateNewBranch(ctx, ctx.Doer, ctx.Repo.Repository, ctx.Repo.BranchName, form.NewBranchName)
+ err = repo_service.CreateNewBranch(ctx, ctx.Doer, ctx.Repo.Repository, ctx.Repo.GitRepo, ctx.Repo.BranchName, form.NewBranchName)
} else {
- err = repo_service.CreateNewBranchFromCommit(ctx, ctx.Doer, ctx.Repo.Repository, ctx.Repo.CommitID, form.NewBranchName)
+ err = repo_service.CreateNewBranchFromCommit(ctx, ctx.Doer, ctx.Repo.Repository, ctx.Repo.GitRepo, ctx.Repo.CommitID, form.NewBranchName)
}
if err != nil {
if models.IsErrProtectedTagName(err) {
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
diff --git a/services/repository/push.go b/services/repository/push.go
index 391c8ad4ca..b5388834c0 100644
--- a/services/repository/push.go
+++ b/services/repository/push.go
@@ -259,7 +259,7 @@ func pushUpdates(optsList []*repo_module.PushUpdateOptions) error {
commits.Commits = commits.Commits[:setting.UI.FeedMaxCommitNum]
}
- if err = git_model.UpdateBranch(ctx, repo.ID, opts.PusherID, branch, newCommit); err != nil {
+ if err = syncBranchToDB(ctx, repo.ID, opts.PusherID, branch, newCommit); err != nil {
return fmt.Errorf("git_model.UpdateBranch %s:%s failed: %v", repo.FullName(), branch, err)
}
diff --git a/tests/integration/api_repo_get_contents_list_test.go b/tests/integration/api_repo_get_contents_list_test.go
index f3a5159115..7874eddfd4 100644
--- a/tests/integration/api_repo_get_contents_list_test.go
+++ b/tests/integration/api_repo_get_contents_list_test.go
@@ -70,15 +70,16 @@ func testAPIGetContentsList(t *testing.T, u *url.URL) {
session = loginUser(t, user4.Name)
token4 := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadRepository)
- // Make a new branch in repo1
- newBranch := "test_branch"
- err := repo_service.CreateNewBranch(git.DefaultContext, user2, repo1, repo1.DefaultBranch, newBranch)
- assert.NoError(t, err)
// Get the commit ID of the default branch
gitRepo, err := git.OpenRepository(git.DefaultContext, repo1.RepoPath())
assert.NoError(t, err)
defer gitRepo.Close()
+ // Make a new branch in repo1
+ newBranch := "test_branch"
+ err = repo_service.CreateNewBranch(git.DefaultContext, user2, repo1, gitRepo, repo1.DefaultBranch, newBranch)
+ assert.NoError(t, err)
+
commitID, _ := gitRepo.GetBranchCommitID(repo1.DefaultBranch)
// Make a new tag in repo1
newTag := "test_tag"
diff --git a/tests/integration/api_repo_get_contents_test.go b/tests/integration/api_repo_get_contents_test.go
index 709bbe082a..1d708a4cdb 100644
--- a/tests/integration/api_repo_get_contents_test.go
+++ b/tests/integration/api_repo_get_contents_test.go
@@ -72,15 +72,16 @@ func testAPIGetContents(t *testing.T, u *url.URL) {
session = loginUser(t, user4.Name)
token4 := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadRepository)
- // Make a new branch in repo1
- newBranch := "test_branch"
- err := repo_service.CreateNewBranch(git.DefaultContext, user2, repo1, repo1.DefaultBranch, newBranch)
- assert.NoError(t, err)
// Get the commit ID of the default branch
gitRepo, err := git.OpenRepository(git.DefaultContext, repo1.RepoPath())
assert.NoError(t, err)
defer gitRepo.Close()
+ // Make a new branch in repo1
+ newBranch := "test_branch"
+ err = repo_service.CreateNewBranch(git.DefaultContext, user2, repo1, gitRepo, repo1.DefaultBranch, newBranch)
+ assert.NoError(t, err)
+
commitID, err := gitRepo.GetBranchCommitID(repo1.DefaultBranch)
assert.NoError(t, err)
// Make a new tag in repo1