2 * Copyright 2000-2014 JetBrains s.r.o.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
16 package com.intellij.openapi.vcs.changes;
18 import com.intellij.ide.highlighter.WorkspaceFileType;
19 import com.intellij.lifecycle.PeriodicalTasksCloser;
20 import com.intellij.openapi.application.ApplicationManager;
21 import com.intellij.openapi.application.ModalityState;
22 import com.intellij.openapi.application.RuntimeInterruptedException;
23 import com.intellij.openapi.components.ProjectComponent;
24 import com.intellij.openapi.diagnostic.Logger;
25 import com.intellij.openapi.fileEditor.FileDocumentManager;
26 import com.intellij.openapi.module.Module;
27 import com.intellij.openapi.module.ModuleManager;
28 import com.intellij.openapi.progress.EmptyProgressIndicator;
29 import com.intellij.openapi.progress.ProcessCanceledException;
30 import com.intellij.openapi.progress.ProgressIndicator;
31 import com.intellij.openapi.project.DumbAwareRunnable;
32 import com.intellij.openapi.project.Project;
33 import com.intellij.openapi.roots.ModuleRootManager;
34 import com.intellij.openapi.roots.ProjectFileIndex;
35 import com.intellij.openapi.roots.impl.DirectoryIndexExcludePolicy;
36 import com.intellij.openapi.ui.MessageType;
37 import com.intellij.openapi.ui.Messages;
38 import com.intellij.openapi.util.*;
39 import com.intellij.openapi.util.io.FileUtil;
40 import com.intellij.openapi.util.registry.Registry;
41 import com.intellij.openapi.util.text.StringUtil;
42 import com.intellij.openapi.vcs.*;
43 import com.intellij.openapi.vcs.changes.conflicts.ChangelistConflictTracker;
44 import com.intellij.openapi.vcs.changes.ui.CommitHelper;
45 import com.intellij.openapi.vcs.checkin.CheckinEnvironment;
46 import com.intellij.openapi.vcs.checkin.CheckinHandler;
47 import com.intellij.openapi.vcs.impl.*;
48 import com.intellij.openapi.vcs.readOnlyHandler.ReadonlyStatusHandlerImpl;
49 import com.intellij.openapi.vcs.ui.VcsBalloonProblemNotifier;
50 import com.intellij.openapi.vfs.*;
51 import com.intellij.ui.EditorNotifications;
52 import com.intellij.util.*;
53 import com.intellij.util.concurrency.Semaphore;
54 import com.intellij.util.containers.ContainerUtil;
55 import com.intellij.util.containers.MultiMap;
56 import com.intellij.util.continuation.ContinuationPause;
57 import com.intellij.util.messages.Topic;
58 import com.intellij.vcsUtil.Rethrow;
59 import com.intellij.vcsUtil.VcsUtil;
60 import org.jdom.Element;
61 import org.jetbrains.annotations.NonNls;
62 import org.jetbrains.annotations.NotNull;
63 import org.jetbrains.annotations.Nullable;
64 import org.jetbrains.annotations.TestOnly;
69 import java.util.concurrent.ScheduledExecutorService;
70 import java.util.concurrent.ScheduledThreadPoolExecutor;
71 import java.util.concurrent.atomic.AtomicReference;
76 public class ChangeListManagerImpl extends ChangeListManagerEx implements ProjectComponent, ChangeListOwner, JDOMExternalizable,
78 public static final Logger LOG = Logger.getInstance("#com.intellij.openapi.vcs.changes.ChangeListManagerImpl");
79 private static final String EXCLUDED_CONVERTED_TO_IGNORED_OPTION = "EXCLUDED_CONVERTED_TO_IGNORED";
81 private final Project myProject;
82 private final VcsConfiguration myConfig;
83 private final ChangesViewI myChangesViewManager;
84 private final FileStatusManager myFileStatusManager;
85 private final UpdateRequestsQueue myUpdater;
87 private static final AtomicReference<ScheduledExecutorService> ourUpdateAlarm = new AtomicReference<ScheduledExecutorService>();
89 ourUpdateAlarm.set(createChangeListExecutor());
92 private static ScheduledThreadPoolExecutor createChangeListExecutor() {
93 return VcsUtil.createExecutor("Change List Updater");
96 private final Modifier myModifier;
98 private FileHolderComposite myComposite;
100 private ChangeListWorker myWorker;
101 private VcsException myUpdateException = null;
102 private Factory<JComponent> myAdditionalInfo;
104 private final EventDispatcher<ChangeListListener> myListeners = EventDispatcher.create(ChangeListListener.class);
106 private final Object myDataLock = new Object();
108 private final List<CommitExecutor> myExecutors = new ArrayList<CommitExecutor>();
110 private final IgnoredFilesComponent myIgnoredIdeaLevel;
111 private boolean myExcludedConvertedToIgnored;
112 private volatile ProgressIndicator myUpdateChangesProgressIndicator = createProgressIndicator();
114 public static final Topic<LocalChangeListsLoadedListener> LISTS_LOADED = new Topic<LocalChangeListsLoadedListener>(
115 "LOCAL_CHANGE_LISTS_LOADED", LocalChangeListsLoadedListener.class);
117 private boolean myShowLocalChangesInvalidated;
118 private final AtomicReference<String> myFreezeName;
120 // notifies myListeners on the same thread that local changes update is done
121 private final DelayedNotificator myDelayedNotificator;
123 private final VcsListener myVcsListener = new VcsListener() {
125 public void directoryMappingChanged() {
126 VcsDirtyScopeManager.getInstance(myProject).markEverythingDirty();
129 private final ChangelistConflictTracker myConflictTracker;
130 private VcsDirtyScopeManager myDirtyScopeManager;
131 private final VcsDirtyScopeVfsListener myVfsListener;
133 private boolean myModalNotificationsBlocked;
134 @NotNull private final Collection<LocalChangeList> myListsToBeDeleted = new HashSet<LocalChangeList>();
136 public static ChangeListManagerImpl getInstanceImpl(final Project project) {
137 return (ChangeListManagerImpl)PeriodicalTasksCloser.getInstance().safeGetComponent(project, ChangeListManager.class);
140 void setDirtyScopeManager(VcsDirtyScopeManager dirtyScopeManager) {
141 myDirtyScopeManager = dirtyScopeManager;
144 public ChangeListManagerImpl(Project project, final VcsConfiguration config) {
147 myFreezeName = new AtomicReference<String>(null);
148 myAdditionalInfo = null;
149 myChangesViewManager = myProject.isDefault() ? new DummyChangesView(myProject) : ChangesViewManager.getInstance(myProject);
150 myVfsListener = ApplicationManager.getApplication().getComponent(VcsDirtyScopeVfsListener.class);
151 myFileStatusManager = FileStatusManager.getInstance(myProject);
152 myComposite = new FileHolderComposite(project);
153 myIgnoredIdeaLevel = new IgnoredFilesComponent(myProject, true);
154 myUpdater = new UpdateRequestsQueue(myProject, ourUpdateAlarm, new ActualUpdater());
156 myWorker = new ChangeListWorker(myProject, new MyChangesDeltaForwarder(myProject, ourUpdateAlarm));
157 myDelayedNotificator = new DelayedNotificator(myListeners, ourUpdateAlarm);
158 myModifier = new Modifier(myWorker, myDelayedNotificator);
160 myConflictTracker = new ChangelistConflictTracker(project, this, myFileStatusManager, EditorNotifications.getInstance(project));
162 myListeners.addListener(new ChangeListAdapter() {
164 public void defaultListChanged(final ChangeList oldDefaultList, ChangeList newDefaultList) {
165 final LocalChangeList oldList = (LocalChangeList)oldDefaultList;
166 if (oldDefaultList == null || oldList.hasDefaultName() || oldDefaultList.equals(newDefaultList)) return;
168 if (!ApplicationManager.getApplication().isUnitTestMode() &&
169 oldDefaultList.getChanges().isEmpty() &&
170 !oldList.isReadOnly()) {
172 invokeAfterUpdate(new Runnable() {
175 if (getChangeList(oldList.getId()) == null) {
176 return; // removed already
178 switch (config.REMOVE_EMPTY_INACTIVE_CHANGELISTS) {
179 case SHOW_CONFIRMATION:
180 if (myModalNotificationsBlocked) {
181 myListsToBeDeleted.add(oldList);
185 if (!showRemoveEmptyChangeListsProposal(config, Collections.singletonList(oldList))) {
189 case DO_NOTHING_SILENTLY:
191 case DO_ACTION_SILENTLY:
194 removeChangeList(oldList);
196 }, InvokeAfterUpdateMode.SILENT, null, null);
203 * Shows the proposal to delete one or more changelists that were default and became empty.
205 * @return true if the changelists have to be deleted, false if not.
207 private boolean showRemoveEmptyChangeListsProposal(@NotNull final VcsConfiguration config, @NotNull Collection<LocalChangeList> lists) {
208 if (lists.isEmpty()) {
212 final String question;
213 if (lists.size() == 1) {
214 question = String.format("<html>The empty changelist '%s' is no longer active.<br>Do you want to remove it?</html>",
215 StringUtil.first(lists.iterator().next().getName(), 30, true));
218 question = String.format("<html>Empty changelists<br/>%s are no longer active.<br>Do you want to remove them?</html>",
219 StringUtil.join(lists, new Function<LocalChangeList, String>() {
221 public String fun(LocalChangeList list) {
222 return StringUtil.first(list.getName(), 30, true);
227 VcsConfirmationDialog dialog = new VcsConfirmationDialog(myProject, new VcsShowConfirmationOption() {
229 public Value getValue() {
230 return config.REMOVE_EMPTY_INACTIVE_CHANGELISTS;
234 public void setValue(Value value) {
235 config.REMOVE_EMPTY_INACTIVE_CHANGELISTS = value;
239 public boolean isPersistent() {
242 }, question, "&Remember my choice");
243 return dialog.showAndGet();
248 public void blockModalNotifications() {
249 myModalNotificationsBlocked = true;
254 public void unblockModalNotifications() {
255 myModalNotificationsBlocked = false;
256 if (myListsToBeDeleted.isEmpty()) {
259 if (showRemoveEmptyChangeListsProposal(myConfig, myListsToBeDeleted)) {
260 for (LocalChangeList list : myListsToBeDeleted) {
261 removeChangeList(list);
264 myListsToBeDeleted.clear();
268 public void projectOpened() {
269 initializeForNewProject();
271 final ProjectLevelVcsManager vcsManager = ProjectLevelVcsManager.getInstance(myProject);
272 if (ApplicationManager.getApplication().isUnitTestMode()) {
273 myUpdater.initialized();
274 vcsManager.addVcsListener(myVcsListener);
277 ((ProjectLevelVcsManagerImpl)vcsManager).addInitializationRequest(
278 VcsInitObject.CHANGE_LIST_MANAGER, new DumbAwareRunnable() {
281 myUpdater.initialized();
282 broadcastStateAfterLoad();
283 vcsManager.addVcsListener(myVcsListener);
288 myConflictTracker.startTracking();
291 private void broadcastStateAfterLoad() {
292 final List<LocalChangeList> listCopy;
293 synchronized (myDataLock) {
294 listCopy = getChangeListsCopy();
296 if (!myProject.isDisposed()) {
297 myProject.getMessageBus().syncPublisher(LISTS_LOADED).processLoadedLists(listCopy);
301 private void initializeForNewProject() {
302 ApplicationManager.getApplication().runReadAction(new Runnable() {
305 synchronized (myDataLock) {
306 if (myWorker.isEmpty()) {
307 final LocalChangeList list = myWorker.addChangeList(VcsBundle.message("changes.default.changelist.name"), null, null);
308 setDefaultChangeList(list);
310 if (myIgnoredIdeaLevel.isEmpty()) {
311 final String name = myProject.getName();
312 myIgnoredIdeaLevel.add(IgnoredBeanFactory.ignoreFile(name + WorkspaceFileType.DOT_DEFAULT_EXTENSION, myProject));
313 myIgnoredIdeaLevel.add(IgnoredBeanFactory.ignoreFile(Project.DIRECTORY_STORE_FOLDER + "/workspace.xml", myProject));
316 if (!Registry.is("ide.hide.excluded.files") && !myExcludedConvertedToIgnored) {
317 convertExcludedToIgnored();
318 myExcludedConvertedToIgnored = true;
325 void convertExcludedToIgnored() {
326 for (DirectoryIndexExcludePolicy policy : DirectoryIndexExcludePolicy.EP_NAME.getExtensions(myProject)) {
327 for (VirtualFile file : policy.getExcludeRootsForProject()) {
328 addDirectoryToIgnoreImplicitly(file.getPath());
332 ProjectFileIndex fileIndex = ProjectFileIndex.SERVICE.getInstance(myProject);
333 VirtualFileManager virtualFileManager = VirtualFileManager.getInstance();
334 for (Module module : ModuleManager.getInstance(myProject).getModules()) {
335 for (String url : ModuleRootManager.getInstance(module).getExcludeRootUrls()) {
336 VirtualFile file = virtualFileManager.findFileByUrl(url);
337 if (file != null && !fileIndex.isExcluded(file)) {
338 //root is included into some inner module so it shouldn't be ignored
341 addDirectoryToIgnoreImplicitly(VfsUtilCore.urlToPath(url));
347 public void projectClosed() {
348 ProjectLevelVcsManager.getInstance(myProject).removeVcsListener(myVcsListener);
350 synchronized (myDataLock) {
351 myUpdateChangesProgressIndicator.cancel();
355 myConflictTracker.stopTracking();
360 public String getComponentName() {
361 return "ChangeListManager";
365 public void initComponent() {
369 public void disposeComponent() {
373 * update itself might produce actions done on AWT thread (invoked-after),
374 * so waiting for its completion on AWT thread is not good runnable is invoked on AWT thread
377 public void invokeAfterUpdate(final Runnable afterUpdate,
378 final InvokeAfterUpdateMode mode,
379 @Nullable final String title,
380 @Nullable final ModalityState state) {
381 myUpdater.invokeAfterUpdate(afterUpdate, mode, title, null, state);
385 public void invokeAfterUpdate(final Runnable afterUpdate, final InvokeAfterUpdateMode mode, final String title,
386 final Consumer<VcsDirtyScopeManager> dirtyScopeManagerFiller, final ModalityState state) {
387 myUpdater.invokeAfterUpdate(afterUpdate, mode, title, dirtyScopeManagerFiller, state);
390 static class DisposedException extends RuntimeException {}
393 public void freeze(final ContinuationPause context, final String reason) {
394 myUpdater.setIgnoreBackgroundOperation(true);
395 // this update is nessesary for git, to refresh local changes before
396 invokeAfterUpdate(new Runnable() {
399 freezeImmediately(reason);
402 }, InvokeAfterUpdateMode.SILENT_CALLBACK_POOLED, "", ModalityState.NON_MODAL);
407 public void freezeImmediately(@Nullable String reason) {
408 myUpdater.setIgnoreBackgroundOperation(false);
410 myUpdateChangesProgressIndicator.cancel();
411 myFreezeName.set(reason);
415 public void letGo() {
417 myFreezeName.set(null);
421 public String isFreezed() {
422 return myFreezeName.get();
426 public void scheduleUpdate() {
427 myUpdater.schedule();
431 public void scheduleUpdate(boolean updateUnversionedFiles) {
432 myUpdater.schedule();
435 private class ActualUpdater implements Runnable {
442 private void filterOutIgnoredFiles(final List<VcsDirtyScope> scopes) {
443 final Set<VirtualFile> refreshFiles = new HashSet<VirtualFile>();
445 synchronized (myDataLock) {
446 final IgnoredFilesHolder fileHolder = (IgnoredFilesHolder)myComposite.get(FileHolder.HolderType.IGNORED);
448 for (Iterator<VcsDirtyScope> iterator = scopes.iterator(); iterator.hasNext(); ) {
449 final VcsModifiableDirtyScope scope = (VcsModifiableDirtyScope)iterator.next();
450 final VcsDirtyScopeModifier modifier = scope.getModifier();
451 if (modifier != null) {
452 fileHolder.notifyVcsStarted(scope.getVcs());
453 final Iterator<FilePath> filesIterator = modifier.getDirtyFilesIterator();
454 while (filesIterator.hasNext()) {
455 final FilePath dirtyFile = filesIterator.next();
456 if ((dirtyFile.getVirtualFile() != null) && isIgnoredFile(dirtyFile.getVirtualFile())) {
457 filesIterator.remove();
458 fileHolder.addFile(dirtyFile.getVirtualFile());
459 refreshFiles.add(dirtyFile.getVirtualFile());
462 final Collection<VirtualFile> roots = modifier.getAffectedVcsRoots();
463 for (VirtualFile root : roots) {
464 final Iterator<FilePath> dirIterator = modifier.getDirtyDirectoriesIterator(root);
465 while (dirIterator.hasNext()) {
466 final FilePath dir = dirIterator.next();
467 if ((dir.getVirtualFile() != null) && isIgnoredFile(dir.getVirtualFile())) {
468 dirIterator.remove();
469 fileHolder.addFile(dir.getVirtualFile());
470 refreshFiles.add(dir.getVirtualFile());
474 modifier.recheckDirtyKeys();
475 if (scope.isEmpty()) {
482 catch (Exception ex) {
485 catch (AssertionError ex) {
488 for (VirtualFile file : refreshFiles) {
489 myFileStatusManager.fileStatusChanged(file);
493 private void updateImmediately() {
494 final ProjectLevelVcsManager vcsManager = ProjectLevelVcsManager.getInstance(myProject);
495 if (!vcsManager.hasActiveVcss()) return;
497 final VcsInvalidated invalidated = myDirtyScopeManager.retrieveScopes();
498 if (checkScopeIsEmpty(invalidated)) return;
500 final boolean wasEverythingDirty = invalidated.isEverythingDirty();
501 final List<VcsDirtyScope> scopes = invalidated.getScopes();
506 // copy existsing data to objects that would be updated.
507 // mark for "modifier" that update started (it would create duplicates of modification commands done by user during update;
508 // after update of copies of objects is complete, it would apply the same modifications to copies.)
509 final DataHolder dataHolder;
510 synchronized (myDataLock) {
511 dataHolder = new DataHolder((FileHolderComposite)myComposite.copy(), myWorker.copy(), wasEverythingDirty);
512 myModifier.enterUpdate();
513 if (wasEverythingDirty) {
514 myUpdateException = null;
515 myAdditionalInfo = null;
518 final String scopeInString = (!LOG.isDebugEnabled()) ? "" : StringUtil.join(scopes, new Function<VcsDirtyScope, String>() {
520 public String fun(VcsDirtyScope scope) {
521 return scope.toString();
524 LOG.debug("refresh procedure started, everything = " + wasEverythingDirty + " dirty scope: " + scopeInString);
525 dataHolder.notifyStart();
526 myChangesViewManager.scheduleRefresh();
528 myUpdateChangesProgressIndicator = createProgressIndicator();
530 iterateScopes(dataHolder, scopes, wasEverythingDirty);
532 final boolean takeChanges = (myUpdateException == null);
534 // update IDEA-level ignored files
535 updateIgnoredFiles(dataHolder.getComposite());
538 clearCurrentRevisionsCache(invalidated);
539 // for the case of project being closed we need a read action here -> to be more consistent
540 ApplicationManager.getApplication().runReadAction(new Runnable() {
543 if (myProject.isDisposed()) {
546 synchronized (myDataLock) {
547 // do same modifications to change lists as was done during update + do delayed notifications
548 dataHolder.notifyEnd();
549 // should be applied for notifications to be delivered (they were delayed) - anyway whether we take changes or not
550 myModifier.finishUpdate(dataHolder.getChangeListWorker());
551 // update member from copy
553 final ChangeListWorker oldWorker = myWorker;
554 myWorker = dataHolder.getChangeListWorker();
555 myWorker.onAfterWorkerSwitch(oldWorker);
556 myModifier.setWorker(myWorker);
557 LOG.debug("refresh procedure finished, unversioned size: " +
558 dataHolder.getComposite().getVFHolder(FileHolder.HolderType.UNVERSIONED).getSize() + "\n changes: " + myWorker);
559 final boolean statusChanged = !myComposite.equals(dataHolder.getComposite());
560 myComposite = dataHolder.getComposite();
562 myDelayedNotificator.getProxyDispatcher().unchangedFileStatusChanged();
565 myShowLocalChangesInvalidated = false;
570 for (VcsDirtyScope scope : scopes) {
571 AbstractVcs vcs = scope.getVcs();
572 if (vcs != null && vcs.isTrackingUnchangedContent()) {
573 scope.iterateExistingInsideScope(new Processor<VirtualFile>() {
575 public boolean process(VirtualFile file) {
576 LastUnchangedContentTracker.markUntouched(file); //todo what if it has become dirty again during update?
584 myChangesViewManager.scheduleRefresh();
586 catch (DisposedException e) {
587 // OK, we're finishing all the stuff now.
589 catch (ProcessCanceledException e) {
590 // OK, we're finishing all the stuff now.
592 catch (RuntimeInterruptedException ignore) {
594 catch (Exception ex) {
597 catch (AssertionError ex) {
601 myDirtyScopeManager.changesProcessed();
603 synchronized (myDataLock) {
604 myDelayedNotificator.getProxyDispatcher().changeListUpdateDone();
605 myChangesViewManager.scheduleRefresh();
610 private boolean checkScopeIsAllIgnored(VcsInvalidated invalidated) {
611 if (!invalidated.isEverythingDirty()) {
612 filterOutIgnoredFiles(invalidated.getScopes());
613 if (invalidated.isEmpty()) {
620 private boolean checkScopeIsEmpty(VcsInvalidated invalidated) {
621 if (invalidated == null || invalidated.isEmpty()) {
622 // a hack here; but otherwise everything here should be refactored ;)
623 if (invalidated != null && invalidated.isEmpty() && invalidated.isEverythingDirty()) {
624 VcsDirtyScopeManager.getInstance(myProject).markEverythingDirty();
628 return checkScopeIsAllIgnored(invalidated);
631 private void iterateScopes(DataHolder dataHolder, List<VcsDirtyScope> scopes, boolean wasEverythingDirty) {
632 final ChangeListManagerGate gate = dataHolder.getChangeListWorker().createSelfGate();
633 // do actual requests about file statuses
634 Getter<Boolean> disposedGetter = new Getter<Boolean>() {
636 public Boolean get() {
637 return myProject.isDisposed() || myUpdater.getIsStoppedGetter().get();
640 final UpdatingChangeListBuilder builder = new UpdatingChangeListBuilder(dataHolder.getChangeListWorker(),
641 dataHolder.getComposite(), disposedGetter, myIgnoredIdeaLevel,
644 for (final VcsDirtyScope scope : scopes) {
645 myUpdateChangesProgressIndicator.checkCanceled();
647 final AbstractVcs vcs = scope.getVcs();
648 if (vcs == null) continue;
649 scope.setWasEverythingDirty(wasEverythingDirty);
650 final VcsModifiableDirtyScope adjustedScope = vcs.adjustDirtyScope((VcsModifiableDirtyScope)scope);
652 myChangesViewManager.setBusy(true);
653 dataHolder.notifyStartProcessingChanges(adjustedScope);
655 actualUpdate(builder, adjustedScope, vcs, dataHolder, gate);
657 if (myUpdateException != null) break;
659 synchronized (myDataLock) {
660 if (myAdditionalInfo == null) {
661 myAdditionalInfo = builder.getAdditionalInfo();
666 private void clearCurrentRevisionsCache(final VcsInvalidated invalidated) {
667 final ContentRevisionCache cache = ProjectLevelVcsManager.getInstance(myProject).getContentRevisionCache();
668 if (invalidated.isEverythingDirty()) {
669 cache.clearAllCurrent();
672 cache.clearScope(invalidated.getScopes());
677 private static ProgressIndicator createProgressIndicator() {
678 return new EmptyProgressIndicator();
681 private class DataHolder {
682 private final boolean myWasEverythingDirty;
683 final FileHolderComposite myComposite;
684 final ChangeListWorker myChangeListWorker;
686 private DataHolder(FileHolderComposite composite, ChangeListWorker changeListWorker, boolean wasEverythingDirty) {
687 myComposite = composite;
688 myChangeListWorker = changeListWorker;
689 myWasEverythingDirty = wasEverythingDirty;
692 public void notifyStart() {
693 if (myWasEverythingDirty) {
694 myComposite.cleanAll();
695 myChangeListWorker.notifyStartProcessingChanges(null);
699 public void notifyStartProcessingChanges(@NotNull final VcsModifiableDirtyScope scope) {
700 if (!myWasEverythingDirty) {
701 myComposite.cleanAndAdjustScope(scope);
702 myChangeListWorker.notifyStartProcessingChanges(scope);
705 myComposite.notifyVcsStarted(scope.getVcs());
706 myChangeListWorker.notifyVcsStarted(scope.getVcs());
709 public void notifyDoneProcessingChanges() {
710 if (!myWasEverythingDirty) {
711 myChangeListWorker.notifyDoneProcessingChanges(myDelayedNotificator.getProxyDispatcher());
715 public void notifyEnd() {
716 if (myWasEverythingDirty) {
717 myChangeListWorker.notifyDoneProcessingChanges(myDelayedNotificator.getProxyDispatcher());
721 public FileHolderComposite getComposite() {
725 public ChangeListWorker getChangeListWorker() {
726 return myChangeListWorker;
730 private void actualUpdate(final UpdatingChangeListBuilder builder, final VcsDirtyScope scope, final AbstractVcs vcs,
731 final DataHolder dataHolder, final ChangeListManagerGate gate) {
733 final ChangeProvider changeProvider = vcs.getChangeProvider();
734 if (changeProvider != null) {
735 final FoldersCutDownWorker foldersCutDownWorker = new FoldersCutDownWorker();
737 builder.setCurrent(scope, foldersCutDownWorker);
738 changeProvider.getChanges(scope, builder, myUpdateChangesProgressIndicator, gate);
740 catch (final VcsException e) {
741 handleUpdateException(e);
745 catch (ProcessCanceledException ignore) {
747 catch (Throwable t) {
749 Rethrow.reThrowRuntime(t);
752 if (!myUpdater.isStopped()) {
753 dataHolder.notifyDoneProcessingChanges();
758 private void handleUpdateException(final VcsException e) {
761 if (e instanceof VcsConnectionProblem) {
762 ApplicationManager.getApplication().invokeLater(new Runnable() {
765 ((VcsConnectionProblem)e).attemptQuickFix(false);
770 if (myUpdateException == null) {
771 if (ApplicationManager.getApplication().isUnitTestMode()) {
772 AbstractVcsHelper helper = AbstractVcsHelper.getInstance(myProject);
773 if (helper instanceof AbstractVcsHelperImpl && ((AbstractVcsHelperImpl)helper).handleCustom(e)) {
776 //noinspection CallToPrintStackTrace
779 myUpdateException = e;
783 private void checkIfDisposed() {
784 if (myUpdater.isStopped()) throw new DisposedException();
787 public static boolean isUnder(final Change change, final VcsDirtyScope scope) {
788 final ContentRevision before = change.getBeforeRevision();
789 final ContentRevision after = change.getAfterRevision();
790 return before != null && scope.belongsTo(before.getFile()) || after != null && scope.belongsTo(after.getFile());
794 public List<LocalChangeList> getChangeListsCopy() {
795 synchronized (myDataLock) {
796 return myWorker.getListsCopy();
801 * @deprecated this method made equivalent to {@link #getChangeListsCopy()} so to don't be confused by method name,
802 * better use {@link #getChangeListsCopy()}
806 public List<LocalChangeList> getChangeLists() {
807 synchronized (myDataLock) {
808 return getChangeListsCopy();
813 public List<File> getAffectedPaths() {
814 synchronized (myDataLock) {
815 return myWorker.getAffectedPaths();
821 public List<VirtualFile> getAffectedFiles() {
822 synchronized (myDataLock) {
823 return myWorker.getAffectedFiles();
829 public Collection<Change> getAllChanges() {
830 synchronized (myDataLock) {
831 return myWorker.getAllChanges();
835 public List<VirtualFile> getUnversionedFiles() {
836 synchronized (myDataLock) {
837 return myComposite.getVFHolder(FileHolder.HolderType.UNVERSIONED).getFiles();
841 Couple<Integer> getUnversionedFilesSize() {
842 synchronized (myDataLock) {
843 final VirtualFileHolder holder = myComposite.getVFHolder(FileHolder.HolderType.UNVERSIONED);
844 return Couple.of(holder.getSize(), holder.getNumDirs());
849 public List<VirtualFile> getModifiedWithoutEditing() {
850 synchronized (myDataLock) {
851 return myComposite.getVFHolder(FileHolder.HolderType.MODIFIED_WITHOUT_EDITING).getFiles();
856 * @return only roots for ignored folders, and ignored files
858 List<VirtualFile> getIgnoredFiles() {
859 synchronized (myDataLock) {
860 return new ArrayList<VirtualFile>(myComposite.getIgnoredFileHolder().values());
864 public List<VirtualFile> getLockedFolders() {
865 synchronized (myDataLock) {
866 return myComposite.getVFHolder(FileHolder.HolderType.LOCKED).getFiles();
870 Map<VirtualFile, LogicalLock> getLogicallyLockedFolders() {
871 synchronized (myDataLock) {
872 return new HashMap<VirtualFile, LogicalLock>(
873 ((LogicallyLockedHolder)myComposite.get(FileHolder.HolderType.LOGICALLY_LOCKED)).getMap());
877 public boolean isLogicallyLocked(final VirtualFile file) {
878 synchronized (myDataLock) {
879 return ((LogicallyLockedHolder)myComposite.get(FileHolder.HolderType.LOGICALLY_LOCKED)).containsKey(file);
883 public boolean isContainedInLocallyDeleted(final FilePath filePath) {
884 synchronized (myDataLock) {
885 return myWorker.isContainedInLocallyDeleted(filePath);
889 public List<LocallyDeletedChange> getDeletedFiles() {
890 synchronized (myDataLock) {
891 return myWorker.getLocallyDeleted().getFiles();
895 MultiMap<String, VirtualFile> getSwitchedFilesMap() {
896 synchronized (myDataLock) {
897 return myWorker.getSwitchedHolder().getBranchToFileMap();
902 Map<VirtualFile, String> getSwitchedRoots() {
903 synchronized (myDataLock) {
904 return ((SwitchedFileHolder)myComposite.get(FileHolder.HolderType.ROOT_SWITCH)).getFilesMapCopy();
908 public VcsException getUpdateException() {
909 synchronized (myDataLock) {
910 return myUpdateException;
914 public Factory<JComponent> getAdditionalUpdateInfo() {
915 synchronized (myDataLock) {
916 return myAdditionalInfo;
921 public boolean isFileAffected(final VirtualFile file) {
922 synchronized (myDataLock) {
923 return myWorker.getStatus(file) != null;
929 public LocalChangeList findChangeList(final String name) {
930 synchronized (myDataLock) {
931 return myWorker.getCopyByName(name);
936 public LocalChangeList getChangeList(String id) {
937 synchronized (myDataLock) {
938 return myWorker.getChangeList(id);
943 public LocalChangeList addChangeList(@NotNull final String name, @Nullable final String comment) {
944 return addChangeList(name, comment, null);
948 public LocalChangeList addChangeList(@NotNull final String name, @Nullable final String comment, @Nullable final Object data) {
949 return ApplicationManager.getApplication().runReadAction(new Computable<LocalChangeList>() {
951 public LocalChangeList compute() {
952 synchronized (myDataLock) {
953 final LocalChangeList changeList = myModifier.addChangeList(name, comment, data);
954 myChangesViewManager.scheduleRefresh();
963 public void removeChangeList(final String name) {
964 ApplicationManager.getApplication().runReadAction(new Runnable() {
967 synchronized (myDataLock) {
968 myModifier.removeChangeList(name);
969 myChangesViewManager.scheduleRefresh();
976 public void removeChangeList(LocalChangeList list) {
977 removeChangeList(list.getName());
981 * does no modification to change lists, only notification is sent
985 public Runnable prepareForChangeDeletion(final Collection<Change> changes) {
986 final Map<String, LocalChangeList> lists = new HashMap<String, LocalChangeList>();
987 final Map<String, List<Change>> map;
988 synchronized (myDataLock) {
989 map = myWorker.listsForChanges(changes, lists);
991 return new Runnable() {
994 final ChangeListListener multicaster = myDelayedNotificator.getProxyDispatcher();
995 ApplicationManager.getApplication().runReadAction(new Runnable() {
998 synchronized (myDataLock) {
999 for (Map.Entry<String, List<Change>> entry : map.entrySet()) {
1000 final List<Change> changes = entry.getValue();
1001 for (Iterator<Change> iterator = changes.iterator(); iterator.hasNext(); ) {
1002 final Change change = iterator.next();
1003 if (getChangeList(change) != null) {
1004 // was not actually rolled back
1008 multicaster.changesRemoved(changes, lists.get(entry.getKey()));
1010 for (String listName : map.keySet()) {
1011 final LocalChangeList byName = myWorker.getCopyByName(listName);
1012 if (byName != null && byName.getChanges().isEmpty() && !byName.isDefault() && !byName.isReadOnly()) {
1013 myWorker.removeChangeList(listName);
1024 public void setDefaultChangeList(@NotNull final LocalChangeList list) {
1025 ApplicationManager.getApplication().runReadAction(new Runnable() {
1028 synchronized (myDataLock) {
1029 myModifier.setDefault(list.getName());
1033 myChangesViewManager.scheduleRefresh();
1038 public LocalChangeList getDefaultChangeList() {
1039 synchronized (myDataLock) {
1040 return myWorker.getDefaultListCopy();
1045 public boolean isDefaultChangeList(ChangeList list) {
1046 return list instanceof LocalChangeList && myWorker.isDefaultList((LocalChangeList)list);
1051 public Collection<LocalChangeList> getInvolvedListsFilterChanges(final Collection<Change> changes, final List<Change> validChanges) {
1052 synchronized (myDataLock) {
1053 return myWorker.getInvolvedListsFilterChanges(changes, validChanges);
1059 public LocalChangeList getChangeList(@NotNull Change change) {
1060 synchronized (myDataLock) {
1061 return myWorker.listForChange(change);
1066 public String getChangeListNameIfOnlyOne(final Change[] changes) {
1067 synchronized (myDataLock) {
1068 return myWorker.listNameIfOnlyOne(changes);
1073 * @deprecated better use normal comparison, with equals
1077 public LocalChangeList getIdentityChangeList(Change change) {
1078 synchronized (myDataLock) {
1079 final List<LocalChangeList> lists = myWorker.getListsCopy();
1080 for (LocalChangeList list : lists) {
1081 for (Change oldChange : list.getChanges()) {
1082 if (oldChange == change) {
1092 public boolean isInUpdate() {
1093 synchronized (myDataLock) {
1094 return myModifier.isInsideUpdate() || myShowLocalChangesInvalidated;
1100 public Change getChange(@NotNull VirtualFile file) {
1101 synchronized (myDataLock) {
1102 final LocalChangeList list = myWorker.getListCopy(file);
1104 for (Change change : list.getChanges()) {
1105 final ContentRevision afterRevision = change.getAfterRevision();
1106 if (afterRevision != null) {
1107 String revisionPath = FileUtil.toSystemIndependentName(afterRevision.getFile().getIOFile().getPath());
1108 if (FileUtil.pathsEqual(revisionPath, file.getPath())) return change;
1110 final ContentRevision beforeRevision = change.getBeforeRevision();
1111 if (beforeRevision != null) {
1112 String revisionPath = FileUtil.toSystemIndependentName(beforeRevision.getFile().getIOFile().getPath());
1113 if (FileUtil.pathsEqual(revisionPath, file.getPath())) return change;
1123 public LocalChangeList getChangeList(@NotNull VirtualFile file) {
1124 synchronized (myDataLock) {
1125 return myWorker.getListCopy(file);
1131 public Change getChange(final FilePath file) {
1132 synchronized (myDataLock) {
1133 return myWorker.getChangeForPath(file);
1138 public boolean isUnversioned(VirtualFile file) {
1139 synchronized (myDataLock) {
1140 return myComposite.getVFHolder(FileHolder.HolderType.UNVERSIONED).containsFile(file);
1146 public FileStatus getStatus(VirtualFile file) {
1147 synchronized (myDataLock) {
1148 if (myComposite.getVFHolder(FileHolder.HolderType.UNVERSIONED).containsFile(file)) return FileStatus.UNKNOWN;
1149 if (myComposite.getVFHolder(FileHolder.HolderType.MODIFIED_WITHOUT_EDITING).containsFile(file)) return FileStatus.HIJACKED;
1150 if (myComposite.getIgnoredFileHolder().containsFile(file)) return FileStatus.IGNORED;
1152 final boolean switched = myWorker.isSwitched(file);
1153 final FileStatus status = myWorker.getStatus(file);
1154 if (status != null) {
1155 return FileStatus.NOT_CHANGED.equals(status) && switched ? FileStatus.SWITCHED : status;
1157 if (switched) return FileStatus.SWITCHED;
1158 return FileStatus.NOT_CHANGED;
1164 public Collection<Change> getChangesIn(VirtualFile dir) {
1165 return getChangesIn(new FilePathImpl(dir));
1170 public ThreeState haveChangesUnder(@NotNull final VirtualFile vf) {
1171 if (!vf.isValid() || !vf.isDirectory()) return ThreeState.NO;
1172 synchronized (myDataLock) {
1173 return myWorker.haveChangesUnder(vf);
1179 public Collection<Change> getChangesIn(final FilePath dirPath) {
1180 synchronized (myDataLock) {
1181 return myWorker.getChangesIn(dirPath);
1186 public void moveChangesTo(final LocalChangeList list, final Change... changes) {
1187 ApplicationManager.getApplication().runReadAction(new Runnable() {
1190 synchronized (myDataLock) {
1191 myModifier.moveChangesTo(list.getName(), changes);
1195 myChangesViewManager.scheduleRefresh();
1199 public void addUnversionedFiles(final LocalChangeList list, @NotNull final List<VirtualFile> files) {
1200 addUnversionedFiles(list, files, new Condition<FileStatus>() {
1202 public boolean value(FileStatus status) {
1203 return status == FileStatus.UNKNOWN;
1208 // TODO this is for quick-fix for GitAdd problem. To be removed after proper fix
1209 // (which should introduce something like VcsAddRemoveEnvironment)
1211 public void addUnversionedFiles(final LocalChangeList list, @NotNull final List<VirtualFile> files,
1212 final Condition<FileStatus> statusChecker) {
1213 final List<VcsException> exceptions = new ArrayList<VcsException>();
1214 final Set<VirtualFile> allProcessedFiles = new HashSet<VirtualFile>();
1215 ChangesUtil.processVirtualFilesByVcs(myProject, files, new ChangesUtil.PerVcsProcessor<VirtualFile>() {
1217 public void process(final AbstractVcs vcs, final List<VirtualFile> items) {
1218 final CheckinEnvironment environment = vcs.getCheckinEnvironment();
1219 if (environment != null) {
1220 Set<VirtualFile> descendants = getUnversionedDescendantsRecursively(items, statusChecker);
1221 Set<VirtualFile> parents =
1222 vcs.areDirectoriesVersionedItems() ? getUnversionedParents(items, statusChecker) : Collections.<VirtualFile>emptySet();
1224 // it is assumed that not-added parents of files passed to scheduleUnversionedFilesForAddition() will also be added to vcs
1225 // (inside the method) - so common add logic just needs to refresh statuses of parents
1226 List<VcsException> result = environment.scheduleUnversionedFilesForAddition(ContainerUtil.newArrayList(descendants));
1228 allProcessedFiles.addAll(descendants);
1229 allProcessedFiles.addAll(parents);
1230 if (result != null) {
1231 exceptions.addAll(result);
1237 if (exceptions.size() > 0) {
1238 StringBuilder message = new StringBuilder(VcsBundle.message("error.adding.files.prompt"));
1239 for (VcsException ex : exceptions) {
1240 message.append("\n").append(ex.getMessage());
1242 Messages.showErrorDialog(myProject, message.toString(), VcsBundle.message("error.adding.files.title"));
1245 for (VirtualFile file : allProcessedFiles) {
1246 myFileStatusManager.fileStatusChanged(file);
1248 VcsDirtyScopeManager.getInstance(myProject).filesDirty(allProcessedFiles, null);
1250 if (!list.isDefault()) {
1251 // find the changes for the added files and move them to the necessary changelist
1252 invokeAfterUpdate(new Runnable() {
1255 ApplicationManager.getApplication().runReadAction(new Runnable() {
1258 synchronized (myDataLock) {
1259 List<Change> changesToMove = new ArrayList<Change>();
1260 final LocalChangeList defaultList = getDefaultChangeList();
1261 for (Change change : defaultList.getChanges()) {
1262 final ContentRevision afterRevision = change.getAfterRevision();
1263 if (afterRevision != null) {
1264 VirtualFile vFile = afterRevision.getFile().getVirtualFile();
1265 if (allProcessedFiles.contains(vFile)) {
1266 changesToMove.add(change);
1271 if (changesToMove.size() > 0) {
1272 moveChangesTo(list, changesToMove.toArray(new Change[changesToMove.size()]));
1278 myChangesViewManager.scheduleRefresh();
1280 }, InvokeAfterUpdateMode.BACKGROUND_NOT_CANCELLABLE_NOT_AWT, VcsBundle.message("change.lists.manager.add.unversioned"), null);
1283 myChangesViewManager.scheduleRefresh();
1288 private Set<VirtualFile> getUnversionedDescendantsRecursively(@NotNull List<VirtualFile> items,
1289 @NotNull final Condition<FileStatus> condition) {
1290 final Set<VirtualFile> result = ContainerUtil.newHashSet();
1291 Processor<VirtualFile> addToResultProcessor = new Processor<VirtualFile>() {
1293 public boolean process(VirtualFile file) {
1294 if (condition.value(getStatus(file))) {
1301 for (VirtualFile item : items) {
1302 VcsRootIterator.iterateVfUnderVcsRoot(myProject, item, addToResultProcessor);
1309 private Set<VirtualFile> getUnversionedParents(@NotNull Collection<VirtualFile> items, @NotNull Condition<FileStatus> condition) {
1310 HashSet<VirtualFile> result = ContainerUtil.newHashSet();
1312 for (VirtualFile item : items) {
1313 VirtualFile parent = item.getParent();
1315 while (parent != null && condition.value(getStatus(parent))) {
1317 parent = parent.getParent();
1325 public Project getProject() {
1330 public void addChangeListListener(ChangeListListener listener) {
1331 myListeners.addListener(listener);
1336 public void removeChangeListListener(ChangeListListener listener) {
1337 myListeners.removeListener(listener);
1341 public void registerCommitExecutor(CommitExecutor executor) {
1342 myExecutors.add(executor);
1346 public void commitChanges(LocalChangeList changeList, List<Change> changes) {
1347 doCommit(changeList, changes, false);
1350 private boolean doCommit(final LocalChangeList changeList, final List<Change> changes, final boolean synchronously) {
1351 FileDocumentManager.getInstance().saveAllDocuments();
1352 return new CommitHelper(myProject, changeList, changes, changeList.getName(),
1353 StringUtil.isEmpty(changeList.getComment()) ? changeList.getName() : changeList.getComment(),
1354 new ArrayList<CheckinHandler>(), false, synchronously, FunctionUtil.nullConstant(), null).doCommit();
1358 public void commitChangesSynchronously(LocalChangeList changeList, List<Change> changes) {
1359 doCommit(changeList, changes, true);
1363 public boolean commitChangesSynchronouslyWithResult(final LocalChangeList changeList, final List<Change> changes) {
1364 return doCommit(changeList, changes, true);
1368 @SuppressWarnings({"unchecked"})
1369 public void readExternal(Element element) throws InvalidDataException {
1370 if (!myProject.isDefault()) {
1371 synchronized (myDataLock) {
1372 myIgnoredIdeaLevel.clear();
1373 new ChangeListManagerSerialization(myIgnoredIdeaLevel, myWorker).readExternal(element);
1374 if ((!myWorker.isEmpty()) && getDefaultChangeList() == null) {
1375 setDefaultChangeList(myWorker.getListsCopy().get(0));
1378 myExcludedConvertedToIgnored = Boolean.parseBoolean(JDOMExternalizerUtil.readField(element, EXCLUDED_CONVERTED_TO_IGNORED_OPTION));
1379 myConflictTracker.loadState(element);
1384 public void writeExternal(Element element) throws WriteExternalException {
1385 if (!myProject.isDefault()) {
1386 final IgnoredFilesComponent ignoredFilesComponent;
1387 final ChangeListWorker worker;
1388 synchronized (myDataLock) {
1389 ignoredFilesComponent = new IgnoredFilesComponent(myIgnoredIdeaLevel);
1390 worker = myWorker.copy();
1392 new ChangeListManagerSerialization(ignoredFilesComponent, worker).writeExternal(element);
1393 if (myExcludedConvertedToIgnored) {
1394 JDOMExternalizerUtil.writeField(element, EXCLUDED_CONVERTED_TO_IGNORED_OPTION, String.valueOf(true));
1396 myConflictTracker.saveState(element);
1402 public void reopenFiles(List<FilePath> paths) {
1403 final ReadonlyStatusHandlerImpl readonlyStatusHandler = (ReadonlyStatusHandlerImpl)ReadonlyStatusHandler.getInstance(myProject);
1404 final boolean savedOption = readonlyStatusHandler.getState().SHOW_DIALOG;
1405 readonlyStatusHandler.getState().SHOW_DIALOG = false;
1407 readonlyStatusHandler.ensureFilesWritable(collectFiles(paths));
1410 readonlyStatusHandler.getState().SHOW_DIALOG = savedOption;
1415 public List<CommitExecutor> getRegisteredExecutors() {
1416 return Collections.unmodifiableList(myExecutors);
1419 private static class MyDirtyFilesScheduler {
1420 private static final int ourPiecesLimit = 100;
1421 final List<VirtualFile> myFiles = new ArrayList<VirtualFile>();
1422 final List<VirtualFile> myDirs = new ArrayList<VirtualFile>();
1423 private boolean myEveryThing;
1425 private final Project myProject;
1427 private MyDirtyFilesScheduler(final Project project) {
1428 myProject = project;
1430 myEveryThing = false;
1433 public void accept(final Collection<VirtualFile> coll) {
1434 for (VirtualFile vf : coll) {
1435 if (myCnt > ourPiecesLimit) {
1436 myEveryThing = true;
1439 if (vf.isDirectory()) {
1449 public void arise() {
1450 final VcsDirtyScopeManager vcsDirtyScopeManager = VcsDirtyScopeManager.getInstance(myProject);
1452 vcsDirtyScopeManager.markEverythingDirty();
1455 vcsDirtyScopeManager.filesDirty(myFiles, myDirs);
1461 public void addFilesToIgnore(final IgnoredFileBean... filesToIgnore) {
1462 myIgnoredIdeaLevel.add(filesToIgnore);
1463 scheduleUnversionedUpdate();
1467 public void addDirectoryToIgnoreImplicitly(@NotNull String path) {
1468 myIgnoredIdeaLevel.addIgnoredDirectoryImplicitly(path, myProject);
1471 public IgnoredFilesComponent getIgnoredFilesComponent() {
1472 return myIgnoredIdeaLevel;
1475 private void scheduleUnversionedUpdate() {
1476 final MyDirtyFilesScheduler scheduler = new MyDirtyFilesScheduler(myProject);
1478 synchronized (myDataLock) {
1479 final VirtualFileHolder unversionedHolder = myComposite.getVFHolder(FileHolder.HolderType.UNVERSIONED);
1480 final IgnoredFilesHolder ignoredHolder = (IgnoredFilesHolder)myComposite.get(FileHolder.HolderType.IGNORED);
1482 scheduler.accept(unversionedHolder.getFiles());
1483 scheduler.accept(ignoredHolder.values());
1490 public void setFilesToIgnore(final IgnoredFileBean... filesToIgnore) {
1491 myIgnoredIdeaLevel.set(filesToIgnore);
1492 scheduleUnversionedUpdate();
1495 private void updateIgnoredFiles(final FileHolderComposite composite) {
1496 final VirtualFileHolder vfHolder = composite.getVFHolder(FileHolder.HolderType.UNVERSIONED);
1497 final List<VirtualFile> unversionedFiles = vfHolder.getFiles();
1498 exchangeWithIgnored(composite, vfHolder, unversionedFiles);
1500 final VirtualFileHolder vfModifiedHolder = composite.getVFHolder(FileHolder.HolderType.MODIFIED_WITHOUT_EDITING);
1501 final List<VirtualFile> modifiedFiles = vfModifiedHolder.getFiles();
1502 exchangeWithIgnored(composite, vfModifiedHolder, modifiedFiles);
1505 private void exchangeWithIgnored(FileHolderComposite composite, VirtualFileHolder vfHolder, List<VirtualFile> unversionedFiles) {
1506 for (VirtualFile file : unversionedFiles) {
1507 if (isIgnoredFile(file)) {
1508 vfHolder.removeFile(file);
1509 composite.getIgnoredFileHolder().addFile(file);
1515 public IgnoredFileBean[] getFilesToIgnore() {
1516 return myIgnoredIdeaLevel.getFilesToIgnore();
1520 public boolean isIgnoredFile(@NotNull VirtualFile file) {
1521 return myIgnoredIdeaLevel.isIgnoredFile(file);
1526 public String getSwitchedBranch(final VirtualFile file) {
1527 synchronized (myDataLock) {
1528 return myWorker.getBranchForFile(file);
1533 public String getDefaultListName() {
1534 synchronized (myDataLock) {
1535 return myWorker.getDefaultListName();
1539 private static VirtualFile[] collectFiles(final List<FilePath> paths) {
1540 final ArrayList<VirtualFile> result = new ArrayList<VirtualFile>();
1541 for (FilePath path : paths) {
1542 if (path.getVirtualFile() != null) {
1543 result.add(path.getVirtualFile());
1547 return VfsUtilCore.toVirtualFileArray(result);
1551 public boolean setReadOnly(final String name, final boolean value) {
1552 return ApplicationManager.getApplication().runReadAction(new Computable<Boolean>() {
1554 public Boolean compute() {
1555 synchronized (myDataLock) {
1556 final boolean result = myModifier.setReadOnly(name, value);
1557 myChangesViewManager.scheduleRefresh();
1565 public boolean editName(@NotNull final String fromName, @NotNull final String toName) {
1566 return ApplicationManager.getApplication().runReadAction(new Computable<Boolean>() {
1568 public Boolean compute() {
1569 synchronized (myDataLock) {
1570 final boolean result = myModifier.editName(fromName, toName);
1571 myChangesViewManager.scheduleRefresh();
1579 public String editComment(@NotNull final String fromName, final String newComment) {
1580 return ApplicationManager.getApplication().runReadAction(new Computable<String>() {
1582 public String compute() {
1583 synchronized (myDataLock) {
1584 final String oldComment = myModifier.editComment(fromName, newComment);
1585 myChangesViewManager.scheduleRefresh();
1593 public void waitUntilRefreshed() {
1594 myVfsListener.flushDirt();
1595 myUpdater.waitUntilRefreshed();
1599 // this is for perforce tests to ensure that LastSuccessfulUpdateTracker receives the event it needs
1600 private static void waitUpdateAlarm() {
1601 final Semaphore semaphore = new Semaphore();
1603 ourUpdateAlarm.get().execute(new Runnable() {
1609 semaphore.waitFor();
1612 public void stopEveryThingIfInTestMode() {
1613 assert ApplicationManager.getApplication().isUnitTestMode();
1614 ourUpdateAlarm.get().shutdownNow();
1615 ourUpdateAlarm.set(createChangeListExecutor());
1618 public void forceGoInTestMode() {
1619 assert ApplicationManager.getApplication().isUnitTestMode();
1620 myUpdater.forceGo();
1623 public void executeOnUpdaterThread(Runnable r) {
1624 ourUpdateAlarm.get().execute(r);
1629 public boolean ensureUpToDate(final boolean canBeCanceled) {
1630 if (ApplicationManager.getApplication().isDispatchThread()) {
1631 updateImmediately();
1634 myVfsListener.flushDirt();
1635 final EnsureUpToDateFromNonAWTThread worker = new EnsureUpToDateFromNonAWTThread(myProject);
1637 myUpdater.waitUntilRefreshed();
1639 return worker.isDone();
1643 public int getChangeListsNumber() {
1644 synchronized (myDataLock) {
1645 return myWorker.getChangeListsNumber();
1649 // only a light attempt to show that some dirty scope request is asynchronously coming
1650 // for users to see changes are not valid
1651 // (commit -> asynch synch VFS -> asynch vcs dirty scope)
1652 public void showLocalChangesInvalidated() {
1653 synchronized (myDataLock) {
1654 myShowLocalChangesInvalidated = true;
1658 public ChangelistConflictTracker getConflictTracker() {
1659 return myConflictTracker;
1662 private static class MyChangesDeltaForwarder implements PlusMinusModify<BaseRevision> {
1663 private final RemoteRevisionsCache myRevisionsCache;
1664 private final ProjectLevelVcsManager myVcsManager;
1665 private final Project myProject;
1666 private final AtomicReference<ScheduledExecutorService> myService;
1668 public MyChangesDeltaForwarder(final Project project, final AtomicReference<ScheduledExecutorService> service) {
1669 myProject = project;
1670 myService = service;
1671 myRevisionsCache = RemoteRevisionsCache.getInstance(project);
1672 myVcsManager = ProjectLevelVcsManager.getInstance(project);
1676 public void modify(final BaseRevision was, final BaseRevision become) {
1677 myService.get().submit(new Runnable() {
1680 final AbstractVcs vcs = getVcs(was);
1682 myRevisionsCache.plus(Pair.create(was.getPath(), vcs));
1684 // maybe define modify method?
1685 myProject.getMessageBus().syncPublisher(VcsAnnotationRefresher.LOCAL_CHANGES_CHANGED).dirty(become);
1691 public void plus(final BaseRevision baseRevision) {
1692 myService.get().submit(new Runnable() {
1695 final AbstractVcs vcs = getVcs(baseRevision);
1697 myRevisionsCache.plus(Pair.create(baseRevision.getPath(), vcs));
1699 myProject.getMessageBus().syncPublisher(VcsAnnotationRefresher.LOCAL_CHANGES_CHANGED).dirty(baseRevision);
1705 public void minus(final BaseRevision baseRevision) {
1706 myService.get().submit(new Runnable() {
1709 final AbstractVcs vcs = getVcs(baseRevision);
1711 myRevisionsCache.minus(Pair.create(baseRevision.getPath(), vcs));
1713 myProject.getMessageBus().syncPublisher(VcsAnnotationRefresher.LOCAL_CHANGES_CHANGED).dirty(baseRevision.getPath());
1719 private AbstractVcs getVcs(final BaseRevision baseRevision) {
1720 VcsKey vcsKey = baseRevision.getVcs();
1721 if (vcsKey == null) {
1722 final String path = baseRevision.getPath();
1723 vcsKey = findVcs(path);
1724 if (vcsKey == null) return null;
1726 return myVcsManager.findVcsByName(vcsKey.getName());
1730 private VcsKey findVcs(final String path) {
1731 // does not matter directory or not
1732 final VirtualFile vf = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(new File(path));
1733 if (vf == null) return null;
1734 final AbstractVcs vcs = myVcsManager.getVcsFor(vf);
1735 return vcs == null ? null : vcs.getKeyInstanceMethod();
1740 public boolean isFreezedWithNotification(String modalTitle) {
1741 final String freezeReason = isFreezed();
1742 if (freezeReason != null) {
1743 if (modalTitle != null) {
1744 Messages.showErrorDialog(myProject, freezeReason, modalTitle);
1747 VcsBalloonProblemNotifier.showOverChangesView(myProject, freezeReason, MessageType.WARNING);
1750 return freezeReason != null;