IDEA-68731 Batch processing of 'hg add' with progress.
authorKirill Likhodedov <kirill.likhodedov@jetbrains.com>
Tue, 26 Apr 2011 12:05:58 +0000 (16:05 +0400)
committerKirill Likhodedov <kirill.likhodedov@jetbrains.com>
Tue, 26 Apr 2011 12:25:38 +0000 (16:25 +0400)
1. Make HgAddCommand capable to add several files at once. If there are many files, show background progressable process.
2. HgAddCommand: get rid of HgFile.
3. HgCopyCommand: get rid of HgFile; add instead of copy in the case of different repos.

plugins/hg4idea/src/org/zmlx/hg4idea/HgVFSListener.java
plugins/hg4idea/src/org/zmlx/hg4idea/command/HgAddCommand.java
plugins/hg4idea/src/org/zmlx/hg4idea/command/HgCopyCommand.java
plugins/hg4idea/src/org/zmlx/hg4idea/provider/commit/HgCheckinEnvironment.java
plugins/hg4idea/src/org/zmlx/hg4idea/util/HgUtil.java

index 10c8dbac9970f4a096f468ab2b150c1420de0fde..720691735337170828d912813878a5a07a62e1f1 100644 (file)
@@ -94,8 +94,8 @@ public class HgVFSListener extends VcsVFSListener {
                                false,
                                VcsConfiguration.getInstance(myProject).getAddRemoveOption() ) {
       @Override public void run(@NotNull ProgressIndicator aProgressIndicator) {
                                false,
                                VcsConfiguration.getInstance(myProject).getAddRemoveOption() ) {
       @Override public void run(@NotNull ProgressIndicator aProgressIndicator) {
-        final ArrayList<HgFile> adds = new ArrayList<HgFile>();
-        final HashMap<HgFile, HgFile> copies = new HashMap<HgFile, HgFile>(); // from -> to
+        final ArrayList<VirtualFile> adds = new ArrayList<VirtualFile>();
+        final HashMap<VirtualFile, VirtualFile> copies = new HashMap<VirtualFile, VirtualFile>(); // from -> to
 
         // separate adds from copies
         for (VirtualFile file : addedFiles) {
 
         // separate adds from copies
         for (VirtualFile file : addedFiles) {
@@ -105,9 +105,9 @@ public class HgVFSListener extends VcsVFSListener {
 
           final VirtualFile copyFrom = copyFromMap.get(file);
           if (copyFrom != null) {
 
           final VirtualFile copyFrom = copyFromMap.get(file);
           if (copyFrom != null) {
-            copies.put(new HgFile(myProject, copyFrom), new HgFile(myProject, file));
+            copies.put(copyFrom, file);
           } else {
           } else {
-            adds.add(new HgFile(myProject, file));
+            adds.add(file);
           }
         }
 
           }
         }
 
@@ -118,7 +118,7 @@ public class HgVFSListener extends VcsVFSListener {
 
         // copy needs to be run for each file separately
         if (!copies.isEmpty()) {
 
         // copy needs to be run for each file separately
         if (!copies.isEmpty()) {
-          for(Map.Entry<HgFile, HgFile> copy : copies.entrySet()) {
+          for(Map.Entry<VirtualFile, VirtualFile> copy : copies.entrySet()) {
             new HgCopyCommand(myProject).execute(copy.getKey(), copy.getValue());
           }
         }
             new HgCopyCommand(myProject).execute(copy.getKey(), copy.getValue());
           }
         }
index fb2445f56450db8f8032a4baffbb69c9dc88aa30..251fe65d4ae2b31926a3835603cc501aa4906225 100644 (file)
 // limitations under the License.
 package org.zmlx.hg4idea.command;
 
 // limitations under the License.
 package org.zmlx.hg4idea.command;
 
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.progress.ProgressIndicator;
+import com.intellij.openapi.progress.Task;
 import com.intellij.openapi.project.Project;
 import com.intellij.openapi.vfs.VirtualFile;
 import com.intellij.openapi.project.Project;
 import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.vcsUtil.VcsFileUtil;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.NotNull;
-import org.zmlx.hg4idea.HgFile;
-import org.zmlx.hg4idea.util.HgUtil;
+import org.jetbrains.annotations.Nullable;
 import org.zmlx.hg4idea.execution.HgCommandExecutor;
 import org.zmlx.hg4idea.execution.HgCommandExecutor;
+import org.zmlx.hg4idea.util.HgUtil;
 
 
-import java.util.Arrays;
 import java.util.Collection;
 import java.util.List;
 import java.util.Map;
 import java.util.Collection;
 import java.util.List;
 import java.util.Map;
@@ -39,17 +42,49 @@ public class HgAddCommand {
    * Adds given files to their Mercurial repositories.
    * @param hgFiles files to be added.
    */
    * Adds given files to their Mercurial repositories.
    * @param hgFiles files to be added.
    */
-  public void execute(@NotNull HgFile... hgFiles) {
-    execute(Arrays.asList(hgFiles));
+  public void execute(@NotNull Collection<VirtualFile> files) {
+    execute(files, null);
   }
 
   }
 
-  /**
-   * Adds given files to their Mercurial repositories.
-   * @param hgFiles files to be added.
-   */
-  public void execute(@NotNull Collection<HgFile> hgFiles) {
-    for(Map.Entry<VirtualFile, List<String>> entry : HgUtil.getRelativePathsByRepository(hgFiles).entrySet()) {
-      new HgCommandExecutor(myProject).executeInCurrentThread(entry.getKey(), "add", entry.getValue());
+  public void addWithProgress(final Collection<VirtualFile> files) {
+    if (files.size() >= HgUtil.MANY_FILES) {
+      new Task.Backgroundable(myProject, "Adding files to Mercurial", true) {
+        @Override public void run(@NotNull ProgressIndicator indicator) {
+          indicator.setIndeterminate(false);
+          execute(files, indicator);
+        }
+      }.queue();
+    } else {
+      ApplicationManager.getApplication().executeOnPooledThread(new Runnable() {
+        @Override public void run() {
+          execute(files);
+        }
+      });
+    }
+  }
+
+  private void execute(@NotNull Collection<VirtualFile> files, @Nullable ProgressIndicator indicator) {
+    final Map<VirtualFile, Collection<VirtualFile>> sorted = HgUtil.sortByHgRoots(myProject, files);
+    for (Map.Entry<VirtualFile, Collection<VirtualFile>> entry : sorted.entrySet()) {
+      if (indicator != null) {
+        if (indicator.isCanceled()) { return; }
+        indicator.setFraction(0);
+        indicator.setText2("Adding files to " + entry.getKey().getPresentableUrl());
+      }
+      addFiles(entry.getKey(), entry.getValue(), indicator);
+    }
+  }
+
+  private void addFiles(VirtualFile repo, Collection<VirtualFile> files, @Nullable ProgressIndicator indicator) {
+    final List<List<String>> chunks = VcsFileUtil.chunkFiles(repo, files);
+    int currentChunk = 0;
+    for (List<String> paths : chunks) {
+      if (indicator != null) {
+        if (indicator.isCanceled()) { return; }
+        indicator.setFraction((double)currentChunk / chunks.size());
+        currentChunk++;
+      }
+      new HgCommandExecutor(myProject).executeInCurrentThread(repo, "add", paths);
     }
   }
 
     }
   }
 
index 7b2ed5b9c4a043c8bca18ac31ef28401f575df48..232b600ef1da3836af158018c6b1d02701cdd61a 100644 (file)
 package org.zmlx.hg4idea.command;
 
 import com.intellij.openapi.project.Project;
 package org.zmlx.hg4idea.command;
 
 import com.intellij.openapi.project.Project;
-import org.zmlx.hg4idea.HgFile;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.vcsUtil.VcsFileUtil;
+import com.intellij.vcsUtil.VcsUtil;
 import org.zmlx.hg4idea.execution.HgCommandExecutor;
 
 import java.util.Arrays;
 import org.zmlx.hg4idea.execution.HgCommandExecutor;
 
 import java.util.Arrays;
+import java.util.Collections;
 
 public class HgCopyCommand {
 
 
 public class HgCopyCommand {
 
-  private final Project project;
+  private final Project myProject;
 
   public HgCopyCommand(Project project) {
 
   public HgCopyCommand(Project project) {
-    this.project = project;
+    myProject = project;
   }
 
   }
 
-  public void execute(HgFile source, HgFile target) {
-    HgCommandExecutor executor = new HgCommandExecutor(project);
-    if (source.getRepo().equals(target.getRepo())) {
-      executor.execute(source.getRepo(), "copy", Arrays.asList("--after", source.getRelativePath(), target.getRelativePath()), null);
+  public void execute(VirtualFile source, VirtualFile target) {
+    HgCommandExecutor executor = new HgCommandExecutor(myProject);
+    VirtualFile sourceRepo = VcsUtil.getVcsRootFor(myProject, source);
+    VirtualFile targetRepo = VcsUtil.getVcsRootFor(myProject, target);
+    if (sourceRepo != null && targetRepo != null && sourceRepo.equals(targetRepo)) {
+      executor.execute(sourceRepo, "copy", Arrays.asList("--after",
+                                                         VcsFileUtil.relativeOrFullPath(sourceRepo, source),
+                                                         VcsFileUtil.relativeOrFullPath(targetRepo, target)), null);
+    } else {
+      // copying from one repository to another => 'hg add' in new repo
+      if (targetRepo != null) {
+        new HgAddCommand(myProject).execute(Collections.singleton(target));
+      }
     }
   }
 
     }
   }
 
index be1d683e5b47d6862a031e63310fa4ab8ca6ea34..77cf14fdc780ec20445c8462684007fd656c96ba 100644 (file)
@@ -24,7 +24,6 @@ import com.intellij.openapi.vcs.changes.ChangeList;
 import com.intellij.openapi.vcs.changes.ContentRevision;
 import com.intellij.openapi.vcs.checkin.CheckinEnvironment;
 import com.intellij.openapi.vcs.ui.RefreshableOnComponent;
 import com.intellij.openapi.vcs.changes.ContentRevision;
 import com.intellij.openapi.vcs.checkin.CheckinEnvironment;
 import com.intellij.openapi.vcs.ui.RefreshableOnComponent;
-import com.intellij.openapi.vfs.VfsUtil;
 import com.intellij.openapi.vfs.VirtualFile;
 import com.intellij.util.FunctionUtil;
 import com.intellij.util.NullableFunction;
 import com.intellij.openapi.vfs.VirtualFile;
 import com.intellij.util.FunctionUtil;
 import com.intellij.util.NullableFunction;
@@ -186,20 +185,8 @@ public class HgCheckinEnvironment implements CheckinEnvironment {
     return null;
   }
 
     return null;
   }
 
-  public List<VcsException> scheduleUnversionedFilesForAddition(List<VirtualFile> files) {
-    final HgAddCommand command = new HgAddCommand(myProject);
-    for (final VirtualFile file : files) {
-      final VirtualFile vcsRoot = VcsUtil.getVcsRootFor(myProject, file);
-      if (vcsRoot == null) {
-        continue;
-      }
-      ApplicationManager.getApplication().executeOnPooledThread(new Runnable() {
-        @Override
-        public void run() {
-          command.execute(new HgFile(vcsRoot, VfsUtil.virtualToIoFile(file)));
-        }
-      });
-    }
+  public List<VcsException> scheduleUnversionedFilesForAddition(final List<VirtualFile> files) {
+    new HgAddCommand(myProject).addWithProgress(files);
     return null;
   }
 
     return null;
   }
 
index 2545612e5831caf905e08a91824b820b8a13ea0f..06032f5946c7ad3c21f9e57de875659d72e35e9e 100644 (file)
@@ -27,7 +27,6 @@ import com.intellij.openapi.vcs.changes.ContentRevision;
 import com.intellij.openapi.vcs.changes.VcsDirtyScopeManager;
 import com.intellij.openapi.vfs.VirtualFile;
 import com.intellij.ui.GuiUtils;
 import com.intellij.openapi.vcs.changes.VcsDirtyScopeManager;
 import com.intellij.openapi.vfs.VirtualFile;
 import com.intellij.ui.GuiUtils;
-import com.intellij.util.containers.HashMap;
 import com.intellij.vcsUtil.VcsUtil;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 import com.intellij.vcsUtil.VcsUtil;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
@@ -45,7 +44,9 @@ import java.util.*;
  * HgUtil is a collection of static utility methods for Mercurial.
  */
 public abstract class HgUtil {
  * HgUtil is a collection of static utility methods for Mercurial.
  */
 public abstract class HgUtil {
-  
+
+  public static final int MANY_FILES = 100;
+
   public static File copyResourceToTempFile(String basename, String extension) throws IOException {
     final InputStream in = HgUtil.class.getClassLoader().getResourceAsStream("python/" + basename + extension);
 
   public static File copyResourceToTempFile(String basename, String extension) throws IOException {
     final InputStream in = HgUtil.class.getClassLoader().getResourceAsStream("python/" + basename + extension);
 
@@ -336,4 +337,39 @@ public abstract class HgUtil {
     return repos;
   }
 
     return repos;
   }
 
+  public static @NotNull Map<VirtualFile, Collection<VirtualFile>> sortByHgRoots(@NotNull Project project, @NotNull Collection<VirtualFile> files) {
+    Map<VirtualFile, Collection<VirtualFile>> sorted = new HashMap<VirtualFile, Collection<VirtualFile>>();
+    for (VirtualFile file : files) {
+      VirtualFile repo = VcsUtil.getVcsRootFor(project, file);
+      if (repo == null) {
+        continue;
+      }
+      Collection<VirtualFile> filesForRoot = sorted.get(repo);
+      if (filesForRoot == null) {
+        filesForRoot = new HashSet<VirtualFile>();
+        sorted.put(repo, filesForRoot);
+      }
+      filesForRoot.add(file);
+    }
+    return sorted;
+  }
+
+  public static @NotNull Map<VirtualFile, Collection<FilePath>> groupFilePathsByHgRoots(@NotNull Project project,
+                                                                                        @NotNull Collection<FilePath> files) {
+    Map<VirtualFile, Collection<FilePath>> sorted = new HashMap<VirtualFile, Collection<FilePath>>();
+    for (FilePath file : files) {
+      VirtualFile repo = VcsUtil.getVcsRootFor(project, file);
+      if (repo == null) {
+        continue;
+      }
+      Collection<FilePath> filesForRoot = sorted.get(repo);
+      if (filesForRoot == null) {
+        filesForRoot = new HashSet<FilePath>();
+        sorted.put(repo, filesForRoot);
+      }
+      filesForRoot.add(file);
+    }
+    return sorted;
+  }
+
 }
 }