mirror of
https://github.com/gitbucket/gitbucket.git
synced 2025-11-02 19:45:57 +01:00
(refs #2)Create pull request is available.
This commit is contained in:
@@ -1,13 +1,13 @@
|
|||||||
CREATE TABLE PULL_REQUEST(
|
CREATE TABLE PULL_REQUEST(
|
||||||
PULL_REQUEST_ID INT AUTO_INCREMENT,
|
|
||||||
USER_NAME VARCHAR(100) NOT NULL,
|
USER_NAME VARCHAR(100) NOT NULL,
|
||||||
REPOSITORY_NAME VARCHAR(100) NOT NULL,
|
REPOSITORY_NAME VARCHAR(100) NOT NULL,
|
||||||
ISSUE_ID INT NOT NULL,
|
ISSUE_ID INT NOT NULL,
|
||||||
|
ORIGIN_BRANCH VARCHAR(100) NOT NULL,
|
||||||
REQUEST_USER_NAME VARCHAR(100) NOT NULL,
|
REQUEST_USER_NAME VARCHAR(100) NOT NULL,
|
||||||
REQUEST_REPOSITORY_NAME VARCHAR(100) NOT NULL,
|
REQUEST_REPOSITORY_NAME VARCHAR(100) NOT NULL,
|
||||||
REQUEST_COMMIT_ID VARCHAR(40) NOT NULL
|
REQUEST_COMMIT_ID VARCHAR(40) NOT NULL
|
||||||
);
|
);
|
||||||
|
|
||||||
ALTER TABLE PULL_REQUEST ADD CONSTRAINT IDX_PULL_REQUEST_PK PRIMARY KEY (PULL_REQUEST_ID);
|
ALTER TABLE PULL_REQUEST ADD CONSTRAINT IDX_PULL_REQUEST_PK PRIMARY KEY (USER_NAME, REPOSITORY_NAME, ISSUE_ID);
|
||||||
ALTER TABLE PULL_REQUEST ADD CONSTRAINT IDX_PULL_REQUEST_FK0 FOREIGN KEY (USER_NAME, REPOSITORY_NAME, ISSUE_ID) REFERENCES ISSUE (USER_NAME, REPOSITORY_NAME, ISSUE_ID);
|
ALTER TABLE PULL_REQUEST ADD CONSTRAINT IDX_PULL_REQUEST_FK0 FOREIGN KEY (USER_NAME, REPOSITORY_NAME, ISSUE_ID) REFERENCES ISSUE (USER_NAME, REPOSITORY_NAME, ISSUE_ID);
|
||||||
ALTER TABLE PULL_REQUEST ADD CONSTRAINT IDX_PULL_REQUEST_FK1 FOREIGN KEY (REQUEST_USER_NAME, REQUEST_REPOSITORY_NAME) REFERENCES REPOSITORY (USER_NAME, REPOSITORY_NAME);
|
ALTER TABLE PULL_REQUEST ADD CONSTRAINT IDX_PULL_REQUEST_FK1 FOREIGN KEY (REQUEST_USER_NAME, REQUEST_REPOSITORY_NAME) REFERENCES REPOSITORY (USER_NAME, REPOSITORY_NAME);
|
||||||
|
|||||||
@@ -80,7 +80,9 @@ trait CreateRepositoryControllerBase extends ControllerBase {
|
|||||||
|
|
||||||
val git = Git.open(tmpdir)
|
val git = Git.open(tmpdir)
|
||||||
git.add.addFilepattern("README.md").call
|
git.add.addFilepattern("README.md").call
|
||||||
git.commit.setMessage("Initial commit").call
|
git.commit
|
||||||
|
.setCommitter(new PersonIdent(loginUserName, loginAccount.mailAddress))
|
||||||
|
.setMessage("Initial commit").call
|
||||||
git.push.call
|
git.push.call
|
||||||
|
|
||||||
} finally {
|
} finally {
|
||||||
|
|||||||
@@ -1,26 +1,42 @@
|
|||||||
package app
|
package app
|
||||||
|
|
||||||
import util.{FileUtil, JGitUtil, ReferrerAuthenticator}
|
import util.{CollaboratorsAuthenticator, FileUtil, JGitUtil, ReferrerAuthenticator}
|
||||||
import util.Directory._
|
import util.Directory._
|
||||||
import service._
|
import service._
|
||||||
import org.eclipse.jgit.treewalk.CanonicalTreeParser
|
import org.eclipse.jgit.treewalk.CanonicalTreeParser
|
||||||
import util.JGitUtil.{DiffInfo, CommitInfo}
|
import util.JGitUtil.{DiffInfo, CommitInfo}
|
||||||
import scala.collection.mutable.ArrayBuffer
|
import scala.collection.mutable.ArrayBuffer
|
||||||
import org.eclipse.jgit.api.Git
|
import org.eclipse.jgit.api.Git
|
||||||
import org.eclipse.jgit.lib.ObjectId
|
import jp.sf.amateras.scalatra.forms._
|
||||||
|
import util.JGitUtil.DiffInfo
|
||||||
|
import scala.Some
|
||||||
|
import util.JGitUtil.CommitInfo
|
||||||
|
|
||||||
class PullRequestsController extends PullRequestsControllerBase
|
class PullRequestsController extends PullRequestsControllerBase
|
||||||
with RepositoryService with AccountService with ReferrerAuthenticator
|
with RepositoryService with AccountService with IssuesService with PullRequestService
|
||||||
|
with ReferrerAuthenticator with CollaboratorsAuthenticator
|
||||||
|
|
||||||
trait PullRequestsControllerBase extends ControllerBase {
|
trait PullRequestsControllerBase extends ControllerBase {
|
||||||
self: ReferrerAuthenticator with RepositoryService =>
|
self: ReferrerAuthenticator with RepositoryService with IssuesService
|
||||||
|
with PullRequestService with CollaboratorsAuthenticator =>
|
||||||
|
|
||||||
|
val form = mapping(
|
||||||
|
"title" -> trim(label("Title" , text(required, maxlength(100)))),
|
||||||
|
"content" -> trim(label("Content", optional(text()))),
|
||||||
|
"branch" -> trim(text(required, maxlength(100))),
|
||||||
|
"requestUserName" -> trim(text(required, maxlength(100))),
|
||||||
|
"requestCommitId" -> trim(text(required, maxlength(40)))
|
||||||
|
)(PullRequestForm.apply)
|
||||||
|
|
||||||
|
case class PullRequestForm(title: String, content: Option[String], branch: String,
|
||||||
|
requestUserName: String, requestCommitId: String)
|
||||||
|
|
||||||
get("/:owner/:repository/pulls")(referrersOnly { repository =>
|
get("/:owner/:repository/pulls")(referrersOnly { repository =>
|
||||||
pulls.html.list(repository)
|
pulls.html.list(repository)
|
||||||
})
|
})
|
||||||
|
|
||||||
// TODO Replace correct authenticator
|
// TODO Replace correct authenticator
|
||||||
get("/:owner/:repository/pulls/compare")(referrersOnly { newRepo =>
|
get("/:owner/:repository/pulls/compare")(collaboratorsOnly { newRepo =>
|
||||||
(newRepo.repository.originUserName, newRepo.repository.originRepositoryName) match {
|
(newRepo.repository.originUserName, newRepo.repository.originRepositoryName) match {
|
||||||
case (None,_)|(_, None) => NotFound // TODO BadRequest?
|
case (None,_)|(_, None) => NotFound // TODO BadRequest?
|
||||||
case (Some(originUserName), Some(originRepositoryName)) => {
|
case (Some(originUserName), Some(originRepositoryName)) => {
|
||||||
@@ -40,7 +56,7 @@ trait PullRequestsControllerBase extends ControllerBase {
|
|||||||
})
|
})
|
||||||
|
|
||||||
// TODO Replace correct authenticator
|
// TODO Replace correct authenticator
|
||||||
get("/:owner/:repository/pulls/compare/*:*...*")(referrersOnly { repository =>
|
get("/:owner/:repository/pulls/compare/*:*...*")(collaboratorsOnly { repository =>
|
||||||
if(repository.repository.originUserName.isEmpty || repository.repository.originRepositoryName.isEmpty){
|
if(repository.repository.originUserName.isEmpty || repository.repository.originRepositoryName.isEmpty){
|
||||||
NotFound // TODO BadRequest?
|
NotFound // TODO BadRequest?
|
||||||
} else {
|
} else {
|
||||||
@@ -94,6 +110,29 @@ trait PullRequestsControllerBase extends ControllerBase {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
post("/:owner/:repository/pulls/new", form)(referrersOnly { (form, repository) =>
|
||||||
|
val loginUserName = context.loginAccount.get.userName
|
||||||
|
|
||||||
|
val issueId = createIssue(
|
||||||
|
repository.owner,
|
||||||
|
repository.name,
|
||||||
|
loginUserName,
|
||||||
|
form.title,
|
||||||
|
form.content,
|
||||||
|
None, None)
|
||||||
|
|
||||||
|
createPullRequest(
|
||||||
|
repository.owner,
|
||||||
|
repository.name,
|
||||||
|
issueId,
|
||||||
|
form.branch,
|
||||||
|
form.requestUserName,
|
||||||
|
repository.name,
|
||||||
|
form.requestCommitId)
|
||||||
|
|
||||||
|
redirect(s"/${repository.owner}/${repository.name}/pulls/${issueId}")
|
||||||
|
})
|
||||||
|
|
||||||
private def withGit[T](oldDir: java.io.File, newDir: java.io.File)(action: (Git, Git) => T): T = {
|
private def withGit[T](oldDir: java.io.File, newDir: java.io.File)(action: (Git, Git) => T): T = {
|
||||||
val oldGit = Git.open(oldDir)
|
val oldGit = Git.open(oldDir)
|
||||||
val newGit = Git.open(newDir)
|
val newGit = Git.open(newDir)
|
||||||
|
|||||||
@@ -3,21 +3,21 @@ package model
|
|||||||
import scala.slick.driver.H2Driver.simple._
|
import scala.slick.driver.H2Driver.simple._
|
||||||
|
|
||||||
object PullRequests extends Table[PullRequest]("PULL_REQUEST") with IssueTemplate {
|
object PullRequests extends Table[PullRequest]("PULL_REQUEST") with IssueTemplate {
|
||||||
def pullRequestId = column[Int]("PULL_REQUEST_ID")
|
|
||||||
def requestUserName = column[String]("REQUEST_USER_NAME")
|
def requestUserName = column[String]("REQUEST_USER_NAME")
|
||||||
def requestRepositoryName = column[String]("REQUEST_REPOSITORY_NAME")
|
def requestRepositoryName = column[String]("REQUEST_REPOSITORY_NAME")
|
||||||
def requestCommitId = column[String]("REQUEST_COMMIT_ID")
|
def requestCommitId = column[String]("REQUEST_COMMIT_ID")
|
||||||
def * = pullRequestId ~ userName ~ repositoryName ~ issueId ~ requestUserName ~ requestRepositoryName ~ requestCommitId <> (PullRequest, PullRequest.unapply _)
|
def originBranch = column[String]("ORIGIN_BRANCH")
|
||||||
|
def * = userName ~ repositoryName ~ issueId ~ originBranch ~ requestUserName ~ requestRepositoryName ~ requestCommitId <> (PullRequest, PullRequest.unapply _)
|
||||||
|
|
||||||
def autoinc = userName ~ repositoryName ~ issueId ~ requestUserName ~ requestRepositoryName ~ requestCommitId returning pullRequestId
|
def byPrimaryKey(userName: String, repositoryName: String, issueId: Int) = byIssue(userName, repositoryName, issueId)
|
||||||
def byPrimaryKey(pullRequestId: Int) = this.pullRequestId is pullRequestId.bind
|
def byPrimaryKey(userName: Column[String], repositoryName: Column[String], issueId: Column[Int]) = byIssue(userName, repositoryName, issueId)
|
||||||
}
|
}
|
||||||
|
|
||||||
case class PullRequest(
|
case class PullRequest(
|
||||||
pullRequestId: Int,
|
|
||||||
userName: String,
|
userName: String,
|
||||||
repositoryName: String,
|
repositoryName: String,
|
||||||
issueId: Int,
|
issueId: Int,
|
||||||
|
originBranch: String,
|
||||||
requestUserName: String,
|
requestUserName: String,
|
||||||
requestRepositoryName: String,
|
requestRepositoryName: String,
|
||||||
requestCommitId: String)
|
requestCommitId: String)
|
||||||
25
src/main/scala/service/PullRequestService.scala
Normal file
25
src/main/scala/service/PullRequestService.scala
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
package service
|
||||||
|
|
||||||
|
import scala.slick.driver.H2Driver.simple._
|
||||||
|
import Database.threadLocalSession
|
||||||
|
|
||||||
|
import model._
|
||||||
|
|
||||||
|
//import scala.slick.jdbc.{StaticQuery => Q}
|
||||||
|
//import Q.interpolation
|
||||||
|
|
||||||
|
|
||||||
|
trait PullRequestService {
|
||||||
|
|
||||||
|
def createPullRequest(originUserName: String, originRepositoryName: String, issueId: Int,
|
||||||
|
originBranch: String, requestUserName: String, requestRepositoryName: String, requestCommitId: String): Unit =
|
||||||
|
PullRequests insert (PullRequest(
|
||||||
|
originUserName,
|
||||||
|
originRepositoryName,
|
||||||
|
issueId,
|
||||||
|
originBranch,
|
||||||
|
requestUserName,
|
||||||
|
requestRepositoryName,
|
||||||
|
requestCommitId))
|
||||||
|
|
||||||
|
}
|
||||||
@@ -49,6 +49,7 @@ object AutoUpdate {
|
|||||||
* The history of versions. A head of this sequence is the current BitBucket version.
|
* The history of versions. A head of this sequence is the current BitBucket version.
|
||||||
*/
|
*/
|
||||||
val versions = Seq(
|
val versions = Seq(
|
||||||
|
Version(1, 4),
|
||||||
new Version(1, 3){
|
new Version(1, 3){
|
||||||
override def update(conn: Connection): Unit = {
|
override def update(conn: Connection): Unit = {
|
||||||
super.update(conn)
|
super.update(conn)
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
<a href="#" id="edit-compare-condition" class="btn btn-mini pull-right">Edit</a>
|
<a href="#" id="edit-compare-condition" class="btn btn-mini pull-right">Edit</a>
|
||||||
<span class="label label-info monospace">@origin:@originId</span> ... <span class="label label-info monospace">@repository.owner:@forkedId</span>
|
<span class="label label-info monospace">@origin:@originId</span> ... <span class="label label-info monospace">@repository.owner:@forkedId</span>
|
||||||
</div>
|
</div>
|
||||||
<div id="compare-edit" style="display: none;">
|
<div id="compare-edit" style="display: none; width: 620px;">
|
||||||
<a href="#" id="refresh-compare" class="pull-right"><i class="icon-remove-circle"></i></a>
|
<a href="#" id="refresh-compare" class="pull-right"><i class="icon-remove-circle"></i></a>
|
||||||
<span class="label label-info monospace">@origin/@repository.name:</span>
|
<span class="label label-info monospace">@origin/@repository.name:</span>
|
||||||
@helper.html.dropdown(originId) {
|
@helper.html.dropdown(originId) {
|
||||||
@@ -29,57 +29,81 @@
|
|||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div style="margin-bottom: 10px;">
|
@if(commits.nonEmpty){
|
||||||
<a href="#" class="btn">Click to create a pull request for this comparison</a>
|
<div style="margin-bottom: 10px;" id="create-pull-request">
|
||||||
</div>
|
<a href="#" class="btn" id="show-form">Click to create a pull request for this comparison</a>
|
||||||
<div class="box">
|
|
||||||
<table class="table table-file-list" style="border: 1px solid silver;">
|
|
||||||
@commits.map { day =>
|
|
||||||
<tr>
|
|
||||||
<th colspan="3" class="box-header" style="font-weight: normal;">@date(day.head.time)</th>
|
|
||||||
</tr>
|
|
||||||
@day.map { commit =>
|
|
||||||
<tr>
|
|
||||||
<td style="width: 20%;">
|
|
||||||
@avatar(commit.committer, 20)
|
|
||||||
<a href="@url(commit.committer)" class="username">@commit.committer</a>
|
|
||||||
</td>
|
|
||||||
<td>@commit.shortMessage</td>
|
|
||||||
<td style="width: 10%; text-align: right;">
|
|
||||||
<a href="@url(repository)/commit/@commit.id" class="monospace">@commit.id.substring(0, 7)</a>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<div class="pull-right" style="margin-bottom: 10px;">
|
|
||||||
<input id="toggle-file-list" type="button" class="btn" value="Show file list"/>
|
|
||||||
</div>
|
</div>
|
||||||
Showing @diffs.size changed @plural(diffs.size, "file")
|
<div id="pull-request-form" style="display: none; width: 600px;">
|
||||||
</div>
|
<form method="POST" action="@path/@origin/@repository.name/pulls/new" validate="true">
|
||||||
<ul id="commit-file-list" style="display: none;">
|
<span class="error" id="error-title"></span>
|
||||||
@diffs.zipWithIndex.map { case (diff, i) =>
|
<input type="text" name="title" style="width: 600px" placeholder="Title"/>
|
||||||
<li@if(i > 0){ class="border"}>
|
@helper.html.preview(repository, "", false, true, "width: 600px; height: 200px;")
|
||||||
<a href="#diff-@i">
|
<input type="hidden" name="branch" value="@originId"/>
|
||||||
@if(diff.changeType == ChangeType.COPY || diff.changeType == ChangeType.RENAME){
|
<input type="hidden" name="requestUserName" value="@repository.owner"/>
|
||||||
<img src="@assets/common/images/diff_move.png"/> @diff.oldPath -> @diff.newPath
|
<input type="hidden" name="requestCommitId" value="@commitId"/>
|
||||||
|
<input type="submit" class="btn btn-success" value="Send pull request"/>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
@if(commits.isEmpty){
|
||||||
|
<table class="table table-bordered table-hover table-issues">
|
||||||
|
<tr>
|
||||||
|
<td style="padding: 20px; background-color: #eee; text-align: center;">
|
||||||
|
<h4>There isn't anything to compare.</h4>
|
||||||
|
<strong>@origin:@originId</strong> and <strong>@repository.owner:@forkedId</strong> are identical.
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
} else {
|
||||||
|
<div class="box">
|
||||||
|
<table class="table table-file-list" style="border: 1px solid silver;">
|
||||||
|
@commits.map { day =>
|
||||||
|
<tr>
|
||||||
|
<th colspan="3" class="box-header" style="font-weight: normal;">@date(day.head.time)</th>
|
||||||
|
</tr>
|
||||||
|
@day.map { commit =>
|
||||||
|
<tr>
|
||||||
|
<td style="width: 20%;">
|
||||||
|
@avatar(commit.committer, 20)
|
||||||
|
<a href="@url(commit.committer)" class="username">@commit.committer</a>
|
||||||
|
</td>
|
||||||
|
<td>@commit.shortMessage</td>
|
||||||
|
<td style="width: 10%; text-align: right;">
|
||||||
|
<a href="@url(repository)/commit/@commit.id" class="monospace">@commit.id.substring(0, 7)</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@if(diff.changeType == ChangeType.ADD){
|
</table>
|
||||||
<img src="@assets/common/images/diff_add.png"/> @diff.newPath
|
</div>
|
||||||
}
|
<div>
|
||||||
@if(diff.changeType == ChangeType.MODIFY){
|
<div class="pull-right" style="margin-bottom: 10px;">
|
||||||
<img src="@assets/common/images/diff_edit.png"/> @diff.newPath
|
<input id="toggle-file-list" type="button" class="btn" value="Show file list"/>
|
||||||
}
|
</div>
|
||||||
@if(diff.changeType == ChangeType.DELETE){
|
Showing @diffs.size changed @plural(diffs.size, "file")
|
||||||
<img src="@assets/common/images/diff_delete.png"/> @diff.oldPath
|
</div>
|
||||||
}
|
<ul id="commit-file-list" style="display: none;">
|
||||||
</a>
|
@diffs.zipWithIndex.map { case (diff, i) =>
|
||||||
</li>
|
<li@if(i > 0){ class="border"}>
|
||||||
}
|
<a href="#diff-@i">
|
||||||
</ul>
|
@if(diff.changeType == ChangeType.COPY || diff.changeType == ChangeType.RENAME){
|
||||||
@helper.html.diff(diffs, repository, Some(commitId))
|
<img src="@assets/common/images/diff_move.png"/> @diff.oldPath -> @diff.newPath
|
||||||
|
}
|
||||||
|
@if(diff.changeType == ChangeType.ADD){
|
||||||
|
<img src="@assets/common/images/diff_add.png"/> @diff.newPath
|
||||||
|
}
|
||||||
|
@if(diff.changeType == ChangeType.MODIFY){
|
||||||
|
<img src="@assets/common/images/diff_edit.png"/> @diff.newPath
|
||||||
|
}
|
||||||
|
@if(diff.changeType == ChangeType.DELETE){
|
||||||
|
<img src="@assets/common/images/diff_delete.png"/> @diff.oldPath
|
||||||
|
}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
}
|
||||||
|
</ul>
|
||||||
|
@helper.html.diff(diffs, repository, Some(commitId))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
<script>
|
<script>
|
||||||
$(function(){
|
$(function(){
|
||||||
@@ -101,6 +125,11 @@ $(function(){
|
|||||||
$.trim($('i.icon-ok').parents('a.forked-branch').text());
|
$.trim($('i.icon-ok').parents('a.forked-branch').text());
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$('#show-form').click(function(){
|
||||||
|
$(this).hide();
|
||||||
|
$('#pull-request-form').show();
|
||||||
|
});
|
||||||
|
|
||||||
$('#toggle-file-list').click(function(){
|
$('#toggle-file-list').click(function(){
|
||||||
$('#commit-file-list').toggle();
|
$('#commit-file-list').toggle();
|
||||||
if($(this).val() == 'Show file list'){
|
if($(this).val() == 'Show file list'){
|
||||||
|
|||||||
Reference in New Issue
Block a user