mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
[space] Add ability to checkout MR branch
GitOrigin-RevId: 342cd559de34cd7796b155984d0248d93e43150a
This commit is contained in:
committed by
intellij-monorepo-bot
parent
d19a4b0d71
commit
558e66f0b0
+180
@@ -0,0 +1,180 @@
|
||||
// Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.intellij.space.vcs.review.details
|
||||
|
||||
import circlet.code.api.MergeRequestBranch
|
||||
import com.intellij.openapi.actionSystem.AnActionEvent
|
||||
import com.intellij.openapi.progress.ProgressIndicator
|
||||
import com.intellij.openapi.progress.Task
|
||||
import com.intellij.openapi.project.DumbAwareAction
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.space.messages.SpaceBundle
|
||||
import com.intellij.space.vcs.SpaceRepoInfo
|
||||
import com.intellij.space.vcs.review.SpaceReviewDataKeys
|
||||
import git4idea.GitLocalBranch
|
||||
import git4idea.GitStandardRemoteBranch
|
||||
import git4idea.branch.GitBranchUiHandlerImpl
|
||||
import git4idea.branch.GitBranchUtil
|
||||
import git4idea.branch.GitBranchWorker
|
||||
import git4idea.branch.GitNewBranchOptions
|
||||
import git4idea.commands.Git
|
||||
import git4idea.fetch.GitFetchSupport
|
||||
import git4idea.repo.GitRepository
|
||||
import org.jetbrains.annotations.Nullable
|
||||
|
||||
class SpaceReviewCheckoutBranchAction : DumbAwareAction(SpaceBundle.messagePointer("review.actions.checkout.branch")) {
|
||||
override fun update(e: AnActionEvent) {
|
||||
val detailsVm = getMergeRequestVm(e)
|
||||
|
||||
if (detailsVm == null) {
|
||||
e.presentation.isEnabledAndVisible = false
|
||||
return
|
||||
}
|
||||
|
||||
val repoInfo = detailsVm.repoInfo.value
|
||||
if (repoInfo == null) {
|
||||
e.presentation.isEnabledAndVisible = false
|
||||
return
|
||||
}
|
||||
val localBranch = detailsVm.mergeRequestBranchInfo.value.localBranch
|
||||
if (localBranch != null) {
|
||||
if (repoInfo.repository.currentBranchName == localBranch.name) {
|
||||
e.presentation.isEnabledAndVisible = false
|
||||
return
|
||||
}
|
||||
}
|
||||
e.presentation.isEnabledAndVisible = true
|
||||
}
|
||||
|
||||
override fun actionPerformed(e: AnActionEvent) {
|
||||
val project = e.project ?: return
|
||||
val detailsVm = getMergeRequestVm(e) ?: return
|
||||
val repoInfo = detailsVm.repoInfo.value ?: return
|
||||
|
||||
val gitRepository = repoInfo.repository
|
||||
val gitRepositories = listOf(gitRepository)
|
||||
|
||||
val sourceBranchInfo = detailsVm.sourceBranchInfo.value ?: return
|
||||
|
||||
val localBranch = detailsVm.mergeRequestBranchInfo.value.localBranch
|
||||
|
||||
if (localBranch != null) {
|
||||
checkoutLocalBranch(project, detailsVm, localBranch, gitRepositories)
|
||||
}
|
||||
else {
|
||||
val remoteBranch = GitStandardRemoteBranch(repoInfo.remote, sourceBranchInfo.displayName)
|
||||
|
||||
val options = GitBranchUtil.getNewBranchNameFromUser(
|
||||
project,
|
||||
gitRepositories,
|
||||
SpaceBundle.message("review.checkout.action.checkout.dialog.title", remoteBranch.name),
|
||||
sourceBranchInfo.displayName,
|
||||
false) ?: return
|
||||
|
||||
if (options.checkout) {
|
||||
checkoutRemoteBranch(project, detailsVm, gitRepository, repoInfo, sourceBranchInfo, options, gitRepositories)
|
||||
}
|
||||
else {
|
||||
object : Task.Backgroundable(project,
|
||||
SpaceBundle.message("review.checkout.action.progress.title.creating.branch", detailsVm.reviewKey),
|
||||
true) {
|
||||
override fun run(indicator: ProgressIndicator) {
|
||||
val git = Git.getInstance()
|
||||
|
||||
GitFetchSupport.fetchSupport(project)
|
||||
.fetch(gitRepository, repoInfo.remote)
|
||||
.throwExceptionIfFailed()
|
||||
|
||||
GitBranchWorker(project, git, GitBranchUiHandlerImpl(project, git, indicator))
|
||||
.createBranch(options.name, mapOf(gitRepository to sourceBranchInfo.ref))
|
||||
|
||||
gitRepository.update()
|
||||
}
|
||||
|
||||
}.queue()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkoutRemoteBranch(project: @Nullable Project,
|
||||
detailsVm: MergeRequestDetailsVm,
|
||||
gitRepository: GitRepository,
|
||||
repoInfo: SpaceRepoInfo,
|
||||
sourceBranchInfo: MergeRequestBranch,
|
||||
options: @Nullable GitNewBranchOptions,
|
||||
gitRepositories: List<GitRepository>) {
|
||||
object : Task.Backgroundable(project,
|
||||
SpaceBundle.message("review.checkout.action.progress.title.checking.out.branch", detailsVm.reviewKey),
|
||||
true) {
|
||||
override fun run(indicator: ProgressIndicator) {
|
||||
val git = Git.getInstance()
|
||||
|
||||
GitFetchSupport.fetchSupport(project)
|
||||
.fetch(gitRepository, repoInfo.remote)
|
||||
.throwExceptionIfFailed()
|
||||
|
||||
val remoteBranch = GitStandardRemoteBranch(repoInfo.remote, sourceBranchInfo.displayName)
|
||||
|
||||
GitBranchWorker(project, git, GitBranchUiHandlerImpl(project, git, indicator))
|
||||
.checkoutNewBranchStartingFrom(options.name, remoteBranch.name, gitRepositories)
|
||||
|
||||
gitRepository.update()
|
||||
}
|
||||
}.queue()
|
||||
}
|
||||
|
||||
private fun checkoutLocalBranch(project: @Nullable Project,
|
||||
detailsVm: MergeRequestDetailsVm,
|
||||
localBranch: GitLocalBranch,
|
||||
gitRepositories: List<GitRepository>) {
|
||||
object : Task.Backgroundable(project,
|
||||
SpaceBundle.message("review.checkout.action.progress.title.checking.out.branch", detailsVm.reviewKey),
|
||||
true) {
|
||||
override fun run(indicator: ProgressIndicator) {
|
||||
val git = Git.getInstance()
|
||||
GitBranchWorker(project, git, GitBranchUiHandlerImpl(project, git, indicator))
|
||||
.checkout(localBranch.name, false, gitRepositories)
|
||||
}
|
||||
}.queue()
|
||||
}
|
||||
}
|
||||
|
||||
class SpaceReviewUpdateBranchAction : DumbAwareAction(SpaceBundle.messagePointer("review.actions.update.branch")) {
|
||||
override fun update(e: AnActionEvent) {
|
||||
val detailsVm = getMergeRequestVm(e)
|
||||
|
||||
if (detailsVm == null) {
|
||||
e.presentation.isEnabledAndVisible = false
|
||||
return
|
||||
}
|
||||
|
||||
val repoInfo = detailsVm.repoInfo.value
|
||||
if (repoInfo == null) {
|
||||
e.presentation.isEnabledAndVisible = false
|
||||
return
|
||||
}
|
||||
val mergeRequestBranchInfo = detailsVm.mergeRequestBranchInfo.value
|
||||
|
||||
if (mergeRequestBranchInfo.localBranch != null && mergeRequestBranchInfo.isCurrentBranch) {
|
||||
e.presentation.isEnabledAndVisible = true
|
||||
return
|
||||
}
|
||||
|
||||
e.presentation.isEnabledAndVisible = false
|
||||
}
|
||||
|
||||
override fun actionPerformed(e: AnActionEvent) {
|
||||
val project = e.project ?: return
|
||||
val detailsVm = getMergeRequestVm(e) ?: return
|
||||
|
||||
val gitRepository = detailsVm.repoInfo.value?.repository ?: return
|
||||
val localBranch = detailsVm.mergeRequestBranchInfo.value.localBranch ?: return
|
||||
|
||||
GitBranchUtil.updateBranches(project, listOf(gitRepository), listOf(localBranch.name))
|
||||
}
|
||||
}
|
||||
|
||||
private fun getMergeRequestVm(e: AnActionEvent): MergeRequestDetailsVm? {
|
||||
val detailsVm = e.getData(SpaceReviewDataKeys.REVIEW_DETAILS_VM)
|
||||
|
||||
return if (detailsVm is MergeRequestDetailsVm) detailsVm else null
|
||||
}
|
||||
+3
-2
@@ -44,9 +44,10 @@ internal class SpaceReviewDetails(parentDisposable: Disposable,
|
||||
uiDisposable?.let { Disposer.dispose(it) }
|
||||
if (reviewListItem == null) return@forEach
|
||||
val detailsLifetime = sequentialLifetimes.next()
|
||||
val detailsVm = createReviewDetailsVm(detailsLifetime, project, workspace, spaceProjectInfo, repoInfo, reviewListItem)
|
||||
|
||||
uiDisposable = Disposer.newDisposable()
|
||||
|
||||
val detailsVm = createReviewDetailsVm(uiDisposable!!, detailsLifetime, project, workspace, spaceProjectInfo, repoInfo, reviewListItem)
|
||||
|
||||
Disposer.register(parentDisposable, uiDisposable as Disposable)
|
||||
|
||||
val detailsTabInfo = TabInfo(SpaceReviewInfoTabPanel(parentDisposable, detailsVm)).apply {
|
||||
|
||||
+86
-3
@@ -10,7 +10,10 @@ import circlet.platform.api.Ref
|
||||
import circlet.platform.api.TID
|
||||
import circlet.platform.client.*
|
||||
import circlet.workspaces.Workspace
|
||||
import com.intellij.openapi.Disposable
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.util.Disposer
|
||||
import com.intellij.space.utils.SpaceUrls
|
||||
import com.intellij.space.vcs.SpaceProjectInfo
|
||||
import com.intellij.space.vcs.SpaceRepoInfo
|
||||
@@ -19,6 +22,9 @@ import com.intellij.space.vcs.review.details.diff.SpaceDiffVmImpl
|
||||
import com.intellij.space.vcs.review.details.diff.SpaceReviewDiffLoader
|
||||
import com.intellij.space.vcs.review.details.process.SpaceReviewStateUpdater
|
||||
import com.intellij.space.vcs.review.details.process.SpaceReviewStateUpdaterImpl
|
||||
import com.intellij.vcs.log.data.DataPackChangeListener
|
||||
import com.intellij.vcs.log.impl.VcsProjectLog
|
||||
import git4idea.GitLocalBranch
|
||||
import libraries.coroutines.extra.Lifetime
|
||||
import libraries.coroutines.extra.Lifetimed
|
||||
import runtime.reactive.*
|
||||
@@ -29,7 +35,7 @@ internal sealed class SpaceReviewDetailsVm<R : CodeReviewRecord>(
|
||||
final override val lifetime: Lifetime,
|
||||
val ideaProject: Project,
|
||||
val spaceProjectInfo: SpaceProjectInfo,
|
||||
spaceReposInfo: Set<SpaceRepoInfo>,
|
||||
val spaceReposInfo: Set<SpaceRepoInfo>,
|
||||
val reviewRef: Ref<R>,
|
||||
val workspace: Workspace
|
||||
) : Lifetimed {
|
||||
@@ -56,7 +62,7 @@ internal sealed class SpaceReviewDetailsVm<R : CodeReviewRecord>(
|
||||
|
||||
val reviewStateUpdater: SpaceReviewStateUpdater = SpaceReviewStateUpdaterImpl(workspace, review.value)
|
||||
|
||||
private val infoByRepos = spaceReposInfo.associateBy(SpaceRepoInfo::name)
|
||||
val infoByRepos = spaceReposInfo.associateBy(SpaceRepoInfo::name)
|
||||
|
||||
private val participantsProperty: Property<LoadingValue<Ref<CodeReviewParticipants>>> = load {
|
||||
client.arena.resolveRefsOrFetch {
|
||||
@@ -129,6 +135,7 @@ private fun <T> selectedOrAll(tab: SelectedTab, selected: T, all: T): T = when (
|
||||
}
|
||||
|
||||
internal class MergeRequestDetailsVm(
|
||||
parentDisposable: Disposable,
|
||||
lifetime: Lifetime,
|
||||
ideaProject: Project,
|
||||
spaceProjectInfo: SpaceProjectInfo,
|
||||
@@ -142,6 +149,67 @@ internal class MergeRequestDetailsVm(
|
||||
val repository: Property<String> = cellProperty { branchPair.live.repository }
|
||||
val targetBranchInfo: Property<MergeRequestBranch?> = cellProperty { branchPair.live.targetBranchInfo }
|
||||
val sourceBranchInfo: Property<MergeRequestBranch?> = cellProperty { branchPair.live.sourceBranchInfo }
|
||||
|
||||
val repoInfo: Property<SpaceRepoInfo?> = map(repository) { repoName ->
|
||||
getRepoInfo(repoName)
|
||||
}
|
||||
|
||||
val mergeRequestBranchInfo: MutableProperty<MergeRequestBranchInfo> = mutableProperty(MergeRequestBranchInfo(null, false))
|
||||
|
||||
private fun getRepoInfo(repoName: String): SpaceRepoInfo? {
|
||||
return spaceReposInfo.firstOrNull { it.name == repoName }
|
||||
}
|
||||
|
||||
init {
|
||||
review.forEach(lifetime) {
|
||||
updateLocalBranch()
|
||||
}
|
||||
|
||||
val dataPackListener = DataPackChangeListener {
|
||||
updateLocalBranch()
|
||||
}
|
||||
|
||||
VcsProjectLog.runWhenLogIsReady(ideaProject) {
|
||||
val log = VcsProjectLog.getInstance(ideaProject)
|
||||
val dataManager = log.dataManager ?: return@runWhenLogIsReady
|
||||
ApplicationManager.getApplication().invokeLater(
|
||||
{
|
||||
dataManager.addDataPackChangeListener(dataPackListener)
|
||||
Disposer.register(parentDisposable, Disposable { dataManager.removeDataPackChangeListener(dataPackListener) })
|
||||
},
|
||||
{
|
||||
Disposer.isDisposed(parentDisposable)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateLocalBranch() {
|
||||
val localBranch = kotlin.run {
|
||||
val repoInfo = repoInfo.value ?: return@run null
|
||||
val branch = sourceBranchInfo.value ?: return@run null
|
||||
findLocalBranch(repoInfo, branch)
|
||||
}
|
||||
|
||||
val isCurrentBranch = kotlin.run {
|
||||
val gitLocalBranch = localBranch ?: return@run false
|
||||
val spaceRepoInfo = repoInfo.value ?: return@run false
|
||||
return@run spaceRepoInfo.repository.currentBranch == gitLocalBranch
|
||||
}
|
||||
|
||||
mergeRequestBranchInfo.value = MergeRequestBranchInfo(localBranch, isCurrentBranch)
|
||||
}
|
||||
|
||||
private fun findLocalBranch(repoInfo: SpaceRepoInfo, branch: MergeRequestBranch): GitLocalBranch? {
|
||||
val gitRepository = repoInfo.repository
|
||||
val allBranches = gitRepository.branches
|
||||
|
||||
val remoteBranch = allBranches.remoteBranches
|
||||
.find { remoteBranch -> remoteBranch.nameForRemoteOperations == branch.displayName }
|
||||
|
||||
return allBranches.localBranches
|
||||
.firstOrNull { it.findTrackedBranch(gitRepository) == remoteBranch }
|
||||
}
|
||||
}
|
||||
|
||||
internal class CommitSetReviewDetailsVm(
|
||||
@@ -153,7 +221,8 @@ internal class CommitSetReviewDetailsVm(
|
||||
workspace: Workspace
|
||||
) : SpaceReviewDetailsVm<CommitSetReviewRecord>(lifetime, ideaProject, spaceProjectInfo, spaceReposInfo, refMrRecord, workspace)
|
||||
|
||||
internal fun createReviewDetailsVm(lifetime: Lifetime,
|
||||
internal fun createReviewDetailsVm(disposable: Disposable,
|
||||
lifetime: Lifetime,
|
||||
project: Project,
|
||||
workspace: Workspace,
|
||||
spaceProjectInfo: SpaceProjectInfo,
|
||||
@@ -162,6 +231,7 @@ internal fun createReviewDetailsVm(lifetime: Lifetime,
|
||||
val client = workspace.client
|
||||
return when (val codeReviewRecord = codeReviewListItem.review.resolve()) {
|
||||
is MergeRequestRecord -> MergeRequestDetailsVm(
|
||||
disposable,
|
||||
lifetime,
|
||||
project,
|
||||
spaceProjectInfo,
|
||||
@@ -192,4 +262,17 @@ private fun SpaceReviewDetailsVm<*>.pendingCounterAsync(client: KCircletClient):
|
||||
reviewRef.extensionRef(CodeReviewPendingMessageCounter::class)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal class MergeRequestBranchInfo(
|
||||
val localBranch: GitLocalBranch?,
|
||||
val isCurrentBranch: Boolean
|
||||
)
|
||||
|
||||
internal fun <R : CodeReviewRecord> SpaceReviewDetailsVm<R>.isMergeRequest(): Boolean {
|
||||
return this is MergeRequestDetailsVm
|
||||
}
|
||||
|
||||
internal fun <R : CodeReviewRecord> SpaceReviewDetailsVm<R>.isCommitSetReview(): Boolean {
|
||||
return this is CommitSetReviewDetailsVm
|
||||
}
|
||||
+30
-1
@@ -3,6 +3,7 @@ package com.intellij.space.vcs.review.details
|
||||
|
||||
import circlet.client.api.englishFullName
|
||||
import com.intellij.ide.plugins.newui.VerticalLayout
|
||||
import com.intellij.openapi.actionSystem.ActionManager
|
||||
import com.intellij.openapi.roots.ui.componentsList.components.ScrollablePanel
|
||||
import com.intellij.openapi.util.NlsContexts
|
||||
import com.intellij.openapi.util.NlsSafe
|
||||
@@ -19,6 +20,7 @@ import com.intellij.ui.ScrollPaneFactory
|
||||
import com.intellij.ui.SimpleTextAttributes
|
||||
import com.intellij.ui.components.JBLabel
|
||||
import com.intellij.ui.components.JBOptionButton
|
||||
import com.intellij.ui.components.labels.ActionLink
|
||||
import com.intellij.ui.components.labels.LinkLabel
|
||||
import com.intellij.ui.components.panels.NonOpaquePanel
|
||||
import com.intellij.util.FontUtil
|
||||
@@ -29,6 +31,7 @@ import com.intellij.util.ui.components.BorderLayoutPanel
|
||||
import net.miginfocom.layout.CC
|
||||
import net.miginfocom.layout.LC
|
||||
import net.miginfocom.swing.MigLayout
|
||||
import runtime.reactive.view
|
||||
import java.awt.FlowLayout
|
||||
import java.beans.PropertyChangeListener
|
||||
import javax.swing.*
|
||||
@@ -121,7 +124,7 @@ internal object SpaceReviewInfoPanelFactory {
|
||||
add(projectDetails)
|
||||
|
||||
if (detailsVm is MergeRequestDetailsVm) {
|
||||
add(createDirectionPanel(detailsVm))
|
||||
add(createBranchesPanel(this, detailsVm))
|
||||
}
|
||||
|
||||
add(titleComponent)
|
||||
@@ -143,6 +146,32 @@ internal object SpaceReviewInfoPanelFactory {
|
||||
}
|
||||
}
|
||||
|
||||
private fun createBranchesPanel(parent: JPanel, detailsVm: MergeRequestDetailsVm): BorderLayoutPanel {
|
||||
val actionManager = ActionManager.getInstance()
|
||||
|
||||
val checkoutAction = actionManager.getAction("com.intellij.space.vcs.review.details.SpaceReviewCheckoutBranchAction")
|
||||
val checkoutActionLink = ActionLink(SpaceBundle.message("review.actions.link.label.checkout"), checkoutAction)
|
||||
|
||||
val updateAction = actionManager.getAction("com.intellij.space.vcs.review.details.SpaceReviewUpdateBranchAction")
|
||||
val updateActionLink = ActionLink(SpaceBundle.message("review.actions.link.label.update"), updateAction)
|
||||
|
||||
val branchActionPanel = NonOpaquePanel().apply {
|
||||
border = JBUI.Borders.emptyLeft(4)
|
||||
}
|
||||
|
||||
val branchesPanel = BorderLayoutPanel()
|
||||
.addToCenter(createDirectionPanel(detailsVm))
|
||||
.addToRight(branchActionPanel)
|
||||
.andTransparent()
|
||||
|
||||
detailsVm.mergeRequestBranchInfo.view(detailsVm.lifetime) { _, mergeRequestBranchInfo ->
|
||||
val action = if (mergeRequestBranchInfo.isCurrentBranch) updateActionLink else checkoutActionLink
|
||||
branchActionPanel.setContent(action)
|
||||
parent.repaint()
|
||||
}
|
||||
return branchesPanel
|
||||
}
|
||||
|
||||
private fun createActionButton(detailsVm: SpaceReviewDetailsVm<*>, controlVM: ParticipantStateControlVM): JComponent? {
|
||||
val actionsBuilder = SpaceReviewActionsBuilder(detailsVm.reviewStateUpdater)
|
||||
return when (controlVM) {
|
||||
|
||||
@@ -131,9 +131,14 @@
|
||||
<separator/>
|
||||
<action id="com.intellij.space.vcs.review.list.SpaceReviewAuthorActionGroup"
|
||||
class="com.intellij.space.vcs.review.list.SpaceReviewAuthorActionGroup"/>
|
||||
|
||||
</group>
|
||||
|
||||
<action id="com.intellij.space.vcs.review.details.SpaceReviewCheckoutBranchAction"
|
||||
class="com.intellij.space.vcs.review.details.SpaceReviewCheckoutBranchAction"/>
|
||||
|
||||
<action id="com.intellij.space.vcs.review.details.SpaceReviewUpdateBranchAction"
|
||||
class="com.intellij.space.vcs.review.details.SpaceReviewUpdateBranchAction"/>
|
||||
|
||||
<group id="space.review.changes.toolbar">
|
||||
<reference id="Diff.ShowDiff"/>
|
||||
<reference id="EditSource"/>
|
||||
@@ -143,6 +148,7 @@
|
||||
<reference id="Diff.ShowDiff"/>
|
||||
<reference id="EditSource"/>
|
||||
</group>
|
||||
|
||||
</actions>
|
||||
|
||||
<resource-bundle>messages.SpaceBundle</resource-bundle>
|
||||
|
||||
@@ -205,6 +205,14 @@ review.participant.tooltip.wait.response=Waits for a response
|
||||
review.actions.wait.for.response=Wait for Response
|
||||
review.actions.resume.review=Resume review
|
||||
review.actions.accept.changes=Accept Changes
|
||||
|
||||
review.actions.link.label.checkout=Checkout
|
||||
review.actions.link.label.update=Update
|
||||
review.checkout.action.checkout.dialog.title=Checkout {0}
|
||||
review.actions.update.branch=Update Branch
|
||||
review.actions.checkout.branch=Checkout Branch...
|
||||
review.checkout.action.progress.title.checking.out.branch=Checking out branch from {0}
|
||||
review.checkout.action.progress.title.creating.branch=Creating branch from {0}
|
||||
|
||||
space.task.run.configuration.name=Space Task
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user