* Add push commits history comment on PR time-line
* Add notify by email and ui of this comment type also
Signed-off-by: a101211279
<1012112796@qq.com>
* Add migrations for IsForcePush
* fix wrong force-push judgement
* Apply suggestions from code review
* Remove commit number check
* add own notify fun
* fix some typo
Co-authored-by: guillep2k <18600385+guillep2k@users.noreply.github.com>
* fix lint
* fix style again, I forgot something before
* Change email notify way
* fix api
* add number check if It's force-push
* Add repo commit link fuction
remove unnecessary check
skip show push commits comment which not have commits alive
* Update issue_comment.go
* Apply suggestions from code review
Co-authored-by: mrsdizzie <info@mrsdizzie.com>
* Apply suggestions from code review
* fix ui view
Co-authored-by: silverwind <me@silverwind.io>
* fix height
* remove unnecessary style define
* simplify GetBranchName
* Apply suggestions from code review
* save commit ids and isForce push by json
* simplify GetBranchName
* fix bug
Co-authored-by: guillep2k <18600385+guillep2k@users.noreply.github.com>
Co-authored-by: mrsdizzie <info@mrsdizzie.com>
Co-authored-by: Lauris BH <lauris@nix.lv>
Co-authored-by: silverwind <me@silverwind.io>
tags/v1.13.0-rc1
package models | package models | ||||
import ( | import ( | ||||
"container/list" | |||||
"encoding/json" | |||||
"fmt" | "fmt" | ||||
"strings" | "strings" | ||||
CommentTypeReviewRequest | CommentTypeReviewRequest | ||||
// merge pull request | // merge pull request | ||||
CommentTypeMergePull | CommentTypeMergePull | ||||
// push to PR head branch | |||||
CommentTypePullPush | |||||
) | ) | ||||
// CommentTag defines comment tag type | // CommentTag defines comment tag type | ||||
RefRepo *Repository `xorm:"-"` | RefRepo *Repository `xorm:"-"` | ||||
RefIssue *Issue `xorm:"-"` | RefIssue *Issue `xorm:"-"` | ||||
RefComment *Comment `xorm:"-"` | RefComment *Comment `xorm:"-"` | ||||
Commits *list.List `xorm:"-"` | |||||
OldCommit string `xorm:"-"` | |||||
NewCommit string `xorm:"-"` | |||||
CommitsNum int64 `xorm:"-"` | |||||
IsForcePush bool `xorm:"-"` | |||||
} | |||||
// PushActionContent is content of push pull comment | |||||
type PushActionContent struct { | |||||
IsForcePush bool `json:"is_force_push"` | |||||
CommitIDs []string `json:"commit_ids"` | |||||
} | } | ||||
// LoadIssue loads issue from database | // LoadIssue loads issue from database | ||||
return fmt.Sprintf("%s/files#%s", c.Issue.HTMLURL(), c.HashTag()) | return fmt.Sprintf("%s/files#%s", c.Issue.HTMLURL(), c.HashTag()) | ||||
} | } | ||||
// LoadPushCommits Load push commits | |||||
func (c *Comment) LoadPushCommits() (err error) { | |||||
if c.Content == "" || c.Commits != nil || c.Type != CommentTypePullPush { | |||||
return nil | |||||
} | |||||
var data PushActionContent | |||||
err = json.Unmarshal([]byte(c.Content), &data) | |||||
if err != nil { | |||||
return | |||||
} | |||||
c.IsForcePush = data.IsForcePush | |||||
if c.IsForcePush { | |||||
if len(data.CommitIDs) != 2 { | |||||
return nil | |||||
} | |||||
c.OldCommit = data.CommitIDs[0] | |||||
c.NewCommit = data.CommitIDs[1] | |||||
} else { | |||||
repoPath := c.Issue.Repo.RepoPath() | |||||
gitRepo, err := git.OpenRepository(repoPath) | |||||
if err != nil { | |||||
return err | |||||
} | |||||
defer gitRepo.Close() | |||||
c.Commits = gitRepo.GetCommitsFromIDs(data.CommitIDs) | |||||
c.CommitsNum = int64(c.Commits.Len()) | |||||
if c.CommitsNum > 0 { | |||||
c.Commits = ValidateCommitsWithEmails(c.Commits) | |||||
c.Commits = ParseCommitsWithSignature(c.Commits, c.Issue.Repo) | |||||
c.Commits = ParseCommitsWithStatus(c.Commits, c.Issue.Repo) | |||||
} | |||||
} | |||||
return err | |||||
} | |||||
func createComment(e *xorm.Session, opts *CreateCommentOptions) (_ *Comment, err error) { | func createComment(e *xorm.Session, opts *CreateCommentOptions) (_ *Comment, err error) { | ||||
var LabelID int64 | var LabelID int64 | ||||
if opts.Label != nil { | if opts.Label != nil { | ||||
RefCommentID: opts.RefCommentID, | RefCommentID: opts.RefCommentID, | ||||
RefAction: opts.RefAction, | RefAction: opts.RefAction, | ||||
RefIsPull: opts.RefIsPull, | RefIsPull: opts.RefIsPull, | ||||
IsForcePush: opts.IsForcePush, | |||||
} | } | ||||
if _, err = e.Insert(comment); err != nil { | if _, err = e.Insert(comment); err != nil { | ||||
return nil, err | return nil, err | ||||
RefCommentID int64 | RefCommentID int64 | ||||
RefAction references.XRefAction | RefAction references.XRefAction | ||||
RefIsPull bool | RefIsPull bool | ||||
IsForcePush bool | |||||
} | } | ||||
// CreateComment creates comment of issue or commit. | // CreateComment creates comment of issue or commit. | ||||
}) | }) | ||||
return err | return err | ||||
} | } | ||||
// CreatePushPullComment create push code to pull base commend | |||||
func CreatePushPullComment(pusher *User, pr *PullRequest, oldCommitID, newCommitID string) (comment *Comment, err error) { | |||||
if pr.HasMerged || oldCommitID == "" || newCommitID == "" { | |||||
return nil, nil | |||||
} | |||||
ops := &CreateCommentOptions{ | |||||
Type: CommentTypePullPush, | |||||
Doer: pusher, | |||||
Repo: pr.BaseRepo, | |||||
} | |||||
var data PushActionContent | |||||
data.CommitIDs, data.IsForcePush, err = getCommitIDsFromRepo(pr.BaseRepo, oldCommitID, newCommitID, pr.BaseBranch) | |||||
if err != nil { | |||||
return nil, err | |||||
} | |||||
ops.Issue = pr.Issue | |||||
dataJSON, err := json.Marshal(data) | |||||
if err != nil { | |||||
return nil, err | |||||
} | |||||
ops.Content = string(dataJSON) | |||||
comment, err = CreateComment(ops) | |||||
return | |||||
} | |||||
// getCommitsFromRepo get commit IDs from repo in betwern oldCommitID and newCommitID | |||||
// isForcePush will be true if oldCommit isn't on the branch | |||||
// Commit on baseBranch will skip | |||||
func getCommitIDsFromRepo(repo *Repository, oldCommitID, newCommitID, baseBranch string) (commitIDs []string, isForcePush bool, err error) { | |||||
repoPath := repo.RepoPath() | |||||
gitRepo, err := git.OpenRepository(repoPath) | |||||
if err != nil { | |||||
return nil, false, err | |||||
} | |||||
defer gitRepo.Close() | |||||
oldCommit, err := gitRepo.GetCommit(oldCommitID) | |||||
if err != nil { | |||||
return nil, false, err | |||||
} | |||||
oldCommitBranch, err := oldCommit.GetBranchName() | |||||
if err != nil { | |||||
return nil, false, err | |||||
} | |||||
if oldCommitBranch == "undefined" { | |||||
commitIDs = make([]string, 2) | |||||
commitIDs[0] = oldCommitID | |||||
commitIDs[1] = newCommitID | |||||
return commitIDs, true, err | |||||
} | |||||
newCommit, err := gitRepo.GetCommit(newCommitID) | |||||
if err != nil { | |||||
return nil, false, err | |||||
} | |||||
var commits *list.List | |||||
commits, err = newCommit.CommitsBeforeUntil(oldCommitID) | |||||
if err != nil { | |||||
return nil, false, err | |||||
} | |||||
commitIDs = make([]string, 0, commits.Len()) | |||||
for e := commits.Back(); e != nil; e = e.Prev() { | |||||
commit := e.Value.(*git.Commit) | |||||
commitBranch, err := commit.GetBranchName() | |||||
if err != nil { | |||||
return nil, false, err | |||||
} | |||||
if commitBranch != baseBranch { | |||||
commitIDs = append(commitIDs, commit.ID.String()) | |||||
} | |||||
} | |||||
return | |||||
} |
return setting.AppURL + repo.FullName() | return setting.AppURL + repo.FullName() | ||||
} | } | ||||
// CommitLink make link to by commit full ID | |||||
// note: won't check whether it's an right id | |||||
func (repo *Repository) CommitLink(commitID string) (result string) { | |||||
if commitID == "" || commitID == "0000000000000000000000000000000000000000" { | |||||
result = "" | |||||
} else { | |||||
result = repo.HTMLURL() + "/commit/" + commitID | |||||
} | |||||
return | |||||
} | |||||
// APIURL returns the repository API URL | // APIURL returns the repository API URL | ||||
func (repo *Repository) APIURL() string { | func (repo *Repository) APIURL() string { | ||||
return setting.AppURL + path.Join("api/v1/repos", repo.FullName()) | return setting.AppURL + path.Join("api/v1/repos", repo.FullName()) |
return nil, nil | return nil, nil | ||||
} | } | ||||
// GetBranchName gets the closes branch name (as returned by 'git name-rev') | |||||
// GetBranchName gets the closes branch name (as returned by 'git name-rev --name-only') | |||||
func (c *Commit) GetBranchName() (string, error) { | func (c *Commit) GetBranchName() (string, error) { | ||||
data, err := NewCommand("name-rev", c.ID.String()).RunInDirBytes(c.repo.Path) | |||||
data, err := NewCommand("name-rev", "--name-only", c.ID.String()).RunInDir(c.repo.Path) | |||||
if err != nil { | if err != nil { | ||||
return "", err | return "", err | ||||
} | } | ||||
// name-rev commitID output will be "COMMIT_ID master" or "COMMIT_ID master~12" | |||||
return strings.Split(strings.Split(string(data), " ")[1], "~")[0], nil | |||||
// name-rev commitID output will be "master" or "master~12" | |||||
return strings.SplitN(strings.TrimSpace(data), "~", 2)[0], nil | |||||
} | } | ||||
// CommitFileStatus represents status of files in a commit. | // CommitFileStatus represents status of files in a commit. |
} | } | ||||
return branches, nil | return branches, nil | ||||
} | } | ||||
// GetCommitsFromIDs get commits from commit IDs | |||||
func (repo *Repository) GetCommitsFromIDs(commitIDs []string) (commits *list.List) { | |||||
if len(commitIDs) == 0 { | |||||
return nil | |||||
} | |||||
commits = list.New() | |||||
for _, commitID := range commitIDs { | |||||
commit, err := repo.GetCommit(commitID) | |||||
if err == nil && commit != nil { | |||||
commits.PushBack(commit) | |||||
} | |||||
} | |||||
return commits | |||||
} |
NotifyPullRequestSynchronized(doer *models.User, pr *models.PullRequest) | NotifyPullRequestSynchronized(doer *models.User, pr *models.PullRequest) | ||||
NotifyPullRequestReview(*models.PullRequest, *models.Review, *models.Comment) | NotifyPullRequestReview(*models.PullRequest, *models.Review, *models.Comment) | ||||
NotifyPullRequestChangeTargetBranch(doer *models.User, pr *models.PullRequest, oldBranch string) | NotifyPullRequestChangeTargetBranch(doer *models.User, pr *models.PullRequest, oldBranch string) | ||||
NotifyPullRequestPushCommits(doer *models.User, pr *models.PullRequest, comment *models.Comment) | |||||
NotifyCreateIssueComment(*models.User, *models.Repository, | NotifyCreateIssueComment(*models.User, *models.Repository, | ||||
*models.Issue, *models.Comment) | *models.Issue, *models.Comment) |
func (*NullNotifier) NotifyPullRequestChangeTargetBranch(doer *models.User, pr *models.PullRequest, oldBranch string) { | func (*NullNotifier) NotifyPullRequestChangeTargetBranch(doer *models.User, pr *models.PullRequest, oldBranch string) { | ||||
} | } | ||||
// NotifyPullRequestPushCommits notifies when push commits to pull request's head branch | |||||
func (*NullNotifier) NotifyPullRequestPushCommits(doer *models.User, pr *models.PullRequest, comment *models.Comment) { | |||||
} | |||||
// NotifyUpdateComment places a place holder function | // NotifyUpdateComment places a place holder function | ||||
func (*NullNotifier) NotifyUpdateComment(doer *models.User, c *models.Comment, oldContent string) { | func (*NullNotifier) NotifyUpdateComment(doer *models.User, c *models.Comment, oldContent string) { | ||||
} | } |
act = models.ActionCommentIssue | act = models.ActionCommentIssue | ||||
} else if comment.Type == models.CommentTypeCode { | } else if comment.Type == models.CommentTypeCode { | ||||
act = models.ActionCommentIssue | act = models.ActionCommentIssue | ||||
} else if comment.Type == models.CommentTypePullPush { | |||||
act = 0 | |||||
} | } | ||||
if err := mailer.MailParticipantsComment(comment, act, issue); err != nil { | if err := mailer.MailParticipantsComment(comment, act, issue); err != nil { | ||||
log.Error("MailParticipants: %v", err) | log.Error("MailParticipants: %v", err) | ||||
} | } | ||||
} | } | ||||
func (m *mailNotifier) NotifyPullRequestPushCommits(doer *models.User, pr *models.PullRequest, comment *models.Comment) { | |||||
var err error | |||||
if err = comment.LoadIssue(); err != nil { | |||||
log.Error("comment.LoadIssue: %v", err) | |||||
return | |||||
} | |||||
if err = comment.Issue.LoadRepo(); err != nil { | |||||
log.Error("comment.Issue.LoadRepo: %v", err) | |||||
return | |||||
} | |||||
if err = comment.Issue.LoadPullRequest(); err != nil { | |||||
log.Error("comment.Issue.LoadPullRequest: %v", err) | |||||
return | |||||
} | |||||
if err = comment.Issue.PullRequest.LoadBaseRepo(); err != nil { | |||||
log.Error("comment.Issue.PullRequest.LoadBaseRepo: %v", err) | |||||
return | |||||
} | |||||
if err := comment.LoadPushCommits(); err != nil { | |||||
log.Error("comment.LoadPushCommits: %v", err) | |||||
} | |||||
comment.Content = "" | |||||
m.NotifyCreateIssueComment(doer, comment.Issue.Repo, comment.Issue, comment) | |||||
} |
} | } | ||||
} | } | ||||
// NotifyPullRequestPushCommits notifies when push commits to pull request's head branch | |||||
func NotifyPullRequestPushCommits(doer *models.User, pr *models.PullRequest, comment *models.Comment) { | |||||
for _, notifier := range notifiers { | |||||
notifier.NotifyPullRequestPushCommits(doer, pr, comment) | |||||
} | |||||
} | |||||
// NotifyUpdateComment notifies update comment to notifiers | // NotifyUpdateComment notifies update comment to notifiers | ||||
func NotifyUpdateComment(doer *models.User, c *models.Comment, oldContent string) { | func NotifyUpdateComment(doer *models.User, c *models.Comment, oldContent string) { | ||||
for _, notifier := range notifiers { | for _, notifier := range notifiers { |
_ = ns.issueQueue.Push(opts) | _ = ns.issueQueue.Push(opts) | ||||
} | } | ||||
func (ns *notificationService) NotifyPullRequestPushCommits(doer *models.User, pr *models.PullRequest, comment *models.Comment) { | |||||
var opts = issueNotificationOpts{ | |||||
IssueID: pr.IssueID, | |||||
NotificationAuthorID: doer.ID, | |||||
CommentID: comment.ID, | |||||
} | |||||
_ = ns.issueQueue.Push(opts) | |||||
} | |||||
func (ns *notificationService) NotifyIssueChangeAssignee(doer *models.User, issue *models.Issue, assignee *models.User, removed bool, comment *models.Comment) { | func (ns *notificationService) NotifyIssueChangeAssignee(doer *models.User, issue *models.Issue, assignee *models.User, removed bool, comment *models.Comment) { | ||||
if !removed { | if !removed { | ||||
var opts = issueNotificationOpts{ | var opts = issueNotificationOpts{ |
issues.invalid_due_date_format = "Due date format must be 'yyyy-mm-dd'." | issues.invalid_due_date_format = "Due date format must be 'yyyy-mm-dd'." | ||||
issues.error_modifying_due_date = "Failed to modify the due date." | issues.error_modifying_due_date = "Failed to modify the due date." | ||||
issues.error_removing_due_date = "Failed to remove the due date." | issues.error_removing_due_date = "Failed to remove the due date." | ||||
issues.push_commit_1 = "added %d commit %s" | |||||
issues.push_commits_n = "added %d commits %s" | |||||
issues.force_push_codes = `force-pushed the %[1]s branch from <a href="%[3]s">%[2]s</a> to <a href="%[5]s">%[4]s</a>. %[6]s` | |||||
issues.due_date_form = "yyyy-mm-dd" | issues.due_date_form = "yyyy-mm-dd" | ||||
issues.due_date_form_add = "Add due date" | issues.due_date_form_add = "Add due date" | ||||
issues.due_date_form_edit = "Edit" | issues.due_date_form_edit = "Edit" |
ctx.ServerError("LoadResolveDoer", err) | ctx.ServerError("LoadResolveDoer", err) | ||||
return | return | ||||
} | } | ||||
} else if comment.Type == models.CommentTypePullPush { | |||||
participants = addParticipant(comment.Poster, participants) | |||||
if err = comment.LoadPushCommits(); err != nil { | |||||
ctx.ServerError("LoadPushCommits", err) | |||||
return | |||||
} | |||||
} | } | ||||
} | } | ||||
name = "code" | name = "code" | ||||
case models.CommentTypeAssignees: | case models.CommentTypeAssignees: | ||||
name = "assigned" | name = "assigned" | ||||
case models.CommentTypePullPush: | |||||
name = "push" | |||||
default: | default: | ||||
name = "default" | name = "default" | ||||
} | } |
"bufio" | "bufio" | ||||
"bytes" | "bytes" | ||||
"context" | "context" | ||||
"encoding/json" | |||||
"fmt" | "fmt" | ||||
"os" | "os" | ||||
"path" | "path" | ||||
notification.NotifyNewPullRequest(pr) | notification.NotifyNewPullRequest(pr) | ||||
// add first push codes comment | |||||
baseGitRepo, err := git.OpenRepository(pr.BaseRepo.RepoPath()) | |||||
if err != nil { | |||||
return err | |||||
} | |||||
defer baseGitRepo.Close() | |||||
compareInfo, err := baseGitRepo.GetCompareInfo(pr.BaseRepo.RepoPath(), | |||||
pr.BaseBranch, pr.GetGitRefName()) | |||||
if err != nil { | |||||
return err | |||||
} | |||||
if compareInfo.Commits.Len() > 0 { | |||||
data := models.PushActionContent{IsForcePush: false} | |||||
data.CommitIDs = make([]string, 0, compareInfo.Commits.Len()) | |||||
for e := compareInfo.Commits.Back(); e != nil; e = e.Prev() { | |||||
data.CommitIDs = append(data.CommitIDs, e.Value.(*git.Commit).ID.String()) | |||||
} | |||||
dataJSON, err := json.Marshal(data) | |||||
if err != nil { | |||||
return err | |||||
} | |||||
ops := &models.CreateCommentOptions{ | |||||
Type: models.CommentTypePullPush, | |||||
Doer: pull.Poster, | |||||
Repo: repo, | |||||
Issue: pr.Issue, | |||||
IsForcePush: false, | |||||
Content: string(dataJSON), | |||||
} | |||||
_, _ = models.CreateComment(ops) | |||||
} | |||||
return nil | return nil | ||||
} | } | ||||
} | } | ||||
addHeadRepoTasks(prs) | addHeadRepoTasks(prs) | ||||
for _, pr := range prs { | |||||
comment, err := models.CreatePushPullComment(doer, pr, oldCommitID, newCommitID) | |||||
if err == nil && comment != nil { | |||||
notification.NotifyPullRequestPushCommits(doer, pr, comment) | |||||
} | |||||
} | |||||
log.Trace("AddTestPullRequestTask [base_repo_id: %d, base_branch: %s]: finding pull requests", repoID, branch) | log.Trace("AddTestPullRequestTask [base_repo_id: %d, base_branch: %s]: finding pull requests", repoID, branch) | ||||
prs, err = models.GetUnmergedPullRequestsByBaseInfo(repoID, branch) | prs, err = models.GetUnmergedPullRequestsByBaseInfo(repoID, branch) |
<body> | <body> | ||||
{{if .IsMention}}<p><b>@{{.Doer.Name}}</b> mentioned you:</p>{{end}} | {{if .IsMention}}<p><b>@{{.Doer.Name}}</b> mentioned you:</p>{{end}} | ||||
{{if eq .Comment.Type 29}} | |||||
<p> | |||||
<b>{{.Doer.Name}}</b> | |||||
{{if .Comment.IsForcePush}} | |||||
{{ $oldCommitLink:= printf "%s%s/%s/commit/%s" AppUrl .Comment.Issue.PullRequest.BaseRepo.OwnerName .Comment.Issue.PullRequest.BaseRepo.Name .Comment.OldCommit}} | |||||
{{ $newCommitLink:= printf "%s%s/%s/commit/%s" AppUrl .Comment.Issue.PullRequest.BaseRepo.OwnerName .Comment.Issue.PullRequest.BaseRepo.Name .Comment.NewCommit}} | |||||
force-pushed the <b>{{.Comment.Issue.PullRequest.HeadBranch}}</b> from | |||||
<a href="{{$oldCommitLink}}"><b>{{ShortSha .Comment.OldCommit}}</b></a> | |||||
to | |||||
<a href="{{$newCommitLink}}"><b>{{ShortSha .Comment.NewCommit}}</b></a>. | |||||
{{else}} | |||||
{{if eq .Comment.Commits.Len 1}} | |||||
{{printf "pushed 1 commit to %s:" .Comment.Issue.PullRequest.HeadBranch}} | |||||
{{else}} | |||||
{{printf "pushed %d commits to %s:" .Comment.Commits.Len .Comment.Issue.PullRequest.HeadBranch}} | |||||
{{end}} | |||||
{{end}} | |||||
</p> | |||||
{{end}} | |||||
<p> | <p> | ||||
{{if eq .ActionName "close"}} | {{if eq .ActionName "close"}} | ||||
Closed #{{.Issue.Index}}. | Closed #{{.Issue.Index}}. | ||||
<pre>{{.Patch}}</pre> | <pre>{{.Patch}}</pre> | ||||
<div>{{.RenderedContent | Safe}}</div> | <div>{{.RenderedContent | Safe}}</div> | ||||
</div> | </div> | ||||
{{end -}} | |||||
{{end -}} | |||||
{{if eq .Comment.Type 29}} | |||||
{{ $r:= List .Comment.Commits}} | |||||
<ul> | |||||
{{range $r}} | |||||
<li> | |||||
<a href="{{AppUrl}}{{$.Comment.Issue.PullRequest.BaseRepo.OwnerName}}/{{$.Comment.Issue.PullRequest.BaseRepo.Name}}/commit/{{.ID}}"> | |||||
{{ShortSha .ID.String}} | |||||
</a> - {{.Summary}} | |||||
</li> | |||||
{{end}} | |||||
</ul> | |||||
{{end}} | |||||
</p> | </p> | ||||
<div class="footer"> | <div class="footer"> | ||||
<p> | <p> |
{{ $r:= List .Commits}} | |||||
{{ $index := 0}} | |||||
{{range $r}} | |||||
{{ $tag := printf "%s-%d" $.HashTag $index }} | |||||
{{ $index = Add $index 1}} | |||||
<div class="timeline-item event small-line-spacing" id="{{$tag}}"> | |||||
<span class="badge badge-commit">{{svg "octicon-git-commit" 16}}</span> | |||||
{{if .User}} | |||||
<a class="ui avatar image" href="{{AppSubUrl}}/{{.User.Name}}"><img src="{{.User.RelAvatarLink}}" alt=""/></a> | |||||
{{else}} | |||||
<img class="ui avatar image" src="{{AvatarLink .Author.Email}}" alt=""/> | |||||
{{end}} | |||||
<span class="ui float right shabox"> | |||||
{{$class := "ui sha label"}} | |||||
{{if .Signature}} | |||||
{{$class = (printf "%s%s" $class " isSigned")}} | |||||
{{if .Verification.Verified}} | |||||
{{if eq .Verification.TrustStatus "trusted"}} | |||||
{{$class = (printf "%s%s" $class " isVerified")}} | |||||
{{else if eq .Verification.TrustStatus "untrusted"}} | |||||
{{$class = (printf "%s%s" $class " isVerifiedUntrusted")}} | |||||
{{else}} | |||||
{{$class = (printf "%s%s" $class " isVerifiedUnmatched")}} | |||||
{{end}} | |||||
{{else if .Verification.Warning}} | |||||
{{$class = (printf "%s%s" $class " isWarning")}} | |||||
{{end}} | |||||
{{end}} | |||||
{{if $.Issue.PullRequest.BaseRepo.Name}} | |||||
<a href="{{AppSubUrl}}/{{$.Issue.PullRequest.BaseRepo.OwnerName}}/{{$.Issue.PullRequest.BaseRepo.Name}}/commit/{{.ID}}" rel="nofollow" class="{{$class}}"> | |||||
{{else}} | |||||
<span class="{{$class}}"> | |||||
{{end}} | |||||
<span class="shortsha">{{ShortSha .ID.String}}</span> | |||||
{{if $.Issue.PullRequest.BaseRepo.Name}} | |||||
</a> | |||||
{{else}} | |||||
</span> | |||||
{{end}} | |||||
</span> | |||||
<span class="message-wrapper"> | |||||
{{ $commitLink:= printf "%s/%s/%s/commit/%s" AppSubUrl $.Issue.PullRequest.BaseRepo.OwnerName $.Issue.PullRequest.BaseRepo.Name .ID }} | |||||
<span class="commit-summary has-emoji{{if gt .ParentCount 1}} grey text{{end}}" title="{{.Summary}}">{{RenderCommitMessageLinkSubject .Message ($.Issue.PullRequest.BaseRepo.Link|Escape) $commitLink $.Issue.PullRequest.BaseRepo.ComposeMetas}}</span> | |||||
</span> | |||||
{{if IsMultilineCommitMessage .Message}} | |||||
<button class="basic compact mini ui icon button commit-button"><i class="ellipsis horizontal icon"></i></button> | |||||
{{end}} | |||||
{{if eq (CommitType .) "SignCommitWithStatuses"}} | |||||
{{template "repo/commit_status" .Status}} | |||||
{{end}} | |||||
{{if IsMultilineCommitMessage .Message}} | |||||
<pre class="commit-body" style="display: none;">{{RenderCommitBody .Message ($.Issue.PullRequest.BaseRepo.Link|Escape) $.Issue.PullRequest.BaseRepo.ComposeMetas}}</pre> | |||||
{{end}} | |||||
</div> | |||||
{{end}} |
13 = STOP_TRACKING, 14 = ADD_TIME_MANUAL, 16 = ADDED_DEADLINE, 17 = MODIFIED_DEADLINE, | 13 = STOP_TRACKING, 14 = ADD_TIME_MANUAL, 16 = ADDED_DEADLINE, 17 = MODIFIED_DEADLINE, | ||||
18 = REMOVED_DEADLINE, 19 = ADD_DEPENDENCY, 20 = REMOVE_DEPENDENCY, 21 = CODE, | 18 = REMOVED_DEADLINE, 19 = ADD_DEPENDENCY, 20 = REMOVE_DEPENDENCY, 21 = CODE, | ||||
22 = REVIEW, 23 = ISSUE_LOCKED, 24 = ISSUE_UNLOCKED, 25 = TARGET_BRANCH_CHANGED, | 22 = REVIEW, 23 = ISSUE_LOCKED, 24 = ISSUE_UNLOCKED, 25 = TARGET_BRANCH_CHANGED, | ||||
26 = DELETE_TIME_MANUAL, 27 = REVIEW_REQUEST, 28 = MERGE_PULL_REQUEST --> | |||||
26 = DELETE_TIME_MANUAL, 27 = REVIEW_REQUEST, 28 = MERGE_PULL_REQUEST, | |||||
29 = PULL_PUSH_EVENT --> | |||||
{{if eq .Type 0}} | {{if eq .Type 0}} | ||||
<div class="timeline-item comment" id="{{.HashTag}}"> | <div class="timeline-item comment" id="{{.HashTag}}"> | ||||
{{if .OriginalAuthor }} | {{if .OriginalAuthor }} | ||||
{{end}} | {{end}} | ||||
</span> | </span> | ||||
</div> | </div> | ||||
{{else if and (eq .Type 29) (or (gt .CommitsNum 0) .IsForcePush)}} | |||||
<div class="timeline-item event" id="{{.HashTag}}"> | |||||
<span class="badge">{{svg "octicon-repo-force-push" 16}}</span> | |||||
<a class="ui avatar image" href="{{.Poster.HomeLink}}"> | |||||
<img src="{{.Poster.RelAvatarLink}}"> | |||||
</a> | |||||
<span class="text grey"> | |||||
<a href="{{.Poster.HomeLink}}">{{.Poster.GetDisplayName}}</a> | |||||
{{ if .IsForcePush }} | |||||
{{$.i18n.Tr "repo.issues.force_push_codes" $.Issue.PullRequest.HeadBranch (ShortSha .OldCommit) ($.Issue.Repo.CommitLink .OldCommit) (ShortSha .NewCommit) ($.Issue.Repo.CommitLink .NewCommit) $createdStr | Safe}} | |||||
{{else}} | |||||
{{$.i18n.Tr (TrN $.i18n.Lang .Commits.Len "repo.issues.push_commit_1" "repo.issues.push_commits_n") .Commits.Len $createdStr | Safe}} | |||||
{{end}} | |||||
</span> | |||||
</div> | |||||
{{if not .IsForcePush}} | |||||
{{template "repo/commits_list_small" .}} | |||||
{{end}} | |||||
{{end}} | {{end}} | ||||
{{end}} | {{end}} |
padding-bottom: 0 !important; | padding-bottom: 0 !important; | ||||
} | } | ||||
.badge.badge-commit { | |||||
border-color: transparent; | |||||
background: radial-gradient(white 60%, transparent 60%) no-repeat; | |||||
height: 0 !important; | |||||
} | |||||
.badge { | .badge { | ||||
width: 32px; | width: 32px; | ||||
height: 32px; | height: 32px; | ||||
line-height: 30px; | line-height: 30px; | ||||
} | } | ||||
&.event.small-line-spacing { | |||||
padding: 2px 0 0 15px; | |||||
} | |||||
&.event > .commit-status-link { | |||||
float: right; | |||||
margin-right: 8px; | |||||
margin-top: 4px; | |||||
} | |||||
.author { | .author { | ||||
font-weight: 700; | font-weight: 700; | ||||
} | } | ||||
} | } | ||||
} | } | ||||
& > .shabox { | |||||
.sha.label { | |||||
margin: 0; | |||||
border: 1px solid #bbbbbb; | |||||
&.isSigned.isWarning { | |||||
border: 1px solid #db2828; | |||||
background: fade(#db2828, 10%); | |||||
.shortsha { | |||||
display: inline-block; | |||||
padding-top: 1px; | |||||
} | |||||
&:hover { | |||||
background: fade(#db2828, 30%) !important; | |||||
} | |||||
} | |||||
&.isSigned.isVerified { | |||||
border: 1px solid #21ba45; | |||||
background: fade(#21ba45, 10%); | |||||
.shortsha { | |||||
display: inline-block; | |||||
padding-top: 1px; | |||||
} | |||||
&:hover { | |||||
background: fade(#21ba45, 30%) !important; | |||||
} | |||||
} | |||||
&.isSigned.isVerifiedUntrusted { | |||||
border: 1px solid #fbbd08; | |||||
background: fade(#fbbd08, 10%); | |||||
.shortsha { | |||||
display: inline-block; | |||||
padding-top: 1px; | |||||
} | |||||
&:hover { | |||||
background: fade(#fbbd08, 30%) !important; | |||||
} | |||||
} | |||||
&.isSigned.isVerifiedUnmatched { | |||||
border: 1px solid #f2711c; | |||||
background: fade(#f2711c, 10%); | |||||
.shortsha { | |||||
display: inline-block; | |||||
padding-top: 1px; | |||||
} | |||||
&:hover { | |||||
background: fade(#f2711c, 30%) !important; | |||||
} | |||||
} | |||||
} | |||||
} | |||||
.detail { | .detail { | ||||
font-size: .9rem; | font-size: .9rem; | ||||
margin-top: 5px; | margin-top: 5px; |
color: #9e9e9e; | color: #9e9e9e; | ||||
} | } | ||||
.repository.view.issue .comment-list .timeline-item .badge.badge-commit { | |||||
background: radial-gradient(#383c4a 60%, transparent 60%) no-repeat; | |||||
height: 0 !important; | |||||
} | |||||
.repository .comment.form .content .form:after { | .repository .comment.form .content .form:after { | ||||
border-right-color: #313c47; | border-right-color: #313c47; | ||||
} | } |