mirror of
https://github.com/gitbucket/gitbucket.git
synced 2025-11-08 06:25:51 +01:00
Merge remote-tracking branch 'master' into improve-pullreq-performance
This commit is contained in:
93
src/main/java/util/PatchUtil.java
Normal file
93
src/main/java/util/PatchUtil.java
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
package util;
|
||||||
|
|
||||||
|
import org.eclipse.jgit.api.errors.PatchApplyException;
|
||||||
|
import org.eclipse.jgit.diff.RawText;
|
||||||
|
import org.eclipse.jgit.internal.JGitText;
|
||||||
|
import org.eclipse.jgit.patch.FileHeader;
|
||||||
|
import org.eclipse.jgit.patch.HunkHeader;
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.text.MessageFormat;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class helps to apply patch. Most of these code came from {@link org.eclipse.jgit.api.ApplyCommand}.
|
||||||
|
*/
|
||||||
|
public class PatchUtil {
|
||||||
|
|
||||||
|
public static String apply(String source, String patch, FileHeader fh)
|
||||||
|
throws IOException, PatchApplyException {
|
||||||
|
RawText rt = new RawText(source.getBytes("UTF-8"));
|
||||||
|
List<String> oldLines = new ArrayList<String>(rt.size());
|
||||||
|
for (int i = 0; i < rt.size(); i++)
|
||||||
|
oldLines.add(rt.getString(i));
|
||||||
|
List<String> newLines = new ArrayList<String>(oldLines);
|
||||||
|
for (HunkHeader hh : fh.getHunks()) {
|
||||||
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
|
out.write(patch.getBytes("UTF-8"), hh.getStartOffset(), hh.getEndOffset() - hh.getStartOffset());
|
||||||
|
RawText hrt = new RawText(out.toByteArray());
|
||||||
|
List<String> hunkLines = new ArrayList<String>(hrt.size());
|
||||||
|
for (int i = 0; i < hrt.size(); i++)
|
||||||
|
hunkLines.add(hrt.getString(i));
|
||||||
|
int pos = 0;
|
||||||
|
for (int j = 1; j < hunkLines.size(); j++) {
|
||||||
|
String hunkLine = hunkLines.get(j);
|
||||||
|
switch (hunkLine.charAt(0)) {
|
||||||
|
case ' ':
|
||||||
|
if (!newLines.get(hh.getNewStartLine() - 1 + pos).equals(
|
||||||
|
hunkLine.substring(1))) {
|
||||||
|
throw new PatchApplyException(MessageFormat.format(
|
||||||
|
JGitText.get().patchApplyException, hh));
|
||||||
|
}
|
||||||
|
pos++;
|
||||||
|
break;
|
||||||
|
case '-':
|
||||||
|
if (!newLines.get(hh.getNewStartLine() - 1 + pos).equals(
|
||||||
|
hunkLine.substring(1))) {
|
||||||
|
throw new PatchApplyException(MessageFormat.format(
|
||||||
|
JGitText.get().patchApplyException, hh));
|
||||||
|
}
|
||||||
|
newLines.remove(hh.getNewStartLine() - 1 + pos);
|
||||||
|
break;
|
||||||
|
case '+':
|
||||||
|
newLines.add(hh.getNewStartLine() - 1 + pos,
|
||||||
|
hunkLine.substring(1));
|
||||||
|
pos++;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!isNoNewlineAtEndOfFile(fh))
|
||||||
|
newLines.add(""); //$NON-NLS-1$
|
||||||
|
if (!rt.isMissingNewlineAtEnd())
|
||||||
|
oldLines.add(""); //$NON-NLS-1$
|
||||||
|
if (!isChanged(oldLines, newLines))
|
||||||
|
return null; // don't touch the file
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
for (String l : newLines) {
|
||||||
|
// don't bother handling line endings - if it was windows, the \r is
|
||||||
|
// still there!
|
||||||
|
sb.append(l).append('\n');
|
||||||
|
}
|
||||||
|
sb.deleteCharAt(sb.length() - 1);
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isChanged(List<String> ol, List<String> nl) {
|
||||||
|
if (ol.size() != nl.size())
|
||||||
|
return true;
|
||||||
|
for (int i = 0; i < ol.size(); i++)
|
||||||
|
if (!ol.get(i).equals(nl.get(i)))
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isNoNewlineAtEndOfFile(FileHeader fh) {
|
||||||
|
HunkHeader lastHunk = fh.getHunks().get(fh.getHunks().size() - 1);
|
||||||
|
RawText lhrt = new RawText(lastHunk.getBuffer());
|
||||||
|
return lhrt.getString(lhrt.size() - 1).equals(
|
||||||
|
"\\ No newline at end of file"); //$NON-NLS-1$
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,7 +8,8 @@ import java.io.File
|
|||||||
import org.eclipse.jgit.api.Git
|
import org.eclipse.jgit.api.Git
|
||||||
import org.apache.commons.io._
|
import org.apache.commons.io._
|
||||||
import jp.sf.amateras.scalatra.forms._
|
import jp.sf.amateras.scalatra.forms._
|
||||||
import org.eclipse.jgit.lib.PersonIdent
|
import org.eclipse.jgit.lib.{FileMode, Constants, PersonIdent}
|
||||||
|
import org.eclipse.jgit.dircache.DirCache
|
||||||
|
|
||||||
class CreateRepositoryController extends CreateRepositoryControllerBase
|
class CreateRepositoryController extends CreateRepositoryControllerBase
|
||||||
with RepositoryService with AccountService with WikiService with LabelsService with ActivityService
|
with RepositoryService with AccountService with WikiService with LabelsService with ActivityService
|
||||||
@@ -73,13 +74,11 @@ trait CreateRepositoryControllerBase extends ControllerBase {
|
|||||||
JGitUtil.initRepository(gitdir)
|
JGitUtil.initRepository(gitdir)
|
||||||
|
|
||||||
if(form.createReadme){
|
if(form.createReadme){
|
||||||
FileUtil.withTmpDir(getInitRepositoryDir(form.owner, form.name)){ tmpdir =>
|
using(Git.open(gitdir)){ git =>
|
||||||
// Clone the repository
|
val builder = DirCache.newInCore.builder()
|
||||||
Git.cloneRepository.setURI(gitdir.toURI.toString).setDirectory(tmpdir).call
|
val inserter = git.getRepository.newObjectInserter()
|
||||||
|
val headId = git.getRepository.resolve(Constants.HEAD + "^{commit}")
|
||||||
// Create README.md
|
val content = if(form.description.nonEmpty){
|
||||||
FileUtils.writeStringToFile(new File(tmpdir, "README.md"),
|
|
||||||
if(form.description.nonEmpty){
|
|
||||||
form.name + "\n" +
|
form.name + "\n" +
|
||||||
"===============\n" +
|
"===============\n" +
|
||||||
"\n" +
|
"\n" +
|
||||||
@@ -87,14 +86,14 @@ trait CreateRepositoryControllerBase extends ControllerBase {
|
|||||||
} else {
|
} else {
|
||||||
form.name + "\n" +
|
form.name + "\n" +
|
||||||
"===============\n"
|
"===============\n"
|
||||||
}, "UTF-8")
|
}
|
||||||
|
|
||||||
val git = Git.open(tmpdir)
|
builder.add(JGitUtil.createDirCacheEntry("README.md", FileMode.REGULAR_FILE,
|
||||||
git.add.addFilepattern("README.md").call
|
inserter.insert(Constants.OBJ_BLOB, content.getBytes("UTF-8"))))
|
||||||
git.commit
|
builder.finish()
|
||||||
.setCommitter(new PersonIdent(loginAccount.fullName, loginAccount.mailAddress))
|
|
||||||
.setMessage("Initial commit").call
|
JGitUtil.createNewCommit(git, inserter, headId, builder.getDirCache.writeTree(inserter),
|
||||||
git.push.call
|
loginAccount.fullName, loginAccount.mailAddress, "Initial commit")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ trait WikiControllerBase extends ControllerBase with FlashMapSupport {
|
|||||||
val Array(from, to) = params("commitId").split("\\.\\.\\.")
|
val Array(from, to) = params("commitId").split("\\.\\.\\.")
|
||||||
|
|
||||||
using(Git.open(getWikiRepositoryDir(repository.owner, repository.name))){ git =>
|
using(Git.open(getWikiRepositoryDir(repository.owner, repository.name))){ git =>
|
||||||
wiki.html.compare(Some(pageName), from, to, JGitUtil.getDiffs(git, from, to, true), repository,
|
wiki.html.compare(Some(pageName), from, to, JGitUtil.getDiffs(git, from, to, true).filter(_.newPath == pageName + ".md"), repository,
|
||||||
hasWritePermission(repository.owner, repository.name, context.loginAccount), flash.get("info"))
|
hasWritePermission(repository.owner, repository.name, context.loginAccount), flash.get("info"))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -96,7 +96,7 @@ trait WikiControllerBase extends ControllerBase with FlashMapSupport {
|
|||||||
val Array(from, to) = params("commitId").split("\\.\\.\\.")
|
val Array(from, to) = params("commitId").split("\\.\\.\\.")
|
||||||
|
|
||||||
if(revertWikiPage(repository.owner, repository.name, from, to, context.loginAccount.get, None)){
|
if(revertWikiPage(repository.owner, repository.name, from, to, context.loginAccount.get, None)){
|
||||||
redirect(s"/${repository.owner}/${repository.name}/wiki/}")
|
redirect(s"/${repository.owner}/${repository.name}/wiki/")
|
||||||
} else {
|
} else {
|
||||||
flash += "info" -> "This patch was not able to be reversed."
|
flash += "info" -> "This patch was not able to be reversed."
|
||||||
redirect(s"/${repository.owner}/${repository.name}/wiki/_compare/${from}...${to}")
|
redirect(s"/${repository.owner}/${repository.name}/wiki/_compare/${from}...${to}")
|
||||||
@@ -195,7 +195,7 @@ trait WikiControllerBase extends ControllerBase with FlashMapSupport {
|
|||||||
|
|
||||||
private def conflictForEdit: Constraint = new Constraint(){
|
private def conflictForEdit: Constraint = new Constraint(){
|
||||||
override def validate(name: String, value: String): Option[String] = {
|
override def validate(name: String, value: String): Option[String] = {
|
||||||
optionIf(targetWikiPage.map(_.id != params("id")).getOrElse(true)){
|
optionIf(targetWikiPage.map(_.id != params("id")).getOrElse(false)){
|
||||||
Some("Someone has edited the wiki since you started. Please reload this page and re-apply your changes.")
|
Some("Someone has edited the wiki since you started. Please reload this page and re-apply your changes.")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +1,20 @@
|
|||||||
package service
|
package service
|
||||||
|
|
||||||
import java.io.File
|
|
||||||
import java.util.Date
|
import java.util.Date
|
||||||
import org.eclipse.jgit.api.Git
|
import org.eclipse.jgit.api.Git
|
||||||
import org.apache.commons.io.FileUtils
|
import org.apache.commons.io.FileUtils
|
||||||
import util.{StringUtil, Directory, JGitUtil, LockUtil}
|
import util.{PatchUtil, Directory, JGitUtil, LockUtil}
|
||||||
import util.ControlUtil._
|
import _root_.util.ControlUtil._
|
||||||
import org.eclipse.jgit.treewalk.CanonicalTreeParser
|
import org.eclipse.jgit.treewalk.{TreeWalk, CanonicalTreeParser}
|
||||||
import org.eclipse.jgit.diff.DiffFormatter
|
import org.eclipse.jgit.lib._
|
||||||
import org.eclipse.jgit.api.errors.PatchApplyException
|
import org.eclipse.jgit.dircache.{DirCache, DirCacheEntry}
|
||||||
|
import org.eclipse.jgit.revwalk.RevWalk
|
||||||
|
import org.eclipse.jgit.diff.{DiffEntry, DiffFormatter}
|
||||||
|
import java.io.ByteArrayInputStream
|
||||||
|
import org.eclipse.jgit.patch._
|
||||||
|
import org.eclipse.jgit.api.errors.PatchFormatException
|
||||||
|
import scala.collection.JavaConverters._
|
||||||
|
|
||||||
|
|
||||||
object WikiService {
|
object WikiService {
|
||||||
|
|
||||||
@@ -42,13 +48,8 @@ trait WikiService {
|
|||||||
LockUtil.lock(s"${owner}/${repository}/wiki"){
|
LockUtil.lock(s"${owner}/${repository}/wiki"){
|
||||||
defining(Directory.getWikiRepositoryDir(owner, repository)){ dir =>
|
defining(Directory.getWikiRepositoryDir(owner, repository)){ dir =>
|
||||||
if(!dir.exists){
|
if(!dir.exists){
|
||||||
try {
|
|
||||||
JGitUtil.initRepository(dir)
|
JGitUtil.initRepository(dir)
|
||||||
saveWikiPage(owner, repository, "Home", "Home", s"Welcome to the ${repository} wiki!!", loginAccount, "Initial Commit", None)
|
saveWikiPage(owner, repository, "Home", "Home", s"Welcome to the ${repository} wiki!!", loginAccount, "Initial Commit", None)
|
||||||
} finally {
|
|
||||||
// once delete cloned repository because initial cloned repository does not have 'branch.master.merge'
|
|
||||||
FileUtils.deleteDirectory(Directory.getWikiWorkDir(owner, repository))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -99,12 +100,13 @@ trait WikiService {
|
|||||||
*/
|
*/
|
||||||
def revertWikiPage(owner: String, repository: String, from: String, to: String,
|
def revertWikiPage(owner: String, repository: String, from: String, to: String,
|
||||||
committer: model.Account, pageName: Option[String]): Boolean = {
|
committer: model.Account, pageName: Option[String]): Boolean = {
|
||||||
LockUtil.lock(s"${owner}/${repository}/wiki"){
|
|
||||||
defining(Directory.getWikiWorkDir(owner, repository)){ workDir =>
|
|
||||||
// clone working copy
|
|
||||||
cloneOrPullWorkingCopy(workDir, owner, repository)
|
|
||||||
|
|
||||||
using(Git.open(workDir)){ git =>
|
case class RevertInfo(operation: String, filePath: String, source: String)
|
||||||
|
|
||||||
|
try {
|
||||||
|
LockUtil.lock(s"${owner}/${repository}/wiki"){
|
||||||
|
using(Git.open(Directory.getWikiRepositoryDir(owner, repository))){ git =>
|
||||||
|
|
||||||
val reader = git.getRepository.newObjectReader
|
val reader = git.getRepository.newObjectReader
|
||||||
val oldTreeIter = new CanonicalTreeParser
|
val oldTreeIter = new CanonicalTreeParser
|
||||||
oldTreeIter.reset(reader, git.getRepository.resolve(from + "^{tree}"))
|
oldTreeIter.reset(reader, git.getRepository.resolve(from + "^{tree}"))
|
||||||
@@ -112,7 +114,6 @@ trait WikiService {
|
|||||||
val newTreeIter = new CanonicalTreeParser
|
val newTreeIter = new CanonicalTreeParser
|
||||||
newTreeIter.reset(reader, git.getRepository.resolve(to + "^{tree}"))
|
newTreeIter.reset(reader, git.getRepository.resolve(to + "^{tree}"))
|
||||||
|
|
||||||
import scala.collection.JavaConverters._
|
|
||||||
val diffs = git.diff.setNewTree(oldTreeIter).setOldTree(newTreeIter).call.asScala.filter { diff =>
|
val diffs = git.diff.setNewTree(oldTreeIter).setOldTree(newTreeIter).call.asScala.filter { diff =>
|
||||||
pageName match {
|
pageName match {
|
||||||
case Some(x) => diff.getNewPath == x + ".md"
|
case Some(x) => diff.getNewPath == x + ".md"
|
||||||
@@ -127,57 +128,123 @@ trait WikiService {
|
|||||||
new String(out.toByteArray, "UTF-8")
|
new String(out.toByteArray, "UTF-8")
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
val p = new Patch()
|
||||||
git.apply.setPatch(new java.io.ByteArrayInputStream(patch.getBytes("UTF-8"))).call
|
p.parse(new ByteArrayInputStream(patch.getBytes("UTF-8")))
|
||||||
git.add.addFilepattern(".").call
|
if(!p.getErrors.isEmpty){
|
||||||
git.commit.setCommitter(committer.fullName, committer.mailAddress).setMessage(pageName match {
|
throw new PatchFormatException(p.getErrors())
|
||||||
case Some(x) => s"Revert ${from} ... ${to} on ${x}"
|
|
||||||
case None => s"Revert ${from} ... ${to}"
|
|
||||||
}).call
|
|
||||||
git.push.call
|
|
||||||
true
|
|
||||||
} catch {
|
|
||||||
case ex: PatchApplyException => false
|
|
||||||
}
|
}
|
||||||
|
val revertInfo = (p.getFiles.asScala.map { fh =>
|
||||||
|
fh.getChangeType match {
|
||||||
|
case DiffEntry.ChangeType.MODIFY => {
|
||||||
|
val source = getWikiPage(owner, repository, fh.getNewPath.replaceFirst("\\.md$", "")).map(_.content).getOrElse("")
|
||||||
|
val applied = PatchUtil.apply(source, patch, fh)
|
||||||
|
if(applied != null){
|
||||||
|
Seq(RevertInfo("ADD", fh.getNewPath, applied))
|
||||||
|
} else Nil
|
||||||
|
}
|
||||||
|
case DiffEntry.ChangeType.ADD => {
|
||||||
|
val applied = PatchUtil.apply("", patch, fh)
|
||||||
|
if(applied != null){
|
||||||
|
Seq(RevertInfo("ADD", fh.getNewPath, applied))
|
||||||
|
} else Nil
|
||||||
|
}
|
||||||
|
case DiffEntry.ChangeType.DELETE => {
|
||||||
|
Seq(RevertInfo("DELETE", fh.getNewPath, ""))
|
||||||
|
}
|
||||||
|
case DiffEntry.ChangeType.RENAME => {
|
||||||
|
val applied = PatchUtil.apply("", patch, fh)
|
||||||
|
if(applied != null){
|
||||||
|
Seq(RevertInfo("DELETE", fh.getOldPath, ""), RevertInfo("ADD", fh.getNewPath, applied))
|
||||||
|
} else {
|
||||||
|
Seq(RevertInfo("DELETE", fh.getOldPath, ""))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case _ => Nil
|
||||||
|
}
|
||||||
|
}).flatten
|
||||||
|
|
||||||
|
if(revertInfo.nonEmpty){
|
||||||
|
val builder = DirCache.newInCore.builder()
|
||||||
|
val inserter = git.getRepository.newObjectInserter()
|
||||||
|
val headId = git.getRepository.resolve(Constants.HEAD + "^{commit}")
|
||||||
|
|
||||||
|
using(new RevWalk(git.getRepository)){ revWalk =>
|
||||||
|
using(new TreeWalk(git.getRepository)){ treeWalk =>
|
||||||
|
val index = treeWalk.addTree(revWalk.parseTree(headId))
|
||||||
|
treeWalk.setRecursive(true)
|
||||||
|
while(treeWalk.next){
|
||||||
|
val path = treeWalk.getPathString
|
||||||
|
val tree = treeWalk.getTree(index, classOf[CanonicalTreeParser])
|
||||||
|
if(revertInfo.find(x => x.filePath == path).isEmpty){
|
||||||
|
builder.add(JGitUtil.createDirCacheEntry(path, tree.getEntryFileMode, tree.getEntryObjectId))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
revertInfo.filter(_.operation == "ADD").foreach { x =>
|
||||||
|
builder.add(JGitUtil.createDirCacheEntry(x.filePath, FileMode.REGULAR_FILE, inserter.insert(Constants.OBJ_BLOB, x.source.getBytes("UTF-8"))))
|
||||||
|
}
|
||||||
|
builder.finish()
|
||||||
|
|
||||||
|
JGitUtil.createNewCommit(git, inserter, headId, builder.getDirCache.writeTree(inserter), committer.fullName, committer.mailAddress,
|
||||||
|
pageName match {
|
||||||
|
case Some(x) => s"Revert ${from} ... ${to} on ${x}"
|
||||||
|
case None => s"Revert ${from} ... ${to}"
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
true
|
||||||
|
} catch {
|
||||||
|
case e: Exception => {
|
||||||
|
e.printStackTrace()
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Save the wiki page.
|
* Save the wiki page.
|
||||||
*/
|
*/
|
||||||
def saveWikiPage(owner: String, repository: String, currentPageName: String, newPageName: String,
|
def saveWikiPage(owner: String, repository: String, currentPageName: String, newPageName: String,
|
||||||
content: String, committer: model.Account, message: String, currentId: Option[String]): Option[String] = {
|
content: String, committer: model.Account, message: String, currentId: Option[String]): Option[String] = {
|
||||||
|
|
||||||
LockUtil.lock(s"${owner}/${repository}/wiki"){
|
LockUtil.lock(s"${owner}/${repository}/wiki"){
|
||||||
defining(Directory.getWikiWorkDir(owner, repository)){ workDir =>
|
using(Git.open(Directory.getWikiRepositoryDir(owner, repository))){ git =>
|
||||||
// clone working copy
|
val builder = DirCache.newInCore.builder()
|
||||||
cloneOrPullWorkingCopy(workDir, owner, repository)
|
val inserter = git.getRepository.newObjectInserter()
|
||||||
|
val headId = git.getRepository.resolve(Constants.HEAD + "^{commit}")
|
||||||
|
var created = true
|
||||||
|
var updated = false
|
||||||
|
var removed = false
|
||||||
|
|
||||||
// write as file
|
if(headId != null){
|
||||||
using(Git.open(workDir)){ git =>
|
using(new RevWalk(git.getRepository)){ revWalk =>
|
||||||
defining(new File(workDir, newPageName + ".md")){ file =>
|
using(new TreeWalk(git.getRepository)){ treeWalk =>
|
||||||
// new page
|
val index = treeWalk.addTree(revWalk.parseTree(headId))
|
||||||
val created = !file.exists
|
treeWalk.setRecursive(true)
|
||||||
|
while(treeWalk.next){
|
||||||
// created or updated
|
val path = treeWalk.getPathString
|
||||||
val added = executeIf(!file.exists || FileUtils.readFileToString(file, "UTF-8") != content){
|
val tree = treeWalk.getTree(index, classOf[CanonicalTreeParser])
|
||||||
FileUtils.writeStringToFile(file, content, "UTF-8")
|
if(path == currentPageName + ".md" && currentPageName != newPageName){
|
||||||
git.add.addFilepattern(file.getName).call
|
removed = true
|
||||||
|
} else if(path != newPageName + ".md"){
|
||||||
|
builder.add(JGitUtil.createDirCacheEntry(path, tree.getEntryFileMode, tree.getEntryObjectId))
|
||||||
|
} else {
|
||||||
|
created = false
|
||||||
|
updated = JGitUtil.getContent(git, tree.getEntryObjectId, true).map(new String(_, "UTF-8") != content).getOrElse(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// delete file
|
optionIf(created || updated || removed){
|
||||||
val deleted = executeIf(currentPageName != "" && currentPageName != newPageName){
|
builder.add(JGitUtil.createDirCacheEntry(newPageName + ".md", FileMode.REGULAR_FILE, inserter.insert(Constants.OBJ_BLOB, content.getBytes("UTF-8"))))
|
||||||
git.rm.addFilepattern(currentPageName + ".md").call
|
builder.finish()
|
||||||
}
|
val newHeadId = JGitUtil.createNewCommit(git, inserter, headId, builder.getDirCache.writeTree(inserter), committer.fullName, committer.mailAddress,
|
||||||
|
if(message.trim.length == 0) {
|
||||||
// commit and push
|
if(removed){
|
||||||
optionIf(added || deleted){
|
|
||||||
defining(git.commit.setCommitter(committer.fullName, committer.mailAddress)
|
|
||||||
.setMessage(if(message.trim.length == 0){
|
|
||||||
if(deleted){
|
|
||||||
s"Rename ${currentPageName} to ${newPageName}"
|
s"Rename ${currentPageName} to ${newPageName}"
|
||||||
} else if(created){
|
} else if(created){
|
||||||
s"Created ${newPageName}"
|
s"Created ${newPageName}"
|
||||||
@@ -186,12 +253,9 @@ trait WikiService {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
message
|
message
|
||||||
}).call){ commit =>
|
})
|
||||||
git.push.call
|
|
||||||
Some(commit.getName)
|
Some(newHeadId)
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -203,35 +267,34 @@ trait WikiService {
|
|||||||
def deleteWikiPage(owner: String, repository: String, pageName: String,
|
def deleteWikiPage(owner: String, repository: String, pageName: String,
|
||||||
committer: String, mailAddress: String, message: String): Unit = {
|
committer: String, mailAddress: String, message: String): Unit = {
|
||||||
LockUtil.lock(s"${owner}/${repository}/wiki"){
|
LockUtil.lock(s"${owner}/${repository}/wiki"){
|
||||||
defining(Directory.getWikiWorkDir(owner, repository)){ workDir =>
|
using(Git.open(Directory.getWikiRepositoryDir(owner, repository))){ git =>
|
||||||
// clone working copy
|
val builder = DirCache.newInCore.builder()
|
||||||
cloneOrPullWorkingCopy(workDir, owner, repository)
|
val inserter = git.getRepository.newObjectInserter()
|
||||||
|
val headId = git.getRepository.resolve(Constants.HEAD + "^{commit}")
|
||||||
|
var removed = false
|
||||||
|
|
||||||
// delete file
|
using(new RevWalk(git.getRepository)){ revWalk =>
|
||||||
new File(workDir, pageName + ".md").delete
|
using(new TreeWalk(git.getRepository)){ treeWalk =>
|
||||||
|
val index = treeWalk.addTree(revWalk.parseTree(headId))
|
||||||
|
treeWalk.setRecursive(true)
|
||||||
|
while(treeWalk.next){
|
||||||
|
val path = treeWalk.getPathString
|
||||||
|
val tree = treeWalk.getTree(index, classOf[CanonicalTreeParser])
|
||||||
|
if(path != pageName + ".md"){
|
||||||
|
builder.add(JGitUtil.createDirCacheEntry(path, tree.getEntryFileMode, tree.getEntryObjectId))
|
||||||
|
} else {
|
||||||
|
removed = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
using(Git.open(workDir)){ git =>
|
if(removed){
|
||||||
git.rm.addFilepattern(pageName + ".md").call
|
builder.finish()
|
||||||
|
JGitUtil.createNewCommit(git, inserter, headId, builder.getDirCache.writeTree(inserter), committer, mailAddress, message)
|
||||||
// commit and push
|
}
|
||||||
git.commit.setCommitter(committer, mailAddress).setMessage(message).call
|
|
||||||
git.push.call
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private def cloneOrPullWorkingCopy(workDir: File, owner: String, repository: String): Unit = {
|
|
||||||
if(!workDir.exists){
|
|
||||||
Git.cloneRepository
|
|
||||||
.setURI(Directory.getWikiRepositoryDir(owner, repository).toURI.toString)
|
|
||||||
.setDirectory(workDir)
|
|
||||||
.call
|
|
||||||
.getRepository
|
|
||||||
.close
|
|
||||||
} else using(Git.open(workDir)){ git =>
|
|
||||||
git.pull.call
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -56,25 +56,10 @@ object Directory {
|
|||||||
def getDownloadWorkDir(owner: String, repository: String, sessionId: String): File =
|
def getDownloadWorkDir(owner: String, repository: String, sessionId: String): File =
|
||||||
new File(getTemporaryDir(owner, repository), s"download/${sessionId}")
|
new File(getTemporaryDir(owner, repository), s"download/${sessionId}")
|
||||||
|
|
||||||
/**
|
|
||||||
* Temporary directory which is used in the repository creation.
|
|
||||||
*
|
|
||||||
* GitBucket generates initial repository contents in this directory and push them.
|
|
||||||
* This directory is removed after the repository creation.
|
|
||||||
*/
|
|
||||||
def getInitRepositoryDir(owner: String, repository: String): File =
|
|
||||||
new File(getTemporaryDir(owner, repository), "init")
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Substance directory of the wiki repository.
|
* Substance directory of the wiki repository.
|
||||||
*/
|
*/
|
||||||
def getWikiRepositoryDir(owner: String, repository: String): File =
|
def getWikiRepositoryDir(owner: String, repository: String): File =
|
||||||
new File(s"${RepositoryHome}/${owner}/${repository}.wiki.git")
|
new File(s"${RepositoryHome}/${owner}/${repository}.wiki.git")
|
||||||
|
|
||||||
/**
|
|
||||||
* Wiki working directory which is cloned from the wiki repository.
|
|
||||||
*/
|
|
||||||
def getWikiWorkDir(owner: String, repository: String): File =
|
|
||||||
new File(getTemporaryDir(owner, repository), "wiki")
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -15,6 +15,7 @@ import org.eclipse.jgit.errors.MissingObjectException
|
|||||||
import java.util.Date
|
import java.util.Date
|
||||||
import org.eclipse.jgit.api.errors.NoHeadException
|
import org.eclipse.jgit.api.errors.NoHeadException
|
||||||
import service.RepositoryService
|
import service.RepositoryService
|
||||||
|
import org.eclipse.jgit.dircache.DirCacheEntry
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides complex JGit operations.
|
* Provides complex JGit operations.
|
||||||
@@ -464,4 +465,32 @@ object JGitUtil {
|
|||||||
}.find(_._1 != null)
|
}.find(_._1 != null)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def createDirCacheEntry(path: String, mode: FileMode, objectId: ObjectId): DirCacheEntry = {
|
||||||
|
val entry = new DirCacheEntry(path)
|
||||||
|
entry.setFileMode(mode)
|
||||||
|
entry.setObjectId(objectId)
|
||||||
|
entry
|
||||||
|
}
|
||||||
|
|
||||||
|
def createNewCommit(git: Git, inserter: ObjectInserter, headId: AnyObjectId, treeId: AnyObjectId,
|
||||||
|
fullName: String, mailAddress: String, message: String): String = {
|
||||||
|
val newCommit = new CommitBuilder()
|
||||||
|
newCommit.setCommitter(new PersonIdent(fullName, mailAddress))
|
||||||
|
newCommit.setAuthor(new PersonIdent(fullName, mailAddress))
|
||||||
|
newCommit.setMessage(message)
|
||||||
|
if(headId != null){
|
||||||
|
newCommit.setParentIds(List(headId).asJava)
|
||||||
|
}
|
||||||
|
newCommit.setTreeId(treeId)
|
||||||
|
|
||||||
|
val newHeadId = inserter.insert(newCommit)
|
||||||
|
inserter.flush()
|
||||||
|
|
||||||
|
val refUpdate = git.getRepository.updateRef(Constants.HEAD)
|
||||||
|
refUpdate.setNewObjectId(newHeadId)
|
||||||
|
refUpdate.update()
|
||||||
|
|
||||||
|
newHeadId.getName
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -57,7 +57,6 @@ object helpers extends AvatarImageProvider with LinkConverter with RequestCache
|
|||||||
value
|
value
|
||||||
}
|
}
|
||||||
|
|
||||||
import scala.util.matching.Regex
|
|
||||||
import scala.util.matching.Regex._
|
import scala.util.matching.Regex._
|
||||||
implicit class RegexReplaceString(s: String) {
|
implicit class RegexReplaceString(s: String) {
|
||||||
def replaceAll(pattern: String, replacer: (Match) => String): String = {
|
def replaceAll(pattern: String, replacer: (Match) => String): String = {
|
||||||
|
|||||||
@@ -35,7 +35,7 @@
|
|||||||
@commits.map { commit =>
|
@commits.map { commit =>
|
||||||
<tr>
|
<tr>
|
||||||
<td width="0%"><input type="checkbox" name="commitId" value="@commit.id"></td>
|
<td width="0%"><input type="checkbox" name="commitId" value="@commit.id"></td>
|
||||||
<td>@avatar(commit.committer, 20) <a href="@url(commit.committer)">@commit.committer</a></td>
|
<td>@avatar(commit, 20) @user(commit.committer, commit.mailAddress)</td>
|
||||||
<td width="80%">
|
<td width="80%">
|
||||||
<span class="muted">@datetime(commit.time):</span> @commit.shortMessage
|
<span class="muted">@datetime(commit.time):</span> @commit.shortMessage
|
||||||
</td>
|
</td>
|
||||||
|
|||||||
Reference in New Issue
Block a user