VCS: fix group by structure for Changes | Local
[fedora-idea.git] / platform / vcs-impl / src / com / intellij / openapi / vcs / changes / ChangeListManagerImpl.java
blobbc751b107d20c367ff282df239f3219035b163f8
1 /*
2 * Copyright 2000-2009 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.AtomicSectionsAware;
20 import com.intellij.lifecycle.ControlledAlarmFactory;
21 import com.intellij.lifecycle.SlowlyClosingAlarm;
22 import com.intellij.notification.Notification;
23 import com.intellij.notification.NotificationDisplayType;
24 import com.intellij.notification.NotificationType;
25 import com.intellij.notification.Notifications;
26 import com.intellij.openapi.application.ApplicationManager;
27 import com.intellij.openapi.application.ModalityState;
28 import com.intellij.openapi.components.ProjectComponent;
29 import com.intellij.openapi.diagnostic.Logger;
30 import com.intellij.openapi.progress.EmptyProgressIndicator;
31 import com.intellij.openapi.progress.ProcessCanceledException;
32 import com.intellij.openapi.progress.ProgressIndicator;
33 import com.intellij.openapi.project.DumbAwareRunnable;
34 import com.intellij.openapi.project.Project;
35 import com.intellij.openapi.ui.Messages;
36 import com.intellij.openapi.util.*;
37 import com.intellij.openapi.util.io.FileUtil;
38 import com.intellij.openapi.vcs.*;
39 import com.intellij.openapi.vcs.changes.conflicts.ChangelistConflictTracker;
40 import com.intellij.openapi.vcs.changes.ui.CommitHelper;
41 import com.intellij.openapi.vcs.checkin.CheckinEnvironment;
42 import com.intellij.openapi.vcs.checkin.CheckinHandler;
43 import com.intellij.openapi.vcs.impl.ProjectLevelVcsManagerImpl;
44 import com.intellij.openapi.vcs.impl.VcsInitObject;
45 import com.intellij.openapi.vcs.readOnlyHandler.ReadonlyStatusHandlerImpl;
46 import com.intellij.openapi.vfs.VfsUtil;
47 import com.intellij.openapi.vfs.VirtualFile;
48 import com.intellij.ui.EditorNotifications;
49 import com.intellij.util.ConcurrencyUtil;
50 import com.intellij.util.Consumer;
51 import com.intellij.util.EventDispatcher;
52 import com.intellij.util.containers.MultiMap;
53 import com.intellij.util.messages.Topic;
54 import org.jdom.Element;
55 import org.jetbrains.annotations.NonNls;
56 import org.jetbrains.annotations.NotNull;
57 import org.jetbrains.annotations.Nullable;
59 import javax.swing.*;
60 import java.io.File;
61 import java.util.*;
62 import java.util.concurrent.ExecutorService;
63 import java.util.concurrent.ScheduledExecutorService;
65 /**
66 * @author max
68 public class ChangeListManagerImpl extends ChangeListManagerEx implements ProjectComponent, ChangeListOwner, JDOMExternalizable {
69 private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.vcs.changes.ChangeListManagerImpl");
71 private final Project myProject;
72 private final ChangesViewManager myChangesViewManager;
73 private final FileStatusManager myFileStatusManager;
74 private final UpdateRequestsQueue myUpdater;
76 @SuppressWarnings({"FieldAccessedSynchronizedAndUnsynchronized"})
77 private static final ScheduledExecutorService ourUpdateAlarm = ConcurrencyUtil.newSingleScheduledThreadExecutor("Change List Updater", Thread.MIN_PRIORITY + 1);
79 private final Modifier myModifier;
81 private FileHolderComposite myComposite;
83 private final ChangeListWorker myWorker;
84 private VcsException myUpdateException = null;
86 @SuppressWarnings({"FieldAccessedSynchronizedAndUnsynchronized"})
87 private final EventDispatcher<ChangeListListener> myListeners = EventDispatcher.create(ChangeListListener.class);
89 private final Object myDataLock = new Object();
91 private final List<CommitExecutor> myExecutors = new ArrayList<CommitExecutor>();
93 private final IgnoredFilesComponent myIgnoredIdeaLevel;
94 private ProgressIndicator myUpdateChangesProgressIndicator;
96 public static final Key<Object> DOCUMENT_BEING_COMMITTED_KEY = new Key<Object>("DOCUMENT_BEING_COMMITTED");
98 public static final Topic<LocalChangeListsLoadedListener> LISTS_LOADED = new Topic<LocalChangeListsLoadedListener>(
99 "LOCAL_CHANGE_LISTS_LOADED", LocalChangeListsLoadedListener.class);
101 private boolean myShowLocalChangesInvalidated;
103 private final DelayedNotificator myDelayedNotificator;
105 private final VcsListener myVcsListener = new VcsListener() {
106 public void directoryMappingChanged() {
107 VcsDirtyScopeManager.getInstanceChecked(myProject).markEverythingDirty();
110 private final ChangelistConflictTracker myConflictTracker;
112 public static ChangeListManagerImpl getInstanceImpl(final Project project) {
113 return (ChangeListManagerImpl) project.getComponent(ChangeListManager.class);
116 public ChangeListManagerImpl(final Project project) {
117 myProject = project;
118 myChangesViewManager = ChangesViewManager.getInstance(myProject);
119 myFileStatusManager = FileStatusManager.getInstance(myProject);
120 myComposite = new FileHolderComposite(project);
121 myIgnoredIdeaLevel = new IgnoredFilesComponent(myProject);
122 myUpdater = new UpdateRequestsQueue(myProject, ourUpdateAlarm, new ActualUpdater());
124 myWorker = new ChangeListWorker(myProject, new MyChangesDeltaForwarder(myProject, ourUpdateAlarm));
125 myDelayedNotificator = new DelayedNotificator(myListeners, ourUpdateAlarm);
126 myModifier = new Modifier(myWorker, myDelayedNotificator);
128 myConflictTracker = new ChangelistConflictTracker(project, this, myFileStatusManager, EditorNotifications.getInstance(project));
131 public void projectOpened() {
132 initializeForNewProject();
134 if (ApplicationManager.getApplication().isUnitTestMode()) {
135 myWorker.initialized();
136 myUpdater.initialized();
137 ProjectLevelVcsManager.getInstance(myProject).addVcsListener(myVcsListener);
139 else {
140 ((ProjectLevelVcsManagerImpl) ProjectLevelVcsManager.getInstance(myProject)).addInitializationRequest(
141 VcsInitObject.CHANGE_LIST_MANAGER, new DumbAwareRunnable() {
142 public void run() {
143 myWorker.initialized();
144 myUpdater.initialized();
145 broadcastStateAfterLoad();
146 ProjectLevelVcsManager.getInstance(myProject).addVcsListener(myVcsListener);
151 myConflictTracker.startTracking();
154 private void broadcastStateAfterLoad() {
155 final List<LocalChangeList> listCopy;
156 synchronized (myDataLock) {
157 listCopy = getChangeListsCopy();
159 if (! listCopy.isEmpty()) {
160 myProject.getMessageBus().syncPublisher(LISTS_LOADED).processLoadedLists(listCopy);
164 private void initializeForNewProject() {
165 synchronized (myDataLock) {
166 if (myWorker.isEmpty()) {
167 final LocalChangeList list = myWorker.addChangeList(VcsBundle.message("changes.default.changlist.name"), null);
168 setDefaultChangeList(list);
170 if (myIgnoredIdeaLevel.isEmpty()) {
171 final String name = myProject.getName();
172 myIgnoredIdeaLevel.add(IgnoredBeanFactory.ignoreFile(name + WorkspaceFileType.DOT_DEFAULT_EXTENSION, myProject));
173 myIgnoredIdeaLevel.add(IgnoredBeanFactory.ignoreFile(Project.DIRECTORY_STORE_FOLDER + "/workspace.xml", myProject));
179 public void projectClosed() {
180 ProjectLevelVcsManager.getInstance(myProject).removeVcsListener(myVcsListener);
182 synchronized (myDataLock) {
183 if (myUpdateChangesProgressIndicator != null) {
184 myUpdateChangesProgressIndicator.cancel();
188 myUpdater.stop();
189 myConflictTracker.stopTracking();
192 @NotNull @NonNls
193 public String getComponentName() {
194 return "ChangeListManager";
197 public void initComponent() {
200 public void disposeComponent() {
204 * update itself might produce actions done on AWT thread (invoked-after),
205 * so waiting for its completion on AWT thread is not good
207 * runnable is invoked on AWT thread
209 public void invokeAfterUpdate(final Runnable afterUpdate, final InvokeAfterUpdateMode mode, final String title, final ModalityState state) {
210 myUpdater.invokeAfterUpdate(afterUpdate, mode, title, null, state);
213 public void invokeAfterUpdate(final Runnable afterUpdate, final InvokeAfterUpdateMode mode, final String title,
214 final Consumer<VcsDirtyScopeManager> dirtyScopeManagerFiller, final ModalityState state) {
215 myUpdater.invokeAfterUpdate(afterUpdate, mode, title, dirtyScopeManagerFiller, state);
218 static class DisposedException extends RuntimeException {}
220 public void scheduleUpdate() {
221 myUpdater.schedule(true);
224 public void scheduleUpdate(boolean updateUnversionedFiles) {
225 myUpdater.schedule(updateUnversionedFiles);
228 private class ActualUpdater implements LocalChangesUpdater {
229 public void execute(boolean updateUnversioned, AtomicSectionsAware atomicSectionsAware) {
230 updateImmediately(updateUnversioned, atomicSectionsAware);
234 private void updateImmediately(final boolean updateUnversionedFiles, final AtomicSectionsAware atomicSectionsAware) {
235 FileHolderComposite composite;
236 ChangeListWorker changeListWorker;
238 final VcsDirtyScopeManagerImpl dirtyScopeManager;
239 try {
240 dirtyScopeManager = ((VcsDirtyScopeManagerImpl) VcsDirtyScopeManager.getInstanceChecked(myProject));
242 catch(ProcessCanceledException ex) {
243 return;
245 catch(Exception ex) {
246 LOG.error(ex);
247 return;
249 final VcsInvalidated invalidated = dirtyScopeManager.retrieveScopes();
250 if (invalidated == null || invalidated.isEmpty()) {
251 // a hack here; but otherwise everything here should be refactored ;)
252 if (invalidated.isEmpty() && invalidated.isEverythingDirty()) {
253 VcsDirtyScopeManager.getInstance(myProject).markEverythingDirty();
255 return;
257 final boolean wasEverythingDirty = invalidated.isEverythingDirty();
258 final List<VcsDirtyScope> scopes = invalidated.getScopes();
260 try {
261 checkIfDisposed();
263 // copy existsing data to objects that would be updated.
264 // mark for "modifier" that update started (it would create duplicates of modification commands done by user during update;
265 // after update of copies of objects is complete, it would apply the same modifications to copies.)
266 synchronized (myDataLock) {
267 changeListWorker = myWorker.copy();
268 composite = updateUnversionedFiles ? (FileHolderComposite) myComposite.copy() : myComposite;
269 myModifier.enterUpdate();
270 if (wasEverythingDirty) {
271 myUpdateException = null;
273 if (updateUnversionedFiles && wasEverythingDirty) {
274 composite.cleanAll();
277 if (wasEverythingDirty) {
278 changeListWorker.notifyStartProcessingChanges(null);
280 myChangesViewManager.scheduleRefresh();
282 final ChangeListManagerGate gate = changeListWorker.createSelfGate();
284 // do actual requests about file statuses
285 final UpdatingChangeListBuilder builder = new UpdatingChangeListBuilder(changeListWorker, composite, new Getter<Boolean>() {
286 public Boolean get() {
287 return myUpdater.isStopped();
289 }, updateUnversionedFiles, myIgnoredIdeaLevel, gate);
291 myUpdateChangesProgressIndicator = new EmptyProgressIndicator() {
292 @Override
293 public boolean isCanceled() {
294 return myUpdater.isStopped() || atomicSectionsAware.shouldExitAsap();
296 @Override
297 public void checkCanceled() {
298 checkIfDisposed();
299 atomicSectionsAware.checkShouldExit();
302 for (final VcsDirtyScope scope : scopes) {
303 atomicSectionsAware.checkShouldExit();
305 final AbstractVcs vcs = scope.getVcs();
306 if (vcs == null) continue;
307 final VcsAppendableDirtyScope adjustedScope = vcs.adjustDirtyScope((VcsAppendableDirtyScope) scope);
309 myChangesViewManager.updateProgressText(VcsBundle.message("changes.update.progress.message", vcs.getDisplayName()), false);
310 if (! wasEverythingDirty) {
311 changeListWorker.notifyStartProcessingChanges(adjustedScope);
313 if (updateUnversionedFiles && !wasEverythingDirty) {
314 composite.cleanScope(adjustedScope);
317 try {
318 actualUpdate(wasEverythingDirty, composite, builder, adjustedScope, vcs, changeListWorker, gate);
320 catch (Throwable t) {
321 LOG.info(t);
322 if (t instanceof Error) {
323 throw (Error) t;
324 } else if (t instanceof RuntimeException) {
325 throw (RuntimeException) t;
327 throw new RuntimeException(t);
330 if (myUpdateException != null) break;
333 final boolean takeChanges = (myUpdateException == null);
335 synchronized (myDataLock) {
336 // do same modifications to change lists as was done during update + do delayed notifications
337 if (wasEverythingDirty) {
338 changeListWorker.notifyDoneProcessingChanges(myDelayedNotificator.getProxyDispatcher());
340 myModifier.exitUpdate();
341 // should be applied for notifications to be delivered (they were delayed)
342 myModifier.apply(changeListWorker);
343 myModifier.clearQueue();
344 // update member from copy
345 if (takeChanges) {
346 myWorker.takeData(changeListWorker);
349 if (takeChanges && updateUnversionedFiles) {
350 boolean statusChanged = !myComposite.equals(composite);
351 myComposite = composite;
352 if (statusChanged) {
353 myDelayedNotificator.getProxyDispatcher().unchangedFileStatusChanged();
357 if (takeChanges) {
358 updateIgnoredFiles(false);
360 myShowLocalChangesInvalidated = false;
362 myChangesViewManager.scheduleRefresh();
364 catch (DisposedException e) {
365 // OK, we're finishing all the stuff now.
367 catch(ProcessCanceledException e) {
368 // OK, we're finishing all the stuff now.
370 catch(Exception ex) {
371 LOG.error(ex);
373 catch(AssertionError ex) {
374 LOG.error(ex);
376 finally {
377 dirtyScopeManager.changesProcessed();
379 synchronized (myDataLock) {
380 myDelayedNotificator.getProxyDispatcher().changeListUpdateDone();
381 myChangesViewManager.scheduleRefresh();
386 private void actualUpdate(final boolean wasEverythingDirty, final FileHolderComposite composite, final UpdatingChangeListBuilder builder,
387 final VcsDirtyScope scope, final AbstractVcs vcs, final ChangeListWorker changeListWorker,
388 final ChangeListManagerGate gate) {
389 try {
390 final ChangeProvider changeProvider = vcs.getChangeProvider();
391 if (changeProvider != null) {
392 final FoldersCutDownWorker foldersCutDownWorker = new FoldersCutDownWorker();
393 try {
394 builder.setCurrent(scope, foldersCutDownWorker);
395 changeProvider.getChanges(scope, builder, myUpdateChangesProgressIndicator, gate);
397 catch (VcsException e) {
398 LOG.info(e);
399 if (myUpdateException == null) {
400 myUpdateException = e;
403 composite.getIgnoredFileHolder().calculateChildren();
406 finally {
407 if ((! myUpdater.isStopped()) && !wasEverythingDirty) {
408 changeListWorker.notifyDoneProcessingChanges(myDelayedNotificator.getProxyDispatcher());
413 private void checkIfDisposed() {
414 if (myUpdater.isStopped()) throw new DisposedException();
417 static boolean isUnder(final Change change, final VcsDirtyScope scope) {
418 final ContentRevision before = change.getBeforeRevision();
419 final ContentRevision after = change.getAfterRevision();
420 return before != null && scope.belongsTo(before.getFile()) || after != null && scope.belongsTo(after.getFile());
423 public List<LocalChangeList> getChangeListsCopy() {
424 synchronized (myDataLock) {
425 return myWorker.getListsCopy();
430 * @deprecated
431 * this method made equivalent to {@link #getChangeListsCopy()} so to don't be confused by method name,
432 * better use {@link #getChangeListsCopy()}
434 @NotNull
435 public List<LocalChangeList> getChangeLists() {
436 synchronized (myDataLock) {
437 return getChangeListsCopy();
441 public List<File> getAffectedPaths() {
442 synchronized (myDataLock) {
443 return myWorker.getAffectedPaths();
447 @NotNull
448 public List<VirtualFile> getAffectedFiles() {
449 synchronized (myDataLock) {
450 return myWorker.getAffectedFiles();
454 List<VirtualFile> getUnversionedFiles() {
455 return new ArrayList<VirtualFile>(myComposite.getVFHolder(FileHolder.HolderType.UNVERSIONED).getFiles());
458 List<VirtualFile> getModifiedWithoutEditing() {
459 return new ArrayList<VirtualFile>(myComposite.getVFHolder(FileHolder.HolderType.MODIFIED_WITHOUT_EDITING).getFiles());
463 * @return only roots for ignored folders, and ignored files
465 List<VirtualFile> getIgnoredFiles() {
466 return new ArrayList<VirtualFile>(myComposite.getIgnoredFileHolder().getBranchToFileMap().values());
469 public List<VirtualFile> getLockedFolders() {
470 return new ArrayList<VirtualFile>(myComposite.getVFHolder(FileHolder.HolderType.LOCKED).getFiles());
473 Map<VirtualFile, LogicalLock> getLogicallyLockedFolders() {
474 return new HashMap<VirtualFile, LogicalLock>(((LogicallyLockedHolder) myComposite.get(FileHolder.HolderType.LOGICALLY_LOCKED)).getMap());
477 public boolean isLogicallyLocked(final VirtualFile file) {
478 return ((LogicallyLockedHolder) myComposite.get(FileHolder.HolderType.LOGICALLY_LOCKED)).getMap().containsKey(file);
481 public boolean isContainedInLocallyDeleted(final FilePath filePath) {
482 synchronized (myDataLock) {
483 return myWorker.isContainedInLocallyDeleted(filePath);
487 public List<LocallyDeletedChange> getDeletedFiles() {
488 synchronized (myDataLock) {
489 return myWorker.getLocallyDeleted().getFiles();
493 MultiMap<String, VirtualFile> getSwitchedFilesMap() {
494 synchronized (myDataLock) {
495 return myWorker.getSwitchedHolder().getBranchToFileMap();
499 @Nullable
500 Map<VirtualFile, String> getSwitchedRoots() {
501 synchronized (myDataLock) {
502 return ((SwitchedFileHolder) myComposite.get(FileHolder.HolderType.ROOT_SWITCH)).getFilesMapCopy();
506 public VcsException getUpdateException() {
507 return myUpdateException;
510 public boolean isFileAffected(final VirtualFile file) {
511 synchronized (myDataLock) {
512 return myWorker.getStatus(file) != null;
516 @Nullable
517 public LocalChangeList findChangeList(final String name) {
518 synchronized (myDataLock) {
519 return myWorker.getCopyByName(name);
523 @Override
524 public LocalChangeList getChangeList(String id) {
525 synchronized (myDataLock) {
526 return myWorker.getChangeList(id);
530 public LocalChangeList addChangeList(@NotNull String name, final String comment) {
531 synchronized (myDataLock) {
532 final LocalChangeList changeList = myModifier.addChangeList(name, comment);
533 myChangesViewManager.scheduleRefresh();
534 return changeList;
538 public void removeChangeList(final String name) {
539 synchronized (myDataLock) {
540 myModifier.removeChangeList(name);
541 myChangesViewManager.scheduleRefresh();
545 public void removeChangeList(LocalChangeList list) {
546 removeChangeList(list.getName());
550 * does no modification to change lists, only notification is sent
552 @NotNull
553 public Runnable prepareForChangeDeletion(final Collection<Change> changes) {
554 final Map<String, LocalChangeList> lists = new HashMap<String, LocalChangeList>();
555 final Map<String, List<Change>> map;
556 synchronized (myDataLock) {
557 map = myWorker.listsForChanges(changes, lists);
559 return new Runnable() {
560 public void run() {
561 final ChangeListListener multicaster = myDelayedNotificator.getProxyDispatcher();
562 synchronized (myDataLock) {
563 for (Map.Entry<String, List<Change>> entry : map.entrySet()) {
564 final List<Change> changes = entry.getValue();
565 for (Iterator<Change> iterator = changes.iterator(); iterator.hasNext();) {
566 final Change change = iterator.next();
567 if (getChangeList(change) != null) {
568 // was not actually rolled back
569 iterator.remove();
572 multicaster.changesRemoved(changes, lists.get(entry.getKey()));
579 public void setDefaultChangeList(@NotNull LocalChangeList list) {
580 synchronized (myDataLock) {
581 myModifier.setDefault(list.getName());
582 myChangesViewManager.scheduleRefresh();
586 @Nullable
587 public LocalChangeList getDefaultChangeList() {
588 synchronized (myDataLock) {
589 return myWorker.getDefaultListCopy();
593 @Override
594 public boolean isDefaultChangeList(ChangeList list) {
595 return list instanceof LocalChangeList && myWorker.isDefaultList((LocalChangeList)list);
598 @NotNull
599 public Collection<LocalChangeList> getInvolvedListsFilterChanges(final Collection<Change> changes, final List<Change> validChanges) {
600 synchronized (myDataLock) {
601 return myWorker.getInvolvedListsFilterChanges(changes, validChanges);
605 @Nullable
606 public LocalChangeList getChangeList(Change change) {
607 synchronized (myDataLock) {
608 return myWorker.listForChange(change);
612 @Override
613 public String getChangeListNameIfOnlyOne(final Change[] changes) {
614 synchronized (myDataLock) {
615 return myWorker.listNameIfOnlyOne(changes);
620 * @deprecated
621 * better use normal comparison, with equals
623 @Nullable
624 public LocalChangeList getIdentityChangeList(Change change) {
625 synchronized (myDataLock) {
626 final List<LocalChangeList> lists = myWorker.getListsCopy();
627 for (LocalChangeList list : lists) {
628 for(Change oldChange: list.getChanges()) {
629 if (oldChange == change) {
630 return list;
634 return null;
638 @Override
639 public boolean isInUpdate() {
640 synchronized (myDataLock) {
641 return myModifier.isInsideUpdate() || myShowLocalChangesInvalidated;
645 @Nullable
646 public Change getChange(@NotNull VirtualFile file) {
647 synchronized (myDataLock) {
648 final LocalChangeList list = myWorker.getListCopy(file);
649 if (list != null) {
650 for (Change change : list.getChanges()) {
651 final ContentRevision afterRevision = change.getAfterRevision();
652 if (afterRevision != null) {
653 String revisionPath = FileUtil.toSystemIndependentName(afterRevision.getFile().getIOFile().getPath());
654 if (FileUtil.pathsEqual(revisionPath, file.getPath())) return change;
656 final ContentRevision beforeRevision = change.getBeforeRevision();
657 if (beforeRevision != null) {
658 String revisionPath = FileUtil.toSystemIndependentName(beforeRevision.getFile().getIOFile().getPath());
659 if (FileUtil.pathsEqual(revisionPath, file.getPath())) return change;
664 return null;
668 @Override
669 public LocalChangeList getChangeList(@NotNull VirtualFile file) {
670 synchronized (myDataLock) {
671 return myWorker.getListCopy(file);
675 @Nullable
676 public Change getChange(final FilePath file) {
677 synchronized (myDataLock) {
678 return myWorker.getChangeForPath(file);
682 public boolean isUnversioned(VirtualFile file) {
683 return myComposite.getVFHolder(FileHolder.HolderType.UNVERSIONED).containsFile(file);
686 @NotNull
687 public FileStatus getStatus(VirtualFile file) {
688 synchronized (myDataLock) {
689 if (myComposite.getVFHolder(FileHolder.HolderType.UNVERSIONED).containsFile(file)) return FileStatus.UNKNOWN;
690 if (myComposite.getVFHolder(FileHolder.HolderType.MODIFIED_WITHOUT_EDITING).containsFile(file)) return FileStatus.HIJACKED;
691 if (myComposite.getIgnoredFileHolder().containsFile(file)) return FileStatus.IGNORED;
693 final FileStatus status = myWorker.getStatus(file);
694 if (status != null) {
695 return status;
697 if (myWorker.isSwitched(file)) return FileStatus.SWITCHED;
698 return FileStatus.NOT_CHANGED;
702 @NotNull
703 public Collection<Change> getChangesIn(VirtualFile dir) {
704 return getChangesIn(new FilePathImpl(dir));
707 @NotNull
708 public Collection<Change> getChangesIn(final FilePath dirPath) {
709 synchronized (myDataLock) {
710 return myWorker.getChangesIn(dirPath);
714 public void moveChangesTo(LocalChangeList list, final Change[] changes) {
715 synchronized (myDataLock) {
716 myModifier.moveChangesTo(list.getName(), changes);
718 SwingUtilities.invokeLater(new Runnable() {
719 public void run() {
720 myChangesViewManager.refreshView();
725 public void addUnversionedFiles(final LocalChangeList list, @NotNull final List<VirtualFile> files) {
726 final List<VcsException> exceptions = new ArrayList<VcsException>();
727 ChangesUtil.processVirtualFilesByVcs(myProject, files, new ChangesUtil.PerVcsProcessor<VirtualFile>() {
728 public void process(final AbstractVcs vcs, final List<VirtualFile> items) {
729 final CheckinEnvironment environment = vcs.getCheckinEnvironment();
730 if (environment != null) {
731 final List<VcsException> result = environment.scheduleUnversionedFilesForAddition(items);
732 if (result != null) {
733 exceptions.addAll(result);
739 if (exceptions.size() > 0) {
740 StringBuilder message = new StringBuilder(VcsBundle.message("error.adding.files.prompt"));
741 for(VcsException ex: exceptions) {
742 message.append("\n").append(ex.getMessage());
744 Messages.showErrorDialog(myProject, message.toString(), VcsBundle.message("error.adding.files.title"));
747 for (VirtualFile file : files) {
748 myFileStatusManager.fileStatusChanged(file);
750 VcsDirtyScopeManager.getInstance(myProject).filesDirty(files, null);
752 if (!list.isDefault()) {
753 // find the changes for the added files and move them to the necessary changelist
754 invokeAfterUpdate(new Runnable() {
755 public void run() {
756 synchronized (myDataLock) {
757 List<Change> changesToMove = new ArrayList<Change>();
758 final LocalChangeList defaultList = getDefaultChangeList();
759 for(Change change: defaultList.getChanges()) {
760 final ContentRevision afterRevision = change.getAfterRevision();
761 if (afterRevision != null) {
762 VirtualFile vFile = afterRevision.getFile().getVirtualFile();
763 if (files.contains(vFile)) {
764 changesToMove.add(change);
769 if (changesToMove.size() > 0) {
770 moveChangesTo(list, changesToMove.toArray(new Change[changesToMove.size()]));
774 myChangesViewManager.scheduleRefresh();
776 }, InvokeAfterUpdateMode.BACKGROUND_NOT_CANCELLABLE, VcsBundle.message("change.lists.manager.add.unversioned"), null);
777 } else {
778 myChangesViewManager.scheduleRefresh();
782 public Project getProject() {
783 return myProject;
786 public void addChangeListListener(ChangeListListener listener) {
787 myListeners.addListener(listener);
791 public void removeChangeListListener(ChangeListListener listener) {
792 myListeners.removeListener(listener);
795 public void registerCommitExecutor(CommitExecutor executor) {
796 myExecutors.add(executor);
799 public void commitChanges(LocalChangeList changeList, List<Change> changes) {
800 doCommit(changeList, changes, false);
803 private boolean doCommit(final LocalChangeList changeList, final List<Change> changes, final boolean synchronously) {
804 return new CommitHelper(myProject, changeList, changes, changeList.getName(),
805 changeList.getComment(), new ArrayList<CheckinHandler>(), false, synchronously, null).doCommit();
808 public void commitChangesSynchronously(LocalChangeList changeList, List<Change> changes) {
809 doCommit(changeList, changes, true);
812 public boolean commitChangesSynchronouslyWithResult(final LocalChangeList changeList, final List<Change> changes) {
813 return doCommit(changeList, changes, true);
816 @SuppressWarnings({"unchecked"})
817 public void readExternal(Element element) throws InvalidDataException {
818 if (! myProject.isDefault()) {
819 synchronized (myDataLock) {
820 myIgnoredIdeaLevel.clear();
821 new ChangeListManagerSerialization(myIgnoredIdeaLevel, myWorker).readExternal(element);
822 if ((! myWorker.isEmpty()) && getDefaultChangeList() == null) {
823 setDefaultChangeList(myWorker.getListsCopy().get(0));
826 myConflictTracker.loadState(element);
830 public void writeExternal(Element element) throws WriteExternalException {
831 if (! myProject.isDefault()) {
832 final IgnoredFilesComponent ignoredFilesComponent;
833 final ChangeListWorker worker;
834 synchronized (myDataLock) {
835 ignoredFilesComponent = new IgnoredFilesComponent(myProject);
836 ignoredFilesComponent.add(myIgnoredIdeaLevel.getFilesToIgnore());
837 worker = myWorker.copy();
839 new ChangeListManagerSerialization(ignoredFilesComponent, worker).writeExternal(element);
840 myConflictTracker.saveState(element);
844 // used in TeamCity
845 public void reopenFiles(List<FilePath> paths) {
846 final ReadonlyStatusHandlerImpl readonlyStatusHandler = (ReadonlyStatusHandlerImpl)ReadonlyStatusHandlerImpl.getInstance(myProject);
847 final boolean savedOption = readonlyStatusHandler.getState().SHOW_DIALOG;
848 readonlyStatusHandler.getState().SHOW_DIALOG = false;
849 try {
850 readonlyStatusHandler.ensureFilesWritable(collectFiles(paths));
852 finally {
853 readonlyStatusHandler.getState().SHOW_DIALOG = savedOption;
857 public List<CommitExecutor> getRegisteredExecutors() {
858 return Collections.unmodifiableList(myExecutors);
861 public void addFilesToIgnore(final IgnoredFileBean... filesToIgnore) {
862 myIgnoredIdeaLevel.add(filesToIgnore);
863 updateIgnoredFiles(true);
866 public void setFilesToIgnore(final IgnoredFileBean... filesToIgnore) {
867 myIgnoredIdeaLevel.set(filesToIgnore);
868 updateIgnoredFiles(true);
871 private void updateIgnoredFiles(final boolean checkIgnored) {
872 synchronized (myDataLock) {
873 List<VirtualFile> unversionedFiles = myComposite.getVFHolder(FileHolder.HolderType.UNVERSIONED).getFiles();
874 //List<VirtualFile> ignoredFiles = myComposite.getVFHolder(FileHolder.HolderType.IGNORED).getFiles();
875 boolean somethingChanged = false;
876 for(VirtualFile file: unversionedFiles) {
877 if (isIgnoredFile(file)) {
878 somethingChanged = true;
879 myComposite.getVFHolder(FileHolder.HolderType.UNVERSIONED).removeFile(file);
880 myComposite.getIgnoredFileHolder().addFile(file, "", false);
883 /*if (checkIgnored) {
884 for(VirtualFile file: ignoredFiles) {
885 if (!isIgnoredFile(file)) {
886 somethingChanged = true;
887 // the file may have been reported as ignored by the VCS, so we can't directly move it to unversioned files
888 VcsDirtyScopeManager.getInstance(myProject).fileDirty(file);
892 if (somethingChanged) {
893 myFileStatusManager.fileStatusesChanged();
894 myChangesViewManager.scheduleRefresh();
899 public IgnoredFileBean[] getFilesToIgnore() {
900 return myIgnoredIdeaLevel.getFilesToIgnore();
903 public boolean isIgnoredFile(@NotNull VirtualFile file) {
904 return myIgnoredIdeaLevel.isIgnoredFile(file);
907 @Nullable
908 public String getSwitchedBranch(final VirtualFile file) {
909 synchronized (myDataLock) {
910 return myWorker.getBranchForFile(file);
914 @Override
915 public String getDefaultListName() {
916 synchronized (myDataLock) {
917 return myWorker.getDefaultListName();
921 private static VirtualFile[] collectFiles(final List<FilePath> paths) {
922 final ArrayList<VirtualFile> result = new ArrayList<VirtualFile>();
923 for (FilePath path : paths) {
924 if (path.getVirtualFile() != null) {
925 result.add(path.getVirtualFile());
929 return VfsUtil.toVirtualFileArray(result);
932 public boolean setReadOnly(final String name, final boolean value) {
933 synchronized (myDataLock) {
934 final boolean result = myModifier.setReadOnly(name, value);
935 myChangesViewManager.scheduleRefresh();
936 return result;
940 public boolean editName(@NotNull final String fromName, @NotNull final String toName) {
941 synchronized (myDataLock) {
942 final boolean result = myModifier.editName(fromName, toName);
943 myChangesViewManager.scheduleRefresh();
944 return result;
948 public String editComment(@NotNull final String fromName, final String newComment) {
949 synchronized (myDataLock) {
950 final String oldComment = myModifier.editComment(fromName, newComment);
951 myChangesViewManager.scheduleRefresh();
952 return oldComment;
957 * Can be called only from not AWT thread; to do smthg after ChangeListManager refresh, call invokeAfterUpdate
959 public boolean ensureUpToDate(final boolean canBeCanceled) {
960 final EnsureUpToDateFromNonAWTThread worker = new EnsureUpToDateFromNonAWTThread(myProject);
961 worker.execute();
962 return worker.isDone();
965 // only a light attempt to show that some dirty scope request is asynchronously coming
966 // for users to see changes are not valid
967 // (commit -> asynch synch VFS -> asynch vcs dirty scope)
968 public void showLocalChangesInvalidated() {
969 synchronized (myDataLock) {
970 myShowLocalChangesInvalidated = true;
974 public ChangelistConflictTracker getConflictTracker() {
975 return myConflictTracker;
978 private static class MyChangesDeltaForwarder implements PlusMinus<Pair<String, AbstractVcs>> {
979 private SlowlyClosingAlarm myAlarm;
980 private RemoteRevisionsCache myRevisionsCache;
981 private final ProjectLevelVcsManager myVcsManager;
983 public MyChangesDeltaForwarder(final Project project, final ExecutorService service) {
984 myAlarm = ControlledAlarmFactory.createOnSharedThread(project, "changes delta consumer forwarder", service);
985 myRevisionsCache = RemoteRevisionsCache.getInstance(project);
986 myVcsManager = ProjectLevelVcsManager.getInstance(project);
989 public void plus(final Pair<String, AbstractVcs> stringAbstractVcsPair) {
990 myAlarm.addRequest(new Runnable() {
991 public void run() {
992 myRevisionsCache.plus(getCorrectedPair(stringAbstractVcsPair));
997 public void minus(final Pair<String, AbstractVcs> stringAbstractVcsPair) {
998 myAlarm.addRequest(new Runnable() {
999 public void run() {
1000 myRevisionsCache.minus(getCorrectedPair(stringAbstractVcsPair));
1005 private Pair<String, AbstractVcs> getCorrectedPair(final Pair<String, AbstractVcs> stringAbstractVcsPair) {
1006 Pair<String, AbstractVcs> correctedPair = stringAbstractVcsPair;
1007 if (stringAbstractVcsPair.getSecond() == null) {
1008 final String path = stringAbstractVcsPair.getFirst();
1009 correctedPair = new Pair<String, AbstractVcs>(path, myVcsManager.findVcsByName(findVcs(path).getName()));
1011 return correctedPair;
1014 @Nullable
1015 private VcsKey findVcs(final String path) {
1016 // does not matter directory or not
1017 final AbstractVcs vcs = myVcsManager.getVcsFor(FilePathImpl.create(new File(path), false));
1018 return vcs == null ? null : vcs.getKeyInstanceMethod();