From c6496ded940bbcdc389bb49782ef16e4302df089 Mon Sep 17 00:00:00 2001 From: Kirill Likhodedov Date: Thu, 21 Oct 2010 17:55:33 +0400 Subject: [PATCH] Git error handling: 1. Check GitExecutableValidator in GitChangeProvider and GitHistoryProvider. 2. Stop "loading history" indicator if process failed to start in GitHistoryUtils. Allows to have several attempts - had to restart IDEA to reinvoke history before that. --- .../git4idea/changes/GitChangeProvider.java | 30 ++++++++------- .../config/GitExecutableValidator.java | 37 ++++++++++++++++--- .../git4idea/history/GitHistoryProvider.java | 23 +++++++++--- .../src/git4idea/history/GitHistoryUtils.java | 1 + 4 files changed, 67 insertions(+), 24 deletions(-) diff --git a/plugins/git4idea/src/git4idea/changes/GitChangeProvider.java b/plugins/git4idea/src/git4idea/changes/GitChangeProvider.java index 6108dbdf5ca6..bc248770abc2 100644 --- a/plugins/git4idea/src/git4idea/changes/GitChangeProvider.java +++ b/plugins/git4idea/src/git4idea/changes/GitChangeProvider.java @@ -26,6 +26,7 @@ import git4idea.GitContentRevision; import git4idea.GitRevisionNumber; import git4idea.GitUtil; import git4idea.GitVcs; +import git4idea.config.GitExecutableValidator; import org.jetbrains.annotations.NotNull; import java.util.Collection; @@ -63,20 +64,23 @@ public class GitChangeProvider implements ChangeProvider { } Collection roots = GitUtil.gitRootsForPaths(affected); - final MyNonChangedHolder holder = new MyNonChangedHolder(myProject, dirtyScope.getDirtyFilesNoExpand(), addGate); - - for (VirtualFile root : roots) { - ChangeCollector c = new ChangeCollector(myProject, dirtyScope, root); - final Collection changes = c.changes(); - holder.changed(changes); - for (Change file : changes) { - builder.processChange(file, GitVcs.getKey()); + try { + final MyNonChangedHolder holder = new MyNonChangedHolder(myProject, dirtyScope.getDirtyFilesNoExpand(), addGate); + for (VirtualFile root : roots) { + ChangeCollector c = new ChangeCollector(myProject, dirtyScope, root); + final Collection changes = c.changes(); + holder.changed(changes); + for (Change file : changes) { + builder.processChange(file, GitVcs.getKey()); + } + for (VirtualFile f : c.unversioned()) { + builder.processUnversionedFile(f); + holder.unversioned(f); + } + holder.feedBuilder(builder); } - for (VirtualFile f : c.unversioned()) { - builder.processUnversionedFile(f); - holder.unversioned(f); - } - holder.feedBuilder(builder); + } catch (VcsException e) {// most probably the error happened because git is not configured + GitExecutableValidator.getInstance(myProject).showNotificationOrThrow(e); } } diff --git a/plugins/git4idea/src/git4idea/config/GitExecutableValidator.java b/plugins/git4idea/src/git4idea/config/GitExecutableValidator.java index 88715ceb5a1e..a60afbb92fe1 100644 --- a/plugins/git4idea/src/git4idea/config/GitExecutableValidator.java +++ b/plugins/git4idea/src/git4idea/config/GitExecutableValidator.java @@ -19,11 +19,14 @@ import com.intellij.notification.Notification; import com.intellij.notification.NotificationListener; import com.intellij.notification.NotificationType; import com.intellij.notification.Notifications; +import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.components.ServiceManager; import com.intellij.openapi.options.ShowSettingsUtil; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.Messages; import com.intellij.openapi.vcs.VcsException; +import com.intellij.ui.GuiUtils; +import com.intellij.util.ui.UIUtil; import git4idea.GitVcs; import git4idea.i18n.GitBundle; import org.jetbrains.annotations.NotNull; @@ -65,10 +68,7 @@ public class GitExecutableValidator { * Expires the notification if user fixes the path to Git from the opened Settings dialog. */ public void showExecutableNotConfiguredNotification() { - if (myNotification != null && !myNotification.isExpired()) { // don't display this notification twice - return; - } - myNotification = new Notification(GitVcs.NOTIFICATION_GROUP_ID, GitBundle.getString("executable.error.title"), + final Notification newNotification = new Notification(GitVcs.NOTIFICATION_GROUP_ID, GitBundle.getString("executable.error.title"), GitBundle.getString("executable.error.description"), NotificationType.ERROR, new NotificationListener() { public void hyperlinkUpdate(@NotNull Notification notification, @NotNull HyperlinkEvent event) { @@ -78,7 +78,19 @@ public class GitExecutableValidator { } } }); - Notifications.Bus.notify(myNotification, myProject); + + // expire() needs to be called from EventDispatch thread. notify handles it by itself. + // but we want to be sure that previous notification expires before new one is shown (and assigned to myNotification). + UIUtil.invokeLaterIfNeeded(new Runnable() { + @Override public void run() { + if (myNotification != null && !myNotification.isExpired()) { + // don't display this notification twice, but better to redisplay it again so that popup appears. + myNotification.expire(); + } + myNotification = newNotification; + Notifications.Bus.notify(myNotification, myProject); + } + }); } /** @@ -89,5 +101,20 @@ public class GitExecutableValidator { showExecutableNotConfiguredNotification(); } } + + /** + * Checks if git executable is valid. If not (which is a common case for low-level vcs exceptions), shows the + * notification. Otherwise throws the exception. + * This is to be used in catch-clauses + * @param e exception which was thrown. + * @throws VcsException if git executable is valid. + */ + public void showNotificationOrThrow(VcsException e) throws VcsException { + if (!isGitExecutableValid()) { + showExecutableNotConfiguredNotification(); + } else { + throw e; + } + } } diff --git a/plugins/git4idea/src/git4idea/history/GitHistoryProvider.java b/plugins/git4idea/src/git4idea/history/GitHistoryProvider.java index 1503d5acd310..9526a05032e4 100644 --- a/plugins/git4idea/src/git4idea/history/GitHistoryProvider.java +++ b/plugins/git4idea/src/git4idea/history/GitHistoryProvider.java @@ -25,6 +25,7 @@ import com.intellij.util.Consumer; import com.intellij.util.ui.ColumnInfo; import git4idea.GitFileRevision; import git4idea.actions.GitShowAllSubmittedFilesAction; +import git4idea.config.GitExecutableValidator; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -43,7 +44,7 @@ public class GitHistoryProvider implements VcsHistoryProvider { /** * the current project instance */ - private final Project project; + private final Project myProject; /** * A constructor @@ -51,7 +52,7 @@ public class GitHistoryProvider implements VcsHistoryProvider { * @param project a context project */ public GitHistoryProvider(@NotNull Project project) { - this.project = project; + this.myProject = project; } /** @@ -89,7 +90,12 @@ public class GitHistoryProvider implements VcsHistoryProvider { */ @Nullable public VcsHistorySession createSessionFor(final FilePath filePath) throws VcsException { - List revisions = GitHistoryUtils.history(project, filePath); + List revisions = null; + try { + revisions = GitHistoryUtils.history(myProject, filePath); + } catch (VcsException e) { + GitExecutableValidator.getInstance(myProject).showNotificationOrThrow(e); + } return createSession(filePath, revisions); } @@ -98,7 +104,7 @@ public class GitHistoryProvider implements VcsHistoryProvider { @Nullable protected VcsRevisionNumber calcCurrentRevisionNumber() { try { - return GitHistoryUtils.getCurrentRevision(project, GitHistoryUtils.getLastCommitName(project, filePath)); + return GitHistoryUtils.getCurrentRevision(myProject, GitHistoryUtils.getLastCommitName(myProject, filePath)); } catch (VcsException e) { // likely the file is not under VCS anymore. @@ -123,13 +129,18 @@ public class GitHistoryProvider implements VcsHistoryProvider { public void reportAppendableHistory(final FilePath path, final VcsAppendableHistorySessionPartner partner) throws VcsException { final VcsAbstractHistorySession emptySession = createSession(path, Collections.emptyList()); partner.reportCreatedEmptySession(emptySession); - GitHistoryUtils.history(project, path, new Consumer() { + final GitExecutableValidator validator = GitExecutableValidator.getInstance(myProject); + GitHistoryUtils.history(myProject, path, new Consumer() { public void consume(GitFileRevision gitFileRevision) { partner.acceptRevision(gitFileRevision); } }, new Consumer() { public void consume(VcsException e) { - partner.reportException(e); + if (!validator.isGitExecutableValid()) { + validator.showExecutableNotConfiguredNotification(); + } else { + partner.reportException(e); + } } }); } diff --git a/plugins/git4idea/src/git4idea/history/GitHistoryUtils.java b/plugins/git4idea/src/git4idea/history/GitHistoryUtils.java index 02453e91c050..2dfe0f0b9692 100644 --- a/plugins/git4idea/src/git4idea/history/GitHistoryUtils.java +++ b/plugins/git4idea/src/git4idea/history/GitHistoryUtils.java @@ -166,6 +166,7 @@ public class GitHistoryUtils { public void startFailed(Throwable exception) { //noinspection ThrowableInstanceNeverThrown exceptionConsumer.consume(new VcsException(exception)); + semaphore.up(); } @Override