Skip to content
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions models/actions/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ func TestMain(m *testing.M) {
unittest.MainTest(m, &unittest.TestOptions{
FixtureFiles: []string{
"action_runner_token.yml",
"action_run.yml",
"repository.yml",
},
})
}
2 changes: 2 additions & 0 deletions models/actions/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -193,9 +193,11 @@ func (run *ActionRun) IsSchedule() bool {
return run.ScheduleID > 0
}

// UpdateRepoRunsNumbers updates the number of runs and closed runs of a repository.
func UpdateRepoRunsNumbers(ctx context.Context, repo *repo_model.Repository) error {
_, err := db.GetEngine(ctx).ID(repo.ID).
NoAutoTime().
Cols("num_action_runs", "num_closed_action_runs").
SetExpr("num_action_runs",
builder.Select("count(*)").From("action_run").
Where(builder.Eq{"repo_id": repo.ID}),
Expand Down
27 changes: 27 additions & 0 deletions models/actions/run_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// Copyright 2025 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT

package actions

import (
"testing"

repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unittest"

"github.com/stretchr/testify/assert"
)

func TestUpdateRepoRunsNumbers(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())

repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 4})
assert.Equal(t, 4, repo.NumActionRuns)
assert.Equal(t, 2, repo.NumClosedActionRuns)

err := UpdateRepoRunsNumbers(t.Context(), repo)
assert.NoError(t, err)
repo = unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 4})
assert.Equal(t, 4, repo.NumActionRuns)
assert.Equal(t, 3, repo.NumClosedActionRuns)
}
2 changes: 1 addition & 1 deletion models/activities/notification.go
Original file line number Diff line number Diff line change
Expand Up @@ -386,7 +386,7 @@ func SetNotificationStatus(ctx context.Context, notificationID int64, user *user

notification.Status = status

_, err = db.GetEngine(ctx).ID(notificationID).Update(notification)
_, err = db.GetEngine(ctx).ID(notificationID).Cols("status").Update(notification)
return notification, err
}

Expand Down
2 changes: 1 addition & 1 deletion models/asymkey/gpg_key_verify.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ func VerifyGPGKey(ctx context.Context, ownerID int64, keyID, token, signature st
}

key.Verified = true
if _, err := db.GetEngine(ctx).ID(key.ID).SetExpr("verified", true).Update(new(GPGKey)); err != nil {
if _, err := db.GetEngine(ctx).ID(key.ID).Cols("verified").Update(key); err != nil {
return "", err
}

Expand Down
2 changes: 2 additions & 0 deletions models/fixtures/repository.yml
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,8 @@
num_closed_milestones: 0
num_projects: 0
num_closed_projects: 1
num_action_runs: 4
num_closed_action_runs: 2 # this is wrong, should be 3. It's for testing purpose of run_test.go
is_private: false
is_empty: false
is_archived: false
Expand Down
2 changes: 1 addition & 1 deletion models/git/branch.go
Original file line number Diff line number Diff line change
Expand Up @@ -368,7 +368,7 @@ func RenameBranch(ctx context.Context, repo *repo_model.Repository, from, to str
}

// 1. update branch in database
if n, err := sess.Where("repo_id=? AND name=?", repo.ID, from).Update(&Branch{
if n, err := sess.Where("repo_id=? AND name=?", repo.ID, from).Cols("name").Update(&Branch{
Name: to,
}); err != nil {
return err
Expand Down
4 changes: 0 additions & 4 deletions models/issues/comment.go
Original file line number Diff line number Diff line change
Expand Up @@ -862,10 +862,6 @@ func updateCommentInfos(ctx context.Context, opts *CreateCommentOptions, comment
if err = UpdateCommentAttachments(ctx, comment, opts.Attachments); err != nil {
return err
}
case CommentTypeReopen, CommentTypeClose:
if err = repo_model.UpdateRepoIssueNumbers(ctx, opts.Issue.RepoID, opts.Issue.IsPull, true); err != nil {
return err
}
}
// update the issue's updated_unix column
return UpdateIssueCols(ctx, opts.Issue, "updated_unix")
Expand Down
21 changes: 18 additions & 3 deletions models/issues/issue_update.go
Original file line number Diff line number Diff line change
Expand Up @@ -145,8 +145,20 @@ func updateIssueNumbers(ctx context.Context, issue *Issue, doer *user_model.User
}
}

colName := util.Iif(issue.IsPull, "num_closed_pulls", "num_closed_issues")
dbSession := db.GetEngine(ctx)
// update repository's issue closed number
if err := repo_model.UpdateRepoIssueNumbers(ctx, issue.RepoID, issue.IsPull, true); err != nil {
switch cmtType {
case CommentTypeClose, CommentTypeMergePull:
dbSession.Incr(colName)
case CommentTypeReopen:
dbSession.Decr(colName)
default:
return nil, fmt.Errorf("invalid comment type: %d", cmtType)
}
if _, err := dbSession.ID(issue.RepoID).
NoAutoCondition().NoAutoTime().
Update(new(repo_model.Repository)); err != nil {
return nil, err
}

Expand Down Expand Up @@ -318,7 +330,6 @@ type NewIssueOptions struct {
Issue *Issue
LabelIDs []int64
Attachments []string // In UUID format.
IsPull bool
}

// NewIssueWithIndex creates issue with given index
Expand Down Expand Up @@ -369,7 +380,11 @@ func NewIssueWithIndex(ctx context.Context, doer *user_model.User, opts NewIssue
}
}

if err := repo_model.UpdateRepoIssueNumbers(ctx, opts.Issue.RepoID, opts.IsPull, false); err != nil {
// Update repository issue count
colName := util.Iif(opts.Issue.IsPull, "num_pulls", "num_issues")
if _, err := db.GetEngine(ctx).Incr(colName).ID(opts.Repo.ID).
NoAutoCondition().NoAutoTime().
Update(new(repo_model.Repository)); err != nil {
return err
}

Expand Down
16 changes: 10 additions & 6 deletions models/issues/label.go
Original file line number Diff line number Diff line change
Expand Up @@ -495,19 +495,23 @@ func CountLabelsByOrgID(ctx context.Context, orgID int64) (int64, error) {
}

func updateLabelCols(ctx context.Context, l *Label, cols ...string) error {
_, err := db.GetEngine(ctx).ID(l.ID).
SetExpr("num_issues",
sess := db.GetEngine(ctx).ID(l.ID)
if slices.Contains(cols, "num_issues") {
sess.SetExpr("num_issues",
builder.Select("count(*)").From("issue_label").
Where(builder.Eq{"label_id": l.ID}),
).
SetExpr("num_closed_issues",
)
}
if slices.Contains(cols, "num_closed_issues") {
sess.SetExpr("num_closed_issues",
builder.Select("count(*)").From("issue_label").
InnerJoin("issue", "issue_label.issue_id = issue.id").
Where(builder.Eq{
"issue_label.label_id": l.ID,
"issue.is_closed": true,
}),
).
Cols(cols...).Update(l)
)
}
_, err := sess.Cols(cols...).Update(l)
return err
}
1 change: 1 addition & 0 deletions models/issues/milestone.go
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,7 @@ func updateMilestone(ctx context.Context, m *Milestone) error {
func UpdateMilestoneCounters(ctx context.Context, id int64) error {
e := db.GetEngine(ctx)
_, err := e.ID(id).
Cols("num_issues", "num_closed_issues").
SetExpr("num_issues", builder.Select("count(*)").From("issue").Where(
builder.Eq{"milestone_id": id},
)).
Expand Down
2 changes: 1 addition & 1 deletion models/issues/pull.go
Original file line number Diff line number Diff line change
Expand Up @@ -467,13 +467,13 @@ func NewPullRequest(ctx context.Context, repo *repo_model.Repository, issue *Iss

issue.Index = idx
issue.Title = util.EllipsisDisplayString(issue.Title, 255)
issue.IsPull = true

if err = NewIssueWithIndex(ctx, issue.Poster, NewIssueOptions{
Repo: repo,
Issue: issue,
LabelIDs: labelIDs,
Attachments: uuids,
IsPull: true,
}); err != nil {
if repo_model.IsErrUserDoesNotHaveAccessToRepo(err) || IsErrNewIssueInsert(err) {
return err
Expand Down
1 change: 1 addition & 0 deletions models/migrations/v1_18/v229.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ func UpdateOpenMilestoneCounts(x *xorm.Engine) error {

for _, id := range openMilestoneIDs {
_, err := x.ID(id).
Cols("num_issues", "num_closed_issues").
SetExpr("num_issues", builder.Select("count(*)").From("issue").Where(
builder.Eq{"milestone_id": id},
)).
Expand Down
2 changes: 1 addition & 1 deletion models/repo/topic.go
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ func RemoveTopicsFromRepo(ctx context.Context, repoID int64) error {
builder.In("id",
builder.Select("topic_id").From("repo_topic").Where(builder.Eq{"repo_id": repoID}),
),
).Cols("repo_count").SetExpr("repo_count", "repo_count-1").Update(&Topic{})
).Decr("repo_count").Update(&Topic{})
if err != nil {
return err
}
Expand Down
2 changes: 1 addition & 1 deletion models/user/must_change_password.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,5 +45,5 @@ func SetMustChangePassword(ctx context.Context, all, mustChangePassword bool, in
cond = cond.And(builder.NotIn("lower_name", exclude))
}

return db.GetEngine(ctx).Where(cond).MustCols("must_change_password").Update(&User{MustChangePassword: mustChangePassword})
return db.GetEngine(ctx).Where(cond).Cols("must_change_password").Update(&User{MustChangePassword: mustChangePassword})
}
19 changes: 11 additions & 8 deletions services/issue/issue.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"code.gitea.io/gitea/modules/gitrepo"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/storage"
"code.gitea.io/gitea/modules/util"
notify_service "code.gitea.io/gitea/services/notify"
)

Expand Down Expand Up @@ -270,15 +271,17 @@ func deleteIssue(ctx context.Context, issue *issues_model.Issue) ([]string, erro
return nil, err
}

// update the total issue numbers
if err := repo_model.UpdateRepoIssueNumbers(ctx, issue.RepoID, issue.IsPull, false); err != nil {
return nil, err
}
// if the issue is closed, update the closed issue numbers
colName := util.Iif(issue.IsPull, "num_pulls", "num_issues")
closedColName := util.Iif(issue.IsPull, "num_closed_pulls", "num_closed_issues")
dbSession := db.GetEngine(ctx)
if issue.IsClosed {
if err := repo_model.UpdateRepoIssueNumbers(ctx, issue.RepoID, issue.IsPull, true); err != nil {
return nil, err
}
dbSession.Decr(closedColName)
}
// update repository's issue both total number and closed number
if _, err := dbSession.ID(issue.RepoID).Decr(colName).
NoAutoCondition().NoAutoTime().
Update(new(repo_model.Repository)); err != nil {
return nil, err
}

if err := issues_model.UpdateMilestoneCounters(ctx, issue.MilestoneID); err != nil {
Expand Down
56 changes: 56 additions & 0 deletions tests/integration/pull_create_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,12 @@ import (
"net/url"
"path"
"strings"
"sync"
"testing"

auth_model "code.gitea.io/gitea/models/auth"
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unittest"
"code.gitea.io/gitea/modules/git/gitcmd"
"code.gitea.io/gitea/modules/test"
"code.gitea.io/gitea/tests"
Expand Down Expand Up @@ -137,8 +140,15 @@ func TestPullCreate(t *testing.T) {
session := loginUser(t, "user1")
testRepoFork(t, session, "user2", "repo1", "user1", "repo1", "")
testEditFile(t, session, "user1", "repo1", "master", "README.md", "Hello, World (Edited)\n")
repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{OwnerName: "user2", Name: "repo1"})
assert.Equal(t, 3, repo1.NumPulls)
assert.Equal(t, 3, repo1.NumOpenPulls)
resp := testPullCreate(t, session, "user1", "repo1", false, "master", "master", "This is a pull title")

repo1 = unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{OwnerName: "user2", Name: "repo1"})
assert.Equal(t, 4, repo1.NumPulls)
assert.Equal(t, 4, repo1.NumOpenPulls)

// check the redirected URL
url := test.RedirectURL(resp)
assert.Regexp(t, "^/user2/repo1/pulls/[0-9]*$", url)
Expand Down Expand Up @@ -285,6 +295,44 @@ func TestPullCreatePrFromBaseToFork(t *testing.T) {
})
}

func TestPullCreateParallel(t *testing.T) {
onGiteaRun(t, func(t *testing.T, u *url.URL) {
sessionFork := loginUser(t, "user1")
testRepoFork(t, sessionFork, "user2", "repo1", "user1", "repo1", "")

repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{OwnerName: "user2", Name: "repo1"})
assert.Equal(t, 3, repo1.NumPulls)
assert.Equal(t, 3, repo1.NumOpenPulls)

var wg sync.WaitGroup
for i := range 5 {
wg.Go(func() {
branchName := fmt.Sprintf("new-branch-%d", i)
testEditFileToNewBranch(t, sessionFork, "user1", "repo1", "master", branchName, "README.md", fmt.Sprintf("Hello, World (Edited) %d\n", i))

// Create a PR
resp := testPullCreateDirectly(t, sessionFork, createPullRequestOptions{
BaseRepoOwner: "user2",
BaseRepoName: "repo1",
BaseBranch: "master",
HeadRepoOwner: "user1",
HeadRepoName: "repo1",
HeadBranch: branchName,
Title: fmt.Sprintf("This is a pull title %d", i),
})
// check the redirected URL
url := test.RedirectURL(resp)
assert.Regexp(t, "^/user2/repo1/pulls/[0-9]*$", url)
})
}
wg.Wait()

repo1 = unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{OwnerName: "user2", Name: "repo1"})
assert.Equal(t, 8, repo1.NumPulls)
assert.Equal(t, 8, repo1.NumOpenPulls)
})
}

func TestCreateAgitPullWithReadPermission(t *testing.T) {
onGiteaRun(t, func(t *testing.T, u *url.URL) {
dstPath := t.TempDir()
Expand All @@ -300,11 +348,19 @@ func TestCreateAgitPullWithReadPermission(t *testing.T) {
TreeFileContent: "temp content",
})(t)

repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{OwnerName: "user2", Name: "repo1"})
assert.Equal(t, 3, repo.NumPulls)
assert.Equal(t, 3, repo.NumOpenPulls)

err := gitcmd.NewCommand("push", "origin", "HEAD:refs/for/master", "-o").
AddDynamicArguments("topic=test-topic").
WithDir(dstPath).
Run(t.Context())
assert.NoError(t, err)

repo = unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{OwnerName: "user2", Name: "repo1"})
assert.Equal(t, 4, repo.NumPulls)
assert.Equal(t, 4, repo.NumOpenPulls)
})
}

Expand Down
Loading
Loading