1 package com
.intellij
.openapi
.vcs
.changes
;
3 import com
.intellij
.ide
.highlighter
.WorkspaceFileType
;
4 import com
.intellij
.lifecycle
.AtomicSectionsAware
;
5 import com
.intellij
.lifecycle
.ControlledAlarmFactory
;
6 import com
.intellij
.lifecycle
.SlowlyClosingAlarm
;
7 import com
.intellij
.openapi
.application
.ApplicationManager
;
8 import com
.intellij
.openapi
.application
.ModalityState
;
9 import com
.intellij
.openapi
.components
.ProjectComponent
;
10 import com
.intellij
.openapi
.diagnostic
.Logger
;
11 import com
.intellij
.openapi
.progress
.EmptyProgressIndicator
;
12 import com
.intellij
.openapi
.progress
.ProcessCanceledException
;
13 import com
.intellij
.openapi
.progress
.ProgressIndicator
;
14 import com
.intellij
.openapi
.project
.DumbAwareRunnable
;
15 import com
.intellij
.openapi
.project
.Project
;
16 import com
.intellij
.openapi
.startup
.StartupManager
;
17 import com
.intellij
.openapi
.ui
.Messages
;
18 import com
.intellij
.openapi
.util
.*;
19 import com
.intellij
.openapi
.util
.io
.FileUtil
;
20 import com
.intellij
.openapi
.vcs
.*;
21 import com
.intellij
.openapi
.vcs
.changes
.ui
.CommitHelper
;
22 import com
.intellij
.openapi
.vcs
.checkin
.CheckinEnvironment
;
23 import com
.intellij
.openapi
.vcs
.checkin
.CheckinHandler
;
24 import com
.intellij
.openapi
.vcs
.readOnlyHandler
.ReadonlyStatusHandlerImpl
;
25 import com
.intellij
.openapi
.vfs
.VirtualFile
;
26 import com
.intellij
.util
.ConcurrencyUtil
;
27 import com
.intellij
.util
.Consumer
;
28 import com
.intellij
.util
.EventDispatcher
;
29 import com
.intellij
.util
.containers
.MultiMap
;
30 import com
.intellij
.util
.messages
.Topic
;
31 import org
.jdom
.Element
;
32 import org
.jetbrains
.annotations
.NonNls
;
33 import org
.jetbrains
.annotations
.NotNull
;
34 import org
.jetbrains
.annotations
.Nullable
;
39 import java
.util
.concurrent
.ExecutorService
;
40 import java
.util
.concurrent
.ScheduledExecutorService
;
45 public class ChangeListManagerImpl
extends ChangeListManagerEx
implements ProjectComponent
, ChangeListOwner
, JDOMExternalizable
{
46 private static final Logger LOG
= Logger
.getInstance("#com.intellij.openapi.vcs.changes.ChangeListManagerImpl");
48 private final Project myProject
;
49 private final ChangesViewManager myChangesViewManager
;
50 private final FileStatusManager myFileStatusManager
;
51 private final UpdateRequestsQueue myUpdater
;
53 @SuppressWarnings({"FieldAccessedSynchronizedAndUnsynchronized"})
54 private static final ScheduledExecutorService ourUpdateAlarm
= ConcurrencyUtil
.newSingleScheduledThreadExecutor("Change List Updater");
56 private final Modifier myModifier
;
58 private FileHolderComposite myComposite
;
60 private final ChangeListWorker myWorker
;
61 private VcsException myUpdateException
= null;
63 @SuppressWarnings({"FieldAccessedSynchronizedAndUnsynchronized"})
64 private final EventDispatcher
<ChangeListListener
> myListeners
= EventDispatcher
.create(ChangeListListener
.class);
66 private final Object myDataLock
= new Object();
68 private final List
<CommitExecutor
> myExecutors
= new ArrayList
<CommitExecutor
>();
70 private final IgnoredFilesComponent myIgnoredIdeaLevel
;
71 private ProgressIndicator myUpdateChangesProgressIndicator
;
73 public static final Key
<Object
> DOCUMENT_BEING_COMMITTED_KEY
= new Key
<Object
>("DOCUMENT_BEING_COMMITTED");
75 public static final Topic
<LocalChangeListsLoadedListener
> LISTS_LOADED
= new Topic
<LocalChangeListsLoadedListener
>(
76 "LOCAL_CHANGE_LISTS_LOADED", LocalChangeListsLoadedListener
.class);
78 private boolean myShowLocalChangesInvalidated
;
80 private final DelayedNotificator myDelayedNotificator
;
82 private final VcsListener myVcsListener
= new VcsListener() {
83 public void directoryMappingChanged() {
84 VcsDirtyScopeManager
.getInstanceChecked(myProject
).markEverythingDirty();
89 public static ChangeListManagerImpl
getInstanceImpl(final Project project
) {
90 return (ChangeListManagerImpl
) project
.getComponent(ChangeListManager
.class);
93 public ChangeListManagerImpl(final Project project
) {
95 myChangesViewManager
= ChangesViewManager
.getInstance(myProject
);
96 myFileStatusManager
= FileStatusManager
.getInstance(myProject
);
97 myComposite
= new FileHolderComposite(project
);
98 myIgnoredIdeaLevel
= new IgnoredFilesComponent(myProject
);
99 myUpdater
= new UpdateRequestsQueue(myProject
, ourUpdateAlarm
, new ActualUpdater());
101 myWorker
= new ChangeListWorker(myProject
, new MyChangesDeltaForwarder(myProject
, ourUpdateAlarm
));
102 myDelayedNotificator
= new DelayedNotificator(myListeners
, ourUpdateAlarm
);
103 myModifier
= new Modifier(myWorker
, myDelayedNotificator
);
106 public void projectOpened() {
107 initializeForNewProject();
109 if (ApplicationManager
.getApplication().isUnitTestMode()) {
110 myWorker
.initialized();
111 myUpdater
.initialized();
112 ProjectLevelVcsManager
.getInstance(myProject
).addVcsListener(myVcsListener
);
115 StartupManager
.getInstance(myProject
).registerPostStartupActivity(new DumbAwareRunnable() {
117 myWorker
.initialized();
118 myUpdater
.initialized();
119 broadcastStateAfterLoad();
120 ProjectLevelVcsManager
.getInstance(myProject
).addVcsListener(myVcsListener
);
126 private void broadcastStateAfterLoad() {
127 final List
<LocalChangeList
> listCopy
;
128 synchronized (myDataLock
) {
129 listCopy
= getChangeListsCopy();
131 if (! listCopy
.isEmpty()) {
132 myProject
.getMessageBus().syncPublisher(LISTS_LOADED
).processLoadedLists(listCopy
);
136 private void initializeForNewProject() {
137 synchronized (myDataLock
) {
138 if (myWorker
.isEmpty()) {
139 final LocalChangeList list
= myWorker
.addChangeList(VcsBundle
.message("changes.default.changlist.name"), null);
140 setDefaultChangeList(list
);
142 if (myIgnoredIdeaLevel
.isEmpty()) {
143 final String name
= myProject
.getName();
144 myIgnoredIdeaLevel
.add(IgnoredBeanFactory
.ignoreFile(name
+ WorkspaceFileType
.DOT_DEFAULT_EXTENSION
, myProject
));
145 myIgnoredIdeaLevel
.add(IgnoredBeanFactory
.ignoreFile(Project
.DIRECTORY_STORE_FOLDER
+ "/workspace.xml", myProject
));
151 public void projectClosed() {
152 ProjectLevelVcsManager
.getInstance(myProject
).removeVcsListener(myVcsListener
);
154 synchronized (myDataLock
) {
155 if (myUpdateChangesProgressIndicator
!= null) {
156 myUpdateChangesProgressIndicator
.cancel();
164 public String
getComponentName() {
165 return "ChangeListManager";
168 public void initComponent() {
171 public void disposeComponent() {
175 * update itself might produce actions done on AWT thread (invoked-after),
176 * so waiting for its completion on AWT thread is not good
178 * runnable is invoked on AWT thread
180 public void invokeAfterUpdate(final Runnable afterUpdate
, final InvokeAfterUpdateMode mode
, final String title
, final ModalityState state
) {
181 myUpdater
.invokeAfterUpdate(afterUpdate
, mode
, title
, null, state
);
184 public void invokeAfterUpdate(final Runnable afterUpdate
, final InvokeAfterUpdateMode mode
, final String title
,
185 final Consumer
<VcsDirtyScopeManager
> dirtyScopeManagerFiller
, final ModalityState state
) {
186 myUpdater
.invokeAfterUpdate(afterUpdate
, mode
, title
, dirtyScopeManagerFiller
, state
);
189 static class DisposedException
extends RuntimeException
{}
191 public void scheduleUpdate() {
192 myUpdater
.schedule(true);
195 public void scheduleUpdate(boolean updateUnversionedFiles
) {
196 myUpdater
.schedule(updateUnversionedFiles
);
199 private class ActualUpdater
implements LocalChangesUpdater
{
200 public void execute(boolean updateUnversioned
, AtomicSectionsAware atomicSectionsAware
) {
201 updateImmediately(updateUnversioned
, atomicSectionsAware
);
205 private void updateImmediately(final boolean updateUnversionedFiles
, final AtomicSectionsAware atomicSectionsAware
) {
206 FileHolderComposite composite
;
207 ChangeListWorker changeListWorker
;
209 final VcsDirtyScopeManagerImpl dirtyScopeManager
;
211 dirtyScopeManager
= ((VcsDirtyScopeManagerImpl
) VcsDirtyScopeManager
.getInstanceChecked(myProject
));
213 catch(ProcessCanceledException ex
) {
216 catch(Exception ex
) {
220 final VcsInvalidated invalidated
= dirtyScopeManager
.retrieveScopes();
221 if (invalidated
== null || invalidated
.isEmpty()) {
224 final boolean wasEverythingDirty
= invalidated
.isEverythingDirty();
225 final List
<VcsDirtyScope
> scopes
= invalidated
.getScopes();
230 // copy existsing data to objects that would be updated.
231 // mark for "modifier" that update started (it would create duplicates of modification commands done by user during update;
232 // after update of copies of objects is complete, it would apply the same modifications to copies.)
233 synchronized (myDataLock
) {
234 changeListWorker
= myWorker
.copy();
235 composite
= updateUnversionedFiles ?
(FileHolderComposite
) myComposite
.copy() : myComposite
;
236 myModifier
.enterUpdate();
237 if (wasEverythingDirty
) {
238 myUpdateException
= null;
240 if (updateUnversionedFiles
&& wasEverythingDirty
) {
241 composite
.cleanAll();
244 if (wasEverythingDirty
) {
245 changeListWorker
.notifyStartProcessingChanges(null);
247 myChangesViewManager
.scheduleRefresh();
249 final ChangeListManagerGate gate
= changeListWorker
.createSelfGate();
251 // do actual requests about file statuses
252 final UpdatingChangeListBuilder builder
= new UpdatingChangeListBuilder(changeListWorker
, composite
, new Getter
<Boolean
>() {
253 public Boolean
get() {
254 return myUpdater
.isStopped();
256 }, updateUnversionedFiles
, myIgnoredIdeaLevel
, gate
);
258 myUpdateChangesProgressIndicator
= new EmptyProgressIndicator() {
260 public boolean isCanceled() {
261 return myUpdater
.isStopped() || atomicSectionsAware
.shouldExitAsap();
264 public void checkCanceled() {
266 atomicSectionsAware
.checkShouldExit();
269 for (final VcsDirtyScope scope
: scopes
) {
270 atomicSectionsAware
.checkShouldExit();
272 final AbstractVcs vcs
= scope
.getVcs();
273 if (vcs
== null) continue;
275 myChangesViewManager
.updateProgressText(VcsBundle
.message("changes.update.progress.message", vcs
.getDisplayName()), false);
276 if (! wasEverythingDirty
) {
277 changeListWorker
.notifyStartProcessingChanges(scope
);
279 if (updateUnversionedFiles
&& !wasEverythingDirty
) {
280 composite
.cleanScope(scope
);
282 actualUpdate(wasEverythingDirty
, composite
, builder
, scope
, vcs
, changeListWorker
, gate
);
283 if (myUpdateException
!= null) break;
286 final boolean takeChanges
= (myUpdateException
== null);
288 synchronized (myDataLock
) {
289 // do same modifications to change lists as was done during update + do delayed notifications
290 if (wasEverythingDirty
) {
291 changeListWorker
.notifyDoneProcessingChanges(myDelayedNotificator
.getProxyDispatcher());
293 myModifier
.exitUpdate();
294 // should be applied for notifications to be delivered (they were delayed)
295 myModifier
.apply(changeListWorker
);
296 myModifier
.clearQueue();
297 // update member from copy
299 myWorker
.takeData(changeListWorker
);
302 if (takeChanges
&& updateUnversionedFiles
) {
303 boolean statusChanged
= !myComposite
.equals(composite
);
304 myComposite
= composite
;
306 myDelayedNotificator
.getProxyDispatcher().unchangedFileStatusChanged();
311 updateIgnoredFiles(false);
313 myShowLocalChangesInvalidated
= false;
315 myChangesViewManager
.scheduleRefresh();
317 catch (DisposedException e
) {
318 // OK, we're finishing all the stuff now.
320 catch(ProcessCanceledException e
) {
321 // OK, we're finishing all the stuff now.
323 catch(Exception ex
) {
326 catch(AssertionError ex
) {
330 synchronized (myDataLock
) {
331 myDelayedNotificator
.getProxyDispatcher().changeListUpdateDone();
332 myChangesViewManager
.scheduleRefresh();
337 private void actualUpdate(final boolean wasEverythingDirty
, final FileHolderComposite composite
, final UpdatingChangeListBuilder builder
,
338 final VcsDirtyScope scope
, final AbstractVcs vcs
, final ChangeListWorker changeListWorker
,
339 final ChangeListManagerGate gate
) {
341 final ChangeProvider changeProvider
= vcs
.getChangeProvider();
342 if (changeProvider
!= null) {
343 final FoldersCutDownWorker foldersCutDownWorker
= new FoldersCutDownWorker();
345 builder
.setCurrent(scope
, foldersCutDownWorker
);
346 changeProvider
.getChanges(scope
, builder
, myUpdateChangesProgressIndicator
, gate
);
348 catch (VcsException e
) {
350 if (myUpdateException
== null) {
351 myUpdateException
= e
;
354 composite
.getIgnoredFileHolder().calculateChildren();
358 if ((! myUpdater
.isStopped()) && !wasEverythingDirty
) {
359 changeListWorker
.notifyDoneProcessingChanges(myDelayedNotificator
.getProxyDispatcher());
364 private void checkIfDisposed() {
365 if (myUpdater
.isStopped()) throw new DisposedException();
368 static boolean isUnder(final Change change
, final VcsDirtyScope scope
) {
369 final ContentRevision before
= change
.getBeforeRevision();
370 final ContentRevision after
= change
.getAfterRevision();
371 return before
!= null && scope
.belongsTo(before
.getFile()) || after
!= null && scope
.belongsTo(after
.getFile());
374 public List
<LocalChangeList
> getChangeListsCopy() {
375 synchronized (myDataLock
) {
376 return myWorker
.getListsCopy();
382 * this method made equivalent to {@link #getChangeListsCopy()} so to don't be confused by method name,
383 * better use {@link #getChangeListsCopy()}
386 public List
<LocalChangeList
> getChangeLists() {
387 synchronized (myDataLock
) {
388 return getChangeListsCopy();
392 public List
<File
> getAffectedPaths() {
393 synchronized (myDataLock
) {
394 return myWorker
.getAffectedPaths();
399 public List
<VirtualFile
> getAffectedFiles() {
400 synchronized (myDataLock
) {
401 return myWorker
.getAffectedFiles();
405 List
<VirtualFile
> getUnversionedFiles() {
406 return new ArrayList
<VirtualFile
>(myComposite
.getVFHolder(FileHolder
.HolderType
.UNVERSIONED
).getFiles());
409 List
<VirtualFile
> getModifiedWithoutEditing() {
410 return new ArrayList
<VirtualFile
>(myComposite
.getVFHolder(FileHolder
.HolderType
.MODIFIED_WITHOUT_EDITING
).getFiles());
414 * @return only roots for ignored folders, and ignored files
416 List
<VirtualFile
> getIgnoredFiles() {
417 return new ArrayList
<VirtualFile
>(myComposite
.getIgnoredFileHolder().getBranchToFileMap().values());
420 public List
<VirtualFile
> getLockedFolders() {
421 return new ArrayList
<VirtualFile
>(myComposite
.getVFHolder(FileHolder
.HolderType
.LOCKED
).getFiles());
424 Map
<VirtualFile
, LogicalLock
> getLogicallyLockedFolders() {
425 return new HashMap
<VirtualFile
, LogicalLock
>(((LogicallyLockedHolder
) myComposite
.get(FileHolder
.HolderType
.LOGICALLY_LOCKED
)).getMap());
428 public boolean isLogicallyLocked(final VirtualFile file
) {
429 return ((LogicallyLockedHolder
) myComposite
.get(FileHolder
.HolderType
.LOGICALLY_LOCKED
)).getMap().containsKey(file
);
432 public boolean isContainedInLocallyDeleted(final FilePath filePath
) {
433 synchronized (myDataLock
) {
434 return myWorker
.isContainedInLocallyDeleted(filePath
);
438 public List
<LocallyDeletedChange
> getDeletedFiles() {
439 synchronized (myDataLock
) {
440 return myWorker
.getLocallyDeleted().getFiles();
444 MultiMap
<String
, VirtualFile
> getSwitchedFilesMap() {
445 synchronized (myDataLock
) {
446 return myWorker
.getSwitchedHolder().getBranchToFileMap();
450 public VcsException
getUpdateException() {
451 return myUpdateException
;
454 public boolean isFileAffected(final VirtualFile file
) {
455 synchronized (myDataLock
) {
456 return myWorker
.getStatus(file
) != null;
461 public LocalChangeList
findChangeList(final String name
) {
462 synchronized (myDataLock
) {
463 return myWorker
.getCopyByName(name
);
468 public LocalChangeList
getChangeList(String id
) {
469 synchronized (myDataLock
) {
470 return myWorker
.getChangeList(id
);
474 public LocalChangeList
addChangeList(@NotNull String name
, final String comment
) {
475 synchronized (myDataLock
) {
476 final LocalChangeList changeList
= myModifier
.addChangeList(name
, comment
);
477 myChangesViewManager
.scheduleRefresh();
482 public void removeChangeList(final String name
) {
483 synchronized (myDataLock
) {
484 myModifier
.removeChangeList(name
);
485 myChangesViewManager
.scheduleRefresh();
489 public void removeChangeList(LocalChangeList list
) {
490 removeChangeList(list
.getName());
494 * does no modification to change lists, only notification is sent
497 public Runnable
prepareForChangeDeletion(final Collection
<Change
> changes
) {
498 final Map
<String
, LocalChangeList
> lists
= new HashMap
<String
, LocalChangeList
>();
499 final Map
<String
, List
<Change
>> map
;
500 synchronized (myDataLock
) {
501 map
= myWorker
.listsForChanges(changes
, lists
);
503 return new Runnable() {
505 final ChangeListListener multicaster
= myDelayedNotificator
.getProxyDispatcher();
506 synchronized (myDataLock
) {
507 for (Map
.Entry
<String
, List
<Change
>> entry
: map
.entrySet()) {
508 final List
<Change
> changes
= entry
.getValue();
509 for (Iterator
<Change
> iterator
= changes
.iterator(); iterator
.hasNext();) {
510 final Change change
= iterator
.next();
511 if (getChangeList(change
) != null) {
512 // was not actually rolled back
516 multicaster
.changesRemoved(changes
, lists
.get(entry
.getKey()));
523 public void setDefaultChangeList(@NotNull LocalChangeList list
) {
524 synchronized (myDataLock
) {
525 myModifier
.setDefault(list
.getName());
526 myChangesViewManager
.scheduleRefresh();
531 public LocalChangeList
getDefaultChangeList() {
532 synchronized (myDataLock
) {
533 return myWorker
.getDefaultListCopy();
538 public Collection
<LocalChangeList
> getInvolvedListsFilterChanges(final Collection
<Change
> changes
, final List
<Change
> validChanges
) {
539 synchronized (myDataLock
) {
540 return myWorker
.getInvolvedListsFilterChanges(changes
, validChanges
);
545 public LocalChangeList
getChangeList(Change change
) {
546 synchronized (myDataLock
) {
547 return myWorker
.listForChange(change
);
552 public String
getChangeListNameIfOnlyOne(final Change
[] changes
) {
553 synchronized (myDataLock
) {
554 return myWorker
.listNameIfOnlyOne(changes
);
560 * better use normal comparison, with equals
563 public LocalChangeList
getIdentityChangeList(Change change
) {
564 synchronized (myDataLock
) {
565 final List
<LocalChangeList
> lists
= myWorker
.getListsCopy();
566 for (LocalChangeList list
: lists
) {
567 for(Change oldChange
: list
.getChanges()) {
568 if (oldChange
== change
) {
578 public boolean isInUpdate() {
579 synchronized (myDataLock
) {
580 return myModifier
.isInsideUpdate() || myShowLocalChangesInvalidated
;
585 public Change
getChange(VirtualFile file
) {
586 synchronized (myDataLock
) {
587 final LocalChangeList list
= myWorker
.getListCopy(file
);
589 for (Change change
: list
.getChanges()) {
590 final ContentRevision afterRevision
= change
.getAfterRevision();
591 if (afterRevision
!= null) {
592 String revisionPath
= FileUtil
.toSystemIndependentName(afterRevision
.getFile().getIOFile().getPath());
593 if (FileUtil
.pathsEqual(revisionPath
, file
.getPath())) return change
;
595 final ContentRevision beforeRevision
= change
.getBeforeRevision();
596 if (beforeRevision
!= null) {
597 String revisionPath
= FileUtil
.toSystemIndependentName(beforeRevision
.getFile().getIOFile().getPath());
598 if (FileUtil
.pathsEqual(revisionPath
, file
.getPath())) return change
;
608 public Change
getChange(final FilePath file
) {
609 synchronized (myDataLock
) {
610 return myWorker
.getChangeForPath(file
);
614 public boolean isUnversioned(VirtualFile file
) {
615 return myComposite
.getVFHolder(FileHolder
.HolderType
.UNVERSIONED
).containsFile(file
);
619 public FileStatus
getStatus(VirtualFile file
) {
620 synchronized (myDataLock
) {
621 if (myComposite
.getVFHolder(FileHolder
.HolderType
.UNVERSIONED
).containsFile(file
)) return FileStatus
.UNKNOWN
;
622 if (myComposite
.getVFHolder(FileHolder
.HolderType
.MODIFIED_WITHOUT_EDITING
).containsFile(file
)) return FileStatus
.HIJACKED
;
623 if (myComposite
.getIgnoredFileHolder().containsFile(file
)) return FileStatus
.IGNORED
;
625 final FileStatus status
= myWorker
.getStatus(file
);
626 if (status
!= null) {
629 if (myWorker
.isSwitched(file
)) return FileStatus
.SWITCHED
;
630 return FileStatus
.NOT_CHANGED
;
635 public Collection
<Change
> getChangesIn(VirtualFile dir
) {
636 return getChangesIn(new FilePathImpl(dir
));
640 public Collection
<Change
> getChangesIn(final FilePath dirPath
) {
641 synchronized (myDataLock
) {
642 return myWorker
.getChangesIn(dirPath
);
646 public void moveChangesTo(LocalChangeList list
, final Change
[] changes
) {
647 synchronized (myDataLock
) {
648 myModifier
.moveChangesTo(list
.getName(), changes
);
650 SwingUtilities
.invokeLater(new Runnable() {
652 myChangesViewManager
.refreshView();
657 public void addUnversionedFiles(final LocalChangeList list
, @NotNull final List
<VirtualFile
> files
) {
658 final List
<VcsException
> exceptions
= new ArrayList
<VcsException
>();
659 ChangesUtil
.processVirtualFilesByVcs(myProject
, files
, new ChangesUtil
.PerVcsProcessor
<VirtualFile
>() {
660 public void process(final AbstractVcs vcs
, final List
<VirtualFile
> items
) {
661 final CheckinEnvironment environment
= vcs
.getCheckinEnvironment();
662 if (environment
!= null) {
663 final List
<VcsException
> result
= environment
.scheduleUnversionedFilesForAddition(items
);
664 if (result
!= null) {
665 exceptions
.addAll(result
);
671 if (exceptions
.size() > 0) {
672 StringBuilder message
= new StringBuilder(VcsBundle
.message("error.adding.files.prompt"));
673 for(VcsException ex
: exceptions
) {
674 message
.append("\n").append(ex
.getMessage());
676 Messages
.showErrorDialog(myProject
, message
.toString(), VcsBundle
.message("error.adding.files.title"));
679 for (VirtualFile file
: files
) {
680 myFileStatusManager
.fileStatusChanged(file
);
682 VcsDirtyScopeManager
.getInstance(myProject
).filesDirty(files
, null);
684 if (!list
.isDefault()) {
685 // find the changes for the added files and move them to the necessary changelist
686 invokeAfterUpdate(new Runnable() {
688 synchronized (myDataLock
) {
689 List
<Change
> changesToMove
= new ArrayList
<Change
>();
690 final LocalChangeList defaultList
= getDefaultChangeList();
691 for(Change change
: defaultList
.getChanges()) {
692 final ContentRevision afterRevision
= change
.getAfterRevision();
693 if (afterRevision
!= null) {
694 VirtualFile vFile
= afterRevision
.getFile().getVirtualFile();
695 if (files
.contains(vFile
)) {
696 changesToMove
.add(change
);
701 if (changesToMove
.size() > 0) {
702 moveChangesTo(list
, changesToMove
.toArray(new Change
[changesToMove
.size()]));
706 myChangesViewManager
.scheduleRefresh();
708 }, InvokeAfterUpdateMode
.BACKGROUND_NOT_CANCELLABLE
, VcsBundle
.message("change.lists.manager.add.unversioned"), null);
710 myChangesViewManager
.scheduleRefresh();
714 public Project
getProject() {
718 public void addChangeListListener(ChangeListListener listener
) {
719 myListeners
.addListener(listener
);
723 public void removeChangeListListener(ChangeListListener listener
) {
724 myListeners
.removeListener(listener
);
727 public void registerCommitExecutor(CommitExecutor executor
) {
728 myExecutors
.add(executor
);
731 public void commitChanges(LocalChangeList changeList
, List
<Change
> changes
) {
732 doCommit(changeList
, changes
, false);
735 private boolean doCommit(final LocalChangeList changeList
, final List
<Change
> changes
, final boolean synchronously
) {
736 return new CommitHelper(myProject
, changeList
, changes
, changeList
.getName(),
737 changeList
.getComment(), new ArrayList
<CheckinHandler
>(), false, synchronously
, null).doCommit();
740 public void commitChangesSynchronously(LocalChangeList changeList
, List
<Change
> changes
) {
741 doCommit(changeList
, changes
, true);
744 public boolean commitChangesSynchronouslyWithResult(final LocalChangeList changeList
, final List
<Change
> changes
) {
745 return doCommit(changeList
, changes
, true);
748 @SuppressWarnings({"unchecked"})
749 public void readExternal(Element element
) throws InvalidDataException
{
750 if (! myProject
.isDefault()) {
751 synchronized (myDataLock
) {
752 myIgnoredIdeaLevel
.clear();
753 new ChangeListManagerSerialization(myIgnoredIdeaLevel
, myWorker
).readExternal(element
);
754 if ((! myWorker
.isEmpty()) && getDefaultChangeList() == null) {
755 setDefaultChangeList(myWorker
.getListsCopy().get(0));
761 public void writeExternal(Element element
) throws WriteExternalException
{
762 if (! myProject
.isDefault()) {
763 final IgnoredFilesComponent ignoredFilesComponent
;
764 final ChangeListWorker worker
;
765 synchronized (myDataLock
) {
766 ignoredFilesComponent
= new IgnoredFilesComponent(myProject
);
767 ignoredFilesComponent
.add(myIgnoredIdeaLevel
.getFilesToIgnore());
768 worker
= myWorker
.copy();
770 new ChangeListManagerSerialization(ignoredFilesComponent
, worker
).writeExternal(element
);
775 public void reopenFiles(List
<FilePath
> paths
) {
776 final ReadonlyStatusHandlerImpl readonlyStatusHandler
= (ReadonlyStatusHandlerImpl
)ReadonlyStatusHandlerImpl
.getInstance(myProject
);
777 final boolean savedOption
= readonlyStatusHandler
.getState().SHOW_DIALOG
;
778 readonlyStatusHandler
.getState().SHOW_DIALOG
= false;
780 readonlyStatusHandler
.ensureFilesWritable(collectFiles(paths
));
783 readonlyStatusHandler
.getState().SHOW_DIALOG
= savedOption
;
787 public List
<CommitExecutor
> getRegisteredExecutors() {
788 return Collections
.unmodifiableList(myExecutors
);
791 public void addFilesToIgnore(final IgnoredFileBean
... filesToIgnore
) {
792 myIgnoredIdeaLevel
.add(filesToIgnore
);
793 updateIgnoredFiles(true);
796 public void setFilesToIgnore(final IgnoredFileBean
... filesToIgnore
) {
797 myIgnoredIdeaLevel
.set(filesToIgnore
);
798 updateIgnoredFiles(true);
801 private void updateIgnoredFiles(final boolean checkIgnored
) {
802 synchronized (myDataLock
) {
803 List
<VirtualFile
> unversionedFiles
= myComposite
.getVFHolder(FileHolder
.HolderType
.UNVERSIONED
).getFiles();
804 //List<VirtualFile> ignoredFiles = myComposite.getVFHolder(FileHolder.HolderType.IGNORED).getFiles();
805 boolean somethingChanged
= false;
806 for(VirtualFile file
: unversionedFiles
) {
807 if (isIgnoredFile(file
)) {
808 somethingChanged
= true;
809 myComposite
.getVFHolder(FileHolder
.HolderType
.UNVERSIONED
).removeFile(file
);
810 myComposite
.getIgnoredFileHolder().addFile(file
, "", false);
813 /*if (checkIgnored) {
814 for(VirtualFile file: ignoredFiles) {
815 if (!isIgnoredFile(file)) {
816 somethingChanged = true;
817 // the file may have been reported as ignored by the VCS, so we can't directly move it to unversioned files
818 VcsDirtyScopeManager.getInstance(myProject).fileDirty(file);
822 if (somethingChanged
) {
823 myFileStatusManager
.fileStatusesChanged();
824 myChangesViewManager
.scheduleRefresh();
829 public IgnoredFileBean
[] getFilesToIgnore() {
830 return myIgnoredIdeaLevel
.getFilesToIgnore();
833 public boolean isIgnoredFile(@NotNull VirtualFile file
) {
834 return myIgnoredIdeaLevel
.isIgnoredFile(file
);
838 public String
getSwitchedBranch(final VirtualFile file
) {
839 synchronized (myDataLock
) {
840 return myWorker
.getBranchForFile(file
);
845 public String
getDefaultListName() {
846 synchronized (myDataLock
) {
847 return myWorker
.getDefaultListName();
851 private static VirtualFile
[] collectFiles(final List
<FilePath
> paths
) {
852 final ArrayList
<VirtualFile
> result
= new ArrayList
<VirtualFile
>();
853 for (FilePath path
: paths
) {
854 if (path
.getVirtualFile() != null) {
855 result
.add(path
.getVirtualFile());
859 return result
.toArray(new VirtualFile
[result
.size()]);
862 public boolean setReadOnly(final String name
, final boolean value
) {
863 synchronized (myDataLock
) {
864 final boolean result
= myModifier
.setReadOnly(name
, value
);
865 myChangesViewManager
.scheduleRefresh();
870 public boolean editName(@NotNull final String fromName
, @NotNull final String toName
) {
871 synchronized (myDataLock
) {
872 final boolean result
= myModifier
.editName(fromName
, toName
);
873 myChangesViewManager
.scheduleRefresh();
878 public String
editComment(@NotNull final String fromName
, final String newComment
) {
879 synchronized (myDataLock
) {
880 final String oldComment
= myModifier
.editComment(fromName
, newComment
);
881 myChangesViewManager
.scheduleRefresh();
887 * Can be called only from not AWT thread; to do smthg after ChangeListManager refresh, call invokeAfterUpdate
889 public boolean ensureUpToDate(final boolean canBeCanceled
) {
890 final EnsureUpToDateFromNonAWTThread worker
= new EnsureUpToDateFromNonAWTThread(myProject
);
892 return worker
.isDone();
895 // only a light attempt to show that some dirty scope request is asynchronously coming
896 // for users to see changes are not valid
897 // (commit -> asynch synch VFS -> asynch vcs dirty scope)
898 public void showLocalChangesInvalidated() {
899 synchronized (myDataLock
) {
900 myShowLocalChangesInvalidated
= true;
904 private static class MyChangesDeltaForwarder
implements PlusMinus
<Pair
<String
, AbstractVcs
>> {
905 private SlowlyClosingAlarm myAlarm
;
906 private RemoteRevisionsCache myRevisionsCache
;
907 private final ProjectLevelVcsManager myVcsManager
;
909 public MyChangesDeltaForwarder(final Project project
, final ExecutorService service
) {
910 myAlarm
= ControlledAlarmFactory
.createOnSharedThread(project
, "changes delta consumer forwarder", service
);
911 myRevisionsCache
= RemoteRevisionsCache
.getInstance(project
);
912 myVcsManager
= ProjectLevelVcsManager
.getInstance(project
);
915 public void plus(final Pair
<String
, AbstractVcs
> stringAbstractVcsPair
) {
916 myAlarm
.addRequest(new Runnable() {
918 myRevisionsCache
.plus(getCorrectedPair(stringAbstractVcsPair
));
923 public void minus(final Pair
<String
, AbstractVcs
> stringAbstractVcsPair
) {
924 myAlarm
.addRequest(new Runnable() {
926 myRevisionsCache
.minus(getCorrectedPair(stringAbstractVcsPair
));
931 private Pair
<String
, AbstractVcs
> getCorrectedPair(final Pair
<String
, AbstractVcs
> stringAbstractVcsPair
) {
932 Pair
<String
, AbstractVcs
> correctedPair
= stringAbstractVcsPair
;
933 if (stringAbstractVcsPair
.getSecond() == null) {
934 final String path
= stringAbstractVcsPair
.getFirst();
935 correctedPair
= new Pair
<String
, AbstractVcs
>(path
, myVcsManager
.findVcsByName(findVcs(path
).getName()));
937 return correctedPair
;
941 private VcsKey
findVcs(final String path
) {
942 // does not matter directory or not
943 final AbstractVcs vcs
= myVcsManager
.getVcsFor(FilePathImpl
.create(new File(path
), false));
944 return vcs
== null ?
null : vcs
.getKeyInstanceMethod();