[Git] IDEA-51187 Auto-merge on unstash.
authorKirill Likhodedov <kirill.likhodedov@jetbrains.com>
Wed, 11 May 2011 13:19:21 +0000 (17:19 +0400)
committerKirill Likhodedov <kirill.likhodedov@jetbrains.com>
Thu, 12 May 2011 07:39:37 +0000 (11:39 +0400)
* dropStash moved to GitStashUtils.
* GitUnstashDialog captures conflicts and uses GitMergeConflictResolver.

plugins/git4idea/src/git4idea/merge/GitMergeConflictResolver.java
plugins/git4idea/src/git4idea/stash/GitStashChangesSaver.java
plugins/git4idea/src/git4idea/stash/GitStashUtils.java
plugins/git4idea/src/git4idea/ui/GitUnstashDialog.java

index faf90fe1c310d629bda28643b5fbcab400f1c603..834249d06afc404ca325aea45fd90923252142cb 100644 (file)
@@ -42,7 +42,7 @@ import java.util.Collection;
 public class GitMergeConflictResolver {
 
   private static final Logger LOG = Logger.getInstance(GitMergeConflictResolver.class);
-  private final @NotNull Project myProject;
+  protected final @NotNull Project myProject;
   private final boolean myReverseMerge;
   private final @NotNull String myErrorNotificationTitle;
   private final @NotNull String myErrorNotificationAdditionalDescription;
index ca8f4120ed408b73d26a96afd9ab4eaf1360ae60..736f9c5a8eef3a350142f2c69e0700a71f5b8b22 100644 (file)
@@ -176,7 +176,7 @@ public class GitStashChangesSaver extends GitChangesSaver {
         boolean conflictsResolved = new UnstashConflictResolver().merge(Collections.singleton(root));
         if (conflictsResolved) {
           LOG.info("loadRoot " + root + " conflicts resolved, dropping stash");
-          dropStash(root);
+          GitStashUtils.dropStash(myProject, root);
         }
       } else {
         LOG.info("unstash failed " + handler.errors());
@@ -185,22 +185,6 @@ public class GitStashChangesSaver extends GitChangesSaver {
     }
   }
 
-  // drops stash (after completing conflicting merge during unstashing), shows a warning in case of error
-  private void dropStash(VirtualFile root) {
-    final GitSimpleHandler handler = new GitSimpleHandler(myProject, root, GitCommand.STASH);
-    handler.setNoSSH(true);
-    handler.addParameters("drop");
-    String output = null;
-    try {
-      output = handler.run();
-    } catch (VcsException e) {
-      LOG.info("dropStash " + output, e);
-      GitUIUtil.notifyMessage(myProject, "Couldn't drop stash",
-                              "Couldn't drop stash after resolving conflicts.<br/>Please drop stash manually.",
-                              WARNING, false, handler.errors());
-    }
-  }
-
   // Sort changes from myChangesLists by their git roots.
   // And use only supplied roots, ignoring changes from other roots.
   private Map<VirtualFile, Collection<Change>> groupChangesByRoots(Collection<VirtualFile> rootsToSave) {
index e25e764df9f25515d138032b8f2b141a84c15a94..53945b0656568d6a6ffed6ec89c070dfba8c3a05 100644 (file)
@@ -15,6 +15,7 @@
  */
 package git4idea.stash;
 
+import com.intellij.openapi.diagnostic.Logger;
 import com.intellij.openapi.project.Project;
 import com.intellij.openapi.vcs.VcsException;
 import com.intellij.openapi.vfs.VirtualFile;
@@ -29,11 +30,15 @@ import org.jetbrains.annotations.NotNull;
 
 import java.nio.charset.Charset;
 
+import static com.intellij.notification.NotificationType.WARNING;
+
 /**
  * The class contains utilities for creating and removing stashes.
  */
 public class GitStashUtils {
 
+  private static final Logger LOG = Logger.getInstance(GitStashUtils.class);
+
   private GitStashUtils() {
   }
 
@@ -76,4 +81,21 @@ public class GitStashUtils {
       consumer.consume(new StashInfo(s.boundedToken(':'), s.boundedToken(':'), s.line().trim()));
     }
   }
+
+  // drops stash (after completing conflicting merge during unstashing), shows a warning in case of error
+  public static void dropStash(Project project, VirtualFile root) {
+    final GitSimpleHandler handler = new GitSimpleHandler(project, root, GitCommand.STASH);
+    handler.setNoSSH(true);
+    handler.addParameters("drop");
+    String output = null;
+    try {
+      output = handler.run();
+    } catch (VcsException e) {
+      LOG.info("dropStash " + output, e);
+      GitUIUtil.notifyMessage(project, "Couldn't drop stash",
+                              "Couldn't drop stash after resolving conflicts.<br/>Please drop stash manually.",
+                              WARNING, false, handler.errors());
+    }
+  }
+
 }
index 820749a286c66391683350c2f197dd7d96399bc3..7110aa8c8061f2a3443220f29a018a423a906112 100644 (file)
  */
 package git4idea.ui;
 
+import com.intellij.notification.Notification;
+import com.intellij.notification.NotificationListener;
+import com.intellij.notification.NotificationType;
+import com.intellij.notification.Notifications;
+import com.intellij.openapi.diagnostic.Logger;
 import com.intellij.openapi.progress.ProgressIndicator;
 import com.intellij.openapi.progress.ProgressManager;
 import com.intellij.openapi.progress.Task;
@@ -23,6 +28,8 @@ import com.intellij.openapi.ui.DialogWrapper;
 import com.intellij.openapi.ui.Messages;
 import com.intellij.openapi.util.Key;
 import com.intellij.openapi.vcs.VcsException;
+import com.intellij.openapi.vcs.history.VcsRevisionNumber;
+import com.intellij.openapi.vcs.merge.MergeDialogCustomizer;
 import com.intellij.openapi.vfs.VirtualFile;
 import com.intellij.ui.DocumentAdapter;
 import com.intellij.util.Consumer;
@@ -33,19 +40,19 @@ import git4idea.actions.GitShowAllSubmittedFilesAction;
 import git4idea.commands.*;
 import git4idea.config.GitVersionSpecialty;
 import git4idea.i18n.GitBundle;
+import git4idea.merge.GitMergeConflictResolver;
 import git4idea.stash.GitStashUtils;
 import git4idea.validators.GitBranchNameValidator;
 import org.jetbrains.annotations.NotNull;
 
 import javax.swing.*;
 import javax.swing.event.DocumentEvent;
+import javax.swing.event.HyperlinkEvent;
 import javax.swing.event.ListSelectionEvent;
 import javax.swing.event.ListSelectionListener;
 import java.awt.event.ActionEvent;
 import java.awt.event.ActionListener;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
+import java.util.*;
 import java.util.concurrent.atomic.AtomicBoolean;
 
 /**
@@ -102,6 +109,7 @@ public class GitUnstashDialog extends DialogWrapper {
    */
   private final Project myProject;
   private GitVcs myVcs;
+  private static final Logger LOG = Logger.getInstance(GitUnstashDialog.class);
 
   /**
    * A constructor
@@ -406,10 +414,14 @@ public class GitUnstashDialog extends DialogWrapper {
     affectedRoots.add(d.getGitRoot());
     GitLineHandler h = d.handler(false);
     final AtomicBoolean needToEscapedBraces = new AtomicBoolean(false);
+    final AtomicBoolean conflict = new AtomicBoolean();
+
     h.addLineListener(new GitLineHandlerAdapter() {
       public void onLineAvailable(String line, Key outputType) {
         if (line.startsWith("fatal: Needed a single revision")) {
           needToEscapedBraces.set(true);
+        } else if (line.contains("Merge conflict")) {
+          conflict.set(true);
         }
       }
     });
@@ -418,8 +430,66 @@ public class GitUnstashDialog extends DialogWrapper {
       h = d.handler(true);
       rc = GitHandlerUtil.doSynchronously(h, GitBundle.getString("unstash.unstashing"), h.printableCommandLine(), false);
     }
-    if (rc != 0) {
+
+    if (conflict.get()) {
+      VirtualFile root = d.getGitRoot();
+      boolean conflictsResolved = new UnstashConflictResolver(project, d.getSelectedStash()).merge(Collections.singleton(root));
+      if (conflictsResolved) {
+        LOG.info("loadRoot " + root + " conflicts resolved, dropping stash");
+        GitStashUtils.dropStash(project, root);
+      }
+    } else if (rc != 0) {
       GitUIUtil.showOperationErrors(project, h.errors(), h.printableCommandLine());
     }
   }
+
+  private static class UnstashConflictResolver extends GitMergeConflictResolver {
+    private StashInfo myStashInfo;
+
+    public UnstashConflictResolver(Project project, StashInfo stashInfo) {
+      super(project, true, new UnstashMergeDialogCustomizer(stashInfo), "Local changes were not restored", "");
+      myStashInfo = stashInfo;
+    }
+
+    @Override
+    protected void notifyUnresolvedRemain(final Collection<VirtualFile> roots) {
+      Notifications.Bus.notify(new Notification(GitVcs.IMPORTANT_ERROR_NOTIFICATION, "Conflicts were not resolved during unstash",
+                                                "Unstash is not complete, you have unresolved merges in your working tree<br/>" +
+                                                "<a href='resolve'>Resolve</a> conflicts.",
+                                                NotificationType.WARNING, new NotificationListener() {
+          @Override
+          public void hyperlinkUpdate(@NotNull Notification notification, @NotNull HyperlinkEvent event) {
+            if (event.getEventType() == HyperlinkEvent.EventType.ACTIVATED) {
+              if (event.getDescription().equals("resolve")) {
+                new UnstashConflictResolver(myProject, myStashInfo).justMerge(roots);
+              }
+            }
+          }
+      }));
+    }
+  }
+
+  private static class UnstashMergeDialogCustomizer extends MergeDialogCustomizer {
+
+    private final StashInfo myStashInfo;
+
+    public UnstashMergeDialogCustomizer(StashInfo stashInfo) {
+      myStashInfo = stashInfo;
+    }
+
+    @Override
+    public String getMultipleFileMergeDescription(Collection<VirtualFile> files) {
+      return "<html>Conflicts during unstashing <code>" + myStashInfo.getStash() + "\"" + myStashInfo.getMessage() + "\"</code></html>";
+    }
+
+    @Override
+    public String getLeftPanelTitle(VirtualFile file) {
+      return "Local changes";
+    }
+
+    @Override
+    public String getRightPanelTitle(VirtualFile file, VcsRevisionNumber lastRevisionNumber) {
+      return "Changes from stash";
+    }
+  }
 }