From a16ca3c57cc1fc05a1839bb604a82abcc62052c5 Mon Sep 17 00:00:00 2001 From: silverwind Date: Fri, 30 Jan 2026 21:12:24 +0100 Subject: [PATCH] Don't create self-references in merged PRs (#36490) Fixes: https://github.com/go-gitea/gitea/issues/36488 Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: Giteabot --- services/issue/commit.go | 23 ++++++++++++++ services/issue/commit_test.go | 56 +++++++++++++++++++++++++++++++++++ 2 files changed, 79 insertions(+) diff --git a/services/issue/commit.go b/services/issue/commit.go index 963d0359fd..66ad93a97d 100644 --- a/services/issue/commit.go +++ b/services/issue/commit.go @@ -89,6 +89,24 @@ func issueAddTime(ctx context.Context, issue *issues_model.Issue, doer *user_mod return err } +// isSelfReference checks if a commit is the merge commit of the PR it references. +// This prevents creating self-referencing timeline entries when a PR merge commit +// contains a reference to its own PR number in the commit message. +func isSelfReference(ctx context.Context, issue *issues_model.Issue, commitSHA string) bool { + if !issue.IsPull { + return false + } + + if err := issue.LoadPullRequest(ctx); err != nil { + if !issues_model.IsErrPullRequestNotExist(err) { + log.Error("LoadPullRequest: %v", err) + } + return false + } + + return issue.PullRequest.MergedCommitID == commitSHA +} + // getIssueFromRef returns the issue referenced by a ref. Returns a nil *Issue // if the provided ref references a non-existent issue. func getIssueFromRef(ctx context.Context, repo *repo_model.Repository, index int64) (*issues_model.Issue, error) { @@ -158,6 +176,11 @@ func UpdateIssuesCommit(ctx context.Context, doer *user_model.User, repo *repo_m continue } + // Skip self-references: if this commit is the merge commit of the PR it references + if isSelfReference(ctx, refIssue, c.Sha1) { + continue + } + message := fmt.Sprintf(`%s`, html.EscapeString(repo.Link()), html.EscapeString(url.PathEscape(c.Sha1)), html.EscapeString(strings.SplitN(c.Message, "\n", 2)[0])) if err = CreateRefComment(ctx, doer, refRepo, refIssue, message, c.Sha1); err != nil { if errors.Is(err, user_model.ErrBlockedUser) { diff --git a/services/issue/commit_test.go b/services/issue/commit_test.go index d19702269a..5950762c44 100644 --- a/services/issue/commit_test.go +++ b/services/issue/commit_test.go @@ -298,3 +298,59 @@ func TestUpdateIssuesCommit_AnotherRepoNoPermission(t *testing.T) { unittest.AssertNotExistsBean(t, issueBean, "is_closed=1") unittest.CheckConsistencyFor(t, &activities_model.Action{}) } + +func TestUpdateIssuesCommit_SelfReference(t *testing.T) { + assert.NoError(t, unittest.PrepareTestDatabase()) + user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) + + // Test that a PR merge commit that references its own PR does not create a self-reference comment + // PR #2 (issue_id=2) has merged_commit_id: 1a8823cd1a9549fde083f992f6b9b87a7ab74fb3 + pushCommits := []*repository.PushCommit{ + { + Sha1: "1a8823cd1a9549fde083f992f6b9b87a7ab74fb3", + CommitterEmail: "user2@example.com", + CommitterName: "User Two", + AuthorEmail: "user2@example.com", + AuthorName: "User Two", + Message: "Merge pull request 'issue2' (#2) from branch1 into master", + }, + } + + repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) + + selfRefCommentBean := &issues_model.Comment{ + Type: issues_model.CommentTypeCommitRef, + CommitSHA: "1a8823cd1a9549fde083f992f6b9b87a7ab74fb3", + PosterID: user.ID, + IssueID: 2, + } + + unittest.AssertNotExistsBean(t, selfRefCommentBean) + assert.NoError(t, UpdateIssuesCommit(t.Context(), user, repo, pushCommits, repo.DefaultBranch)) + unittest.AssertNotExistsBean(t, selfRefCommentBean) + unittest.CheckConsistencyFor(t, &activities_model.Action{}) + + // Test that normal commit references are still created + pushCommits2 := []*repository.PushCommit{ + { + Sha1: "abcdef9876543210", + CommitterEmail: "user2@example.com", + CommitterName: "User Two", + AuthorEmail: "user2@example.com", + AuthorName: "User Two", + Message: "Fix bug, refs #1", + }, + } + + otherRefCommentBean := &issues_model.Comment{ + Type: issues_model.CommentTypeCommitRef, + CommitSHA: "abcdef9876543210", + PosterID: user.ID, + IssueID: 1, + } + + unittest.AssertNotExistsBean(t, otherRefCommentBean) + assert.NoError(t, UpdateIssuesCommit(t.Context(), user, repo, pushCommits2, repo.DefaultBranch)) + unittest.AssertExistsAndLoadBean(t, otherRefCommentBean) + unittest.CheckConsistencyFor(t, &activities_model.Action{}) +}