diff --git a/src/main/scala/gitbucket/core/api/ApiCommit.scala b/src/main/scala/gitbucket/core/api/ApiCommit.scala index 9229d0c..8540d03 100644 --- a/src/main/scala/gitbucket/core/api/ApiCommit.scala +++ b/src/main/scala/gitbucket/core/api/ApiCommit.scala @@ -35,18 +35,18 @@ object ApiCommit{ def apply(git: Git, repositoryName: RepositoryName, commit: CommitInfo, urlIsHtmlUrl: Boolean = false): ApiCommit = { - val diffs = JGitUtil.getDiffs(git, commit.id, false) + val diffs = JGitUtil.getDiffs(git, None, commit.id, false, false) ApiCommit( id = commit.id, message = commit.fullMessage, timestamp = commit.commitTime, - added = diffs._1.collect { + added = diffs.collect { case x if x.changeType == DiffEntry.ChangeType.ADD => x.newPath }, - removed = diffs._1.collect { + removed = diffs.collect { case x if x.changeType == DiffEntry.ChangeType.DELETE => x.oldPath }, - modified = diffs._1.collect { + modified = diffs.collect { case x if x.changeType != DiffEntry.ChangeType.ADD && x.changeType != DiffEntry.ChangeType.DELETE => x.newPath }, author = ApiPersonIdent.author(commit), diff --git a/src/main/scala/gitbucket/core/controller/ApiController.scala b/src/main/scala/gitbucket/core/controller/ApiController.scala index d7a6e75..bde2f12 100644 --- a/src/main/scala/gitbucket/core/controller/ApiController.scala +++ b/src/main/scala/gitbucket/core/controller/ApiController.scala @@ -655,7 +655,7 @@ JsonFormat(ApiCommits( repositoryName = RepositoryName(repository), commitInfo = commitInfo, - diffs = JGitUtil.getDiffs(git, commitInfo.parents.head, commitInfo.id, false, true), + diffs = JGitUtil.getDiffs(git, Some(commitInfo.parents.head), commitInfo.id, false, true), author = getAccount(commitInfo.authorName, commitInfo.authorEmailAddress), committer = getAccount(commitInfo.committerName, commitInfo.committerEmailAddress), commentCount = getCommitComment(repository.owner, repository.name, sha).size diff --git a/src/main/scala/gitbucket/core/controller/RepositoryViewerController.scala b/src/main/scala/gitbucket/core/controller/RepositoryViewerController.scala index 8fb6370..e6cd4f8 100644 --- a/src/main/scala/gitbucket/core/controller/RepositoryViewerController.scala +++ b/src/main/scala/gitbucket/core/controller/RepositoryViewerController.scala @@ -451,14 +451,14 @@ try { using(Git.open(getRepositoryDir(repository.owner, repository.name))) { git => defining(JGitUtil.getRevCommitFromId(git, git.getRepository.resolve(id))) { revCommit => - JGitUtil.getDiffs(git, id, true) match { - case (diffs, oldCommitId) => - html.commit(id, new JGitUtil.CommitInfo(revCommit), - JGitUtil.getBranchesOfCommit(git, revCommit.getName), - JGitUtil.getTagsOfCommit(git, revCommit.getName), - getCommitComments(repository.owner, repository.name, id, true), - repository, diffs, oldCommitId, hasDeveloperRole(repository.owner, repository.name, context.loginAccount)) - } + val diffs = JGitUtil.getDiffs(git, None, id, true, false) + val oldCommitId = JGitUtil.getParentCommitId(git, id) + + html.commit(id, new JGitUtil.CommitInfo(revCommit), + JGitUtil.getBranchesOfCommit(git, revCommit.getName), + JGitUtil.getTagsOfCommit(git, revCommit.getName), + getCommitComments(repository.owner, repository.name, id, true), + repository, diffs, oldCommitId, hasDeveloperRole(repository.owner, repository.name, context.loginAccount)) } } } catch { @@ -466,6 +466,31 @@ } }) + get("/:owner/:repository/patch/:id")(referrersOnly { repository => + try { + using(Git.open(getRepositoryDir(repository.owner, repository.name))) { git => + val diff = JGitUtil.getPatch(git, None, params("id")) + contentType = formats("txt") + diff + } + } catch { + case e:MissingObjectException => NotFound() + } + }) + + get("/:owner/:repository/patch/*...*")(referrersOnly { repository => + try { + val Seq(fromId, toId) = multiParams("splat") + using(Git.open(getRepositoryDir(repository.owner, repository.name))) { git => + val diff = JGitUtil.getPatch(git, Some(fromId), toId) + contentType = formats("txt") + diff + } + } catch { + case e: MissingObjectException => NotFound() + } + }) + post("/:owner/:repository/commit/:id/comment/new", commentForm)(readableUsersOnly { (form, repository) => val id = params("id") createCommitComment(repository.owner, repository.name, id, context.loginAccount.get.userName, form.content, diff --git a/src/main/scala/gitbucket/core/controller/WikiController.scala b/src/main/scala/gitbucket/core/controller/WikiController.scala index 5dde40e..aadd36c 100644 --- a/src/main/scala/gitbucket/core/controller/WikiController.scala +++ b/src/main/scala/gitbucket/core/controller/WikiController.scala @@ -76,7 +76,7 @@ val Array(from, to) = params("commitId").split("\\.\\.\\.") using(Git.open(getWikiRepositoryDir(repository.owner, repository.name))){ git => - html.compare(Some(pageName), from, to, JGitUtil.getDiffs(git, from, to, true, false).filter(_.newPath == pageName + ".md"), repository, + html.compare(Some(pageName), from, to, JGitUtil.getDiffs(git, Some(from), to, true, false).filter(_.newPath == pageName + ".md"), repository, isEditable(repository), flash.get("info")) } }) @@ -85,7 +85,7 @@ val Array(from, to) = params("commitId").split("\\.\\.\\.") using(Git.open(getWikiRepositoryDir(repository.owner, repository.name))){ git => - html.compare(None, from, to, JGitUtil.getDiffs(git, from, to, true, false), repository, + html.compare(None, from, to, JGitUtil.getDiffs(git, Some(from), to, true, false), repository, isEditable(repository), flash.get("info")) } }) diff --git a/src/main/scala/gitbucket/core/service/PullRequestService.scala b/src/main/scala/gitbucket/core/service/PullRequestService.scala index d8d419b..1ee36f9 100644 --- a/src/main/scala/gitbucket/core/service/PullRequestService.scala +++ b/src/main/scala/gitbucket/core/service/PullRequestService.scala @@ -230,7 +230,7 @@ helpers.date(commit1.commitTime) == view.helpers.date(commit2.commitTime) } - val diffs = JGitUtil.getDiffs(newGit, oldId.getName, newId.getName, true, false) + val diffs = JGitUtil.getDiffs(newGit, Some(oldId.getName), newId.getName, true, false) (commits, diffs) } diff --git a/src/main/scala/gitbucket/core/servlet/GitRepositoryServlet.scala b/src/main/scala/gitbucket/core/servlet/GitRepositoryServlet.scala index 2fa3859..d2592d8 100644 --- a/src/main/scala/gitbucket/core/servlet/GitRepositoryServlet.scala +++ b/src/main/scala/gitbucket/core/servlet/GitRepositoryServlet.scala @@ -347,8 +347,8 @@ commitIds.map { case (oldCommitId, newCommitId) => val commits = using(Git.open(Directory.getWikiRepositoryDir(owner, repository))) { git => JGitUtil.getCommitLog(git, oldCommitId, newCommitId).flatMap { commit => - val diffs = JGitUtil.getDiffs(git, commit.id, false) - diffs._1.collect { case diff if diff.newPath.toLowerCase.endsWith(".md") => + val diffs = JGitUtil.getDiffs(git, None, commit.id, false, false) + diffs.collect { case diff if diff.newPath.toLowerCase.endsWith(".md") => val action = if(diff.changeType == ChangeType.ADD) "created" else "edited" val fileName = diff.newPath (action, fileName, commit.id) diff --git a/src/main/scala/gitbucket/core/util/JGitUtil.scala b/src/main/scala/gitbucket/core/util/JGitUtil.scala index 493255b..fd70e67 100644 --- a/src/main/scala/gitbucket/core/util/JGitUtil.scala +++ b/src/main/scala/gitbucket/core/util/JGitUtil.scala @@ -24,8 +24,9 @@ import org.cache2k.{Cache2kBuilder, CacheEntry} import org.eclipse.jgit.api.errors.{InvalidRefNameException, JGitInternalException, NoHeadException, RefAlreadyExistsException} -import org.eclipse.jgit.diff.{DiffEntry, DiffFormatter} +import org.eclipse.jgit.diff.{DiffEntry, DiffFormatter, RawTextComparator} import org.eclipse.jgit.dircache.DirCacheEntry +import org.eclipse.jgit.util.io.DisabledOutputStream import org.slf4j.LoggerFactory /** @@ -518,93 +519,49 @@ }.toMap } - /** - * Returns the tuple of diff of the given commit and parent commit ids. - * DiffInfos returned from this method don't include the patch property. - */ - def getDiffs(git: Git, id: String, fetchContent: Boolean): (List[DiffInfo], Option[String]) = { - @scala.annotation.tailrec - def getCommitLog(i: java.util.Iterator[RevCommit], logs: List[RevCommit]): List[RevCommit] = - i.hasNext match { - case true if(logs.size < 2) => getCommitLog(i, logs :+ i.next) - case _ => logs - } + def getPatch(git: Git, from: Option[String], to: String): String = { + val out = new ByteArrayOutputStream() + val df = new DiffFormatter(out) + df.setRepository(git.getRepository) + df.setDiffComparator(RawTextComparator.DEFAULT) + df.setDetectRenames(true) + df.format(getDiffEntries(git, from, to).head) + new String(out.toByteArray, "UTF-8") + } + private def getDiffEntries(git: Git, from: Option[String], to: String): Seq[DiffEntry] = { using(new RevWalk(git.getRepository)){ revWalk => - revWalk.markStart(revWalk.parseCommit(git.getRepository.resolve(id))) - val commits = getCommitLog(revWalk.iterator, Nil) - val revCommit = commits(0) + val df = new DiffFormatter(DisabledOutputStream.INSTANCE) + df.setRepository(git.getRepository) - if(commits.length >= 2){ - // not initial commit - val oldCommit = if(revCommit.getParentCount >= 2) { - // merge commit - revCommit.getParents.head - } else { - commits(1) - } - (getDiffs(git, oldCommit.getName, id, fetchContent, false), Some(oldCommit.getName)) - - } else { - // initial commit - using(new TreeWalk(git.getRepository)){ treeWalk => - treeWalk.setRecursive(true) - treeWalk.addTree(revCommit.getTree) - val buffer = new scala.collection.mutable.ListBuffer[DiffInfo]() - while(treeWalk.next){ - val newIsImage = FileUtil.isImage(treeWalk.getPathString) - buffer.append((if(!fetchContent){ - DiffInfo( - changeType = ChangeType.ADD, - oldPath = "", - newPath = treeWalk.getPathString, - oldContent = None, - newContent = None, - oldIsImage = false, - newIsImage = newIsImage, - oldObjectId = None, - newObjectId = Option(treeWalk.getObjectId(0)).map(_.name), - oldMode = treeWalk.getFileMode(0).toString, - newMode = treeWalk.getFileMode(0).toString, - tooLarge = false, - patch = None - ) - } else { - DiffInfo( - changeType = ChangeType.ADD, - oldPath = "", - newPath = treeWalk.getPathString, - oldContent = None, - newContent = JGitUtil.getContentFromId(git, treeWalk.getObjectId(0), false).filter(FileUtil.isText).map(convertFromByteArray), - oldIsImage = false, - newIsImage = newIsImage, - oldObjectId = None, - newObjectId = Option(treeWalk.getObjectId(0)).map(_.name), - oldMode = treeWalk.getFileMode(0).toString, - newMode = treeWalk.getFileMode(0).toString, - tooLarge = false, - patch = None - ) - })) + val toCommit = revWalk.parseCommit(git.getRepository.resolve(to)) + from match { + case None => { + toCommit.getParentCount match { + case 0 => df.scan(new EmptyTreeIterator(), new CanonicalTreeParser(null, git.getRepository.newObjectReader(), toCommit.getTree)).asScala + case _ => df.scan(toCommit.getParent(0), toCommit.getTree).asScala } - (buffer.toList, None) + } + case Some(from) => { + val fromCommit = revWalk.parseCommit(git.getRepository.resolve(from)) + df.scan(fromCommit.getTree, toCommit.getTree).asScala } } } } - def getDiffs(git: Git, from: String, to: String, fetchContent: Boolean, makePatch: Boolean): List[DiffInfo] = { - val reader = git.getRepository.newObjectReader - val oldTreeIter = new CanonicalTreeParser - oldTreeIter.reset(reader, git.getRepository.resolve(from + "^{tree}")) + def getParentCommitId(git: Git, id: String): Option[String] = { + using(new RevWalk(git.getRepository)){ revWalk => + val commit = revWalk.parseCommit(git.getRepository.resolve(id)) + commit.getParentCount match { + case 0 => None + case _ => Some(commit.getParent(0).getName) + } + } + } - val newTreeIter = new CanonicalTreeParser - newTreeIter.reset(reader, git.getRepository.resolve(to + "^{tree}")) - - import scala.collection.JavaConverters._ - git.getRepository.getConfig.setString("diff", null, "renames", "copies") - - val diffs = git.diff.setNewTree(newTreeIter).setOldTree(oldTreeIter).call.asScala + def getDiffs(git: Git, from: Option[String], to: String, fetchContent: Boolean, makePatch: Boolean): List[DiffInfo] = { + val diffs = getDiffEntries(git, from, to) diffs.map { diff => if(diffs.size > 100){ DiffInfo( @@ -639,7 +596,7 @@ oldMode = diff.getOldMode.toString, newMode = diff.getNewMode.toString, tooLarge = false, - patch = (if(makePatch) Some(makePatchFromDiffEntry(git, diff)) else None) + patch = (if(makePatch) Some(makePatchFromDiffEntry(git, diff)) else None) // TODO use DiffFormatter ) } else { DiffInfo( @@ -655,7 +612,7 @@ oldMode = diff.getOldMode.toString, newMode = diff.getNewMode.toString, tooLarge = false, - patch = (if(makePatch) Some(makePatchFromDiffEntry(git, diff)) else None) + patch = (if(makePatch) Some(makePatchFromDiffEntry(git, diff)) else None) // TODO use DiffFormatter ) } } diff --git a/src/main/twirl/gitbucket/core/helper/diff.scala.html b/src/main/twirl/gitbucket/core/helper/diff.scala.html index 2cc6f9f..46ccbf6 100644 --- a/src/main/twirl/gitbucket/core/helper/diff.scala.html +++ b/src/main/twirl/gitbucket/core/helper/diff.scala.html @@ -10,9 +10,15 @@ @import org.eclipse.jgit.diff.DiffEntry.ChangeType @if(showIndex){
Showing @diffs.size changed @helpers.plural(diffs.size, "file")