Improve checkConflict

* Delete temporary RefSpec after checkConflict
* mergeguide use checkconflictInPullRequest instead of checkconflict
This commit is contained in:
Tomofumi Tanaka
2013-10-30 01:33:56 +09:00
parent dcbf283c9d
commit f96040eade
2 changed files with 84 additions and 55 deletions

View File

@@ -9,7 +9,6 @@ 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 org.apache.commons.io.FileUtils
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 org.eclipse.jgit.api.MergeCommand.FastForwardMode import org.eclipse.jgit.api.MergeCommand.FastForwardMode
@@ -20,6 +19,7 @@ import service.RepositoryService.RepositoryTreeNode
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
class PullRequestsController extends PullRequestsControllerBase class PullRequestsController extends PullRequestsControllerBase
with RepositoryService with AccountService with IssuesService with PullRequestService with MilestonesService with ActivityService with RepositoryService with AccountService with IssuesService with PullRequestService with MilestonesService with ActivityService
@@ -95,7 +95,7 @@ trait PullRequestsControllerBase extends ControllerBase {
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(
checkConflict(owner, name, pullreq.branch, pullreq.requestUserName, name, pullreq.requestBranch), checkConflictInPullRequest(owner, name, pullreq.branch, pullreq.requestUserName, name, pullreq.requestBranch, issueId),
pullreq, pullreq,
s"${baseUrl}${context.path}/git/${pullreq.requestUserName}/${pullreq.requestRepositoryName}.git") s"${baseUrl}${context.path}/git/${pullreq.requestUserName}/${pullreq.requestRepositoryName}.git")
} }
@@ -319,72 +319,89 @@ trait PullRequestsControllerBase extends ControllerBase {
*/ */
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 = {
// TODO remove logging code
logger.info(s"userName: ${userName}" +
s", repositoryName:${repositoryName}" +
s", branch: ${branch}" +
s", requestUserName: ${requestUserName}" +
s", requestRepositoryName:${requestRepositoryName}" +
s", requestBranch:${requestBranch}")
LockUtil.lock(s"${userName}/${repositoryName}/merge-check"){ LockUtil.lock(s"${userName}/${repositoryName}/merge-check"){
using(Git.open(getRepositoryDir(requestUserName, requestRepositoryName))) { git => using(Git.open(getRepositoryDir(requestUserName, requestRepositoryName))) { git =>
// fetch objects from origin repository branch
val fromRefName = s"refs/heads/${branch}" val fromRefName = s"refs/heads/${branch}"
val toRefName = s"refs/merge-check/${userName}/${branch}" val toRefName = s"refs/merge-check/${userName}/${branch}"
val refSpec = new RefSpec(s"${fromRefName}:${toRefName}").setForceUpdate(true)
git.fetch withTmpRefSpec(new RefSpec(s"${fromRefName}:${toRefName}").setForceUpdate(true), git) { ref =>
.setRemote(getRepositoryDir(userName, repositoryName).toURI.toString) // fetch objects from origin repository branch
.setRefSpecs(refSpec) git.fetch
.call .setRemote(getRepositoryDir(userName, repositoryName).toURI.toString)
.setRefSpecs(ref)
// merge check .call
val merger = MergeStrategy.RECURSIVE.newMerger(git.getRepository, true) // merge conflict check
val to = git.getRepository.resolve(s"refs/merge-check/${userName}/${branch}") val merger = MergeStrategy.RECURSIVE.newMerger(git.getRepository, true)
val from = git.getRepository.resolve(s"refs/heads/${requestBranch}") val mergeTip = git.getRepository.resolve(toRefName)
val mergeBaseTip = git.getRepository.resolve(s"refs/heads/${requestBranch}")
logger.info(s"to: ${to}, from: ${from}") try {
try { !merger.merge(mergeTip, mergeBaseTip)
!merger.merge(to, from) } catch {
} catch { case e: NoMergeBaseException => true
case e: Throwable => {
logger.error("Merge Check Failed.", e)
true
} }
} }
}
}
}
// TODO Delete refs/merge-check /**
* 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,
requestUserName: String, requestRepositoryName: String, requestBranch: String,
issueId: Int): Boolean = {
LockUtil.lock(s"${issueId}/merge") {
using(Git.open(getRepositoryDir(userName, repositoryName))) { git =>
// fetch pull request content into refs/pull/${issueId}/head
val headName = s"refs/pull/${issueId}/head"
val headRef = new RefSpec(s"refs/heads/${requestBranch}:${headName}").setForceUpdate(true)
git.fetch
.setRemote(getRepositoryDir(requestUserName, requestRepositoryName).toURI.toString)
.setRefSpecs(headRef)
.call
// MEMO CODE. create merge commit and update refs. // setup mergeBase refs/pull/${issueId}/merge
/* val mergeName = s"refs/pull/${issueId}/merge"
val updateMergeRef = git.getRepository.updateRef(mergeName)
updateMergeRef.setNewObjectId(git.getRepository.resolve(s"refs/heads/${branch}"))
updateMergeRef.forceUpdate
// creates a mergeCommit Object // merge
val mergeCommit = new CommitBuilder() val merger = MergeStrategy.RECURSIVE.newMerger(git.getRepository, true)
mergeCommit.setTreeId(merger.getResultTreeId) val mergeTip = git.getRepository.resolve(headName)
mergeCommit.setParentIds(Array[ObjectId](to, from): _*) val mergeBaseTip = git.getRepository.resolve(s"refs/heads/${branch}")
val conflicted = try {
!merger.merge(mergeTip, mergeBaseTip)
} catch {
case e: NoMergeBaseException => true
}
val author = new PersonIdent("GitBucket Auto Merger", "gitbucket@example.com") if (!conflicted) {
mergeCommit.setAuthor(author) // creates merge commit
mergeCommit.setCommitter(author) val mergeCommit = new CommitBuilder()
mergeCommit.setMessage("Merge pull request test1") mergeCommit.setTreeId(merger.getResultTreeId)
val inserter = git.getRepository.newObjectInserter mergeCommit.setParentIds(Array[ObjectId](mergeBaseTip, mergeTip): _*)
val loginAccount = context.loginAccount.get
val committer = new PersonIdent(loginAccount.fullName, loginAccount.mailAddress)
mergeCommit.setAuthor(committer)
mergeCommit.setCommitter(committer)
mergeCommit.setMessage(s"Merge pull #${issueId} from ${requestUserName}/${requestRepositoryName}\n")
val inserter = git.getRepository.newObjectInserter
// insertObject and got mergeCommit Object Id // insertObject and got mergeCommit Object Id
val mergeCommitId = inserter.insert(mergeCommit) val mergeCommitId = inserter.insert(mergeCommit)
logger.debug("mergeCommitId: " + mergeCommitId) inserter.flush()
inserter.flush() inserter.release()
inserter.release()
// update refs // update refs refs/pull/${issueId}/merge
val refUpdate = git.getRepository.updateRef("refs/heads/merge/test01") val refUpdate = git.getRepository.updateRef(mergeName)
refUpdate.setNewObjectId(mergeCommitId) refUpdate.setNewObjectId(mergeCommitId)
refUpdate.setForceUpdate(true) refUpdate.setForceUpdate(true)
refUpdate.setRefLogIdent(author) refUpdate.setRefLogIdent(committer)
refUpdate.setRefLogMessage("merged", true) refUpdate.setRefLogMessage("merged", true)
val updateResult = refUpdate.update() refUpdate.update()
}
*/ conflicted
} }
} }
} }

View File

@@ -3,6 +3,7 @@ package util
import org.eclipse.jgit.api.Git import org.eclipse.jgit.api.Git
import org.eclipse.jgit.revwalk.RevWalk import org.eclipse.jgit.revwalk.RevWalk
import org.eclipse.jgit.treewalk.TreeWalk import org.eclipse.jgit.treewalk.TreeWalk
import org.eclipse.jgit.transport.RefSpec
/** /**
* Provides control facilities. * Provides control facilities.
@@ -37,6 +38,17 @@ object ControlUtil {
def using[T](treeWalk: TreeWalk)(f: TreeWalk => T): T = def using[T](treeWalk: TreeWalk)(f: TreeWalk => T): T =
try f(treeWalk) finally treeWalk.release() try f(treeWalk) finally treeWalk.release()
def withTmpRefSpec[T](ref: RefSpec, git: Git)(f: RefSpec => T): T = {
try {
f(ref)
} finally {
val refUpdate = git.getRepository.updateRef(ref.getDestination)
refUpdate.setForceUpdate(true)
refUpdate.delete()
}
}
def executeIf(condition: => Boolean)(action: => Unit): Boolean = def executeIf(condition: => Boolean)(action: => Unit): Boolean =
if(condition){ if(condition){
action action