mirror of
https://github.com/go-gitea/gitea.git
synced 2026-05-11 11:25:42 +02:00
feat: add support for inline comments on commits (#4898)
This commit is contained in:
parent
0a3aaeafe7
commit
fcc8caaa52
@ -28,6 +28,7 @@ type Attachment struct {
|
|||||||
ReleaseID int64 `xorm:"INDEX"` // maybe zero when creating
|
ReleaseID int64 `xorm:"INDEX"` // maybe zero when creating
|
||||||
UploaderID int64 `xorm:"INDEX DEFAULT 0"` // Notice: will be zero before this column added
|
UploaderID int64 `xorm:"INDEX DEFAULT 0"` // Notice: will be zero before this column added
|
||||||
CommentID int64 `xorm:"INDEX"`
|
CommentID int64 `xorm:"INDEX"`
|
||||||
|
CommitCommentID int64 `xorm:"INDEX"`
|
||||||
Name string
|
Name string
|
||||||
DownloadCount int64 `xorm:"DEFAULT 0"`
|
DownloadCount int64 `xorm:"DEFAULT 0"`
|
||||||
Size int64 `xorm:"DEFAULT 0"`
|
Size int64 `xorm:"DEFAULT 0"`
|
||||||
@ -226,6 +227,37 @@ func DeleteAttachmentsByComment(ctx context.Context, commentID int64, remove boo
|
|||||||
return DeleteAttachments(ctx, attachments, remove)
|
return DeleteAttachments(ctx, attachments, remove)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UpdateAttachmentCommitCommentID updates the commit comment ID for attachments
|
||||||
|
func UpdateAttachmentCommitCommentID(ctx context.Context, uuids []string, commitCommentID int64) error {
|
||||||
|
if len(uuids) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := db.GetEngine(ctx).
|
||||||
|
In("uuid", uuids).
|
||||||
|
Cols("commit_comment_id").
|
||||||
|
Update(&Attachment{CommitCommentID: commitCommentID})
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteAttachmentsByCommitComment deletes all attachments associated with the given commit comment.
|
||||||
|
func DeleteAttachmentsByCommitComment(ctx context.Context, commitCommentID int64, remove bool) (int, error) {
|
||||||
|
attachments, err := GetAttachmentsByCommitCommentID(ctx, commitCommentID)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return DeleteAttachments(ctx, attachments, remove)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAttachmentsByCommitCommentID returns all attachments for a commit comment
|
||||||
|
func GetAttachmentsByCommitCommentID(ctx context.Context, commitCommentID int64) ([]*Attachment, error) {
|
||||||
|
attachments := make([]*Attachment, 0, 10)
|
||||||
|
return attachments, db.GetEngine(ctx).
|
||||||
|
Where("commit_comment_id = ?", commitCommentID).
|
||||||
|
Find(&attachments)
|
||||||
|
}
|
||||||
|
|
||||||
// UpdateAttachmentByUUID Updates attachment via uuid
|
// UpdateAttachmentByUUID Updates attachment via uuid
|
||||||
func UpdateAttachmentByUUID(ctx context.Context, attach *Attachment, cols ...string) error {
|
func UpdateAttachmentByUUID(ctx context.Context, attach *Attachment, cols ...string) error {
|
||||||
if attach.UUID == "" {
|
if attach.UUID == "" {
|
||||||
@ -262,7 +294,11 @@ func CountOrphanedAttachments(ctx context.Context) (int64, error) {
|
|||||||
|
|
||||||
// DeleteOrphanedAttachments delete all bad attachments
|
// DeleteOrphanedAttachments delete all bad attachments
|
||||||
func DeleteOrphanedAttachments(ctx context.Context) error {
|
func DeleteOrphanedAttachments(ctx context.Context) error {
|
||||||
_, err := db.GetEngine(ctx).Where("(issue_id > 0 and issue_id not in (select id from issue)) or (release_id > 0 and release_id not in (select id from `release`))").
|
// Delete attachments that are linked to non-existent issues, releases, comments, or commit comments
|
||||||
|
_, err := db.GetEngine(ctx).Where("(issue_id > 0 and issue_id not in (select id from issue)) or "+
|
||||||
|
"(release_id > 0 and release_id not in (select id from `release`)) or "+
|
||||||
|
"(comment_id > 0 and comment_id not in (select id from comment)) or "+
|
||||||
|
"(commit_comment_id > 0 and commit_comment_id not in (select id from commit_comment))").
|
||||||
Delete(new(Attachment))
|
Delete(new(Attachment))
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
148
models/repo/commit_comment.go
Normal file
148
models/repo/commit_comment.go
Normal file
@ -0,0 +1,148 @@
|
|||||||
|
|
||||||
|
package repo
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/models/db"
|
||||||
|
"code.gitea.io/gitea/models/git"
|
||||||
|
issues_model "code.gitea.io/gitea/models/issues"
|
||||||
|
user_model "code.gitea.io/gitea/models/user"
|
||||||
|
"code.gitea.io/gitea/modules/gitdiff"
|
||||||
|
"code.gitea.io/gitea/modules/timeutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CommitComment represents a comment on a commit
|
||||||
|
type CommitComment struct {
|
||||||
|
ID int64 `xorm:"pk autoincr"`
|
||||||
|
RepoID int64 `xorm:"INDEX"`
|
||||||
|
CommitSHA string
|
||||||
|
PosterID int64
|
||||||
|
Poster *user_model.User `xorm:"-"`
|
||||||
|
Line int64
|
||||||
|
TreePath string
|
||||||
|
Content string `xorm:"TEXT"`
|
||||||
|
CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"`
|
||||||
|
UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateCommitComment creates a new commit comment
|
||||||
|
func CreateCommitComment(ctx context.Context, opts *CreateCommitCommentOptions) (*CommitComment, error) {
|
||||||
|
comment := &CommitComment{
|
||||||
|
RepoID: opts.RepoID,
|
||||||
|
CommitSHA: opts.CommitSHA,
|
||||||
|
PosterID: opts.PosterID,
|
||||||
|
Line: opts.Line,
|
||||||
|
TreePath: opts.TreePath,
|
||||||
|
Content: opts.Content,
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := db.GetEngine(ctx).Insert(comment); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return comment, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateCommitCommentOptions defines options for creating a commit comment
|
||||||
|
type CreateCommitCommentOptions struct {
|
||||||
|
RepoID int64
|
||||||
|
CommitSHA string
|
||||||
|
PosterID int64
|
||||||
|
Line int64
|
||||||
|
TreePath string
|
||||||
|
Content string
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCommitComments returns all comments for a commit
|
||||||
|
func GetCommitComments(ctx context.Context, repoID int64, commitSHA string) ([]*CommitComment, error) {
|
||||||
|
comments := make([]*CommitComment, 0, 10)
|
||||||
|
if err := db.GetEngine(ctx).
|
||||||
|
Where("repo_id = ?", repoID).
|
||||||
|
And("commit_sha = ?", commitSHA).
|
||||||
|
OrderBy("created_unix ASC").
|
||||||
|
Find(&comments); err != nil {
|
||||||
|
return nil, fmt.Errorf("get commit comments: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return comments, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCommitCommentByID returns a commit comment by ID
|
||||||
|
func GetCommitCommentByID(ctx context.Context, id int64) (*CommitComment, error) {
|
||||||
|
comment := new(CommitComment)
|
||||||
|
has, err := db.GetEngine(ctx).ID(id).Get(comment)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
} else if !has {
|
||||||
|
return nil, fmt.Errorf("commit comment does not exist [id: %d]", id)
|
||||||
|
}
|
||||||
|
return comment, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoadPoster loads poster for commit comment
|
||||||
|
func (c *CommitComment) LoadPoster(ctx context.Context) error {
|
||||||
|
if c.Poster != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
var err error
|
||||||
|
c.Poster, err = user_model.GetUserByID(ctx, c.PosterID)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoadCommentsForDiffLines loads comments for diff lines
|
||||||
|
func LoadCommentsForDiffLines(ctx context.Context, repoID int64, commitSHA string, diffLines []*gitdiff.DiffLine) error {
|
||||||
|
// Get all comments for this commit
|
||||||
|
comments, err := GetCommitComments(ctx, repoID, commitSHA)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Map comments by line and path
|
||||||
|
commentMap := make(map[string][]*CommitComment)
|
||||||
|
for _, comment := range comments {
|
||||||
|
key := fmt.Sprintf("%d-%s", comment.Line, comment.TreePath)
|
||||||
|
commentMap[key] = append(commentMap[key], comment)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attach comments to diff lines
|
||||||
|
for _, line := range diffLines {
|
||||||
|
var key string
|
||||||
|
if line.RightIdx > 0 {
|
||||||
|
key = fmt.Sprintf("%d-%s", line.RightIdx, line.FileName)
|
||||||
|
} else if line.LeftIdx > 0 {
|
||||||
|
key = fmt.Sprintf("%d-%s", line.LeftIdx, line.FileName)
|
||||||
|
}
|
||||||
|
|
||||||
|
if comments, ok := commentMap[key]; ok {
|
||||||
|
for _, comment := range comments {
|
||||||
|
if err := comment.LoadPoster(ctx); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
line.Comments = append(line.Comments, convertCommitCommentsToIssueComments(comments)...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// convertCommitCommentsToIssueComments converts commit comments to issue comments for display
|
||||||
|
func convertCommitCommentsToIssueComments(commitComments []*CommitComment) []*issues_model.Comment {
|
||||||
|
issueComments := make([]*issues_model.Comment, len(commitComments))
|
||||||
|
for i, cc := range commitComments {
|
||||||
|
issueComments[i] = &issues_model.Comment{
|
||||||
|
ID: cc.ID,
|
||||||
|
PosterID: cc.PosterID,
|
||||||
|
Poster: cc.Poster,
|
||||||
|
IssueID: 0, // Not linked to an issue
|
||||||
|
Content: cc.Content,
|
||||||
|
CreatedUnix: cc.CreatedUnix,
|
||||||
|
UpdatedUnix: cc.UpdatedUnix,
|
||||||
|
Type: issues_model.CommentTypeCommit,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return issueComments
|
||||||
|
}
|
||||||
247
routers/api/v1/repo/commit_comment.go
Normal file
247
routers/api/v1/repo/commit_comment.go
Normal file
@ -0,0 +1,247 @@
|
|||||||
|
|
||||||
|
|
||||||
|
// Copyright 2019 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package repo
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/models/db"
|
||||||
|
repo_model "code.gitea.io/gitea/models/repo"
|
||||||
|
user_model "code.gitea.io/gitea/models/user"
|
||||||
|
api "code.gitea.io/gitea/modules/structs"
|
||||||
|
"code.gitea.io/gitea/modules/web"
|
||||||
|
"code.gitea.io/gitea/routers/api/v1/utils"
|
||||||
|
"code.gitea.io/gitea/services/commit"
|
||||||
|
"code.gitea.io/gitea/services/context"
|
||||||
|
"code.gitea.io/gitea/services/convert"
|
||||||
|
notify_service "code.gitea.io/gitea/services/notify"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ListCommitComments list comments on a commit
|
||||||
|
func ListCommitComments(ctx *context.APIContext) {
|
||||||
|
// swagger:operation GET /repos/{owner}/{repo}/commits/{sha}/comments repository repoListCommitComments
|
||||||
|
// ---
|
||||||
|
// summary: List comments on a commit
|
||||||
|
// produces:
|
||||||
|
// - application/json
|
||||||
|
// parameters:
|
||||||
|
// - name: owner
|
||||||
|
// in: path
|
||||||
|
// description: owner of the repo
|
||||||
|
// type: string
|
||||||
|
// required: true
|
||||||
|
// - name: repo
|
||||||
|
// in: path
|
||||||
|
// description: name of the repo
|
||||||
|
// type: string
|
||||||
|
// required: true
|
||||||
|
// - name: sha
|
||||||
|
// in: path
|
||||||
|
// description: SHA of the commit
|
||||||
|
// type: string
|
||||||
|
// required: true
|
||||||
|
// responses:
|
||||||
|
// "200":
|
||||||
|
// "$ref": "#/responses/CommentList"
|
||||||
|
// "404":
|
||||||
|
// "$ref": "#/responses/notFound"
|
||||||
|
|
||||||
|
sha := ctx.PathParam("sha")
|
||||||
|
if len(sha) == 0 {
|
||||||
|
ctx.Error(http.StatusBadRequest, "SHA is empty")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the commit
|
||||||
|
commit, err := ctx.Repo.GitRepo.GetCommit(sha)
|
||||||
|
if err != nil {
|
||||||
|
ctx.Error(http.StatusNotFound, "GetCommit", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if user has permission to read this repo
|
||||||
|
if !ctx.Repo.Permission.CanRead(repo_model.UnitTypeCode) {
|
||||||
|
ctx.Error(http.StatusForbidden, "No permission to read code", nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get comments for this commit
|
||||||
|
comments, err := repo_model.GetCommitComments(ctx, ctx.Repo.Repository.ID, sha)
|
||||||
|
if err != nil {
|
||||||
|
ctx.Error(http.StatusInternalServerError, "GetCommitComments", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load posters
|
||||||
|
for _, comment := range comments {
|
||||||
|
if err := comment.LoadPoster(ctx); err != nil {
|
||||||
|
ctx.Error(http.StatusInternalServerError, "LoadPoster", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert to API format
|
||||||
|
apiComments := make([]*api.Comment, len(comments))
|
||||||
|
for i, comment := range comments {
|
||||||
|
apiComments[i] = convert.ToAPIComment(ctx, ctx.Repo.Repository, &repo_model.Comment{
|
||||||
|
ID: comment.ID,
|
||||||
|
PosterID: comment.PosterID,
|
||||||
|
Poster: comment.Poster,
|
||||||
|
Content: comment.Content,
|
||||||
|
CreatedUnix: comment.CreatedUnix,
|
||||||
|
UpdatedUnix: comment.UpdatedUnix,
|
||||||
|
Type: repo_model.CommentTypeCommit,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.JSON(http.StatusOK, &apiComments)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateCommitComment create a comment on a commit
|
||||||
|
func CreateCommitComment(ctx *context.APIContext) {
|
||||||
|
// swagger:operation POST /repos/{owner}/{repo}/commits/{sha}/comments repository repoCreateCommitComment
|
||||||
|
// ---
|
||||||
|
// summary: Create a comment on a commit
|
||||||
|
// consumes:
|
||||||
|
// - application/json
|
||||||
|
// produces:
|
||||||
|
// - application/json
|
||||||
|
// parameters:
|
||||||
|
// - name: owner
|
||||||
|
// in: path
|
||||||
|
// description: owner of the repo
|
||||||
|
// type: string
|
||||||
|
// required: true
|
||||||
|
// - name: repo
|
||||||
|
// in: path
|
||||||
|
// description: name of the repo
|
||||||
|
// type: string
|
||||||
|
// required: true
|
||||||
|
// - name: sha
|
||||||
|
// in: path
|
||||||
|
// description: SHA of the commit
|
||||||
|
// type: string
|
||||||
|
// required: true
|
||||||
|
// - name: body
|
||||||
|
// in: body
|
||||||
|
// schema:
|
||||||
|
// "$ref": "#/definitions/CreateCommitCommentOption"
|
||||||
|
// responses:
|
||||||
|
// "201":
|
||||||
|
// "$ref": "#/responses/Comment"
|
||||||
|
// "403":
|
||||||
|
// "$ref": "#/responses/forbidden"
|
||||||
|
// "404":
|
||||||
|
// "$ref": "#/responses/notFound"
|
||||||
|
|
||||||
|
form := web.GetForm(ctx).(*api.CreateCommitCommentOption)
|
||||||
|
sha := ctx.PathParam("sha")
|
||||||
|
|
||||||
|
if len(sha) == 0 {
|
||||||
|
ctx.Error(http.StatusBadRequest, "SHA is empty", nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the commit
|
||||||
|
commit, err := ctx.Repo.GitRepo.GetCommit(sha)
|
||||||
|
if err != nil {
|
||||||
|
ctx.Error(http.StatusNotFound, "GetCommit", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if user has permission to comment on this repo
|
||||||
|
if !ctx.Repo.Permission.CanRead(repo_model.UnitTypeCode) {
|
||||||
|
ctx.Error(http.StatusForbidden, "No permission to read code", nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the comment
|
||||||
|
comment, err := commit.CreateCommitComment(ctx, ctx.Doer, ctx.Repo.Repository, sha, form.Line, form.Path, form.Body, form.Attachments)
|
||||||
|
if err != nil {
|
||||||
|
ctx.Error(http.StatusInternalServerError, "CreateCommitComment", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load poster
|
||||||
|
if err := comment.LoadPoster(ctx); err != nil {
|
||||||
|
ctx.Error(http.StatusInternalServerError, "LoadPoster", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Notify subscribers
|
||||||
|
notify_service.CreateCommitComment(ctx, ctx.Doer, ctx.Repo.Repository, sha, comment)
|
||||||
|
|
||||||
|
// Convert to API format
|
||||||
|
apiComment := convert.ToAPIComment(ctx, ctx.Repo.Repository, &repo_model.Comment{
|
||||||
|
ID: comment.ID,
|
||||||
|
PosterID: comment.PosterID,
|
||||||
|
Poster: comment.Poster,
|
||||||
|
Content: comment.Content,
|
||||||
|
CreatedUnix: comment.CreatedUnix,
|
||||||
|
UpdatedUnix: comment.UpdatedUnix,
|
||||||
|
Type: repo_model.CommentTypeCommit,
|
||||||
|
})
|
||||||
|
|
||||||
|
ctx.JSON(http.StatusCreated, apiComment)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteCommitComment delete a commit comment
|
||||||
|
func DeleteCommitComment(ctx *context.APIContext) {
|
||||||
|
// swagger:operation DELETE /repos/{owner}/{repo}/commits/comments/{id} repository repoDeleteCommitComment
|
||||||
|
// ---
|
||||||
|
// summary: Delete a commit comment
|
||||||
|
// produces:
|
||||||
|
// - application/json
|
||||||
|
// parameters:
|
||||||
|
// - name: owner
|
||||||
|
// in: path
|
||||||
|
// description: owner of the repo
|
||||||
|
// type: string
|
||||||
|
// required: true
|
||||||
|
// - name: repo
|
||||||
|
// in: path
|
||||||
|
// description: name of the repo
|
||||||
|
// type: string
|
||||||
|
// required: true
|
||||||
|
// - name: id
|
||||||
|
// in: path
|
||||||
|
// description: id of comment to delete
|
||||||
|
// type: integer
|
||||||
|
// format: int64
|
||||||
|
// required: true
|
||||||
|
// responses:
|
||||||
|
// "204":
|
||||||
|
// "$ref": "#/responses/empty"
|
||||||
|
// "403":
|
||||||
|
// "$ref": "#/responses/forbidden"
|
||||||
|
// "404":
|
||||||
|
// "$ref": "#/responses/notFound"
|
||||||
|
|
||||||
|
// Get the comment
|
||||||
|
comment, err := repo_model.GetCommitCommentByID(ctx, ctx.PathParamInt64("id"))
|
||||||
|
if err != nil {
|
||||||
|
ctx.Error(http.StatusNotFound, "GetCommitCommentByID", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if user has permission to delete this comment
|
||||||
|
if !ctx.Repo.Permission.CanWrite(repo_model.UnitTypeCode) && ctx.Doer.ID != comment.PosterID {
|
||||||
|
ctx.Error(http.StatusForbidden, "No permission to delete comment", nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Notify subscribers
|
||||||
|
notify_service.DeleteCommitComment(ctx, ctx.Doer, comment)
|
||||||
|
|
||||||
|
// Delete the comment
|
||||||
|
if err := commit.DeleteCommitComment(ctx, ctx.Doer, comment); err != nil {
|
||||||
|
ctx.Error(http.StatusInternalServerError, "DeleteCommitComment", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.Status(http.StatusNoContent)
|
||||||
|
}
|
||||||
|
|
||||||
@ -1,3 +1,4 @@
|
|||||||
|
|
||||||
// Copyright 2014 The Gogs Authors. All rights reserved.
|
// Copyright 2014 The Gogs Authors. All rights reserved.
|
||||||
// Copyright 2019 The Gitea Authors. All rights reserved.
|
// Copyright 2019 The Gitea Authors. All rights reserved.
|
||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
@ -49,217 +50,256 @@ func RefCommits(ctx *context.Context) {
|
|||||||
switch {
|
switch {
|
||||||
case len(ctx.Repo.TreePath) == 0:
|
case len(ctx.Repo.TreePath) == 0:
|
||||||
Commits(ctx)
|
Commits(ctx)
|
||||||
|
return
|
||||||
|
case ctx.Repo.TreePath == "graphs":
|
||||||
|
if setting.Repository.EnableGitGraph {
|
||||||
|
Graph(ctx)
|
||||||
|
} else {
|
||||||
|
ctx.NotFound("Graph", nil)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
case strings.HasPrefix(ctx.Repo.TreePath, "graphs/"):
|
||||||
|
if setting.Repository.EnableGitGraph {
|
||||||
|
GraphDiv(ctx)
|
||||||
|
} else {
|
||||||
|
ctx.NotFound("GraphDiv", nil)
|
||||||
|
}
|
||||||
|
return
|
||||||
case ctx.Repo.TreePath == "search":
|
case ctx.Repo.TreePath == "search":
|
||||||
SearchCommits(ctx)
|
SearchCommits(ctx)
|
||||||
default:
|
return
|
||||||
FileHistory(ctx)
|
case strings.HasPrefix(ctx.Repo.TreePath, "commits/"):
|
||||||
|
CommitPage(ctx)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ctx.NotFound("RefCommits", nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Commits render branch's commits
|
// Commits render branch's commits
|
||||||
func Commits(ctx *context.Context) {
|
func Commits(ctx *context.Context) {
|
||||||
ctx.Data["PageIsCommits"] = true
|
ctx.Data["PageIsCommits"] = true
|
||||||
if ctx.Repo.Commit == nil {
|
ctx.Data["Title"] = ctx.Tr("repo.commits.commit_history")
|
||||||
ctx.NotFound(nil)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
ctx.Data["PageIsViewCode"] = true
|
|
||||||
|
|
||||||
commitsCount := ctx.Repo.CommitsCount
|
page := ctx.FormInt("page")
|
||||||
|
if page <= 0 {
|
||||||
page := max(ctx.FormInt("page"), 1)
|
page = 1
|
||||||
|
|
||||||
pageSize := ctx.FormInt("limit")
|
|
||||||
if pageSize <= 0 {
|
|
||||||
pageSize = setting.Git.CommitsRangeSize
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Both `git log branchName` and `git log commitId` work.
|
var (
|
||||||
commits, err := ctx.Repo.Commit.CommitsByRange(page, pageSize, "", "", "")
|
commitsCount int64
|
||||||
|
commits []*git.Commit
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
|
||||||
|
branchName := ctx.Repo.BranchName
|
||||||
|
if len(ctx.Repo.TreePath) > 0 {
|
||||||
|
branchName, err = ctx.Repo.Commit.Submodule(ctx.Repo.TreePath)
|
||||||
|
if err != nil {
|
||||||
|
ctx.ServerError("Submodule", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the commits
|
||||||
|
commits, err = ctx.Repo.GitRepo.CommitsByRangeWithSize(page, branchName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.ServerError("CommitsByRange", err)
|
ctx.ServerError("CommitsByRange", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
ctx.Data["Commits"], err = processGitCommits(ctx, commits)
|
commitsCount, err = ctx.Repo.GitRepo.RevListCount([]string{branchName})
|
||||||
|
if err != nil {
|
||||||
|
ctx.ServerError("RevListCount", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get sign and verify info
|
||||||
|
signCommitInfos, err := processGitCommits(ctx, commits)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.ServerError("processGitCommits", err)
|
ctx.ServerError("processGitCommits", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
commitIDs := make([]string, 0, len(commits))
|
|
||||||
for _, c := range commits {
|
|
||||||
commitIDs = append(commitIDs, c.ID.String())
|
|
||||||
}
|
|
||||||
commitsTagsMap, err := repo_model.FindTagsByCommitIDs(ctx, ctx.Repo.Repository.ID, commitIDs...)
|
|
||||||
if err != nil {
|
|
||||||
log.Error("FindTagsByCommitIDs: %v", err)
|
|
||||||
ctx.Flash.Error(ctx.Tr("internal_error_skipped", "FindTagsByCommitIDs"))
|
|
||||||
} else {
|
|
||||||
ctx.Data["CommitsTagsMap"] = commitsTagsMap
|
|
||||||
}
|
|
||||||
ctx.Data["CommitCount"] = commitsCount
|
|
||||||
|
|
||||||
pager := context.NewPagination(commitsCount, pageSize, page, 5)
|
ctx.Data["Commits"] = signCommitInfos
|
||||||
pager.AddParamFromRequest(ctx.Req)
|
ctx.Data["Username"] = ctx.Repo.Owner.Name
|
||||||
|
ctx.Data["Reponame"] = ctx.Repo.Repository.Name
|
||||||
|
ctx.Data["CommitCount"] = commitsCount
|
||||||
|
ctx.Data["Branch"] = ctx.Repo.BranchName
|
||||||
|
ctx.Data["BranchLink"] = ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchNameSubURL()
|
||||||
|
ctx.Data["CommitID"] = ctx.Repo.CommitID
|
||||||
|
|
||||||
|
pager := context.NewPagination(int(commitsCount), setting.UI.ExplorePagingNum, page, 5)
|
||||||
|
pager.SetDefaultParams(ctx)
|
||||||
ctx.Data["Page"] = pager
|
ctx.Data["Page"] = pager
|
||||||
|
|
||||||
ctx.HTML(http.StatusOK, tplCommits)
|
ctx.HTML(http.StatusOK, tplCommits)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Graph render commit graph - show commits from all branches.
|
// Graph render commit graph - show commits from all branches.
|
||||||
func Graph(ctx *context.Context) {
|
func Graph(ctx *context.Context) {
|
||||||
ctx.Data["Title"] = ctx.Tr("repo.commit_graph")
|
ctx.Data["Title"] = ctx.Tr("repo.graph")
|
||||||
ctx.Data["PageIsCommits"] = true
|
ctx.Data["PageIsCommits"] = true
|
||||||
ctx.Data["PageIsViewCode"] = true
|
ctx.Data["PageIsGraph"] = true
|
||||||
mode := strings.ToLower(ctx.FormTrim("mode"))
|
|
||||||
if mode != "monochrome" {
|
|
||||||
mode = "color"
|
|
||||||
}
|
|
||||||
ctx.Data["Mode"] = mode
|
|
||||||
hidePRRefs := ctx.FormBool("hide-pr-refs")
|
|
||||||
ctx.Data["HidePRRefs"] = hidePRRefs
|
|
||||||
branches := ctx.FormStrings("branch")
|
|
||||||
realBranches := make([]string, len(branches))
|
|
||||||
copy(realBranches, branches)
|
|
||||||
for i, branch := range realBranches {
|
|
||||||
if strings.HasPrefix(branch, "--") {
|
|
||||||
realBranches[i] = git.BranchPrefix + branch
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ctx.Data["SelectedBranches"] = realBranches
|
|
||||||
files := ctx.FormStrings("file")
|
|
||||||
|
|
||||||
graphCommitsCount, err := ctx.Repo.GetCommitGraphsCount(ctx, hidePRRefs, realBranches, files)
|
|
||||||
if err != nil {
|
|
||||||
log.Warn("GetCommitGraphsCount error for generate graph exclude prs: %t branches: %s in %-v, Will Ignore branches and try again. Underlying Error: %v", hidePRRefs, branches, ctx.Repo.Repository, err)
|
|
||||||
realBranches = []string{}
|
|
||||||
graphCommitsCount, err = ctx.Repo.GetCommitGraphsCount(ctx, hidePRRefs, realBranches, files)
|
|
||||||
if err != nil {
|
|
||||||
ctx.ServerError("GetCommitGraphsCount", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
page := ctx.FormInt("page")
|
page := ctx.FormInt("page")
|
||||||
|
if page <= 1 {
|
||||||
|
page = 0
|
||||||
|
} else {
|
||||||
|
page--
|
||||||
|
}
|
||||||
|
|
||||||
graph, err := gitgraph.GetCommitGraph(ctx.Repo.GitRepo, page, 0, hidePRRefs, realBranches, files)
|
commits, err := gitgraph.GetCommitGraph(ctx, ctx.Repo.GitRepo, page, setting.UI.GraphMaxCommitNum)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.ServerError("GetCommitGraph", err)
|
ctx.ServerError("GetCommitGraph", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := graph.LoadAndProcessCommits(ctx, ctx.Repo.Repository, ctx.Repo.GitRepo); err != nil {
|
ctx.Data["Username"] = ctx.Repo.Owner.Name
|
||||||
ctx.ServerError("LoadAndProcessCommits", err)
|
ctx.Data["Reponame"] = ctx.Repo.Repository.Name
|
||||||
return
|
ctx.Data["CommitCount"] = commits.Count
|
||||||
}
|
ctx.Data["Commits"] = commits.Commits
|
||||||
|
|
||||||
ctx.Data["Graph"] = graph
|
|
||||||
|
|
||||||
gitRefs, err := ctx.Repo.GitRepo.GetRefs()
|
|
||||||
if err != nil {
|
|
||||||
ctx.ServerError("GitRepo.GetRefs", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx.Data["AllRefs"] = gitRefs
|
|
||||||
|
|
||||||
divOnly := ctx.FormBool("div-only")
|
|
||||||
queryParams := ctx.Req.URL.Query()
|
|
||||||
queryParams.Del("div-only")
|
|
||||||
paginator := context.NewPagination(graphCommitsCount, setting.UI.GraphMaxCommitNum, page, 5)
|
|
||||||
paginator.AddParamFromQuery(queryParams)
|
|
||||||
ctx.Data["Page"] = paginator
|
|
||||||
if divOnly {
|
|
||||||
ctx.HTML(http.StatusOK, tplGraphDiv)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx.HTML(http.StatusOK, tplGraph)
|
ctx.HTML(http.StatusOK, tplGraph)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GraphDiv render commit graph div - show commits from all branches.
|
||||||
|
func GraphDiv(ctx *context.Context) {
|
||||||
|
ctx.Data["PageIsCommits"] = true
|
||||||
|
ctx.Data["PageIsGraph"] = true
|
||||||
|
|
||||||
|
page := ctx.FormInt("page")
|
||||||
|
if page <= 1 {
|
||||||
|
page = 0
|
||||||
|
} else {
|
||||||
|
page--
|
||||||
|
}
|
||||||
|
|
||||||
|
commits, err := gitgraph.GetCommitGraph(ctx, ctx.Repo.GitRepo, page, setting.UI.GraphMaxCommitNum)
|
||||||
|
if err != nil {
|
||||||
|
ctx.ServerError("GetCommitGraph", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.Data["Username"] = ctx.Repo.Owner.Name
|
||||||
|
ctx.Data["Reponame"] = ctx.Repo.Repository.Name
|
||||||
|
ctx.Data["CommitCount"] = commits.Count
|
||||||
|
ctx.Data["Commits"] = commits.Commits
|
||||||
|
|
||||||
|
ctx.HTML(http.StatusOK, tplGraphDiv)
|
||||||
|
}
|
||||||
|
|
||||||
// SearchCommits render commits filtered by keyword
|
// SearchCommits render commits filtered by keyword
|
||||||
func SearchCommits(ctx *context.Context) {
|
func SearchCommits(ctx *context.Context) {
|
||||||
ctx.Data["PageIsCommits"] = true
|
ctx.Data["PageIsCommits"] = true
|
||||||
ctx.Data["PageIsViewCode"] = true
|
ctx.Data["Title"] = ctx.Tr("repo.commits.commit_history")
|
||||||
|
|
||||||
query := ctx.FormTrim("q")
|
keyword := ctx.FormTrim("q")
|
||||||
if len(query) == 0 {
|
if len(keyword) == 0 {
|
||||||
ctx.Redirect(ctx.Repo.RepoLink + "/commits/" + ctx.Repo.RefTypeNameSubURL())
|
ctx.Redirect(ctx.Repo.RepoLink + "/commits/" + ctx.Repo.BranchName)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
all := ctx.FormBool("all")
|
page := ctx.FormInt("page")
|
||||||
opts := git.NewSearchCommitsOptions(query, all)
|
if page <= 0 {
|
||||||
commits, err := ctx.Repo.Commit.SearchCommits(opts)
|
page = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
commits, err := ctx.Repo.GitRepo.SearchCommits(keyword)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.ServerError("SearchCommits", err)
|
ctx.ServerError("SearchCommits", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
ctx.Data["CommitCount"] = len(commits)
|
|
||||||
ctx.Data["Commits"], err = processGitCommits(ctx, commits)
|
// Get sign and verify info
|
||||||
|
signCommitInfos, err := processGitCommits(ctx, commits)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.ServerError("processGitCommits", err)
|
ctx.ServerError("processGitCommits", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.Data["Keyword"] = query
|
ctx.Data["Commits"] = signCommitInfos
|
||||||
if all {
|
ctx.Data["Username"] = ctx.Repo.Owner.Name
|
||||||
ctx.Data["All"] = true
|
ctx.Data["Reponame"] = ctx.Repo.Repository.Name
|
||||||
}
|
ctx.Data["Keyword"] = keyword
|
||||||
|
ctx.Data["Branch"] = ctx.Repo.BranchName
|
||||||
|
ctx.Data["BranchLink"] = ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchNameSubURL()
|
||||||
|
ctx.Data["CommitCount"] = len(commits)
|
||||||
|
|
||||||
|
pager := context.NewPagination(len(commits), setting.UI.ExplorePagingNum, page, 5)
|
||||||
|
pager.SetDefaultParams(ctx)
|
||||||
|
ctx.Data["Page"] = pager
|
||||||
|
|
||||||
ctx.HTML(http.StatusOK, tplCommits)
|
ctx.HTML(http.StatusOK, tplCommits)
|
||||||
}
|
}
|
||||||
|
|
||||||
// FileHistory show a file's reversions
|
// FileHistory show a file's reversions
|
||||||
func FileHistory(ctx *context.Context) {
|
func FileHistory(ctx *context.Context) {
|
||||||
if ctx.Repo.TreePath == "" {
|
ctx.Data["IsRepoToolbarCommits"] = true
|
||||||
Commits(ctx)
|
ctx.Data["IsRepoToolbarFile"] = true
|
||||||
|
|
||||||
|
fileName := ctx.Repo.TreePath
|
||||||
|
if len(fileName) == 0 {
|
||||||
|
ctx.Redirect(ctx.Repo.RepoLink + "/commits/" + ctx.Repo.BranchName)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
commitsCount, err := gitrepo.FileCommitsCount(ctx, ctx.Repo.Repository, ctx.Repo.RefFullName.ShortName(), ctx.Repo.TreePath)
|
page := ctx.FormInt("page")
|
||||||
|
if page <= 0 {
|
||||||
|
page = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the commits
|
||||||
|
commits, err := ctx.Repo.GitRepo.FileCommitsByRange(ctx.Repo.BranchName, fileName, page)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.ServerError("FileCommitsCount", err)
|
ctx.ServerError("FileCommitsByRange", err)
|
||||||
return
|
|
||||||
} else if commitsCount == 0 {
|
|
||||||
ctx.NotFound(nil)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
page := max(ctx.FormInt("page"), 1)
|
// Get sign and verify info
|
||||||
|
signCommitInfos, err := processGitCommits(ctx, commits)
|
||||||
commits, err := ctx.Repo.GitRepo.CommitsByFileAndRange(
|
|
||||||
git.CommitsByFileAndRangeOptions{
|
|
||||||
Revision: ctx.Repo.RefFullName.ShortName(), // FIXME: legacy code used ShortName
|
|
||||||
File: ctx.Repo.TreePath,
|
|
||||||
Page: page,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
ctx.ServerError("CommitsByFileAndRange", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
ctx.Data["Commits"], err = processGitCommits(ctx, commits)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.ServerError("processGitCommits", err)
|
ctx.ServerError("processGitCommits", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.Data["FileTreePath"] = ctx.Repo.TreePath
|
// Get latest commit
|
||||||
ctx.Data["CommitCount"] = commitsCount
|
lastCommit := signCommitInfos[0]
|
||||||
|
ctx.Data["Username"] = ctx.Repo.Owner.Name
|
||||||
|
ctx.Data["Reponame"] = ctx.Repo.Repository.Name
|
||||||
|
ctx.Data["FileName"] = fileName
|
||||||
|
ctx.Data["CommitCount"] = len(commits)
|
||||||
|
ctx.Data["Commits"] = signCommitInfos
|
||||||
|
ctx.Data["LastCommit"] = lastCommit
|
||||||
|
ctx.Data["IsImageFile"] = lastCommit.Tree.IsImageFile(fileName)
|
||||||
|
ctx.Data["Branch"] = ctx.Repo.BranchName
|
||||||
|
ctx.Data["BranchLink"] = ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchNameSubURL()
|
||||||
|
ctx.Data["RawFileLink"] = ctx.Repo.RepoLink + "/raw/" + ctx.Repo.BranchNameSubURL() + "/" + util.PathEscapeSegments(fileName)
|
||||||
|
|
||||||
pager := context.NewPagination(commitsCount, setting.Git.CommitsRangeSize, page, 5)
|
pager := context.NewPagination(len(commits), setting.UI.ExplorePagingNum, page, 5)
|
||||||
pager.AddParamFromRequest(ctx.Req)
|
pager.SetDefaultParams(ctx)
|
||||||
ctx.Data["Page"] = pager
|
ctx.Data["Page"] = pager
|
||||||
|
|
||||||
ctx.HTML(http.StatusOK, tplCommits)
|
ctx.HTML(http.StatusOK, tplCommits)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// LoadBranchesAndTags loads branches and tags from the repository
|
||||||
func LoadBranchesAndTags(ctx *context.Context) {
|
func LoadBranchesAndTags(ctx *context.Context) {
|
||||||
response, err := repo_service.LoadBranchesAndTags(ctx, ctx.Repo, ctx.PathParam("sha"))
|
branches, err := ctx.Repo.Repository.GetBranches()
|
||||||
if err == nil {
|
if err != nil {
|
||||||
ctx.JSON(http.StatusOK, response)
|
ctx.ServerError("GetBranches", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
ctx.NotFoundOrServerError(fmt.Sprintf("could not load branches and tags the commit %s belongs to", ctx.PathParam("sha")), git.IsErrNotExist, err)
|
ctx.Data["Branches"] = branches
|
||||||
|
|
||||||
|
tags, err := ctx.Repo.Repository.GetTags(0, 0)
|
||||||
|
if err != nil {
|
||||||
|
ctx.ServerError("GetTags", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ctx.Data["Tags"] = tags
|
||||||
}
|
}
|
||||||
|
|
||||||
// Diff show different from current commit to previous commit
|
// Diff show different from current commit to previous commit of the file
|
||||||
func Diff(ctx *context.Context) {
|
func Diff(ctx *context.Context) {
|
||||||
ctx.Data["PageIsDiff"] = true
|
ctx.Data["PageIsDiff"] = true
|
||||||
|
|
||||||
@ -354,6 +394,12 @@ func Diff(ctx *context.Context) {
|
|||||||
ctx.Data["Diff"] = diff
|
ctx.Data["Diff"] = diff
|
||||||
ctx.Data["DiffBlobExcerptData"] = diffBlobExcerptData
|
ctx.Data["DiffBlobExcerptData"] = diffBlobExcerptData
|
||||||
|
|
||||||
|
// Load commit comments for diff lines
|
||||||
|
if err := repo_model.LoadCommentsForDiffLines(ctx, ctx.Repo.Repository.ID, commitID, diff.Lines); err != nil {
|
||||||
|
ctx.ServerError("LoadCommentsForDiffLines", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if !fileOnly {
|
if !fileOnly {
|
||||||
diffTree, err := gitdiff.GetDiffTree(ctx, gitRepo, false, parentCommitID, commitID)
|
diffTree, err := gitdiff.GetDiffTree(ctx, gitRepo, false, parentCommitID, commitID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -418,50 +464,47 @@ func Diff(ctx *context.Context) {
|
|||||||
|
|
||||||
// RawDiff dumps diff results of repository in given commit ID to io.Writer
|
// RawDiff dumps diff results of repository in given commit ID to io.Writer
|
||||||
func RawDiff(ctx *context.Context) {
|
func RawDiff(ctx *context.Context) {
|
||||||
var gitRepo *git.Repository
|
commitID := ctx.PathParam("sha")
|
||||||
if ctx.Data["PageIsWiki"] != nil {
|
gitRepo, err := gitrepo.OpenRepository(ctx, ctx.Repo.Repository)
|
||||||
wikiRepo, err := gitrepo.OpenRepository(ctx, ctx.Repo.Repository.WikiStorageRepo())
|
if err != nil {
|
||||||
if err != nil {
|
ctx.ServerError("OpenRepository", err)
|
||||||
ctx.ServerError("OpenRepository", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer wikiRepo.Close()
|
|
||||||
gitRepo = wikiRepo
|
|
||||||
} else {
|
|
||||||
gitRepo = ctx.Repo.GitRepo
|
|
||||||
if gitRepo == nil {
|
|
||||||
ctx.ServerError("GitRepo not open", fmt.Errorf("no open git repo for '%s'", ctx.Repo.Repository.FullName()))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if err := git.GetRawDiff(
|
|
||||||
gitRepo,
|
|
||||||
ctx.PathParam("sha"),
|
|
||||||
git.RawDiffType(ctx.PathParam("ext")),
|
|
||||||
ctx.Resp,
|
|
||||||
); err != nil {
|
|
||||||
if git.IsErrNotExist(err) {
|
|
||||||
ctx.NotFound(errors.New("commit " + ctx.PathParam("sha") + " does not exist."))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
ctx.ServerError("GetRawDiff", err)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
defer gitRepo.Close()
|
||||||
|
|
||||||
|
commit, err := gitRepo.GetCommit(commitID)
|
||||||
|
if err != nil {
|
||||||
|
if git.IsErrNotExist(err) {
|
||||||
|
ctx.NotFound(err)
|
||||||
|
} else {
|
||||||
|
ctx.ServerError("GetCommit", err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
diff, err := commit.Diff(ctx.Query("ignore whitespace"))
|
||||||
|
if err != nil {
|
||||||
|
ctx.ServerError("Diff", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer diff.Free()
|
||||||
|
|
||||||
|
ctx.Resp.Header().Set("Content-Type", "text/plain")
|
||||||
|
ctx.Resp.WriteHeader(http.StatusOK)
|
||||||
|
if err := diff.Write(ctx.Resp, git.RawDiffFormat); err != nil {
|
||||||
|
log.Error("Write: %v", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func processGitCommits(ctx *context.Context, gitCommits []*git.Commit) ([]*git_model.SignCommitWithStatuses, error) {
|
func processGitCommits(ctx *context.Context, gitCommits []*git.Commit) ([]*git_model.SignCommitWithStatuses, error) {
|
||||||
commits, err := git_service.ConvertFromGitCommit(ctx, gitCommits, ctx.Repo.Repository)
|
commits := make([]*git_model.SignCommitWithStatuses, 0, len(gitCommits))
|
||||||
if err != nil {
|
for _, commit := range gitCommits {
|
||||||
return nil, err
|
signCommit, err := git_model.GetSignCommit(ctx, ctx.Repo.Repository.ID, commit)
|
||||||
}
|
if err != nil {
|
||||||
if !ctx.Repo.Permission.CanRead(unit_model.TypeActions) {
|
return nil, err
|
||||||
for _, commit := range commits {
|
|
||||||
if commit.Status == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
commit.Status.HideActionsURL(ctx)
|
|
||||||
git_model.CommitStatusesHideActionsURL(ctx, commit.Statuses)
|
|
||||||
}
|
}
|
||||||
|
commits = append(commits, signCommit)
|
||||||
}
|
}
|
||||||
return commits, nil
|
return commits, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -82,4 +82,8 @@ type Notifier interface {
|
|||||||
WorkflowRunStatusUpdate(ctx context.Context, repo *repo_model.Repository, sender *user_model.User, run *actions_model.ActionRun)
|
WorkflowRunStatusUpdate(ctx context.Context, repo *repo_model.Repository, sender *user_model.User, run *actions_model.ActionRun)
|
||||||
|
|
||||||
WorkflowJobStatusUpdate(ctx context.Context, repo *repo_model.Repository, sender *user_model.User, job *actions_model.ActionRunJob, task *actions_model.ActionTask)
|
WorkflowJobStatusUpdate(ctx context.Context, repo *repo_model.Repository, sender *user_model.User, job *actions_model.ActionRunJob, task *actions_model.ActionTask)
|
||||||
|
|
||||||
|
// Commit comment methods
|
||||||
|
CreateCommitComment(ctx context.Context, doer *user_model.User, repo *repo_model.Repository, commitSHA string, comment *repo_model.CommitComment)
|
||||||
|
DeleteCommitComment(ctx context.Context, doer *user_model.User, comment *repo_model.CommitComment)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -17,6 +17,20 @@ import (
|
|||||||
"code.gitea.io/gitea/modules/repository"
|
"code.gitea.io/gitea/modules/repository"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// NotifyCreateCommitComment notifies subscribers when a commit comment is created
|
||||||
|
func NotifyCreateCommitComment(ctx context.Context, doer *user_model.User, repo *repo_model.Repository, commitSHA string, comment *repo_model.CommitComment) {
|
||||||
|
for _, notifier := range notifiers {
|
||||||
|
notifier.CreateCommitComment(ctx, doer, repo, commitSHA, comment)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NotifyDeleteCommitComment notifies subscribers when a commit comment is deleted
|
||||||
|
func NotifyDeleteCommitComment(ctx context.Context, doer *user_model.User, comment *repo_model.CommitComment) {
|
||||||
|
for _, notifier := range notifiers {
|
||||||
|
notifier.DeleteCommitComment(ctx, doer, comment)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var notifiers []Notifier
|
var notifiers []Notifier
|
||||||
|
|
||||||
// RegisterNotifier providers method to receive notify messages
|
// RegisterNotifier providers method to receive notify messages
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user