mirror of
https://github.com/gitbucket/gitbucket.git
synced 2025-11-06 13:35:50 +01:00
(refs #180)Implementing reverting from history without ApplyCommand.
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.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, 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()) {
|
||||||
|
StringBuilder hunk = new StringBuilder();
|
||||||
|
for (int j = hh.getStartOffset(); j < hh.getEndOffset(); j++)
|
||||||
|
hunk.append((char) hh.getBuffer()[j]);
|
||||||
|
RawText hrt = new RawText(hunk.toString().getBytes("UTF-8"));
|
||||||
|
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$
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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}")
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ package service
|
|||||||
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.{Directory, JGitUtil, LockUtil}
|
import util.{PatchUtil, Directory, JGitUtil, LockUtil}
|
||||||
import _root_.util.ControlUtil._
|
import _root_.util.ControlUtil._
|
||||||
import org.eclipse.jgit.treewalk.{TreeWalk, CanonicalTreeParser}
|
import org.eclipse.jgit.treewalk.{TreeWalk, CanonicalTreeParser}
|
||||||
import org.eclipse.jgit.lib._
|
import org.eclipse.jgit.lib._
|
||||||
@@ -11,6 +11,11 @@ import org.eclipse.jgit.dircache.{DirCache, DirCacheEntry}
|
|||||||
import org.eclipse.jgit.merge.{ResolveMerger, MergeStrategy}
|
import org.eclipse.jgit.merge.{ResolveMerger, MergeStrategy}
|
||||||
import org.eclipse.jgit.revwalk.RevWalk
|
import org.eclipse.jgit.revwalk.RevWalk
|
||||||
import scala.collection.JavaConverters._
|
import scala.collection.JavaConverters._
|
||||||
|
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 {
|
||||||
@@ -102,6 +107,82 @@ 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"){
|
||||||
|
using(Git.open(Directory.getWikiRepositoryDir(owner, repository))){ git =>
|
||||||
|
val reader = git.getRepository.newObjectReader
|
||||||
|
val oldTreeIter = new CanonicalTreeParser
|
||||||
|
oldTreeIter.reset(reader, git.getRepository.resolve(from + "^{tree}"))
|
||||||
|
|
||||||
|
val newTreeIter = new CanonicalTreeParser
|
||||||
|
newTreeIter.reset(reader, git.getRepository.resolve(to + "^{tree}"))
|
||||||
|
|
||||||
|
import scala.collection.JavaConverters._
|
||||||
|
val diffs = git.diff.setNewTree(oldTreeIter).setOldTree(newTreeIter).call.asScala.filter { diff =>
|
||||||
|
pageName match {
|
||||||
|
case Some(x) => diff.getNewPath == x + ".md"
|
||||||
|
case None => true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val patch = using(new java.io.ByteArrayOutputStream()){ out =>
|
||||||
|
val formatter = new DiffFormatter(out)
|
||||||
|
formatter.setRepository(git.getRepository)
|
||||||
|
formatter.format(diffs.asJava)
|
||||||
|
new String(out.toByteArray, "UTF-8")
|
||||||
|
}
|
||||||
|
|
||||||
|
val p = new Patch()
|
||||||
|
p.parse(new ByteArrayInputStream(patch.getBytes("UTF-8")))
|
||||||
|
if(!p.getErrors.isEmpty){
|
||||||
|
throw new PatchFormatException(p.getErrors())
|
||||||
|
}
|
||||||
|
val revertInfo = (p.getFiles.asScala.map { fh =>
|
||||||
|
fh.getChangeType match {
|
||||||
|
case DiffEntry.ChangeType.MODIFY => {
|
||||||
|
val page = getWikiPage(owner, repository, fh.getNewPath.replaceFirst("\\.md$", "")).get
|
||||||
|
Seq(RevertInfo("ADD", fh.getNewPath, PatchUtil.apply(page.content, fh)))
|
||||||
|
}
|
||||||
|
case DiffEntry.ChangeType.ADD => {
|
||||||
|
Seq(RevertInfo("ADD", fh.getNewPath, PatchUtil.apply("", fh)))
|
||||||
|
}
|
||||||
|
case DiffEntry.ChangeType.DELETE => {
|
||||||
|
Seq(RevertInfo("DELETE", fh.getNewPath, ""))
|
||||||
|
}
|
||||||
|
case DiffEntry.ChangeType.RENAME => {
|
||||||
|
Seq(
|
||||||
|
RevertInfo("DELETE", fh.getOldPath, ""),
|
||||||
|
RevertInfo("ADD", fh.getNewPath, PatchUtil.apply("", fh))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
case _ => Nil
|
||||||
|
}
|
||||||
|
}).flatten
|
||||||
|
|
||||||
|
revertInfo.foreach { revert =>
|
||||||
|
println(revert)
|
||||||
|
}
|
||||||
|
|
||||||
|
// val source = getWikiPage(owner, repository, pageName.get)
|
||||||
|
// PatchUtil.applyToFile(PatchUtil.createPatch(patch), source.get.content, pageName + ".md")
|
||||||
|
|
||||||
|
// try {
|
||||||
|
// git.apply.setPatch(new java.io.ByteArrayInputStream(patch.getBytes("UTF-8"))).call
|
||||||
|
// git.add.addFilepattern(".").call
|
||||||
|
// git.commit.setCommitter(committer.fullName, committer.mailAddress).setMessage(pageName match {
|
||||||
|
// 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
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// LockUtil.lock(s"${owner}/${repository}/wiki"){
|
// LockUtil.lock(s"${owner}/${repository}/wiki"){
|
||||||
// defining(Directory.getWikiWorkDir(owner, repository)){ workDir =>
|
// defining(Directory.getWikiWorkDir(owner, repository)){ workDir =>
|
||||||
// // clone working copy
|
// // clone working copy
|
||||||
@@ -283,6 +364,8 @@ trait WikiService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case class RevertInfo(operation: String, filePath: String, source: String)
|
||||||
|
|
||||||
// private def cloneOrPullWorkingCopy(workDir: File, owner: String, repository: String): Unit = {
|
// private def cloneOrPullWorkingCopy(workDir: File, owner: String, repository: String): Unit = {
|
||||||
// if(!workDir.exists){
|
// if(!workDir.exists){
|
||||||
// Git.cloneRepository
|
// Git.cloneRepository
|
||||||
|
|||||||
Reference in New Issue
Block a user