mirror of
				https://github.com/gitbucket/gitbucket.git
				synced 2025-11-03 20:15:59 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			256 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			Scala
		
	
	
	
	
	
			
		
		
	
	
			256 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			Scala
		
	
	
	
	
	
package app
 | 
						|
 | 
						|
import util.{CollaboratorsAuthenticator, FileUtil, JGitUtil, ReferrerAuthenticator}
 | 
						|
import util.Directory._
 | 
						|
import service._
 | 
						|
import org.eclipse.jgit.treewalk.CanonicalTreeParser
 | 
						|
import util.JGitUtil.{DiffInfo, CommitInfo}
 | 
						|
import scala.collection.mutable.ArrayBuffer
 | 
						|
import org.eclipse.jgit.api.Git
 | 
						|
import jp.sf.amateras.scalatra.forms._
 | 
						|
import util.JGitUtil.DiffInfo
 | 
						|
import scala.Some
 | 
						|
import util.JGitUtil.CommitInfo
 | 
						|
import org.eclipse.jgit.transport.RefSpec
 | 
						|
import org.apache.commons.io.FileUtils
 | 
						|
import org.eclipse.jgit.lib.PersonIdent
 | 
						|
 | 
						|
class PullRequestsController extends PullRequestsControllerBase
 | 
						|
  with RepositoryService with AccountService with IssuesService with PullRequestService with MilestonesService with ActivityService
 | 
						|
  with ReferrerAuthenticator with CollaboratorsAuthenticator
 | 
						|
 | 
						|
trait PullRequestsControllerBase extends ControllerBase {
 | 
						|
  self: RepositoryService with IssuesService with MilestonesService with ActivityService with PullRequestService
 | 
						|
    with ReferrerAuthenticator with CollaboratorsAuthenticator =>
 | 
						|
 | 
						|
  val pullRequestForm = 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))),
 | 
						|
    "requestBranch"   -> trim(text(required, maxlength(100)))
 | 
						|
  )(PullRequestForm.apply)
 | 
						|
 | 
						|
  val mergeForm = mapping(
 | 
						|
    "message" -> trim(label("Message", text(required)))
 | 
						|
  )(MergeForm.apply)
 | 
						|
 | 
						|
  case class PullRequestForm(title: String, content: Option[String], branch: String,
 | 
						|
                             requestUserName: String, requestBranch: String)
 | 
						|
 | 
						|
  case class MergeForm(message: String)
 | 
						|
 | 
						|
  get("/:owner/:repository/pulls")(referrersOnly { repository =>
 | 
						|
    pulls.html.list(repository)
 | 
						|
  })
 | 
						|
 | 
						|
  get("/:owner/:repository/pulls/:id")(referrersOnly { repository =>
 | 
						|
    val owner   = repository.owner
 | 
						|
    val name    = repository.name
 | 
						|
    val issueId = params("id").toInt
 | 
						|
 | 
						|
    getPullRequest(owner, name, issueId) map { case(issue, pullreq) =>
 | 
						|
      pulls.html.pullreq(
 | 
						|
        issue, pullreq,
 | 
						|
        getComments(owner, name, issueId.toInt),
 | 
						|
        (getCollaborators(owner, name) :+ owner).sorted,
 | 
						|
        getMilestones(owner, name),
 | 
						|
        hasWritePermission(owner, name, context.loginAccount),
 | 
						|
        repository)
 | 
						|
    } getOrElse NotFound
 | 
						|
  })
 | 
						|
 | 
						|
  get("/:owner/:repository/pulls/:id/commits")(referrersOnly { repository =>
 | 
						|
    val owner   = repository.owner
 | 
						|
    val name    = repository.name
 | 
						|
    val issueId = params("id").toInt
 | 
						|
 | 
						|
    getPullRequest(owner, name, issueId) map { case(issue, pullreq) =>
 | 
						|
      pulls.html.commits(
 | 
						|
        issue, pullreq,
 | 
						|
        getCompareInfo(owner, name, pullreq.branch, pullreq.requestUserName, pullreq.requestRepositoryName, pullreq.requestBranch)._1,
 | 
						|
        hasWritePermission(owner, name, context.loginAccount),
 | 
						|
        repository)
 | 
						|
    } getOrElse NotFound
 | 
						|
  })
 | 
						|
 | 
						|
  get("/:owner/:repository/pulls/:id/files")(referrersOnly { repository =>
 | 
						|
    val owner   = repository.owner
 | 
						|
    val name    = repository.name
 | 
						|
    val issueId = params("id").toInt
 | 
						|
 | 
						|
    getPullRequest(owner, name, issueId) map { case(issue, pullreq) =>
 | 
						|
      JGitUtil.withGit(getRepositoryDir(owner, name)){ git =>
 | 
						|
        val newId = git.getRepository.resolve(pullreq.requestBranch)
 | 
						|
 | 
						|
        pulls.html.files(
 | 
						|
          issue, pullreq,
 | 
						|
          getCompareInfo(owner, name, pullreq.branch, pullreq.requestUserName, pullreq.requestRepositoryName, pullreq.requestBranch)._2,
 | 
						|
          newId.getName,
 | 
						|
          hasWritePermission(owner, name, context.loginAccount),
 | 
						|
          repository)
 | 
						|
      }
 | 
						|
    } getOrElse NotFound
 | 
						|
  })
 | 
						|
 | 
						|
 | 
						|
  post("/:owner/:repository/pulls/:id/merge", mergeForm)(collaboratorsOnly { (form, repository) =>
 | 
						|
    val issueId = params("id").toInt
 | 
						|
 | 
						|
    getPullRequest(repository.owner, repository.name, issueId).map { case (issue, pullreq) =>
 | 
						|
      val remote = getRepositoryDir(repository.owner, repository.name)
 | 
						|
      val tmpdir = new java.io.File(getTemporaryDir(repository.owner, repository.name), s"merge-${issueId}")
 | 
						|
      val git = Git.cloneRepository.setDirectory(tmpdir).setURI(remote.toURI.toString).call
 | 
						|
 | 
						|
      try {
 | 
						|
        // TODO merge and close issue
 | 
						|
        val loginAccount = context.loginAccount.get
 | 
						|
        recordMergeActivity(repository.owner, repository.name, loginAccount.userName, issueId, form.message)
 | 
						|
 | 
						|
        git.checkout.setName(pullreq.branch).call
 | 
						|
 | 
						|
        git.fetch
 | 
						|
          .setRemote(getRepositoryDir(pullreq.requestUserName, pullreq.requestRepositoryName).toURI.toString)
 | 
						|
          .setRefSpecs(new RefSpec(s"refs/heads/${pullreq.branch}:refs/heads/${pullreq.requestBranch}")).call
 | 
						|
 | 
						|
        git.merge.include(git.getRepository.resolve("FETCH_HEAD")).setCommit(false).call
 | 
						|
 | 
						|
        git.commit
 | 
						|
          .setCommitter(new PersonIdent(loginAccount.userName, loginAccount.mailAddress))
 | 
						|
          .setMessage(s"Merge pull request #${issueId} from ${pullreq.requestUserName}/${pullreq.requestRepositoryName}\n"
 | 
						|
                     + form.message).call
 | 
						|
        git.push.call
 | 
						|
 | 
						|
      } finally {
 | 
						|
        git.getRepository.close
 | 
						|
        FileUtils.deleteDirectory(tmpdir)
 | 
						|
      }
 | 
						|
    } getOrElse NotFound
 | 
						|
  })
 | 
						|
 | 
						|
  get("/:owner/:repository/pulls/compare")(collaboratorsOnly { newRepo =>
 | 
						|
    (newRepo.repository.originUserName, newRepo.repository.originRepositoryName) match {
 | 
						|
      case (None,_)|(_, None) => NotFound // TODO BadRequest?
 | 
						|
      case (Some(originUserName), Some(originRepositoryName)) => {
 | 
						|
        getRepository(originUserName, originRepositoryName, baseUrl).map { oldRepo =>
 | 
						|
          withGit(
 | 
						|
            getRepositoryDir(originUserName, originRepositoryName),
 | 
						|
            getRepositoryDir(params("owner"), params("repository"))
 | 
						|
          ){ (oldGit, newGit) =>
 | 
						|
            val oldBranch = JGitUtil.getDefaultBranch(oldGit, oldRepo).get._2
 | 
						|
            val newBranch = JGitUtil.getDefaultBranch(newGit, newRepo).get._2
 | 
						|
 | 
						|
            redirect(s"${context.path}/${newRepo.owner}/${newRepo.name}/pulls/compare/${originUserName}:${oldBranch}...${newBranch}")
 | 
						|
          }
 | 
						|
        } getOrElse NotFound
 | 
						|
      }
 | 
						|
    }
 | 
						|
  })
 | 
						|
 | 
						|
  get("/:owner/:repository/pulls/compare/*:*...*")(collaboratorsOnly { repository =>
 | 
						|
    if(repository.repository.originUserName.isEmpty || repository.repository.originRepositoryName.isEmpty){
 | 
						|
      NotFound // TODO BadRequest?
 | 
						|
    } else {
 | 
						|
      getRepository(
 | 
						|
        repository.repository.originUserName.get,
 | 
						|
        repository.repository.originRepositoryName.get, baseUrl
 | 
						|
      ).map{ originRepository =>
 | 
						|
        val Seq(origin, originId, forkedId) = multiParams("splat")
 | 
						|
        val userName       = params("owner")
 | 
						|
        val repositoryName = params("repository")
 | 
						|
 | 
						|
        JGitUtil.withGit(getRepositoryDir(userName, repositoryName)){ git =>
 | 
						|
          val newId = git.getRepository.resolve(forkedId)
 | 
						|
 | 
						|
          val pullreq = getCompareInfo(
 | 
						|
            origin, repository.repository.originRepositoryName.get, originId,
 | 
						|
            params("owner"), params("repository"), forkedId)
 | 
						|
 | 
						|
          pulls.html.compare(pullreq._1, pullreq._2, origin, originId, forkedId, newId.getName, repository, originRepository)
 | 
						|
        }
 | 
						|
      } getOrElse NotFound
 | 
						|
    }
 | 
						|
  })
 | 
						|
 | 
						|
  post("/:owner/:repository/pulls/new", pullRequestForm)(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.requestBranch)
 | 
						|
 | 
						|
    recordPullRequestActivity(repository.owner, repository.name, loginUserName, issueId, form.title)
 | 
						|
 | 
						|
    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 = {
 | 
						|
    val oldGit = Git.open(oldDir)
 | 
						|
    val newGit = Git.open(newDir)
 | 
						|
    try {
 | 
						|
      action(oldGit, newGit)
 | 
						|
    } finally {
 | 
						|
      oldGit.getRepository.close
 | 
						|
      newGit.getRepository.close
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Returns the commits and diffs between specified repository and revision.
 | 
						|
   */
 | 
						|
  private def getCompareInfo(userName: String, repositoryName: String, branch: String,
 | 
						|
      requestUserName: String, requestRepositoryName: String, requestBranch: String): (Seq[Seq[CommitInfo]], Seq[DiffInfo]) = {
 | 
						|
    withGit(
 | 
						|
      getRepositoryDir(userName, repositoryName),
 | 
						|
      getRepositoryDir(requestUserName, requestRepositoryName)
 | 
						|
    ){ (oldGit, newGit) =>
 | 
						|
      val oldReader = oldGit.getRepository.newObjectReader
 | 
						|
      val oldTreeIter = new CanonicalTreeParser
 | 
						|
      oldTreeIter.reset(oldReader, oldGit.getRepository.resolve(s"${branch}^{tree}"))
 | 
						|
 | 
						|
      val newReader = newGit.getRepository.newObjectReader
 | 
						|
      val newTreeIter = new CanonicalTreeParser
 | 
						|
      newTreeIter.reset(newReader, newGit.getRepository.resolve(s"${requestBranch}^{tree}"))
 | 
						|
 | 
						|
      import scala.collection.JavaConverters._
 | 
						|
      import util.Implicits._
 | 
						|
 | 
						|
      val oldId = oldGit.getRepository.resolve(branch)
 | 
						|
      val newId = newGit.getRepository.resolve(requestBranch)
 | 
						|
      val i = newGit.log.addRange(oldId, newId).call.iterator.asScala
 | 
						|
 | 
						|
      val commits = newGit.log.addRange(oldId, newId).call.iterator.asScala.map { revCommit =>
 | 
						|
        new CommitInfo(revCommit)
 | 
						|
      }.toSeq.splitWith{ (commit1, commit2) =>
 | 
						|
        view.helpers.date(commit1.time) == view.helpers.date(commit2.time)
 | 
						|
      }
 | 
						|
 | 
						|
      val diffs = newGit.diff.setOldTree(oldTreeIter).setNewTree(newTreeIter).call.asScala.map { diff =>
 | 
						|
        if(FileUtil.isImage(diff.getOldPath) || FileUtil.isImage(diff.getNewPath)){
 | 
						|
          DiffInfo(diff.getChangeType, diff.getOldPath, diff.getNewPath, None, None)
 | 
						|
        } else {
 | 
						|
          DiffInfo(diff.getChangeType, diff.getOldPath, diff.getNewPath,
 | 
						|
            JGitUtil.getContent(oldGit, diff.getOldId.toObjectId, false).filter(FileUtil.isText).map(new String(_, "UTF-8")),
 | 
						|
            JGitUtil.getContent(newGit, diff.getNewId.toObjectId, false).filter(FileUtil.isText).map(new String(_, "UTF-8")))
 | 
						|
        }
 | 
						|
      }.toSeq
 | 
						|
 | 
						|
      (commits, diffs)
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
}
 |