mirror of
https://gitflic.ru/project/openide/openide.git
synced 2025-12-16 22:51:17 +07:00
[git] IJPL-73913 Suggest stashing/shelving changes and retry cherry-pick
GitOrigin-RevId: d73345928d536ddfe9f22666b4f2dd3272263c31
This commit is contained in:
committed by
intellij-monorepo-bot
parent
ed0d05e826
commit
37600c5ef0
@@ -199,8 +199,8 @@ abstract class VcsPlatformTest : HeavyPlatformTestCase() {
|
|||||||
return assertHasNotification(NotificationType.WARNING, title, message, vcsNotifier.notifications)
|
return assertHasNotification(NotificationType.WARNING, title, message, vcsNotifier.notifications)
|
||||||
}
|
}
|
||||||
|
|
||||||
protected fun assertErrorNotification(title: String, message: String): Notification {
|
protected fun assertErrorNotification(title: String, message: String, actions: List<String>? = null): Notification {
|
||||||
return assertHasNotification(NotificationType.ERROR, title, message, vcsNotifier.notifications)
|
return assertHasNotification(NotificationType.ERROR, title, message, actions, vcsNotifier.notifications)
|
||||||
}
|
}
|
||||||
|
|
||||||
protected fun assertNoNotification() {
|
protected fun assertNoNotification() {
|
||||||
|
|||||||
@@ -30,6 +30,9 @@ apply.changes.operation.canceled={0} canceled
|
|||||||
apply.changes.operation.failed={0} failed
|
apply.changes.operation.failed={0} failed
|
||||||
apply.changes.operation.performed.with.conflicts={0} was performed with conflicts
|
apply.changes.operation.performed.with.conflicts={0} was performed with conflicts
|
||||||
apply.changes.operation.successful.for.commits=However, {0} succeeded for the following {1,choice,1#commit|2#commits}:
|
apply.changes.operation.successful.for.commits=However, {0} succeeded for the following {1,choice,1#commit|2#commits}:
|
||||||
|
apply.changes.restore.notification.description=Local changes were saved before {0}
|
||||||
|
apply.changes.restore.notification.title=Restore local changes
|
||||||
|
apply.changes.save.and.retry.operation={0} Changes and Retry
|
||||||
apply.changes.skipped={0} {1,choice,1#was|2#were} skipped, because all changes have already been {2}.
|
apply.changes.skipped={0} {1,choice,1#was|2#were} skipped, because all changes have already been {2}.
|
||||||
apply.changes.everything.applied=All changes from {0} have already been {1}
|
apply.changes.everything.applied=All changes from {0} have already been {1}
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import com.intellij.history.LocalHistory
|
|||||||
import com.intellij.openapi.application.ApplicationManager
|
import com.intellij.openapi.application.ApplicationManager
|
||||||
import com.intellij.openapi.application.ModalityState
|
import com.intellij.openapi.application.ModalityState
|
||||||
import com.intellij.openapi.application.runInEdt
|
import com.intellij.openapi.application.runInEdt
|
||||||
|
import com.intellij.openapi.components.service
|
||||||
import com.intellij.openapi.diagnostic.logger
|
import com.intellij.openapi.diagnostic.logger
|
||||||
import com.intellij.openapi.project.Project
|
import com.intellij.openapi.project.Project
|
||||||
import com.intellij.openapi.util.NlsContexts
|
import com.intellij.openapi.util.NlsContexts
|
||||||
@@ -15,7 +16,9 @@ import com.intellij.openapi.util.NlsSafe
|
|||||||
import com.intellij.openapi.util.text.StringUtil
|
import com.intellij.openapi.util.text.StringUtil
|
||||||
import com.intellij.openapi.vcs.AbstractVcsHelper
|
import com.intellij.openapi.vcs.AbstractVcsHelper
|
||||||
import com.intellij.openapi.vcs.VcsApplicationSettings
|
import com.intellij.openapi.vcs.VcsApplicationSettings
|
||||||
|
import com.intellij.openapi.vcs.VcsBundle
|
||||||
import com.intellij.openapi.vcs.VcsException
|
import com.intellij.openapi.vcs.VcsException
|
||||||
|
import com.intellij.openapi.vcs.VcsNotificationIdsHolder
|
||||||
import com.intellij.openapi.vcs.VcsNotifier
|
import com.intellij.openapi.vcs.VcsNotifier
|
||||||
import com.intellij.openapi.vcs.changes.*
|
import com.intellij.openapi.vcs.changes.*
|
||||||
import com.intellij.openapi.vcs.update.RefreshVFsSynchronously
|
import com.intellij.openapi.vcs.update.RefreshVFsSynchronously
|
||||||
@@ -27,6 +30,8 @@ import com.intellij.xml.util.XmlStringUtil.wrapInHtml
|
|||||||
import com.intellij.xml.util.XmlStringUtil.wrapInHtmlTag
|
import com.intellij.xml.util.XmlStringUtil.wrapInHtmlTag
|
||||||
import git4idea.GitUtil.refreshChangedVfs
|
import git4idea.GitUtil.refreshChangedVfs
|
||||||
import git4idea.actions.GitAbortOperationAction
|
import git4idea.actions.GitAbortOperationAction
|
||||||
|
import git4idea.applyChanges.GitApplyChangesLocalChangesDetectedNotification
|
||||||
|
import git4idea.applyChanges.GitApplyChangesNotificationsHandler
|
||||||
import git4idea.changes.GitChangeUtils.getStagedChanges
|
import git4idea.changes.GitChangeUtils.getStagedChanges
|
||||||
import git4idea.cherrypick.GitLocalChangesConflictDetector
|
import git4idea.cherrypick.GitLocalChangesConflictDetector
|
||||||
import git4idea.commands.GitCommandResult
|
import git4idea.commands.GitCommandResult
|
||||||
@@ -34,7 +39,6 @@ import git4idea.commands.GitLineHandlerListener
|
|||||||
import git4idea.commands.GitSimpleEventDetector
|
import git4idea.commands.GitSimpleEventDetector
|
||||||
import git4idea.commands.GitSimpleEventDetector.Event.CHERRY_PICK_CONFLICT
|
import git4idea.commands.GitSimpleEventDetector.Event.CHERRY_PICK_CONFLICT
|
||||||
import git4idea.commands.GitUntrackedFilesOverwrittenByOperationDetector
|
import git4idea.commands.GitUntrackedFilesOverwrittenByOperationDetector
|
||||||
import git4idea.config.GitVcsSettings
|
|
||||||
import git4idea.i18n.GitBundle
|
import git4idea.i18n.GitBundle
|
||||||
import git4idea.index.isStagingAreaAvailable
|
import git4idea.index.isStagingAreaAvailable
|
||||||
import git4idea.index.showStagingArea
|
import git4idea.index.showStagingArea
|
||||||
@@ -42,6 +46,7 @@ import git4idea.merge.GitConflictResolver
|
|||||||
import git4idea.merge.GitDefaultMergeDialogCustomizer
|
import git4idea.merge.GitDefaultMergeDialogCustomizer
|
||||||
import git4idea.repo.GitRepository
|
import git4idea.repo.GitRepository
|
||||||
import git4idea.repo.GitRepositoryManager
|
import git4idea.repo.GitRepositoryManager
|
||||||
|
import git4idea.stash.GitChangesSaver
|
||||||
import git4idea.util.GitUntrackedFilesHelper
|
import git4idea.util.GitUntrackedFilesHelper
|
||||||
import org.jetbrains.annotations.Nls
|
import org.jetbrains.annotations.Nls
|
||||||
import org.jetbrains.annotations.NonNls
|
import org.jetbrains.annotations.NonNls
|
||||||
@@ -69,6 +74,7 @@ internal abstract class GitApplyChangesProcess(
|
|||||||
private val vcsNotifier = VcsNotifier.getInstance(project)
|
private val vcsNotifier = VcsNotifier.getInstance(project)
|
||||||
private val changeListManager = ChangeListManagerEx.getInstanceEx(project)
|
private val changeListManager = ChangeListManagerEx.getInstanceEx(project)
|
||||||
private val vcsHelper = AbstractVcsHelper.getInstance(project)
|
private val vcsHelper = AbstractVcsHelper.getInstance(project)
|
||||||
|
private val notificationsHandler = project.service<GitApplyChangesNotificationsHandler>()
|
||||||
protected val autoCommit = forceAutoCommit || !changeListManager.areChangeListsEnabled()
|
protected val autoCommit = forceAutoCommit || !changeListManager.areChangeListsEnabled()
|
||||||
|
|
||||||
protected abstract fun isEmptyCommit(result: GitCommandResult): Boolean
|
protected abstract fun isEmptyCommit(result: GitCommandResult): Boolean
|
||||||
@@ -84,6 +90,11 @@ internal abstract class GitApplyChangesProcess(
|
|||||||
): GitCommandResult
|
): GitCommandResult
|
||||||
|
|
||||||
fun execute() {
|
fun execute() {
|
||||||
|
notificationsHandler.beforeApply()
|
||||||
|
execute(null, commits)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun execute(changesSaver: GitChangesSaver?, commits: List<VcsCommitMetadata>) {
|
||||||
// ensure there are no stall changes (ex: from recent commit) that prevent changes from being moved into temp changelist
|
// ensure there are no stall changes (ex: from recent commit) that prevent changes from being moved into temp changelist
|
||||||
if (changeListManager.areChangeListsEnabled()) {
|
if (changeListManager.areChangeListsEnabled()) {
|
||||||
changeListManager.waitForUpdate()
|
changeListManager.waitForUpdate()
|
||||||
@@ -92,30 +103,44 @@ internal abstract class GitApplyChangesProcess(
|
|||||||
val commitsInRoots = DvcsUtil.groupCommitsByRoots(repositoryManager, commits)
|
val commitsInRoots = DvcsUtil.groupCommitsByRoots(repositoryManager, commits)
|
||||||
LOG.info("${operationName}ing commits: " + toString(commitsInRoots))
|
LOG.info("${operationName}ing commits: " + toString(commitsInRoots))
|
||||||
|
|
||||||
|
if (changesSaver != null) {
|
||||||
|
if (!trySaveChanges(commitsInRoots.map { (repo, _) -> repo.root }, changesSaver)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
val successfulCommits = mutableListOf<VcsCommitMetadata>()
|
val successfulCommits = mutableListOf<VcsCommitMetadata>()
|
||||||
val skippedCommits = mutableListOf<VcsCommitMetadata>()
|
val skippedCommits = mutableListOf<VcsCommitMetadata>()
|
||||||
|
|
||||||
for ((repository, repoCommits) in commitsInRoots) {
|
for ((repository, repoCommits) in commitsInRoots) {
|
||||||
val success = executeForRepository(repository, repoCommits, successfulCommits, skippedCommits)
|
try {
|
||||||
if (!success) return
|
for (commit in repoCommits) {
|
||||||
|
if (!executeForCommit(repository, commit, successfulCommits, skippedCommits)) {
|
||||||
|
notificationsHandler.operationFailed(operationName, repository, changesSaver)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
repository.update()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
notifyResult(successfulCommits, skippedCommits)
|
notifyResult(successfulCommits, skippedCommits)
|
||||||
|
if (changesSaver != null) {
|
||||||
|
LOG.info("Restoring saved changes after successful $operationName")
|
||||||
|
changesSaver.load()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun executeForRepository(repository: GitRepository,
|
fun trySaveChanges(roots: List<VirtualFile>, changesSaver: GitChangesSaver): Boolean {
|
||||||
repoCommits: List<VcsCommitMetadata>,
|
val errorMessage = changesSaver.saveLocalChangesOrError(roots) ?: return true
|
||||||
successfulCommits: MutableList<VcsCommitMetadata>,
|
|
||||||
skippedCommits: MutableList<VcsCommitMetadata>): Boolean {
|
VcsNotifier.getInstance(project)
|
||||||
try {
|
.notifyError(VcsNotificationIdsHolder.UNCOMMITTED_CHANGES_SAVING_ERROR,
|
||||||
for (commit in repoCommits) {
|
VcsBundle.message("notification.title.couldn.t.save.uncommitted.changes"),
|
||||||
val success = executeForCommit(repository, commit, successfulCommits, skippedCommits)
|
errorMessage)
|
||||||
if (!success) return false
|
return false
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
repository.update()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -195,9 +220,7 @@ internal abstract class GitApplyChangesProcess(
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
else if (localChangesOverwrittenDetector.isDetected) {
|
else if (localChangesOverwrittenDetector.isDetected) {
|
||||||
val savingStrategy = GitVcsSettings.getInstance(project).saveChangesPolicy
|
handleLocalChangesDetected(repository, commit.takeIf { localChangesOverwrittenDetector.byMerge }, successfulCommits, alreadyPicked)
|
||||||
val message = GitBundle.message("warning.your.local.changes.would.be.overwritten.by", operationName, savingStrategy.text.lowercase())
|
|
||||||
notifyError(message, commit, successfulCommits)
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
else if (isEmptyCommit(result)) {
|
else if (isEmptyCommit(result)) {
|
||||||
@@ -215,6 +238,23 @@ internal abstract class GitApplyChangesProcess(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun handleLocalChangesDetected(
|
||||||
|
repository: GitRepository,
|
||||||
|
failedOnCommit: VcsCommitMetadata?,
|
||||||
|
successfulCommits: MutableList<VcsCommitMetadata>,
|
||||||
|
alreadyPicked: MutableList<VcsCommitMetadata>,
|
||||||
|
) {
|
||||||
|
val notification = GitApplyChangesLocalChangesDetectedNotification(operationName, failedOnCommit, successfulCommits, repository) { saver ->
|
||||||
|
val alreadyPickedSet = buildSet {
|
||||||
|
addAll(alreadyPicked)
|
||||||
|
addAll(successfulCommits)
|
||||||
|
}
|
||||||
|
LOG.info("Re-trying $operationName, skipping ${alreadyPickedSet.size} already processed commits")
|
||||||
|
execute(saver, commits.filter { commit -> !alreadyPickedSet.contains(commit) })
|
||||||
|
}
|
||||||
|
vcsNotifier.notify(notification)
|
||||||
|
}
|
||||||
|
|
||||||
private abstract class CommitStrategy {
|
private abstract class CommitStrategy {
|
||||||
open fun start() = Unit
|
open fun start() = Unit
|
||||||
open fun finish() = Unit
|
open fun finish() = Unit
|
||||||
@@ -455,16 +495,8 @@ internal abstract class GitApplyChangesProcess(
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Nls
|
@Nls
|
||||||
private fun getSuccessfulCommitDetailsIfAny(successfulCommits: List<VcsCommitMetadata>): String {
|
private fun getSuccessfulCommitDetailsIfAny(successfulCommits: List<VcsCommitMetadata>) =
|
||||||
var description = ""
|
getSuccessfulCommitDetailsIfAny(successfulCommits, operationName)
|
||||||
if (successfulCommits.isNotEmpty()) {
|
|
||||||
description += UIUtil.HR +
|
|
||||||
GitBundle.message("apply.changes.operation.successful.for.commits", operationName, successfulCommits.size) +
|
|
||||||
UIUtil.BR
|
|
||||||
description += getCommitsDetails(successfulCommits)
|
|
||||||
}
|
|
||||||
return description
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nls
|
@Nls
|
||||||
private fun formSkippedDescription(skipped: List<VcsCommitMetadata>, but: Boolean): String {
|
private fun formSkippedDescription(skipped: List<VcsCommitMetadata>, but: Boolean): String {
|
||||||
@@ -475,21 +507,6 @@ internal abstract class GitApplyChangesProcess(
|
|||||||
return GitBundle.message("apply.changes.everything.applied", hashes, appliedWord)
|
return GitBundle.message("apply.changes.everything.applied", hashes, appliedWord)
|
||||||
}
|
}
|
||||||
|
|
||||||
@NlsSafe
|
|
||||||
private fun getCommitsDetails(successfulCommits: List<VcsCommitMetadata>): String {
|
|
||||||
var description = ""
|
|
||||||
for (commit in successfulCommits) {
|
|
||||||
if (description.isNotEmpty()) description += UIUtil.BR
|
|
||||||
description += commitDetails(commit)
|
|
||||||
}
|
|
||||||
return description
|
|
||||||
}
|
|
||||||
|
|
||||||
@NlsSafe
|
|
||||||
private fun commitDetails(commit: VcsCommitMetadata): String {
|
|
||||||
return commit.id.toShortString() + " " + StringUtil.escapeXmlEntities(commit.subject)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun toString(commitsInRoots: Map<GitRepository, List<VcsCommitMetadata>>): String {
|
private fun toString(commitsInRoots: Map<GitRepository, List<VcsCommitMetadata>>): String {
|
||||||
return commitsInRoots.entries.joinToString("; ") { entry ->
|
return commitsInRoots.entries.joinToString("; ") { entry ->
|
||||||
val commits = entry.value.joinToString { it.id.asString() }
|
val commits = entry.value.joinToString { it.id.asString() }
|
||||||
@@ -510,8 +527,35 @@ internal abstract class GitApplyChangesProcess(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
internal companion object {
|
||||||
private val LOG = logger<GitApplyChangesProcess>()
|
private val LOG = logger<GitApplyChangesProcess>()
|
||||||
|
|
||||||
|
@NlsSafe
|
||||||
|
fun commitDetails(commit: VcsCommitMetadata): String {
|
||||||
|
return commit.id.toShortString() + " " + StringUtil.escapeXmlEntities(commit.subject)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nls
|
||||||
|
fun getSuccessfulCommitDetailsIfAny(successfulCommits: List<VcsCommitMetadata>, operationName: String): String {
|
||||||
|
var description = ""
|
||||||
|
if (successfulCommits.isNotEmpty()) {
|
||||||
|
description += UIUtil.HR +
|
||||||
|
GitBundle.message("apply.changes.operation.successful.for.commits", operationName, successfulCommits.size) +
|
||||||
|
UIUtil.BR
|
||||||
|
description += getCommitsDetails(successfulCommits)
|
||||||
|
}
|
||||||
|
return description
|
||||||
|
}
|
||||||
|
|
||||||
|
@NlsSafe
|
||||||
|
private fun getCommitsDetails(successfulCommits: List<VcsCommitMetadata>): String {
|
||||||
|
var description = ""
|
||||||
|
for (commit in successfulCommits) {
|
||||||
|
if (description.isNotEmpty()) description += UIUtil.BR
|
||||||
|
description += commitDetails(commit)
|
||||||
|
}
|
||||||
|
return description
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ class GitNotificationIdsHolder : NotificationIdsHolder {
|
|||||||
APPLY_CHANGES_SUCCESS,
|
APPLY_CHANGES_SUCCESS,
|
||||||
APPLY_CHANGES_CONFLICTS,
|
APPLY_CHANGES_CONFLICTS,
|
||||||
APPLY_CHANGES_ERROR,
|
APPLY_CHANGES_ERROR,
|
||||||
|
APPLY_CHANGES_LOCAL_CHANGES_DETECTED,
|
||||||
BRANCH_UPDATE_FORCE_PUSHED_BRANCH_NOT_ALL_CHERRY_PICKED,
|
BRANCH_UPDATE_FORCE_PUSHED_BRANCH_NOT_ALL_CHERRY_PICKED,
|
||||||
BRANCH_UPDATE_FORCE_PUSHED_BRANCH_SUCCESS,
|
BRANCH_UPDATE_FORCE_PUSHED_BRANCH_SUCCESS,
|
||||||
BRANCH_CHECKOUT_FAILED,
|
BRANCH_CHECKOUT_FAILED,
|
||||||
@@ -123,6 +124,7 @@ class GitNotificationIdsHolder : NotificationIdsHolder {
|
|||||||
const val APPLY_CHANGES_SUCCESS = "git.apply.changes.success"
|
const val APPLY_CHANGES_SUCCESS = "git.apply.changes.success"
|
||||||
const val APPLY_CHANGES_CONFLICTS = "git.apply.changes.conflicts"
|
const val APPLY_CHANGES_CONFLICTS = "git.apply.changes.conflicts"
|
||||||
const val APPLY_CHANGES_ERROR = "git.apply.changes.error"
|
const val APPLY_CHANGES_ERROR = "git.apply.changes.error"
|
||||||
|
const val APPLY_CHANGES_LOCAL_CHANGES_DETECTED = "git.apply.changes.local.changes.detected"
|
||||||
const val BRANCH_UPDATE_FORCE_PUSHED_BRANCH_NOT_ALL_CHERRY_PICKED = "git.update.force.pushed.branch.not.all.cherry.picked"
|
const val BRANCH_UPDATE_FORCE_PUSHED_BRANCH_NOT_ALL_CHERRY_PICKED = "git.update.force.pushed.branch.not.all.cherry.picked"
|
||||||
const val BRANCH_UPDATE_FORCE_PUSHED_BRANCH_SUCCESS = "git.update.force.pushed.branch.success"
|
const val BRANCH_UPDATE_FORCE_PUSHED_BRANCH_SUCCESS = "git.update.force.pushed.branch.success"
|
||||||
const val BRANCH_CHECKOUT_FAILED = "git.branch.checkout.failed"
|
const val BRANCH_CHECKOUT_FAILED = "git.branch.checkout.failed"
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||||
|
package git4idea
|
||||||
|
|
||||||
|
import com.intellij.notification.Notification
|
||||||
|
import com.intellij.notification.NotificationAction
|
||||||
|
import com.intellij.openapi.actionSystem.AnActionEvent
|
||||||
|
import git4idea.i18n.GitBundle
|
||||||
|
import git4idea.stash.GitChangesSaver
|
||||||
|
|
||||||
|
internal class GitRestoreSavedChangesNotificationAction(private val saver: GitChangesSaver) : NotificationAction(
|
||||||
|
saver.saveMethod.selectBundleMessage(
|
||||||
|
GitBundle.message("rebase.notification.action.view.stash.text"),
|
||||||
|
GitBundle.message("rebase.notification.action.view.shelf.text")
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
override fun actionPerformed(e: AnActionEvent, notification: Notification) {
|
||||||
|
saver.showSavedChanges()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||||
|
package git4idea.applyChanges
|
||||||
|
|
||||||
|
import com.intellij.notification.NotificationAction
|
||||||
|
import com.intellij.notification.NotificationType
|
||||||
|
import com.intellij.openapi.project.Project
|
||||||
|
import com.intellij.openapi.vcs.VcsBundle
|
||||||
|
import com.intellij.openapi.vcs.VcsNotifier
|
||||||
|
import com.intellij.platform.ide.progress.withBackgroundProgress
|
||||||
|
import git4idea.GitApplyChangesNotification
|
||||||
|
import git4idea.GitDisposable
|
||||||
|
import git4idea.GitRestoreSavedChangesNotificationAction
|
||||||
|
import git4idea.i18n.GitBundle
|
||||||
|
import git4idea.stash.GitChangesSaver
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import org.jetbrains.annotations.Nls
|
||||||
|
|
||||||
|
internal class GitApplyChangesCanRestoreNotification(
|
||||||
|
project: Project,
|
||||||
|
changesSaver: GitChangesSaver,
|
||||||
|
operationName: @Nls String,
|
||||||
|
) : GitApplyChangesNotification(
|
||||||
|
VcsNotifier.importantNotification().displayId,
|
||||||
|
GitBundle.message("apply.changes.restore.notification.title"),
|
||||||
|
GitBundle.message("apply.changes.restore.notification.description", operationName),
|
||||||
|
NotificationType.INFORMATION,
|
||||||
|
) {
|
||||||
|
init {
|
||||||
|
addAction(GitRestoreSavedChangesNotificationAction(changesSaver))
|
||||||
|
addAction(NotificationAction.createExpiring(changesSaver.saveMethod.selectBundleMessage(
|
||||||
|
GitBundle.message("unstash.title"),
|
||||||
|
VcsBundle.message("unshelve.changes.action")
|
||||||
|
)) { _, _ ->
|
||||||
|
GitDisposable.getInstance(project).coroutineScope.launch {
|
||||||
|
withBackgroundProgress(project, changesSaver.saveMethod.selectBundleMessage(
|
||||||
|
GitBundle.message("unstash.unstashing"),
|
||||||
|
VcsBundle.message("unshelve.changes.progress.title")
|
||||||
|
)) {
|
||||||
|
changesSaver.load()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,112 @@
|
|||||||
|
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||||
|
package git4idea.applyChanges
|
||||||
|
|
||||||
|
import com.intellij.ide.IdeBundle
|
||||||
|
import com.intellij.notification.NotificationAction
|
||||||
|
import com.intellij.notification.NotificationType
|
||||||
|
import com.intellij.openapi.progress.EmptyProgressIndicator
|
||||||
|
import com.intellij.openapi.project.Project
|
||||||
|
import com.intellij.openapi.util.text.StringUtil
|
||||||
|
import com.intellij.openapi.vcs.FilePath
|
||||||
|
import com.intellij.openapi.vcs.VcsBundle
|
||||||
|
import com.intellij.openapi.vcs.VcsNotificationIdsHolder
|
||||||
|
import com.intellij.openapi.vcs.VcsNotifier
|
||||||
|
import com.intellij.platform.ide.progress.withBackgroundProgress
|
||||||
|
import com.intellij.util.ui.UIUtil
|
||||||
|
import com.intellij.vcs.log.VcsCommitMetadata
|
||||||
|
import git4idea.GitApplyChangesNotification
|
||||||
|
import git4idea.GitApplyChangesProcess
|
||||||
|
import git4idea.GitDisposable
|
||||||
|
import git4idea.GitNotificationIdsHolder
|
||||||
|
import git4idea.GitUtil
|
||||||
|
import git4idea.commands.Git
|
||||||
|
import git4idea.config.GitSaveChangesPolicy
|
||||||
|
import git4idea.config.GitVcsApplicationSettings
|
||||||
|
import git4idea.config.GitVcsSettings
|
||||||
|
import git4idea.i18n.GitBundle
|
||||||
|
import git4idea.repo.GitRepository
|
||||||
|
import git4idea.stash.GitChangesSaver
|
||||||
|
import git4idea.util.LocalChangesWouldBeOverwrittenHelper
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import org.jetbrains.annotations.Nls
|
||||||
|
|
||||||
|
internal class GitApplyChangesLocalChangesDetectedNotification(
|
||||||
|
operationName: @Nls String,
|
||||||
|
failedOnCommit: VcsCommitMetadata?,
|
||||||
|
successfulCommits: List<VcsCommitMetadata>,
|
||||||
|
repository: GitRepository,
|
||||||
|
retryAction: (GitChangesSaver) -> Unit,
|
||||||
|
) : GitApplyChangesNotification(
|
||||||
|
VcsNotifier.importantNotification().displayId,
|
||||||
|
GitBundle.message("apply.changes.operation.failed", operationName.capitalize()),
|
||||||
|
getDescription(operationName, repository, failedOnCommit, successfulCommits),
|
||||||
|
NotificationType.ERROR,
|
||||||
|
) {
|
||||||
|
init {
|
||||||
|
val project = repository.project
|
||||||
|
val affectedPaths = repository.getStagingAreaHolder().allRecords.map { it.path }
|
||||||
|
val localChanges =
|
||||||
|
GitUtil.findLocalChangesForPaths(project, repository.getRoot(), affectedPaths.map(FilePath::getPath), false)
|
||||||
|
|
||||||
|
setDisplayId(GitNotificationIdsHolder.Companion.APPLY_CHANGES_LOCAL_CHANGES_DETECTED)
|
||||||
|
|
||||||
|
addAction(NotificationAction.createSimple(IdeBundle.messagePointer("action.show.files")) {
|
||||||
|
LocalChangesWouldBeOverwrittenHelper.showErrorDialog(
|
||||||
|
project,
|
||||||
|
operationName,
|
||||||
|
null,
|
||||||
|
localChanges,
|
||||||
|
affectedPaths.map { it.path }
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
if (localChanges.isNotEmpty()) {
|
||||||
|
addAction(saveAndRetryAction(repository, operationName, retryAction))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun saveAndRetryAction(
|
||||||
|
repository: GitRepository,
|
||||||
|
operationName: @Nls String,
|
||||||
|
retryAction: (GitChangesSaver) -> Unit,
|
||||||
|
): NotificationAction {
|
||||||
|
val savingStrategy = getSavingStrategy(repository.project)
|
||||||
|
val actionText = GitBundle.message("apply.changes.save.and.retry.operation", savingStrategy.text)
|
||||||
|
return NotificationAction.createExpiring(actionText) { _, _ ->
|
||||||
|
GitDisposable.getInstance(repository.project).coroutineScope.launch {
|
||||||
|
withBackgroundProgress(repository.project, savingStrategy.selectBundleMessage(
|
||||||
|
GitBundle.message("stashing.progress.title"),
|
||||||
|
VcsBundle.message("shelve.changes.progress.text")
|
||||||
|
)) {
|
||||||
|
val changesSaver = GitChangesSaver.getSaver(repository.project, Git.getInstance(), EmptyProgressIndicator(),
|
||||||
|
VcsBundle.message("stash.changes.message", operationName), savingStrategy)
|
||||||
|
retryAction(changesSaver)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private companion object {
|
||||||
|
fun getSavingStrategy(project: Project) =
|
||||||
|
if (GitVcsApplicationSettings.getInstance().isStagingAreaEnabled) GitSaveChangesPolicy.STASH
|
||||||
|
else GitVcsSettings.getInstance(project).saveChangesPolicy
|
||||||
|
|
||||||
|
|
||||||
|
fun getDescription(
|
||||||
|
operationName: @Nls String,
|
||||||
|
repository: GitRepository,
|
||||||
|
failedOnCommit: VcsCommitMetadata?,
|
||||||
|
successfulCommits: List<VcsCommitMetadata>,
|
||||||
|
): @Nls String {
|
||||||
|
var description = if (failedOnCommit != null) {
|
||||||
|
GitApplyChangesProcess.commitDetails(failedOnCommit) + UIUtil.BR
|
||||||
|
}
|
||||||
|
else ""
|
||||||
|
|
||||||
|
description += GitBundle.message("warning.your.local.changes.would.be.overwritten.by", operationName,
|
||||||
|
StringUtil.toLowerCase(getSavingStrategy(repository.project).text))
|
||||||
|
description += GitApplyChangesProcess.getSuccessfulCommitDetailsIfAny(successfulCommits, operationName)
|
||||||
|
return description
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,80 @@
|
|||||||
|
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||||
|
package git4idea.applyChanges
|
||||||
|
|
||||||
|
import com.intellij.dvcs.repo.Repository
|
||||||
|
import com.intellij.openapi.components.Service
|
||||||
|
import com.intellij.openapi.diagnostic.thisLogger
|
||||||
|
import com.intellij.openapi.project.Project
|
||||||
|
import com.intellij.openapi.vcs.VcsNotifier
|
||||||
|
import git4idea.GitApplyChangesNotification
|
||||||
|
import git4idea.GitDisposable
|
||||||
|
import git4idea.repo.GitRepository
|
||||||
|
import git4idea.repo.GitRepositoryChangeListener
|
||||||
|
import git4idea.stash.GitChangesSaver
|
||||||
|
import org.jetbrains.annotations.Nls
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean
|
||||||
|
import java.util.concurrent.atomic.AtomicReference
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper class for hiding or showing notifications related to applying changes
|
||||||
|
* when [git4idea.GitApplyChangesProcess.execute] is already finished
|
||||||
|
*/
|
||||||
|
@Service(Service.Level.PROJECT)
|
||||||
|
internal class GitApplyChangesNotificationsHandler(private val project: Project) {
|
||||||
|
private var shouldHideNotifications = AtomicBoolean()
|
||||||
|
private var changesSaverAndOperation = AtomicReference<Pair<GitChangesSaver, @Nls String>?>()
|
||||||
|
|
||||||
|
init {
|
||||||
|
project.messageBus.connect(GitDisposable.Companion.getInstance(project))
|
||||||
|
.subscribe(GitRepository.GIT_REPO_CHANGE, GitRepositoryChangeListener {
|
||||||
|
if (!isCherryPickingOrReverting(it)) {
|
||||||
|
if (shouldHideNotifications.compareAndSet(true, false)) {
|
||||||
|
LOG.debug("Hiding notifications")
|
||||||
|
GitApplyChangesNotification.Companion.expireAll<GitApplyChangesNotification.ExpireAfterRepoStateChanged>(project)
|
||||||
|
}
|
||||||
|
|
||||||
|
changesSaverAndOperation.getAndSet(null)?.let { (changesSaver, operation) ->
|
||||||
|
LOG.debug("Suggesting to restore saved changes after $operation")
|
||||||
|
showRestoreChangesNotification(changesSaver, operation)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fun beforeApply() {
|
||||||
|
shouldHideNotifications.set(false)
|
||||||
|
changesSaverAndOperation.set(null)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun operationFailed(operationName: @Nls String, repository: GitRepository, changesSaver: GitChangesSaver?) {
|
||||||
|
val cherryPickingOrReverting = isCherryPickingOrReverting(repository)
|
||||||
|
if (cherryPickingOrReverting) {
|
||||||
|
shouldHideNotifications.set(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (changesSaver != null) {
|
||||||
|
if (cherryPickingOrReverting) {
|
||||||
|
changesSaverAndOperation.set(changesSaver to operationName)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
showRestoreChangesNotification(changesSaver, operationName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun showRestoreChangesNotification(changesSaver: GitChangesSaver, operation: @Nls String) {
|
||||||
|
VcsNotifier.getInstance(project).notify(
|
||||||
|
GitApplyChangesCanRestoreNotification(project, changesSaver, operation)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun isCherryPickingOrReverting(repository: GitRepository): Boolean =
|
||||||
|
repository.state == Repository.State.GRAFTING || repository.state == Repository.State.REVERTING
|
||||||
|
|
||||||
|
internal companion object {
|
||||||
|
private val LOG = thisLogger()
|
||||||
|
|
||||||
|
fun getInstance(project: Project): GitApplyChangesNotificationsHandler =
|
||||||
|
project.getService(GitApplyChangesNotificationsHandler::class.java)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,7 +2,6 @@
|
|||||||
package git4idea.cherrypick
|
package git4idea.cherrypick
|
||||||
|
|
||||||
import com.intellij.dvcs.ui.DvcsBundle
|
import com.intellij.dvcs.ui.DvcsBundle
|
||||||
import com.intellij.openapi.components.service
|
|
||||||
import com.intellij.openapi.diagnostic.thisLogger
|
import com.intellij.openapi.diagnostic.thisLogger
|
||||||
import com.intellij.openapi.progress.ProgressIndicator
|
import com.intellij.openapi.progress.ProgressIndicator
|
||||||
import com.intellij.openapi.project.Project
|
import com.intellij.openapi.project.Project
|
||||||
@@ -43,10 +42,6 @@ internal class GitCherryPickProcess(
|
|||||||
|
|
||||||
fun isSuccess() = successfullyCherryPickedCount == totalCommitsToCherryPick
|
fun isSuccess() = successfullyCherryPickedCount == totalCommitsToCherryPick
|
||||||
|
|
||||||
init {
|
|
||||||
project.service<GitCherryPickNotificationsHandler>()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun isEmptyCommit(result: GitCommandResult): Boolean {
|
override fun isEmptyCommit(result: GitCommandResult): Boolean {
|
||||||
val stdout = result.outputAsJoinedString
|
val stdout = result.outputAsJoinedString
|
||||||
val stderr = result.errorOutputAsJoinedString
|
val stderr = result.errorOutputAsJoinedString
|
||||||
|
|||||||
@@ -1,29 +0,0 @@
|
|||||||
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
|
||||||
package git4idea.cherrypick
|
|
||||||
|
|
||||||
import com.intellij.dvcs.repo.Repository
|
|
||||||
import com.intellij.openapi.components.Service
|
|
||||||
import com.intellij.openapi.project.Project
|
|
||||||
import git4idea.GitApplyChangesNotification
|
|
||||||
import git4idea.GitApplyChangesNotification.ExpireAfterRepoStateChanged
|
|
||||||
import git4idea.GitDisposable
|
|
||||||
import git4idea.repo.GitRepository
|
|
||||||
import git4idea.repo.GitRepositoryChangeListener
|
|
||||||
import java.util.concurrent.ConcurrentHashMap
|
|
||||||
|
|
||||||
@Service(Service.Level.PROJECT)
|
|
||||||
internal class GitCherryPickNotificationsHandler(project: Project) {
|
|
||||||
private val cherryPickingIn = ConcurrentHashMap.newKeySet<GitRepository>()
|
|
||||||
|
|
||||||
init {
|
|
||||||
project.messageBus.connect(GitDisposable.getInstance(project))
|
|
||||||
.subscribe(GitRepository.GIT_REPO_CHANGE, GitRepositoryChangeListener {
|
|
||||||
if (it.state == Repository.State.GRAFTING) {
|
|
||||||
cherryPickingIn.add(it)
|
|
||||||
}
|
|
||||||
else if (cherryPickingIn.remove(it)) {
|
|
||||||
GitApplyChangesNotification.expireAll<ExpireAfterRepoStateChanged>(project)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -30,10 +30,7 @@ import com.intellij.util.concurrency.annotations.RequiresBackgroundThread;
|
|||||||
import com.intellij.util.progress.StepsProgressIndicator;
|
import com.intellij.util.progress.StepsProgressIndicator;
|
||||||
import com.intellij.vcs.log.Hash;
|
import com.intellij.vcs.log.Hash;
|
||||||
import com.intellij.vcs.log.TimedVcsCommit;
|
import com.intellij.vcs.log.TimedVcsCommit;
|
||||||
import git4idea.DialogManager;
|
import git4idea.*;
|
||||||
import git4idea.GitActivity;
|
|
||||||
import git4idea.GitNotificationIdsHolder;
|
|
||||||
import git4idea.GitProtectedBranchesKt;
|
|
||||||
import git4idea.branch.GitRebaseParams;
|
import git4idea.branch.GitRebaseParams;
|
||||||
import git4idea.commands.*;
|
import git4idea.commands.*;
|
||||||
import git4idea.config.GitSaveChangesPolicy;
|
import git4idea.config.GitSaveChangesPolicy;
|
||||||
@@ -106,13 +103,7 @@ public class GitRebaseProcess {
|
|||||||
myProgressManager = ProgressManager.getInstance();
|
myProgressManager = ProgressManager.getInstance();
|
||||||
myDirtyScopeManager = VcsDirtyScopeManager.getInstance(myProject);
|
myDirtyScopeManager = VcsDirtyScopeManager.getInstance(myProject);
|
||||||
|
|
||||||
VIEW_STASH_ACTION = NotificationAction.createSimple(
|
VIEW_STASH_ACTION = new GitRestoreSavedChangesNotificationAction(mySaver);
|
||||||
mySaver.getSaveMethod().selectBundleMessage(
|
|
||||||
GitBundle.message("rebase.notification.action.view.stash.text"),
|
|
||||||
GitBundle.message("rebase.notification.action.view.shelf.text")
|
|
||||||
),
|
|
||||||
() -> mySaver.showSavedChanges()
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void rebase() {
|
public void rebase() {
|
||||||
|
|||||||
@@ -30,22 +30,25 @@ public final class LocalChangesWouldBeOverwrittenHelper {
|
|||||||
final Collection<String> absolutePaths = GitUtil.toAbsolute(root, relativeFilePaths);
|
final Collection<String> absolutePaths = GitUtil.toAbsolute(root, relativeFilePaths);
|
||||||
final List<Change> changes = GitUtil.findLocalChangesForPaths(project, root, absolutePaths, false);
|
final List<Change> changes = GitUtil.findLocalChangesForPaths(project, root, absolutePaths, false);
|
||||||
|
|
||||||
|
String description = getOverwrittenByMergeMessage();
|
||||||
VcsNotifier.importantNotification()
|
VcsNotifier.importantNotification()
|
||||||
.createNotification(GitBundle.message("notification.title.git.operation.failed", StringUtil.capitalize(operationName)),
|
.createNotification(GitBundle.message("notification.title.git.operation.failed", StringUtil.capitalize(operationName)),
|
||||||
GitBundle.message(getOverwrittenByMergeMessage()),
|
description,
|
||||||
NotificationType.ERROR)
|
NotificationType.ERROR)
|
||||||
.setDisplayId(displayId)
|
.setDisplayId(displayId)
|
||||||
.addAction(NotificationAction.createSimple(
|
.addAction(NotificationAction.createSimple(
|
||||||
GitBundle.messagePointer("local.changes.would.be.overwritten.by.merge.view.them.action"), () -> {
|
GitBundle.messagePointer("local.changes.would.be.overwritten.by.merge.view.them.action"), () -> {
|
||||||
showErrorDialog(project, operationName, changes, absolutePaths);
|
showErrorDialog(project, operationName, description, changes, absolutePaths);
|
||||||
}))
|
}))
|
||||||
.notify(project);
|
.notify(project);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void showErrorDialog(@NotNull Project project, @NotNull String operationName, @NotNull List<? extends Change> changes,
|
public static void showErrorDialog(@NotNull Project project,
|
||||||
@NotNull Collection<String> absolutePaths) {
|
@NotNull String operationName,
|
||||||
|
@Nls String description,
|
||||||
|
@NotNull List<? extends Change> changes,
|
||||||
|
@NotNull Collection<String> absolutePaths) {
|
||||||
String title = GitBundle.message("dialog.title.local.changes.prevent.from.operation", StringUtil.capitalize(operationName));
|
String title = GitBundle.message("dialog.title.local.changes.prevent.from.operation", StringUtil.capitalize(operationName));
|
||||||
String description = GitBundle.message(getOverwrittenByMergeMessage());
|
|
||||||
if (changes.isEmpty()) {
|
if (changes.isEmpty()) {
|
||||||
GitUtil.showPathsInDialog(project, absolutePaths, title, description);
|
GitUtil.showPathsInDialog(project, absolutePaths, title, description);
|
||||||
}
|
}
|
||||||
@@ -53,7 +56,9 @@ public final class LocalChangesWouldBeOverwrittenHelper {
|
|||||||
ChangesBrowserWithRollback changesViewer = new ChangesBrowserWithRollback(project, changes);
|
ChangesBrowserWithRollback changesViewer = new ChangesBrowserWithRollback(project, changes);
|
||||||
|
|
||||||
DialogBuilder builder = new DialogBuilder(project);
|
DialogBuilder builder = new DialogBuilder(project);
|
||||||
builder.setNorthPanel(new MultiLineLabel(description));
|
if (description != null) {
|
||||||
|
builder.setNorthPanel(new MultiLineLabel(description));
|
||||||
|
}
|
||||||
builder.setCenterPanel(changesViewer);
|
builder.setCenterPanel(changesViewer);
|
||||||
builder.addDisposable(changesViewer);
|
builder.addDisposable(changesViewer);
|
||||||
builder.addOkAction();
|
builder.addOkAction();
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
// Copyright 2000-2019 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.
|
// Copyright 2000-2019 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 git4idea.cherrypick
|
package git4idea.cherrypick
|
||||||
|
|
||||||
|
import com.intellij.ide.IdeBundle
|
||||||
import git4idea.i18n.GitBundle
|
import git4idea.i18n.GitBundle
|
||||||
import git4idea.test.*
|
import git4idea.test.*
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
@@ -194,4 +195,18 @@ class GitCherryPickAutoCommitTest(private val createChangelistAutomatically: Boo
|
|||||||
${shortHash(commit3)} fix #2
|
${shortHash(commit3)} fix #2
|
||||||
${shortHash(emptyCommit)} was skipped, because all changes have already been applied.""")
|
${shortHash(emptyCommit)} was skipped, because all changes have already been applied.""")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `staged changes prevent cherry-pick`() {
|
||||||
|
val commit = file("a.txt").create().addCommit("fix #1").hash()
|
||||||
|
file("b.txt").create().add()
|
||||||
|
|
||||||
|
cherryPick(commit, expectSuccess = false)
|
||||||
|
|
||||||
|
assertErrorNotification("Cherry-pick failed",
|
||||||
|
GitBundle.message("warning.your.local.changes.would.be.overwritten.by", "cherry-pick", "shelve"),
|
||||||
|
listOf(IdeBundle.message("action.show.files"),
|
||||||
|
GitBundle.message("apply.changes.save.and.retry.operation", "Shelve"))
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -15,7 +15,10 @@
|
|||||||
*/
|
*/
|
||||||
package git4idea.cherrypick
|
package git4idea.cherrypick
|
||||||
|
|
||||||
|
import com.intellij.ide.IdeBundle
|
||||||
import com.intellij.openapi.vcs.VcsApplicationSettings
|
import com.intellij.openapi.vcs.VcsApplicationSettings
|
||||||
|
import com.intellij.util.ui.Html
|
||||||
|
import com.intellij.util.ui.UIUtil
|
||||||
import com.intellij.vcs.log.impl.HashImpl
|
import com.intellij.vcs.log.impl.HashImpl
|
||||||
import git4idea.i18n.GitBundle
|
import git4idea.i18n.GitBundle
|
||||||
import git4idea.test.*
|
import git4idea.test.*
|
||||||
@@ -38,9 +41,12 @@ abstract class GitCherryPickTest : GitSingleRepoTest() {
|
|||||||
|
|
||||||
cherryPick(commit, expectSuccess = false)
|
cherryPick(commit, expectSuccess = false)
|
||||||
|
|
||||||
assertErrorNotification("Cherry-pick failed", """
|
assertErrorNotification("Cherry-pick failed",
|
||||||
${shortHash(commit)} fix #1
|
"${shortHash(commit)} fix #1" +
|
||||||
""" + GitBundle.message("warning.your.local.changes.would.be.overwritten.by", "cherry-pick", "shelve"))
|
UIUtil.BR +
|
||||||
|
GitBundle.message("warning.your.local.changes.would.be.overwritten.by", "cherry-pick", "shelve"),
|
||||||
|
listOf(IdeBundle.message("action.show.files"),
|
||||||
|
GitBundle.message("apply.changes.save.and.retry.operation", "Shelve")))
|
||||||
}
|
}
|
||||||
|
|
||||||
protected fun `check untracked file conflicting with commit`() {
|
protected fun `check untracked file conflicting with commit`() {
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
*/
|
*/
|
||||||
package git4idea.revert
|
package git4idea.revert
|
||||||
|
|
||||||
|
import com.intellij.ide.IdeBundle
|
||||||
import com.intellij.openapi.vcs.VcsApplicationSettings
|
import com.intellij.openapi.vcs.VcsApplicationSettings
|
||||||
import com.intellij.openapi.vcs.changes.Change
|
import com.intellij.openapi.vcs.changes.Change
|
||||||
import com.intellij.vcs.log.VcsFullCommitDetails
|
import com.intellij.vcs.log.VcsFullCommitDetails
|
||||||
@@ -25,6 +26,7 @@ import git4idea.GitRevisionNumber
|
|||||||
import git4idea.history.GitHistoryUtils
|
import git4idea.history.GitHistoryUtils
|
||||||
import git4idea.i18n.GitBundle
|
import git4idea.i18n.GitBundle
|
||||||
import git4idea.test.*
|
import git4idea.test.*
|
||||||
|
import org.junit.Test
|
||||||
import java.nio.charset.Charset
|
import java.nio.charset.Charset
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -259,6 +261,19 @@ class GitRevertTest : GitSingleRepoTest() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun `test staged changes prevent revert with auto-commit`() {
|
||||||
|
val commit = file("a.txt").create().addCommit("fix #1").details()
|
||||||
|
file("c.txt").create().add()
|
||||||
|
|
||||||
|
revertAutoCommit(commit)
|
||||||
|
|
||||||
|
assertErrorNotification("Revert failed",
|
||||||
|
GitBundle.message("warning.your.local.changes.would.be.overwritten.by", "revert", "shelve"),
|
||||||
|
listOf(IdeBundle.message("action.show.files"),
|
||||||
|
GitBundle.message("apply.changes.save.and.retry.operation", "Shelve"))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
private fun commitMessageForRevert(commit: VcsFullCommitDetails): String {
|
private fun commitMessageForRevert(commit: VcsFullCommitDetails): String {
|
||||||
return """
|
return """
|
||||||
Revert "${commit.subject}"
|
Revert "${commit.subject}"
|
||||||
|
|||||||
Reference in New Issue
Block a user