diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/VcsNotifier.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/VcsNotifier.java index 3e76cf2b5b13..9d61640d0d14 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/VcsNotifier.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/VcsNotifier.java @@ -220,7 +220,7 @@ public class VcsNotifier { public Notification notifyMinorWarning(@NonNls @Nullable String displayId, @NotificationTitle @NotNull String title, @NotificationContent @NotNull String message) { - return notifyMinorWarning(displayId, title, message, null); + return notifyMinorWarning(displayId, title, message, (NotificationListener)null); } @NotNull @@ -229,6 +229,14 @@ public class VcsNotifier { return notify(STANDARD_NOTIFICATION, displayId, "", message, NotificationType.WARNING, (NotificationListener)null); } + @NotNull + public Notification notifyMinorWarning(@NonNls @Nullable String displayId, + @NotificationTitle @NotNull String title, + @NotificationContent @NotNull String message, + NotificationAction... actions) { + return notify(STANDARD_NOTIFICATION, displayId, title, message, NotificationType.WARNING, actions); + } + @NotNull public Notification notifyMinorWarning(@NonNls @Nullable String displayId, @NotificationTitle @NotNull String title, diff --git a/plugins/git4idea/resources/messages/GitBundle.properties b/plugins/git4idea/resources/messages/GitBundle.properties index f74eafc5e73d..86064aff726e 100644 --- a/plugins/git4idea/resources/messages/GitBundle.properties +++ b/plugins/git4idea/resources/messages/GitBundle.properties @@ -421,6 +421,9 @@ stash.files.in.roots.success=Files in {0} stashed successfully stash.view.stashes.link=View Git stashes stash.enable.stashes.link=Enable and view "{0}" tab +stash.changes.non.empty.index.for.roots.notification.text=Staging area in {0} contains modifications to other files which were saved to the stash +stash.changes.non.empty.index.notification.action=Do not show again + tag.action.name=Tag tag.button=Create Tag tag.commit.label=&Commit: diff --git a/plugins/git4idea/src/git4idea/GitNotificationIdsHolder.kt b/plugins/git4idea/src/git4idea/GitNotificationIdsHolder.kt index 1526bd5e2602..1680fafb1284 100644 --- a/plugins/git4idea/src/git4idea/GitNotificationIdsHolder.kt +++ b/plugins/git4idea/src/git4idea/GitNotificationIdsHolder.kt @@ -87,6 +87,7 @@ class GitNotificationIdsHolder : NotificationIdsHolder { STASH_SUCCESSFUL, STASH_FAILED, STASH_LOCAL_CHANGES_DETECTED, + STASH_NON_EMPTY_INDEX_DETECTED, TAG_CREATED, TAG_NOT_CREATED, TAG_DELETED, @@ -196,6 +197,7 @@ class GitNotificationIdsHolder : NotificationIdsHolder { const val STASH_SUCCESSFUL = "git.stash.successful" const val STASH_FAILED = "git.stash.failed" const val STASH_LOCAL_CHANGES_DETECTED = "git.stash.local.changes.detected" + const val STASH_NON_EMPTY_INDEX_DETECTED = "git.stash.non.empty.index.detected" const val TAG_CREATED = "git.tag.created" const val TAG_NOT_CREATED = "git.tag.not.created" const val TAG_DELETED = "git.tag.deleted" diff --git a/plugins/git4idea/src/git4idea/index/actions/GitStageStashFilesAction.kt b/plugins/git4idea/src/git4idea/index/actions/GitStageStashFilesAction.kt index 7c1907eceff7..823b440d8849 100644 --- a/plugins/git4idea/src/git4idea/index/actions/GitStageStashFilesAction.kt +++ b/plugins/git4idea/src/git4idea/index/actions/GitStageStashFilesAction.kt @@ -1,16 +1,22 @@ // Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. package git4idea.index.actions +import com.intellij.ide.util.PropertiesComponent +import com.intellij.notification.NotificationAction import com.intellij.openapi.actionSystem.AnActionEvent import com.intellij.openapi.project.Project +import com.intellij.openapi.vcs.FilePath import com.intellij.openapi.vcs.VcsException +import com.intellij.openapi.vcs.VcsNotifier import com.intellij.openapi.vfs.VirtualFile import com.intellij.util.containers.MultiMap +import git4idea.GitNotificationIdsHolder import git4idea.GitStashUsageCollector import git4idea.commands.Git import git4idea.config.GitExecutableManager import git4idea.config.GitVersionSpecialty import git4idea.i18n.GitBundle +import git4idea.index.GitStageTracker import git4idea.index.ui.GitFileStatusNode import git4idea.index.ui.NodeKind import git4idea.stash.GitStashOperations @@ -43,12 +49,46 @@ object GitStashOperation : StagingAreaOperation { refreshStash(project, root) } - override fun reportResult(project: Project, successfulRoots: Collection, exceptionsByRoot: MultiMap) { - super.reportResult(project, successfulRoots, exceptionsByRoot) + override fun reportResult(project: Project, + nodes: List, + successfulRoots: Collection, + exceptionsByRoot: MultiMap) { + super.reportResult(project, nodes, successfulRoots, exceptionsByRoot) if (successfulRoots.isNotEmpty()) { GitStashOperations.showSuccessNotification(project, successfulRoots, !exceptionsByRoot.isEmpty) + showNonEmptyStagingAreaNotification(project, nodes, successfulRoots) } } + + private const val SHOW_NOTIFICATION_PROPERTY = "git.stash.notify.non.empty.index" + + private fun showNonEmptyStagingAreaNotification(project: Project, nodes: List, successfulRoots: Collection) { + if (PropertiesComponent.getInstance(project).getBoolean(SHOW_NOTIFICATION_PROPERTY, true)) { + val tracker = GitStageTracker.getInstance(project) + val stagedRoots = tracker.state.stagedRoots + + val stagedRootsToStash = mutableSetOf() + for ((root, nodesInRoot) in nodes.filter { stagedRoots.contains(it.root) && successfulRoots.contains(it.root) }.groupBy { it.root }) { + val paths = nodesInRoot.mapTo(mutableSetOf()) { it.filePath } + if (tracker.stagedFiles(root).any { !paths.contains(it) }) { + stagedRootsToStash.add(root) + } + } + if (stagedRootsToStash.isEmpty()) return + + val message = GitBundle.message("stash.changes.non.empty.index.for.roots.notification.text", + GitStashOperations.getRootsText(project, stagedRootsToStash)) + val action = NotificationAction.createSimpleExpiring(GitBundle.message("stash.changes.non.empty.index.notification.action")) { + PropertiesComponent.getInstance(project).setValue(SHOW_NOTIFICATION_PROPERTY, false) + } + VcsNotifier.getInstance(project).notifyMinorWarning(GitNotificationIdsHolder.STASH_NON_EMPTY_INDEX_DETECTED, "", message, action) + } + } + + private fun GitStageTracker.stagedFiles(root: VirtualFile): Collection { + val rootState = state.rootStates[root] ?: return emptyList() + return rootState.statuses.filterValues { it.getStagedStatus() != null }.map { it.key } + } } class GitStageStashFilesAction : StagingAreaOperationAction(GitStashOperation) { diff --git a/plugins/git4idea/src/git4idea/index/actions/StagingAreaOperation.kt b/plugins/git4idea/src/git4idea/index/actions/StagingAreaOperation.kt index a371478ccc51..3ae6457ca460 100644 --- a/plugins/git4idea/src/git4idea/index/actions/StagingAreaOperation.kt +++ b/plugins/git4idea/src/git4idea/index/actions/StagingAreaOperation.kt @@ -36,7 +36,7 @@ interface StagingAreaOperation { @Throws(VcsException::class) fun processPaths(project: Project, root: VirtualFile, nodes: List) - fun reportResult(project: Project, successfulRoots: Collection, exceptionsByRoot: MultiMap) { + fun reportResult(project: Project, nodes: List, successfulRoots: Collection, exceptionsByRoot: MultiMap) { if (!exceptionsByRoot.isEmpty) { showErrorMessage(project, errorMessage, exceptionsByRoot.values()) } diff --git a/plugins/git4idea/src/git4idea/index/actions/StagingAreaOperationAction.kt b/plugins/git4idea/src/git4idea/index/actions/StagingAreaOperationAction.kt index 03dbf550c7df..aa06eb5f6ca2 100644 --- a/plugins/git4idea/src/git4idea/index/actions/StagingAreaOperationAction.kt +++ b/plugins/git4idea/src/git4idea/index/actions/StagingAreaOperationAction.kt @@ -78,7 +78,7 @@ fun performStageOperation(project: Project, nodes: List, oper } } - operation.reportResult(project, successfulRoots, exceptions) + operation.reportResult(project, nodes, successfulRoots, exceptions) stagingAreaActionInvoked() } } diff --git a/plugins/git4idea/src/git4idea/stash/GitStashUtils.kt b/plugins/git4idea/src/git4idea/stash/GitStashUtils.kt index 99ccbe30cf13..c122197502c4 100644 --- a/plugins/git4idea/src/git4idea/stash/GitStashUtils.kt +++ b/plugins/git4idea/src/git4idea/stash/GitStashUtils.kt @@ -311,7 +311,7 @@ object GitStashOperations { return GitBundle.message("stash.files.in.roots.success", rootsText) } - private fun getRootsText(project: Project, roots: Collection): String { + internal fun getRootsText(project: Project, roots: Collection): String { val rootsText = roots.joinToString(", ") { "'${VcsImplUtil.getShortVcsRootName(project, it)}'" } return StringUtil.shortenTextWithEllipsis(rootsText, 100, 0) }