diff --git a/modules/git/commit_message.go b/modules/git/commit_message.go index 0729609d87e..9aa3a6966a3 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 1d13f818033..ba5927d9fd6 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 d8953878184..b1b1909e10c 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