diff --git a/src/main/scala/gitbucket/core/controller/PullRequestsController.scala b/src/main/scala/gitbucket/core/controller/PullRequestsController.scala index bb6ec2640..05a4ec7c3 100644 --- a/src/main/scala/gitbucket/core/controller/PullRequestsController.scala +++ b/src/main/scala/gitbucket/core/controller/PullRequestsController.scala @@ -375,6 +375,11 @@ trait PullRequestsControllerBase extends ControllerBase { get("/:owner/:repository/compare")(referrersOnly { forkedRepository => val headBranch = params.get("head") + val quickLoad = params + .get("quick") + .map(_.equalsIgnoreCase("true")) + .getOrElse(context.settings.basicBehavior.compareNoCheckByDefault) + val quickQuery = if (quickLoad) "?quick=true" else "" (forkedRepository.repository.originUserName, forkedRepository.repository.originRepositoryName) match { case (Some(originUserName), Some(originRepositoryName)) => getRepository(originUserName, originRepositoryName).map { originRepository => @@ -388,7 +393,7 @@ trait PullRequestsControllerBase extends ControllerBase { .getOrElse(JGitUtil.getDefaultBranch(oldGit, originRepository).get._2) redirect( - s"/${forkedRepository.owner}/${forkedRepository.name}/compare/$originUserName:$oldBranch...$newBranch" + s"/${forkedRepository.owner}/${forkedRepository.name}/compare/${originUserName}:${oldBranch}...${newBranch}${quickQuery}" ) } } getOrElse NotFound() @@ -396,7 +401,7 @@ trait PullRequestsControllerBase extends ControllerBase { Using.resource(Git.open(getRepositoryDir(forkedRepository.owner, forkedRepository.name))) { git => JGitUtil.getDefaultBranch(git, forkedRepository).map { case (_, defaultBranch) => redirect( - s"/${forkedRepository.owner}/${forkedRepository.name}/compare/$defaultBranch...${headBranch.getOrElse(defaultBranch)}" + s"/${forkedRepository.owner}/${forkedRepository.name}/compare/${defaultBranch}...${headBranch.getOrElse(defaultBranch)}${quickQuery}" ) } getOrElse { redirect(s"/${forkedRepository.owner}/${forkedRepository.name}") @@ -436,76 +441,112 @@ trait PullRequestsControllerBase extends ControllerBase { val Seq(origin, forked) = multiParams("splat") val (originOwner, originId) = parseCompareIdentifier(origin, forkedRepository.owner) val (forkedOwner, forkedId) = parseCompareIdentifier(forked, forkedRepository.owner) + val requestedCheck = params.get("check").contains("true") + val quickLoad = params + .get("quick") + .map(_.equalsIgnoreCase("true")) + .getOrElse(!requestedCheck && context.settings.basicBehavior.compareNoCheckByDefault) (for ( originRepositoryName <- getOriginRepositoryName(originOwner, forkedOwner, forkedRepository); originRepository <- getRepository(originOwner, originRepositoryName) ) yield { - val (oldId, newId) = - getPullRequestCommitFromTo(originRepository, forkedRepository, originId, forkedId) + val members = + ((forkedRepository.repository.originUserName, forkedRepository.repository.originRepositoryName) match { + case (Some(userName), Some(repositoryName)) => + getRepository(userName, repositoryName) match { + case Some(x) => x.repository :: getForkedRepositories(userName, repositoryName) + case None => getForkedRepositories(userName, repositoryName) + } + case _ => + forkedRepository.repository :: getForkedRepositories(forkedRepository.owner, forkedRepository.name) + }).map { repository => + (repository.userName, repository.repositoryName, repository.defaultBranch) + } - (oldId, newId) match { - case (Some(oldId), Some(newId)) => - val (commits, diffs) = getRequestCompareInfo( - originRepository.owner, - originRepository.name, - oldId.getName, - forkedRepository.owner, - forkedRepository.name, - newId.getName, - context.settings - ) + val text = forkedId.replaceAll("[\\-_]", " ") + val fallbackTitle = text.substring(0, 1).toUpperCase + text.substring(1) - val title = if (commits.flatten.length == 1) { - commits.flatten.head.shortMessage - } else { - val text = forkedId.replaceAll("[\\-_]", " ") - text.substring(0, 1).toUpperCase + text.substring(1) - } + if (quickLoad) { + html.compare( + fallbackTitle, + Seq.empty, + Seq.empty, + members, + List.empty, + originId, + forkedId, + "", + "", + getContentTemplate(originRepository, "PULL_REQUEST_TEMPLATE"), + forkedRepository, + originRepository, + forkedRepository, + hasDeveloperRole(originRepository.owner, originRepository.name, context.loginAccount), + getAssignableUserNames(originRepository.owner, originRepository.name), + getMilestones(originRepository.owner, originRepository.name), + getPriorities(originRepository.owner, originRepository.name), + getDefaultPriority(originRepository.owner, originRepository.name), + getLabels(originRepository.owner, originRepository.name), + getCustomFields(originRepository.owner, originRepository.name).filter(_.enableForPullRequests), + quickLoad + ) + } else { + val (oldId, newId) = + getPullRequestCommitFromTo(originRepository, forkedRepository, originId, forkedId) - html.compare( - title, - commits, - diffs, - ((forkedRepository.repository.originUserName, forkedRepository.repository.originRepositoryName) match { - case (Some(userName), Some(repositoryName)) => - getRepository(userName, repositoryName) match { - case Some(x) => x.repository :: getForkedRepositories(userName, repositoryName) - case None => getForkedRepositories(userName, repositoryName) - } - case _ => - forkedRepository.repository :: getForkedRepositories(forkedRepository.owner, forkedRepository.name) - }).map { repository => - (repository.userName, repository.repositoryName, repository.defaultBranch) - }, - commits.flatten - .flatMap(commit => - getCommitComments(forkedRepository.owner, forkedRepository.name, commit.id, includePullRequest = false) - ) - .toList, - originId, - forkedId, - oldId.getName, - newId.getName, - getContentTemplate(originRepository, "PULL_REQUEST_TEMPLATE"), - forkedRepository, - originRepository, - forkedRepository, - hasDeveloperRole(originRepository.owner, originRepository.name, context.loginAccount), - getAssignableUserNames(originRepository.owner, originRepository.name), - getMilestones(originRepository.owner, originRepository.name), - getPriorities(originRepository.owner, originRepository.name), - getDefaultPriority(originRepository.owner, originRepository.name), - getLabels(originRepository.owner, originRepository.name), - getCustomFields(originRepository.owner, originRepository.name).filter(_.enableForPullRequests) - ) - case (oldId, newId) => - redirect( - s"/${forkedRepository.owner}/${forkedRepository.name}/compare/" + - s"$originOwner:${oldId.map(_ => originId).getOrElse(originRepository.repository.defaultBranch)}..." + - s"$forkedOwner:${newId.map(_ => forkedId).getOrElse(forkedRepository.repository.defaultBranch)}" - ) + (oldId, newId) match { + case (Some(oldId), Some(newId)) => + val (commits, diffs) = getRequestCompareInfo( + originRepository.owner, + originRepository.name, + oldId.getName, + forkedRepository.owner, + forkedRepository.name, + newId.getName, + context.settings + ) + val title = if (commits.flatten.length == 1) { + commits.flatten.head.shortMessage + } else { + fallbackTitle + } + + val commitComments = commits.flatten + .flatMap(commit => getCommitComments(forkedRepository.owner, forkedRepository.name, commit.id, false)) + .toList + + html.compare( + title, + commits, + diffs, + members, + commitComments, + originId, + forkedId, + oldId.getName, + newId.getName, + getContentTemplate(originRepository, "PULL_REQUEST_TEMPLATE"), + forkedRepository, + originRepository, + forkedRepository, + hasDeveloperRole(originRepository.owner, originRepository.name, context.loginAccount), + getAssignableUserNames(originRepository.owner, originRepository.name), + getMilestones(originRepository.owner, originRepository.name), + getPriorities(originRepository.owner, originRepository.name), + getDefaultPriority(originRepository.owner, originRepository.name), + getLabels(originRepository.owner, originRepository.name), + getCustomFields(originRepository.owner, originRepository.name).filter(_.enableForPullRequests), + quickLoad + ) + case (oldId, newId) => + redirect( + s"/${forkedRepository.owner}/${forkedRepository.name}/compare/" + + s"${originOwner}:${oldId.map(_ => originId).getOrElse(originRepository.repository.defaultBranch)}..." + + s"${forkedOwner}:${newId.map(_ => forkedId).getOrElse(forkedRepository.repository.defaultBranch)}" + ) + } } }) getOrElse NotFound() }) diff --git a/src/main/scala/gitbucket/core/controller/SystemSettingsController.scala b/src/main/scala/gitbucket/core/controller/SystemSettingsController.scala index 3702dac67..12150bb1c 100644 --- a/src/main/scala/gitbucket/core/controller/SystemSettingsController.scala +++ b/src/main/scala/gitbucket/core/controller/SystemSettingsController.scala @@ -49,6 +49,7 @@ trait SystemSettingsControllerBase extends AccountManagementControllerBase { "gravatar" -> trim(label("Gravatar", boolean())), "notification" -> trim(label("Notification", boolean())), "limitVisibleRepositories" -> trim(label("limitVisibleRepositories", boolean())), + "compareNoCheckByDefault" -> trim(label("Default compare mode", boolean())), )(BasicBehavior.apply), "ssh" -> mapping( "enabled" -> trim(label("SSH access", boolean())), diff --git a/src/main/scala/gitbucket/core/service/SystemSettingsService.scala b/src/main/scala/gitbucket/core/service/SystemSettingsService.scala index 479f68679..eb43b56a2 100644 --- a/src/main/scala/gitbucket/core/service/SystemSettingsService.scala +++ b/src/main/scala/gitbucket/core/service/SystemSettingsService.scala @@ -30,6 +30,7 @@ trait SystemSettingsService { props.setProperty(Gravatar, settings.basicBehavior.gravatar.toString) props.setProperty(Notification, settings.basicBehavior.notification.toString) props.setProperty(LimitVisibleRepositories, settings.basicBehavior.limitVisibleRepositories.toString) + props.setProperty(CompareNoCheckByDefault, settings.basicBehavior.compareNoCheckByDefault.toString) props.setProperty(SshEnabled, settings.ssh.enabled.toString) settings.ssh.bindAddress.foreach { bindAddress => props.setProperty(SshBindAddressHost, bindAddress.host.trim()) @@ -128,7 +129,8 @@ trait SystemSettingsService { ), getValue(props, Gravatar, false), getValue(props, Notification, false), - getValue(props, LimitVisibleRepositories, false) + getValue(props, LimitVisibleRepositories, false), + getValue(props, CompareNoCheckByDefault, false) ), Ssh( enabled = getValue(props, SshEnabled, false), @@ -281,6 +283,7 @@ object SystemSettingsService { gravatar: Boolean, notification: Boolean, limitVisibleRepositories: Boolean, + compareNoCheckByDefault: Boolean, ) case class RepositoryOperation( @@ -413,6 +416,7 @@ object SystemSettingsService { private val Gravatar = "gravatar" private val Notification = "notification" private val LimitVisibleRepositories = "limitVisibleRepositories" + private val CompareNoCheckByDefault = "compare_no_check_by_default" private val SshEnabled = "ssh" private val SshHost = "ssh.host" private val SshPort = "ssh.port" diff --git a/src/main/twirl/gitbucket/core/admin/settings_system.scala.html b/src/main/twirl/gitbucket/core/admin/settings_system.scala.html index 4fef350d8..d7837e535 100644 --- a/src/main/twirl/gitbucket/core/admin/settings_system.scala.html +++ b/src/main/twirl/gitbucket/core/admin/settings_system.scala.html @@ -240,6 +240,21 @@ + + +
+ +
+ + +
+
diff --git a/src/main/twirl/gitbucket/core/issues/list.scala.html b/src/main/twirl/gitbucket/core/issues/list.scala.html index 0c6368030..ecf345140 100644 --- a/src/main/twirl/gitbucket/core/issues/list.scala.html +++ b/src/main/twirl/gitbucket/core/issues/list.scala.html @@ -37,7 +37,7 @@ New issue } @if(target == "pulls"){ - New pull request + New pull request } } diff --git a/src/main/twirl/gitbucket/core/issues/listparts.scala.html b/src/main/twirl/gitbucket/core/issues/listparts.scala.html index 2c369e7ea..bf0f9e1ab 100644 --- a/src/main/twirl/gitbucket/core/issues/listparts.scala.html +++ b/src/main/twirl/gitbucket/core/issues/listparts.scala.html @@ -197,7 +197,7 @@ @if(target == "issues"){ Create a new issue. } else { - Create a new pull request. + Create a new pull request. } } @* diff --git a/src/main/twirl/gitbucket/core/pulls/compare.scala.html b/src/main/twirl/gitbucket/core/pulls/compare.scala.html index fbfeafe5a..53bf5a0fc 100644 --- a/src/main/twirl/gitbucket/core/pulls/compare.scala.html +++ b/src/main/twirl/gitbucket/core/pulls/compare.scala.html @@ -17,11 +17,11 @@ priorities: List[gitbucket.core.model.Priority], defaultPriority: Option[gitbucket.core.model.Priority], labels: List[gitbucket.core.model.Label], - customFields: List[gitbucket.core.model.CustomField])(implicit context: gitbucket.core.controller.Context) + customFields: List[gitbucket.core.model.CustomField], + quickLoad: Boolean)(implicit context: gitbucket.core.controller.Context) @import gitbucket.core.view.helpers @gitbucket.core.html.main(s"Pull requests - ${repository.owner}/${repository.name}", Some(repository)){ @gitbucket.core.html.menu("pulls", repository){ -
@gitbucket.core.helper.html.dropdown(originRepository.owner + "/" + originRepository.name, "base fork", filter=("origin_repo", "Find Repository...")) { @@ -54,14 +54,25 @@ }
- @if(originRepository.branchList.contains(originId) && forkedRepository.branchList.contains(forkedId)){ - - }
- @if(commits.nonEmpty && context.loginAccount.isDefined && originRepository.branchList.contains(originId) && forkedRepository.branchList.contains(forkedId)){ -
+ @if(originRepository.branchList.contains(originId) && forkedRepository.branchList.contains(forkedId)){ +
+ +
+ + } + +
+ @if(commits.nonEmpty && context.loginAccount.isDefined && originRepository.branchList.contains(originId) && forkedRepository.branchList.contains(forkedId)){ +
+
@@ -115,10 +126,15 @@ )
-
- } -
- @if(commits.isEmpty){ + + + } + @if(quickLoad){ +
+

Comparison is ready to load.

+ Click Check mergeability to compute changes and show merge status. +
+ } else if(commits.isEmpty){

There isn't anything to compare.

@originRepository.owner:@originId and @forkedRepository.owner:@forkedId are identical. @@ -194,6 +210,17 @@ $(function(){ $(function(){ + var compareState = $('#compare-state'); + var isQuickLoad = compareState.data('quick-load') === true || compareState.data('quick-load') === 'true'; + var defaultNoCheck = compareState.data('default-no-check') === true || compareState.data('default-no-check') === 'true'; + var autoMergecheck = + /(?:\?|&)check=true(?:&|$)/.test(window.location.search) || (!isQuickLoad && !defaultNoCheck); + var hasValidBranches = + compareState.data('has-valid-branches') === true || compareState.data('has-valid-branches') === 'true'; + var canAutoMergecheck = + compareState.data('can-auto-mergecheck') === true || compareState.data('can-auto-mergecheck') === 'true'; + var compareQuery = (defaultNoCheck || isQuickLoad) ? '?quick=true' : ''; + function updateSelector(e){ e.parents('ul').find('i').attr('class', 'octicon'); e.find('i').addClass('octicon-check'); @@ -209,7 +236,7 @@ $(function(){ $.trim($('i.octicon-check').parents('a.origin-owner' ).data('owner')) + ':' + $.trim($('i.octicon-check').parents('a.origin-owner' ).data('default-branch')) + '...' + $.trim($('i.octicon-check').parents('a.forked-owner' ).data('owner')) + ':' + - $.trim($('i.octicon-check').parents('a.forked-branch').data('branch')); + $.trim($('i.octicon-check').parents('a.forked-branch').data('branch')) + compareQuery; }); $('a.forked-owner').click(function(){ @@ -221,7 +248,7 @@ $(function(){ $.trim($('i.octicon-check').parents('a.origin-owner' ).data('owner')) + ':' + $.trim($('i.octicon-check').parents('a.origin-branch').data('branch')) + '...' + $.trim($('i.octicon-check').parents('a.forked-owner' ).data('owner')) + ':' + - $.trim($('i.octicon-check').parents('a.forked-owner' ).data('default-branch')); + $.trim($('i.octicon-check').parents('a.forked-owner' ).data('default-branch')) + compareQuery; }); $('a.origin-branch, a.forked-branch').click(function(){ @@ -233,22 +260,44 @@ $(function(){ $.trim($('i.octicon-check').parents('a.origin-owner' ).data('owner')) + ':' + $.trim($('i.octicon-check').parents('a.origin-branch').data('branch')) + '...' + $.trim($('i.octicon-check').parents('a.forked-owner' ).data('owner')) + ':' + - $.trim($('i.octicon-check').parents('a.forked-branch').data('branch')); + $.trim($('i.octicon-check').parents('a.forked-branch').data('branch')) + compareQuery; }); - @if(context.loginAccount.isDefined && originRepository.branchList.contains(originId) && forkedRepository.branchList.contains(forkedId)){ + if (isQuickLoad && hasValidBranches) { + $('#check-conflict-button').click(function(){ + location.href = '@helpers.url(forkedRepository)/compare/' + + $.trim($('i.octicon-check').parents('a.origin-owner' ).data('owner')) + ':' + + $.trim($('i.octicon-check').parents('a.origin-branch').data('branch')) + '...' + + $.trim($('i.octicon-check').parents('a.forked-owner' ).data('owner')) + ':' + + $.trim($('i.octicon-check').parents('a.forked-branch').data('branch')) + '?check=true'; + }); + } else if (canAutoMergecheck) { function checkConflict(from, to){ + var button = $('#check-conflict-button'); + button.attr('disabled', 'disabled'); $('.check-conflict').show(); + $('.check-conflict').html(' Checking...'); $.get('@helpers.url(forkedRepository)/compare/' + from + '...' + to + '/mergecheck', - function(data){ $('.check-conflict').html(data); }); + function(data){ $('.check-conflict').html(data); } + ).fail(function(){ + $('.check-conflict').html('Failed to check mergeability.'); + }).always(function(){ + button.removeAttr('disabled'); + }); } - checkConflict( - $.trim($('i.octicon-check').parents('a.origin-owner' ).data('owner')) + ":" + - $.trim($('i.octicon-check').parents('a.origin-branch').data('branch')), - $.trim($('i.octicon-check').parents('a.forked-owner' ).data('owner')) + ":" + - $.trim($('i.octicon-check').parents('a.forked-branch').data('branch')) - ); + $('#check-conflict-button').click(function(){ + checkConflict( + $.trim($('i.octicon-check').parents('a.origin-owner' ).data('owner')) + ":" + + $.trim($('i.octicon-check').parents('a.origin-branch').data('branch')), + $.trim($('i.octicon-check').parents('a.forked-owner' ).data('owner')) + ":" + + $.trim($('i.octicon-check').parents('a.forked-branch').data('branch')) + ); + }); + + if (autoMergecheck) { + $('#check-conflict-button').click(); + } } }); diff --git a/src/main/twirl/gitbucket/core/pulls/menu.scala.html b/src/main/twirl/gitbucket/core/pulls/menu.scala.html index ed4349549..0f7a10425 100644 --- a/src/main/twirl/gitbucket/core/pulls/menu.scala.html +++ b/src/main/twirl/gitbucket/core/pulls/menu.scala.html @@ -19,7 +19,7 @@ Edit } @if(context.loginAccount.isDefined) { - New pull request + New pull request }