Merge branch 'master' of git.labs.intellij.net:idea/community
authorirengrig <Irina.Chernushina@jetbrains.com>
Fri, 2 Jul 2010 12:45:14 +0000 (16:45 +0400)
committerirengrig <Irina.Chernushina@jetbrains.com>
Fri, 2 Jul 2010 12:45:14 +0000 (16:45 +0400)
16 files changed:
platform/vcs-api/src/com/intellij/openapi/vcs/AbstractVcs.java
platform/vcs-api/src/com/intellij/openapi/vcs/VcsShowConfirmationOption.java
platform/vcs-api/src/com/intellij/util/ui/ConfirmationDialog.java
platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/formove/PatchApplier.java
platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/formove/PathMerger.java
platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/formove/PathsVerifier.java
platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/formove/TriggerAdditionOrDeletion.java [new file with mode: 0644]
platform/vcs-impl/src/com/intellij/openapi/vcs/VcsShowConfirmationOptionImpl.java
platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ChangeListManagerImpl.java
platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/AbstractSelectFilesDialog.java
platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/CommitHelper.java
platform/vcs-impl/src/com/intellij/openapi/vcs/impl/AbstractVcsHelperImpl.java
platform/vcs-impl/src/com/intellij/openapi/vcs/impl/ProjectLevelVcsManagerImpl.java
plugins/cvs/cvs-plugin/src/com/intellij/cvsSupport2/CvsVcs2.java
plugins/svn4idea/src/org/jetbrains/idea/svn/ForNestedRootChecker.java
plugins/svn4idea/src/org/jetbrains/idea/svn/SvnVcs.java

index 8a66c04795b58280fdd08c28b0f74f71201ef077..10d66bd2789dca91fa3f57b7b49f680205a16335 100644 (file)
@@ -446,6 +446,10 @@ public abstract class AbstractVcs<ComList extends CommittedChangeList> extends S
     return RemoteDifferenceStrategy.ASK_LATEST_REVISION;
   }
 
+  public boolean areDirectoriesVersionedItems() {
+    return false;
+  }
+
   @Nullable
   protected TreeDiffProvider getTreeDiffProviderImpl() {
     return null;
index d077e7621ee482488f83e84894b6a5a13e131d89..0eb8ca02c32d557ff7166dd9138d13021d2d8210 100644 (file)
@@ -43,6 +43,21 @@ public interface VcsShowConfirmationOption {
     }
   }
 
+  public static final VcsShowConfirmationOption STATIC_SHOW_CONFIRMATION = new VcsShowConfirmationOption() {
+    @Override
+    public Value getValue() {
+      return Value.SHOW_CONFIRMATION;
+    }
+    @Override
+    public void setValue(Value value) {
+    }
+    @Override
+    public boolean isPersistent() {
+      return false;
+    }
+  };
+
   Value getValue();
   void setValue(Value value);
+  boolean isPersistent();
 }
index fce6d54ba9d880b29b0c33eebf7616ad69618cf3..d82ef7ee79a64c7c19244ede77a15b0fab1a8921 100644 (file)
@@ -15,9 +15,9 @@
  */
 package com.intellij.util.ui;
 
+import com.intellij.CommonBundle;
 import com.intellij.openapi.project.Project;
 import com.intellij.openapi.vcs.VcsShowConfirmationOption;
-import com.intellij.CommonBundle;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 
@@ -34,6 +34,9 @@ public class ConfirmationDialog extends OptionsMessageDialog{
                                                @Nullable Icon icon) {
     if (option.getValue() == VcsShowConfirmationOption.Value.DO_NOTHING_SILENTLY) return false;
     final ConfirmationDialog dialog = new ConfirmationDialog(project, message, title, icon, option);
+    if (! option.isPersistent()) {
+      dialog.setDoNotAskOption(null);
+    }
     dialog.show();
     return dialog.isOK();
   }
index 0be7938a10e5746ea0144ba1f0b525abe4d73efd..1ae3557eb2f33e7e3aa21bbed3909bc0a440f0df 100644 (file)
@@ -33,6 +33,7 @@ import com.intellij.openapi.util.Computable;
 import com.intellij.openapi.util.Pair;
 import com.intellij.openapi.util.Ref;
 import com.intellij.openapi.vcs.FilePath;
+import com.intellij.openapi.vcs.FilePathImpl;
 import com.intellij.openapi.vcs.VcsBundle;
 import com.intellij.openapi.vcs.changes.*;
 import com.intellij.openapi.vcs.changes.patch.ApplyPatchAction;
@@ -77,6 +78,11 @@ public class PatchApplier<BinaryType extends FilePatch> {
       public VirtualFile getFile(FilePatch patch, String path) {
         return PathMerger.getFile(myBaseDirectory, path);
       }
+
+      @Override
+      public FilePath getPath(FilePatch patch, String path) {
+        return PathMerger.getFile(new FilePathImpl(myBaseDirectory), path);
+      }
     });
   }
 
@@ -87,7 +93,10 @@ public class PatchApplier<BinaryType extends FilePatch> {
   public ApplyPatchStatus execute(boolean showSuccessNotification) {
     myRemainingPatches.addAll(myPatches);
 
-    final ApplyPatchStatus status = ApplicationManager.getApplication().runWriteAction(new Computable<ApplyPatchStatus>() {
+    final ApplyPatchStatus patchStatus = nonWriteActionPreCheck();
+    if (ApplyPatchStatus.FAILURE.equals(patchStatus)) return patchStatus;
+
+    final ApplyPatchStatus applyStatus = ApplicationManager.getApplication().runWriteAction(new Computable<ApplyPatchStatus>() {
       public ApplyPatchStatus compute() {
         final Ref<ApplyPatchStatus> refStatus = new Ref<ApplyPatchStatus>(ApplyPatchStatus.FAILURE);
         CommandProcessor.getInstance().executeCommand(myProject, new Runnable() {
@@ -98,10 +107,16 @@ public class PatchApplier<BinaryType extends FilePatch> {
         return refStatus.get();
       }
     });
+    final ApplyPatchStatus status = ApplyPatchStatus.SUCCESS.equals(patchStatus) ? applyStatus :
+                                    ApplyPatchStatus.and(patchStatus, applyStatus);
+    // listeners finished, all 'legal' file additions/deletions with VCS are done
+    final TriggerAdditionOrDeletion trigger = new TriggerAdditionOrDeletion(myProject);
+    addSkippedItems(trigger);
+    trigger.process();
     if(showSuccessNotification || !ApplyPatchStatus.SUCCESS.equals(status)) {
       showApplyStatus(myProject, status);
     }
-    refreshFiles();
+    refreshFiles(trigger.getAffected());
     return status;
   }
 
@@ -109,7 +124,12 @@ public class PatchApplier<BinaryType extends FilePatch> {
     if (group.isEmpty()) return ApplyPatchStatus.SUCCESS; //?
     final Project project = group.iterator().next().myProject;
 
-    ApplyPatchStatus result = ApplicationManager.getApplication().runWriteAction(new Computable<ApplyPatchStatus>() {
+    ApplyPatchStatus result = ApplyPatchStatus.SUCCESS;
+    for (PatchApplier patchApplier : group) {
+      result = ApplyPatchStatus.and(result, patchApplier.nonWriteActionPreCheck());
+      if (ApplyPatchStatus.FAILURE.equals(result)) return result;
+    }
+    result = ApplyPatchStatus.and(result, ApplicationManager.getApplication().runWriteAction(new Computable<ApplyPatchStatus>() {
       public ApplyPatchStatus compute() {
         final Ref<ApplyPatchStatus> refStatus = new Ref<ApplyPatchStatus>(null);
         CommandProcessor.getInstance().executeCommand(project, new Runnable() {
@@ -121,16 +141,36 @@ public class PatchApplier<BinaryType extends FilePatch> {
         }, VcsBundle.message("patch.apply.command"), null);
         return refStatus.get();
       }
-    });
+    }));
     result = result == null ? ApplyPatchStatus.FAILURE : result;
+    final TriggerAdditionOrDeletion trigger = new TriggerAdditionOrDeletion(project);
+    for (PatchApplier applier : group) {
+      applier.addSkippedItems(trigger);
+    }
+    trigger.process();
 
     for (PatchApplier applier : group) {
-      applier.refreshFiles();
+      applier.refreshFiles(trigger.getAffected());
     }
     showApplyStatus(project, result);
     return result;
   }
 
+  protected void addSkippedItems(final TriggerAdditionOrDeletion trigger) {
+    trigger.addExisting(myVerifier.getToBeAdded());
+    trigger.addDeleted(myVerifier.getToBeDeleted());
+  }
+
+  public ApplyPatchStatus nonWriteActionPreCheck() {
+    final boolean value = myVerifier.nonWriteActionPreCheck();
+    if (! value) return ApplyPatchStatus.FAILURE;
+
+    final List<FilePatch> skipped = myVerifier.getSkipped();
+    final boolean applyAll = skipped.isEmpty();
+    myPatches.removeAll(skipped);
+    return applyAll ? ApplyPatchStatus.SUCCESS : ((skipped.size() == myPatches.size()) ? ApplyPatchStatus.ALREADY_APPLIED : ApplyPatchStatus.PARTIAL) ;
+  }
+
   protected ApplyPatchStatus executeWritable() {
     return ApplicationManager.getApplication().runWriteAction(new Computable<ApplyPatchStatus>() {
       public ApplyPatchStatus compute() {
@@ -175,9 +215,10 @@ public class PatchApplier<BinaryType extends FilePatch> {
     }
   }
 
-  protected void refreshFiles() {
+  protected void refreshFiles(final Collection<FilePath> additionalDirectly) {
     final List<FilePath> directlyAffected = myVerifier.getDirectlyAffected();
     final List<VirtualFile> indirectlyAffected = myVerifier.getAllAffected();
+    directlyAffected.addAll(additionalDirectly);
 
     final RefreshSession session = RefreshQueue.getInstance().createSession(false, true, new Runnable() {
       public void run() {
@@ -274,6 +315,10 @@ public class PatchApplier<BinaryType extends FilePatch> {
     return myRemainingPatches;
   }
 
+  public boolean hasRemainingPatches() {
+    return ! myRemainingPatches.isEmpty();
+  }
+
   private boolean makeWritable(final List<VirtualFile> filesToMakeWritable) {
     final VirtualFile[] fileArray = VfsUtil.toVirtualFileArray(filesToMakeWritable);
     final ReadonlyStatusHandler.OperationStatus readonlyStatus = ReadonlyStatusHandler.getInstance(myProject).ensureFilesWritable(fileArray);
index 13979a71803d167e0461e3bd3569c892bf87e744..04dc13aa6306630e47eda815b4abb15097132d24 100644 (file)
@@ -16,6 +16,8 @@
 package com.intellij.openapi.diff.impl.patch.formove;
 
 import com.intellij.openapi.util.SystemInfo;
+import com.intellij.openapi.vcs.FilePath;
+import com.intellij.openapi.vcs.FilePathImpl;
 import com.intellij.openapi.vcs.changes.patch.RelativePathCalculator;
 import com.intellij.openapi.vfs.VirtualFile;
 import org.jetbrains.annotations.Nullable;
@@ -49,6 +51,16 @@ public class PathMerger {
     return getFile(new IoFilePathMerger(base), path, tail);
   }
   
+  @Nullable
+  public static FilePath getFile(final FilePath base, final String path) {
+    return getFile(new FilePathPathMerger(base), path);
+  }
+
+  @Nullable
+  public static FilePath getFile(final FilePath base, final String path, final List<String> tail) {
+    return getFile(new FilePathPathMerger(base), path, tail);
+  }
+
   @Nullable
   public static <T> T getFile(final FilePathMerger<T> merger, final String path) {
     if (path == null) {
@@ -194,6 +206,39 @@ public class PathMerger {
     }
   }
 
+  public static class FilePathPathMerger implements FilePathMerger<FilePath> {
+    private final IoFilePathMerger myIoDelegate;
+    private boolean myIsDirectory;
+
+    public FilePathPathMerger(final FilePath base) {
+      myIoDelegate = new IoFilePathMerger(base.getIOFile());
+    }
+
+    @Override
+    public boolean down(String name) {
+      return myIoDelegate.down(name);
+    }
+
+    @Override
+    public boolean up() {
+      return myIoDelegate.up();
+    }
+
+    @Override
+    public FilePath getResult() {
+      return new FilePathImpl(myIoDelegate.getResult(), myIsDirectory);
+    }
+
+    @Override
+    public String getCurrentName() {
+      return myIoDelegate.getCurrentName();
+    }
+
+    public void setIsDirectory(boolean isDirectory) {
+      myIsDirectory = isDirectory;
+    }
+  }
+
   public interface FilePathMerger<T> {
     boolean up();
 
index f5870cd2d08d0b5de239a12057f2c5b7dd99f440..f6f5b20f91bf7417d18054eed7c692af748ee004 100644 (file)
@@ -24,10 +24,7 @@ import com.intellij.openapi.diff.impl.patch.apply.ApplyTextFilePatch;
 import com.intellij.openapi.project.Project;
 import com.intellij.openapi.util.Comparing;
 import com.intellij.openapi.util.Pair;
-import com.intellij.openapi.vcs.FilePath;
-import com.intellij.openapi.vcs.FilePathImpl;
-import com.intellij.openapi.vcs.ProjectLevelVcsManager;
-import com.intellij.openapi.vcs.VcsBundle;
+import com.intellij.openapi.vcs.*;
 import com.intellij.openapi.vcs.changes.patch.RelativePathCalculator;
 import com.intellij.openapi.vcs.changes.shelf.ShelveChangesManager;
 import com.intellij.openapi.vcs.impl.ProjectLevelVcsManagerImpl;
@@ -54,6 +51,8 @@ public class PathsVerifier<BinaryType extends FilePatch> {
   private final List<VirtualFile> myWritableFiles;
   private final BaseMapper myBaseMapper;
   private ProjectLevelVcsManager myVcsManager;
+  private final List<FilePatch> mySkipped;
+  private DelayedPrecheckContext myDelayedPrecheckContext;
 
   public PathsVerifier(final Project project, final VirtualFile baseDirectory, final List<FilePatch> patches, BaseMapper baseMapper) {
     myProject = project;
@@ -68,6 +67,7 @@ public class PathsVerifier<BinaryType extends FilePatch> {
     myBinaryPatches = new ArrayList<Pair<VirtualFile, ApplyFilePatchBase<BinaryType>>>();
     myWritableFiles = new ArrayList<VirtualFile>();
     myVcsManager = ProjectLevelVcsManager.getInstance(myProject);
+    mySkipped = new ArrayList<FilePatch>();
   }
 
   // those to be moved to CL: target + created dirs
@@ -108,15 +108,32 @@ public class PathsVerifier<BinaryType extends FilePatch> {
     }
   }
 
+  @CalledInAwt
+  public boolean nonWriteActionPreCheck() {
+    final List<CheckPath> checkers = new ArrayList<CheckPath>(myPatches.size());
+    myDelayedPrecheckContext = new DelayedPrecheckContext(myProject);
+    for (FilePatch patch : myPatches) {
+      final CheckPath checker = getChecker(patch);
+      if (! checker.canBeApplied(myDelayedPrecheckContext)) {
+        revert(checker.getErrorMessage());
+        return false;
+      }
+    }
+    final Collection<FilePatch> skipped = myDelayedPrecheckContext.doDelayed();
+    mySkipped.addAll(skipped);
+    myPatches.remove(skipped);
+    return true;
+  }
+
+  public List<FilePatch> getSkipped() {
+    return mySkipped;
+  }
+
   public boolean execute() {
     try {
       final List<CheckPath> checkers = new ArrayList<CheckPath>(myPatches.size());
       for (FilePatch patch : myPatches) {
         final CheckPath checker = getChecker(patch);
-        if (! checker.canBeApplied()) {
-          revert(checker.getErrorMessage());
-          return false;
-        }
         checkers.add(checker);
       }
       for (CheckPath checker : checkers) {
@@ -150,6 +167,14 @@ public class PathsVerifier<BinaryType extends FilePatch> {
     }
   }
 
+  public Collection<FilePath> getToBeAdded() {
+    return myDelayedPrecheckContext.getOverridenPaths();
+  }
+
+  public Collection<FilePath> getToBeDeleted() {
+    return myDelayedPrecheckContext.getAlreadyDeletedPaths();
+  }
+
   private class CheckModified extends CheckDeleted {
     private CheckModified(final FilePatch path) {
       super(path);
@@ -161,17 +186,15 @@ public class PathsVerifier<BinaryType extends FilePatch> {
       super(path);
     }
 
-    protected boolean precheck(final VirtualFile beforeFile, final VirtualFile afterFile) {
+    protected boolean precheck(final VirtualFile beforeFile, final VirtualFile afterFile, DelayedPrecheckContext context) {
       if (beforeFile == null) {
-        setErrorMessage(fileNotFoundMessage(myBeforeName));
-        return false;
+        context.addSkip(myBaseMapper.getPath(myPatch, myBeforeName), myPatch);
       }
       return true;
     }
 
     protected boolean check() throws IOException {
       final VirtualFile beforeFile = myBaseMapper.getFile(myPatch, myBeforeName);
-      // todo maybe deletion may be ok, just warning
       if (! checkExistsAndValid(beforeFile, myBeforeName)) {
         return false;
       }
@@ -186,10 +209,9 @@ public class PathsVerifier<BinaryType extends FilePatch> {
       super(path);
     }
 
-    protected boolean precheck(final VirtualFile beforeFile, final VirtualFile afterFile) {
+    protected boolean precheck(final VirtualFile beforeFile, final VirtualFile afterFile, DelayedPrecheckContext context) {
       if (afterFile != null) {
-        setErrorMessage(fileAlreadyExists(afterFile.getPath()));
-        return false;
+        context.addOverrideExisting(myPatch, new FilePathImpl(afterFile));
       }
       return true;
     }
@@ -216,7 +238,7 @@ public class PathsVerifier<BinaryType extends FilePatch> {
     }
 
     // before exists; after does not exist
-    protected boolean precheck(final VirtualFile beforeFile, final VirtualFile afterFile) {
+    protected boolean precheck(final VirtualFile beforeFile, final VirtualFile afterFile, final DelayedPrecheckContext context) {
       if (beforeFile == null) {
         setErrorMessage(fileNotFoundMessage(myBeforeName));
       } else if (afterFile != null) {
@@ -262,13 +284,15 @@ public class PathsVerifier<BinaryType extends FilePatch> {
       myErrorMessage = errorMessage;
     }
 
-    public boolean canBeApplied() {
+    public boolean canBeApplied(DelayedPrecheckContext context) {
       final VirtualFile beforeFile = myBaseMapper.getFile(myPatch, myBeforeName);
       final VirtualFile afterFile = myBaseMapper.getFile(myPatch, myAfterName);
-      return precheck(beforeFile, afterFile);
+      return precheck(beforeFile, afterFile, context);
     }
 
-    protected abstract boolean precheck(final VirtualFile beforeFile, final VirtualFile afterFile);
+    protected abstract boolean precheck(final VirtualFile beforeFile,
+                                        final VirtualFile afterFile,
+                                        DelayedPrecheckContext context);
     protected abstract boolean check() throws IOException;
 
     protected boolean checkExistsAndValid(final VirtualFile file, final String name) {
@@ -486,5 +510,60 @@ public class PathsVerifier<BinaryType extends FilePatch> {
   public interface BaseMapper {
     @Nullable
     VirtualFile getFile(final FilePatch patch, final String path);
+    FilePath getPath(final FilePatch patch, final String path);
+  }
+
+  private static class DelayedPrecheckContext {
+    private final Map<FilePath, FilePatch> mySkipDeleted;
+    private final Map<FilePath, FilePatch> myOverrideExisting;
+    private final List<FilePath> myOverridenPaths;
+    private final Project myProject;
+
+    private DelayedPrecheckContext(final Project project) {
+      myProject = project;
+      myOverrideExisting = new HashMap<FilePath, FilePatch>();
+      mySkipDeleted = new HashMap<FilePath, FilePatch>();
+      myOverridenPaths = new LinkedList<FilePath>();
+    }
+
+    public void addSkip(final FilePath path, final FilePatch filePatch) {
+      mySkipDeleted.put(path, filePatch);
+    }
+
+    public void addOverrideExisting(final FilePatch patch, final FilePath filePath) {
+      if (! myOverrideExisting.containsKey(filePath)) {
+        myOverrideExisting.put(filePath, patch);
+      }
+    }
+
+    // returns those to be skipped
+    public Collection<FilePatch> doDelayed() {
+      final List<FilePatch> result = new LinkedList<FilePatch>();
+      if (! myOverrideExisting.isEmpty()) {
+        final String prompt = myOverrideExisting.size() == 1 ? "The following file already exists. Do you want to override it?" :
+          "The following files already exist. Do you want to override them?";
+        final String title = "Override existing files";
+        final Collection<FilePath> selected = AbstractVcsHelper.getInstance(myProject).selectFilePathsToProcess(
+          new ArrayList<FilePath>(myOverrideExisting.keySet()), title,
+          "\nThe following files should be created by patch, but they already exist.\nDo you want to override them?\n", title,
+          "The following file should be created by patch, but it already exists.\nDo you want to override it?\n{0}",
+          VcsShowConfirmationOption.STATIC_SHOW_CONFIRMATION);
+        for (FilePath path : selected) {
+          myOverrideExisting.remove(path);
+        }
+        result.addAll(myOverrideExisting.values());
+        myOverridenPaths.addAll(selected);
+      }
+      result.addAll(mySkipDeleted.values());
+      return result;
+    }
+
+    public List<FilePath> getOverridenPaths() {
+      return myOverridenPaths;
+    }
+
+    public Collection<FilePath> getAlreadyDeletedPaths() {
+      return mySkipDeleted.keySet();
+    }
   }
 }
diff --git a/platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/formove/TriggerAdditionOrDeletion.java b/platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/formove/TriggerAdditionOrDeletion.java
new file mode 100644 (file)
index 0000000..b477f42
--- /dev/null
@@ -0,0 +1,186 @@
+/*
+ * Copyright 2000-2010 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.intellij.openapi.diff.impl.patch.formove;
+
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.vcs.*;
+import com.intellij.openapi.vcs.changes.ChangeListManager;
+import com.intellij.openapi.vcs.changes.SortByVcsRoots;
+import com.intellij.openapi.vcs.checkin.CheckinEnvironment;
+import com.intellij.openapi.vfs.VfsUtil;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.util.FilePathByPathComparator;
+import com.intellij.util.containers.Convertor;
+import com.intellij.util.containers.MultiMap;
+
+import java.util.*;
+
+public class TriggerAdditionOrDeletion {
+  private final Collection<FilePath> myExisting;
+  private final Collection<FilePath> myDeleted;
+  private final Set<FilePath> myAffected;
+  private final Project myProject;
+  private ProjectLevelVcsManager myVcsManager;
+  private AbstractVcsHelper myVcsHelper;
+
+  public TriggerAdditionOrDeletion(final Project project) {
+    myProject = project;
+    myExisting = new HashSet<FilePath>();
+    myDeleted = new HashSet<FilePath>();
+    myVcsManager = ProjectLevelVcsManager.getInstance(myProject);
+    myVcsHelper = AbstractVcsHelper.getInstance(myProject);
+    myAffected = new HashSet<FilePath>();
+  }
+
+  public void addExisting(final Collection<FilePath> files) {
+    myExisting.addAll(files);
+  }
+
+  public void addDeleted(final Collection<FilePath> files) {
+    myDeleted.addAll(files);
+  }
+
+  public void process() {
+    if (myExisting.isEmpty() && myDeleted.isEmpty()) return;
+    
+    final SortByVcsRoots<FilePath> sortByVcsRoots = new SortByVcsRoots<FilePath>(myProject, new Convertor.IntoSelf());
+    final ChangeListManager clManager = ChangeListManager.getInstance(myProject);
+
+    if (! myExisting.isEmpty()) {
+      processAddition(sortByVcsRoots);
+    }
+    if (! myDeleted.isEmpty()) {
+      processDeletion(sortByVcsRoots);
+    }
+  }
+
+  public Set<FilePath> getAffected() {
+    return myAffected;
+  }
+
+  private void processDeletion(SortByVcsRoots<FilePath> sortByVcsRoots) {
+    final MultiMap<VcsRoot, FilePath> map = sortByVcsRoots.sort(myDeleted);
+    for (VcsRoot vcsRoot : map.keySet()) {
+      if (vcsRoot != null && vcsRoot.vcs != null) {
+        final CheckinEnvironment localChangesProvider = vcsRoot.vcs.getCheckinEnvironment();
+        if (localChangesProvider == null) continue;
+        final boolean takeDirs = vcsRoot.vcs.areDirectoriesVersionedItems();
+
+        final Collection<FilePath> files = map.get(vcsRoot);
+        final List<FilePath> toBeDeleted = new LinkedList<FilePath>();
+        for (FilePath file : files) {
+          final FilePath parent = file.getParentPath();
+          if ((takeDirs || (! file.isDirectory())) && parent != null && parent.getIOFile().exists()) {
+            toBeDeleted.add(file);
+          }
+        }
+        if (toBeDeleted.isEmpty()) return;
+        askUserIfNeededDeletion(vcsRoot.vcs, toBeDeleted);
+        myAffected.addAll(toBeDeleted);
+        localChangesProvider.scheduleMissingFileForDeletion(toBeDeleted);
+      }
+    }
+  }
+
+  private void processAddition(SortByVcsRoots<FilePath> sortByVcsRoots) {
+    final MultiMap<VcsRoot, FilePath> map = sortByVcsRoots.sort(myExisting);
+    for (VcsRoot vcsRoot : map.keySet()) {
+      if (vcsRoot != null && vcsRoot.vcs != null) {
+        final CheckinEnvironment localChangesProvider = vcsRoot.vcs.getCheckinEnvironment();
+        if (localChangesProvider == null) continue;
+        final boolean takeDirs = vcsRoot.vcs.areDirectoriesVersionedItems();
+
+        final Collection<FilePath> files = map.get(vcsRoot);
+        final List<FilePath> toBeAdded;
+        if (takeDirs) {
+          final RecursiveCheckAdder adder = new RecursiveCheckAdder(vcsRoot.path);
+          for (FilePath file : files) {
+            adder.process(file);
+          }
+          toBeAdded = adder.getToBeAdded();
+        } else {
+          toBeAdded = new LinkedList<FilePath>();
+          for (FilePath file : files) {
+            if (! file.isDirectory()) {
+              toBeAdded.add(file);
+            }
+          }
+        }
+        if (toBeAdded.isEmpty()) return;
+        Collections.sort(toBeAdded, FilePathByPathComparator.getInstance());
+
+        askUserIfNeededAddition(vcsRoot.vcs, toBeAdded);
+        myAffected.addAll(toBeAdded);
+        localChangesProvider.scheduleUnversionedFilesForAddition(ObjectsConvertor.fp2vf(toBeAdded));
+      }
+    }
+  }
+
+  private void askUserIfNeededAddition(final AbstractVcs vcs, final List<FilePath> toBeAdded) {
+    final VcsShowConfirmationOption confirmationOption = myVcsManager.getStandardConfirmation(VcsConfiguration.StandardConfirmation.ADD, vcs);
+    if (VcsShowConfirmationOption.Value.DO_NOTHING_SILENTLY.equals(confirmationOption.getValue())) {
+      toBeAdded.clear();
+    } else if (VcsShowConfirmationOption.Value.SHOW_CONFIRMATION.equals(confirmationOption.getValue())) {
+      final Collection<FilePath> files = myVcsHelper.selectFilePathsToProcess(toBeAdded, "Select files to add to " + vcs.getDisplayName(), null,
+        "Schedule for addition", "Do you want to schedule the following file for addition to " + vcs.getDisplayName() + "\n{0}", confirmationOption);
+      toBeAdded.retainAll(files);
+    }
+  }
+
+  private void askUserIfNeededDeletion(final AbstractVcs vcs, final List<FilePath> toBeDeleted) {
+    final VcsShowConfirmationOption confirmationOption = myVcsManager.getStandardConfirmation(VcsConfiguration.StandardConfirmation.REMOVE, vcs);
+    if (VcsShowConfirmationOption.Value.DO_NOTHING_SILENTLY.equals(confirmationOption.getValue())) {
+      toBeDeleted.clear();
+    } else if (VcsShowConfirmationOption.Value.SHOW_CONFIRMATION.equals(confirmationOption.getValue())) {
+      final Collection<FilePath> files = myVcsHelper.selectFilePathsToProcess(toBeDeleted, "Select files to remove from " + vcs.getDisplayName(), null,
+        "Schedule for deletion", "Do you want to schedule the following file for deletion from " + vcs.getDisplayName() + "\n{0}", confirmationOption);
+      toBeDeleted.retainAll(files);
+    }
+  }
+
+  private class RecursiveCheckAdder {
+    private final Set<FilePath> myToBeAdded;
+    private ChangeListManager myChangeListManager;
+    private final VirtualFile myRoot;
+
+    private RecursiveCheckAdder(final VirtualFile root) {
+      myRoot = root;
+      myToBeAdded = new HashSet<FilePath>();
+      myChangeListManager = ChangeListManager.getInstance(myProject);
+    }
+
+    public void process(final FilePath path) {
+      FilePath current = path;
+      while (current != null) {
+        final VirtualFile vf = current.getVirtualFile();
+        if (vf == null) return;
+        if (! VfsUtil.isAncestor(myRoot, vf, true)) return;
+
+        final FileStatus status = myChangeListManager.getStatus(vf);
+        if (FileStatus.UNKNOWN.equals(status)) {
+          myToBeAdded.add(current);
+          current = current.getParentPath();
+        } else {
+          return;
+        }
+      }
+    }
+
+    public List<FilePath> getToBeAdded() {
+      return new ArrayList<FilePath>(myToBeAdded);
+    }
+  }
+}
index 62d3f658a4eed20e7a435aaa89bb70743f55e306..092122b68287975d28acfeb7b20d24360dd1420e 100644 (file)
@@ -44,4 +44,9 @@ public class VcsShowConfirmationOptionImpl extends VcsAbstractSetting implements
   public void setValue(Value value) {
     myValue = value;
   }
+
+  @Override
+  public boolean isPersistent() {
+    return true;
+  }
 }
index 5b56c0fc7a03657b309cbab173962c6a478bb795..645aed04a90cfc83fd0943f543ef29fd2a439c31 100644 (file)
@@ -146,6 +146,11 @@ public class ChangeListManagerImpl extends ChangeListManagerEx implements Projec
                     public void setValue(Value value) {
                       config.REMOVE_EMPTY_INACTIVE_CHANGELISTS = value;
                     }
+
+                    @Override
+                    public boolean isPersistent() {
+                      return true;
+                    }
                   }, "<html>The empty changelist '" + StringUtil.first(oldDefaultList.getName(), 30, true) + "' is no longer active.<br>" +
                      "Do you want to remove it?</html>", "&Remember my choice");
                   dialog.show();
index d5df34ac2a29ed9967e3b83fb0b86264b7d7ef29..a2981acb8136214584e18848dd97ac53a13d4dd8 100644 (file)
@@ -25,6 +25,7 @@ import com.intellij.openapi.vcs.VcsShowConfirmationOption;
 import org.jetbrains.annotations.Nullable;
 
 import javax.swing.*;
+import javax.swing.border.EmptyBorder;
 import java.awt.*;
 
 /**
@@ -46,6 +47,7 @@ public abstract class AbstractSelectFilesDialog<T> extends DialogWrapper {
     if (prompt != null) {
       final JLabel label = new JLabel(prompt);
       label.setUI(new MultiLineLabelUI());
+      label.setBorder(new EmptyBorder(5, 1, 5, 1));
       myPanel.add(label, BorderLayout.NORTH);
     }
 
index c9359fb459996fe969103a6371eeff6b76c47269..326441cc2bce8fca21cce4424ced3f114235f5e9 100644 (file)
@@ -489,6 +489,11 @@ public class CommitHelper {
         public void setValue(final Value value) {
           configuration.MOVE_TO_FAILED_COMMIT_CHANGELIST = value;
         }
+
+        @Override
+        public boolean isPersistent() {
+          return true;
+        }
       };
       boolean result = ConfirmationDialog.requestForConfirmation(option, project, VcsBundle.message("commit.failed.confirm.prompt"),
                                                                  VcsBundle.message("commit.failed.confirm.title"),
index c13e85eeaed83be16ad90e6cc9e184c4a7327e2b..9280b6d4f8bd2b434dcb297f04a88eec21122d3a 100644 (file)
@@ -245,6 +245,9 @@ public class AbstractVcsHelperImpl extends AbstractVcsHelper {
 
     SelectFilesDialog dlg = new SelectFilesDialog(myProject, files, prompt, confirmationOption);
     dlg.setTitle(title);
+    if (! confirmationOption.isPersistent()) {
+      dlg.setDoNotAskOption(null);
+    }
     dlg.show();
     if (dlg.isOK()) {
       final Collection<VirtualFile> selection = dlg.getSelectedFiles();
@@ -278,6 +281,9 @@ public class AbstractVcsHelperImpl extends AbstractVcsHelper {
 
     SelectFilePathsDialog dlg = new SelectFilePathsDialog(myProject, files, prompt, confirmationOption);
     dlg.setTitle(title);
+    if (! confirmationOption.isPersistent()) {
+      dlg.setDoNotAskOption(null);
+    }
     dlg.show();
     return dlg.isOK() ? dlg.getSelectedFiles() : null;
   }
index 0521789b86f06d0c25cadd14770aa267f16c424e..229f95016d4f383075e73b9c28597daebc2c20f7 100644 (file)
@@ -97,6 +97,7 @@ public class ProjectLevelVcsManagerImpl extends ProjectLevelVcsManagerEx impleme
 
   private final List<CheckinHandlerFactory> myRegisteredBeforeCheckinHandlers = new ArrayList<CheckinHandlerFactory>();
   private final EventDispatcher<VcsListener> myEventDispatcher = EventDispatcher.create(VcsListener.class);
+  
   private boolean myMappingsLoaded = false;
   private boolean myHaveLegacyVcsConfiguration = false;
   private boolean myCheckinHandlerFactoriesLoaded = false;
index d777dc60858466934f1327e5094f5e0a17ac3023..f23d6184686da02493c26d08b1c24e72b0f3cbff 100644 (file)
@@ -459,5 +459,10 @@ public class CvsVcs2 extends AbstractVcs implements TransactionProvider, EditFil
   public static VcsKey getKey() {
     return ourKey;
   }
+
+  @Override
+  public boolean areDirectoriesVersionedItems() {
+    return true;
+  }
 }
 
index 8c9e4aea84ff4ecce9b282494157dddb3f772fac..4559a3e603e2bcc2a1de87a3910617a7828d6d97 100644 (file)
  */
 package org.jetbrains.idea.svn;
 
+import com.intellij.openapi.progress.ProcessCanceledException;
 import com.intellij.openapi.util.Getter;
 import com.intellij.openapi.vcs.impl.VcsRootIterator;
 import com.intellij.openapi.vfs.VfsUtil;
 import com.intellij.openapi.vfs.VirtualFile;
-import com.intellij.openapi.progress.ProcessCanceledException;
 import org.jetbrains.annotations.Nullable;
 import org.tmatesoft.svn.core.internal.util.SVNEncodingUtil;
 import org.tmatesoft.svn.core.internal.util.SVNPathUtil;
@@ -178,6 +178,7 @@ public class ForNestedRootChecker {
     queue.add(constructor.createReplaceable(root));
     while (! queue.isEmpty()) {
       final Node node = queue.removeFirst();
+      if (Boolean.TRUE.equals(cancelledGetter.get())) throw new ProcessCanceledException();
 
       // check self
       final Real real = constructor.createReal(node.getFile(), root);
index 209325d8c928bb3e5d6ff63e41c24aa48609119f..1a8fdaeb9d5d60265fedb0867b13803878aa5b9b 100644 (file)
@@ -1048,4 +1048,9 @@ public class SvnVcs extends AbstractVcs<CommittedChangeList> {
   public SvnBranchPointsCalculator getSvnBranchPointsCalculator() {
     return mySvnBranchPointsCalculator;
   }
+
+  @Override
+  public boolean areDirectoriesVersionedItems() {
+    return true;
+  }
 }