diff --git a/plugins/git4idea/src/git4idea/stash/GitStashChangesSaver.java b/plugins/git4idea/src/git4idea/stash/GitStashChangesSaver.java index b191394a758c..70928b08624a 100644 --- a/plugins/git4idea/src/git4idea/stash/GitStashChangesSaver.java +++ b/plugins/git4idea/src/git4idea/stash/GitStashChangesSaver.java @@ -25,19 +25,14 @@ import com.intellij.openapi.vcs.VcsException; import com.intellij.openapi.vcs.VcsNotifier; import com.intellij.openapi.vcs.history.VcsRevisionNumber; import com.intellij.openapi.vcs.merge.MergeDialogCustomizer; -import com.intellij.openapi.vfs.VfsUtil; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.containers.ContainerUtil; import git4idea.GitUtil; -import git4idea.commands.Git; -import git4idea.commands.GitCommandResult; -import git4idea.commands.GitHandlerUtil; -import git4idea.commands.GitSimpleEventDetector; +import git4idea.commands.*; import git4idea.merge.GitConflictResolver; import git4idea.repo.GitRepository; import git4idea.repo.GitRepositoryManager; import git4idea.ui.GitUnstashDialog; -import git4idea.util.GitUIUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -101,12 +96,11 @@ public class GitStashChangesSaver extends GitChangesSaver { @Override public void load() { - for (VirtualFile root : myStashedRoots) { - loadRoot(root); - } - - boolean conflictsResolved = new UnstashConflictResolver(myProject, myGit, myStashedRoots, myParams).merge(); - LOG.info("load: conflicts resolved status is " + conflictsResolved + " in roots " + myStashedRoots); + GitStashUtils.unstash(myProject, myStashedRoots, (root) -> { + GitLineHandler handler = new GitLineHandler(myProject, root, GitCommand.STASH); + handler.addParameters("pop"); + return handler; + }, new UnstashConflictResolver(myProject, myGit, myStashedRoots, myParams)); } @Override @@ -130,36 +124,6 @@ public class GitStashChangesSaver extends GitChangesSaver { GitUnstashDialog.showUnstashDialog(myProject, new ArrayList<>(myStashedRoots), myStashedRoots.iterator().next()); } - /** - * Returns true if the root was loaded with conflict. - * False is returned in all other cases: in the case of success and in case of some other error. - */ - private boolean loadRoot(final VirtualFile root) { - LOG.info("loadRoot " + root); - myProgressIndicator.setText(GitHandlerUtil.formatOperationName("Unstashing changes to", root)); - - GitRepository repository = myRepositoryManager.getRepositoryForRoot(root); - if (repository == null) { - LOG.error("Repository is null for root " + root); - return false; - } - - GitSimpleEventDetector conflictDetector = new GitSimpleEventDetector(GitSimpleEventDetector.Event.MERGE_CONFLICT_ON_UNSTASH); - GitCommandResult result = myGit.stashPop(repository, conflictDetector); - VfsUtil.markDirtyAndRefresh(false, true, false, root); - if (result.success()) { - return false; - } - else if (conflictDetector.hasHappened()) { - return true; - } - else { - LOG.info("unstash failed " + result.getErrorOutputAsJoinedString()); - GitUIUtil.notifyImportantError(myProject, "Couldn't unstash", "
" + result.getErrorOutputAsHtmlString()); - return false; - } - } - @Override public String toString() { return "StashChangesSaver. Roots: " + myStashedRoots; diff --git a/plugins/git4idea/src/git4idea/stash/GitStashUtils.kt b/plugins/git4idea/src/git4idea/stash/GitStashUtils.kt index 7f907a5c201d..5047a192edf5 100644 --- a/plugins/git4idea/src/git4idea/stash/GitStashUtils.kt +++ b/plugins/git4idea/src/git4idea/stash/GitStashUtils.kt @@ -17,18 +17,79 @@ package git4idea.stash +import com.intellij.dvcs.DvcsUtil import com.intellij.openapi.project.Project import com.intellij.openapi.vcs.VcsException +import com.intellij.openapi.vcs.VcsNotifier +import com.intellij.openapi.vfs.VfsUtil import com.intellij.openapi.vfs.VirtualFile import com.intellij.util.Consumer -import git4idea.commands.GitCommand -import git4idea.commands.GitSimpleHandler +import git4idea.commands.* import git4idea.config.GitConfigUtil +import git4idea.merge.GitConflictResolver import git4idea.ui.StashInfo import git4idea.util.GitUIUtil +import git4idea.util.GitUntrackedFilesHelper +import git4idea.util.LocalChangesWouldBeOverwrittenHelper import git4idea.util.StringScanner import java.nio.charset.Charset +/** + * Unstash the given root, handling common error scenarios. + */ +fun unstash(project: Project, root: VirtualFile, handler: GitLineHandler, conflictResolver: GitConflictResolver) { + unstash(project, listOf(root), { handler }, conflictResolver) +} + +/** + * Unstash the given roots one by one, handling common error scenarios. + * + * If there's an error in one of the roots, stop and show the error. + * If there's a conflict, show the merge dialog, and if the conflicts get resolved, continue with other roots. + */ +fun unstash(project: Project, + roots: Collection, + handlerProvider: (VirtualFile) -> GitLineHandler, + conflictResolver: GitConflictResolver) { + val token = DvcsUtil.workingTreeChangeStarted(project) + try { + for (root in roots) { + val handler = handlerProvider(root) + + val conflictDetector = GitSimpleEventDetector(GitSimpleEventDetector.Event.MERGE_CONFLICT_ON_UNSTASH) + val untrackedFilesDetector = GitUntrackedFilesOverwrittenByOperationDetector(root) + val localChangesDetector = GitLocalChangesWouldBeOverwrittenDetector(root, GitLocalChangesWouldBeOverwrittenDetector.Operation.MERGE) + handler.addLineListener(conflictDetector) + handler.addLineListener(untrackedFilesDetector) + handler.addLineListener(localChangesDetector) + + val result = Git.getInstance().runCommand { handler } + + VfsUtil.markDirtyAndRefresh(false, true, false, root) + + if (conflictDetector.hasHappened()) { + val conflictsResolved = conflictResolver.merge() + if (!conflictsResolved) return + } + else if (untrackedFilesDetector.wasMessageDetected()) { + GitUntrackedFilesHelper.notifyUntrackedFilesOverwrittenBy(project, root, untrackedFilesDetector.relativeFilePaths, "unstash", null) + return + } + else if (localChangesDetector.wasMessageDetected()) { + LocalChangesWouldBeOverwrittenHelper.showErrorDialog(project, root, "unstash", localChangesDetector.relativeFilePaths) + return + } + else if (!result.success()) { + VcsNotifier.getInstance(project).notifyError("Unstash Failed", result.errorOutputAsHtmlString) + return + } + } + } + finally { + token.finish() + } +} + fun loadStashStack(project: Project, root: VirtualFile, consumer: Consumer) { loadStashStack(project, root, Charset.forName(GitConfigUtil.getLogEncoding(project, root)), consumer) } diff --git a/plugins/git4idea/src/git4idea/ui/GitUnstashDialog.java b/plugins/git4idea/src/git4idea/ui/GitUnstashDialog.java index 64c98d285659..923a66c65fec 100644 --- a/plugins/git4idea/src/git4idea/ui/GitUnstashDialog.java +++ b/plugins/git4idea/src/git4idea/ui/GitUnstashDialog.java @@ -16,10 +16,8 @@ package git4idea.ui; import com.intellij.CommonBundle; -import com.intellij.dvcs.DvcsUtil; import com.intellij.notification.Notification; import com.intellij.notification.NotificationListener; -import com.intellij.openapi.application.AccessToken; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.application.ModalityState; import com.intellij.openapi.diagnostic.Logger; @@ -29,14 +27,10 @@ import com.intellij.openapi.progress.Task; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.DialogWrapper; import com.intellij.openapi.ui.Messages; -import com.intellij.openapi.util.Computable; -import com.intellij.openapi.util.Key; -import com.intellij.openapi.util.Ref; import com.intellij.openapi.vcs.VcsException; import com.intellij.openapi.vcs.VcsNotifier; import com.intellij.openapi.vcs.history.VcsRevisionNumber; import com.intellij.openapi.vcs.merge.MergeDialogCustomizer; -import com.intellij.openapi.vfs.VfsUtil; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.ui.DocumentAdapter; import git4idea.GitRevisionNumber; @@ -48,8 +42,6 @@ import git4idea.merge.GitConflictResolver; import git4idea.repo.GitRepository; import git4idea.stash.GitStashUtils; import git4idea.util.GitUIUtil; -import git4idea.util.GitUntrackedFilesHelper; -import git4idea.util.LocalChangesWouldBeOverwrittenHelper; import git4idea.validators.GitBranchNameValidator; import org.jetbrains.annotations.NotNull; @@ -64,9 +56,6 @@ import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.List; -import java.util.concurrent.atomic.AtomicBoolean; - -import static git4idea.commands.GitLocalChangesWouldBeOverwrittenDetector.Operation.MERGE; /** * The unstash dialog @@ -312,51 +301,15 @@ public class GitUnstashDialog extends DialogWrapper { @Override protected void doOKAction() { VirtualFile root = getGitRoot(); - final GitLineHandler h = handler(); - final AtomicBoolean conflict = new AtomicBoolean(); + GitLineHandler h = handler(); - h.addLineListener(new GitLineHandlerAdapter() { - public void onLineAvailable(String line, Key outputType) { - if (line.contains("Merge conflict")) { - conflict.set(true); - } - } - }); - GitUntrackedFilesOverwrittenByOperationDetector untrackedFilesDetector = new GitUntrackedFilesOverwrittenByOperationDetector(root); - GitLocalChangesWouldBeOverwrittenDetector localChangesDetector = new GitLocalChangesWouldBeOverwrittenDetector(root, MERGE); - h.addLineListener(untrackedFilesDetector); - h.addLineListener(localChangesDetector); + boolean completed = ProgressManager.getInstance().runProcessWithProgressSynchronously(() -> { + GitStashUtils.unstash(myProject, root, h, new UnstashConflictResolver(myProject, root, getSelectedStash())); + }, GitBundle.getString("unstash.unstashing"), true, myProject); - AccessToken token = DvcsUtil.workingTreeChangeStarted(myProject); - try { - final Ref result = Ref.create(); - final ProgressManager progressManager = ProgressManager.getInstance(); - boolean completed = progressManager.runProcessWithProgressSynchronously(() -> { - h.addLineListener(new GitHandlerUtil.GitLineHandlerListenerProgress(progressManager.getProgressIndicator(), h, "stash", false)); - Git git = Git.getInstance(); - result.set(git.runCommand(new Computable.PredefinedValueComputable<>(h))); - }, GitBundle.getString("unstash.unstashing"), true, myProject); - - if (!completed) return; - - VfsUtil.markDirtyAndRefresh(false, true, false, root); - GitCommandResult res = result.get(); - if (conflict.get()) { - boolean conflictsResolved = new UnstashConflictResolver(myProject, root, getSelectedStash()).merge(); - LOG.info("loadRoot " + root + ", conflictsResolved: " + conflictsResolved); - } else if (untrackedFilesDetector.wasMessageDetected()) { - GitUntrackedFilesHelper.notifyUntrackedFilesOverwrittenBy(myProject, root, untrackedFilesDetector.getRelativeFilePaths(), - "unstash", null); - } else if (localChangesDetector.wasMessageDetected()) { - LocalChangesWouldBeOverwrittenHelper.showErrorDialog(myProject, root, "unstash", localChangesDetector.getRelativeFilePaths()); - } else if (!res.success()) { - GitUIUtil.showOperationErrors(myProject, h.errors(), h.printableCommandLine()); - } + if (completed) { + super.doOKAction(); } - finally { - token.finish(); - } - super.doOKAction(); } public static void showUnstashDialog(Project project, List gitRoots, VirtualFile defaultRoot) {