(refs #508)Fix line separator

This commit is contained in:
Naoki Takezoe
2014-11-11 12:09:48 +09:00
parent bc0b11b60a
commit 37a399c3a2

View File

@@ -1,483 +1,483 @@
package app package app
import util._ import util._
import util.Directory._ import util.Directory._
import util.Implicits._ import util.Implicits._
import util.ControlUtil._ import util.ControlUtil._
import service._ import service._
import org.eclipse.jgit.api.Git import org.eclipse.jgit.api.Git
import jp.sf.amateras.scalatra.forms._ import jp.sf.amateras.scalatra.forms._
import org.eclipse.jgit.transport.RefSpec import org.eclipse.jgit.transport.RefSpec
import scala.collection.JavaConverters._ import scala.collection.JavaConverters._
import org.eclipse.jgit.lib.{ObjectId, CommitBuilder, PersonIdent} import org.eclipse.jgit.lib.{ObjectId, CommitBuilder, PersonIdent}
import service.IssuesService._ import service.IssuesService._
import service.PullRequestService._ import service.PullRequestService._
import util.JGitUtil.DiffInfo import util.JGitUtil.DiffInfo
import util.JGitUtil.CommitInfo import util.JGitUtil.CommitInfo
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
import org.eclipse.jgit.merge.MergeStrategy import org.eclipse.jgit.merge.MergeStrategy
import org.eclipse.jgit.errors.NoMergeBaseException import org.eclipse.jgit.errors.NoMergeBaseException
import service.WebHookService.WebHookPayload import service.WebHookService.WebHookPayload
import util.JGitUtil.DiffInfo import util.JGitUtil.DiffInfo
import scala.Some import scala.Some
import util.JGitUtil.CommitInfo import util.JGitUtil.CommitInfo
class PullRequestsController extends PullRequestsControllerBase class PullRequestsController extends PullRequestsControllerBase
with RepositoryService with AccountService with IssuesService with PullRequestService with MilestonesService with LabelsService with RepositoryService with AccountService with IssuesService with PullRequestService with MilestonesService with LabelsService
with ActivityService with WebHookService with ReferrerAuthenticator with CollaboratorsAuthenticator with ActivityService with WebHookService with ReferrerAuthenticator with CollaboratorsAuthenticator
trait PullRequestsControllerBase extends ControllerBase { trait PullRequestsControllerBase extends ControllerBase {
self: RepositoryService with AccountService with IssuesService with MilestonesService with LabelsService self: RepositoryService with AccountService with IssuesService with MilestonesService with LabelsService
with ActivityService with PullRequestService with WebHookService with ReferrerAuthenticator with CollaboratorsAuthenticator => with ActivityService with PullRequestService with WebHookService with ReferrerAuthenticator with CollaboratorsAuthenticator =>
private val logger = LoggerFactory.getLogger(classOf[PullRequestsControllerBase]) private val logger = LoggerFactory.getLogger(classOf[PullRequestsControllerBase])
val pullRequestForm = mapping( val pullRequestForm = mapping(
"title" -> trim(label("Title" , text(required, maxlength(100)))), "title" -> trim(label("Title" , text(required, maxlength(100)))),
"content" -> trim(label("Content", optional(text()))), "content" -> trim(label("Content", optional(text()))),
"targetUserName" -> trim(text(required, maxlength(100))), "targetUserName" -> trim(text(required, maxlength(100))),
"targetBranch" -> trim(text(required, maxlength(100))), "targetBranch" -> trim(text(required, maxlength(100))),
"requestUserName" -> trim(text(required, maxlength(100))), "requestUserName" -> trim(text(required, maxlength(100))),
"requestRepositoryName" -> trim(text(required, maxlength(100))), "requestRepositoryName" -> trim(text(required, maxlength(100))),
"requestBranch" -> trim(text(required, maxlength(100))), "requestBranch" -> trim(text(required, maxlength(100))),
"commitIdFrom" -> trim(text(required, maxlength(40))), "commitIdFrom" -> trim(text(required, maxlength(40))),
"commitIdTo" -> trim(text(required, maxlength(40))) "commitIdTo" -> trim(text(required, maxlength(40)))
)(PullRequestForm.apply) )(PullRequestForm.apply)
val mergeForm = mapping( val mergeForm = mapping(
"message" -> trim(label("Message", text(required))) "message" -> trim(label("Message", text(required)))
)(MergeForm.apply) )(MergeForm.apply)
case class PullRequestForm( case class PullRequestForm(
title: String, title: String,
content: Option[String], content: Option[String],
targetUserName: String, targetUserName: String,
targetBranch: String, targetBranch: String,
requestUserName: String, requestUserName: String,
requestRepositoryName: String, requestRepositoryName: String,
requestBranch: String, requestBranch: String,
commitIdFrom: String, commitIdFrom: String,
commitIdTo: String) commitIdTo: String)
case class MergeForm(message: String) case class MergeForm(message: String)
get("/:owner/:repository/pulls")(referrersOnly { repository => get("/:owner/:repository/pulls")(referrersOnly { repository =>
val q = request.getParameter("q") val q = request.getParameter("q")
if(Option(q).exists(_.contains("is:issue"))){ if(Option(q).exists(_.contains("is:issue"))){
redirect(s"/${repository.owner}/${repository.name}/issues?q=" + StringUtil.urlEncode(q)) redirect(s"/${repository.owner}/${repository.name}/issues?q=" + StringUtil.urlEncode(q))
} else { } else {
searchPullRequests(None, repository) searchPullRequests(None, repository)
} }
}) })
get("/:owner/:repository/pull/:id")(referrersOnly { repository => get("/:owner/:repository/pull/:id")(referrersOnly { repository =>
params("id").toIntOpt.flatMap{ issueId => params("id").toIntOpt.flatMap{ issueId =>
val owner = repository.owner val owner = repository.owner
val name = repository.name val name = repository.name
getPullRequest(owner, name, issueId) map { case(issue, pullreq) => getPullRequest(owner, name, issueId) map { case(issue, pullreq) =>
using(Git.open(getRepositoryDir(owner, name))){ git => using(Git.open(getRepositoryDir(owner, name))){ git =>
val (commits, diffs) = val (commits, diffs) =
getRequestCompareInfo(owner, name, pullreq.commitIdFrom, owner, name, pullreq.commitIdTo) getRequestCompareInfo(owner, name, pullreq.commitIdFrom, owner, name, pullreq.commitIdTo)
pulls.html.pullreq( pulls.html.pullreq(
issue, pullreq, issue, pullreq,
getComments(owner, name, issueId), getComments(owner, name, issueId),
getIssueLabels(owner, name, issueId), getIssueLabels(owner, name, issueId),
(getCollaborators(owner, name) ::: (if(getAccountByUserName(owner).get.isGroupAccount) Nil else List(owner))).sorted, (getCollaborators(owner, name) ::: (if(getAccountByUserName(owner).get.isGroupAccount) Nil else List(owner))).sorted,
getMilestonesWithIssueCount(owner, name), getMilestonesWithIssueCount(owner, name),
getLabels(owner, name), getLabels(owner, name),
commits, commits,
diffs, diffs,
hasWritePermission(owner, name, context.loginAccount), hasWritePermission(owner, name, context.loginAccount),
repository) repository)
} }
} }
} getOrElse NotFound } getOrElse NotFound
}) })
ajaxGet("/:owner/:repository/pull/:id/mergeguide")(collaboratorsOnly { repository => ajaxGet("/:owner/:repository/pull/:id/mergeguide")(collaboratorsOnly { repository =>
params("id").toIntOpt.flatMap{ issueId => params("id").toIntOpt.flatMap{ issueId =>
val owner = repository.owner val owner = repository.owner
val name = repository.name val name = repository.name
getPullRequest(owner, name, issueId) map { case(issue, pullreq) => getPullRequest(owner, name, issueId) map { case(issue, pullreq) =>
pulls.html.mergeguide( pulls.html.mergeguide(
checkConflictInPullRequest(owner, name, pullreq.branch, pullreq.requestUserName, name, pullreq.requestBranch, issueId), checkConflictInPullRequest(owner, name, pullreq.branch, pullreq.requestUserName, name, pullreq.requestBranch, issueId),
pullreq, pullreq,
s"${context.baseUrl}/git/${pullreq.requestUserName}/${pullreq.requestRepositoryName}.git") s"${context.baseUrl}/git/${pullreq.requestUserName}/${pullreq.requestRepositoryName}.git")
} }
} getOrElse NotFound } getOrElse NotFound
}) })
get("/:owner/:repository/pull/:id/delete/*")(collaboratorsOnly { repository => get("/:owner/:repository/pull/:id/delete/*")(collaboratorsOnly { repository =>
params("id").toIntOpt.map { issueId => params("id").toIntOpt.map { issueId =>
val branchName = multiParams("splat").head val branchName = multiParams("splat").head
val userName = context.loginAccount.get.userName val userName = context.loginAccount.get.userName
if(repository.repository.defaultBranch != branchName){ if(repository.repository.defaultBranch != branchName){
using(Git.open(getRepositoryDir(repository.owner, repository.name))){ git => using(Git.open(getRepositoryDir(repository.owner, repository.name))){ git =>
git.branchDelete().setForce(true).setBranchNames(branchName).call() git.branchDelete().setForce(true).setBranchNames(branchName).call()
recordDeleteBranchActivity(repository.owner, repository.name, userName, branchName) recordDeleteBranchActivity(repository.owner, repository.name, userName, branchName)
} }
} }
createComment(repository.owner, repository.name, userName, issueId, branchName, "delete_branch") createComment(repository.owner, repository.name, userName, issueId, branchName, "delete_branch")
redirect(s"/${repository.owner}/${repository.name}/pull/${issueId}") redirect(s"/${repository.owner}/${repository.name}/pull/${issueId}")
} getOrElse NotFound } getOrElse NotFound
}) })
post("/:owner/:repository/pull/:id/merge", mergeForm)(collaboratorsOnly { (form, repository) => post("/:owner/:repository/pull/:id/merge", mergeForm)(collaboratorsOnly { (form, repository) =>
params("id").toIntOpt.flatMap { issueId => params("id").toIntOpt.flatMap { issueId =>
val owner = repository.owner val owner = repository.owner
val name = repository.name val name = repository.name
LockUtil.lock(s"${owner}/${name}"){ LockUtil.lock(s"${owner}/${name}"){
getPullRequest(owner, name, issueId).map { case (issue, pullreq) => getPullRequest(owner, name, issueId).map { case (issue, pullreq) =>
using(Git.open(getRepositoryDir(owner, name))) { git => using(Git.open(getRepositoryDir(owner, name))) { git =>
// mark issue as merged and close. // mark issue as merged and close.
val loginAccount = context.loginAccount.get val loginAccount = context.loginAccount.get
createComment(owner, name, loginAccount.userName, issueId, form.message, "merge") createComment(owner, name, loginAccount.userName, issueId, form.message, "merge")
createComment(owner, name, loginAccount.userName, issueId, "Close", "close") createComment(owner, name, loginAccount.userName, issueId, "Close", "close")
updateClosed(owner, name, issueId, true) updateClosed(owner, name, issueId, true)
// record activity // record activity
recordMergeActivity(owner, name, loginAccount.userName, issueId, form.message) recordMergeActivity(owner, name, loginAccount.userName, issueId, form.message)
// merge // merge
val mergeBaseRefName = s"refs/heads/${pullreq.branch}" val mergeBaseRefName = s"refs/heads/${pullreq.branch}"
val merger = MergeStrategy.RECURSIVE.newMerger(git.getRepository, true) val merger = MergeStrategy.RECURSIVE.newMerger(git.getRepository, true)
val mergeBaseTip = git.getRepository.resolve(mergeBaseRefName) val mergeBaseTip = git.getRepository.resolve(mergeBaseRefName)
val mergeTip = git.getRepository.resolve(s"refs/pull/${issueId}/head") val mergeTip = git.getRepository.resolve(s"refs/pull/${issueId}/head")
val conflicted = try { val conflicted = try {
!merger.merge(mergeBaseTip, mergeTip) !merger.merge(mergeBaseTip, mergeTip)
} catch { } catch {
case e: NoMergeBaseException => true case e: NoMergeBaseException => true
} }
if (conflicted) { if (conflicted) {
throw new RuntimeException("This pull request can't merge automatically.") throw new RuntimeException("This pull request can't merge automatically.")
} }
// creates merge commit // creates merge commit
val mergeCommit = new CommitBuilder() val mergeCommit = new CommitBuilder()
mergeCommit.setTreeId(merger.getResultTreeId) mergeCommit.setTreeId(merger.getResultTreeId)
mergeCommit.setParentIds(Array[ObjectId](mergeBaseTip, mergeTip): _*) mergeCommit.setParentIds(Array[ObjectId](mergeBaseTip, mergeTip): _*)
val personIdent = new PersonIdent(loginAccount.fullName, loginAccount.mailAddress) val personIdent = new PersonIdent(loginAccount.fullName, loginAccount.mailAddress)
mergeCommit.setAuthor(personIdent) mergeCommit.setAuthor(personIdent)
mergeCommit.setCommitter(personIdent) mergeCommit.setCommitter(personIdent)
mergeCommit.setMessage(s"Merge pull request #${issueId} from ${pullreq.requestUserName}/${pullreq.requestBranch}\n\n" + mergeCommit.setMessage(s"Merge pull request #${issueId} from ${pullreq.requestUserName}/${pullreq.requestBranch}\n\n" +
form.message) form.message)
// insertObject and got mergeCommit Object Id // insertObject and got mergeCommit Object Id
val inserter = git.getRepository.newObjectInserter val inserter = git.getRepository.newObjectInserter
val mergeCommitId = inserter.insert(mergeCommit) val mergeCommitId = inserter.insert(mergeCommit)
inserter.flush() inserter.flush()
inserter.release() inserter.release()
// update refs // update refs
val refUpdate = git.getRepository.updateRef(mergeBaseRefName) val refUpdate = git.getRepository.updateRef(mergeBaseRefName)
refUpdate.setNewObjectId(mergeCommitId) refUpdate.setNewObjectId(mergeCommitId)
refUpdate.setForceUpdate(false) refUpdate.setForceUpdate(false)
refUpdate.setRefLogIdent(personIdent) refUpdate.setRefLogIdent(personIdent)
refUpdate.setRefLogMessage("merged", true) refUpdate.setRefLogMessage("merged", true)
refUpdate.update() refUpdate.update()
val (commits, _) = getRequestCompareInfo(owner, name, pullreq.commitIdFrom, val (commits, _) = getRequestCompareInfo(owner, name, pullreq.commitIdFrom,
pullreq.requestUserName, pullreq.requestRepositoryName, pullreq.commitIdTo) pullreq.requestUserName, pullreq.requestRepositoryName, pullreq.commitIdTo)
// close issue by content of pull request // close issue by content of pull request
val defaultBranch = getRepository(owner, name, context.baseUrl).get.repository.defaultBranch val defaultBranch = getRepository(owner, name, context.baseUrl).get.repository.defaultBranch
if(pullreq.branch == defaultBranch){ if(pullreq.branch == defaultBranch){
commits.flatten.foreach { commit => commits.flatten.foreach { commit =>
closeIssuesFromMessage(commit.fullMessage, loginAccount.userName, owner, name) closeIssuesFromMessage(commit.fullMessage, loginAccount.userName, owner, name)
} }
issue.content match { issue.content match {
case Some(content) => closeIssuesFromMessage(content, loginAccount.userName, owner, name) case Some(content) => closeIssuesFromMessage(content, loginAccount.userName, owner, name)
case _ => case _ =>
} }
closeIssuesFromMessage(form.message, loginAccount.userName, owner, name) closeIssuesFromMessage(form.message, loginAccount.userName, owner, name)
} }
// call web hook // call web hook
getWebHookURLs(owner, name) match { getWebHookURLs(owner, name) match {
case webHookURLs if(webHookURLs.nonEmpty) => case webHookURLs if(webHookURLs.nonEmpty) =>
for(ownerAccount <- getAccountByUserName(owner)){ for(ownerAccount <- getAccountByUserName(owner)){
callWebHook(owner, name, webHookURLs, callWebHook(owner, name, webHookURLs,
WebHookPayload(git, loginAccount, mergeBaseRefName, repository, commits.flatten.toList, ownerAccount)) WebHookPayload(git, loginAccount, mergeBaseRefName, repository, commits.flatten.toList, ownerAccount))
} }
case _ => case _ =>
} }
// notifications // notifications
Notifier().toNotify(repository, issueId, "merge"){ Notifier().toNotify(repository, issueId, "merge"){
Notifier.msgStatus(s"${context.baseUrl}/${owner}/${name}/pull/${issueId}") Notifier.msgStatus(s"${context.baseUrl}/${owner}/${name}/pull/${issueId}")
} }
redirect(s"/${owner}/${name}/pull/${issueId}") redirect(s"/${owner}/${name}/pull/${issueId}")
} }
} }
} }
} getOrElse NotFound } getOrElse NotFound
}) })
get("/:owner/:repository/compare")(referrersOnly { forkedRepository => get("/:owner/:repository/compare")(referrersOnly { forkedRepository =>
(forkedRepository.repository.originUserName, forkedRepository.repository.originRepositoryName) match { (forkedRepository.repository.originUserName, forkedRepository.repository.originRepositoryName) match {
case (Some(originUserName), Some(originRepositoryName)) => { case (Some(originUserName), Some(originRepositoryName)) => {
getRepository(originUserName, originRepositoryName, context.baseUrl).map { originRepository => getRepository(originUserName, originRepositoryName, context.baseUrl).map { originRepository =>
using( using(
Git.open(getRepositoryDir(originUserName, originRepositoryName)), Git.open(getRepositoryDir(originUserName, originRepositoryName)),
Git.open(getRepositoryDir(forkedRepository.owner, forkedRepository.name)) Git.open(getRepositoryDir(forkedRepository.owner, forkedRepository.name))
){ (oldGit, newGit) => ){ (oldGit, newGit) =>
val oldBranch = JGitUtil.getDefaultBranch(oldGit, originRepository).get._2 val oldBranch = JGitUtil.getDefaultBranch(oldGit, originRepository).get._2
val newBranch = JGitUtil.getDefaultBranch(newGit, forkedRepository).get._2 val newBranch = JGitUtil.getDefaultBranch(newGit, forkedRepository).get._2
redirect(s"/${forkedRepository.owner}/${forkedRepository.name}/compare/${originUserName}:${oldBranch}...${newBranch}") redirect(s"/${forkedRepository.owner}/${forkedRepository.name}/compare/${originUserName}:${oldBranch}...${newBranch}")
} }
} getOrElse NotFound } getOrElse NotFound
} }
case _ => { case _ => {
using(Git.open(getRepositoryDir(forkedRepository.owner, forkedRepository.name))){ git => using(Git.open(getRepositoryDir(forkedRepository.owner, forkedRepository.name))){ git =>
JGitUtil.getDefaultBranch(git, forkedRepository).map { case (_, defaultBranch) => JGitUtil.getDefaultBranch(git, forkedRepository).map { case (_, defaultBranch) =>
redirect(s"/${forkedRepository.owner}/${forkedRepository.name}/compare/${defaultBranch}...${defaultBranch}") redirect(s"/${forkedRepository.owner}/${forkedRepository.name}/compare/${defaultBranch}...${defaultBranch}")
} getOrElse { } getOrElse {
redirect(s"/${forkedRepository.owner}/${forkedRepository.name}") redirect(s"/${forkedRepository.owner}/${forkedRepository.name}")
} }
} }
} }
} }
}) })
get("/:owner/:repository/compare/*...*")(referrersOnly { forkedRepository => get("/:owner/:repository/compare/*...*")(referrersOnly { forkedRepository =>
val Seq(origin, forked) = multiParams("splat") val Seq(origin, forked) = multiParams("splat")
val (originOwner, tmpOriginBranch) = parseCompareIdentifie(origin, forkedRepository.owner) val (originOwner, tmpOriginBranch) = parseCompareIdentifie(origin, forkedRepository.owner)
val (forkedOwner, tmpForkedBranch) = parseCompareIdentifie(forked, forkedRepository.owner) val (forkedOwner, tmpForkedBranch) = parseCompareIdentifie(forked, forkedRepository.owner)
(for( (for(
originRepositoryName <- if(originOwner == forkedOwner){ originRepositoryName <- if(originOwner == forkedOwner){
Some(forkedRepository.name) Some(forkedRepository.name)
} else { } else {
forkedRepository.repository.originRepositoryName.orElse { forkedRepository.repository.originRepositoryName.orElse {
getForkedRepositories(forkedRepository.owner, forkedRepository.name).find(_._1 == originOwner).map(_._2) getForkedRepositories(forkedRepository.owner, forkedRepository.name).find(_._1 == originOwner).map(_._2)
} }
}; };
originRepository <- getRepository(originOwner, originRepositoryName, context.baseUrl) originRepository <- getRepository(originOwner, originRepositoryName, context.baseUrl)
) yield { ) yield {
using( using(
Git.open(getRepositoryDir(originRepository.owner, originRepository.name)), Git.open(getRepositoryDir(originRepository.owner, originRepository.name)),
Git.open(getRepositoryDir(forkedRepository.owner, forkedRepository.name)) Git.open(getRepositoryDir(forkedRepository.owner, forkedRepository.name))
){ case (oldGit, newGit) => ){ case (oldGit, newGit) =>
val originBranch = JGitUtil.getDefaultBranch(oldGit, originRepository, tmpOriginBranch).get._2 val originBranch = JGitUtil.getDefaultBranch(oldGit, originRepository, tmpOriginBranch).get._2
val forkedBranch = JGitUtil.getDefaultBranch(newGit, forkedRepository, tmpForkedBranch).get._2 val forkedBranch = JGitUtil.getDefaultBranch(newGit, forkedRepository, tmpForkedBranch).get._2
val forkedId = JGitUtil.getForkedCommitId(oldGit, newGit, val forkedId = JGitUtil.getForkedCommitId(oldGit, newGit,
originRepository.owner, originRepository.name, originBranch, originRepository.owner, originRepository.name, originBranch,
forkedRepository.owner, forkedRepository.name, forkedBranch) forkedRepository.owner, forkedRepository.name, forkedBranch)
val oldId = oldGit.getRepository.resolve(forkedId) val oldId = oldGit.getRepository.resolve(forkedId)
val newId = newGit.getRepository.resolve(forkedBranch) val newId = newGit.getRepository.resolve(forkedBranch)
val (commits, diffs) = getRequestCompareInfo( val (commits, diffs) = getRequestCompareInfo(
originRepository.owner, originRepository.name, oldId.getName, originRepository.owner, originRepository.name, oldId.getName,
forkedRepository.owner, forkedRepository.name, newId.getName) forkedRepository.owner, forkedRepository.name, newId.getName)
pulls.html.compare( pulls.html.compare(
commits, commits,
diffs, diffs,
(forkedRepository.repository.originUserName, forkedRepository.repository.originRepositoryName) match { (forkedRepository.repository.originUserName, forkedRepository.repository.originRepositoryName) match {
case (Some(userName), Some(repositoryName)) => (userName, repositoryName) :: getForkedRepositories(userName, repositoryName) case (Some(userName), Some(repositoryName)) => (userName, repositoryName) :: getForkedRepositories(userName, repositoryName)
case _ => (forkedRepository.owner, forkedRepository.name) :: getForkedRepositories(forkedRepository.owner, forkedRepository.name) case _ => (forkedRepository.owner, forkedRepository.name) :: getForkedRepositories(forkedRepository.owner, forkedRepository.name)
}, },
originBranch, originBranch,
forkedBranch, forkedBranch,
oldId.getName, oldId.getName,
newId.getName, newId.getName,
forkedRepository, forkedRepository,
originRepository, originRepository,
forkedRepository, forkedRepository,
hasWritePermission(forkedRepository.owner, forkedRepository.name, context.loginAccount)) hasWritePermission(forkedRepository.owner, forkedRepository.name, context.loginAccount))
} }
}) getOrElse NotFound }) getOrElse NotFound
}) })
ajaxGet("/:owner/:repository/compare/*...*/mergecheck")(collaboratorsOnly { forkedRepository => ajaxGet("/:owner/:repository/compare/*...*/mergecheck")(collaboratorsOnly { forkedRepository =>
val Seq(origin, forked) = multiParams("splat") val Seq(origin, forked) = multiParams("splat")
val (originOwner, tmpOriginBranch) = parseCompareIdentifie(origin, forkedRepository.owner) val (originOwner, tmpOriginBranch) = parseCompareIdentifie(origin, forkedRepository.owner)
val (forkedOwner, tmpForkedBranch) = parseCompareIdentifie(forked, forkedRepository.owner) val (forkedOwner, tmpForkedBranch) = parseCompareIdentifie(forked, forkedRepository.owner)
(for( (for(
originRepositoryName <- if(originOwner == forkedOwner){ originRepositoryName <- if(originOwner == forkedOwner){
Some(forkedRepository.name) Some(forkedRepository.name)
} else { } else {
forkedRepository.repository.originRepositoryName.orElse { forkedRepository.repository.originRepositoryName.orElse {
getForkedRepositories(forkedRepository.owner, forkedRepository.name).find(_._1 == originOwner).map(_._2) getForkedRepositories(forkedRepository.owner, forkedRepository.name).find(_._1 == originOwner).map(_._2)
} }
}; };
originRepository <- getRepository(originOwner, originRepositoryName, context.baseUrl) originRepository <- getRepository(originOwner, originRepositoryName, context.baseUrl)
) yield { ) yield {
using( using(
Git.open(getRepositoryDir(originRepository.owner, originRepository.name)), Git.open(getRepositoryDir(originRepository.owner, originRepository.name)),
Git.open(getRepositoryDir(forkedRepository.owner, forkedRepository.name)) Git.open(getRepositoryDir(forkedRepository.owner, forkedRepository.name))
){ case (oldGit, newGit) => ){ case (oldGit, newGit) =>
val originBranch = JGitUtil.getDefaultBranch(oldGit, originRepository, tmpOriginBranch).get._2 val originBranch = JGitUtil.getDefaultBranch(oldGit, originRepository, tmpOriginBranch).get._2
val forkedBranch = JGitUtil.getDefaultBranch(newGit, forkedRepository, tmpForkedBranch).get._2 val forkedBranch = JGitUtil.getDefaultBranch(newGit, forkedRepository, tmpForkedBranch).get._2
pulls.html.mergecheck( pulls.html.mergecheck(
checkConflict(originRepository.owner, originRepository.name, originBranch, checkConflict(originRepository.owner, originRepository.name, originBranch,
forkedRepository.owner, forkedRepository.name, forkedBranch)) forkedRepository.owner, forkedRepository.name, forkedBranch))
} }
}) getOrElse NotFound }) getOrElse NotFound
}) })
post("/:owner/:repository/pulls/new", pullRequestForm)(referrersOnly { (form, repository) => post("/:owner/:repository/pulls/new", pullRequestForm)(referrersOnly { (form, repository) =>
val loginUserName = context.loginAccount.get.userName val loginUserName = context.loginAccount.get.userName
val issueId = createIssue( val issueId = createIssue(
owner = repository.owner, owner = repository.owner,
repository = repository.name, repository = repository.name,
loginUser = loginUserName, loginUser = loginUserName,
title = form.title, title = form.title,
content = form.content, content = form.content,
assignedUserName = None, assignedUserName = None,
milestoneId = None, milestoneId = None,
isPullRequest = true) isPullRequest = true)
createPullRequest( createPullRequest(
originUserName = repository.owner, originUserName = repository.owner,
originRepositoryName = repository.name, originRepositoryName = repository.name,
issueId = issueId, issueId = issueId,
originBranch = form.targetBranch, originBranch = form.targetBranch,
requestUserName = form.requestUserName, requestUserName = form.requestUserName,
requestRepositoryName = form.requestRepositoryName, requestRepositoryName = form.requestRepositoryName,
requestBranch = form.requestBranch, requestBranch = form.requestBranch,
commitIdFrom = form.commitIdFrom, commitIdFrom = form.commitIdFrom,
commitIdTo = form.commitIdTo) commitIdTo = form.commitIdTo)
// fetch requested branch // fetch requested branch
using(Git.open(getRepositoryDir(repository.owner, repository.name))){ git => using(Git.open(getRepositoryDir(repository.owner, repository.name))){ git =>
git.fetch git.fetch
.setRemote(getRepositoryDir(form.requestUserName, form.requestRepositoryName).toURI.toString) .setRemote(getRepositoryDir(form.requestUserName, form.requestRepositoryName).toURI.toString)
.setRefSpecs(new RefSpec(s"refs/heads/${form.requestBranch}:refs/pull/${issueId}/head")) .setRefSpecs(new RefSpec(s"refs/heads/${form.requestBranch}:refs/pull/${issueId}/head"))
.call .call
} }
// record activity // record activity
recordPullRequestActivity(repository.owner, repository.name, loginUserName, issueId, form.title) recordPullRequestActivity(repository.owner, repository.name, loginUserName, issueId, form.title)
// notifications // notifications
Notifier().toNotify(repository, issueId, form.content.getOrElse("")){ Notifier().toNotify(repository, issueId, form.content.getOrElse("")){
Notifier.msgPullRequest(s"${context.baseUrl}/${repository.owner}/${repository.name}/pull/${issueId}") Notifier.msgPullRequest(s"${context.baseUrl}/${repository.owner}/${repository.name}/pull/${issueId}")
} }
redirect(s"/${repository.owner}/${repository.name}/pull/${issueId}") redirect(s"/${repository.owner}/${repository.name}/pull/${issueId}")
}) })
/** /**
* Checks whether conflict will be caused in merging. Returns true if conflict will be caused. * Checks whether conflict will be caused in merging. Returns true if conflict will be caused.
*/ */
private def checkConflict(userName: String, repositoryName: String, branch: String, private def checkConflict(userName: String, repositoryName: String, branch: String,
requestUserName: String, requestRepositoryName: String, requestBranch: String): Boolean = { requestUserName: String, requestRepositoryName: String, requestBranch: String): Boolean = {
LockUtil.lock(s"${userName}/${repositoryName}"){ LockUtil.lock(s"${userName}/${repositoryName}"){
using(Git.open(getRepositoryDir(requestUserName, requestRepositoryName))) { git => using(Git.open(getRepositoryDir(requestUserName, requestRepositoryName))) { git =>
val remoteRefName = s"refs/heads/${branch}" val remoteRefName = s"refs/heads/${branch}"
val tmpRefName = s"refs/merge-check/${userName}/${branch}" val tmpRefName = s"refs/merge-check/${userName}/${branch}"
val refSpec = new RefSpec(s"${remoteRefName}:${tmpRefName}").setForceUpdate(true) val refSpec = new RefSpec(s"${remoteRefName}:${tmpRefName}").setForceUpdate(true)
try { try {
// fetch objects from origin repository branch // fetch objects from origin repository branch
git.fetch git.fetch
.setRemote(getRepositoryDir(userName, repositoryName).toURI.toString) .setRemote(getRepositoryDir(userName, repositoryName).toURI.toString)
.setRefSpecs(refSpec) .setRefSpecs(refSpec)
.call .call
// merge conflict check // merge conflict check
val merger = MergeStrategy.RECURSIVE.newMerger(git.getRepository, true) val merger = MergeStrategy.RECURSIVE.newMerger(git.getRepository, true)
val mergeBaseTip = git.getRepository.resolve(s"refs/heads/${requestBranch}") val mergeBaseTip = git.getRepository.resolve(s"refs/heads/${requestBranch}")
val mergeTip = git.getRepository.resolve(tmpRefName) val mergeTip = git.getRepository.resolve(tmpRefName)
try { try {
!merger.merge(mergeBaseTip, mergeTip) !merger.merge(mergeBaseTip, mergeTip)
} catch { } catch {
case e: NoMergeBaseException => true case e: NoMergeBaseException => true
} }
} finally { } finally {
val refUpdate = git.getRepository.updateRef(refSpec.getDestination) val refUpdate = git.getRepository.updateRef(refSpec.getDestination)
refUpdate.setForceUpdate(true) refUpdate.setForceUpdate(true)
refUpdate.delete() refUpdate.delete()
} }
} }
} }
} }
/** /**
* Checks whether conflict will be caused in merging within pull request. Returns true if conflict will be caused. * Checks whether conflict will be caused in merging within pull request. Returns true if conflict will be caused.
*/ */
private def checkConflictInPullRequest(userName: String, repositoryName: String, branch: String, private def checkConflictInPullRequest(userName: String, repositoryName: String, branch: String,
requestUserName: String, requestRepositoryName: String, requestBranch: String, requestUserName: String, requestRepositoryName: String, requestBranch: String,
issueId: Int): Boolean = { issueId: Int): Boolean = {
LockUtil.lock(s"${userName}/${repositoryName}") { LockUtil.lock(s"${userName}/${repositoryName}") {
using(Git.open(getRepositoryDir(userName, repositoryName))) { git => using(Git.open(getRepositoryDir(userName, repositoryName))) { git =>
// merge // merge
val merger = MergeStrategy.RECURSIVE.newMerger(git.getRepository, true) val merger = MergeStrategy.RECURSIVE.newMerger(git.getRepository, true)
val mergeBaseTip = git.getRepository.resolve(s"refs/heads/${branch}") val mergeBaseTip = git.getRepository.resolve(s"refs/heads/${branch}")
val mergeTip = git.getRepository.resolve(s"refs/pull/${issueId}/head") val mergeTip = git.getRepository.resolve(s"refs/pull/${issueId}/head")
try { try {
!merger.merge(mergeBaseTip, mergeTip) !merger.merge(mergeBaseTip, mergeTip)
} catch { } catch {
case e: NoMergeBaseException => true case e: NoMergeBaseException => true
} }
} }
} }
} }
/** /**
* Parses branch identifier and extracts owner and branch name as tuple. * Parses branch identifier and extracts owner and branch name as tuple.
* *
* - "owner:branch" to ("owner", "branch") * - "owner:branch" to ("owner", "branch")
* - "branch" to ("defaultOwner", "branch") * - "branch" to ("defaultOwner", "branch")
*/ */
private def parseCompareIdentifie(value: String, defaultOwner: String): (String, String) = private def parseCompareIdentifie(value: String, defaultOwner: String): (String, String) =
if(value.contains(':')){ if(value.contains(':')){
val array = value.split(":") val array = value.split(":")
(array(0), array(1)) (array(0), array(1))
} else { } else {
(defaultOwner, value) (defaultOwner, value)
} }
private def getRequestCompareInfo(userName: String, repositoryName: String, branch: String, private def getRequestCompareInfo(userName: String, repositoryName: String, branch: String,
requestUserName: String, requestRepositoryName: String, requestCommitId: String): (Seq[Seq[CommitInfo]], Seq[DiffInfo]) = requestUserName: String, requestRepositoryName: String, requestCommitId: String): (Seq[Seq[CommitInfo]], Seq[DiffInfo]) =
using( using(
Git.open(getRepositoryDir(userName, repositoryName)), Git.open(getRepositoryDir(userName, repositoryName)),
Git.open(getRepositoryDir(requestUserName, requestRepositoryName)) Git.open(getRepositoryDir(requestUserName, requestRepositoryName))
){ (oldGit, newGit) => ){ (oldGit, newGit) =>
val oldId = oldGit.getRepository.resolve(branch) val oldId = oldGit.getRepository.resolve(branch)
val newId = newGit.getRepository.resolve(requestCommitId) val newId = newGit.getRepository.resolve(requestCommitId)
val commits = newGit.log.addRange(oldId, newId).call.iterator.asScala.map { revCommit => val commits = newGit.log.addRange(oldId, newId).call.iterator.asScala.map { revCommit =>
new CommitInfo(revCommit) new CommitInfo(revCommit)
}.toList.splitWith { (commit1, commit2) => }.toList.splitWith { (commit1, commit2) =>
view.helpers.date(commit1.commitTime) == view.helpers.date(commit2.commitTime) view.helpers.date(commit1.commitTime) == view.helpers.date(commit2.commitTime)
} }
val diffs = JGitUtil.getDiffs(newGit, oldId.getName, newId.getName, true) val diffs = JGitUtil.getDiffs(newGit, oldId.getName, newId.getName, true)
(commits, diffs) (commits, diffs)
} }
private def searchPullRequests(userName: Option[String], repository: RepositoryService.RepositoryInfo) = private def searchPullRequests(userName: Option[String], repository: RepositoryService.RepositoryInfo) =
defining(repository.owner, repository.name){ case (owner, repoName) => defining(repository.owner, repository.name){ case (owner, repoName) =>
val page = IssueSearchCondition.page(request) val page = IssueSearchCondition.page(request)
val sessionKey = Keys.Session.Pulls(owner, repoName) val sessionKey = Keys.Session.Pulls(owner, repoName)
// retrieve search condition // retrieve search condition
val condition = session.putAndGet(sessionKey, val condition = session.putAndGet(sessionKey,
if(request.hasQueryString) IssueSearchCondition(request) if(request.hasQueryString) IssueSearchCondition(request)
else session.getAs[IssueSearchCondition](sessionKey).getOrElse(IssueSearchCondition()) else session.getAs[IssueSearchCondition](sessionKey).getOrElse(IssueSearchCondition())
) )
issues.html.list( issues.html.list(
"pulls", "pulls",
searchIssue(condition, Map.empty, true, (page - 1) * PullRequestLimit, PullRequestLimit, owner -> repoName), searchIssue(condition, Map.empty, true, (page - 1) * PullRequestLimit, PullRequestLimit, owner -> repoName),
page, page,
(getCollaborators(owner, repoName) :+ owner).sorted, (getCollaborators(owner, repoName) :+ owner).sorted,
getMilestones(owner, repoName), getMilestones(owner, repoName),
getLabels(owner, repoName), getLabels(owner, repoName),
countIssue(condition.copy(state = "open" ), Map.empty, true, owner -> repoName), countIssue(condition.copy(state = "open" ), Map.empty, true, owner -> repoName),
countIssue(condition.copy(state = "closed"), Map.empty, true, owner -> repoName), countIssue(condition.copy(state = "closed"), Map.empty, true, owner -> repoName),
condition, condition,
repository, repository,
hasWritePermission(owner, repoName, context.loginAccount)) hasWritePermission(owner, repoName, context.loginAccount))
} }
} }