Merge branch 'nz/shared_shelf'
authorNadya Zabrodina <Nadya.Zabrodina@jetbrains.com>
Mon, 17 Aug 2015 16:39:55 +0000 (19:39 +0300)
committerNadya Zabrodina <Nadya.Zabrodina@jetbrains.com>
Mon, 17 Aug 2015 16:39:55 +0000 (19:39 +0300)
Conflicts:
platform/vcs-impl/src/com/intellij/openapi/vcs/changes/shelf/CompoundShelfFileProcessor.java
platform/vcs-impl/src/com/intellij/openapi/vcs/changes/shelf/ShelveChangesManager.java

1  2 
platform/vcs-impl/src/com/intellij/openapi/vcs/changes/shelf/ShelveChangesManager.java

index 577e8f488169b2fed7b1deae428dcfe99166d818,9a4f0bdf767f7948a3b73b5d19bcdb3490ca099f..da65f1d9741b0e5c7e9f0e71ad8dfced5795ae5a
   */
  package com.intellij.openapi.vcs.changes.shelf;
  
- import com.intellij.ide.impl.ProjectUtil;
  import com.intellij.lifecycle.PeriodicalTasksCloser;
  import com.intellij.openapi.application.ApplicationManager;
 +import com.intellij.openapi.application.ModalityState;
 +import com.intellij.openapi.application.PathManager;
  import com.intellij.openapi.application.impl.LaterInvocator;
  import com.intellij.openapi.components.AbstractProjectComponent;
+ import com.intellij.openapi.components.PathMacroManager;
+ import com.intellij.openapi.components.TrackingPathMacroSubstitutor;
  import com.intellij.openapi.diagnostic.Logger;
  import com.intellij.openapi.diff.impl.patch.*;
  import com.intellij.openapi.diff.impl.patch.apply.ApplyFilePatchBase;
  import com.intellij.openapi.diff.impl.patch.formove.CustomBinaryPatchApplier;
  import com.intellij.openapi.diff.impl.patch.formove.PatchApplier;
+ import com.intellij.openapi.options.BaseSchemeProcessor;
+ import com.intellij.openapi.options.SchemesManager;
+ import com.intellij.openapi.options.SchemesManagerFactory;
+ import com.intellij.openapi.progress.AsynchronousExecution;
  import com.intellij.openapi.progress.ProgressIndicator;
  import com.intellij.openapi.progress.ProgressManager;
  import com.intellij.openapi.project.Project;
  import com.intellij.openapi.util.*;
  import com.intellij.openapi.util.io.FileUtil;
- import com.intellij.openapi.util.io.FileUtilRt;
+ import com.intellij.openapi.util.text.StringUtil;
  import com.intellij.openapi.vcs.*;
  import com.intellij.openapi.vcs.changes.*;
  import com.intellij.openapi.vcs.changes.patch.ApplyPatchDefaultExecutor;
@@@ -52,13 -55,16 +57,16 @@@ import com.intellij.openapi.vfs.Virtual
  import com.intellij.util.Consumer;
  import com.intellij.util.PathUtil;
  import com.intellij.util.SmartList;
 -import com.intellij.util.continuation.*;
+ import com.intellij.util.containers.ContainerUtil;
  import com.intellij.util.messages.MessageBus;
  import com.intellij.util.messages.Topic;
  import com.intellij.util.text.CharArrayCharSequence;
+ import com.intellij.util.text.UniqueNameGenerator;
  import com.intellij.util.ui.UIUtil;
  import com.intellij.vcsUtil.FilesProgress;
  import org.jdom.Element;
 +import org.jetbrains.annotations.CalledInAny;
+ import org.jdom.Parent;
  import org.jetbrains.annotations.NonNls;
  import org.jetbrains.annotations.NotNull;
  import org.jetbrains.annotations.Nullable;
@@@ -68,39 -74,59 +76,59 @@@ import javax.swing.event.ChangeListener
  import java.io.*;
  import java.util.*;
  
- import static com.intellij.openapi.vcs.changes.shelf.CompoundShelfFileProcessor.SHELF_DIR_NAME;
  public class ShelveChangesManager extends AbstractProjectComponent implements JDOMExternalizable {
    private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.vcs.changes.shelf.ShelveChangesManager");
+   @NonNls private static final String ELEMENT_CHANGELIST = "changelist";
+   @NonNls private static final String ELEMENT_RECYCLED_CHANGELIST = "recycled_changelist";
+   @NonNls private static final String DEFAULT_PATCH_NAME = "shelved";
+   @NotNull private final TrackingPathMacroSubstitutor myPathMacroSubstitutor;
+   @NotNull private final SchemesManager<ShelvedChangeList, ShelvedChangeList> mySchemeManager;
  
    public static ShelveChangesManager getInstance(Project project) {
      return PeriodicalTasksCloser.getInstance().safeGetComponent(project, ShelveChangesManager.class);
    }
  
+   private static final String SHELVE_MANAGER_DIR_PATH = "shelf";
    private final MessageBus myBus;
-   private final List<ShelvedChangeList> myShelvedChangeLists = new ArrayList<ShelvedChangeList>();
-   private final List<ShelvedChangeList> myRecycledShelvedChangeLists = new ArrayList<ShelvedChangeList>();
  
    @NonNls private static final String ATTRIBUTE_SHOW_RECYCLED = "show_recycled";
-   private final CompoundShelfFileProcessor myFileProcessor;
+   @NotNull private final CompoundShelfFileProcessor myFileProcessor;
  
    public static final Topic<ChangeListener> SHELF_TOPIC = new Topic<ChangeListener>("shelf updates", ChangeListener.class);
    private boolean myShowRecycled;
  
    public ShelveChangesManager(final Project project, final MessageBus bus) {
      super(project);
+     myPathMacroSubstitutor = PathMacroManager.getInstance(myProject).createTrackingSubstitutor();
      myBus = bus;
-     if (project.isDefault()) {
-       myFileProcessor = new CompoundShelfFileProcessor(null, PathManager.getConfigPath() + File.separator + SHELF_DIR_NAME);
-     }
-     else if (ProjectUtil.isDirectoryBased(project)) {
-       VirtualFile dir = project.getBaseDir();
-       String shelfBaseDirPath = dir == null ? "" : dir.getPath() + File.separator + Project.DIRECTORY_STORE_FOLDER;
-       myFileProcessor = new CompoundShelfFileProcessor(shelfBaseDirPath);
-     }
-     else {
-       myFileProcessor = new CompoundShelfFileProcessor();
-     }
+     mySchemeManager =
+       SchemesManagerFactory.getInstance(project).create(SHELVE_MANAGER_DIR_PATH, new BaseSchemeProcessor<ShelvedChangeList>() {
+         @Nullable
+         @Override
+         public ShelvedChangeList readScheme(@NotNull Element element) throws InvalidDataException {
+           return readOneShelvedChangeList(element);
+         }
+         @Override
+         public Parent writeScheme(@NotNull ShelvedChangeList scheme) throws WriteExternalException {
+           Element child = new Element(ELEMENT_CHANGELIST);
+           scheme.writeExternal(child);
+           myPathMacroSubstitutor.collapsePaths(child);
+           return child;
+         }
+       });
+     myFileProcessor = new CompoundShelfFileProcessor(mySchemeManager.getRootDirectory());
+     ChangeListManager.getInstance(project).addDirectoryToIgnoreImplicitly(mySchemeManager.getRootDirectory().getAbsolutePath());
+     mySchemeManager.loadSchemes();
+   }
+   @NotNull
+   private ShelvedChangeList readOneShelvedChangeList(@NotNull Element element) throws InvalidDataException {
+     ShelvedChangeList data = new ShelvedChangeList();
+     myPathMacroSubstitutor.expandPaths(element);
+     data.readExternal(element);
+     return data;
    }
  
    @Override
  
    @Override
    public void readExternal(Element element) throws InvalidDataException {
-     //noinspection unchecked
      final String showRecycled = element.getAttributeValue(ATTRIBUTE_SHOW_RECYCLED);
      if (showRecycled != null) {
        myShowRecycled = Boolean.parseBoolean(showRecycled);
      else {
        myShowRecycled = true;
      }
+     migrateOldShelfInfo(element, true);
+     migrateOldShelfInfo(element, false);
+   }
  
-     readExternal(element, myShelvedChangeLists, myRecycledShelvedChangeLists);
+   //load old shelf information from workspace.xml without moving .patch and binary files into new directory
+   private void migrateOldShelfInfo(@NotNull Element element, boolean recycled) throws InvalidDataException {
+     for (Element changeSetElement : element.getChildren(recycled ? ELEMENT_RECYCLED_CHANGELIST : ELEMENT_CHANGELIST)) {
+       ShelvedChangeList list = readOneShelvedChangeList(changeSetElement);
+       File uniqueDir = generateUniqueSchemePatchDir(list.DESCRIPTION, false);
+       list.setName(uniqueDir.getName());
+       list.setRecycled(recycled);
+       mySchemeManager.addNewScheme(list, false);
+     }
    }
  
-   public static void readExternal(final Element element, final List<ShelvedChangeList> changes, final List<ShelvedChangeList> recycled)
-     throws InvalidDataException {
-     changes.addAll(ShelvedChangeList.readChanges(element, false, true));
+   /**
+    * Should be called only once: when Settings Repository plugin runs first time
+    *
+    * @return collection of non-migrated or not deleted files to show a error somewhere outside
+    */
+   @NotNull
+   public Collection<String> checkAndMigrateOldPatchResourcesToNewSchemeStorage() {
+     Collection<String> nonMigratedPaths = ContainerUtil.newArrayList();
+     for (ShelvedChangeList list : mySchemeManager.getAllSchemes()) {
+       File patchDir = new File(myFileProcessor.getBaseDir(), list.getName());
+       nonMigratedPaths.addAll(migrateIfNeededToSchemeDir(list, patchDir));
+     }
+     return nonMigratedPaths;
+   }
+   @NotNull
+   private static Collection<String> migrateIfNeededToSchemeDir(@NotNull ShelvedChangeList list, @NotNull File targetDirectory) {
+     // it should be enough for migration to check if resource directory exists. If any bugs appeared add isAncestor checks for each path
+     if (targetDirectory.exists() || !targetDirectory.mkdirs()) return ContainerUtil.emptyList();
+     Collection<String> nonMigratedPaths = ContainerUtil.newArrayList();
+     //try to move .patch file
+     File patchFile = new File(list.PATH);
+     if (patchFile.exists()) {
+       File newPatchFile = getPatchFileInConfigDir(targetDirectory);
+       try {
+         FileUtil.copy(patchFile, newPatchFile);
+         list.PATH = newPatchFile.getPath();
+         FileUtil.delete(patchFile);
+       }
+       catch (IOException e) {
+         nonMigratedPaths.add(list.PATH);
+       }
+     }
  
-     recycled.addAll(ShelvedChangeList.readChanges(element, true, true));
+     for (ShelvedBinaryFile file : list.getBinaryFiles()) {
+       if (file.SHELVED_PATH != null) {
+         File shelvedFile = new File(file.SHELVED_PATH);
+         if (!StringUtil.isEmptyOrSpaces(file.AFTER_PATH) && shelvedFile.exists()) {
+           File newShelvedFile = new File(targetDirectory, PathUtil.getFileName(file.AFTER_PATH));
+           try {
+             FileUtil.copy(shelvedFile, newShelvedFile);
+             file.SHELVED_PATH = newShelvedFile.getPath();
+             FileUtil.delete(shelvedFile);
+           }
+           catch (IOException e) {
+             nonMigratedPaths.add(shelvedFile.getPath());
+           }
+         }
+       }
+     }
+     return nonMigratedPaths;
    }
  
    @Override
    public void writeExternal(Element element) throws WriteExternalException {
      element.setAttribute(ATTRIBUTE_SHOW_RECYCLED, Boolean.toString(myShowRecycled));
-     ShelvedChangeList.writeChanges(myShelvedChangeLists, myRecycledShelvedChangeLists, element);
    }
  
    public List<ShelvedChangeList> getShelvedChangeLists() {
-     return Collections.unmodifiableList(myShelvedChangeLists);
+     return getRecycled(false);
+   }
+   @NotNull
+   private List<ShelvedChangeList> getRecycled(final boolean recycled) {
+     return ContainerUtil.newUnmodifiableList(ContainerUtil.filter(mySchemeManager.getAllSchemes(), new Condition<ShelvedChangeList>() {
+       @Override
+       public boolean value(ShelvedChangeList list) {
+         return recycled ? list.isRecycled() : !list.isRecycled();
+       }
+     }));
    }
  
    public ShelvedChangeList shelveChanges(final Collection<Change> changes, final String commitMessage, final boolean rollback)
      if (progressIndicator != null) {
        progressIndicator.setText(VcsBundle.message("shelve.changes.progress.title"));
      }
+     File schemePatchDir = generateUniqueSchemePatchDir(commitMessage, true);
      final List<Change> textChanges = new ArrayList<Change>();
      final List<ShelvedBinaryFile> binaryFiles = new ArrayList<ShelvedBinaryFile>();
      for (Change change : changes) {
          continue;
        }
        if (change.getBeforeRevision() instanceof BinaryContentRevision || change.getAfterRevision() instanceof BinaryContentRevision) {
-         binaryFiles.add(shelveBinaryFile(change));
+         binaryFiles.add(shelveBinaryFile(schemePatchDir, change));
        }
        else {
          textChanges.add(change);
  
      final ShelvedChangeList changeList;
      try {
-       File patchPath = getPatchPath(commitMessage);
+       File patchPath = getPatchFileInConfigDir(schemePatchDir);
        ProgressManager.checkCanceled();
        final List<FilePatch> patches =
          IdeaTextPatchBuilder.buildPatch(myProject, textChanges, myProject.getBaseDir().getPresentableUrl(), false);
          patchPath, commitContext);
  
        changeList = new ShelvedChangeList(patchPath.toString(), commitMessage.replace('\n', ' '), binaryFiles);
+       changeList.setName(schemePatchDir.getName());
        ProgressManager.checkCanceled();
-       myShelvedChangeLists.add(changeList);
+       mySchemeManager.addNewScheme(changeList, false);
  
        if (rollback) {
          final String operationName = UIUtil.removeMnemonic(RollbackChangesDialog.operationNameByChanges(myProject, changes));
      return changeList;
    }
  
+   @NotNull
+   private static File getPatchFileInConfigDir(@NotNull File schemePatchDir) {
+     return new File(schemePatchDir, DEFAULT_PATCH_NAME + "." + VcsConfiguration.PATCH);
+   }
    private void baseRevisionsOfDvcsIntoContext(List<Change> textChanges, CommitContext commitContext) {
      ProjectLevelVcsManager vcsManager = ProjectLevelVcsManager.getInstance(myProject);
      if (vcsManager.dvcsUsedInProject() && VcsConfiguration.getInstance(myProject).INCLUDE_TEXT_INTO_SHELF) {
    public ShelvedChangeList importFilePatches(final String fileName, final List<FilePatch> patches, final PatchEP[] patchTransitExtensions)
      throws IOException {
      try {
-       final File patchPath = getPatchPath(fileName);
+       File schemePatchDir = generateUniqueSchemePatchDir(fileName, true);
+       File patchPath = getPatchFileInConfigDir(schemePatchDir);
        myFileProcessor.savePathFile(
          new CompoundShelfFileProcessor.ContentProvider() {
            @Override
  
        final ShelvedChangeList changeList =
          new ShelvedChangeList(patchPath.toString(), fileName.replace('\n', ' '), new SmartList<ShelvedBinaryFile>());
-       myShelvedChangeLists.add(changeList);
+       changeList.setName(schemePatchDir.getName());
+       mySchemeManager.addNewScheme(changeList, false);
        return changeList;
      }
      finally {
        for (VirtualFile file : files) {
          filesProgress.updateIndicator(file);
          final String description = file.getNameWithoutExtension().replace('_', ' ');
-         final File patchPath = getPatchPath(description);
+         File schemeNameDir = generateUniqueSchemePatchDir(description, true);
+         final File patchPath = getPatchFileInConfigDir(schemeNameDir);
          final ShelvedChangeList list = new ShelvedChangeList(patchPath.getPath(), description, new SmartList<ShelvedBinaryFile>(),
                                                               file.getTimeStamp());
+         list.setName(schemeNameDir.getName());
          try {
            final List<TextFilePatch> patchesList = loadPatches(myProject, file.getPath(), new CommitContext());
            if (!patchesList.isEmpty()) {
              FileUtil.copy(new File(file.getPath()), patchPath);
              // add only if ok to read patch
-             myShelvedChangeLists.add(list);
+             mySchemeManager.addNewScheme(list, false);
              result.add(list);
            }
          }
      return result;
    }
  
-   private ShelvedBinaryFile shelveBinaryFile(final Change change) throws IOException {
+   private ShelvedBinaryFile shelveBinaryFile(@NotNull File schemePatchDir, final Change change) throws IOException {
      final ContentRevision beforeRevision = change.getBeforeRevision();
      final ContentRevision afterRevision = change.getAfterRevision();
      File beforeFile = beforeRevision == null ? null : beforeRevision.getFile().getIOFile();
      File afterFile = afterRevision == null ? null : afterRevision.getFile().getIOFile();
      String shelvedPath = null;
      if (afterFile != null) {
-       String shelvedName = FileUtil.getNameWithoutExtension(afterFile.getName());
-       String shelvedExt = FileUtilRt.getExtension(afterFile.getName());
-       File shelvedFile = FileUtil.findSequentNonexistentFile(myFileProcessor.getBaseIODir(), shelvedName, shelvedExt);
-       myFileProcessor.saveFile(afterRevision.getFile().getIOFile(), shelvedFile);
+       File shelvedFile = new File(schemePatchDir, afterFile.getName());
+       FileUtil.copy(afterRevision.getFile().getIOFile(), shelvedFile);
        shelvedPath = shelvedFile.getPath();
      }
      String beforePath = ChangesUtil.getProjectRelativePath(myProject, beforeFile);
      }
    }
  
-   private File getPatchPath(@NonNls final String commitMessage) {
-     File file = myFileProcessor.getBaseIODir();
-     if (!file.exists()) {
+   @NotNull
+   private File generateUniqueSchemePatchDir(@NotNull final String defaultName, boolean createResourceDirectory) {
+     String uniqueName = UniqueNameGenerator
+       .generateUniqueName(shortenAndSanitize(defaultName), mySchemeManager.getAllSchemeNames());
+     File dir = new File(myFileProcessor.getBaseDir(), uniqueName);
+     if (createResourceDirectory && !dir.exists()) {
        //noinspection ResultOfMethodCallIgnored
-       file.mkdirs();
+       dir.mkdirs();
      }
-     return suggestPatchName(myProject, commitMessage.length() > PatchNameChecker.MAX ? commitMessage.substring(0, PatchNameChecker.MAX) :
-                                        commitMessage, file, VcsConfiguration.PATCH);
+     return dir;
    }
  
-   public static File suggestPatchName(Project project, final String commitMessage, final File file, String extension) {
-     @NonNls String defaultPath = PathUtil.suggestFileName(commitMessage);
-     if (defaultPath.isEmpty()) {
-       defaultPath = "unnamed";
-     }
-     if (defaultPath.length() > PatchNameChecker.MAX - 10) {
-       defaultPath = defaultPath.substring(0, PatchNameChecker.MAX - 10);
-     }
+   @NotNull
+   // for create patch only; todo move or unify with unique directory creation
+   public static File suggestPatchName(Project project, @NotNull final String commitMessage, final File file, String extension) {
+     @NonNls String defaultPath = shortenAndSanitize(commitMessage);
      while (true) {
        final File nonexistentFile = FileUtil.findSequentNonexistentFile(file, defaultPath,
                                                                         extension == null
      }
    }
  
 -  public void unshelveChangeList(@NotNull final ShelvedChangeList changeList, @Nullable final List<ShelvedChange> changes,
 -                                 @Nullable final List<ShelvedBinaryFile> binaryFiles, final LocalChangeList targetChangeList) {
 -    unshelveChangeList(changeList, changes, binaryFiles, targetChangeList, true);
+   @NotNull
+   private static String shortenAndSanitize(@NotNull String commitMessage) {
+     @NonNls String defaultPath = FileUtil.sanitizeFileName(commitMessage);
+     if (defaultPath.isEmpty()) {
+       defaultPath = "unnamed";
+     }
+     if (defaultPath.length() > PatchNameChecker.MAX - 10) {
+       defaultPath = defaultPath.substring(0, PatchNameChecker.MAX - 10);
+     }
+     return defaultPath;
+   }
 +  @CalledInAny
 +  public void unshelveChangeList(final ShelvedChangeList changeList,
 +                                 @Nullable final List<ShelvedChange> changes,
 +                                 @Nullable final List<ShelvedBinaryFile> binaryFiles,
 +                                 @Nullable final LocalChangeList targetChangeList,
 +                                 boolean showSuccessNotification) {
 +    unshelveChangeList(changeList, changes, binaryFiles, targetChangeList, showSuccessNotification, false, false, null, null);
    }
  
 -  @AsynchronousExecution
 -  private void unshelveChangeList(@NotNull final ShelvedChangeList changeList,
 -                                  @Nullable final List<ShelvedChange> changes,
 -                                  @Nullable final List<ShelvedBinaryFile> binaryFiles,
 -                                  @Nullable final LocalChangeList targetChangeList,
 -                                  boolean showSuccessNotification) {
 -    final Continuation continuation = Continuation.createForCurrentProgress(myProject, true, "Unshelve changes");
 -    final GatheringContinuationContext initContext = new GatheringContinuationContext();
 -    scheduleUnshelveChangeList(changeList, changes, binaryFiles, targetChangeList, showSuccessNotification, initContext, false,
 -                               false, null, null);
 -    continuation.run(initContext.getList());
 -  }
 -
 -  @AsynchronousExecution
 -  public void scheduleUnshelveChangeList(@NotNull final ShelvedChangeList changeList,
 -                                         @Nullable final List<ShelvedChange> changes,
 -                                         @Nullable final List<ShelvedBinaryFile> binaryFiles,
 -                                         @Nullable final LocalChangeList targetChangeList,
 -                                         final boolean showSuccessNotification,
 -                                         final ContinuationContext context,
 -                                         final boolean systemOperation,
 -                                         final boolean reverse,
 -                                         final String leftConflictTitle,
 -                                         final String rightConflictTitle) {
 -    context.next(new TaskDescriptor("", Where.AWT) {
 -      @Override
 -      public void run(ContinuationContext contextInner) {
 -        final List<FilePatch> remainingPatches = new ArrayList<FilePatch>();
 -
 -        final CommitContext commitContext = new CommitContext();
 -        final List<TextFilePatch> textFilePatches;
 -        try {
 -          textFilePatches = loadTextPatches(myProject, changeList, changes, remainingPatches, commitContext);
 -        }
 -        catch (IOException e) {
 -          LOG.info(e);
 -          PatchApplier.showError(myProject, "Cannot load patch(es): " + e.getMessage(), true);
 -          return;
 -        }
 -        catch (PatchSyntaxException e) {
 -          PatchApplier.showError(myProject, "Cannot load patch(es): " + e.getMessage(), true);
 -          LOG.info(e);
 -          return;
 -        }
 +  @CalledInAny
 +  public void unshelveChangeList(final ShelvedChangeList changeList,
 +                                 @Nullable final List<ShelvedChange> changes,
 +                                 @Nullable final List<ShelvedBinaryFile> binaryFiles,
 +                                 @Nullable final LocalChangeList targetChangeList,
 +                                 final boolean showSuccessNotification,
 +                                 final boolean systemOperation,
 +                                 final boolean reverse,
 +                                 final String leftConflictTitle,
 +                                 final String rightConflictTitle) {
 +    final List<FilePatch> remainingPatches = new ArrayList<FilePatch>();
 +
 +    final CommitContext commitContext = new CommitContext();
 +    final List<TextFilePatch> textFilePatches;
 +    try {
 +      textFilePatches = loadTextPatches(myProject, changeList, changes, remainingPatches, commitContext);
 +    }
 +    catch (IOException e) {
 +      LOG.info(e);
 +      PatchApplier.showError(myProject, "Cannot load patch(es): " + e.getMessage(), true);
 +      return;
 +    }
 +    catch (PatchSyntaxException e) {
 +      PatchApplier.showError(myProject, "Cannot load patch(es): " + e.getMessage(), true);
 +      LOG.info(e);
 +      return;
 +    }
  
 -        final List<FilePatch> patches = new ArrayList<FilePatch>(textFilePatches);
 +    final List<FilePatch> patches = new ArrayList<FilePatch>(textFilePatches);
  
 -        final List<ShelvedBinaryFile> remainingBinaries = new ArrayList<ShelvedBinaryFile>();
 -        final List<ShelvedBinaryFile> binaryFilesToUnshelve = getBinaryFilesToUnshelve(changeList, binaryFiles, remainingBinaries);
 +    final List<ShelvedBinaryFile> remainingBinaries = new ArrayList<ShelvedBinaryFile>();
 +    final List<ShelvedBinaryFile> binaryFilesToUnshelve = getBinaryFilesToUnshelve(changeList, binaryFiles, remainingBinaries);
  
 -        for (final ShelvedBinaryFile shelvedBinaryFile : binaryFilesToUnshelve) {
 -          patches.add(new ShelvedBinaryFilePatch(shelvedBinaryFile));
 -        }
 +    for (final ShelvedBinaryFile shelvedBinaryFile : binaryFilesToUnshelve) {
 +      patches.add(new ShelvedBinaryFilePatch(shelvedBinaryFile));
 +    }
  
 +    ApplicationManager.getApplication().invokeAndWait(new Runnable() {
 +      @Override
 +      public void run() {
          final BinaryPatchApplier binaryPatchApplier = new BinaryPatchApplier();
          final PatchApplier<ShelvedBinaryFilePatch> patchApplier =
            new PatchApplier<ShelvedBinaryFilePatch>(myProject, myProject.getBaseDir(),
                                                     rightConflictTitle);
          patchApplier.setIsSystemOperation(systemOperation);
  
 -        // after patch applier part
 -        contextInner.next(new TaskDescriptor("", Where.AWT) {
 -          @Override
 -          public void run(ContinuationContext context) {
 -            remainingPatches.addAll(patchApplier.getRemainingPatches());
 -
 -            if (remainingPatches.isEmpty() && remainingBinaries.isEmpty()) {
 -              recycleChangeList(changeList);
 -            }
 -            else {
 -              saveRemainingPatches(changeList, remainingPatches, remainingBinaries, commitContext);
 -            }
 -          }
 -        });
 +        remainingPatches.addAll(patchApplier.getRemainingPatches());
 +
 +        if (remainingPatches.isEmpty() && remainingBinaries.isEmpty()) {
 +          recycleChangeList(changeList);
 +        }
 +        else {
 +          saveRemainingPatches(changeList, remainingPatches, remainingBinaries, commitContext);
 +        }
  
 -        patchApplier.scheduleSelf(showSuccessNotification, contextInner, systemOperation);
 +        patchApplier.execute(showSuccessNotification, systemOperation);
        }
 -    });
 +    }, ModalityState.defaultModalityState());
    }
  
    private static List<TextFilePatch> loadTextPatches(final Project project,
  
    void saveRemainingPatches(final ShelvedChangeList changeList, final List<FilePatch> remainingPatches,
                              final List<ShelvedBinaryFile> remainingBinaries, CommitContext commitContext) {
-     final File newPath = getPatchPath(changeList.DESCRIPTION);
+     final File newPatchDir = generateUniqueSchemePatchDir(changeList.DESCRIPTION, true);
+     final File newPath = getPatchFileInConfigDir(newPatchDir);
      try {
        FileUtil.copy(new File(changeList.PATH), newPath);
      }
      }
      final ShelvedChangeList listCopy = new ShelvedChangeList(newPath.getAbsolutePath(), changeList.DESCRIPTION,
                                                               new ArrayList<ShelvedBinaryFile>(changeList.getBinaryFiles()));
+     listCopy.setName(newPatchDir.getName());
      listCopy.DATE = changeList.DATE == null ? null : new Date(changeList.DATE.getTime());
  
      writePatchesToFile(myProject, changeList.PATH, remainingPatches, commitContext);
      changeList.getBinaryFiles().retainAll(remainingBinaries);
      changeList.clearLoadedChanges();
      recycleChangeList(listCopy, changeList);
+     // all newly create ShelvedChangeList have to be added to SchemesManger as new scheme
+     mySchemeManager.addNewScheme(listCopy, false);
      notifyStateChanged();
    }
  
-   public void restoreList(final ShelvedChangeList changeList) {
-     myShelvedChangeLists.add(changeList);
-     myRecycledShelvedChangeLists.remove(changeList);
-     changeList.setRecycled(false);
+   public void restoreList(@NotNull final ShelvedChangeList changeList) {
+     ShelvedChangeList list = mySchemeManager.findSchemeByName(changeList.getName());
+     if (list != null) {
+       list.setRecycled(false);
+     }
      notifyStateChanged();
    }
  
    public List<ShelvedChangeList> getRecycledShelvedChangeLists() {
-     return myRecycledShelvedChangeLists;
+     return getRecycled(true);
    }
  
    public void clearRecycled() {
-     for (ShelvedChangeList list : myRecycledShelvedChangeLists) {
+     for (ShelvedChangeList list : getRecycledShelvedChangeLists()) {
        deleteListImpl(list);
+       mySchemeManager.removeScheme(list);
      }
-     myRecycledShelvedChangeLists.clear();
      notifyStateChanged();
    }
  
-   private void recycleChangeList(final ShelvedChangeList listCopy, final ShelvedChangeList newList) {
+   private void recycleChangeList(@NotNull final ShelvedChangeList listCopy, @Nullable final ShelvedChangeList newList) {
      if (newList != null) {
        for (Iterator<ShelvedBinaryFile> shelvedChangeListIterator = listCopy.getBinaryFiles().iterator();
             shelvedChangeListIterator.hasNext(); ) {
  
      if (!listCopy.getBinaryFiles().isEmpty() || !listCopy.getChanges(myProject).isEmpty()) {
        listCopy.setRecycled(true);
-       myRecycledShelvedChangeLists.add(listCopy);
        notifyStateChanged();
      }
    }
  
-   private void recycleChangeList(final ShelvedChangeList changeList) {
+   private void recycleChangeList(@NotNull final ShelvedChangeList changeList) {
      recycleChangeList(changeList, null);
-     myShelvedChangeLists.remove(changeList);
      notifyStateChanged();
    }
  
-   public void deleteChangeList(final ShelvedChangeList changeList) {
+   public void deleteChangeList(@NotNull final ShelvedChangeList changeList) {
      deleteListImpl(changeList);
-     if (!changeList.isRecycled()) {
-       myShelvedChangeLists.remove(changeList);
-     }
-     else {
-       myRecycledShelvedChangeLists.remove(changeList);
-     }
+     mySchemeManager.removeScheme(changeList);
      notifyStateChanged();
    }
  
-   private void deleteListImpl(final ShelvedChangeList changeList) {
-     File file = new File(changeList.PATH);
-     myFileProcessor.delete(file.getName());
-     for (ShelvedBinaryFile binaryFile : changeList.getBinaryFiles()) {
-       final String path = binaryFile.SHELVED_PATH;
-       if (path != null) {
-         File binFile = new File(path);
-         myFileProcessor.delete(binFile.getName());
-       }
-     }
+   private void deleteListImpl(@NotNull final ShelvedChangeList changeList) {
+     FileUtil.delete(new File(myFileProcessor.getBaseDir(), changeList.getName()));
    }
  
    public void renameChangeList(final ShelvedChangeList changeList, final String newName) {