Skip to content

Commit f985e67

Browse files
committed
Adds functionality to change target branch of created pull requests
Signed-off-by: Mario Lubenka <[email protected]>
1 parent bf5af87 commit f985e67

File tree

11 files changed

+240
-18
lines changed

11 files changed

+240
-18
lines changed

models/issue_comment.go

+19
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,8 @@ const (
8484
CommentTypeLock
8585
// Unlocks a previously locked issue
8686
CommentTypeUnlock
87+
// Change pull request's target branch
88+
CommentTypeChangeTargetBranch
8789
)
8890

8991
// CommentTag defines comment tag type
@@ -116,6 +118,8 @@ type Comment struct {
116118
Assignee *User `xorm:"-"`
117119
OldTitle string
118120
NewTitle string
121+
OldBranch string
122+
NewBranch string
119123
DependentIssueID int64
120124
DependentIssue *Issue `xorm:"-"`
121125

@@ -525,6 +529,8 @@ func createComment(e *xorm.Session, opts *CreateCommentOptions) (_ *Comment, err
525529
Content: opts.Content,
526530
OldTitle: opts.OldTitle,
527531
NewTitle: opts.NewTitle,
532+
OldBranch: opts.OldBranch,
533+
NewBranch: opts.NewBranch,
528534
DependentIssueID: opts.DependentIssueID,
529535
TreePath: opts.TreePath,
530536
ReviewID: opts.ReviewID,
@@ -737,6 +743,17 @@ func createChangeTitleComment(e *xorm.Session, doer *User, repo *Repository, iss
737743
})
738744
}
739745

746+
func createChangePullRequestTargetBranchComment(e *xorm.Session, doer *User, repo *Repository, issue *Issue, oldBranch string, newBranch string) (*Comment, error) {
747+
return createComment(e, &CreateCommentOptions{
748+
Type: CommentTypeChangeTargetBranch,
749+
Doer: doer,
750+
Repo: repo,
751+
Issue: issue,
752+
OldBranch: oldBranch,
753+
NewBranch: newBranch,
754+
})
755+
}
756+
740757
func createDeleteBranchComment(e *xorm.Session, doer *User, repo *Repository, issue *Issue, branchName string) (*Comment, error) {
741758
return createComment(e, &CreateCommentOptions{
742759
Type: CommentTypeDeleteBranch,
@@ -798,6 +815,8 @@ type CreateCommentOptions struct {
798815
RemovedAssignee bool
799816
OldTitle string
800817
NewTitle string
818+
OldBranch string
819+
NewBranch string
801820
CommitID int64
802821
CommitSHA string
803822
Patch string

models/migrations/migrations.go

+2
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,8 @@ var migrations = []Migration{
221221
NewMigration("hot fix for wrong release sha1 on release table", fixReleaseSha1OnReleaseTable),
222222
// v83 -> v84
223223
NewMigration("add uploader id for table attachment", addUploaderIDForAttachment),
224+
// v84 -> v85
225+
NewMigration("new feature: change target branch of pull requests", featureChangeTargetBranch),
224226
}
225227

226228
// Migrate database to current version

models/migrations/v84.go

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// Copyright 2019 The Gitea Authors. All rights reserved.
2+
// Use of this source code is governed by a MIT-style
3+
// license that can be found in the LICENSE file.
4+
5+
package migrations
6+
7+
import (
8+
"fmt"
9+
"github.com/go-xorm/xorm"
10+
)
11+
12+
func featureChangeTargetBranch(x *xorm.Engine) error {
13+
type Comment struct {
14+
OldBranch string
15+
NewBranch string
16+
}
17+
18+
if err := x.Sync2(new(Comment)); err != nil {
19+
return fmt.Errorf("Sync2: %v", err)
20+
}
21+
return nil
22+
}

models/pull.go

+44
Original file line numberDiff line numberDiff line change
@@ -1410,6 +1410,50 @@ func ChangeUsernameInPullRequests(oldUserName, newUserName string) error {
14101410
return err
14111411
}
14121412

1413+
// ChangeTargetBranch changes the target branch of this pull request, as the given user.
1414+
func (pr *PullRequest) ChangeTargetBranch(doer *User, targetBranch string) (err error) {
1415+
oldBranch := pr.BaseBranch
1416+
if oldBranch == targetBranch {
1417+
return nil
1418+
}
1419+
pr.BaseBranch = targetBranch
1420+
sess := x.NewSession()
1421+
defer sess.Close()
1422+
1423+
if err = sess.Begin(); err != nil {
1424+
return err
1425+
}
1426+
1427+
if _, err := sess.ID(pr.ID).Cols("base_branch").Update(pr); err != nil {
1428+
return fmt.Errorf("update pull request: %v", err)
1429+
}
1430+
1431+
issue := pr.Issue
1432+
1433+
if _, err = createChangePullRequestTargetBranchComment(sess, doer, issue.Repo, issue, oldBranch, targetBranch); err != nil {
1434+
return fmt.Errorf("createChangePullRequestTargetBranchComment: %v", err)
1435+
}
1436+
1437+
if err = sess.Commit(); err != nil {
1438+
return err
1439+
}
1440+
1441+
mode, _ := AccessLevel(issue.Poster, issue.Repo)
1442+
err = PrepareWebhooks(issue.Repo, HookEventPullRequest, &api.PullRequestPayload{
1443+
Action: api.HookIssueEdited,
1444+
Index: issue.Index,
1445+
PullRequest: pr.APIFormat(),
1446+
Repository: issue.Repo.APIFormat(mode),
1447+
Sender: doer.APIFormat(),
1448+
})
1449+
if err != nil {
1450+
log.Error("PrepareWebhooks [is_pull: true]: %v", err)
1451+
} else {
1452+
go HookQueue.Add(issue.RepoID)
1453+
}
1454+
return nil
1455+
}
1456+
14131457
// checkAndUpdateStatus checks if pull request is possible to leaving checking status,
14141458
// and set to be either conflict or mergeable.
14151459
func (pr *PullRequest) checkAndUpdateStatus() {

options/locale/locale_en-US.ini

+2-1
Original file line numberDiff line numberDiff line change
@@ -920,8 +920,9 @@ pulls.no_results = No results found.
920920
pulls.nothing_to_compare = These branches are equal. There is no need to create a pull request.
921921
pulls.has_pull_request = `A pull request between these branches already exists: <a href="%[1]s/pulls/%[3]d">%[2]s#%[3]d</a>`
922922
pulls.create = Create Pull Request
923-
pulls.title_desc = wants to merge %[1]d commits from <code>%[2]s</code> into <code>%[3]s</code>
923+
pulls.title_desc = wants to merge %[1]d commits from <code>%[2]s</code> into <code id="branch_target">%[3]s</code>
924924
pulls.merged_title_desc = merged %[1]d commits from <code>%[2]s</code> into <code>%[3]s</code> %[4]s
925+
pulls.change_target_branch_at = `changed target branch from <b>%s</b> to <b>%s</b> %s`
925926
pulls.tab_conversation = Conversation
926927
pulls.tab_commits = Commits
927928
pulls.tab_files = Files Changed

public/js/index.js

+47-13
Original file line numberDiff line numberDiff line change
@@ -786,28 +786,62 @@ function initRepository() {
786786
$issueTitle.toggle();
787787
$('.not-in-edit').toggle();
788788
$('#edit-title-input').toggle();
789+
$('#pull-desc').toggle();
790+
$('#pull-desc-edit').toggle();
789791
$('.in-edit').toggle();
790792
$editInput.focus();
791793
return false;
792794
};
795+
796+
var changeBranchSelect = function () {
797+
var selectionTextField = $('#pull-target-branch');
798+
799+
var baseName = selectionTextField.data('basename');
800+
var branchNameNew = $(this).data('branch');
801+
var branchNameOld = selectionTextField.data('branch');
802+
803+
// Replace branch name to keep translation from HTML template
804+
selectionTextField.html(selectionTextField.html().replace(baseName + ':' + branchNameOld, baseName + ':' + branchNameNew));
805+
selectionTextField.data('branch', branchNameNew); // update branch name in setting
806+
};
807+
$('#branch-select > .item').click(changeBranchSelect);
808+
793809
$('#edit-title').click(editTitleToggle);
794810
$('#cancel-edit-title').click(editTitleToggle);
795811
$('#save-edit-title').click(editTitleToggle).click(function () {
796-
if ($editInput.val().length == 0 ||
797-
$editInput.val() == $issueTitle.text()) {
812+
813+
var pullrequest_targetbranch_change = function(update_url) {
814+
var target_branch = $('#pull-target-branch').data('branch');
815+
if (target_branch === $('#branch_target').text()) {
816+
$editInput.val($issueTitle.text());
817+
return false;
818+
}
819+
$.post(update_url, {
820+
"_csrf": csrf,
821+
"target_branch": target_branch
822+
},
823+
function (data) {
824+
$('#branch_target').text(data.base_branch);
825+
reload();
826+
});
827+
};
828+
829+
var pullrequest_target_update_url = $(this).data('target-update-url');
830+
if ($editInput.val().length === 0 ||
831+
$editInput.val() === $issueTitle.text()) {
798832
$editInput.val($issueTitle.text());
799-
return false;
833+
pullrequest_targetbranch_change(pullrequest_target_update_url);
834+
} else {
835+
$.post($(this).data('update-url'), {
836+
"_csrf": csrf,
837+
"title": $editInput.val()
838+
},
839+
function (data) {
840+
$editInput.val(data.title);
841+
$issueTitle.text(data.title);
842+
pullrequest_targetbranch_change(pullrequest_target_update_url);
843+
});
800844
}
801-
802-
$.post($(this).data('update-url'), {
803-
"_csrf": csrf,
804-
"title": $editInput.val()
805-
},
806-
function (data) {
807-
$editInput.val(data.title);
808-
$issueTitle.text(data.title);
809-
reload();
810-
});
811845
return false;
812846
});
813847

routers/repo/issue.go

+23
Original file line numberDiff line numberDiff line change
@@ -589,6 +589,28 @@ func commentTag(repo *models.Repository, poster *models.User, issue *models.Issu
589589
return models.CommentTagNone, nil
590590
}
591591

592+
func getBranchData(ctx *context.Context, issue *models.Issue) {
593+
ctx.Data["BaseBranch"] = nil
594+
ctx.Data["HeadBranch"] = nil
595+
ctx.Data["HeadUserName"] = nil
596+
ctx.Data["BaseName"] = ctx.Repo.Repository.OwnerName
597+
if issue.IsPull {
598+
pull := issue.PullRequest
599+
ctx.Data["BaseBranch"] = pull.BaseBranch
600+
ctx.Data["HeadBranch"] = pull.HeadBranch
601+
ctx.Data["HeadUserName"] = pull.HeadUserName
602+
603+
headUser, err := models.GetUserByName(pull.HeadUserName)
604+
if headUser.ID == ctx.Repo.Owner.ID {
605+
ctx.Data["BaseName"] = headUser.Name
606+
}
607+
if err != nil {
608+
ctx.ServerError("getBranchData", err)
609+
return
610+
}
611+
}
612+
}
613+
592614
// ViewIssue render issue view page
593615
func ViewIssue(ctx *context.Context) {
594616
issue, err := models.GetIssueByIndex(ctx.Repo.Repository.ID, ctx.ParamsInt64(":index"))
@@ -848,6 +870,7 @@ func ViewIssue(ctx *context.Context) {
848870
}
849871
}
850872

873+
getBranchData(ctx, issue)
851874
if issue.IsPull {
852875
pull := issue.PullRequest
853876
pull.Issue = issue

routers/repo/pull.go

+34
Original file line numberDiff line numberDiff line change
@@ -396,6 +396,7 @@ func ViewPullCommits(ctx *context.Context) {
396396
ctx.Data["Commits"] = commits
397397
ctx.Data["CommitCount"] = commits.Len()
398398

399+
getBranchData(ctx, issue)
399400
ctx.HTML(200, tplPullCommits)
400401
}
401402

@@ -519,6 +520,7 @@ func ViewPullFiles(ctx *context.Context) {
519520
ctx.ServerError("GetCurrentReview", err)
520521
return
521522
}
523+
getBranchData(ctx, issue)
522524
ctx.HTML(200, tplPullFiles)
523525
}
524526

@@ -1184,3 +1186,35 @@ func DownloadPullPatch(ctx *context.Context) {
11841186
return
11851187
}
11861188
}
1189+
1190+
// UpdatePullRequestTarget change pull request's target branch
1191+
func UpdatePullRequestTarget(ctx *context.Context) {
1192+
issue := GetActionIssue(ctx)
1193+
pr := issue.PullRequest
1194+
if !issue.IsPull {
1195+
return
1196+
}
1197+
if ctx.Written() {
1198+
return
1199+
}
1200+
1201+
if !ctx.IsSigned || (!issue.IsPoster(ctx.User.ID) && !ctx.Repo.CanWriteIssuesOrPulls(issue.IsPull)) {
1202+
ctx.Error(403)
1203+
return
1204+
}
1205+
1206+
targetBranch := ctx.QueryTrim("target_branch")
1207+
if len(targetBranch) == 0 {
1208+
ctx.Error(204)
1209+
return
1210+
}
1211+
1212+
if err := pr.ChangeTargetBranch(ctx.User, targetBranch); err != nil {
1213+
ctx.ServerError("UpdatePullRequestTarget", err)
1214+
return
1215+
}
1216+
1217+
ctx.JSON(200, map[string]interface{}{
1218+
"base_branch": pr.BaseBranch,
1219+
})
1220+
}

routers/routes/routes.go

+5
Original file line numberDiff line numberDiff line change
@@ -698,6 +698,11 @@ func RegisterRoutes(m *macaron.Macaron) {
698698
m.Combo("/compare/*", context.RepoMustNotBeArchived(), reqRepoCodeReader, reqRepoPullsReader, repo.MustAllowPulls, repo.SetEditorconfigIfExists).
699699
Get(repo.SetDiffViewStyle, repo.CompareAndPullRequest).
700700
Post(bindIgnErr(auth.CreateIssueForm{}), repo.CompareAndPullRequestPost)
701+
m.Group("/pull", func() {
702+
m.Group("/:index", func() {
703+
m.Post("/target_branch", repo.UpdatePullRequestTarget)
704+
}, context.RepoMustNotBeArchived())
705+
}, context.RepoMustNotBeArchived())
701706

702707
m.Group("", func() {
703708
m.Group("", func() {

templates/repo/issue/view_content/comments.tmpl

+11-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
5 = COMMENT_REF, 6 = PULL_REF, 7 = COMMENT_LABEL, 12 = START_TRACKING,
77
13 = STOP_TRACKING, 14 = ADD_TIME_MANUAL, 16 = ADDED_DEADLINE, 17 = MODIFIED_DEADLINE,
88
18 = REMOVED_DEADLINE, 19 = ADD_DEPENDENCY, 20 = REMOVE_DEPENDENCY, 21 = CODE,
9-
22 = REVIEW, 23 = ISSUE_LOCKED, 24 = ISSUE_UNLOCKED -->
9+
22 = REVIEW, 23 = ISSUE_LOCKED, 24 = ISSUE_UNLOCKED, 25 = TARGET_BRANCH_CHANGED -->
1010
{{if eq .Type 0}}
1111
<div class="comment" id="{{.HashTag}}">
1212
<a class="avatar" {{if gt .Poster.ID 0}}href="{{.Poster.HomeLink}}"{{end}}>
@@ -389,5 +389,15 @@
389389
{{$.i18n.Tr "repo.issues.unlock_comment" $createdStr | Safe}}
390390
</span>
391391
</div>
392+
{{else if eq .Type 25}}
393+
<div class="event">
394+
<span class="octicon octicon-primitive-dot"></span>
395+
<a class="ui avatar image" href="{{.Poster.HomeLink}}">
396+
<img src="{{.Poster.RelAvatarLink}}">
397+
</a>
398+
<span class="text grey"><a href="{{.Poster.HomeLink}}">{{.Poster.Name}}</a>
399+
{{$.i18n.Tr "repo.pulls.change_target_branch_at" (.OldBranch|Escape) (.NewBranch|Escape) $createdStr | Safe}}
400+
</span>
401+
</div>
392402
{{end}}
393403
{{end}}

templates/repo/issue/view_title.tmpl

+31-3
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
<div class="edit-zone text right">
1212
<div id="edit-title" class="ui basic green not-in-edit button">{{.i18n.Tr "repo.issues.edit"}}</div>
1313
<div id="cancel-edit-title" class="ui basic blue in-edit button" style="display: none">{{.i18n.Tr "repo.issues.cancel"}}</div>
14-
<div id="save-edit-title" class="ui green in-edit button" style="display: none" data-update-url="{{$.RepoLink}}/issues/{{.Issue.Index}}/title">{{.i18n.Tr "repo.issues.save"}}</div>
14+
<div id="save-edit-title" class="ui green in-edit button" style="display: none" data-update-url="{{$.RepoLink}}/issues/{{.Issue.Index}}/title" {{if .Issue.IsPull}}data-target-update-url="{{$.RepoLink}}/pull/{{.Issue.Index}}/target_branch"{{end}}>{{.i18n.Tr "repo.issues.save"}}</div>
1515
</div>
1616
</div>
1717
{{end}}
@@ -30,8 +30,36 @@
3030
<a {{if gt .Issue.PullRequest.Merger.ID 0}}href="{{.Issue.PullRequest.Merger.HomeLink}}"{{end}}>{{.Issue.PullRequest.Merger.Name}}</a>
3131
<span class="pull-desc">{{$.i18n.Tr "repo.pulls.merged_title_desc" .NumCommits .HeadTarget .BaseTarget $mergedStr | Str2html}}</span>
3232
{{else}}
33-
<a {{if gt .Issue.Poster.ID 0}}href="{{.Issue.Poster.HomeLink}}"{{end}}>{{.Issue.Poster.Name}}</a>
34-
<span class="pull-desc">{{$.i18n.Tr "repo.pulls.title_desc" .NumCommits .HeadTarget .BaseTarget | Str2html}}</span>
33+
<span id="pull-desc" class="pull-desc"><a {{if gt .Issue.Poster.ID 0}}href="{{.Issue.Poster.HomeLink}}"{{end}}>{{.Issue.Poster.Name}}</a> {{$.i18n.Tr "repo.pulls.title_desc" .NumCommits .HeadTarget .BaseTarget | Str2html}}</span>
34+
<span id="pull-desc-edit" style="display: none">
35+
<div class="ui floating filter dropdown" data-no-results="{{.i18n.Tr "repo.pulls.no_results"}}">
36+
<div class="ui basic small button">
37+
<span class="text" id="pull-target-branch" data-basename="{{$.BaseName}}" data-branch="{{$.BaseBranch}}">{{.i18n.Tr "repo.pulls.compare_base"}}: {{$.BaseName}}:{{$.BaseBranch}}</span>
38+
<i class="dropdown icon"></i>
39+
</div>
40+
<div class="menu">
41+
<div class="ui icon search input">
42+
<i class="filter icon"></i>
43+
<input name="search" placeholder="{{.i18n.Tr "repo.pulls.filter_branch"}}...">
44+
</div>
45+
<div class="scrolling menu" id="branch-select">
46+
{{range .Branches}}
47+
{{ $sameBase := ne $.BaseName $.HeadUserName }}
48+
{{ $differentBranch := ne . $.HeadBranch }}
49+
{{ if or $sameBase $differentBranch }}
50+
<div class="item {{if eq $.BaseBranch .}}selected{{end}}" data-branch="{{.}}">{{$.BaseName}}{{if $.HeadRepo}}/{{$.HeadRepo}}{{end}}:{{.}}</div>
51+
{{ end }}
52+
{{end}}
53+
</div>
54+
</div>
55+
</div>
56+
...
57+
<div class="ui floating filter dropdown">
58+
<div class="ui basic small button">
59+
<span class="text">{{.i18n.Tr "repo.pulls.compare_compare"}}: {{$.HeadTarget}}</span>
60+
</div>
61+
</div>
62+
</span>
3563
{{end}}
3664
{{else}}
3765
{{ $createdStr:= TimeSinceUnix .Issue.CreatedUnix $.Lang }}

0 commit comments

Comments
 (0)