From 27e33b7ba1cbf9eeede88d9709ae71b5c1f18798 Mon Sep 17 00:00:00 2001 From: bircni Date: Thu, 9 Jul 2026 22:31:14 +0200 Subject: [PATCH] fix: incorrect co-author detection on commit page (#38386) The commit page built its "co-authored by" list from `AllParticipantIdentities()[1:]`, which only drops the author and leaves the committer in the list even though the committer is already shown separately as "committed by". This caused the committer to be wrongly rendered as a co-author (issue #38384): a commit authored by `silverwind`, committed by `bircni`, with a `Co-authored-by: silverwind` trailer displayed "co-authored by bircni" instead of the actual trailer identity. This adds a `Commit.CoAuthorIdentities()` method that excludes both the author and the committer, uses it on the commit page, and covers the fix with a regression test. Closes #38384 --- modules/git/commit_message.go | 14 +++++++++++ modules/git/commit_message_test.go | 39 ++++++++++++++++++++++++++++++ routers/web/repo/commit.go | 2 +- 3 files changed, 54 insertions(+), 1 deletion(-) diff --git a/modules/git/commit_message.go b/modules/git/commit_message.go index 0729609d87..9aa3a6966a 100644 --- a/modules/git/commit_message.go +++ b/modules/git/commit_message.go @@ -131,3 +131,17 @@ func (c *Commit) AllParticipantIdentities() []*CommitIdentity { } return c.allParticipants } + +// CoAuthorIdentities returns the commit's co-authors: the participants without the +// author and the committer, which are already displayed separately. This avoids +// surfacing the committer as a "co-author" (see issue #38384). +func (c *Commit) CoAuthorIdentities() []*CommitIdentity { + var ret []*CommitIdentity + for _, p := range c.AllParticipantIdentities()[1:] { // index 0 is always the author + if c.Committer.Email != "" && strings.EqualFold(p.Email, c.Committer.Email) { + continue + } + ret = append(ret, p) + } + return ret +} diff --git a/modules/git/commit_message_test.go b/modules/git/commit_message_test.go index 1d13f81803..ba5927d9fd 100644 --- a/modules/git/commit_message_test.go +++ b/modules/git/commit_message_test.go @@ -80,3 +80,42 @@ func TestCommitMessageAllParticipantIdentities(t *testing.T) { assert.Equal(t, c.participant, c.commit.AllParticipantIdentities()) } } + +func TestCommitMessageCoAuthorIdentities(t *testing.T) { + sig := func(n, e string) *Signature { return &Signature{Name: n, Email: e} } + idt := func(n, e string) *CommitIdentity { return &CommitIdentity{Name: n, Email: e} } + cases := []struct { + commit *Commit + coAuthors []*CommitIdentity + }{ + { + // a genuine co-author (neither author nor committer) is reported + &Commit{ + Author: sig("a", "a@m.com"), Committer: sig("c", "c@m.com"), + CommitMessage: CommitMessage{MessageRaw: "Co-authored-by: x "}, + }, + []*CommitIdentity{idt("x", "x@m.com")}, + }, + { + // the committer is shown separately as "committed by", so it must + // not appear as a co-author even though it is a participant + &Commit{ + Author: sig("a", "a@m.com"), Committer: sig("c", "c@m.com"), + CommitMessage: CommitMessage{MessageRaw: "Co-authored-by: c "}, + }, + nil, + }, + { + // regression for #38384: a Co-authored-by trailer naming the author + // must not cause the committer to be surfaced as the co-author + &Commit{ + Author: sig("silverwind", "me@silverwind.io"), Committer: sig("bircni", "bircni@icloud.com"), + CommitMessage: CommitMessage{MessageRaw: "Co-authored-by: silverwind "}, + }, + nil, + }, + } + for _, c := range cases { + assert.Equal(t, c.coAuthors, c.commit.CoAuthorIdentities()) + } +} diff --git a/routers/web/repo/commit.go b/routers/web/repo/commit.go index d895387818..b1b1909e10 100644 --- a/routers/web/repo/commit.go +++ b/routers/web/repo/commit.go @@ -398,7 +398,7 @@ func Diff(ctx *context.Context) { verification := asymkey_service.ParseCommitWithSignature(ctx, commit) ctx.Data["Verification"] = verification ctx.Data["Author"] = user_model.GetUserByGitAuthor(ctx, commit) - ctx.Data["CommitOtherParticipants"] = gituser.BuildAvatarStackData(ctx, commit.AllParticipantIdentities(), nil).Participants[1:] + ctx.Data["CommitOtherParticipants"] = gituser.BuildAvatarStackData(ctx, commit.CoAuthorIdentities(), nil).Participants ctx.Data["Parents"] = parents ctx.Data["DiffNotAvailable"] = diffShortStat.NumFiles == 0