From 4c6ae376c96687335a0959a232b0efe05fc25c7e Mon Sep 17 00:00:00 2001 From: Kirill Likhodedov Date: Wed, 5 Dec 2012 15:20:30 +0400 Subject: [PATCH] [git] Cherry pick: use the common commit dialog. Instead of using a custom commit executor & custom commit session, use the common commit dialog. The problem is that the commit dialog "onOK"-procedure is asynchronous. To fix this provide a CommitResultHandler that is called on successful or failed commit, and wait (in the GitCherryPicker) until it reports that commit has completed. Don't show notification or report error during commit if the CommitResultHandler is set. This fixes IDEA-84825, IDEA-88236 and is necessary for IDEA-87476. --- .../com/intellij/dvcs/test/MockVcsHelper.java | 6 +- .../openapi/vcs/AbstractVcsHelper.java | 14 +- .../vcs/changes/CommitResultHandler.java | 35 ++++ .../vcs/changes/ChangeListManagerImpl.java | 2 +- .../changes/ui/CommitChangeListDialog.java | 32 +-- .../openapi/vcs/changes/ui/CommitHelper.java | 68 ++++--- .../vcs/impl/AbstractVcsHelperImpl.java | 13 +- .../history/browser/GitCherryPicker.java | 187 +++--------------- .../zmlx/hg4idea/test/HgMockVcsHelper.java | 6 +- 9 files changed, 150 insertions(+), 213 deletions(-) create mode 100644 platform/vcs-api/src/com/intellij/openapi/vcs/changes/CommitResultHandler.java diff --git a/platform/dvcs/testFramework/com/intellij/dvcs/test/MockVcsHelper.java b/platform/dvcs/testFramework/com/intellij/dvcs/test/MockVcsHelper.java index 7cf6b346d536..e14bd69fe468 100644 --- a/platform/dvcs/testFramework/com/intellij/dvcs/test/MockVcsHelper.java +++ b/platform/dvcs/testFramework/com/intellij/dvcs/test/MockVcsHelper.java @@ -20,7 +20,7 @@ import com.intellij.openapi.vcs.*; import com.intellij.openapi.vcs.annotate.AnnotationProvider; import com.intellij.openapi.vcs.annotate.FileAnnotation; import com.intellij.openapi.vcs.changes.Change; -import com.intellij.openapi.vcs.changes.CommitExecutor; +import com.intellij.openapi.vcs.changes.CommitResultHandler; import com.intellij.openapi.vcs.changes.LocalChangeList; import com.intellij.openapi.vcs.history.VcsFileRevision; import com.intellij.openapi.vcs.history.VcsHistoryProvider; @@ -185,8 +185,8 @@ public class MockVcsHelper extends AbstractVcsHelper { } @Override - public boolean commitChanges(@NotNull List changes, @NotNull LocalChangeList initialChangeList, - @NotNull String commitMessage, @Nullable CommitExecutor executor) { + public boolean commitChanges(@NotNull Collection changes, @NotNull LocalChangeList initialChangeList, + @NotNull String commitMessage, @Nullable CommitResultHandler customResultHandler) { if (myCommitHandler != null) { return myCommitHandler.commit(commitMessage); } diff --git a/platform/vcs-api/src/com/intellij/openapi/vcs/AbstractVcsHelper.java b/platform/vcs-api/src/com/intellij/openapi/vcs/AbstractVcsHelper.java index fbdc14b28527..b90eee515c20 100644 --- a/platform/vcs-api/src/com/intellij/openapi/vcs/AbstractVcsHelper.java +++ b/platform/vcs-api/src/com/intellij/openapi/vcs/AbstractVcsHelper.java @@ -21,7 +21,7 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.vcs.annotate.AnnotationProvider; import com.intellij.openapi.vcs.annotate.FileAnnotation; import com.intellij.openapi.vcs.changes.Change; -import com.intellij.openapi.vcs.changes.CommitExecutor; +import com.intellij.openapi.vcs.changes.CommitResultHandler; import com.intellij.openapi.vcs.changes.LocalChangeList; import com.intellij.openapi.vcs.history.VcsFileRevision; import com.intellij.openapi.vcs.history.VcsHistoryProvider; @@ -167,11 +167,11 @@ public abstract class AbstractVcsHelper { /** - * Shows commit dialog, fills it with the given changes and given commit message, initially selects the given changelist. - * @return true if user commits the changes, and commit succeeds; false if user presses Cancel or commit fails with errors. + *

Shows commit dialog, fills it with the given changes and given commit message, initially selects the given changelist.

+ *

Note that the method is asynchronous: it returns right after user presses "Commit" or "Cancel" and after all pre-commit handlers + * have been called. It doesn't wait for commit itself to succeed or fail - for this use the {@code customResultHandler}.

+ * @return true if user decides to commit the changes, false if user presses Cancel. */ - public abstract boolean commitChanges(@NotNull List changes, @NotNull LocalChangeList initialChangeList, - @NotNull String commitMessage, @Nullable CommitExecutor executor); - - + public abstract boolean commitChanges(@NotNull Collection changes, @NotNull LocalChangeList initialChangeList, + @NotNull String commitMessage, @Nullable CommitResultHandler customResultHandler); } diff --git a/platform/vcs-api/src/com/intellij/openapi/vcs/changes/CommitResultHandler.java b/platform/vcs-api/src/com/intellij/openapi/vcs/changes/CommitResultHandler.java new file mode 100644 index 000000000000..aaf7575de301 --- /dev/null +++ b/platform/vcs-api/src/com/intellij/openapi/vcs/changes/CommitResultHandler.java @@ -0,0 +1,35 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.openapi.vcs.changes; + +import com.intellij.openapi.vcs.AbstractVcsHelper; +import org.jetbrains.annotations.NotNull; + +import java.util.Collection; + +/** + * CommitResultHandler may be passed to {@link AbstractVcsHelper#commitChanges(Collection, LocalChangeList, String, CommitResultHandler)}. + * It is called after commit is performed: successful or failed. + * + * @author Kirill Likhodedov + */ +public interface CommitResultHandler { + + void onSuccess(@NotNull String commitMessage); + + void onFailure(); + +} diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ChangeListManagerImpl.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ChangeListManagerImpl.java index eb615cd57a33..232301901c35 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ChangeListManagerImpl.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ChangeListManagerImpl.java @@ -1158,7 +1158,7 @@ public class ChangeListManagerImpl extends ChangeListManagerEx implements Projec private boolean doCommit(final LocalChangeList changeList, final List changes, final boolean synchronously) { return new CommitHelper(myProject, changeList, changes, changeList.getName(), - changeList.getComment(), new ArrayList(), false, synchronously, NullableFunction.NULL).doCommit(); + changeList.getComment(), new ArrayList(), false, synchronously, NullableFunction.NULL, null).doCommit(); } public void commitChangesSynchronously(LocalChangeList changeList, List changes) { diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/CommitChangeListDialog.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/CommitChangeListDialog.java index 065b05204d95..561ec3170e12 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/CommitChangeListDialog.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/CommitChangeListDialog.java @@ -111,6 +111,7 @@ public class CommitChangeListDialog extends DialogWrapper implements CheckinProj private final ZipperUpdater myZipperUpdater; private final Runnable myRefreshDetails; private CommitAction myCommitAction; + @Nullable private CommitResultHandler myResultHandler; private static class MyUpdateButtonsRunnable implements Runnable { private CommitChangeListDialog myDialog; @@ -139,7 +140,8 @@ public class CommitChangeListDialog extends DialogWrapper implements CheckinProj private final MyUpdateButtonsRunnable myUpdateButtonsRunnable = new MyUpdateButtonsRunnable(this); private static boolean commit(final Project project, final List changes, final LocalChangeList initialSelection, - final List executors, final boolean showVcsCommit, final String comment) { + final List executors, final boolean showVcsCommit, final String comment, + @Nullable CommitResultHandler customResultHandler) { final AbstractVcs[] allActiveVcss = ProjectLevelVcsManager.getInstance(project).getAllActiveVcss(); final List factoryList = CheckinHandlersManager.getInstance().getMatchingVcsFactories(Arrays.asList(allActiveVcss)); @@ -155,7 +157,7 @@ public class CommitChangeListDialog extends DialogWrapper implements CheckinProj final ArrayList changeLists = new ArrayList(manager.getChangeListsCopy()); CommitChangeListDialog dialog = new CommitChangeListDialog(project, changes, initialSelection, executors, showVcsCommit, defaultList, changeLists, null, false, - comment); + comment, customResultHandler); if (!ApplicationManager.getApplication().isUnitTestMode()) { dialog.show(); } @@ -179,10 +181,10 @@ public class CommitChangeListDialog extends DialogWrapper implements CheckinProj public static boolean commitChanges(final Project project, final Collection changes, final LocalChangeList initialSelection, @Nullable final CommitExecutor executor, final String comment) { if (executor == null) { - return commitChanges(project, changes, initialSelection, collectExecutors(project, changes), true, comment); + return commitChanges(project, changes, initialSelection, collectExecutors(project, changes), true, comment, null); } else { - return commitChanges(project, changes, initialSelection, Collections.singletonList(executor), false, comment); + return commitChanges(project, changes, initialSelection, Collections.singletonList(executor), false, comment, null); } } @@ -197,22 +199,29 @@ public class CommitChangeListDialog extends DialogWrapper implements CheckinProj return result; } + /** + * Shows the commit dialog, and performs the selected action: commit, commit & push, create patch, etc. + * @param customResultHandler If this is not null, after commit is completed, custom result handler is called instead of + * showing the default notification in case of commit or failure. + * @return true if user agreed to commit, false if he pressed "Cancel". + */ public static boolean commitChanges(final Project project, final Collection changes, final LocalChangeList initialSelection, - final List executors, final boolean showVcsCommit, final String comment) { + final List executors, final boolean showVcsCommit, final String comment, + @Nullable CommitResultHandler customResultHandler) { if (changes.isEmpty() && !ApplicationManager.getApplication().isUnitTestMode()) { Messages.showInfoMessage(project, VcsBundle.message("commit.dialog.no.changes.detected.text") , VcsBundle.message("commit.dialog.no.changes.detected.title")); return false; } - return commit(project, new ArrayList(changes), initialSelection, executors, showVcsCommit, comment); + return commit(project, new ArrayList(changes), initialSelection, executors, showVcsCommit, comment, customResultHandler); } public static void commitAlienChanges(final Project project, final List changes, final AbstractVcs vcs, final String changelistName, final String comment) { final LocalChangeList lcl = new AlienLocalChangeList(changes, changelistName); new CommitChangeListDialog(project, changes, null, null, true, AlienLocalChangeList.DEFAULT_ALIEN, Collections.singletonList(lcl), vcs, - true, comment).show(); + true, comment, null).show(); } private CommitChangeListDialog(final Project project, @@ -221,13 +230,14 @@ public class CommitChangeListDialog extends DialogWrapper implements CheckinProj final List executors, final boolean showVcsCommit, final LocalChangeList defaultChangeList, final List changeLists, final AbstractVcs singleVcs, final boolean isAlien, - final String comment) { + final String comment, @Nullable CommitResultHandler customResultHandler) { super(project, true); myCommitContext = new CommitContext(); myProject = project; myExecutors = executors; myShowVcsCommit = showVcsCommit; myVcs = singleVcs; + myResultHandler = customResultHandler; myListComments = new HashMap(); myAdditionalData = new PseudoMap(); myDiffDetails = new ShortDiffDetails(myProject, new Getter() { @@ -570,7 +580,7 @@ public class CommitChangeListDialog extends DialogWrapper implements CheckinProj runBeforeCommitHandlers(new Runnable() { public void run() { CommitChangeListDialog.super.doOKAction(); - doCommit(); + doCommit(myResultHandler); } }, null); @@ -925,7 +935,7 @@ public class CommitChangeListDialog extends DialogWrapper implements CheckinProj super.doCancelAction(); } - private void doCommit() { + private void doCommit(@Nullable CommitResultHandler customResultHandler) { final CommitHelper helper = new CommitHelper( myProject, myBrowser.getSelectedChangeList(), @@ -933,7 +943,7 @@ public class CommitChangeListDialog extends DialogWrapper implements CheckinProj myActionName, getCommitMessage(), myHandlers, - myAllOfDefaultChangeListChangesIncluded, false, myAdditionalData); + myAllOfDefaultChangeListChangesIncluded, false, myAdditionalData, customResultHandler); if (myIsAlien) { helper.doAlienCommit(myVcs); diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/CommitHelper.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/CommitHelper.java index e84293f2bbee..df86e92215db 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/CommitHelper.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/CommitHelper.java @@ -44,6 +44,7 @@ import com.intellij.util.NullableFunction; import com.intellij.util.WaitForProgressToShow; import com.intellij.util.ui.ConfirmationDialog; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.util.ArrayList; import java.util.Collection; @@ -66,6 +67,7 @@ public class CommitHelper { private final boolean myAllOfDefaultChangeListChangesIncluded; private final boolean myForceSyncCommit; private final NullableFunction myAdditionalData; + @Nullable private final CommitResultHandler myCustomResultHandler; private final List myCommittingDocuments = new ArrayList(); private final VcsConfiguration myConfiguration; private final VcsDirtyScopeManager myDirtyScopeManager; @@ -78,7 +80,8 @@ public class CommitHelper { final String commitMessage, final List handlers, final boolean allOfDefaultChangeListChangesIncluded, - final boolean synchronously, final NullableFunction additionalDataHolder) { + final boolean synchronously, final NullableFunction additionalDataHolder, + @Nullable CommitResultHandler customResultHandler) { myProject = project; myChangeList = changeList; myIncludedChanges = includedChanges; @@ -88,6 +91,7 @@ public class CommitHelper { myAllOfDefaultChangeListChangesIncluded = allOfDefaultChangeListChangesIncluded; myForceSyncCommit = synchronously; myAdditionalData = additionalDataHolder; + myCustomResultHandler = customResultHandler; myConfiguration = VcsConfiguration.getInstance(myProject); myDirtyScopeManager = VcsDirtyScopeManager.getInstance(myProject); myFeedback = new HashSet(); @@ -133,8 +137,11 @@ public class CommitHelper { @Override public NotificationInfo notifyFinished() { - String text = reportSuccess(processor); - return new NotificationInfo("VCS Commit", "VCS Commit Finished", text, true); + if (myCustomResultHandler == null) { + String text = reportSuccess(processor); + return new NotificationInfo("VCS Commit", "VCS Commit Finished", text, true); + } + return null; } }; ProgressManager.getInstance().run(task); @@ -511,6 +518,9 @@ public class CommitHelper { } processor.afterSuccessfulCheckIn(); + if (myCustomResultHandler != null) { + myCustomResultHandler.onSuccess(myCommitMessage); + } } else { for (CheckinHandler handler : myHandlers) { @@ -523,28 +533,38 @@ public class CommitHelper { if (indicator != null) { indicator.setText(VcsBundle.message("commit.dialog.completed.successfully")); } - } else { - WaitForProgressToShow.runOrInvokeLaterAboveProgress(new Runnable() { - public void run() { - final String message; - if (errorsSize > 0 && warningsSize > 0) { - message = VcsBundle.message("message.text.commit.failed.with.errors.and.warnings"); - } - else if (errorsSize > 0) { - message = VcsBundle.message("message.text.commit.failed.with.errors"); - } - else { - message = VcsBundle.message("message.text.commit.finished.with.warnings"); - } - //new VcsBalloonProblemNotifier(myProject, message, MessageType.ERROR).run(); - Messages.showErrorDialog(message, VcsBundle.message("message.title.commit")); - - if (errorsSize > 0) { - processor.afterFailedCheckIn(); - } - } - }, null, myProject); } + else { + if (myCustomResultHandler == null) { + showErrorDialogAndMoveToAnotherList(processor, errorsSize, warningsSize); + } + else { + myCustomResultHandler.onFailure(); + } + } + } + + private void showErrorDialogAndMoveToAnotherList(final GeneralCommitProcessor processor, final int errorsSize, final int warningsSize) { + WaitForProgressToShow.runOrInvokeLaterAboveProgress(new Runnable() { + public void run() { + final String message; + if (errorsSize > 0 && warningsSize > 0) { + message = VcsBundle.message("message.text.commit.failed.with.errors.and.warnings"); + } + else if (errorsSize > 0) { + message = VcsBundle.message("message.text.commit.failed.with.errors"); + } + else { + message = VcsBundle.message("message.text.commit.finished.with.warnings"); + } + //new VcsBalloonProblemNotifier(myProject, message, MessageType.ERROR).run(); + Messages.showErrorDialog(message, VcsBundle.message("message.title.commit")); + + if (errorsSize > 0) { + processor.afterFailedCheckIn(); + } + } + }, null, myProject); } public static void moveToFailedList(final ChangeList changeList, diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/impl/AbstractVcsHelperImpl.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/impl/AbstractVcsHelperImpl.java index 1cbbae8458f8..2ee68e92031c 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/impl/AbstractVcsHelperImpl.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/impl/AbstractVcsHelperImpl.java @@ -47,10 +47,7 @@ import com.intellij.openapi.vcs.*; import com.intellij.openapi.vcs.actions.AnnotateToggleAction; import com.intellij.openapi.vcs.annotate.AnnotationProvider; import com.intellij.openapi.vcs.annotate.FileAnnotation; -import com.intellij.openapi.vcs.changes.BackgroundFromStartOption; -import com.intellij.openapi.vcs.changes.Change; -import com.intellij.openapi.vcs.changes.CommitExecutor; -import com.intellij.openapi.vcs.changes.LocalChangeList; +import com.intellij.openapi.vcs.changes.*; import com.intellij.openapi.vcs.changes.committed.*; import com.intellij.openapi.vcs.changes.ui.*; import com.intellij.openapi.vcs.history.*; @@ -225,9 +222,11 @@ public class AbstractVcsHelperImpl extends AbstractVcsHelper { } @Override - public boolean commitChanges(@NotNull List changes, @NotNull LocalChangeList initialChangeList, - @NotNull String commitMessage, @Nullable CommitExecutor executor) { - return CommitChangeListDialog.commitChanges(myProject, changes, initialChangeList, executor, commitMessage); + public boolean commitChanges(@NotNull Collection changes, @NotNull LocalChangeList initialChangeList, + @NotNull String commitMessage, @Nullable CommitResultHandler customResultHandler) { + return CommitChangeListDialog.commitChanges(myProject, changes, initialChangeList, + CommitChangeListDialog.collectExecutors(myProject, changes), true, commitMessage, + customResultHandler); } private static void addDirectMessages(VcsErrorViewPanel vcsErrorViewPanel, List abstractVcsExceptions) { diff --git a/plugins/git4idea/src/git4idea/history/browser/GitCherryPicker.java b/plugins/git4idea/src/git4idea/history/browser/GitCherryPicker.java index c13413180501..66728c5a2354 100644 --- a/plugins/git4idea/src/git4idea/history/browser/GitCherryPicker.java +++ b/plugins/git4idea/src/git4idea/history/browser/GitCherryPicker.java @@ -18,24 +18,17 @@ package git4idea.history.browser; import com.intellij.notification.Notification; import com.intellij.notification.NotificationListener; import com.intellij.openapi.application.ModalityState; -import com.intellij.openapi.components.ServiceManager; import com.intellij.openapi.diagnostic.Logger; -import com.intellij.openapi.editor.Document; import com.intellij.openapi.project.Project; -import com.intellij.openapi.ui.Messages; import com.intellij.openapi.vcs.FilePath; -import com.intellij.openapi.vcs.VcsException; import com.intellij.openapi.vcs.changes.*; -import com.intellij.openapi.vcs.changes.ui.CommitHelper; import com.intellij.openapi.vcs.checkin.CheckinEnvironment; import com.intellij.openapi.vcs.history.VcsRevisionNumber; import com.intellij.openapi.vcs.merge.MergeDialogCustomizer; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.ArrayUtil; import com.intellij.util.Consumer; -import com.intellij.util.WaitForProgressToShow; import git4idea.GitPlatformFacade; -import git4idea.checkin.GitCheckinEnvironment; import git4idea.commands.Git; import git4idea.commands.GitCommandResult; import git4idea.commands.GitSimpleEventDetector; @@ -43,15 +36,13 @@ import git4idea.commands.GitUntrackedFilesOverwrittenByOperationDetector; import git4idea.merge.GitConflictResolver; import git4idea.repo.GitRepository; import git4idea.util.UntrackedFilesNotifier; -import org.jetbrains.annotations.Nls; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import javax.swing.*; import javax.swing.event.HyperlinkEvent; import java.io.File; import java.io.IOException; import java.util.*; +import java.util.concurrent.Semaphore; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; @@ -246,26 +237,44 @@ public class GitCherryPicker { private boolean showCommitDialog(@NotNull final GitRepository repository, @NotNull final GitCommitWrapper commit, @NotNull final LocalChangeList changeList, @NotNull final String commitMessage) { final AtomicBoolean commitSucceeded = new AtomicBoolean(); + final Semaphore sem = new Semaphore(0); myPlatformFacade.invokeAndWait(new Runnable() { @Override public void run() { cancelCherryPick(repository); List changes = commit.getCommit().getChanges(); - CherryPickCommitExecutor executor = new CherryPickCommitExecutor(myProject, myPlatformFacade, changes, commitMessage); - boolean commitNotCancelled = myPlatformFacade.getVcsHelper(myProject).commitChanges(changes, changeList, commitMessage, executor); - boolean success = commitNotCancelled && !executor.hasCommitFailed(); - if (success) { - commit.setActualSubject(getSubjectFromCommitMessage(executor.getActualCommitMessage())); + boolean commitNotCancelled = myPlatformFacade.getVcsHelper(myProject).commitChanges(changes, changeList, commitMessage, + new CommitResultHandler() { + @Override + public void onSuccess(@NotNull String commitMessage) { + commit.setActualSubject(commitMessage); + commitSucceeded.set(true); + sem.release(); + } + + @Override + public void onFailure() { + commitSucceeded.set(false); + sem.release(); + } + }); + + if (!commitNotCancelled) { + sem.release(); + commitSucceeded.set(false); } - commitSucceeded.set(success); } }, ModalityState.NON_MODAL); - return commitSucceeded.get(); - } - private static String getSubjectFromCommitMessage(String commitMessage) { - int newLine = commitMessage.indexOf("\n"); - return newLine < 0 ? commitMessage : commitMessage.substring(0, newLine); + // need additional waiting, because commitChanges is asynchronous + try { + sem.acquire(); + } + catch (InterruptedException e) { + LOG.error(e); + return false; + } + return commitSucceeded.get(); } /** @@ -451,142 +460,6 @@ public class GitCherryPicker { } } - /* - Commit procedure is overridden by the executor with its own CommitSession. - The reason of that is the asynchronous nature of the CommitHelper: it returns, we continue cherry-picking and occasionally pick - the next commit in the queue, and only then Git is called for commit. Thus it commits two cherry-picks at once, which is wrong. - - Here we call GitCheckinEnvironment manually - */ - private static class CherryPickCommitExecutor implements CommitExecutor { - - @NotNull private final Project myProject; - @NotNull private final GitPlatformFacade myPlatformFacade; - @NotNull private final List myChanges; - @NotNull private final String myOriginalCommitMessage; - private boolean myCommitFailed; - - @Nullable private CherryPickCommitExecutor.CherryPickCommitSession myCommitSession; - - CherryPickCommitExecutor(@NotNull Project project, @NotNull GitPlatformFacade platformFacade, - @NotNull List changes, @NotNull String originalCommitMessage) { - myProject = project; - myPlatformFacade = platformFacade; - myChanges = changes; - myOriginalCommitMessage = originalCommitMessage; - } - - @Nls - @Override - public String getActionText() { - return "Commit"; - } - - @NotNull - @Override - public CommitSession createCommitSession() { - myCommitSession = new CherryPickCommitSession(); - return myCommitSession; - } - - public boolean hasCommitFailed() { - return myCommitFailed; - } - - @NotNull - public String getActualCommitMessage() { - return myCommitSession == null ? myOriginalCommitMessage : myCommitSession.getActualCommitMessage(); - } - - private class CherryPickCommitSession implements CommitSession { - private String myActualCommitMessage; - - @Override - public JComponent getAdditionalConfigurationUI() { - return null; - } - - @Override - public JComponent getAdditionalConfigurationUI(Collection changes, String commitMessage) { - return null; - } - - @Override - public boolean canExecute(Collection changes, String commitMessage) { - return true; - } - - @Override - public void execute(Collection changes, String commitMessage) { - final Collection committingDocs = markCommittingDocs(); - try { - GitCheckinEnvironment ce = ServiceManager.getService(myProject, GitCheckinEnvironment.class); - try { - ce.reset(); - List exceptions = ce.commit(myChanges, commitMessage); - VcsDirtyScopeManager.getInstance(myProject).filePathsDirty(ChangesUtil.getPaths(myChanges), null); - if (exceptions != null && !exceptions.isEmpty()) { - VcsException exception = exceptions.get(0); - handleError(exception); - } - myActualCommitMessage = commitMessage; - } - catch (Throwable e) { - LOG.error(e); - handleError(e); - } - } - finally { - unmarkCommittingDocs(committingDocs); - } - } - - private void handleError(Throwable exception) { - myCommitFailed = true; - final String errorMessage = exception.getMessage(); - WaitForProgressToShow.runOrInvokeLaterAboveProgress(new Runnable() { - public void run() { - Messages.showErrorDialog(myProject, errorMessage, "Commit Failed"); - } - }, null, myProject); - } - - @Override - public void executionCanceled() { - } - - @Override - public String getHelpId() { - return null; - } - - private void unmarkCommittingDocs(final Collection committingDocs) { - myPlatformFacade.runReadAction(new Runnable() { - @Override - public void run() { - CommitHelper.unmarkCommittingDocuments(committingDocs); - } - }); - } - - @NotNull - private Collection markCommittingDocs() { - final Collection committingDocs = new ArrayList(); - myPlatformFacade.runReadAction(new Runnable() { - @Override - public void run() { - committingDocs.addAll(CommitHelper.markCommittingDocuments(myProject, myChanges)); - } - }); - return committingDocs; - } - - public String getActualCommitMessage() { - return myActualCommitMessage; - } - } - } - /** * This class is needed to hold both the original GitCommit, and the commit message which could be changed by the user. * Only the subject of the commit message is needed. diff --git a/plugins/hg4idea/testSrc/org/zmlx/hg4idea/test/HgMockVcsHelper.java b/plugins/hg4idea/testSrc/org/zmlx/hg4idea/test/HgMockVcsHelper.java index 4c75d27c207c..c8ffe9e6b71d 100644 --- a/plugins/hg4idea/testSrc/org/zmlx/hg4idea/test/HgMockVcsHelper.java +++ b/plugins/hg4idea/testSrc/org/zmlx/hg4idea/test/HgMockVcsHelper.java @@ -20,7 +20,7 @@ import com.intellij.openapi.vcs.*; import com.intellij.openapi.vcs.annotate.AnnotationProvider; import com.intellij.openapi.vcs.annotate.FileAnnotation; import com.intellij.openapi.vcs.changes.Change; -import com.intellij.openapi.vcs.changes.CommitExecutor; +import com.intellij.openapi.vcs.changes.CommitResultHandler; import com.intellij.openapi.vcs.changes.LocalChangeList; import com.intellij.openapi.vcs.history.VcsFileRevision; import com.intellij.openapi.vcs.history.VcsHistoryProvider; @@ -171,8 +171,8 @@ public class HgMockVcsHelper extends AbstractVcsHelper { } @Override - public boolean commitChanges(@NotNull List changes, @NotNull LocalChangeList initialChangeList, - @NotNull String commitMessage, @Nullable CommitExecutor executor) { + public boolean commitChanges(@NotNull Collection changes, @NotNull LocalChangeList initialChangeList, + @NotNull String commitMessage, @Nullable CommitResultHandler customResultHandler) { throw new UnsupportedOperationException(); }