mirror of
https://github.com/gitbucket/gitbucket.git
synced 2025-11-01 11:06:06 +01:00
Add lock for repository operation.
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
package app
|
||||
|
||||
import util.Directory._
|
||||
import util.{JGitUtil, UsersAuthenticator, ReferrerAuthenticator}
|
||||
import util.{LockUtil, JGitUtil, UsersAuthenticator, ReferrerAuthenticator}
|
||||
import service._
|
||||
import java.io.File
|
||||
import org.eclipse.jgit.api.Git
|
||||
@@ -48,6 +48,8 @@ trait CreateRepositoryControllerBase extends ControllerBase {
|
||||
* Create new repository.
|
||||
*/
|
||||
post("/new", newForm)(usersOnly { form =>
|
||||
LockUtil.lock(s"${form.owner}/${form.name}/create"){
|
||||
if(getRepository(form.owner, form.name, baseUrl).isEmpty){
|
||||
val ownerAccount = getAccountByUserName(form.owner).get
|
||||
val loginAccount = context.loginAccount.get
|
||||
val loginUserName = loginAccount.userName
|
||||
@@ -104,15 +106,18 @@ trait CreateRepositoryControllerBase extends ControllerBase {
|
||||
|
||||
// Record activity
|
||||
recordCreateRepositoryActivity(form.owner, form.name, loginUserName)
|
||||
}
|
||||
|
||||
// redirect to the repository
|
||||
redirect(s"/${form.owner}/${form.name}")
|
||||
}
|
||||
})
|
||||
|
||||
post("/:owner/:repository/_fork")(referrersOnly { repository =>
|
||||
val loginAccount = context.loginAccount.get
|
||||
val loginUserName = loginAccount.userName
|
||||
|
||||
LockUtil.lock(s"${loginUserName}/${repository.name}/create"){
|
||||
if(getRepository(loginUserName, repository.name, baseUrl).isEmpty){
|
||||
// Insert to the database at first
|
||||
val originUserName = repository.repository.originUserName.getOrElse(repository.owner)
|
||||
@@ -161,6 +166,7 @@ trait CreateRepositoryControllerBase extends ControllerBase {
|
||||
}
|
||||
// redirect to the repository
|
||||
redirect("/%s/%s".format(loginUserName, repository.name))
|
||||
}
|
||||
})
|
||||
|
||||
private def insertDefaultLabels(userName: String, repositoryName: String): Unit = {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package app
|
||||
|
||||
import util.{CollaboratorsAuthenticator, JGitUtil, ReferrerAuthenticator}
|
||||
import util.{LockUtil, CollaboratorsAuthenticator, JGitUtil, ReferrerAuthenticator}
|
||||
import util.Directory._
|
||||
import util.Implicits._
|
||||
import util.JGitUtil.{DiffInfo, CommitInfo}
|
||||
@@ -82,6 +82,7 @@ trait PullRequestsControllerBase extends ControllerBase {
|
||||
})
|
||||
|
||||
post("/:owner/:repository/pulls/:id/merge", mergeForm)(collaboratorsOnly { (form, repository) =>
|
||||
LockUtil.lock(s"${repository.owner}/${repository.name}/merge"){
|
||||
val issueId = params("id").toInt
|
||||
|
||||
getPullRequest(repository.owner, repository.name, issueId).map { case (issue, pullreq) =>
|
||||
@@ -110,10 +111,11 @@ trait PullRequestsControllerBase extends ControllerBase {
|
||||
throw new RuntimeException("This pull request can't merge automatically.")
|
||||
}
|
||||
|
||||
// git.commit
|
||||
// .setCommitter(new PersonIdent(loginAccount.userName, loginAccount.mailAddress))
|
||||
// .setMessage(s"Merge pull request #${issueId} from ${pullreq.requestUserName}/${pullreq.requestRepositoryName}\n"
|
||||
// + form.message).call
|
||||
// TODO merge commit
|
||||
// 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
|
||||
|
||||
val (commits, _) = getRequestCompareInfo(repository.owner, repository.name, pullreq.commitIdFrom,
|
||||
@@ -130,14 +132,19 @@ trait PullRequestsControllerBase extends ControllerBase {
|
||||
FileUtils.deleteDirectory(tmpdir)
|
||||
}
|
||||
} getOrElse NotFound
|
||||
}
|
||||
})
|
||||
|
||||
private def checkConflict(userName: String, repositoryName: String, branch: String,
|
||||
requestUserName: String, requestRepositoryName: String, requestBranch: String): Boolean = {
|
||||
LockUtil.lock(s"${userName}/${repositoryName}/merge-check"){
|
||||
val remote = getRepositoryDir(userName, repositoryName)
|
||||
val tmpdir = new java.io.File(getTemporaryDir(userName, repositoryName), "merge-check")
|
||||
val git = Git.cloneRepository.setDirectory(tmpdir).setURI(remote.toURI.toString).call
|
||||
try {
|
||||
if(tmpdir.exists()){
|
||||
FileUtils.deleteDirectory(tmpdir)
|
||||
}
|
||||
git.checkout.setName(branch).call
|
||||
|
||||
git.fetch
|
||||
@@ -155,6 +162,7 @@ trait PullRequestsControllerBase extends ControllerBase {
|
||||
FileUtils.deleteDirectory(tmpdir)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
get("/:owner/:repository/pulls/compare")(collaboratorsOnly { newRepo =>
|
||||
(newRepo.repository.originUserName, newRepo.repository.originRepositoryName) match {
|
||||
|
||||
@@ -106,8 +106,9 @@ trait WikiControllerBase extends ControllerBase {
|
||||
|
||||
get("/:owner/:repository/wiki/:page/_delete")(collaboratorsOnly { repository =>
|
||||
val pageName = StringUtil.urlDecode(params("page"))
|
||||
val account = context.loginAccount.get
|
||||
|
||||
deleteWikiPage(repository.owner, repository.name, pageName, context.loginAccount.get.userName, s"Delete ${pageName}")
|
||||
deleteWikiPage(repository.owner, repository.name, pageName, account.userName, account.mailAddress, s"Delete ${pageName}")
|
||||
updateLastActivityDate(repository.owner, repository.name)
|
||||
|
||||
redirect(s"/${repository.owner}/${repository.name}/wiki")
|
||||
|
||||
@@ -4,10 +4,7 @@ import java.io.File
|
||||
import java.util.Date
|
||||
import org.eclipse.jgit.api.Git
|
||||
import org.apache.commons.io.FileUtils
|
||||
import util.JGitUtil.DiffInfo
|
||||
import util.{Directory, JGitUtil}
|
||||
import org.eclipse.jgit.treewalk.CanonicalTreeParser
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
import util.{Directory, JGitUtil, LockUtil}
|
||||
|
||||
object WikiService {
|
||||
|
||||
@@ -31,32 +28,39 @@ object WikiService {
|
||||
*/
|
||||
case class WikiPageHistoryInfo(name: String, committer: String, message: String, date: Date)
|
||||
|
||||
/**
|
||||
* lock objects
|
||||
*/
|
||||
private val locks = new ConcurrentHashMap[String, AnyRef]()
|
||||
|
||||
/**
|
||||
* Returns the lock object for the specified repository.
|
||||
*/
|
||||
private def getLockObject(owner: String, repository: String): AnyRef = synchronized {
|
||||
val key = owner + "/" + repository
|
||||
if(!locks.containsKey(key)){
|
||||
locks.put(key, new AnyRef())
|
||||
}
|
||||
locks.get(key)
|
||||
}
|
||||
|
||||
/**
|
||||
* Synchronizes a given function which modifies the working copy of the wiki repository.
|
||||
*
|
||||
* @param owner the repository owner
|
||||
* @param repository the repository name
|
||||
* @param f the function which modifies the working copy of the wiki repository
|
||||
* @tparam T the return type of the given function
|
||||
* @return the result of the given function
|
||||
*/
|
||||
def lock[T](owner: String, repository: String)(f: => T): T = getLockObject(owner, repository).synchronized(f)
|
||||
// /**
|
||||
// * lock objects
|
||||
// */
|
||||
// private val locks = new ConcurrentHashMap[String, Lock]()
|
||||
//
|
||||
// /**
|
||||
// * Returns the lock object for the specified repository.
|
||||
// */
|
||||
// private def getLockObject(owner: String, repository: String): Lock = synchronized {
|
||||
// val key = owner + "/" + repository
|
||||
// if(!locks.containsKey(key)){
|
||||
// locks.put(key, new ReentrantLock())
|
||||
// }
|
||||
// locks.get(key)
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * Synchronizes a given function which modifies the working copy of the wiki repository.
|
||||
// *
|
||||
// * @param owner the repository owner
|
||||
// * @param repository the repository name
|
||||
// * @param f the function which modifies the working copy of the wiki repository
|
||||
// * @tparam T the return type of the given function
|
||||
// * @return the result of the given function
|
||||
// */
|
||||
// def lock[T](owner: String, repository: String)(f: => T): T = {
|
||||
// val lock = getLockObject(owner, repository)
|
||||
// try {
|
||||
// f
|
||||
// } finally {
|
||||
// lock.unlock()
|
||||
// }
|
||||
// }
|
||||
|
||||
}
|
||||
|
||||
@@ -64,7 +68,7 @@ trait WikiService {
|
||||
import WikiService._
|
||||
|
||||
def createWikiRepository(loginAccount: model.Account, owner: String, repository: String): Unit = {
|
||||
lock(owner, repository){
|
||||
LockUtil.lock(s"${owner}/${repository}/wiki"){
|
||||
val dir = Directory.getWikiRepositoryDir(owner, repository)
|
||||
if(!dir.exists){
|
||||
try {
|
||||
@@ -132,7 +136,7 @@ trait WikiService {
|
||||
def saveWikiPage(owner: String, repository: String, currentPageName: String, newPageName: String,
|
||||
content: String, committer: model.Account, message: String): Unit = {
|
||||
|
||||
lock(owner, repository){
|
||||
LockUtil.lock(s"${owner}/${repository}/wiki"){
|
||||
// clone working copy
|
||||
val workDir = Directory.getWikiWorkDir(owner, repository)
|
||||
cloneOrPullWorkingCopy(workDir, owner, repository)
|
||||
@@ -168,8 +172,9 @@ trait WikiService {
|
||||
/**
|
||||
* Delete the wiki page.
|
||||
*/
|
||||
def deleteWikiPage(owner: String, repository: String, pageName: String, committer: String, message: String): Unit = {
|
||||
lock(owner, repository){
|
||||
def deleteWikiPage(owner: String, repository: String, pageName: String,
|
||||
committer: String, mailAddress: String, message: String): Unit = {
|
||||
LockUtil.lock(s"${owner}/${repository}/wiki"){
|
||||
// clone working copy
|
||||
val workDir = Directory.getWikiWorkDir(owner, repository)
|
||||
cloneOrPullWorkingCopy(workDir, owner, repository)
|
||||
@@ -181,8 +186,7 @@ trait WikiService {
|
||||
git.rm.addFilepattern(pageName + ".md").call
|
||||
|
||||
// commit and push
|
||||
// TODO committer's mail address
|
||||
git.commit.setAuthor(committer, committer + "@devnull").setMessage(message).call
|
||||
git.commit.setAuthor(committer, mailAddress).setMessage(message).call
|
||||
git.push.call
|
||||
}
|
||||
}
|
||||
|
||||
36
src/main/scala/util/LockUtil.scala
Normal file
36
src/main/scala/util/LockUtil.scala
Normal file
@@ -0,0 +1,36 @@
|
||||
package util
|
||||
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
import java.util.concurrent.locks.{ReentrantLock, Lock}
|
||||
|
||||
object LockUtil {
|
||||
|
||||
/**
|
||||
* lock objects
|
||||
*/
|
||||
private val locks = new ConcurrentHashMap[String, Lock]()
|
||||
|
||||
/**
|
||||
* Returns the lock object for the specified repository.
|
||||
*/
|
||||
private def getLockObject(key: String): Lock = synchronized {
|
||||
if(!locks.containsKey(key)){
|
||||
locks.put(key, new ReentrantLock())
|
||||
}
|
||||
locks.get(key)
|
||||
}
|
||||
|
||||
/**
|
||||
* Synchronizes a given function which modifies the working copy of the wiki repository.
|
||||
*/
|
||||
def lock[T](key: String)(f: => T): T = {
|
||||
val lock = getLockObject(key)
|
||||
try {
|
||||
lock.lock()
|
||||
f
|
||||
} finally {
|
||||
lock.unlock()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user