import com.intellij.openapi.ui.DialogWrapper
import com.intellij.openapi.ui.DialogWrapper.IdeModalityType.IDE
import com.intellij.openapi.ui.DialogWrapper.IdeModalityType.PROJECT
-import com.intellij.openapi.ui.Messages
import com.intellij.openapi.ui.Messages.*
import com.intellij.openapi.util.registry.Registry
import com.intellij.ui.ColoredTableCellRenderer
import git4idea.repo.GitRemote.ORIGIN
import git4idea.repo.GitRepository
import org.jetbrains.annotations.Nls
+import java.awt.Component
import java.awt.Font
import java.util.*
import javax.swing.*
import javax.swing.table.AbstractTableModel
import kotlin.math.min
+private val LOG = logger<GitConfigureRemotesDialog>()
+
class GitConfigureRemotesDialog(val project: Project, val repositories: Collection<GitRepository>) :
DialogWrapper(project, true, getModalityType()) {
private val git = service<Git>()
- private val LOG = logger<GitConfigureRemotesDialog>()
private val NAME_COLUMN = 0
private val URL_COLUMN = 1
runInModalTask(message("remotes.dialog.adding.remote"),
message("remote.dialog.add.remote"),
message("remotes.dialog.cannot.add.remote.error.message", dialog.remoteName, dialog.remoteUrl),
- repository) {
+ repository, rebuildTreeOnSuccess) {
git.addRemote(repository, dialog.remoteName, dialog.remoteUrl)
}
}
private fun removeRemote() {
val remoteNode = getSelectedRemote()!!
val remote = remoteNode.remote
- if (YES == showYesNoDialog(rootPane,
- message("remotes.dialog.remove.remote.message", remote.name, getUrl(remote)),
- message("remotes.dialog.remove.remote.title"), getQuestionIcon())) {
- runInModalTask(message("remotes.dialog.removing.remote.progress"),
- message("remotes.dialog.removing.remote.error.title"),
- message("remotes.dialog.removing.remote.error.message", remote),
- remoteNode.repository) {
- git.removeRemote(remoteNode.repository, remote)
- }
- }
+ val repository = remoteNode.repository
+
+ removeRemote(git, repository, remote, rootPane, rebuildTreeOnSuccess)
}
private fun editRemote() {
val remoteNode = getSelectedRemote()!!
val remote = remoteNode.remote
val repository = remoteNode.repository
- val oldName = remote.name
- val oldUrl = getUrl(remote)
- val dialog = GitDefineRemoteDialog(repository, git, oldName, oldUrl)
- if (dialog.showAndGet()) {
- val newRemoteName = dialog.remoteName
- val newRemoteUrl = dialog.remoteUrl
- if (newRemoteName == oldName && newRemoteUrl == oldUrl) return
- runInModalTask(message("remotes.changing.remote.progress"),
- message("remotes.changing.remote.error.title"),
- message("remotes.changing.remote.error.message", oldName, newRemoteName, newRemoteUrl),
- repository) {
- changeRemote(repository, oldName, oldUrl, newRemoteName, newRemoteUrl)
- }
- }
- }
-
- private fun changeRemote(repo: GitRepository, oldName: String, oldUrl: String, newName: String, newUrl: String): GitCommandResult {
- var result : GitCommandResult? = null
- if (newName != oldName) {
- result = git.renameRemote(repo, oldName, newName)
- if (!result.success()) return result
- }
- if (newUrl != oldUrl) {
- result = git.setRemoteUrl(repo, newName, newUrl) // NB: remote name has just been changed
- }
- return result!! // at least one of two has changed
+ editRemote(git, repository, remote, rebuildTreeOnSuccess)
}
private fun updateTableWidth() {
(table.model as RemotesTableModel).fireTableDataChanged()
}
- private fun runInModalTask(@Nls(capitalization = Nls.Capitalization.Title) title: String,
- @Nls(capitalization = Nls.Capitalization.Title) errorTitle: String,
- @Nls(capitalization = Nls.Capitalization.Sentence) errorMessage: String,
- repository: GitRepository,
- operation: () -> GitCommandResult) {
- ProgressManager.getInstance().run(object : Task.Modal(project, title, true) {
- private var result: GitCommandResult? = null
-
- override fun run(indicator: ProgressIndicator) {
- result = operation()
- repository.update()
- }
-
- override fun onSuccess() {
- rebuildTable()
- if (result == null || !result!!.success()) {
- val errorDetails = if (result == null) message("remotes.operation.not.executed.message") else result!!.errorOutputAsJoinedString
- val message = message("remotes.operation.error.message", errorMessage, repository, errorDetails)
- LOG.warn(message)
- Messages.showErrorDialog(myProject, message, errorTitle)
- }
- }
- })
- }
+ private val rebuildTreeOnSuccess: () -> Unit = { rebuildTable() }
private fun getSelectedRepo(): GitRepository {
val selectedRow = table.selectedRow
private fun isRemoteSelected() = getSelectedRemote() != null
- private fun getUrl(remote: GitRemote) = remote.urls.firstOrNull() ?: ""
-
private abstract class Node {
abstract fun getPresentableString() : String
}
}
}
+fun removeRemote(git: Git, repository: GitRepository, remote: GitRemote, parent: Component? = null, onSuccess: () -> Unit = {}) {
+ if (YES == showYesNoDialog(if (parent == null) parent else repository.project,
+ message("remotes.dialog.remove.remote.message", remote.name, getUrl(remote)),
+ message("remotes.dialog.remove.remote.title"), getQuestionIcon())) {
+ runInModalTask(message("remotes.dialog.removing.remote.progress"),
+ message("remotes.dialog.removing.remote.error.title"),
+ message("remotes.dialog.removing.remote.error.message", remote),
+ repository, onSuccess) {
+ git.removeRemote(repository, remote)
+ }
+ }
+}
+
+fun editRemote(git: Git, repository: GitRepository, remote: GitRemote, onSuccess: () -> Unit = {}) {
+ val oldName = remote.name
+ val oldUrl = getUrl(remote)
+ val dialog = GitDefineRemoteDialog(repository, git, oldName, oldUrl)
+ if (dialog.showAndGet()) {
+ val newRemoteName = dialog.remoteName
+ val newRemoteUrl = dialog.remoteUrl
+ if (newRemoteName == oldName && newRemoteUrl == oldUrl) return
+ runInModalTask(message("remotes.changing.remote.progress"),
+ message("remotes.changing.remote.error.title"),
+ message("remotes.changing.remote.error.message", oldName, newRemoteName, newRemoteUrl),
+ repository, onSuccess) {
+ changeRemote(git, repository, oldName, oldUrl, newRemoteName, newRemoteUrl)
+ }
+ }
+}
+
+private fun getUrl(remote: GitRemote) = remote.urls.firstOrNull() ?: ""
+
+private fun changeRemote(git: Git, repo: GitRepository, oldName: String, oldUrl: String, newName: String, newUrl: String): GitCommandResult {
+ var result : GitCommandResult? = null
+ if (newName != oldName) {
+ result = git.renameRemote(repo, oldName, newName)
+ if (!result.success()) return result
+ }
+ if (newUrl != oldUrl) {
+ result = git.setRemoteUrl(repo, newName, newUrl) // NB: remote name has just been changed
+ }
+ return result!! // at least one of two has changed
+}
+
+private fun runInModalTask(@Nls(capitalization = Nls.Capitalization.Title) title: String,
+ @Nls(capitalization = Nls.Capitalization.Title) errorTitle: String,
+ @Nls(capitalization = Nls.Capitalization.Sentence) errorMessage: String,
+ repository: GitRepository,
+ onSuccess: () -> Unit,
+ operation: () -> GitCommandResult) {
+ ProgressManager.getInstance().run(object : Task.Modal(repository.project, title, true) {
+ private var result: GitCommandResult? = null
+
+ override fun run(indicator: ProgressIndicator) {
+ result = operation()
+ repository.update()
+ }
+
+ override fun onSuccess() {
+ onSuccess()
+ if (result == null || !result!!.success()) {
+ val errorDetails = if (result == null) message("remotes.operation.not.executed.message") else result!!.errorOutputAsJoinedString
+ val message = message("remotes.operation.error.message", errorMessage, repository, errorDetails)
+ LOG.warn(message)
+ showErrorDialog(myProject, message, errorTitle)
+ }
+ }
+ })
+}
+
private fun getModalityType() = if (Registry.`is`("ide.perProjectModality")) PROJECT else IDE
import git4idea.i18n.GitBundle.message
import git4idea.i18n.GitBundleExtensions.messagePointer
import git4idea.isRemoteBranchProtected
+import git4idea.remote.editRemote
+import git4idea.remote.removeRemote
+import git4idea.repo.GitRemote
import git4idea.repo.GitRepository
import git4idea.repo.GitRepositoryManager
import git4idea.ui.branch.*
import org.jetbrains.annotations.Nls
+import org.jetbrains.annotations.NonNls
import javax.swing.Icon
internal object BranchesDashboardActions {
class BranchesTreeActionGroup(private val project: Project, private val tree: FilteringBranchesTree) : ActionGroup(), DumbAware {
- override fun update(e: AnActionEvent) {
- val enabledAndVisible = tree.getSelectedBranches().isNotEmpty()
- e.presentation.isEnabledAndVisible = enabledAndVisible
- isPopup = enabledAndVisible
+
+ init {
+ isPopup = true
}
override fun hideIfNoVisibleChildren() = true
}
class MultipleLocalBranchActions : ActionGroup(), DumbAware {
- override fun getChildren(e: AnActionEvent?): Array<AnAction> = arrayOf(ShowArbitraryBranchesDiffAction(), UpdateSelectedBranchAction(), DeleteBranchAction())
+ override fun getChildren(e: AnActionEvent?): Array<AnAction> =
+ arrayOf(ShowArbitraryBranchesDiffAction(), UpdateSelectedBranchAction(), DeleteBranchAction())
}
class CurrentBranchActions(project: Project,
arrayListOf<AnAction>(*super.getChildren(e)).toTypedArray()
}
+ class RemoteBranchActions(project: Project,
+ repositories: List<GitRepository>,
+ @NonNls branchName: String,
+ private val currentRepository: GitRepository)
+ : GitBranchPopupActions.RemoteBranchActions(project, repositories, branchName, currentRepository) {
+
+ override fun getChildren(e: AnActionEvent?): Array<AnAction> =
+ arrayListOf<AnAction>(*super.getChildren(e), Separator(), EditRemoteAction(currentRepository), RemoveRemoteAction(currentRepository))
+ .toTypedArray()
+ }
+
+ class GroupActions(private val currentRepository: GitRepository) : ActionGroup(), DumbAware {
+
+ override fun getChildren(e: AnActionEvent?): Array<AnAction> =
+ arrayListOf<AnAction>(EditRemoteAction(currentRepository), RemoveRemoteAction(currentRepository)).toTypedArray()
+ }
+
class BranchActionsBuilder(private val project: Project, private val tree: FilteringBranchesTree) {
fun build(): ActionGroup? {
val selectedBranches = tree.getSelectedBranches()
if (multipleBranchSelection) {
return MultipleLocalBranchActions()
}
- else {
- val branchInfo = selectedBranches.singleOrNull() ?: return null
+
+ val branchInfo = selectedBranches.singleOrNull()
+ if (branchInfo != null) {
return when {
branchInfo.isCurrent -> CurrentBranchActions(project, branchInfo.repositories, branchInfo.branchName, guessRepo)
branchInfo.isLocal -> LocalBranchActions(project, branchInfo.repositories, branchInfo.branchName, guessRepo)
- else -> GitBranchPopupActions.RemoteBranchActions(project, branchInfo.repositories, branchInfo.branchName, guessRepo)
+ else -> RemoteBranchActions(project, branchInfo.repositories, branchInfo.branchName, guessRepo)
}
}
+
+ val selectedRemotes = tree.getSelectedRemotes()
+ if (selectedRemotes.size == 1) {
+ return GroupActions(guessRepo)
+ }
+
+ return null
}
}
}
}
+ class RemoveRemoteAction(private val repository: GitRepository) : RemoteActionBase(repository, messagePointer("action.Git.Log.Remove.Remote.text")) {
+
+ override fun doAction(e: AnActionEvent, project: Project, remotes: Set<GitRemote>) {
+ removeRemote(service(), repository, remotes.first())
+ }
+ }
+
+ class EditRemoteAction(private val repository: GitRepository) :
+ RemoteActionBase(repository, messagePointer("action.Git.Log.Edit.Remote.text")) {
+
+ override fun update(e: AnActionEvent, project: Project, remoteNames: Set<String>) {
+ if (remoteNames.size != 1) {
+ e.presentation.isEnabledAndVisible = false
+ }
+ }
+
+ override fun doAction(e: AnActionEvent, project: Project, remotes: Set<GitRemote>) {
+ editRemote(service(), repository, remotes.first())
+ }
+ }
+
+ abstract class RemoteActionBase(private val repository: GitRepository,
+ @Nls(capitalization = Nls.Capitalization.Title) text: () -> String = { "" },
+ @Nls(capitalization = Nls.Capitalization.Sentence) private val description: () -> String = { "" },
+ icon: Icon? = null) :
+ DumbAwareAction(text, description, icon) {
+
+ open fun update(e: AnActionEvent, project: Project, remoteNames: Set<String>) {}
+ abstract fun doAction(e: AnActionEvent, project: Project, remotes: Set<GitRemote>)
+
+ override fun update(e: AnActionEvent) {
+ val project = e.project
+ val remoteNames = getSelectedRemoteNames(e)
+ val enabled = project != null && remoteNames.isNotEmpty() && repository.remotes.any { remoteNames.contains(it.name) }
+ e.presentation.isEnabled = enabled
+ e.presentation.description = description()
+ if (enabled) {
+ update(e, project!!, remoteNames)
+ }
+ }
+
+ override fun actionPerformed(e: AnActionEvent) {
+ val project = e.project ?: return
+ val remoteNames = getSelectedRemoteNames(e)
+ val remotes = repository.remotes.filterTo(hashSetOf()) { remoteNames.contains(it.name) }
+
+ doAction(e, project, remotes)
+ }
+
+ private fun getSelectedRemoteNames(e: AnActionEvent): Set<String> {
+ val remoteNamesFromBranches =
+ e.getData(GIT_BRANCHES)
+ ?.asSequence()
+ ?.filterNot(BranchInfo::isLocal)
+ ?.mapNotNull { it.branchName.split("/").getOrNull(0) }?.toSet()
+ val selectedRemoteNames = e.getData(GIT_REMOTES)
+ return hashSetOf<String>().apply {
+ if (selectedRemoteNames != null) addAll(selectedRemoteNames)
+ if (remoteNamesFromBranches != null) addAll(remoteNamesFromBranches)
+ }
+ }
+ }
+
abstract class BranchesActionBase(@Nls(capitalization = Nls.Capitalization.Title) text: () -> String = { "" },
@Nls(capitalization = Nls.Capitalization.Sentence) private val description: () -> String = { "" },
icon: Icon? = null) :