Skip to content

Commit 0903b1a

Browse files
a1012112796guillep2kmrsdizzielafrikssilverwind
authored
Add push commits history comment on PR time-line (#11167)
* Add push commits history comment on PR time-line * Add notify by email and ui of this comment type also Signed-off-by: a1012112796 <[email protected]> * 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 <[email protected]> * 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 <[email protected]> * Apply suggestions from code review * fix ui view Co-authored-by: silverwind <[email protected]> * 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 <[email protected]> Co-authored-by: mrsdizzie <[email protected]> Co-authored-by: Lauris BH <[email protected]> Co-authored-by: silverwind <[email protected]>
1 parent 9e0e2a9 commit 0903b1a

File tree

18 files changed

+478
-6
lines changed

18 files changed

+478
-6
lines changed

models/issue_comment.go

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
package models
88

99
import (
10+
"container/list"
11+
"encoding/json"
1012
"fmt"
1113
"strings"
1214

@@ -90,6 +92,8 @@ const (
9092
CommentTypeReviewRequest
9193
// merge pull request
9294
CommentTypeMergePull
95+
// push to PR head branch
96+
CommentTypePullPush
9397
)
9498

9599
// CommentTag defines comment tag type
@@ -167,6 +171,18 @@ type Comment struct {
167171
RefRepo *Repository `xorm:"-"`
168172
RefIssue *Issue `xorm:"-"`
169173
RefComment *Comment `xorm:"-"`
174+
175+
Commits *list.List `xorm:"-"`
176+
OldCommit string `xorm:"-"`
177+
NewCommit string `xorm:"-"`
178+
CommitsNum int64 `xorm:"-"`
179+
IsForcePush bool `xorm:"-"`
180+
}
181+
182+
// PushActionContent is content of push pull comment
183+
type PushActionContent struct {
184+
IsForcePush bool `json:"is_force_push"`
185+
CommitIDs []string `json:"commit_ids"`
170186
}
171187

172188
// LoadIssue loads issue from database
@@ -543,6 +559,47 @@ func (c *Comment) CodeCommentURL() string {
543559
return fmt.Sprintf("%s/files#%s", c.Issue.HTMLURL(), c.HashTag())
544560
}
545561

562+
// LoadPushCommits Load push commits
563+
func (c *Comment) LoadPushCommits() (err error) {
564+
if c.Content == "" || c.Commits != nil || c.Type != CommentTypePullPush {
565+
return nil
566+
}
567+
568+
var data PushActionContent
569+
570+
err = json.Unmarshal([]byte(c.Content), &data)
571+
if err != nil {
572+
return
573+
}
574+
575+
c.IsForcePush = data.IsForcePush
576+
577+
if c.IsForcePush {
578+
if len(data.CommitIDs) != 2 {
579+
return nil
580+
}
581+
c.OldCommit = data.CommitIDs[0]
582+
c.NewCommit = data.CommitIDs[1]
583+
} else {
584+
repoPath := c.Issue.Repo.RepoPath()
585+
gitRepo, err := git.OpenRepository(repoPath)
586+
if err != nil {
587+
return err
588+
}
589+
defer gitRepo.Close()
590+
591+
c.Commits = gitRepo.GetCommitsFromIDs(data.CommitIDs)
592+
c.CommitsNum = int64(c.Commits.Len())
593+
if c.CommitsNum > 0 {
594+
c.Commits = ValidateCommitsWithEmails(c.Commits)
595+
c.Commits = ParseCommitsWithSignature(c.Commits, c.Issue.Repo)
596+
c.Commits = ParseCommitsWithStatus(c.Commits, c.Issue.Repo)
597+
}
598+
}
599+
600+
return err
601+
}
602+
546603
func createComment(e *xorm.Session, opts *CreateCommentOptions) (_ *Comment, err error) {
547604
var LabelID int64
548605
if opts.Label != nil {
@@ -576,6 +633,7 @@ func createComment(e *xorm.Session, opts *CreateCommentOptions) (_ *Comment, err
576633
RefCommentID: opts.RefCommentID,
577634
RefAction: opts.RefAction,
578635
RefIsPull: opts.RefIsPull,
636+
IsForcePush: opts.IsForcePush,
579637
}
580638
if _, err = e.Insert(comment); err != nil {
581639
return nil, err
@@ -738,6 +796,7 @@ type CreateCommentOptions struct {
738796
RefCommentID int64
739797
RefAction references.XRefAction
740798
RefIsPull bool
799+
IsForcePush bool
741800
}
742801

743802
// CreateComment creates comment of issue or commit.
@@ -1016,3 +1075,92 @@ func UpdateCommentsMigrationsByType(tp structs.GitServiceType, originalAuthorID
10161075
})
10171076
return err
10181077
}
1078+
1079+
// CreatePushPullComment create push code to pull base commend
1080+
func CreatePushPullComment(pusher *User, pr *PullRequest, oldCommitID, newCommitID string) (comment *Comment, err error) {
1081+
if pr.HasMerged || oldCommitID == "" || newCommitID == "" {
1082+
return nil, nil
1083+
}
1084+
1085+
ops := &CreateCommentOptions{
1086+
Type: CommentTypePullPush,
1087+
Doer: pusher,
1088+
Repo: pr.BaseRepo,
1089+
}
1090+
1091+
var data PushActionContent
1092+
1093+
data.CommitIDs, data.IsForcePush, err = getCommitIDsFromRepo(pr.BaseRepo, oldCommitID, newCommitID, pr.BaseBranch)
1094+
if err != nil {
1095+
return nil, err
1096+
}
1097+
1098+
ops.Issue = pr.Issue
1099+
dataJSON, err := json.Marshal(data)
1100+
if err != nil {
1101+
return nil, err
1102+
}
1103+
1104+
ops.Content = string(dataJSON)
1105+
1106+
comment, err = CreateComment(ops)
1107+
1108+
return
1109+
}
1110+
1111+
// getCommitsFromRepo get commit IDs from repo in betwern oldCommitID and newCommitID
1112+
// isForcePush will be true if oldCommit isn't on the branch
1113+
// Commit on baseBranch will skip
1114+
func getCommitIDsFromRepo(repo *Repository, oldCommitID, newCommitID, baseBranch string) (commitIDs []string, isForcePush bool, err error) {
1115+
repoPath := repo.RepoPath()
1116+
gitRepo, err := git.OpenRepository(repoPath)
1117+
if err != nil {
1118+
return nil, false, err
1119+
}
1120+
defer gitRepo.Close()
1121+
1122+
oldCommit, err := gitRepo.GetCommit(oldCommitID)
1123+
if err != nil {
1124+
return nil, false, err
1125+
}
1126+
1127+
oldCommitBranch, err := oldCommit.GetBranchName()
1128+
if err != nil {
1129+
return nil, false, err
1130+
}
1131+
1132+
if oldCommitBranch == "undefined" {
1133+
commitIDs = make([]string, 2)
1134+
commitIDs[0] = oldCommitID
1135+
commitIDs[1] = newCommitID
1136+
1137+
return commitIDs, true, err
1138+
}
1139+
1140+
newCommit, err := gitRepo.GetCommit(newCommitID)
1141+
if err != nil {
1142+
return nil, false, err
1143+
}
1144+
1145+
var commits *list.List
1146+
commits, err = newCommit.CommitsBeforeUntil(oldCommitID)
1147+
if err != nil {
1148+
return nil, false, err
1149+
}
1150+
1151+
commitIDs = make([]string, 0, commits.Len())
1152+
1153+
for e := commits.Back(); e != nil; e = e.Prev() {
1154+
commit := e.Value.(*git.Commit)
1155+
commitBranch, err := commit.GetBranchName()
1156+
if err != nil {
1157+
return nil, false, err
1158+
}
1159+
1160+
if commitBranch != baseBranch {
1161+
commitIDs = append(commitIDs, commit.ID.String())
1162+
}
1163+
}
1164+
1165+
return
1166+
}

models/repo.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,17 @@ func (repo *Repository) HTMLURL() string {
264264
return setting.AppURL + repo.FullName()
265265
}
266266

267+
// CommitLink make link to by commit full ID
268+
// note: won't check whether it's an right id
269+
func (repo *Repository) CommitLink(commitID string) (result string) {
270+
if commitID == "" || commitID == "0000000000000000000000000000000000000000" {
271+
result = ""
272+
} else {
273+
result = repo.HTMLURL() + "/commit/" + commitID
274+
}
275+
return
276+
}
277+
267278
// APIURL returns the repository API URL
268279
func (repo *Repository) APIURL() string {
269280
return setting.AppURL + path.Join("api/v1/repos", repo.FullName())

modules/git/commit.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -466,15 +466,15 @@ func (c *Commit) GetSubModule(entryname string) (*SubModule, error) {
466466
return nil, nil
467467
}
468468

469-
// GetBranchName gets the closes branch name (as returned by 'git name-rev')
469+
// GetBranchName gets the closes branch name (as returned by 'git name-rev --name-only')
470470
func (c *Commit) GetBranchName() (string, error) {
471-
data, err := NewCommand("name-rev", c.ID.String()).RunInDirBytes(c.repo.Path)
471+
data, err := NewCommand("name-rev", "--name-only", c.ID.String()).RunInDir(c.repo.Path)
472472
if err != nil {
473473
return "", err
474474
}
475475

476-
// name-rev commitID output will be "COMMIT_ID master" or "COMMIT_ID master~12"
477-
return strings.Split(strings.Split(string(data), " ")[1], "~")[0], nil
476+
// name-rev commitID output will be "master" or "master~12"
477+
return strings.SplitN(strings.TrimSpace(data), "~", 2)[0], nil
478478
}
479479

480480
// CommitFileStatus represents status of files in a commit.

modules/git/repo_commit.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -454,3 +454,21 @@ func (repo *Repository) getBranches(commit *Commit, limit int) ([]string, error)
454454
}
455455
return branches, nil
456456
}
457+
458+
// GetCommitsFromIDs get commits from commit IDs
459+
func (repo *Repository) GetCommitsFromIDs(commitIDs []string) (commits *list.List) {
460+
if len(commitIDs) == 0 {
461+
return nil
462+
}
463+
464+
commits = list.New()
465+
466+
for _, commitID := range commitIDs {
467+
commit, err := repo.GetCommit(commitID)
468+
if err == nil && commit != nil {
469+
commits.PushBack(commit)
470+
}
471+
}
472+
473+
return commits
474+
}

modules/notification/base/notifier.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ type Notifier interface {
3636
NotifyPullRequestSynchronized(doer *models.User, pr *models.PullRequest)
3737
NotifyPullRequestReview(*models.PullRequest, *models.Review, *models.Comment)
3838
NotifyPullRequestChangeTargetBranch(doer *models.User, pr *models.PullRequest, oldBranch string)
39+
NotifyPullRequestPushCommits(doer *models.User, pr *models.PullRequest, comment *models.Comment)
3940

4041
NotifyCreateIssueComment(*models.User, *models.Repository,
4142
*models.Issue, *models.Comment)

modules/notification/base/null.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,10 @@ func (*NullNotifier) NotifyPullRequestSynchronized(doer *models.User, pr *models
5454
func (*NullNotifier) NotifyPullRequestChangeTargetBranch(doer *models.User, pr *models.PullRequest, oldBranch string) {
5555
}
5656

57+
// NotifyPullRequestPushCommits notifies when push commits to pull request's head branch
58+
func (*NullNotifier) NotifyPullRequestPushCommits(doer *models.User, pr *models.PullRequest, comment *models.Comment) {
59+
}
60+
5761
// NotifyUpdateComment places a place holder function
5862
func (*NullNotifier) NotifyUpdateComment(doer *models.User, c *models.Comment, oldContent string) {
5963
}

modules/notification/mail/mail.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ func (m *mailNotifier) NotifyCreateIssueComment(doer *models.User, repo *models.
3737
act = models.ActionCommentIssue
3838
} else if comment.Type == models.CommentTypeCode {
3939
act = models.ActionCommentIssue
40+
} else if comment.Type == models.CommentTypePullPush {
41+
act = 0
4042
}
4143

4244
if err := mailer.MailParticipantsComment(comment, act, issue); err != nil {
@@ -117,3 +119,29 @@ func (m *mailNotifier) NotifyMergePullRequest(pr *models.PullRequest, doer *mode
117119
log.Error("MailParticipants: %v", err)
118120
}
119121
}
122+
123+
func (m *mailNotifier) NotifyPullRequestPushCommits(doer *models.User, pr *models.PullRequest, comment *models.Comment) {
124+
var err error
125+
if err = comment.LoadIssue(); err != nil {
126+
log.Error("comment.LoadIssue: %v", err)
127+
return
128+
}
129+
if err = comment.Issue.LoadRepo(); err != nil {
130+
log.Error("comment.Issue.LoadRepo: %v", err)
131+
return
132+
}
133+
if err = comment.Issue.LoadPullRequest(); err != nil {
134+
log.Error("comment.Issue.LoadPullRequest: %v", err)
135+
return
136+
}
137+
if err = comment.Issue.PullRequest.LoadBaseRepo(); err != nil {
138+
log.Error("comment.Issue.PullRequest.LoadBaseRepo: %v", err)
139+
return
140+
}
141+
if err := comment.LoadPushCommits(); err != nil {
142+
log.Error("comment.LoadPushCommits: %v", err)
143+
}
144+
comment.Content = ""
145+
146+
m.NotifyCreateIssueComment(doer, comment.Issue.Repo, comment.Issue, comment)
147+
}

modules/notification/notification.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,13 @@ func NotifyPullRequestChangeTargetBranch(doer *models.User, pr *models.PullReque
9494
}
9595
}
9696

97+
// NotifyPullRequestPushCommits notifies when push commits to pull request's head branch
98+
func NotifyPullRequestPushCommits(doer *models.User, pr *models.PullRequest, comment *models.Comment) {
99+
for _, notifier := range notifiers {
100+
notifier.NotifyPullRequestPushCommits(doer, pr, comment)
101+
}
102+
}
103+
97104
// NotifyUpdateComment notifies update comment to notifiers
98105
func NotifyUpdateComment(doer *models.User, c *models.Comment, oldContent string) {
99106
for _, notifier := range notifiers {

modules/notification/ui/ui.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,15 @@ func (ns *notificationService) NotifyPullRequestReview(pr *models.PullRequest, r
105105
_ = ns.issueQueue.Push(opts)
106106
}
107107

108+
func (ns *notificationService) NotifyPullRequestPushCommits(doer *models.User, pr *models.PullRequest, comment *models.Comment) {
109+
var opts = issueNotificationOpts{
110+
IssueID: pr.IssueID,
111+
NotificationAuthorID: doer.ID,
112+
CommentID: comment.ID,
113+
}
114+
_ = ns.issueQueue.Push(opts)
115+
}
116+
108117
func (ns *notificationService) NotifyIssueChangeAssignee(doer *models.User, issue *models.Issue, assignee *models.User, removed bool, comment *models.Comment) {
109118
if !removed {
110119
var opts = issueNotificationOpts{

options/locale/locale_en-US.ini

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1029,6 +1029,9 @@ issues.due_date = Due Date
10291029
issues.invalid_due_date_format = "Due date format must be 'yyyy-mm-dd'."
10301030
issues.error_modifying_due_date = "Failed to modify the due date."
10311031
issues.error_removing_due_date = "Failed to remove the due date."
1032+
issues.push_commit_1 = "added %d commit %s"
1033+
issues.push_commits_n = "added %d commits %s"
1034+
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`
10321035
issues.due_date_form = "yyyy-mm-dd"
10331036
issues.due_date_form_add = "Add due date"
10341037
issues.due_date_form_edit = "Edit"

routers/repo/issue.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -999,6 +999,12 @@ func ViewIssue(ctx *context.Context) {
999999
ctx.ServerError("LoadResolveDoer", err)
10001000
return
10011001
}
1002+
} else if comment.Type == models.CommentTypePullPush {
1003+
participants = addParticipant(comment.Poster, participants)
1004+
if err = comment.LoadPushCommits(); err != nil {
1005+
ctx.ServerError("LoadPushCommits", err)
1006+
return
1007+
}
10021008
}
10031009
}
10041010

services/mailer/mail.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -319,6 +319,8 @@ func actionToTemplate(issue *models.Issue, actionType models.ActionType,
319319
name = "code"
320320
case models.CommentTypeAssignees:
321321
name = "assigned"
322+
case models.CommentTypePullPush:
323+
name = "push"
322324
default:
323325
name = "default"
324326
}

0 commit comments

Comments
 (0)