Skip to content

Add inline commit coments #35240

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 8 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
546 changes: 546 additions & 0 deletions models/git/commit_comment.go

Large diffs are not rendered by default.

493 changes: 489 additions & 4 deletions routers/web/repo/commit.go

Large diffs are not rendered by default.

20 changes: 20 additions & 0 deletions routers/web/web.go
Original file line number Diff line number Diff line change
Expand Up @@ -1551,6 +1551,26 @@ func registerWebRoutes(m *web.Router) {
}, optSignIn, context.RepoAssignment, repo.MustAllowPulls, reqUnitPullsReader)
// end "/{username}/{reponame}/pulls/{index}": repo pull request

m.Group("/{username}/{reponame}", func() {
m.Group("/commit/{sha:([a-f0-9]{7,64})$}", func() {
m.Get("", repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.SetWhitespaceBehavior, repo.Diff)
m.Post("/attachments", repo.UploadCommitAttachment)
m.Get("/{id}/attachments/{uuid}", repo.GetCommitAttachmentByUUID)
m.Group("/comments", func() {
m.Get("/new_comment", repo.RenderCommitCommentForm)
m.Post("/cancel", repo.CancelCommitComment)
m.Post("/add", web.Bind(forms.CodeCommentForm{}), repo.SetShowOutdatedComments, repo.CreateCommitComment)
m.Post("/{id}/delete", repo.DeleteCommitComment)
m.Post("/{id}/update", repo.UpdateCommitComment)
m.Get("/{id}/attachments", repo.GetCommitAttachments)
m.Post("/attachments/remove", repo.DeleteCommitAttachment)
}, context.RepoMustNotBeArchived())
m.Group("/comments/{id}", func() {
m.Post("/reactions/{action}", web.Bind(forms.ReactionForm{}), repo.ChangeCommitCommentReaction)
})
})
}, optSignIn, context.RepoAssignment, repo.MustAllowPulls, reqUnitPullsReader)

m.Group("/{username}/{reponame}", func() {
m.Group("/activity_author_data", func() {
m.Get("", repo.ActivityAuthors)
Expand Down
14 changes: 12 additions & 2 deletions services/context/upload/upload.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
package upload

import (
"fmt"
"mime"
"net/http"
"net/url"
Expand Down Expand Up @@ -89,6 +90,9 @@ func Verify(buf []byte, fileName, allowedTypesStr string) error {

// AddUploadContext renders template values for dropzone
func AddUploadContext(ctx *context.Context, uploadType string) {
PageIsDiff := ctx.Data["PageIsDiff"]
ctx.Data["CommitSHA"] = ctx.PathParam("sha")
CommitSHA := ctx.PathParam("sha")
switch uploadType {
case "release":
ctx.Data["UploadUrl"] = ctx.Repo.RepoLink + "/releases/attachments"
Expand All @@ -98,8 +102,14 @@ func AddUploadContext(ctx *context.Context, uploadType string) {
ctx.Data["UploadMaxFiles"] = setting.Attachment.MaxFiles
ctx.Data["UploadMaxSize"] = setting.Attachment.MaxSize
case "comment":
ctx.Data["UploadUrl"] = ctx.Repo.RepoLink + "/issues/attachments"
ctx.Data["UploadRemoveUrl"] = ctx.Repo.RepoLink + "/issues/attachments/remove"
if PageIsDiff == true {
ctx.Data["UploadUrl"] = fmt.Sprintf("%s/commit/%s/attachments", ctx.Repo.RepoLink, CommitSHA)
ctx.Data["UploadRemoveUrl"] = fmt.Sprintf("%s/commit/%s/comments/attachments/remove", ctx.Repo.RepoLink, CommitSHA)
} else {
ctx.Data["UploadUrl"] = ctx.Repo.RepoLink + "/issues/attachments"
ctx.Data["UploadRemoveUrl"] = ctx.Repo.RepoLink + "/issues/attachments/remove"
}

if len(ctx.PathParam("index")) > 0 {
ctx.Data["UploadLinkUrl"] = ctx.Repo.RepoLink + "/issues/" + url.PathEscape(ctx.PathParam("index")) + "/attachments"
} else {
Expand Down
64 changes: 64 additions & 0 deletions services/git/commit.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ package git

import (
"context"
"sort"

asymkey_model "code.gitea.io/gitea/models/asymkey"
"code.gitea.io/gitea/models/db"
Expand All @@ -14,6 +15,7 @@ import (
"code.gitea.io/gitea/modules/container"
"code.gitea.io/gitea/modules/git"
asymkey_service "code.gitea.io/gitea/services/asymkey"
"code.gitea.io/gitea/services/gitdiff"
)

// ParseCommitsWithSignature checks if signaute of commits are corresponding to users gpg keys.
Expand Down Expand Up @@ -88,3 +90,65 @@ func ParseCommitsWithStatus(ctx context.Context, oldCommits []*asymkey_model.Sig
}
return newCommits, nil
}

func CreateCommitComment(ctx context.Context, doer *user_model.User, gitRepo *git.Repository, opts git_model.CreateCommitCommentOptions) (*git_model.CommitComment, error) {
comment, err := git_model.CreateCommitComment(ctx, &opts)
if err != nil {
return nil, err
}
return comment, nil
}

// LoadComments loads comments into each line
func LoadCommitComments(ctx context.Context, diff *gitdiff.Diff, commitComment *git_model.CommitComment, currentUser *user_model.User) error {
opts := git_model.FindCommitCommentOptions{
CommitSHA: commitComment.CommitSHA,
}

commitComments, err := git_model.FindCommitCommentsByCommit(ctx, &opts, commitComment)
if err != nil {
return err
}

for _, file := range diff.Files {
for _, cc := range commitComments {
if cc.FileName == file.Name {
for _, section := range file.Sections {
for _, line := range section.Lines {
if cc.Line == int64(line.LeftIdx*-1) {
line.CommitComments = append(line.CommitComments, cc)
cc.Repo = commitComment.Repo
cc.Poster = commitComment.Poster
}
if cc.Line == int64(line.RightIdx) {
line.CommitComments = append(line.CommitComments, cc)
cc.Repo = commitComment.Repo
cc.Poster = commitComment.Poster
}
sort.SliceStable(line.CommitComments, func(i, j int) bool {
return line.CommitComments[i].CreatedUnix < line.CommitComments[j].CreatedUnix
})
}
}
}
}
}
return nil
}

// CreateCommentReaction creates a reaction on a comment.
func CreateCommentReaction(ctx context.Context, doer *user_model.User, commitComment *git_model.CommitComment, reaction string) error {
err := git_model.CreateCommitCommentReaction(ctx, reaction, doer.ID, commitComment)
if err != nil {
return err
}
return nil
}

func DeleteCommentReaction(ctx context.Context, doer *user_model.User, commitComment *git_model.CommitComment, reaction string) error {
err := git_model.DeleteCommentReaction(ctx, reaction, doer.ID, commitComment)
if err != nil {
return err
}
return nil
}
28 changes: 21 additions & 7 deletions services/gitdiff/gitdiff.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,13 +77,14 @@ const (

// DiffLine represents a line difference in a DiffSection.
type DiffLine struct {
LeftIdx int // line number, 1-based
RightIdx int // line number, 1-based
Match int // the diff matched index. -1: no match. 0: plain and no need to match. >0: for add/del, "Lines" slice index of the other side
Type DiffLineType
Content string
Comments issues_model.CommentList // related PR code comments
SectionInfo *DiffLineSectionInfo
LeftIdx int // line number, 1-based
RightIdx int // line number, 1-based
Match int // the diff matched index. -1: no match. 0: plain and no need to match. >0: for add/del, "Lines" slice index of the other side
Type DiffLineType
Content string
Comments issues_model.CommentList // related PR code commzents
CommitComments git_model.CommitCommentList // related to commit comments
SectionInfo *DiffLineSectionInfo
}

// DiffLineSectionInfo represents diff line section meta data
Expand Down Expand Up @@ -133,6 +134,11 @@ func (d *DiffLine) CanComment() bool {
return len(d.Comments) == 0 && d.Type != DiffLineSection
}

// CanCommitComment returns whether a line can get commented
func (d *DiffLine) CanCommitComment() bool {
return len(d.CommitComments) == 0 && d.Type != DiffLineSection
}

// GetCommentSide returns the comment side of the first comment, if not set returns empty string
func (d *DiffLine) GetCommentSide() string {
if len(d.Comments) == 0 {
Expand All @@ -141,6 +147,14 @@ func (d *DiffLine) GetCommentSide() string {
return d.Comments[0].DiffSide()
}

// GetCommentSide returns the comment side of the first comment, if not set returns empty string
func (d *DiffLine) GetCommitCommentSide() string {
if len(d.CommitComments) == 0 {
return ""
}
return d.CommitComments[0].DiffSide()
}

// GetLineTypeMarker returns the line type marker
func (d *DiffLine) GetLineTypeMarker() string {
if strings.IndexByte(" +-", d.Content[0]) > -1 {
Expand Down
8 changes: 7 additions & 1 deletion templates/repo/diff/box.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,13 @@
{{end}}
</div>
{{else}}
<table class="chroma" data-new-comment-url="{{$.Issue.Link}}/files/reviews/new_comment" data-path="{{$file.Name}}">
{{- $newCommentUrl := "" -}}
{{if $.PageIsPullFiles}}
{{$newCommentUrl = printf "%s/files/reviews/new_comment" $.Issue.Link -}}
{{else if $.PageIsDiff}}
{{$newCommentUrl = printf "%s/commit/%s/comments/new_comment" $.RepoLink $.CommitID -}}
{{end}}
<table class="chroma" data-new-comment-url="{{$newCommentUrl}}" data-path="{{$file.Name}}">
{{if $.IsSplitStyle}}
{{template "repo/diff/section_split" dict "file" . "root" $}}
{{else}}
Expand Down
25 changes: 20 additions & 5 deletions templates/repo/diff/comment_form.tmpl
Original file line number Diff line number Diff line change
@@ -1,7 +1,18 @@
{{if and $.root.SignedUserID (not $.Repository.IsArchived)}}
<form class="ui form {{if $.hidden}}tw-hidden comment-form{{end}}" action="{{$.root.Issue.Link}}/files/reviews/comments" method="post">
{{- $action := "" -}}
{{- $origin := "" -}}
{{if $.root.PageIsPullFiles}}
{{$action = printf "%s/files/reviews/comments" $.root.Issue.Link -}}
{{$origin = "diff"}}
{{else if $.root.PageIsDiff}}
{{$action = printf "%s/commit/%s/comments/add" $.root.RepoLink $.root.CommitSHA -}}
{{$origin = "diff"}}
{{else}}
{{$origin = "timeline"}}
{{end}}
<form class="ui form {{if $.hidden}}tw-hidden comment-form{{end}}" action="{{$action}}" method="post">
{{$.root.CsrfTokenHtml}}
<input type="hidden" name="origin" value="{{if $.root.PageIsPullFiles}}diff{{else}}timeline{{end}}">
<input type="hidden" name="origin" value="{{$origin}}">
<input type="hidden" name="latest_commit_id" value="{{$.root.AfterCommitID}}">
<input type="hidden" name="side" value="{{if $.Side}}{{$.Side}}{{end}}">
<input type="hidden" name="line" value="{{if $.Line}}{{$.Line}}{{end}}">
Expand Down Expand Up @@ -36,12 +47,16 @@
{{if $.root.CurrentReview}}
<button name="pending_review" type="submit" class="ui submit primary tiny button btn-add-comment">{{ctx.Locale.Tr "repo.diff.comment.add_review_comment"}}</button>
{{else}}
<button name="pending_review" type="submit" class="ui submit primary tiny button btn-start-review">{{ctx.Locale.Tr "repo.diff.comment.start_review"}}</button>
<button name="single_review" value="true" type="submit" class="ui submit tiny basic button btn-add-single">{{ctx.Locale.Tr "repo.diff.comment.add_single_comment"}}</button>
{{if $.root.PageIsDiff}}
<button type="submit" name="type" value="comment" class="ui submit tiny basic button btn-submit">{{ctx.Locale.Tr "repo.diff.review.comment"}}</button>
{{else}}
<button name="pending_review" type="submit" class="ui submit primary tiny button btn-start-review">{{ctx.Locale.Tr "repo.diff.comment.start_review"}}</button>
<button name="single_review" value="true" type="submit" class="ui submit tiny basic button btn-add-single">{{ctx.Locale.Tr "repo.diff.comment.add_single_comment"}}</button>
{{end}}
{{end}}
{{end}}
{{if or (not $.HasComments) $.hidden}}
<button type="button" class="ui submit tiny basic button btn-cancel cancel-code-comment">{{ctx.Locale.Tr "cancel"}}</button>
<button type="button" data-global-click="onCancelCodeCommentButtonClick" class="ui submit tiny basic button btn-cancel cancel-code-comment">{{ctx.Locale.Tr "cancel"}}</button>
{{end}}
</div>
</div>
Expand Down
43 changes: 43 additions & 0 deletions templates/repo/diff/commit_comments.tmpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
{{range .comments}}
{{$createdStr:= DateUtils.TimeSince .CreatedUnix}}
<div class="comment" id="{{.HashTag}}">
<div class="tw-mt-2 tw-mr-4">
{{template "shared/user/avatarlink" dict "user" .Poster}}
</div>
<div class="content comment-container">
<div class="comment-header avatar-content-left-arrow">
<div class="comment-header-left">
<span class="text grey muted-links">
{{template "shared/user/namelink" .Poster}}
{{ctx.Locale.Tr "repo.issues.commented_at" .HashTag $createdStr}}
</span>
</div>
<div class="comment-header-right">
{{if not $.root.Repository.IsArchived}}
{{template "repo/issue/view_content/add_reaction" dict "ActionURL" (printf "%s/commit/%s/comments/%d/reactions" $.root.RepoLink .CommitSHA .ID)}}
{{end}}
{{template "repo/issue/view_content/context_menu" dict "item" . "delete" true "issue" false "diff" true "IsCommentPoster" (and $.root.IsSigned (eq $.root.SignedUserID .PosterID))}}
</div>
</div>
<div class="ui attached segment comment-body">
<div class="render-content markup" {{if or $.Permission.IsAdmin $.HasIssuesOrPullsWritePermission (and $.root.IsSigned (eq $.root.SignedUserID .PosterID))}}data-can-edit="true"{{end}}>
{{if .RenderedComment}}
{{.RenderedComment}}
{{else}}
<span class="no-content">{{ctx.Locale.Tr "repo.issues.no_content"}}</span>
{{end}}
</div>
<div id="issuecomment-{{.ID}}-raw" class="raw-content tw-hidden">{{.Comment}}</div>
<div class="edit-content-zone tw-hidden" data-update-url="{{(printf "%s/commit/%s/comments/%d/update" $.root.RepoLink .CommitSHA .ID)}}" data-content-version="{{.ContentVersion}}" data-context="{{$.root.RepoLink}}" data-attachment-url="{{(printf "%s/commit/%s/comments/%d/attachments" $.root.RepoLink .CommitSHA .ID)}}"></div>
{{$attachments := .GroupAttachmentsByUUID}}
{{if $attachments}}
{{template "repo/issue/view_content/commit_attachments" dict "CommitComment" . "Attachments" $attachments "RenderedContent" .RenderedComment}}
{{end}}
</div>
{{$reactions := .GroupReactionsByType}}
{{if $reactions}}
{{template "repo/issue/view_content/commit_reactions" dict "CommitComment" . "ActionURL" (printf "%s/commit/%s/comments/%d/reactions" $.root.RepoLink .CommitSHA .ID) "Reactions" $reactions}}
{{end}}
</div>
</div>
{{end}}
30 changes: 30 additions & 0 deletions templates/repo/diff/commit_conversation.tmpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
{{if len .comments}}
{{$comment := index .comments 0}}
<div class="conversation-holder" data-path="{{$comment.FileName}}" data-side="{{if lt $comment.Line 0}}left{{else}}right{{end}}" data-idx="{{$comment.Line}}">
<div id="code-comments-{{$comment.ID}}" class="field comment-code-cloud">
<div class="comment-list">
<div class="ui comments">
{{template "repo/diff/commit_comments" dict "root" $ "comments" .comments}}
</div>
</div>
<div class="flex-text-block tw-mt-2 tw-flex-wrap tw-justify-end">
<div class="ui buttons">
<button class="ui icon tiny basic button previous-conversation">
{{svg "octicon-arrow-up" 12}} {{ctx.Locale.Tr "repo.issues.previous"}}
</button>
<button class="ui icon tiny basic button next-conversation">
{{svg "octicon-arrow-down" 12}} {{ctx.Locale.Tr "repo.issues.next"}}
</button>
</div>
{{if and $.SignedUserID (not $.Repository.IsArchived)}}
<button class="comment-form-reply ui primary icon tiny button">
{{svg "octicon-reply" 12}}{{ctx.Locale.Tr "repo.diff.comment.reply"}}
</button>
{{end}}
</div>
{{template "repo/diff/comment_form_datahandler" dict "hidden" true "reply" $comment.ID "root" $ "comment" $comment}}
</div>
</div>
{{else}}
{{template "repo/diff/conversation_outdated"}}
{{end}}
Loading