Allow multiple projects per issue and pull requests (#36784)

Add ability to add and remove multiple projects per issue
and pull request.

Resolve #12974

---------

Signed-off-by: Icy Avocado <avocado@ovacoda.com>
Co-authored-by: Tyrone Yeh <siryeh@gmail.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: OpenCode (gpt-5.2-codex) <opencode@openai.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: Claude (Opus 4.7) <noreply@anthropic.com>
This commit is contained in:
Icy Avocado
2026-04-30 08:38:05 -06:00
committed by GitHub
parent 52d6baf5a8
commit 81692ceafa
58 changed files with 1597 additions and 430 deletions
+18 -7
View File
@@ -59,11 +59,13 @@ func MoveIssuesOnProjectColumn(ctx context.Context, doer *user_model.User, colum
continue
}
projectColumnID, err := curIssue.ProjectColumnID(ctx)
projectColumnMap, err := curIssue.ProjectColumnMap(ctx)
if err != nil {
return err
}
projectColumnID := projectColumnMap[column.ProjectID]
if projectColumnID != column.ID {
// add timeline to issue
if _, err := issues_model.CreateComment(ctx, &issues_model.CreateCommentOptions{
@@ -80,7 +82,16 @@ func MoveIssuesOnProjectColumn(ctx context.Context, doer *user_model.User, colum
}
}
_, err = db.Exec(ctx, "UPDATE `project_issue` SET project_board_id=?, sorting=? WHERE issue_id=?", column.ID, sorting, issueID)
// Update the column and sorting for this specific issue in this specific project.
// IMPORTANT: The WHERE clause must include both issue_id AND project_id to ensure
// that moving an issue's column in one project doesn't affect its column in other
// projects when the issue is assigned to multiple projects.
_, err = db.GetEngine(ctx).Table("project_issue").
Where("issue_id = ? AND project_id = ?", issueID, column.ProjectID).
Update(map[string]any{
"project_board_id": column.ID,
"sorting": sorting,
})
if err != nil {
return err
}
@@ -117,7 +128,7 @@ func LoadIssuesAssigneesForProject(ctx context.Context, issuesMap map[int64]issu
// LoadIssuesFromProject load issues assigned to each project column inside the given project
func LoadIssuesFromProject(ctx context.Context, project *project_model.Project, opts *issues_model.IssuesOptions) (results map[int64]issues_model.IssueList, _ error) {
issueList, err := issues_model.Issues(ctx, opts.Copy(func(o *issues_model.IssuesOptions) {
o.ProjectID = project.ID
o.ProjectIDs = []int64{project.ID}
o.SortType = "project-column-sorting"
}))
if err != nil {
@@ -211,10 +222,10 @@ func LoadIssueNumbersForProject(ctx context.Context, project *project_model.Proj
// for user or org projects, we need to check access permissions
opts := issues_model.IssuesOptions{
ProjectID: project.ID,
Doer: doer,
AllPublic: doer == nil,
Owner: project.Owner,
ProjectIDs: []int64{project.ID},
Doer: doer,
AllPublic: doer == nil,
Owner: project.Owner,
}
var err error