Commit Diff


commit - f4f674a37c137f1977cd8043a8a05de6752f9277
commit + 72c6ec218af4da0b1abb8c7740c3e1fd3e9e1eef
blob - 1b85568799abb3fcbf7961aefcb27025bd1c4fc0
blob + 6a0bb32c7e8d756c4961baa329c7d45ae1176ee9
--- src/main/kotlin/dev/nezzontli/gotvcs/GotVcs.kt
+++ src/main/kotlin/dev/nezzontli/gotvcs/GotVcs.kt
@@ -29,7 +29,7 @@ class GotVcs(project: Project) : AbstractVcs(project, 
     private val rollbackEnvironment = GotRollbackEnvironment(project, commandLine)
     private val historyProvider = GotVcsHistoryProvider(project, commandLine)
     private val updateEnvironment = GotUpdateEnvironment(commandLine)
-    private val commitAndSendExecutor = GotCommitAndSendExecutor(project, checkinEnvironment)
+    private val commitAndSendExecutor = GotCommitAndSendExecutor()
 
     override fun getDisplayName(): String = NAME
 
blob - 00dd2062abec9b198fe10c55daef4490c8bc8ce1
blob + 5c092520c9796da978ad16911533733ced67cccc
--- src/main/kotlin/dev/nezzontli/gotvcs/checkin/GotCommitAndSendExecutor.kt
+++ src/main/kotlin/dev/nezzontli/gotvcs/checkin/GotCommitAndSendExecutor.kt
@@ -1,114 +1,33 @@
 package dev.nezzontli.gotvcs.checkin
 
-import com.intellij.notification.NotificationGroupManager
-import com.intellij.notification.NotificationType
-import com.intellij.openapi.actionSystem.ActionManager
-import com.intellij.openapi.actionSystem.ActionUiKind
-import com.intellij.openapi.actionSystem.AnActionEvent
-import com.intellij.openapi.actionSystem.Presentation
-import com.intellij.openapi.actionSystem.ex.ActionUtil
-import com.intellij.openapi.actionSystem.impl.SimpleDataContext
-import com.intellij.openapi.application.ApplicationManager
-import com.intellij.openapi.project.Project
-import com.intellij.openapi.vcs.FilePath
-import com.intellij.openapi.vcs.ProjectLevelVcsManager
-import com.intellij.openapi.vcs.changes.Change
-import com.intellij.openapi.vcs.changes.ChangeListManager
-import com.intellij.openapi.vcs.changes.ChangeListManagerImpl
 import com.intellij.openapi.vcs.changes.CommitContext
 import com.intellij.openapi.vcs.changes.CommitExecutor
 import com.intellij.openapi.vcs.changes.CommitSession
-import com.intellij.vcsUtil.VcsUtil
-import dev.nezzontli.gotvcs.repo.GotRepositoryManager
 
 const val COMMIT_AND_SEND_EXECUTOR_ID = "Got.Commit.And.Send"
 
 /**
- * Commits exactly like the default "Commit" button (delegates to the same
- * CheckinEnvironment), then opens the native Push dialog (same action as
- * Ctrl+Shift+K) so the user can review and send from there.
+ * Mirrors Git's own "Commit and Push": rather than reimplementing the
+ * commit, this just flags the CommitContext and returns CommitSession
+ * .VCS_COMMIT, the sentinel meaning "run the normal commit" -- so the
+ * platform's own automatic handling of unversioned/deleted files (which a
+ * fully custom CommitSession does not get; verified by decompiling
+ * ChangesViewCommitWorkflowHandler and CommitSessionInfo.isVcsCommit())
+ * still applies. GotCheckinEnvironment checks PUSH_AFTER_COMMIT_KEY once
+ * the commit succeeds and opens the native Push dialog from there.
  */
-class GotCommitAndSendExecutor(
-    private val project: Project,
-    private val checkinEnvironment: GotCheckinEnvironment,
-) : CommitExecutor {
+class GotCommitAndSendExecutor : CommitExecutor {
 
     override fun getActionText(): String = "Commit and Send"
 
     override fun getId(): String = COMMIT_AND_SEND_EXECUTOR_ID
 
-    override fun createCommitSession(context: CommitContext): CommitSession = session
+    override fun useDefaultAction(): Boolean = false
 
-    private val session = object : CommitSession {
-        // execute() runs on a background thread (same as the default commit
-        // action), but opening the Push dialog is a UI action and must
-        // happen on the EDT -- see com.intellij.execution.process
-        // .OSProcessHandler#checkEdtAndReadAction-style threading rules.
-        override fun execute(changes: MutableCollection<out Change>, commitMessage: String?) {
-            val message = commitMessage.orEmpty()
+    override fun requiresSyncCommitChecks(): Boolean = true
 
-            // Unlike the default "Commit" button, a custom CommitExecutor's
-            // session does NOT get scheduleUnversionedFilesForAddition() /
-            // scheduleMissingFileForDeletion() called automatically by the
-            // platform first (verified by decompiling
-            // ChangesViewCommitWorkflowHandler.addUnversionedFiles(), which
-            // is a no-op unless CommitSessionInfo.isVcsCommit() is true) --
-            // and `got commit -m msg <path>` errors with "no changes to
-            // commit" for a path that hasn't been `got add`-ed yet (verified
-            // directly against the CLI). So new/deleted files must be
-            // staged here ourselves before the actual commit.
-            val changeListManager = ChangeListManager.getInstance(project) as ChangeListManagerImpl
-            val unversionedFiles = changeListManager.unversionedFilesPaths.mapNotNull { it.virtualFile }
-            val deletedPaths = changeListManager.deletedFiles.map { it.path }
-
-            if (unversionedFiles.isNotEmpty()) {
-                checkinEnvironment.scheduleUnversionedFilesForAddition(unversionedFiles.toMutableList())
-            }
-            if (deletedPaths.isNotEmpty()) {
-                checkinEnvironment.scheduleMissingFileForDeletion(deletedPaths.toMutableList())
-            }
-
-            val trackedPaths = changes.mapNotNull { it.afterRevision?.file ?: it.beforeRevision?.file }
-            val unversionedFilePaths = unversionedFiles.map { VcsUtil.getFilePath(it) }
-            val allPaths: List<FilePath> = (trackedPaths + unversionedFilePaths + deletedPaths).distinct()
-
-            val errors = checkinEnvironment.commitFilePaths(allPaths, message)
-            if (errors.isNotEmpty()) {
-                val group = NotificationGroupManager.getInstance().getNotificationGroup("got")
-                group.createNotification(
-                    "got commit failed",
-                    errors.joinToString("\n") { it.message.orEmpty() },
-                    NotificationType.ERROR,
-                ).notify(project)
-                return
-            }
-
-            // GotRepository's cached branch/revision (used to resolve the
-            // Push dialog's target) is normally refreshed by the platform's
-            // own post-commit VCS-root-dirty machinery, but that happens
-            // asynchronously and can race with the dialog opening right
-            // below -- refreshing it here ourselves, still on this
-            // background thread, guarantees the dialog sees the commit that
-            // was just made instead of possibly-stale cached state.
-            val vcsManager = ProjectLevelVcsManager.getInstance(project)
-            val repositoryManager = project.getService(GotRepositoryManager::class.java)
-            val affectedRoots = allPaths.mapNotNull { vcsManager.getVcsRootFor(it) }.toSet()
-            for (root in affectedRoots) {
-                repositoryManager.getRepositoryForRoot(root)?.update()
-            }
-
-            ApplicationManager.getApplication().invokeLater {
-                val pushAction = ActionManager.getInstance().getAction("Vcs.Push") ?: return@invokeLater
-                val dataContext = SimpleDataContext.getProjectContext(project)
-                val event = AnActionEvent.createEvent(
-                    dataContext,
-                    Presentation(),
-                    "GotCommitAndSend",
-                    ActionUiKind.NONE,
-                    null,
-                )
-                ActionUtil.performAction(pushAction, event)
-            }
-        }
+    override fun createCommitSession(context: CommitContext): CommitSession {
+        context.putUserData(PUSH_AFTER_COMMIT_KEY, true)
+        return CommitSession.VCS_COMMIT
     }
 }