@@ -12,7 +12,6 @@ import (
1212 "code.gitea.io/gitea/models/db"
1313 git_model "code.gitea.io/gitea/models/git"
1414 "code.gitea.io/gitea/models/issues"
15- issues_model "code.gitea.io/gitea/models/issues"
1615 "code.gitea.io/gitea/models/repo"
1716 "code.gitea.io/gitea/modules/log"
1817 "code.gitea.io/gitea/modules/optional"
@@ -41,6 +40,9 @@ type ReadyResponse struct {
4140 ReadyIssues []ReadyIssue `json:"ready_issues"`
4241}
4342
43+ // taskBranchRe matches task/<IDX>-* branch naming convention (e.g., task/69-ci)
44+ var taskBranchRe = regexp .MustCompile (`^task/(\d+)-` )
45+
4446// Ready returns issues that are ready to be worked on (no blocking dependencies)
4547func Ready (ctx * context.APIContext ) {
4648 // 1. Check feature enabled
@@ -300,29 +302,35 @@ func getInProgressIssues(ctx *context.APIContext, repository *repo.Repository) (
300302 return inProgress , nil
301303}
302304
303- // findInProgressByBranches finds issues that have a branch matching task/<IDX>-* pattern
305+ // findInProgressByBranches finds issues that have a branch matching task/<IDX>-* pattern.
306+ // Uses a single batch query instead of N+1 per-branch lookups.
304307func findInProgressByBranches (ctx * context.APIContext , repository * repo.Repository , inProgress map [int64 ]bool ) error {
305- // Get all non-deleted branches for this repo
306308 branches , err := git_model .GetBranches (ctx , repository .ID , nil , false )
307309 if err != nil {
308310 return err
309311 }
310312
311- // Pattern to match task/<IDX>-* e.g., task/69-ci
312- pattern := regexp .MustCompile (`^task/(\d+)-` )
313-
313+ var indices []string
314314 for _ , branch := range branches {
315- matches := pattern .FindStringSubmatch (branch .Name )
315+ matches := taskBranchRe .FindStringSubmatch (branch .Name )
316316 if len (matches ) >= 2 {
317- issueIndex := matches [1 ]
318- // We need to find the issue with this index
319- issueID , err := getIssueIDByIndex (ctx , repository .ID , issueIndex )
320- if err == nil && issueID > 0 {
321- inProgress [issueID ] = true
322- }
317+ indices = append (indices , matches [1 ])
323318 }
324319 }
325320
321+ if len (indices ) == 0 {
322+ return nil
323+ }
324+
325+ issueIDs , err := getIssueIDsByIndices (ctx , repository .ID , indices )
326+ if err != nil {
327+ return err
328+ }
329+
330+ for id := range issueIDs {
331+ inProgress [id ] = true
332+ }
333+
326334 return nil
327335}
328336
@@ -346,11 +354,12 @@ func findInProgressByPRs(ctx *context.APIContext, repository *repo.Repository, i
346354
347355// findInProgressByLabels finds issues with status/in-progress label
348356func findInProgressByLabels (ctx * context.APIContext , repository * repo.Repository , inProgress map [int64 ]bool ) error {
349- // Get the status/in-progress label
350- label , err := issues_model .GetLabelInRepoByName (ctx , repository .ID , "status/in-progress" )
357+ label , err := issues .GetLabelInRepoByName (ctx , repository .ID , "status/in-progress" )
351358 if err != nil {
352- // Label doesn't exist, that's fine - no issues can have it
353- return nil
359+ if issues .IsErrRepoLabelNotExist (err ) {
360+ return nil
361+ }
362+ return err
354363 }
355364
356365 // Get all issues with this label
@@ -366,23 +375,48 @@ func findInProgressByLabels(ctx *context.APIContext, repository *repo.Repository
366375 return nil
367376}
368377
369- // getIssueIDByIndex returns the issue ID for a given repository and issue index
370- func getIssueIDByIndex (ctx * context.APIContext , repoID int64 , indexStr string ) (int64 , error ) {
371- var issueID int64
372- _ , err := db .GetEngine (ctx ).
378+ // getIssueIDsByIndices returns a set of issue IDs for the given repo and issue indices.
379+ // Uses a single batch query to avoid N+1 lookups.
380+ // Does not filter by is_closed: the ready list only contains open issues,
381+ // so a branch for a closed issue is harmless (a stale branch).
382+ func getIssueIDsByIndices (ctx * context.APIContext , repoID int64 , indices []string ) (map [int64 ]bool , error ) {
383+ result := make (map [int64 ]bool )
384+ if len (indices ) == 0 {
385+ return result , nil
386+ }
387+
388+ type row struct {
389+ ID int64
390+ }
391+
392+ rows := make ([]row , 0 )
393+ sess := db .GetEngine (ctx ).
373394 Table ("issue" ).
374395 Select ("id" ).
375- Where ("repo_id = ? AND index = ? AND is_closed = ? AND is_pull = ?" , repoID , indexStr , false , false ).
376- Get (& issueID )
377- if err != nil {
378- return 0 , err
396+ Where ("repo_id = ? AND is_pull = ?" , repoID , false ).
397+ In ("index" , toAnySlice (indices ))
398+ if err := sess .Find (& rows ); err != nil {
399+ return nil , err
400+ }
401+
402+ for _ , r := range rows {
403+ result [r .ID ] = true
404+ }
405+ return result , nil
406+ }
407+
408+ // toAnySlice converts []string to []any for xorm In() clause
409+ func toAnySlice (strs []string ) []any {
410+ out := make ([]any , len (strs ))
411+ for i , s := range strs {
412+ out [i ] = s
379413 }
380- return issueID , nil
414+ return out
381415}
382416
383417// getOpenPullRequestsForRepo returns all open pull requests for a repository
384- func getOpenPullRequestsForRepo (ctx * context.APIContext , repoID int64 ) ([]* issues_model .PullRequest , error ) {
385- prs := make ([]* issues_model .PullRequest , 0 )
418+ func getOpenPullRequestsForRepo (ctx * context.APIContext , repoID int64 ) ([]* issues .PullRequest , error ) {
419+ prs := make ([]* issues .PullRequest , 0 )
386420 err := db .GetEngine (ctx ).
387421 Table ("pull_request" ).
388422 Join ("INNER" , "issue" , "pull_request.issue_id = issue.id" ).
@@ -391,13 +425,14 @@ func getOpenPullRequestsForRepo(ctx *context.APIContext, repoID int64) ([]*issue
391425 return prs , err
392426}
393427
394- // getIssueIDsByLabel returns all issue IDs that have the given label
428+ // getIssueIDsByLabel returns all issue IDs in the given repo that have the given label
395429func getIssueIDsByLabel (ctx * context.APIContext , repoID , labelID int64 ) ([]int64 , error ) {
396430 issueIDs := make ([]int64 , 0 )
397431 err := db .GetEngine (ctx ).
398432 Table ("issue_label" ).
399- Select ("issue_id" ).
400- Where ("label_id = ?" , labelID ).
433+ Select ("issue_label.issue_id" ).
434+ Join ("INNER" , "issue" , "issue_label.issue_id = issue.id" ).
435+ Where ("issue_label.label_id = ? AND issue.repo_id = ?" , labelID , repoID ).
401436 Find (& issueIDs )
402437 return issueIDs , err
403438}
0 commit comments