diff --git a/vcs-impl/src/com/intellij/openapi/vcs/ObjectsConvertor.java b/vcs-impl/src/com/intellij/openapi/vcs/ObjectsConvertor.java index 566489a5ba77..13119f59a692 100644 --- a/vcs-impl/src/com/intellij/openapi/vcs/ObjectsConvertor.java +++ b/vcs-impl/src/com/intellij/openapi/vcs/ObjectsConvertor.java @@ -3,27 +3,43 @@ package com.intellij.openapi.vcs; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.containers.Convertor; +import java.io.File; import java.util.ArrayList; +import java.util.Collection; import java.util.List; public class ObjectsConvertor { + private static final Convertor FILEPATH_TO_VIRTUAL = new Convertor() { + public VirtualFile convert(FilePath fp) { + return fp.getVirtualFile(); + } + }; + + private static final Convertor VIRTUAL_FILEPATH = new Convertor() { + public FilePath convert(VirtualFile vf) { + return new FilePathImpl(vf); + } + }; + + private static final Convertor FILEPATH_FILE = new Convertor() { + public File convert(FilePath fp) { + return fp.getIOFile(); + } + }; + public static List fp2vf(final List in) { - return convert(in, new Convertor() { - public VirtualFile convert(final FilePath fp) { - return fp.getVirtualFile(); - } - }); + return convert(in, FILEPATH_TO_VIRTUAL); } public static List vf2fp(final List in) { - return convert(in, new Convertor() { - public FilePath convert(final VirtualFile vf) { - return new FilePathImpl(vf); - } - }); + return convert(in, VIRTUAL_FILEPATH); } - public static List convert(final List in, final Convertor convertor) { + public static List fp2jiof(final Collection in) { + return convert(in, FILEPATH_FILE); + } + + public static List convert(final Collection in, final Convertor convertor) { final List out = new ArrayList(); for (T t : in) { out.add(convertor.convert(t)); diff --git a/vcs-impl/src/com/intellij/openapi/vcs/changes/VcsDirtyScopeManagerImpl.java b/vcs-impl/src/com/intellij/openapi/vcs/changes/VcsDirtyScopeManagerImpl.java index 187a4d9e92fb..86d76fa2dd29 100644 --- a/vcs-impl/src/com/intellij/openapi/vcs/changes/VcsDirtyScopeManagerImpl.java +++ b/vcs-impl/src/com/intellij/openapi/vcs/changes/VcsDirtyScopeManagerImpl.java @@ -63,16 +63,32 @@ public class VcsDirtyScopeManagerImpl extends VcsDirtyScopeManager implements Pr } } + public void suspendMe() { + myLife.suspendMe(); + } + + public void reanimate() { + final Ref wasNotEmptyRef = new Ref(); + myLife.releaseMe(new Runnable() { + public void run() { + wasNotEmptyRef.set(! myDirtBuilder.isEmpty()); + } + }); + if (Boolean.TRUE.equals(wasNotEmptyRef.get())) { + myChangeListManager.scheduleUpdate(); + } + } + public void markEverythingDirty() { if (myProject.isDisposed()) return; - final boolean done = myLife.doIfAlive(new Runnable() { + final LifeDrop lifeDrop = myLife.doIfAlive(new Runnable() { public void run() { myDirtBuilder.everythingDirty(); } }); - if (done) { + if (lifeDrop.isDone() && (! lifeDrop.isSuspened())) { myChangeListManager.scheduleUpdate(); } } @@ -112,17 +128,20 @@ public class VcsDirtyScopeManagerImpl extends VcsDirtyScopeManager implements Pr } private boolean takeDirt(final Consumer filler) { - final boolean done = myLife.doIfAlive(new Runnable() { + final Ref wasNotEmptyRef = new Ref(); + final Runnable runnable = new Runnable() { public void run() { filler.consume(myDirtBuilder); + wasNotEmptyRef.set(!myDirtBuilder.isEmpty()); } - }); + }; + final LifeDrop lifeDrop = myLife.doIfAlive(runnable); - if (done && (! myDirtBuilder.isEmpty())) { + if (lifeDrop.isDone() && (! lifeDrop.isSuspened()) && (Boolean.TRUE.equals(wasNotEmptyRef.get()))) { myChangeListManager.scheduleUpdate(); } // no sence in checking correct here any more: vcs is searched for asynchronously - return (! done); + return (! lifeDrop.isDone()); } public boolean filesDirty(@Nullable final Collection filesDirty, @Nullable final Collection dirsRecursivelyDirty) { @@ -182,14 +201,14 @@ public class VcsDirtyScopeManagerImpl extends VcsDirtyScopeManager implements Pr public VcsInvalidated retrieveScopes() { final Ref dirtCopyRef = new Ref(); - final boolean done = myLife.doIfAlive(new Runnable() { + final LifeDrop lifeDrop = myLife.doIfAlive(new Runnable() { public void run() { dirtCopyRef.set(new DirtBuilder(myDirtBuilder)); myDirtBuilder.reset(); } }); - - if (done && (! dirtCopyRef.isNull())) { + + if (lifeDrop.isDone() && (! dirtCopyRef.isNull())) { return ApplicationManager.getApplication().runReadAction(new Computable() { public VcsInvalidated compute() { final Scopes scopes = new Scopes(myProject, myGuess); @@ -201,6 +220,23 @@ public class VcsDirtyScopeManagerImpl extends VcsDirtyScopeManager implements Pr return null; } + private String toStringScopes(final VcsInvalidated vcsInvalidated) { + final StringBuilder sb = new StringBuilder(); + sb.append("is everything dirty: ").append(vcsInvalidated.isEverythingDirty()).append(";\n"); + for (VcsDirtyScope scope : vcsInvalidated.getScopes()) { + sb.append("|\nFiles: "); + for (FilePath path : scope.getDirtyFiles()) { + sb.append(path).append('\n'); + } + sb.append("\nDirs: "); + for (FilePath filePath : scope.getRecursivelyDirtyDirectories()) { + sb.append(filePath).append('\n'); + } + } + sb.append("-------------"); + return sb.toString(); + } + private class MyVfsListener extends VirtualFileAdapter { @Override public void contentsChanged(VirtualFileEvent event) { @@ -267,9 +303,28 @@ public class VcsDirtyScopeManagerImpl extends VcsDirtyScopeManager implements Pr } } + private static class LifeDrop { + private final boolean myDone; + private final boolean mySuspened; + + private LifeDrop(boolean done, boolean suspened) { + myDone = done; + mySuspened = suspened; + } + + public boolean isDone() { + return myDone; + } + + public boolean isSuspened() { + return mySuspened; + } + } + private static class SynchronizedLife { private LifeStages myStage; private final Object myLock; + private boolean mySuspended; private SynchronizedLife() { myStage = LifeStages.NOT_BORN; @@ -288,15 +343,44 @@ public class VcsDirtyScopeManagerImpl extends VcsDirtyScopeManager implements Pr } } + public void suspendMe() { + synchronized (myLock) { + if (LifeStages.ALIVE.equals(myStage)) { + mySuspended = true; + } + } + } + + public void releaseMe(final Runnable runnable) { + synchronized (myLock) { + if (LifeStages.ALIVE.equals(myStage)) { + mySuspended = false; + runnable.run(); + } + } + } + + public LifeDrop doIfAliveAndNotSuspended(final Runnable runnable) { + synchronized (myLock) { + synchronized (myLock) { + if (LifeStages.ALIVE.equals(myStage) && (! mySuspended)) { + runnable.run(); + return new LifeDrop(true, mySuspended); + } + return new LifeDrop(false, mySuspended); + } + } + } + // allow work under inner lock: inner class, not wide scope - public boolean doIfAlive(final Runnable runnable) { + public LifeDrop doIfAlive(final Runnable runnable) { synchronized (myLock) { if (LifeStages.ALIVE.equals(myStage)) { runnable.run(); - return true; + return new LifeDrop(true, mySuspended); } + return new LifeDrop(false, mySuspended); } - return false; } private static enum LifeStages { diff --git a/vcs-impl/src/com/intellij/openapi/vcs/update/AbstractCommonUpdateAction.java b/vcs-impl/src/com/intellij/openapi/vcs/update/AbstractCommonUpdateAction.java index 74472e66c08f..6e0a1e81f682 100644 --- a/vcs-impl/src/com/intellij/openapi/vcs/update/AbstractCommonUpdateAction.java +++ b/vcs-impl/src/com/intellij/openapi/vcs/update/AbstractCommonUpdateAction.java @@ -50,6 +50,7 @@ import com.intellij.openapi.vcs.*; import com.intellij.openapi.vcs.actions.AbstractVcsAction; import com.intellij.openapi.vcs.actions.VcsContext; import com.intellij.openapi.vcs.changes.VcsDirtyScopeManager; +import com.intellij.openapi.vcs.changes.VcsDirtyScopeManagerImpl; import com.intellij.openapi.vcs.changes.committed.CommittedChangesAdapter; import com.intellij.openapi.vcs.changes.committed.CommittedChangesCache; import com.intellij.openapi.vcs.changes.ui.ChangesViewContentManager; @@ -305,11 +306,13 @@ public abstract class AbstractCommonUpdateAction extends AbstractVcsAction { // vcs name, context object private final Map myContextInfo; + private VcsDirtyScopeManager myDirtyScopeManager; public Updater(final Project project, final FilePath[] roots, final Map> vcsToVirtualFiles) { super(project, getTemplatePresentation().getText(), true, VcsConfiguration.getInstance(project).getUpdateOption()); myProject = project; myProjectLevelVcsManager = ProjectLevelVcsManagerEx.getInstanceEx(project); + myDirtyScopeManager = VcsDirtyScopeManager.getInstance(myProject); myRoots = roots; myVcsToVirtualFiles = vcsToVirtualFiles; @@ -329,7 +332,36 @@ public abstract class AbstractCommonUpdateAction extends AbstractVcsAction { ++ myUpdateNumber; } + private void suspendIfNeeded() { + if (! myActionInfo.canChangeFileStatus()) { + // i.e. for update but not for integrate or status + ((VcsDirtyScopeManagerImpl) myDirtyScopeManager).suspendMe(); + } + } + + private void releaseIfNeeded() { + if (! myActionInfo.canChangeFileStatus()) { + // i.e. for update but not for integrate or status + ((VcsDirtyScopeManagerImpl) myDirtyScopeManager).reanimate(); + } + } + public void run(@NotNull final ProgressIndicator indicator) { + suspendIfNeeded(); + try { + runImpl(indicator); + } catch (Throwable t) { + releaseIfNeeded(); + if (t instanceof Error) { + throw ((Error) t); + } else if (t instanceof RuntimeException) { + throw ((RuntimeException) t); + } + throw new RuntimeException(t); + } + } + + private void runImpl(@NotNull final ProgressIndicator indicator) { ProjectManagerEx.getInstanceEx().blockReloadingProjectOnExternalChanges(); myProjectLevelVcsManager.startBackgroundVcsOperation(); ProgressIndicator progressIndicator = ProgressManager.getInstance().getProgressIndicator(); @@ -405,6 +437,14 @@ public abstract class AbstractCommonUpdateAction extends AbstractVcsAction { } public void onSuccess() { + try { + onSuccessImpl(); + } finally { + releaseIfNeeded(); + } + } + + private void onSuccessImpl() { if (myProject.isDisposed()) { ProjectManagerEx.getInstanceEx().unblockReloadingProjectOnExternalChanges(); return; @@ -423,7 +463,6 @@ public abstract class AbstractCommonUpdateAction extends AbstractVcsAction { } if (myActionInfo.canChangeFileStatus()) { - final VcsDirtyScopeManager myManager = VcsDirtyScopeManager.getInstance(myProject); final List files = new ArrayList(); UpdateFilesHelper.iterateFileGroupFiles(myUpdatedFiles, new UpdateFilesHelper.Callback() { public void onFile(final String filePath, final String groupId) { @@ -434,7 +473,7 @@ public abstract class AbstractCommonUpdateAction extends AbstractVcsAction { } } }); - myManager.filesDirty(files, null); + myDirtyScopeManager.filesDirty(files, null); } final boolean updateSuccess = (! someSessionWasCancelled) && (myVcsExceptions.isEmpty());