From 9f48631207e3e0c7ca193d0791efb85b10c7493e Mon Sep 17 00:00:00 2001 From: Constantine Plotnikov Date: Fri, 18 Dec 2009 16:49:47 +0300 Subject: [PATCH] git4idea: Add and revert commands are now done in background thread --- plugins/git4idea/src/git4idea/GitVcs.java | 24 ++++- .../git4idea/src/git4idea/actions/BasicAction.java | 116 ++++++++++++++++----- plugins/git4idea/src/git4idea/actions/GitAdd.java | 36 ++++--- .../src/git4idea/actions/GitMergeTool.java | 11 +- .../git4idea/src/git4idea/actions/GitRevert.java | 44 ++++---- .../src/git4idea/i18n/GitBundle.properties | 3 + 6 files changed, 172 insertions(+), 62 deletions(-) diff --git a/plugins/git4idea/src/git4idea/GitVcs.java b/plugins/git4idea/src/git4idea/GitVcs.java index d383c086de..cc7e914ed4 100644 --- a/plugins/git4idea/src/git4idea/GitVcs.java +++ b/plugins/git4idea/src/git4idea/GitVcs.java @@ -20,6 +20,8 @@ import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.diff.impl.patch.formove.FilePathComparator; import com.intellij.openapi.editor.markup.TextAttributes; import com.intellij.openapi.options.Configurable; +import com.intellij.openapi.progress.BackgroundTaskQueue; +import com.intellij.openapi.progress.Task; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.Messages; import com.intellij.openapi.util.Disposer; @@ -80,6 +82,9 @@ public class GitVcs extends AbstractVcs { * Vcs name */ @NonNls public static final String NAME = "Git"; + /** + * The git vcs key + */ private static final VcsKey ourKey = createKey(NAME); /** * change provider @@ -156,11 +161,11 @@ public class GitVcs extends AbstractVcs { /** * The dispatcher object for root events */ - private EventDispatcher myRootListeners = EventDispatcher.create(GitRootsListener.class); + private final EventDispatcher myRootListeners = EventDispatcher.create(GitRootsListener.class); /** * The dispatcher object for git configuration events */ - private EventDispatcher myConfigListeners = EventDispatcher.create(GitConfigListener.class); + private final EventDispatcher myConfigListeners = EventDispatcher.create(GitConfigListener.class); /** * Tracker for ignored files */ @@ -169,6 +174,10 @@ public class GitVcs extends AbstractVcs { * Configuration file tracker */ private GitConfigTracker myConfigTracker; + /** + * The queue that is used to schedule background task from actions + */ + private final BackgroundTaskQueue myTaskQueue; private final TreeDiffProvider myTreeDiffProvider; @@ -204,6 +213,16 @@ public class GitVcs extends AbstractVcs { myOutgoingChangesProvider = new GitOutgoingChangesProvider(myProject); myTreeDiffProvider = new GitTreeDiffProvider(myProject); myCommitAndPushExecutor = new GitCommitAndPushExecutor(gitCheckinEnvironment); + myTaskQueue = new BackgroundTaskQueue(myProject, GitBundle.getString("task.queue.title")); + } + + /** + * Run task in background using the common queue (per project) + * + * @param task the task to run + */ + public void runInBackground(Task.Backgroundable task) { + myTaskQueue.run(task); } /** @@ -638,6 +657,7 @@ public class GitVcs extends AbstractVcs { } private final GitOutgoingChangesProvider myOutgoingChangesProvider; + @Override protected VcsOutgoingChangesProvider getOutgoingProviderImpl() { return myOutgoingChangesProvider; diff --git a/plugins/git4idea/src/git4idea/actions/BasicAction.java b/plugins/git4idea/src/git4idea/actions/BasicAction.java index d070d9238c..0e29c2c6b9 100644 --- a/plugins/git4idea/src/git4idea/actions/BasicAction.java +++ b/plugins/git4idea/src/git4idea/actions/BasicAction.java @@ -20,16 +20,19 @@ import com.intellij.openapi.actionSystem.PlatformDataKeys; import com.intellij.openapi.actionSystem.Presentation; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.fileEditor.FileDocumentManager; +import com.intellij.openapi.progress.ProgressIndicator; +import com.intellij.openapi.progress.Task; import com.intellij.openapi.project.DumbAwareAction; import com.intellij.openapi.project.Project; -import com.intellij.openapi.vcs.AbstractVcsHelper; import com.intellij.openapi.vcs.ProjectLevelVcsManager; -import com.intellij.openapi.vcs.TransactionRunnable; import com.intellij.openapi.vcs.VcsException; import com.intellij.openapi.vfs.VfsUtil; import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.util.Consumer; +import com.intellij.util.ui.UIUtil; import git4idea.GitUtil; import git4idea.GitVcs; +import git4idea.ui.GitUIUtil; import org.jetbrains.annotations.NotNull; import java.util.ArrayList; @@ -40,7 +43,9 @@ import java.util.List; * Basic abstract action handler for all Git actions to extend. */ public abstract class BasicAction extends DumbAwareAction { - + /** + * {@inheritDoc} + */ @Override public void actionPerformed(@NotNull AnActionEvent event) { final Project project = event.getData(PlatformDataKeys.PROJECT); @@ -57,34 +62,70 @@ public abstract class BasicAction extends DumbAwareAction { if (!ProjectLevelVcsManager.getInstance(project).checkAllFilesAreUnder(vcs, vFiles)) { return; } + final String actionName = getActionName(); + + final VirtualFile[] affectedFiles = collectAffectedFiles(project, vFiles); + final List exceptions = new ArrayList(); + final boolean background = perform(project, vcs, exceptions, affectedFiles); + if (!background) { + vcs.runInBackground(new Task.Backgroundable(project, getActionName()) { + @Override + public void run(@NotNull ProgressIndicator indicator) { + GitUtil.refreshFiles(project, Arrays.asList(affectedFiles)); + UIUtil.invokeLaterIfNeeded(new Runnable() { + public void run() { + GitUIUtil.showOperationErrors(project, exceptions, actionName); + } + }); + } + }); + } + } - String actionName = getActionName(); - AbstractVcsHelper helper = AbstractVcsHelper.getInstance(project); - //Runs the runnable inside the vcs transaction (if needed), collects all exceptions, commits/rollbacks transaction and returns all exceptions together. - List exceptions = helper.runTransactionRunnable(vcs, new TransactionRunnable() { - public void run(List exceptions) { - final VirtualFile[] affectedFiles = collectAffectedFiles(project, vFiles); - //noinspection unchecked - try { - perform(project, vcs, exceptions, affectedFiles); - } - catch (VcsException e) { - exceptions.add(e); - } + /** + * Perform the action over set of files + * + * @param project the context project + * @param mksVcs the vcs instance + * @param exceptions the list of exceptions to be collected. + * @param affectedFiles the files to be affected by the operation + * @return true if the operation scheduled a background job, or cleanup is not needed + */ + protected abstract boolean perform(@NotNull Project project, + GitVcs mksVcs, + @NotNull List exceptions, + @NotNull VirtualFile[] affectedFiles); + + /** + * Perform the action over set of files in background + * + * @param project the context project + * @param exceptions the list of exceptions to be collected. + * @param affectedFiles the files to be affected by the operation + * @param action the action to be run in background + * @return true value + */ + protected boolean toBackground(final Project project, + GitVcs vcs, + final VirtualFile[] affectedFiles, + final List exceptions, + final Consumer action) { + vcs.runInBackground(new Task.Backgroundable(project, getActionName()) { + @Override + public void run(@NotNull ProgressIndicator indicator) { + action.consume(indicator); GitUtil.refreshFiles(project, Arrays.asList(affectedFiles)); + UIUtil.invokeLaterIfNeeded(new Runnable() { + public void run() { + GitUIUtil.showOperationErrors(project, exceptions, getActionName()); + } + }); } - - }, null); - vcs.showErrors(exceptions, actionName); + }); + return true; } - - protected abstract void perform(@NotNull Project project, - GitVcs mksVcs, - @NotNull List exceptions, - @NotNull VirtualFile[] affectedFiles) throws VcsException; - /** * given a list of action-target files, returns ALL the files that should be * subject to the action Does not keep directories, but recursively adds @@ -132,14 +173,28 @@ public abstract class BasicAction extends DumbAwareAction { } } + /** + * @return the name of action (it is used in a number of ui elements) + */ @NotNull protected abstract String getActionName(); + + /** + * @return true if the action could be applied recursively + */ @SuppressWarnings({"MethodMayBeStatic"}) protected boolean isRecursive() { return true; } + /** + * Check if the action is applicable to the file. The default checks if the file is a directory + * + * @param project the context project + * @param file the file to check + * @return true if the action is applicable to the virtual file + */ @SuppressWarnings({"MethodMayBeStatic", "UnusedDeclaration"}) protected boolean appliesTo(@NotNull Project project, @NotNull VirtualFile file) { return !file.isDirectory(); @@ -175,8 +230,19 @@ public abstract class BasicAction extends DumbAwareAction { presentation.setVisible(enabled); } + /** + * Check if the action should be enabled for the set of the fils + * + * @param project the context project + * @param vcs the vcs to use + * @param vFiles the set of files + * @return true if the action should be enabled + */ protected abstract boolean isEnabled(@NotNull Project project, @NotNull GitVcs vcs, @NotNull VirtualFile... vFiles); + /** + * Save all files in the application (the operation creates write action) + */ public static void saveAll() { ApplicationManager.getApplication().runWriteAction(new Runnable() { public void run() { diff --git a/plugins/git4idea/src/git4idea/actions/GitAdd.java b/plugins/git4idea/src/git4idea/actions/GitAdd.java index 79c5f4c403..907b5ce825 100644 --- a/plugins/git4idea/src/git4idea/actions/GitAdd.java +++ b/plugins/git4idea/src/git4idea/actions/GitAdd.java @@ -15,13 +15,14 @@ */ package git4idea.actions; +import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.project.Project; import com.intellij.openapi.vcs.FileStatus; import com.intellij.openapi.vcs.FileStatusManager; import com.intellij.openapi.vcs.ProjectLevelVcsManager; import com.intellij.openapi.vcs.VcsException; -import com.intellij.openapi.vcs.changes.VcsDirtyScopeManager; import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.util.Consumer; import git4idea.GitUtil; import git4idea.GitVcs; import git4idea.commands.GitFileUtils; @@ -38,32 +39,39 @@ import java.util.Map; public class GitAdd extends BasicAction { @Override - public void perform(@NotNull Project project, GitVcs vcs, @NotNull List exceptions, @NotNull VirtualFile[] affectedFiles) - throws VcsException { + public boolean perform(@NotNull final Project project, + final GitVcs vcs, + @NotNull final List exceptions, + @NotNull final VirtualFile[] affectedFiles) { saveAll(); + if (!ProjectLevelVcsManager.getInstance(project).checkAllFilesAreUnder(GitVcs.getInstance(project), affectedFiles)) return false; + return toBackground(project, vcs, affectedFiles, exceptions, new Consumer() { + public void consume(ProgressIndicator indicator) { + try { + addFiles(project, affectedFiles, indicator); + } + catch (VcsException e) { + exceptions.add(e); + } + } + }); - if (!ProjectLevelVcsManager.getInstance(project).checkAllFilesAreUnder(GitVcs.getInstance(project), affectedFiles)) return; - - addFiles(project, affectedFiles); } /** * Add the specified files to the project. * * @param project The project to add files to - * @param files The files to add - * @throws VcsException If an error occurs + * @param files The files to add @throws VcsException If an error occurs + * @param pi progress indicator */ - public static void addFiles(@NotNull Project project, @NotNull VirtualFile[] files) throws VcsException { + public static void addFiles(@NotNull final Project project, @NotNull final VirtualFile[] files, ProgressIndicator pi) + throws VcsException { final Map> roots = GitUtil.sortFilesByGitRoot(Arrays.asList(files)); for (Map.Entry> entry : roots.entrySet()) { + pi.setText(entry.getKey().getPresentableUrl()); GitFileUtils.addFiles(project, entry.getKey(), entry.getValue()); } - VcsDirtyScopeManager mgr = VcsDirtyScopeManager.getInstance(project); - for (VirtualFile file : files) { - mgr.fileDirty(file); - file.refresh(true, true); - } } @Override diff --git a/plugins/git4idea/src/git4idea/actions/GitMergeTool.java b/plugins/git4idea/src/git4idea/actions/GitMergeTool.java index d00205acf0..28c93018c1 100644 --- a/plugins/git4idea/src/git4idea/actions/GitMergeTool.java +++ b/plugins/git4idea/src/git4idea/actions/GitMergeTool.java @@ -39,8 +39,10 @@ public class GitMergeTool extends BasicAction { * {@inheritDoc} */ @Override - public void perform(@NotNull Project project, GitVcs vcs, @NotNull List exceptions, @NotNull VirtualFile[] affectedFiles) - throws VcsException { + public boolean perform(@NotNull Project project, + GitVcs vcs, + @NotNull List exceptions, + @NotNull VirtualFile[] affectedFiles) { saveAll(); // ensure that all selected files actually has unresolved conflicts ChangeListManager changes = ChangeListManager.getInstance(project); @@ -48,11 +50,14 @@ public class GitMergeTool extends BasicAction { Change change = changes.getChange(file); if (change != null && change.getFileStatus() != FileStatus.MERGED_WITH_CONFLICTS) { File f = new File(file.getPath()); - throw new VcsException(GitBundle.message("merge.is.not.needed", f.getAbsolutePath())); + //noinspection ThrowableInstanceNeverThrown + exceptions.add(new VcsException(GitBundle.message("merge.is.not.needed", f.getAbsolutePath()))); + return true; } } // perform merge AbstractVcsHelper.getInstance(project).showMergeDialog(Arrays.asList(affectedFiles), vcs.getMergeProvider()); + return false; } /** diff --git a/plugins/git4idea/src/git4idea/actions/GitRevert.java b/plugins/git4idea/src/git4idea/actions/GitRevert.java index e3cdbc5860..6efb5d5fea 100644 --- a/plugins/git4idea/src/git4idea/actions/GitRevert.java +++ b/plugins/git4idea/src/git4idea/actions/GitRevert.java @@ -16,7 +16,6 @@ package git4idea.actions; import com.intellij.openapi.progress.ProgressIndicator; -import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.project.Project; import com.intellij.openapi.vcs.FileStatus; import com.intellij.openapi.vcs.FileStatusManager; @@ -30,6 +29,7 @@ import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.newvfs.RefreshQueue; import com.intellij.openapi.vfs.newvfs.RefreshSession; +import com.intellij.util.Consumer; import git4idea.GitUtil; import git4idea.GitVcs; import git4idea.i18n.GitBundle; @@ -44,38 +44,46 @@ import java.util.List; * Git "revert" action */ public class GitRevert extends BasicAction { + /** + * {@inheritDoc} + */ @Override - public void perform(@NotNull final Project project, - GitVcs vcs, - @NotNull final List exceptions, - @NotNull VirtualFile[] affectedFiles) throws VcsException { + public boolean perform(@NotNull final Project project, + GitVcs vcs, + @NotNull final List exceptions, + @NotNull VirtualFile[] affectedFiles) { saveAll(); final ChangeListManager changeManager = ChangeListManager.getInstance(project); final List changes = new ArrayList(); final HashSet files = new HashSet(); - for (VirtualFile f : affectedFiles) { - Change ch = changeManager.getChange(f); - if (ch != null) { - files.add(GitUtil.getGitRoot(f)); - changes.add(ch); + try { + for (VirtualFile f : affectedFiles) { + Change ch = changeManager.getChange(f); + if (ch != null) { + files.add(GitUtil.getGitRoot(f)); + changes.add(ch); + } } } - final ProgressManager progress = ProgressManager.getInstance(); - progress.runProcessWithProgressSynchronously(new Runnable() { - public void run() { - ProgressIndicator pi = progress.getProgressIndicator(); + catch (VcsException ex) { + exceptions.add(ex); + return true; + } + return toBackground(project, vcs, affectedFiles, exceptions, new Consumer() { + public void consume(ProgressIndicator pi) { pi.setIndeterminate(true); GitRollbackEnvironment re = GitRollbackEnvironment.getInstance(project); re.rollbackChanges(changes, exceptions, RollbackProgressListener.EMPTY); - if(changes.size() == 1) { + if (changes.size() == 1) { Change c = changes.get(0); ContentRevision r = c.getAfterRevision(); - if(r == null) { + if (r == null) { r = c.getBeforeRevision(); assert r != null; } pi.setText2(r.getFile().getPath()); - } else { + } + else { pi.setText2(GitBundle.message("revert.reverting.mulitple", changes.size())); } LocalFileSystem lfs = LocalFileSystem.getInstance(); @@ -114,7 +122,7 @@ public class GitRevert extends BasicAction { session.addAllFiles(files); session.launch(); } - }, GitBundle.getString("revert.reverting"), false, project); + }); } @Override diff --git a/plugins/git4idea/src/git4idea/i18n/GitBundle.properties b/plugins/git4idea/src/git4idea/i18n/GitBundle.properties index 030c7b2f73..478996b93b 100644 --- a/plugins/git4idea/src/git4idea/i18n/GitBundle.properties +++ b/plugins/git4idea/src/git4idea/i18n/GitBundle.properties @@ -1,5 +1,6 @@ action.text.show.all.submitted=Show all files changed by this commit add.action.name=Add +add.adding=Adding files... addrefspec.button=Add addrefspec.get.references.tooltip=Get remote tag and branch references (depending on checkbox state). addrefspec.get.references=&Get References @@ -69,6 +70,7 @@ common.do.not.show=&Do not show this dialog again common.git.root.tooltip=Select git vcs root common.git.root=Git &Root: common.no.active.branch= +common.refreshing=Refreshing files computing.annotation=Computing annotation for {0} crlf.convert.convert=Convert Selected crlf.convert.label=&The following text files have line separator that do not match the project line separator.
Select files you wish to convert to project default line separators before commit. @@ -394,6 +396,7 @@ tag.title=Tag tag.validate.tooltip=Click this button to the validate commit to be tagged. tag.validate=&Validate tagging.title=Tagging files... +task.queue.title=Git Operations unindexed.files.changlelist.name=Unindexed Files unstash.action.name=UnStash unstash.branch.label\:=As new &branch: -- 2.11.4.GIT