diff --git a/build.sbt b/build.sbt index df90251..e87fe26 100644 --- a/build.sbt +++ b/build.sbt @@ -15,6 +15,7 @@ // dependency settings resolvers ++= Seq( Classpaths.typesafeReleases, + Resolver.jcenterRepo, "amateras" at "http://amateras.sourceforge.jp/mvn/", "sonatype-snapshot" at "https://oss.sonatype.org/content/repositories/snapshots/", "amateras-snapshot" at "http://amateras.sourceforge.jp/mvn-snapshot/" @@ -45,6 +46,7 @@ "com.typesafe" % "config" % "1.3.0", "com.typesafe.akka" %% "akka-actor" % "2.3.15", "fr.brouillard.oss.security.xhub" % "xhub4j-core" % "1.0.0", + "com.github.bkromhout" % "java-diff-utils" % "2.1.1", "com.enragedginger" %% "akka-quartz-scheduler" % "1.4.0-akka-2.3.x" exclude("c3p0","c3p0"), "org.eclipse.jetty" % "jetty-webapp" % JettyVersion % "provided", "javax.servlet" % "javax.servlet-api" % "3.1.0" % "provided", diff --git a/src/main/scala/gitbucket/core/controller/ApiController.scala b/src/main/scala/gitbucket/core/controller/ApiController.scala index ba32532..344969e 100644 --- a/src/main/scala/gitbucket/core/controller/ApiController.scala +++ b/src/main/scala/gitbucket/core/controller/ApiController.scala @@ -22,6 +22,7 @@ with IssuesService with LabelsService with PullRequestService + with CommitsService with CommitStatusService with RepositoryCreationService with HandleCommentService diff --git a/src/main/scala/gitbucket/core/controller/DashboardController.scala b/src/main/scala/gitbucket/core/controller/DashboardController.scala index 1227f24..9aec00f 100644 --- a/src/main/scala/gitbucket/core/controller/DashboardController.scala +++ b/src/main/scala/gitbucket/core/controller/DashboardController.scala @@ -1,13 +1,13 @@ package gitbucket.core.controller import gitbucket.core.dashboard.html -import gitbucket.core.service.{RepositoryService, PullRequestService, AccountService, IssuesService} -import gitbucket.core.util.{StringUtil, Keys, UsersAuthenticator} +import gitbucket.core.service._ +import gitbucket.core.util.{Keys, UsersAuthenticator} import gitbucket.core.util.Implicits._ import gitbucket.core.service.IssuesService._ class DashboardController extends DashboardControllerBase - with IssuesService with PullRequestService with RepositoryService with AccountService + with IssuesService with PullRequestService with RepositoryService with AccountService with CommitsService with UsersAuthenticator trait DashboardControllerBase extends ControllerBase { diff --git a/src/main/scala/gitbucket/core/controller/IssuesController.scala b/src/main/scala/gitbucket/core/controller/IssuesController.scala index f0afabb..4222dba 100644 --- a/src/main/scala/gitbucket/core/controller/IssuesController.scala +++ b/src/main/scala/gitbucket/core/controller/IssuesController.scala @@ -14,12 +14,33 @@ class IssuesController extends IssuesControllerBase - with IssuesService with RepositoryService with AccountService with LabelsService with MilestonesService with ActivityService with HandleCommentService - with ReadableUsersAuthenticator with ReferrerAuthenticator with WritableUsersAuthenticator with PullRequestService with WebHookIssueCommentService + with IssuesService + with RepositoryService + with AccountService + with LabelsService + with MilestonesService + with ActivityService + with HandleCommentService + with ReadableUsersAuthenticator + with ReferrerAuthenticator + with WritableUsersAuthenticator + with PullRequestService + with WebHookIssueCommentService + with CommitsService trait IssuesControllerBase extends ControllerBase { - self: IssuesService with RepositoryService with AccountService with LabelsService with MilestonesService with ActivityService with HandleCommentService - with ReadableUsersAuthenticator with ReferrerAuthenticator with WritableUsersAuthenticator with PullRequestService with WebHookIssueCommentService => + self: IssuesService + with RepositoryService + with AccountService + with LabelsService + with MilestonesService + with ActivityService + with HandleCommentService + with ReadableUsersAuthenticator + with ReferrerAuthenticator + with WritableUsersAuthenticator + with PullRequestService + with WebHookIssueCommentService => case class IssueCreateForm(title: String, content: Option[String], assignedUserName: Option[String], milestoneId: Option[Int], labelNames: Option[String]) diff --git a/src/main/scala/gitbucket/core/controller/PullRequestsController.scala b/src/main/scala/gitbucket/core/controller/PullRequestsController.scala index ebcc953..52f675b 100644 --- a/src/main/scala/gitbucket/core/controller/PullRequestsController.scala +++ b/src/main/scala/gitbucket/core/controller/PullRequestsController.scala @@ -11,7 +11,6 @@ import gitbucket.core.util.ControlUtil._ import gitbucket.core.util.Directory._ import gitbucket.core.util.Implicits._ -import gitbucket.core.util.JGitUtil._ import gitbucket.core.util._ import gitbucket.core.view import gitbucket.core.view.helpers @@ -498,25 +497,25 @@ (defaultOwner, value) } - private def getRequestCompareInfo(userName: String, repositoryName: String, branch: String, - requestUserName: String, requestRepositoryName: String, requestCommitId: String): (Seq[Seq[CommitInfo]], Seq[DiffInfo]) = - using( - Git.open(getRepositoryDir(userName, repositoryName)), - Git.open(getRepositoryDir(requestUserName, requestRepositoryName)) - ){ (oldGit, newGit) => - val oldId = oldGit.getRepository.resolve(branch) - val newId = newGit.getRepository.resolve(requestCommitId) - - val commits = newGit.log.addRange(oldId, newId).call.iterator.asScala.map { revCommit => - new CommitInfo(revCommit) - }.toList.splitWith { (commit1, commit2) => - helpers.date(commit1.commitTime) == view.helpers.date(commit2.commitTime) - } - - val diffs = JGitUtil.getDiffs(newGit, oldId.getName, newId.getName, true) - - (commits, diffs) - } +// private def getRequestCompareInfo(userName: String, repositoryName: String, branch: String, +// requestUserName: String, requestRepositoryName: String, requestCommitId: String): (Seq[Seq[CommitInfo]], Seq[DiffInfo]) = +// using( +// Git.open(getRepositoryDir(userName, repositoryName)), +// Git.open(getRepositoryDir(requestUserName, requestRepositoryName)) +// ){ (oldGit, newGit) => +// val oldId = oldGit.getRepository.resolve(branch) +// val newId = newGit.getRepository.resolve(requestCommitId) +// +// val commits = newGit.log.addRange(oldId, newId).call.iterator.asScala.map { revCommit => +// new CommitInfo(revCommit) +// }.toList.splitWith { (commit1, commit2) => +// helpers.date(commit1.commitTime) == view.helpers.date(commit2.commitTime) +// } +// +// val diffs = JGitUtil.getDiffs(newGit, oldId.getName, newId.getName, true) +// +// (commits, diffs) +// } private def searchPullRequests(userName: Option[String], repository: RepositoryService.RepositoryInfo) = defining(repository.owner, repository.name){ case (owner, repoName) => diff --git a/src/main/scala/gitbucket/core/service/CommitsService.scala b/src/main/scala/gitbucket/core/service/CommitsService.scala index 5ccffd5..229519d 100644 --- a/src/main/scala/gitbucket/core/service/CommitsService.scala +++ b/src/main/scala/gitbucket/core/service/CommitsService.scala @@ -42,12 +42,18 @@ updatedDate = currentDate, issueId = issueId) + def updateCommitCommentPosition(commentId: Int, commitId: String, oldLine: Option[Int], newLine: Option[Int])(implicit s: Session): Unit = + CommitComments.filter(_.byPrimaryKey(commentId)) + .map { t => + (t.commitId, t.oldLine, t.newLine) + }.update(commitId, oldLine, newLine) + def updateCommitComment(commentId: Int, content: String)(implicit s: Session) = CommitComments .filter (_.byPrimaryKey(commentId)) .map { t => - t.content -> t.updatedDate - }.update (content, currentDate) + t.content -> t.updatedDate + }.update (content, currentDate) def deleteCommitComment(commentId: Int)(implicit s: Session) = CommitComments filter (_.byPrimaryKey(commentId)) delete diff --git a/src/main/scala/gitbucket/core/service/PullRequestService.scala b/src/main/scala/gitbucket/core/service/PullRequestService.scala index e90ba3f..6f94ab3 100644 --- a/src/main/scala/gitbucket/core/service/PullRequestService.scala +++ b/src/main/scala/gitbucket/core/service/PullRequestService.scala @@ -1,12 +1,22 @@ package gitbucket.core.service -import gitbucket.core.model.{Account, Issue, PullRequest, WebHook, CommitStatus, CommitState} +import difflib.{Delta, DiffUtils} +import gitbucket.core.model.{Session => _, _} import gitbucket.core.model.Profile._ +import gitbucket.core.util.ControlUtil._ +import gitbucket.core.util.Directory._ +import gitbucket.core.util.Implicits._ import gitbucket.core.util.JGitUtil +import gitbucket.core.util.JGitUtil.{CommitInfo, DiffInfo} +import gitbucket.core.view +import gitbucket.core.view.helpers +import org.eclipse.jgit.api.Git import profile.simple._ +import scala.collection.JavaConverters._ -trait PullRequestService { self: IssuesService => + +trait PullRequestService { self: IssuesService with CommitsService => import PullRequestService._ def getPullRequest(owner: String, repository: String, issueId: Int) @@ -111,9 +121,26 @@ def updatePullRequests(owner: String, repository: String, branch: String)(implicit s: Session): Unit = getPullRequestsByRequest(owner, repository, branch, false).foreach { pullreq => if(Repositories.filter(_.byRepository(pullreq.userName, pullreq.repositoryName)).exists.run){ + // Update the git repository val (commitIdTo, commitIdFrom) = JGitUtil.updatePullRequest( pullreq.userName, pullreq.repositoryName, pullreq.branch, pullreq.issueId, pullreq.requestUserName, pullreq.requestRepositoryName, pullreq.requestBranch) + + // Update comments position + val comments = getCommitComments(pullreq.userName, pullreq.repositoryName, pullreq.commitIdTo, true) + println(comments) + comments.foreach { comment => + comment match { + case CommitComment(_, _, _, commitId, _, _, Some(fileName), _, Some(newLine), _, _, _) => { + getNewLineNumber(fileName, newLine, pullreq.requestUserName, pullreq.requestRepositoryName, pullreq.commitIdTo, commitIdTo).foreach { lineNumber => + updateCommitCommentPosition(commitId, commitIdTo, None, Some(lineNumber)) + } + } + case _ => // Do nothing + } + } + + // Update commit id in the PULL_REQUEST table updateCommitId(pullreq.userName, pullreq.repositoryName, pullreq.issueId, commitIdTo, commitIdFrom) } } @@ -137,6 +164,62 @@ .firstOption } } + + def getNewLineNumber(file: String, lineNumber: Int, userName: String, repositoryName: String, oldCommitId: String, newCommitId: String): Option[Int] = { + val (_, diffs) = getRequestCompareInfo(userName, repositoryName, oldCommitId, userName, repositoryName, newCommitId) + + var counter = lineNumber + + diffs.find(x => x.oldPath == "test").foreach { diff => + (diff.oldContent, diff.newContent) match { + case (Some(oldContent), Some(newContent)) => { + val oldLines = oldContent.replace("\r\n", "\n").split("\n") + val newLines = newContent.replace("\r\n", "\n").split("\n") + val deltas = DiffUtils.diff(oldLines.toList.asJava, newLines.toList.asJava) + + deltas.getDeltas.asScala.filter(_.getOriginal.getPosition < lineNumber).foreach { delta => + delta.getType match { + case Delta.TYPE.CHANGE => + if(delta.getOriginal.getPosition <= lineNumber - 1 && lineNumber <= delta.getOriginal.getPosition + delta.getRevised.getLines.size){ + counter = -1 + } else { + counter = counter + (delta.getRevised.getLines.size - delta.getOriginal.getLines.size) + } + case Delta.TYPE.INSERT => + counter = counter + delta.getRevised.getLines.size + case Delta.TYPE.DELETE => + counter = counter - delta.getOriginal.getLines.size + } + } + counter + } + case _ => counter = -1 + } + } + + if(counter >= 0) Some(counter) else None + } + + def getRequestCompareInfo(userName: String, repositoryName: String, branch: String, + requestUserName: String, requestRepositoryName: String, requestCommitId: String): (Seq[Seq[CommitInfo]], Seq[DiffInfo]) = + using( + Git.open(getRepositoryDir(userName, repositoryName)), + Git.open(getRepositoryDir(requestUserName, requestRepositoryName)) + ){ (oldGit, newGit) => + val oldId = oldGit.getRepository.resolve(branch) + val newId = newGit.getRepository.resolve(requestCommitId) + + val commits = newGit.log.addRange(oldId, newId).call.iterator.asScala.map { revCommit => + new CommitInfo(revCommit) + }.toList.splitWith { (commit1, commit2) => + helpers.date(commit1.commitTime) == view.helpers.date(commit2.commitTime) + } + + val diffs = JGitUtil.getDiffs(newGit, oldId.getName, newId.getName, true) + + (commits, diffs) + } + } object PullRequestService { diff --git a/src/main/scala/gitbucket/core/servlet/GitRepositoryServlet.scala b/src/main/scala/gitbucket/core/servlet/GitRepositoryServlet.scala index ad8bc03..76f1f06 100644 --- a/src/main/scala/gitbucket/core/servlet/GitRepositoryServlet.scala +++ b/src/main/scala/gitbucket/core/servlet/GitRepositoryServlet.scala @@ -110,7 +110,7 @@ class CommitLogHook(owner: String, repository: String, pusher: String, baseUrl: String)(implicit session: Session) extends PostReceiveHook with PreReceiveHook with RepositoryService with AccountService with IssuesService with ActivityService with PullRequestService with WebHookService - with WebHookPullRequestService { + with WebHookPullRequestService with CommitsService { private val logger = LoggerFactory.getLogger(classOf[CommitLogHook]) private var existIds: Seq[String] = Nil diff --git a/src/main/twirl/gitbucket/core/helper/diff.scala.html b/src/main/twirl/gitbucket/core/helper/diff.scala.html index cb56b01..17fdea2 100644 --- a/src/main/twirl/gitbucket/core/helper/diff.scala.html +++ b/src/main/twirl/gitbucket/core/helper/diff.scala.html @@ -240,7 +240,6 @@ var oldline = $v.attr('oldline'); var newline = $v.attr('newline'); var tmp; - var diff; if (typeof oldline !== 'undefined') { if (typeof newline !== 'undefined') { tmp = getInlineContainer(); @@ -248,13 +247,11 @@ tmp = getInlineContainer('old'); } tmp.children('td:first').html($v.clone().show()); - diff.find('table.diff').find('.oldline[line-number=' + oldline + ']') - .parent().nextAll(':not(.not-diff):first').before(tmp); + diff.find('table.diff').find('.oldline[line-number=' + oldline + ']').parent().nextAll(':not(.not-diff):first').before(tmp); } else { tmp = getInlineContainer('new'); tmp.children('td:last').html($v.clone().show()); - diff.find('table.diff').find('.newline[line-number=' + newline + ']') - .parent().nextAll(':not(.not-diff):first').before(tmp); + diff.find('table.diff').find('.newline[line-number=' + newline + ']').parent().nextAll(':not(.not-diff):first').before(tmp); } if (!diff.find('.toggle-notes').prop('checked')) { tmp.hide(); @@ -263,10 +260,10 @@ function renderStatBar(add, del){ if(add + del > 5){ if(add){ - if(add+'+add+'-'+del+'').append(renderStatBar(add,del).attr('title',(add+del)+" lines changed").tooltip()); + $('span.diffstat[data-diff-id="'+i+'"]') + .html('+' + add + '-' + del + '') + .append(renderStatBar(add, del).attr('title', (add + del) + " lines changed").tooltip()); @if(hasWritePermission) { diffText.find('.body').each(function(){ $('+').prependTo(this); }); @@ -301,7 +300,7 @@ @if(showLineNotes){ var fileName = table.attr('filename'); $('.inline-comment').each(function(i, v) { - if($(this).attr('filename')==fileName){ + if($(this).attr('filename') == fileName){ renderOneCommitCommentIntoDiff($(this), table); } });