Commit Diff


commit - 7479a8a7b30eb85db1b28e9dd920a4f13791a563
commit + 7ec62b12361bdec10782a2cd24c4125cf0585d98
blob - 71f3a0954bab5118fb9fe7ed584cbadb0c13b056
blob + 4fb1f50fbdb4bf46e11a89b1330a03e41e92e66b
--- README.md
+++ README.md
@@ -17,7 +17,8 @@ and update/push workflow you get with Git, backed by t
 - Native diff and modified-lines gutter (`got cat` against the base commit)
 - Commit and rollback from the Commit tool window (`got commit`, `got revert`)
 - File history (`got log`) and Update Project (`got update` / `got fetch`)
-- A **Send** action (`got send`), bound to `Ctrl+Shift+K` by default
+- Current branch name shown in the main toolbar, and the native Push dialog
+  (`Ctrl+Shift+K`) for `got send`
 - A Settings panel (*Settings > Version Control > got*) to override the
   `got` binary path and `SSH_AUTH_SOCK`, for setups where auto-detection
   doesn't apply
@@ -37,15 +38,17 @@ corresponding IntelliJ Platform VCS API.
 | Rollback           | `got revert -R <paths>`           |
 | History            | `got log`                         |
 | Update Project     | `got fetch` + `got update`        |
-| Send               | `got send`                        |
+| Branch widget       | `got info` ("work tree branch reference") |
+| Push               | `got send`                        |
 
 Source is organized by responsibility under `dev.nezzontli.gotvcs`:
 
 - `cli` — the single wrapper around `got` invocations (`GotCommandLineWrapper`)
 - `changes`, `checkin`, `history`, `update`, `roots` — one package per VCS
   extension point implemented
+- `repo` — the `Repository` model and the branch-name status bar widget
+- `push` — native Push dialog support (`PushSupport` and friends)
 - `settings` — the persisted configuration and its Settings panel
-- `actions` — the `Send` menu action
 
 ## Requirements
 
blob - 013a80906f0cc310c026f4a16fa95ce921844391
blob + 59801df9877633c858b4d08ab598a6e0a8b2799d
--- build.gradle.kts
+++ build.gradle.kts
@@ -30,6 +30,8 @@ dependencies {
         // classes live here, not under com.intellij.modules.vcs.
         bundledModule("intellij.platform.vcs.dvcs")
         bundledModule("intellij.platform.vcs.dvcs.impl")
+        // Referenced by OutgoingResult's commit list type (com.intellij.dvcs.push).
+        bundledModule("intellij.platform.vcs.log")
     }
 }
 
@@ -40,9 +42,9 @@ intellijPlatform {
         }
 
         changeNotes = """
-            Read-only file status, native diff, commit/rollback, history,
-            update/fetch, a Send action, and a Settings panel for the got
-            binary path and SSH_AUTH_SOCK.
+            File status, native diff, commit/rollback, history, update/fetch,
+            a branch-name widget, the native Push dialog, and a Settings
+            panel for the got binary path and SSH_AUTH_SOCK.
         """.trimIndent()
     }
 
blob - 0f1813e13cbe9ec189834ba3e7bd4fcf19b8dbc7 (mode 644)
blob + /dev/null
--- src/main/kotlin/dev/nezzontli/gotvcs/actions/GotSendAction.kt
+++ /dev/null
@@ -1,65 +0,0 @@
-package dev.nezzontli.gotvcs.actions
-
-import com.intellij.notification.NotificationGroupManager
-import com.intellij.notification.NotificationType
-import com.intellij.openapi.actionSystem.AnAction
-import com.intellij.openapi.actionSystem.AnActionEvent
-import com.intellij.openapi.progress.ProgressIndicator
-import com.intellij.openapi.progress.ProgressManager
-import com.intellij.openapi.progress.Task
-import com.intellij.openapi.project.Project
-import com.intellij.openapi.vcs.ProjectLevelVcsManager
-import com.intellij.openapi.vcs.VcsException
-import com.intellij.openapi.vfs.VirtualFile
-import dev.nezzontli.gotvcs.GotVcs
-import dev.nezzontli.gotvcs.cli.GotCommandLineWrapper
-import java.io.File
-
-/**
- * Simple menu action for `got send` (the equivalent of "push"). This does
- * not hook into IntelliJ's native Push dialog (Ctrl+Shift+K): that requires
- * a PushSupport backed by a full Repository/RepositoryManager model,
- * comparable in size to the rest of this plugin combined. A single action
- * that runs `got send` and reports the result covers the real use case
- * without that complexity.
- */
-class GotSendAction : AnAction() {
-
-    override fun update(e: AnActionEvent) {
-        e.presentation.isEnabledAndVisible = e.project?.let { gotRoots(it).isNotEmpty() } ?: false
-    }
-
-    override fun actionPerformed(e: AnActionEvent) {
-        val project = e.project ?: return
-        val roots = gotRoots(project)
-        if (roots.isEmpty()) return
-
-        val commandLine = GotCommandLineWrapper()
-        ProgressManager.getInstance().run(object : Task.Backgroundable(project, "got send", true) {
-            override fun run(indicator: ProgressIndicator) {
-                val errors = mutableListOf<String>()
-                for (root in roots) {
-                    try {
-                        commandLine.send(File(root.path))
-                    } catch (ex: VcsException) {
-                        errors.add("${root.path}: ${ex.message}")
-                    }
-                }
-                val group = NotificationGroupManager.getInstance().getNotificationGroup("got")
-                if (errors.isEmpty()) {
-                    group.createNotification("got send: changes sent successfully", NotificationType.INFORMATION)
-                        .notify(project)
-                } else {
-                    group.createNotification("got send failed", errors.joinToString("\n"), NotificationType.ERROR)
-                        .notify(project)
-                }
-            }
-        })
-    }
-
-    private fun gotRoots(project: Project): List<VirtualFile> {
-        val vcsManager = ProjectLevelVcsManager.getInstance(project)
-        val gotVcs = vcsManager.findVcsByName(GotVcs.NAME) ?: return emptyList()
-        return vcsManager.getRootsUnderVcs(gotVcs).toList()
-    }
-}
blob - /dev/null
blob + a2df57f27c8576ff4321027caeb61a6e2c2217dc (mode 644)
--- /dev/null
+++ src/main/kotlin/dev/nezzontli/gotvcs/push/GotOutgoingCommitsProvider.kt
@@ -0,0 +1,20 @@
+package dev.nezzontli.gotvcs.push
+
+import com.intellij.dvcs.push.OutgoingCommitsProvider
+import com.intellij.dvcs.push.OutgoingResult
+import com.intellij.dvcs.push.PushSpec
+import dev.nezzontli.gotvcs.repo.GotRepository
+
+/**
+ * got has no cheap local equivalent of "commits not yet on the remote"
+ * without contacting the server first, so this reports an empty preview
+ * rather than an inaccurate one. The Push dialog still works; it just
+ * doesn't list outgoing commits ahead of time.
+ */
+class GotOutgoingCommitsProvider : OutgoingCommitsProvider<GotRepository, GotPushSource, GotPushTarget>() {
+    override fun getOutgoingCommits(
+        repository: GotRepository,
+        pushSpec: PushSpec<GotPushSource, GotPushTarget>,
+        initial: Boolean,
+    ): OutgoingResult = OutgoingResult(emptyList(), emptyList())
+}
blob - /dev/null
blob + c569400424f0a71f3b7c8132e9e28a03eedac88d (mode 644)
--- /dev/null
+++ src/main/kotlin/dev/nezzontli/gotvcs/push/GotPushSource.kt
@@ -0,0 +1,7 @@
+package dev.nezzontli.gotvcs.push
+
+import com.intellij.dvcs.push.PushSource
+
+class GotPushSource(private val branch: String) : PushSource {
+    override fun getPresentation(): String = branch
+}
blob - /dev/null
blob + ae7f6076ead4322cc0a63a24d017ac0f76b98a8e (mode 644)
--- /dev/null
+++ src/main/kotlin/dev/nezzontli/gotvcs/push/GotPushSupport.kt
@@ -0,0 +1,48 @@
+package dev.nezzontli.gotvcs.push
+
+import com.intellij.dvcs.push.PushSupport
+import com.intellij.dvcs.push.PushTargetPanel
+import com.intellij.dvcs.repo.RepositoryManager
+import com.intellij.openapi.project.Project
+import com.intellij.openapi.vcs.AbstractVcs
+import com.intellij.openapi.vcs.ProjectLevelVcsManager
+import dev.nezzontli.gotvcs.GotVcs
+import dev.nezzontli.gotvcs.cli.GotCommandLineWrapper
+import dev.nezzontli.gotvcs.repo.GotRepository
+import dev.nezzontli.gotvcs.repo.GotRepositoryManager
+
+class GotPushSupport(private val project: Project) : PushSupport<GotRepository, GotPushSource, GotPushTarget>() {
+
+    private val commandLine = GotCommandLineWrapper()
+    private val pusher = GotPusher(commandLine)
+    private val outgoingCommitsProvider = GotOutgoingCommitsProvider()
+
+    override fun getVcs(): AbstractVcs = ProjectLevelVcsManager.getInstance(project).findVcsByName(GotVcs.NAME)!!
+
+    override fun getPusher() = pusher
+
+    override fun getOutgoingCommitsProvider() = outgoingCommitsProvider
+
+    override fun getDefaultTarget(repository: GotRepository): GotPushTarget? {
+        val branch = repository.currentBranchName ?: return null
+        return GotPushTarget("origin", branch)
+    }
+
+    override fun getSource(repository: GotRepository): GotPushSource =
+        GotPushSource(repository.currentBranchName ?: "")
+
+    override fun getRepositoryManager(): RepositoryManager<GotRepository> =
+        project.getService(GotRepositoryManager::class.java)
+
+    override fun createTargetPanel(
+        repository: GotRepository,
+        source: GotPushSource,
+        target: GotPushTarget?,
+    ): PushTargetPanel<GotPushTarget> = GotPushTargetPanel(target ?: GotPushTarget("origin", source.presentation))
+
+    override fun isForcePushAllowed(repository: GotRepository, target: GotPushTarget): Boolean = true
+
+    override fun isSilentForcePushAllowed(target: GotPushTarget): Boolean = false
+
+    override fun saveSilentForcePushTarget(target: GotPushTarget) = Unit
+}
blob - /dev/null
blob + ba31c5d498654310fa732f8323f263e9daa97f3b (mode 644)
--- /dev/null
+++ src/main/kotlin/dev/nezzontli/gotvcs/push/GotPushTarget.kt
@@ -0,0 +1,13 @@
+package dev.nezzontli.gotvcs.push
+
+import com.intellij.dvcs.push.PushTarget
+
+/**
+ * got does not expose a cheap way to tell ahead of time whether a send would
+ * be a no-op, so [hasSomethingToPush] always returns true: `got send` itself
+ * reports "nothing to send" if that turns out to be the case.
+ */
+class GotPushTarget(private val remoteName: String, private val branch: String) : PushTarget {
+    override fun hasSomethingToPush(): Boolean = true
+    override fun getPresentation(): String = "$remoteName/$branch"
+}
blob - /dev/null
blob + 6938d5fb20afe3150e1254d9cf4a0d07f65c14a5 (mode 644)
--- /dev/null
+++ src/main/kotlin/dev/nezzontli/gotvcs/push/GotPushTargetPanel.kt
@@ -0,0 +1,31 @@
+package dev.nezzontli.gotvcs.push
+
+import com.intellij.dvcs.push.PushTargetPanel
+import com.intellij.dvcs.push.ui.PushTargetEditorListener
+import com.intellij.openapi.ui.ValidationInfo
+import com.intellij.ui.ColoredTreeCellRenderer
+import com.intellij.ui.SimpleTextAttributes
+
+/**
+ * A read-only target panel: got sends to whichever remote/branch got.conf
+ * and the work tree resolve by default, so there is nothing to edit here
+ * (unlike Git, which lets you retarget a push to an arbitrary branch).
+ */
+class GotPushTargetPanel(private val target: GotPushTarget) : PushTargetPanel<GotPushTarget>() {
+
+    override fun render(renderer: ColoredTreeCellRenderer, isSelected: Boolean, isActive: Boolean, text: String?) {
+        renderer.append(target.presentation, SimpleTextAttributes.REGULAR_ATTRIBUTES)
+    }
+
+    override fun getValue(): GotPushTarget = target
+
+    override fun fireOnCancel() = Unit
+
+    override fun fireOnChange() = Unit
+
+    override fun verify(): ValidationInfo? = null
+
+    override fun setFireOnChangeAction(action: Runnable) = Unit
+
+    override fun addTargetEditorListener(listener: PushTargetEditorListener) = Unit
+}
blob - /dev/null
blob + b974d16779def762c4a4d1815d1e6b8aae07b979 (mode 644)
--- /dev/null
+++ src/main/kotlin/dev/nezzontli/gotvcs/push/GotPusher.kt
@@ -0,0 +1,38 @@
+package dev.nezzontli.gotvcs.push
+
+import com.intellij.dvcs.push.PushSpec
+import com.intellij.dvcs.push.Pusher
+import com.intellij.dvcs.push.VcsPushOptionValue
+import com.intellij.notification.NotificationGroupManager
+import com.intellij.notification.NotificationType
+import com.intellij.openapi.vcs.VcsException
+import dev.nezzontli.gotvcs.cli.GotCommandLineWrapper
+import dev.nezzontli.gotvcs.repo.GotRepository
+import java.io.File
+
+class GotPusher(private val commandLine: GotCommandLineWrapper) :
+    Pusher<GotRepository, GotPushSource, GotPushTarget>() {
+
+    override fun push(
+        pushSpecs: MutableMap<GotRepository, PushSpec<GotPushSource, GotPushTarget>>,
+        forcePushOptionValue: VcsPushOptionValue?,
+        force: Boolean,
+    ) {
+        val errors = mutableListOf<String>()
+        for (repository in pushSpecs.keys) {
+            try {
+                commandLine.send(File(repository.root.path))
+            } catch (e: VcsException) {
+                errors.add("${repository.root.path}: ${e.message}")
+            }
+        }
+
+        val project = pushSpecs.keys.firstOrNull()?.project ?: return
+        val group = NotificationGroupManager.getInstance().getNotificationGroup("got")
+        if (errors.isEmpty()) {
+            group.createNotification("got send: changes sent successfully", NotificationType.INFORMATION).notify(project)
+        } else {
+            group.createNotification("got send failed", errors.joinToString("\n"), NotificationType.ERROR).notify(project)
+        }
+    }
+}
blob - 22f036062dac0b0f2971b4e5d54c5738836fd31b
blob + 10b7ba43876f4f2897f39aa65ff5c2488054c3e1
--- src/main/resources/META-INF/plugin.xml
+++ src/main/resources/META-INF/plugin.xml
@@ -9,7 +9,8 @@
             <li>File status, native diff, and modified-lines gutter</li>
             <li>Commit and rollback from the Commit tool window</li>
             <li>File history and Update Project (got update / got fetch)</li>
-            <li>Send action (got send) and a Settings panel for the got binary path</li>
+            <li>Branch name widget and native Push dialog (Ctrl+Shift+K)</li>
+            <li>Settings panel for the got binary path and SSH_AUTH_SOCK</li>
         </ul>
         See the <a href="https://github.com/Richard7987/gotvcs-intellij">README</a> for details on how each feature maps to the underlying got command.
     ]]></description>
@@ -30,13 +31,6 @@
         <statusBarWidgetFactory id="got.branch.widget"
                                  implementation="dev.nezzontli.gotvcs.repo.GotBranchWidget$Factory"
                                  order="after CodeStyleStatusBarWidget"/>
+        <pushSupport implementation="dev.nezzontli.gotvcs.push.GotPushSupport"/>
     </extensions>
-
-    <actions>
-        <action id="Got.Send" class="dev.nezzontli.gotvcs.actions.GotSendAction" text="Send (got send)"
-                description="Sends local changes to the remote via got send">
-            <add-to-group group-id="Vcs.Operations.Popup" anchor="last"/>
-            <keyboard-shortcut keymap="$default" first-keystroke="ctrl shift K"/>
-        </action>
-    </actions>
 </idea-plugin>