IDEA-187498 vcs: use CLM to delete empty changelist after shelve

We can't deduce if all changes in a changelist were shelved
using only `List<Change>`, as some ranges could've been excluded from commit.
Instead, schedule deletion after CLM refresh.

Also, ask `AbstractVcs.mayRemoveChangeList` if changelist can be safely deleted.

follow-up: 73316157d8
This commit is contained in:
Aleksey Pivovarov
2018-03-05 15:28:22 +03:00
parent 37389f3538
commit 7b4666d532
5 changed files with 76 additions and 34 deletions
@@ -161,6 +161,7 @@ public abstract class ChangeListManager implements ChangeListModification {
public abstract void scheduleAutomaticEmptyChangeListDeletion(@NotNull LocalChangeList list);
public abstract void scheduleAutomaticEmptyChangeListDeletion(@NotNull LocalChangeList list, boolean silently);
@NotNull
public abstract IgnoredFileBean[] getFilesToIgnore();
@@ -187,7 +187,7 @@ public class ChangesUtil {
}
@Nullable
public static ChangeList getChangeListIfOnlyOne(@NotNull Project project, @Nullable Change[] changes) {
public static LocalChangeList getChangeListIfOnlyOne(@NotNull Project project, @Nullable Change[] changes) {
ChangeListManager manager = ChangeListManager.getInstance(project);
String changeListName = manager.getChangeListNameIfOnlyOne(changes);
@@ -25,6 +25,7 @@ import com.intellij.openapi.util.*;
import com.intellij.openapi.util.registry.Registry;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vcs.*;
import com.intellij.openapi.vcs.VcsShowConfirmationOption.Value;
import com.intellij.openapi.vcs.changes.ChangeListWorker.ChangeListUpdater;
import com.intellij.openapi.vcs.changes.actions.ChangeListRemoveConfirmation;
import com.intellij.openapi.vcs.changes.conflicts.ChangelistConflictTracker;
@@ -95,7 +96,9 @@ public class ChangeListManagerImpl extends ChangeListManagerEx implements Projec
@NotNull private ProgressIndicator myUpdateChangesProgressIndicator = createProgressIndicator();
private volatile String myFreezeName;
@NotNull private final Collection<LocalChangeList> myListsToBeDeleted = new HashSet<>();
@NotNull private final Set<String> myListsToBeDeletedSilently = new HashSet<>();
@NotNull private final Set<String> myListsToBeDeleted = new HashSet<>();
private boolean myEmptyListDeletionScheduled;
private boolean myModalNotificationsBlocked;
private final List<CommitExecutor> myRegisteredCommitExecutors = new ArrayList<>();
@@ -149,33 +152,71 @@ public class ChangeListManagerImpl extends ChangeListManagerEx implements Projec
}
@Override
public void scheduleAutomaticEmptyChangeListDeletion(@NotNull LocalChangeList oldList) {
if (ApplicationManager.getApplication().isUnitTestMode() &&
myConfig.REMOVE_EMPTY_INACTIVE_CHANGELISTS == VcsShowConfirmationOption.Value.SHOW_CONFIRMATION) {
return;
}
invokeAfterUpdate(() -> {
LocalChangeList actualList = getChangeList(oldList.getId());
if (actualList == null || actualList.isDefault() || !actualList.getChanges().isEmpty()) {
return;
}
if (myModalNotificationsBlocked &&
myConfig.REMOVE_EMPTY_INACTIVE_CHANGELISTS == VcsShowConfirmationOption.Value.SHOW_CONFIRMATION) {
myListsToBeDeleted.add(oldList);
} else {
deleteEmptyChangeLists(Collections.singletonList(actualList));
}
}, InvokeAfterUpdateMode.SILENT, null, null);
public void scheduleAutomaticEmptyChangeListDeletion(@NotNull LocalChangeList list) {
scheduleAutomaticEmptyChangeListDeletion(list, false);
}
private void deleteEmptyChangeLists(@NotNull Collection<LocalChangeList> lists) {
if (lists.isEmpty() || myConfig.REMOVE_EMPTY_INACTIVE_CHANGELISTS == VcsShowConfirmationOption.Value.DO_NOTHING_SILENTLY) {
return;
@Override
public void scheduleAutomaticEmptyChangeListDeletion(@NotNull LocalChangeList oldList, boolean silently) {
synchronized (myDataLock) {
if (silently) {
myListsToBeDeletedSilently.add(oldList.getId());
}
else {
myListsToBeDeleted.add(oldList.getId());
}
if (!myEmptyListDeletionScheduled) {
myEmptyListDeletionScheduled = true;
invokeAfterUpdate(() -> deleteEmptyChangeLists(), InvokeAfterUpdateMode.SILENT, null, null);
}
}
}
@CalledInAwt
private void deleteEmptyChangeLists() {
List<LocalChangeList> listsToBeDeletedSilently;
List<LocalChangeList> listsToBeDeleted;
Function<String, LocalChangeList> toDeleteMapping = id -> {
LocalChangeList list = getChangeList(id);
if (list == null || list.isDefault() || list.isReadOnly() || !list.getChanges().isEmpty()) return null;
return list;
};
synchronized (myDataLock) {
myListsToBeDeleted.removeAll(myListsToBeDeletedSilently);
listsToBeDeletedSilently = ContainerUtil.mapNotNull(myListsToBeDeletedSilently, toDeleteMapping);
myListsToBeDeletedSilently.clear();
boolean askLater = myModalNotificationsBlocked &&
myConfig.REMOVE_EMPTY_INACTIVE_CHANGELISTS == Value.SHOW_CONFIRMATION;
if (!askLater) {
listsToBeDeleted = ContainerUtil.mapNotNull(myListsToBeDeleted, toDeleteMapping);
myListsToBeDeleted.clear();
}
else {
listsToBeDeleted = Collections.emptyList();
}
myEmptyListDeletionScheduled = false;
}
ChangeListRemoveConfirmation.processLists(myProject, false, lists, new ChangeListRemoveConfirmation() {
if (myConfig.REMOVE_EMPTY_INACTIVE_CHANGELISTS == Value.DO_NOTHING_SILENTLY ||
myConfig.REMOVE_EMPTY_INACTIVE_CHANGELISTS == Value.SHOW_CONFIRMATION &&
ApplicationManager.getApplication().isUnitTestMode()) {
listsToBeDeleted.clear();
}
ChangeListRemoveConfirmation.processLists(myProject, false, listsToBeDeletedSilently, new ChangeListRemoveConfirmation() {
@Override
public boolean askIfShouldRemoveChangeLists(@NotNull List<? extends LocalChangeList> toAsk) {
return true;
}
});
ChangeListRemoveConfirmation.processLists(myProject, false, listsToBeDeleted, new ChangeListRemoveConfirmation() {
@Override
public boolean askIfShouldRemoveChangeLists(@NotNull List<? extends LocalChangeList> toAsk) {
return myConfig.REMOVE_EMPTY_INACTIVE_CHANGELISTS == VcsShowConfirmationOption.Value.DO_ACTION_SILENTLY ||
@@ -235,8 +276,7 @@ public class ChangeListManagerImpl extends ChangeListManagerEx implements Projec
@CalledInAwt
public void unblockModalNotifications() {
myModalNotificationsBlocked = false;
deleteEmptyChangeLists(myListsToBeDeleted);
myListsToBeDeleted.clear();
deleteEmptyChangeLists();
}
@Override
@@ -77,13 +77,9 @@ public class ShelveChangesCommitExecutor extends LocalCommitExecutor {
ShelvedChangesViewManager.getInstance(myProject).activateView(list);
Change[] changesArray = changes.toArray(new Change[0]);
// todo better under lock
ChangeList changeList = ChangesUtil.getChangeListIfOnlyOne(myProject, changesArray);
if (changeList instanceof LocalChangeList) {
LocalChangeList localChangeList = (LocalChangeList) changeList;
if (localChangeList.getChanges().size() == changes.size() && !localChangeList.isReadOnly() && (! localChangeList.isDefault())) {
ChangeListManager.getInstance(myProject).removeChangeList(localChangeList.getName());
}
LocalChangeList changeList = ChangesUtil.getChangeListIfOnlyOne(myProject, changesArray);
if (changeList != null) {
ChangeListManager.getInstance(myProject).scheduleAutomaticEmptyChangeListDeletion(changeList, true);
}
}
catch (final Exception ex) {
@@ -173,6 +173,11 @@ public class MockChangeListManager extends ChangeListManagerEx {
@Override
public void scheduleAutomaticEmptyChangeListDeletion(@NotNull LocalChangeList list) {
scheduleAutomaticEmptyChangeListDeletion(list, false);
}
@Override
public void scheduleAutomaticEmptyChangeListDeletion(@NotNull LocalChangeList list, boolean silently) {
throw new UnsupportedOperationException();
}