From 7b4666d532c46bfaa8330d61b05d1a7f74638f8e Mon Sep 17 00:00:00 2001 From: Aleksey Pivovarov Date: Sun, 4 Mar 2018 00:25:08 +0300 Subject: [PATCH] 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`, 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: 73316157d815f1f7bddc54c199e0c44c2393f161 --- .../vcs/changes/ChangeListManager.java | 1 + .../openapi/vcs/changes/ChangesUtil.java | 2 +- .../vcs/changes/ChangeListManagerImpl.java | 92 +++++++++++++------ .../shelf/ShelveChangesCommitExecutor.java | 10 +- .../vcs/MockChangeListManager.java | 5 + 5 files changed, 76 insertions(+), 34 deletions(-) diff --git a/platform/vcs-api/src/com/intellij/openapi/vcs/changes/ChangeListManager.java b/platform/vcs-api/src/com/intellij/openapi/vcs/changes/ChangeListManager.java index bd6d55d53c79..4f01f2f087f8 100644 --- a/platform/vcs-api/src/com/intellij/openapi/vcs/changes/ChangeListManager.java +++ b/platform/vcs-api/src/com/intellij/openapi/vcs/changes/ChangeListManager.java @@ -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(); diff --git a/platform/vcs-api/src/com/intellij/openapi/vcs/changes/ChangesUtil.java b/platform/vcs-api/src/com/intellij/openapi/vcs/changes/ChangesUtil.java index c2e4bbc14f2a..e355157414bf 100644 --- a/platform/vcs-api/src/com/intellij/openapi/vcs/changes/ChangesUtil.java +++ b/platform/vcs-api/src/com/intellij/openapi/vcs/changes/ChangesUtil.java @@ -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); diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ChangeListManagerImpl.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ChangeListManagerImpl.java index 7cd821a03309..39fee406bd69 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ChangeListManagerImpl.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ChangeListManagerImpl.java @@ -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 myListsToBeDeleted = new HashSet<>(); + @NotNull private final Set myListsToBeDeletedSilently = new HashSet<>(); + @NotNull private final Set myListsToBeDeleted = new HashSet<>(); + private boolean myEmptyListDeletionScheduled; private boolean myModalNotificationsBlocked; private final List 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 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 listsToBeDeletedSilently; + List listsToBeDeleted; + + Function 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 toAsk) { + return true; + } + }); + + ChangeListRemoveConfirmation.processLists(myProject, false, listsToBeDeleted, new ChangeListRemoveConfirmation() { @Override public boolean askIfShouldRemoveChangeLists(@NotNull List 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 diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/shelf/ShelveChangesCommitExecutor.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/shelf/ShelveChangesCommitExecutor.java index f539f9fed75e..62703650c920 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/shelf/ShelveChangesCommitExecutor.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/shelf/ShelveChangesCommitExecutor.java @@ -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) { diff --git a/platform/vcs-impl/testSrc/com/intellij/testFramework/vcs/MockChangeListManager.java b/platform/vcs-impl/testSrc/com/intellij/testFramework/vcs/MockChangeListManager.java index d39fcbf1afed..667dcdc29aa0 100644 --- a/platform/vcs-impl/testSrc/com/intellij/testFramework/vcs/MockChangeListManager.java +++ b/platform/vcs-impl/testSrc/com/intellij/testFramework/vcs/MockChangeListManager.java @@ -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(); }