Unify git unstash procedures

This fixes IDEA-174332
This commit is contained in:
Kirill Likhodedov
2017-07-01 19:41:42 +03:00
parent 30837cbf22
commit aaaca8ded1
3 changed files with 75 additions and 97 deletions
@@ -25,19 +25,14 @@ import com.intellij.openapi.vcs.VcsException;
import com.intellij.openapi.vcs.VcsNotifier; import com.intellij.openapi.vcs.VcsNotifier;
import com.intellij.openapi.vcs.history.VcsRevisionNumber; import com.intellij.openapi.vcs.history.VcsRevisionNumber;
import com.intellij.openapi.vcs.merge.MergeDialogCustomizer; import com.intellij.openapi.vcs.merge.MergeDialogCustomizer;
import com.intellij.openapi.vfs.VfsUtil;
import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.ContainerUtil;
import git4idea.GitUtil; import git4idea.GitUtil;
import git4idea.commands.Git; import git4idea.commands.*;
import git4idea.commands.GitCommandResult;
import git4idea.commands.GitHandlerUtil;
import git4idea.commands.GitSimpleEventDetector;
import git4idea.merge.GitConflictResolver; import git4idea.merge.GitConflictResolver;
import git4idea.repo.GitRepository; import git4idea.repo.GitRepository;
import git4idea.repo.GitRepositoryManager; import git4idea.repo.GitRepositoryManager;
import git4idea.ui.GitUnstashDialog; import git4idea.ui.GitUnstashDialog;
import git4idea.util.GitUIUtil;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
@@ -101,12 +96,11 @@ public class GitStashChangesSaver extends GitChangesSaver {
@Override @Override
public void load() { public void load() {
for (VirtualFile root : myStashedRoots) { GitStashUtils.unstash(myProject, myStashedRoots, (root) -> {
loadRoot(root); GitLineHandler handler = new GitLineHandler(myProject, root, GitCommand.STASH);
} handler.addParameters("pop");
return handler;
boolean conflictsResolved = new UnstashConflictResolver(myProject, myGit, myStashedRoots, myParams).merge(); }, new UnstashConflictResolver(myProject, myGit, myStashedRoots, myParams));
LOG.info("load: conflicts resolved status is " + conflictsResolved + " in roots " + myStashedRoots);
} }
@Override @Override
@@ -130,36 +124,6 @@ public class GitStashChangesSaver extends GitChangesSaver {
GitUnstashDialog.showUnstashDialog(myProject, new ArrayList<>(myStashedRoots), myStashedRoots.iterator().next()); 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", "<br/>" + result.getErrorOutputAsHtmlString());
return false;
}
}
@Override @Override
public String toString() { public String toString() {
return "StashChangesSaver. Roots: " + myStashedRoots; return "StashChangesSaver. Roots: " + myStashedRoots;
@@ -17,18 +17,79 @@
package git4idea.stash package git4idea.stash
import com.intellij.dvcs.DvcsUtil
import com.intellij.openapi.project.Project import com.intellij.openapi.project.Project
import com.intellij.openapi.vcs.VcsException 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.openapi.vfs.VirtualFile
import com.intellij.util.Consumer import com.intellij.util.Consumer
import git4idea.commands.GitCommand import git4idea.commands.*
import git4idea.commands.GitSimpleHandler
import git4idea.config.GitConfigUtil import git4idea.config.GitConfigUtil
import git4idea.merge.GitConflictResolver
import git4idea.ui.StashInfo import git4idea.ui.StashInfo
import git4idea.util.GitUIUtil import git4idea.util.GitUIUtil
import git4idea.util.GitUntrackedFilesHelper
import git4idea.util.LocalChangesWouldBeOverwrittenHelper
import git4idea.util.StringScanner import git4idea.util.StringScanner
import java.nio.charset.Charset 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<VirtualFile>,
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<StashInfo>) { fun loadStashStack(project: Project, root: VirtualFile, consumer: Consumer<StashInfo>) {
loadStashStack(project, root, Charset.forName(GitConfigUtil.getLogEncoding(project, root)), consumer) loadStashStack(project, root, Charset.forName(GitConfigUtil.getLogEncoding(project, root)), consumer)
} }
@@ -16,10 +16,8 @@
package git4idea.ui; package git4idea.ui;
import com.intellij.CommonBundle; import com.intellij.CommonBundle;
import com.intellij.dvcs.DvcsUtil;
import com.intellij.notification.Notification; import com.intellij.notification.Notification;
import com.intellij.notification.NotificationListener; import com.intellij.notification.NotificationListener;
import com.intellij.openapi.application.AccessToken;
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.diagnostic.Logger; 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.project.Project;
import com.intellij.openapi.ui.DialogWrapper; import com.intellij.openapi.ui.DialogWrapper;
import com.intellij.openapi.ui.Messages; 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.VcsException;
import com.intellij.openapi.vcs.VcsNotifier; import com.intellij.openapi.vcs.VcsNotifier;
import com.intellij.openapi.vcs.history.VcsRevisionNumber; import com.intellij.openapi.vcs.history.VcsRevisionNumber;
import com.intellij.openapi.vcs.merge.MergeDialogCustomizer; import com.intellij.openapi.vcs.merge.MergeDialogCustomizer;
import com.intellij.openapi.vfs.VfsUtil;
import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.ui.DocumentAdapter; import com.intellij.ui.DocumentAdapter;
import git4idea.GitRevisionNumber; import git4idea.GitRevisionNumber;
@@ -48,8 +42,6 @@ import git4idea.merge.GitConflictResolver;
import git4idea.repo.GitRepository; import git4idea.repo.GitRepository;
import git4idea.stash.GitStashUtils; import git4idea.stash.GitStashUtils;
import git4idea.util.GitUIUtil; import git4idea.util.GitUIUtil;
import git4idea.util.GitUntrackedFilesHelper;
import git4idea.util.LocalChangesWouldBeOverwrittenHelper;
import git4idea.validators.GitBranchNameValidator; import git4idea.validators.GitBranchNameValidator;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
@@ -64,9 +56,6 @@ import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import static git4idea.commands.GitLocalChangesWouldBeOverwrittenDetector.Operation.MERGE;
/** /**
* The unstash dialog * The unstash dialog
@@ -312,51 +301,15 @@ public class GitUnstashDialog extends DialogWrapper {
@Override @Override
protected void doOKAction() { protected void doOKAction() {
VirtualFile root = getGitRoot(); VirtualFile root = getGitRoot();
final GitLineHandler h = handler(); GitLineHandler h = handler();
final AtomicBoolean conflict = new AtomicBoolean();
h.addLineListener(new GitLineHandlerAdapter() { boolean completed = ProgressManager.getInstance().runProcessWithProgressSynchronously(() -> {
public void onLineAvailable(String line, Key outputType) { GitStashUtils.unstash(myProject, root, h, new UnstashConflictResolver(myProject, root, getSelectedStash()));
if (line.contains("Merge conflict")) { }, GitBundle.getString("unstash.unstashing"), true, myProject);
conflict.set(true);
}
}
});
GitUntrackedFilesOverwrittenByOperationDetector untrackedFilesDetector = new GitUntrackedFilesOverwrittenByOperationDetector(root);
GitLocalChangesWouldBeOverwrittenDetector localChangesDetector = new GitLocalChangesWouldBeOverwrittenDetector(root, MERGE);
h.addLineListener(untrackedFilesDetector);
h.addLineListener(localChangesDetector);
AccessToken token = DvcsUtil.workingTreeChangeStarted(myProject); if (completed) {
try { super.doOKAction();
final Ref<GitCommandResult> 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());
}
} }
finally {
token.finish();
}
super.doOKAction();
} }
public static void showUnstashDialog(Project project, List<VirtualFile> gitRoots, VirtualFile defaultRoot) { public static void showUnstashDialog(Project project, List<VirtualFile> gitRoots, VirtualFile defaultRoot) {