diff --git a/src/main/scala/gitbucket/core/controller/PullRequestsController.scala b/src/main/scala/gitbucket/core/controller/PullRequestsController.scala index 21481cc..d7059ee 100644 --- a/src/main/scala/gitbucket/core/controller/PullRequestsController.scala +++ b/src/main/scala/gitbucket/core/controller/PullRequestsController.scala @@ -144,25 +144,6 @@ getRepository(pullreq.requestUserName, pullreq.requestRepositoryName), flash.iterator.map(f => f._1 -> f._2.toString).toMap ) - -// html.pullreq( -// issue, -// pullreq, -// comments, -// getIssueLabels(owner, name, issueId), -// getAssignableUserNames(owner, name), -// getMilestonesWithIssueCount(owner, name), -// getPriorities(owner, name), -// getLabels(owner, name), -// commits, -// diffs, -// isEditable(repository), -// isManageable(repository), -// hasDeveloperRole(pullreq.requestUserName, pullreq.requestRepositoryName, context.loginAccount), -// repository, -// getRepository(pullreq.requestUserName, pullreq.requestRepositoryName), -// flash.toMap.map(f => f._1 -> f._2.toString) -// ) } } getOrElse NotFound() }) @@ -396,9 +377,9 @@ }) get("/:owner/:repository/compare")(referrersOnly { forkedRepository => - val headBranch: Option[String] = params.get("head") + val headBranch = params.get("head") (forkedRepository.repository.originUserName, forkedRepository.repository.originRepositoryName) match { - case (Some(originUserName), Some(originRepositoryName)) => { + case (Some(originUserName), Some(originRepositoryName)) => getRepository(originUserName, originRepositoryName).map { originRepository => Using.resources( @@ -415,8 +396,7 @@ ) } } getOrElse NotFound() - } - case _ => { + case _ => Using.resource(Git.open(getRepositoryDir(forkedRepository.owner, forkedRepository.name))) { git => JGitUtil.getDefaultBranch(git, forkedRepository).map { case (_, defaultBranch) => @@ -427,41 +407,48 @@ redirect(s"/${forkedRepository.owner}/${forkedRepository.name}") } } - } } }) + private def getOriginRepositoryName( + originOwner: String, + forkedOwner: String, + forkedRepository: RepositoryInfo + ): Option[String] = { + if (originOwner == forkedOwner) { + // Self repository + Some(forkedRepository.name) + } else if (forkedRepository.repository.originUserName.isEmpty) { + // when ForkedRepository is the original repository + getForkedRepositories(forkedRepository.owner, forkedRepository.name) + .find(_.userName == originOwner) + .map(_.repositoryName) + } else if (Some(originOwner) == forkedRepository.repository.originUserName) { + // Original repository + forkedRepository.repository.originRepositoryName + } else { + // Sibling repository + getUserRepositories(originOwner) + .find { x => + x.repository.originUserName == forkedRepository.repository.originUserName && + x.repository.originRepositoryName == forkedRepository.repository.originRepositoryName + } + .map(_.repository.repositoryName) + } + } + get("/:owner/:repository/compare/*...*")(referrersOnly { forkedRepository => val Seq(origin, forked) = multiParams("splat") val (originOwner, originId) = parseCompareIdentifier(origin, forkedRepository.owner) val (forkedOwner, forkedId) = parseCompareIdentifier(forked, forkedRepository.owner) - (for (originRepositoryName <- if (originOwner == forkedOwner) { - // Self repository - Some(forkedRepository.name) - } else if (forkedRepository.repository.originUserName.isEmpty) { - // when ForkedRepository is the original repository - getForkedRepositories(forkedRepository.owner, forkedRepository.name) - .find(_.userName == originOwner) - .map(_.repositoryName) - } else if (Some(originOwner) == forkedRepository.repository.originUserName) { - // Original repository - forkedRepository.repository.originRepositoryName - } else { - // Sibling repository - getUserRepositories(originOwner) - .find { x => - x.repository.originUserName == forkedRepository.repository.originUserName && - x.repository.originRepositoryName == forkedRepository.repository.originRepositoryName - } - .map(_.repository.repositoryName) - }; + (for (originRepositoryName <- getOriginRepositoryName(originOwner, forkedOwner, forkedRepository); originRepository <- getRepository(originOwner, originRepositoryName)) yield { val (oldId, newId) = getPullRequestCommitFromTo(originRepository, forkedRepository, originId, forkedId) (oldId, newId) match { - case (Some(oldId), Some(newId)) => { + case (Some(oldId), Some(newId)) => val (commits, diffs) = getRequestCompareInfo( originRepository.owner, originRepository.name, @@ -512,7 +499,6 @@ getLabels(originRepository.owner, originRepository.name), getCustomFields(originRepository.owner, originRepository.name).filter(_.enableForPullRequests) ) - } case (oldId, newId) => redirect( s"/${forkedRepository.owner}/${forkedRepository.name}/compare/" + @@ -524,6 +510,54 @@ }) getOrElse NotFound() }) + ajaxGet("/:owner/:repository/diff/:id")(referrersOnly { repository => + (for { + commitId <- params.get("id") + path <- params.get("path") + diff <- getSingleDiff(repository.owner, repository.name, commitId, path) + } yield { + contentType = formats("json") + org.json4s.jackson.Serialization.write( + Map( + "oldContent" -> diff.oldContent, + "newContent" -> diff.newContent + ) + ) + }) getOrElse NotFound() + }) + + ajaxGet("/:owner/:repository/diff/*...*")(referrersOnly { forkedRepository => + val Seq(origin, forked) = multiParams("splat") + val (originOwner, originId) = parseCompareIdentifier(origin, forkedRepository.owner) + val (forkedOwner, forkedId) = parseCompareIdentifier(forked, forkedRepository.owner) + + (for { + path <- params.get("path") + originRepositoryName <- getOriginRepositoryName(originOwner, forkedOwner, forkedRepository) + originRepository <- getRepository(originOwner, originRepositoryName) + (oldId, newId) = getPullRequestCommitFromTo(originRepository, forkedRepository, originId, forkedId) + oldId <- oldId + newId <- newId + diff <- getSingleDiff( + originRepository.owner, + originRepository.name, + oldId.getName, + forkedRepository.owner, + forkedRepository.name, + newId.getName, + path + ) + } yield { + contentType = formats("json") + org.json4s.jackson.Serialization.write( + Map( + "oldContent" -> diff.oldContent, + "newContent" -> diff.newContent + ) + ) + }) getOrElse NotFound() + }) + ajaxGet("/:owner/:repository/compare/*...*/mergecheck")(readableUsersOnly { forkedRepository => val Seq(origin, forked) = multiParams("splat") val (originOwner, tmpOriginBranch) = parseCompareIdentifier(origin, forkedRepository.owner) diff --git a/src/main/scala/gitbucket/core/controller/RepositoryViewerController.scala b/src/main/scala/gitbucket/core/controller/RepositoryViewerController.scala index 4026a53..ba09e06 100644 --- a/src/main/scala/gitbucket/core/controller/RepositoryViewerController.scala +++ b/src/main/scala/gitbucket/core/controller/RepositoryViewerController.scala @@ -331,15 +331,12 @@ post("/:owner/:repository/upload", uploadForm)(writableUsersOnly { (form, repository) => def _commit( branchName: String, - //files: Seq[CommitFile], newFiles: Seq[CommitFile], loginAccount: Account ): Either[String, ObjectId] = { commitFiles( repository = repository, branch = branchName, - //path = form.path, - //files = files.toIndexedSeq, message = form.message.getOrElse("Add files via upload"), loginAccount = loginAccount, settings = context.settings @@ -614,7 +611,7 @@ } else { _commit(form.branch, loginAccount) match { case Right(_) => - if (form.path.length == 0) { + if (form.path.isEmpty) { redirect(s"/${repository.owner}/${repository.name}/tree/${encodeRefName(form.branch)}") } else { redirect( diff --git a/src/main/scala/gitbucket/core/service/PullRequestService.scala b/src/main/scala/gitbucket/core/service/PullRequestService.scala index 925a4f7..b98ce2b 100644 --- a/src/main/scala/gitbucket/core/service/PullRequestService.scala +++ b/src/main/scala/gitbucket/core/service/PullRequestService.scala @@ -472,6 +472,40 @@ } } + def getSingleDiff( + userName: String, + repositoryName: String, + commitId: String, + path: String + ): Option[DiffInfo] = { + Using.resource( + Git.open(getRepositoryDir(userName, repositoryName)) + ) { git => + val newId = git.getRepository.resolve(commitId) + JGitUtil.getDiff(git, None, newId.getName, path) + } + } + + def getSingleDiff( + userName: String, + repositoryName: String, + branch: String, + requestUserName: String, + requestRepositoryName: String, + requestCommitId: String, + path: String + ): Option[DiffInfo] = { + Using.resources( + Git.open(getRepositoryDir(userName, repositoryName)), + Git.open(getRepositoryDir(requestUserName, requestRepositoryName)) + ) { (oldGit, newGit) => + val oldId = oldGit.getRepository.resolve(branch) + val newId = newGit.getRepository.resolve(requestCommitId) + + JGitUtil.getDiff(newGit, Some(oldId.getName), newId.getName, path) + } + } + def getRequestCompareInfo( userName: String, repositoryName: String, diff --git a/src/main/scala/gitbucket/core/util/JGitUtil.scala b/src/main/scala/gitbucket/core/util/JGitUtil.scala index 77e3449..ab63856 100644 --- a/src/main/scala/gitbucket/core/util/JGitUtil.scala +++ b/src/main/scala/gitbucket/core/util/JGitUtil.scala @@ -37,7 +37,7 @@ private val logger = LoggerFactory.getLogger(JGitUtil.getClass) - implicit val objectDatabaseReleasable: Releasable[ObjectDatabase] = + private implicit val objectDatabaseReleasable: Releasable[ObjectDatabase] = _.close() /** @@ -690,7 +690,7 @@ val toCommit = revWalk.parseCommit(git.getRepository.resolve(to)) (from match { - case None => { + case None => toCommit.getParentCount match { case 0 => df.scan( @@ -700,11 +700,9 @@ .asScala case _ => df.scan(toCommit.getParent(0), toCommit.getTree).asScala } - } - case Some(from) => { + case Some(from) => val fromCommit = revWalk.parseCommit(git.getRepository.resolve(from)) df.scan(fromCommit.getTree, toCommit.getTree).asScala - } }).toSeq } } @@ -719,6 +717,29 @@ } } + def getDiff(git: Git, from: Option[String], to: String, path: String): Option[DiffInfo] = { + getDiffEntries(git, from, to).find(_.getNewPath == path).map { diff => + val oldIsImage = FileUtil.isImage(diff.getOldPath) + val newIsImage = FileUtil.isImage(diff.getNewPath) + val includeContent = oldIsImage || newIsImage + DiffInfo( + changeType = diff.getChangeType, + oldPath = diff.getOldPath, + newPath = diff.getNewPath, + oldContent = if (includeContent) None else getTextContent(git, diff.getOldId.toObjectId), + newContent = if (includeContent) None else getTextContent(git, diff.getNewId.toObjectId), + oldIsImage = oldIsImage, + newIsImage = newIsImage, + oldObjectId = Option(diff.getOldId).map(_.name), + newObjectId = Option(diff.getNewId).map(_.name), + oldMode = diff.getOldMode.toString, + newMode = diff.getNewMode.toString, + tooLarge = false, + patch = None + ) + } + } + def getDiffs( git: Git, from: Option[String], @@ -728,7 +749,7 @@ ): List[DiffInfo] = { val diffs = getDiffEntries(git, from, to) diffs.map { diff => - if (diffs.size > 100) { + if (diffs.size > 100) { // Don't show diff if there are more than 100 files DiffInfo( changeType = diff.getChangeType, oldPath = diff.getOldPath, @@ -747,49 +768,35 @@ } else { val oldIsImage = FileUtil.isImage(diff.getOldPath) val newIsImage = FileUtil.isImage(diff.getNewPath) - if (!fetchContent || oldIsImage || newIsImage) { - DiffInfo( - changeType = diff.getChangeType, - oldPath = diff.getOldPath, - newPath = diff.getNewPath, - oldContent = None, - newContent = None, - oldIsImage = oldIsImage, - newIsImage = newIsImage, - oldObjectId = Option(diff.getOldId).map(_.name), - newObjectId = Option(diff.getNewId).map(_.name), - oldMode = diff.getOldMode.toString, - newMode = diff.getNewMode.toString, - tooLarge = false, - patch = (if (makePatch) Some(makePatchFromDiffEntry(git, diff)) else None) // TODO use DiffFormatter - ) - } else { - DiffInfo( - changeType = diff.getChangeType, - oldPath = diff.getOldPath, - newPath = diff.getNewPath, - oldContent = JGitUtil - .getContentFromId(git, diff.getOldId.toObjectId, false) - .filter(FileUtil.isText) - .map(convertFromByteArray), - newContent = JGitUtil - .getContentFromId(git, diff.getNewId.toObjectId, false) - .filter(FileUtil.isText) - .map(convertFromByteArray), - oldIsImage = oldIsImage, - newIsImage = newIsImage, - oldObjectId = Option(diff.getOldId).map(_.name), - newObjectId = Option(diff.getNewId).map(_.name), - oldMode = diff.getOldMode.toString, - newMode = diff.getNewMode.toString, - tooLarge = false, - patch = (if (makePatch) Some(makePatchFromDiffEntry(git, diff)) else None) // TODO use DiffFormatter - ) - } + val patch = if (oldIsImage || newIsImage) None else Some(makePatchFromDiffEntry(git, diff)) // TODO use DiffFormatter + val tooLarge = patch.exists(_.count(_ == '\n') > 1000) // Don't show diff if the file has more than 1000 lines diff + val includeContent = tooLarge || !fetchContent || oldIsImage || newIsImage + DiffInfo( + changeType = diff.getChangeType, + oldPath = diff.getOldPath, + newPath = diff.getNewPath, + oldContent = if (includeContent) None else getTextContent(git, diff.getOldId.toObjectId), + newContent = if (includeContent) None else getTextContent(git, diff.getNewId.toObjectId), + oldIsImage = oldIsImage, + newIsImage = newIsImage, + oldObjectId = Option(diff.getOldId).map(_.name), + newObjectId = Option(diff.getNewId).map(_.name), + oldMode = diff.getOldMode.toString, + newMode = diff.getNewMode.toString, + tooLarge = tooLarge, + patch = if (makePatch) patch else None + ) } }.toList } + private def getTextContent(git: Git, objectId: ObjectId): Option[String] = { + JGitUtil + .getContentFromId(git, objectId, false) + .filter(FileUtil.isText) + .map(convertFromByteArray) + } + private def makePatchFromDiffEntry(git: Git, diff: DiffEntry): String = { val out = new ByteArrayOutputStream() Using.resource(new DiffFormatter(out)) { formatter => diff --git a/src/main/twirl/gitbucket/core/helper/diff.scala.html b/src/main/twirl/gitbucket/core/helper/diff.scala.html index 92999c9..4579a90 100644 --- a/src/main/twirl/gitbucket/core/helper/diff.scala.html +++ b/src/main/twirl/gitbucket/core/helper/diff.scala.html @@ -100,38 +100,48 @@