mirror of
https://github.com/gitbucket/gitbucket.git
synced 2025-11-06 05:25: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("\\.\\.\\.")
|
||||
|
||||
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 {
|
||||
flash += "info" -> "This patch was not able to be reversed."
|
||||
redirect(s"/${repository.owner}/${repository.name}/wiki/_compare/${from}...${to}")
|
||||
|
||||
@@ -3,7 +3,7 @@ package service
|
||||
import java.util.Date
|
||||
import org.eclipse.jgit.api.Git
|
||||
import org.apache.commons.io.FileUtils
|
||||
import util.{Directory, JGitUtil, LockUtil}
|
||||
import util.{PatchUtil, Directory, JGitUtil, LockUtil}
|
||||
import _root_.util.ControlUtil._
|
||||
import org.eclipse.jgit.treewalk.{TreeWalk, CanonicalTreeParser}
|
||||
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.revwalk.RevWalk
|
||||
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 {
|
||||
@@ -102,6 +107,82 @@ trait WikiService {
|
||||
*/
|
||||
def revertWikiPage(owner: String, repository: String, from: String, to: String,
|
||||
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"){
|
||||
// defining(Directory.getWikiWorkDir(owner, repository)){ workDir =>
|
||||
// // 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 = {
|
||||
// if(!workDir.exists){
|
||||
// Git.cloneRepository
|
||||
|
||||
Reference in New Issue
Block a user