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.
This commit is contained in:
Kirill Likhodedov
2010-10-21 17:55:33 +04:00
parent 08777c5eeb
commit c6496ded94
4 changed files with 67 additions and 24 deletions
@@ -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<VirtualFile> 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<Change> 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<Change> 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);
}
}
@@ -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;
}
}
}
@@ -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<VcsFileRevision> revisions = GitHistoryUtils.history(project, filePath);
List<VcsFileRevision> 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.<VcsFileRevision>emptyList());
partner.reportCreatedEmptySession(emptySession);
GitHistoryUtils.history(project, path, new Consumer<GitFileRevision>() {
final GitExecutableValidator validator = GitExecutableValidator.getInstance(myProject);
GitHistoryUtils.history(myProject, path, new Consumer<GitFileRevision>() {
public void consume(GitFileRevision gitFileRevision) {
partner.acceptRevision(gitFileRevision);
}
}, new Consumer<VcsException>() {
public void consume(VcsException e) {
partner.reportException(e);
if (!validator.isGitExecutableValid()) {
validator.showExecutableNotConfiguredNotification();
} else {
partner.reportException(e);
}
}
});
}
@@ -166,6 +166,7 @@ public class GitHistoryUtils {
public void startFailed(Throwable exception) {
//noinspection ThrowableInstanceNeverThrown
exceptionConsumer.consume(new VcsException(exception));
semaphore.up();
}
@Override