Merge branch 'master' of git.labs.intellij.net:idea/community
authorirengrig <Irina.Chernushina@jetbrains.com>
Wed, 10 Mar 2010 16:17:39 +0000 (19:17 +0300)
committerirengrig <Irina.Chernushina@jetbrains.com>
Wed, 10 Mar 2010 16:17:39 +0000 (19:17 +0300)
platform/lang-impl/src/com/intellij/openapi/vcs/changes/ui/ChangesModuleGroupingPolicy.java
platform/vcs-api/src/com/intellij/openapi/vcs/Heavy.java [new file with mode: 0644]
platform/vcs-api/src/com/intellij/openapi/vcs/changes/FilePathsHelper.java [new file with mode: 0644]
platform/vcs-impl/src/com/intellij/openapi/vcs/FilePathImpl.java
platform/vcs-impl/src/com/intellij/openapi/vcs/changes/DeletedFilesHolder.java
platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/ChangesGroupingPolicy.java
platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/StaticFilePath.java [new file with mode: 0644]
platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/TreeModelBuilder.java
platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/VirtualFileHierarchicalComparator.java [new file with mode: 0644]
plugins/git4idea/src/git4idea/GitUtil.java

index 7cde5c9b92b1f8d78de60901619163e0f160da3a..3a043cbc7fc2b922f35d81d45aef715186458cc2 100644 (file)
@@ -20,7 +20,6 @@ import com.intellij.openapi.module.Module;
 import com.intellij.openapi.project.Project;
 import com.intellij.openapi.roots.ProjectFileIndex;
 import com.intellij.openapi.roots.ProjectRootManager;
-import com.intellij.openapi.vcs.FilePath;
 import com.intellij.openapi.vfs.VirtualFile;
 import org.jetbrains.annotations.Nullable;
 
@@ -43,10 +42,10 @@ public class ChangesModuleGroupingPolicy implements ChangesGroupingPolicy {
   }
 
   @Nullable
-  public ChangesBrowserNode getParentNodeFor(final ChangesBrowserNode node, final ChangesBrowserNode rootNode) {
+  public ChangesBrowserNode getParentNodeFor(final StaticFilePath node, final ChangesBrowserNode rootNode) {
     ProjectFileIndex index = ProjectRootManager.getInstance(myProject).getFileIndex();
-    final FilePath path = TreeModelBuilder.getPathForObject(node.getUserObject());
-    VirtualFile vFile = path.getVirtualFile();
+
+    VirtualFile vFile = node.getVf();
     if (vFile != null && vFile == index.getContentRootForFile(vFile)) {
       Module module = index.getModuleForFile(vFile);
       return getNodeForModule(module, rootNode);
diff --git a/platform/vcs-api/src/com/intellij/openapi/vcs/Heavy.java b/platform/vcs-api/src/com/intellij/openapi/vcs/Heavy.java
new file mode 100644 (file)
index 0000000..60bcf30
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * 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.vcs;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Retention(RetentionPolicy.SOURCE)
+@Target({ElementType.METHOD, ElementType.CONSTRUCTOR})
+public @interface Heavy {
+}
diff --git a/platform/vcs-api/src/com/intellij/openapi/vcs/changes/FilePathsHelper.java b/platform/vcs-api/src/com/intellij/openapi/vcs/changes/FilePathsHelper.java
new file mode 100644 (file)
index 0000000..e30abfe
--- /dev/null
@@ -0,0 +1,24 @@
+package com.intellij.openapi.vcs.changes;
+
+import com.intellij.openapi.util.SystemInfo;
+import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.openapi.vcs.FilePath;
+import com.intellij.openapi.vfs.VirtualFile;
+
+public class FilePathsHelper {
+  private FilePathsHelper() {
+  }
+
+  public static String convertPath(final VirtualFile vf) {
+    return convertPath(vf.getPath());
+  }
+
+  public static String convertPath(final FilePath fp) {
+    return convertPath(fp.getPath());
+  }
+
+  public static String convertPath(final String s) {
+    String result = FileUtil.toSystemIndependentName(s);
+    return SystemInfo.isFileSystemCaseSensitive ? result : result.toUpperCase();
+  }
+}
index a29aa03ef312734dd7e20bf7b9954f987ee10c9c..fc41c6058ab948534c8d12880a793aa08e7c075b 100644 (file)
@@ -65,10 +65,12 @@ public class FilePathImpl implements FilePath {
     }
   }
 
+  @Heavy
   public FilePathImpl(VirtualFile virtualParent, String name, final boolean isDirectory) {
     this(virtualParent, name, isDirectory, null, false);
   }
 
+  @Heavy
   private FilePathImpl(VirtualFile virtualParent, String name, final boolean isDirectory, final boolean forDeleted) {
     this(virtualParent, name, isDirectory, null, forDeleted);
   }
index c545d40b81a13f967f70b71decd87a69bc40e742..acb851894f4e8bc2b6a0361c73fe00b2eadf9e46 100644 (file)
  */
 package com.intellij.openapi.vcs.changes;
 
-import com.intellij.openapi.application.ApplicationManager;
 import com.intellij.openapi.vcs.FilePath;
 
-import java.util.*;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
 
 /**
  * @author max
@@ -36,19 +38,15 @@ public class DeletedFilesHolder implements FileHolder {
   }
 
   public void cleanScope(final VcsDirtyScope scope) {
-    ApplicationManager.getApplication().runReadAction(new Runnable() {
-      public void run() {
-        if (scope == null) {
-          myFiles.clear();
-        }
-        final List<LocallyDeletedChange> currentFiles = new ArrayList<LocallyDeletedChange>(myFiles.values());
-        for (LocallyDeletedChange change : currentFiles) {
-          if (scope.belongsTo(change.getPath())) {
-            myFiles.remove(change.getPresentableUrl());
-          }
-        }
+    if (scope == null) {
+      myFiles.clear();
+    }
+    final List<LocallyDeletedChange> currentFiles = new ArrayList<LocallyDeletedChange>(myFiles.values());
+    for (LocallyDeletedChange change : currentFiles) {
+      if (scope.belongsTo(change.getPath())) {
+        myFiles.remove(change.getPresentableUrl());
       }
-    });
+    }
   }
 
   public HolderType getType() {
index 4002220fb28963b5ba6abc2fe566cb06910bbd70..e7f15995b819becb5ab279cadeb67a69242402c5 100644 (file)
@@ -19,7 +19,7 @@ import org.jetbrains.annotations.Nullable;
 
 public interface ChangesGroupingPolicy {
   @Nullable
-  ChangesBrowserNode getParentNodeFor(final ChangesBrowserNode node, final ChangesBrowserNode rootNode);
+  ChangesBrowserNode getParentNodeFor(final StaticFilePath node, final ChangesBrowserNode rootNode);
 
   void clear();
 }
\ No newline at end of file
diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/StaticFilePath.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/StaticFilePath.java
new file mode 100644 (file)
index 0000000..80475f2
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * 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.vcs.changes.ui;
+
+import com.intellij.openapi.vcs.changes.FilePathsHelper;
+import com.intellij.openapi.vfs.VirtualFile;
+import org.jetbrains.annotations.Nullable;
+
+public class StaticFilePath {
+  private final String myKey;
+  private final String myPath;
+  private final boolean myIsDirectory;
+  // todo?
+  private final VirtualFile myVf;
+
+  public StaticFilePath(boolean isDirectory, String path, VirtualFile vf) {
+    myIsDirectory = isDirectory;
+    myPath = path;
+    myVf = vf;
+    myKey = FilePathsHelper.convertPath(path);
+  }
+
+  private StaticFilePath(boolean isDirectory, String path, final String key, final VirtualFile vf) {
+    myIsDirectory = isDirectory;
+    myPath = path;
+    myVf = vf;
+    myKey = key;
+  }
+
+  public boolean isDirectory() {
+    return myIsDirectory;
+  }
+
+  public String getPath() {
+    return myPath;
+  }
+
+  public String getKey() {
+    return myKey;
+  }
+
+  public VirtualFile getVf() {
+    return myVf;
+  }
+
+  @Nullable
+  public StaticFilePath getParent() {
+    final int idx = myKey.lastIndexOf('/');
+    return (idx == -1) || (idx == 0) ? null :
+           new StaticFilePath(true, myPath.substring(0, idx), myKey.substring(0, idx), myVf == null ? null : myVf.getParent());
+  }
+}
index 4ff479b52113d82cba2f8d610253b8b33378c28a..a93db56c25219a52bd98b87dadb3f86082e188d5 100644 (file)
@@ -32,8 +32,8 @@ import org.jetbrains.annotations.NonNls;
 import org.jetbrains.annotations.Nullable;
 
 import javax.swing.tree.DefaultTreeModel;
-import javax.swing.tree.MutableTreeNode;
 import javax.swing.tree.TreeNode;
+import java.io.File;
 import java.util.*;
 
 /**
@@ -48,7 +48,7 @@ public class TreeModelBuilder {
   private final ChangesBrowserNode root;
   private boolean myPolicyInitialized;
   private ChangesGroupingPolicy myPolicy;
-  private final HashMap<String, ChangesBrowserNode> myFoldersCache;
+  private HashMap<String, ChangesBrowserNode> myFoldersCache;
 
   public TreeModelBuilder(final Project project, final boolean showFlatten) {
     myProject = project;
@@ -171,20 +171,21 @@ public class TreeModelBuilder {
   }
 
   private void resetGrouping() {
-    myFoldersCache.clear();
+    myFoldersCache = new HashMap<String, ChangesBrowserNode>();
     myPolicyInitialized = false;
   }
 
   public DefaultTreeModel buildModel(List<? extends ChangeList> changeLists) {
     final RemoteRevisionsCache revisionsCache = RemoteRevisionsCache.getInstance(myProject);
     for (ChangeList list : changeLists) {
-      final Collection<Change> changes = list.getChanges();
+      final List<Change> changes = new ArrayList<Change>(list.getChanges());
       final ChangeListRemoteState listRemoteState = new ChangeListRemoteState(changes.size());
       ChangesBrowserNode listNode = new ChangesBrowserChangeListNode(myProject, list, listRemoteState);
       model.insertNodeInto(listNode, root, 0);
       resetGrouping();
       final ChangesGroupingPolicy policy = createGroupingPolicy();
       int i = 0;
+      Collections.sort(changes, MyChangePathLengthComparator.getInstance());
       for (final Change change : changes) {
         final MyChangeNodeUnderChangeListDecorator decorator =
           new MyChangeNodeUnderChangeListDecorator(revisionsCache, new ChangeListRemoteState.Reporter(i, listRemoteState));
@@ -199,14 +200,30 @@ public class TreeModelBuilder {
     return model;
   }
 
-  private void buildVirtualFiles(final Iterator<FilePath> iterator, @Nullable final Object tag) {
+  private static class MyChangePathLengthComparator implements Comparator<Change> {
+    private final static MyChangePathLengthComparator ourInstance = new MyChangePathLengthComparator();
+
+    public static MyChangePathLengthComparator getInstance() {
+      return ourInstance;
+    }
+
+    public int compare(Change o1, Change o2) {
+      final FilePath fp1 = ChangesUtil.getFilePath(o1);
+      final FilePath fp2 = ChangesUtil.getFilePath(o2);
+
+      final int diff = fp1.getPath().length() - fp2.getPath().length();
+      return diff == 0 ? 0 : (diff < 0 ? -1 : 1);
+    }
+  }
+
+  /*private void buildVirtualFiles(final Iterator<FilePath> iterator, @Nullable final Object tag) {
     final ChangesBrowserNode baseNode = createNode(tag);
     final ChangesGroupingPolicy policy = createGroupingPolicy();
     for (; ; iterator.hasNext()) {
       final FilePath path = iterator.next();
       insertChangeNode(path.getVirtualFile(), policy, baseNode, defaultNodeCreator(path.getVirtualFile()));
     }
-  }
+  } */
 
   private void buildVirtualFiles(final List<VirtualFile> files, @Nullable final Object tag) {
     final ChangesBrowserNode baseNode = createNode(tag);
@@ -225,8 +242,10 @@ public class TreeModelBuilder {
     return baseNode;
   }
 
-  private void insertFilesIntoNode(List<VirtualFile> files, ChangesBrowserNode baseNode) {
+  private void insertFilesIntoNode(final List<VirtualFile> files, ChangesBrowserNode baseNode) {
     final ChangesGroupingPolicy policy = createGroupingPolicy();
+    Collections.sort(files, VirtualFileHierarchicalComparator.getInstance());
+    
     for (VirtualFile file : files) {
       insertChangeNode(file, policy, baseNode, defaultNodeCreator(file));
     }
@@ -235,26 +254,31 @@ public class TreeModelBuilder {
   private void buildLocallyDeletedPaths(final Collection<LocallyDeletedChange> locallyDeletedChanges, final ChangesBrowserNode baseNode) {
     final ChangesGroupingPolicy policy = createGroupingPolicy();
     for (LocallyDeletedChange change : locallyDeletedChanges) {
-      ChangesBrowserNode oldNode = myFoldersCache.get(change.getPresentableUrl());
+      // whether a folder does not matter
+      final StaticFilePath key = new StaticFilePath(false, change.getPresentableUrl(), change.getPath().getVirtualFile());
+      ChangesBrowserNode oldNode = myFoldersCache.get(key.getKey());
       if (oldNode == null) {
         final ChangesBrowserNode node = ChangesBrowserNode.create(myProject, change);
-        final ChangesBrowserNode parent = getParentNodeFor(node, policy, baseNode);
+        final ChangesBrowserNode parent = getParentNodeFor(key, policy, baseNode);
         model.insertNodeInto(node, parent, parent.getChildCount());
-        myFoldersCache.put(change.getPresentableUrl(), node);
+        myFoldersCache.put(key.getKey(), node);
       }
     }
   }
 
-  public void buildFilePaths(final Collection<FilePath> filePaths, final ChangesBrowserNode baseNode) {
+  private void buildFilePaths(final Collection<FilePath> filePaths, final ChangesBrowserNode baseNode) {
     final ChangesGroupingPolicy policy = createGroupingPolicy();
     for (FilePath file : filePaths) {
       assert file != null;
-      ChangesBrowserNode oldNode = myFoldersCache.get(file.getIOFile().getAbsolutePath());
+      // whether a folder does not matter
+      final StaticFilePath pathKey = new StaticFilePath(false, file.getIOFile().getAbsolutePath(), file.getVirtualFile());
+      ChangesBrowserNode oldNode = myFoldersCache.get(pathKey.getKey());
       if (oldNode == null) {
         final ChangesBrowserNode node = ChangesBrowserNode.create(myProject, file);
-        final ChangesBrowserNode parentNode = getParentNodeFor(node, policy, baseNode);
+        final ChangesBrowserNode parentNode = getParentNodeFor(pathKey, policy, baseNode);
         model.insertNodeInto(node, parentNode, 0);
-        myFoldersCache.put(file.getIOFile().getAbsolutePath(), node);
+        // we could also ask whether a file or directory, though for deleted files not a good idea
+        myFoldersCache.put(pathKey.getKey(), node);
       }
     }
   }
@@ -264,7 +288,10 @@ public class TreeModelBuilder {
     rootsHeadNode.setAttributes(SimpleTextAttributes.GRAYED_BOLD_ATTRIBUTES);
     model.insertNodeInto(rootsHeadNode, root, root.getChildCount());
 
-    for (VirtualFile vf : switchedRoots.keySet()) {
+    final List<VirtualFile> files = new ArrayList<VirtualFile>(switchedRoots.keySet());
+    Collections.sort(files, VirtualFileHierarchicalComparator.getInstance());
+    
+    for (VirtualFile vf : files) {
       final ChangesGroupingPolicy policy = createGroupingPolicy();
       final ContentRevision cr = new CurrentContentRevision(new FilePathImpl(vf));
       final Change change = new Change(cr, cr, FileStatus.NOT_CHANGED);
@@ -290,12 +317,13 @@ public class TreeModelBuilder {
     ChangesBrowserNode baseNode = ChangesBrowserNode.create(myProject, ChangesBrowserNode.SWITCHED_FILES_TAG);
     model.insertNodeInto(baseNode, root, root.getChildCount());
     for(String branchName: switchedFiles.keySet()) {
-      final Collection<VirtualFile> switchedFileList = switchedFiles.get(branchName);
+      final List<VirtualFile> switchedFileList = new ArrayList<VirtualFile>(switchedFiles.get(branchName));
       if (switchedFileList.size() > 0) {
         ChangesBrowserNode branchNode = ChangesBrowserNode.create(myProject, branchName);
         model.insertNodeInto(branchNode, baseNode, baseNode.getChildCount());
 
         final ChangesGroupingPolicy policy = createGroupingPolicy();
+        Collections.sort(switchedFileList, VirtualFileHierarchicalComparator.getInstance());
         for (VirtualFile file : switchedFileList) {
           insertChangeNode(file, policy, branchNode, defaultNodeCreator(file));
         }
@@ -307,15 +335,16 @@ public class TreeModelBuilder {
     final ChangesBrowserNode baseNode = createNode(ChangesBrowserNode.LOGICALLY_LOCKED_TAG);
 
     final ChangesGroupingPolicy policy = createGroupingPolicy();
-    for (Map.Entry<VirtualFile, LogicalLock> entry : logicallyLockedFiles.entrySet()) {
-      final VirtualFile file = entry.getKey();
-      final LogicalLock lock = entry.getValue();
+    final List<VirtualFile> keys = new ArrayList<VirtualFile>(logicallyLockedFiles.keySet());
+    Collections.sort(keys, VirtualFileHierarchicalComparator.getInstance());
+
+    for (final VirtualFile file : keys) {
+      final LogicalLock lock = logicallyLockedFiles.get(file);
       final ChangesBrowserLogicallyLockedFile obj = new ChangesBrowserLogicallyLockedFile(myProject, file, lock);
-      insertChangeNode(obj, policy, baseNode,
-                       defaultNodeCreator(obj));
+      insertChangeNode(obj, policy, baseNode, defaultNodeCreator(obj));
     }
   }
-  
+
   private Computable<ChangesBrowserNode> defaultNodeCreator(final Object change) {
     return new Computable<ChangesBrowserNode>() {
       public ChangesBrowserNode compute() {
@@ -326,45 +355,34 @@ public class TreeModelBuilder {
 
   private void insertChangeNode(final Object change, final ChangesGroupingPolicy policy,
                                 final ChangesBrowserNode listNode, final Computable<ChangesBrowserNode> nodeCreator) {
-    final FilePath nodePath = getPathForObject(change);
-    ChangesBrowserNode oldNode = (nodePath == null) ? null : myFoldersCache.get(nodePath.getIOFile().getAbsolutePath());
-    ChangesBrowserNode node = nodeCreator.compute();
-    if (oldNode != null) {
-      for(int i=oldNode.getChildCount()-1; i >= 0; i--) {
-        MutableTreeNode child = (MutableTreeNode) model.getChild(oldNode, i);
-        model.removeNodeFromParent(child);
-        model.insertNodeInto(child, node, model.getChildCount(node));
-      }
-      final MutableTreeNode parent = (MutableTreeNode)oldNode.getParent();
-      int index = model.getIndexOfChild(parent, oldNode);
-      model.removeNodeFromParent(oldNode);
-      model.insertNodeInto(node, parent, index);
-      myFoldersCache.put(nodePath.getIOFile().getAbsolutePath(), node);
-    }
-    else {
-      ChangesBrowserNode parentNode = getParentNodeFor(node, policy, listNode);
-      model.insertNodeInto(node, parentNode, model.getChildCount(parentNode));
-      // ?
-      if (nodePath != null) {
-        myFoldersCache.put(nodePath.getIOFile().getAbsolutePath(), node);
-      }
+    final StaticFilePath pathKey = getKey(change);
+    final ChangesBrowserNode node = nodeCreator.compute();
+    ChangesBrowserNode parentNode = getParentNodeFor(pathKey, policy, listNode);
+    model.insertNodeInto(node, parentNode, model.getChildCount(parentNode));
+
+    if (pathKey != null && pathKey.isDirectory()) {
+      myFoldersCache.put(pathKey.getKey(), node);
     }
   }
 
   private void sortNodes() {
-    TreeUtil.sort(model, new Comparator() {
-      public int compare(final Object n1, final Object n2) {
-        final ChangesBrowserNode node1 = (ChangesBrowserNode)n1;
-        final ChangesBrowserNode node2 = (ChangesBrowserNode)n2;
+    TreeUtil.sort(model, MyChangesBrowserNodeComparator.getInstance());
 
-        final int classdiff = node1.getSortWeight() - node2.getSortWeight();
-        if (classdiff != 0) return classdiff;
+    model.nodeStructureChanged((TreeNode)model.getRoot());
+  }
 
-        return node1.compareUserObjects(node2.getUserObject());
-      }
-   });
+  private static class MyChangesBrowserNodeComparator implements Comparator<ChangesBrowserNode> {
+    private static final MyChangesBrowserNodeComparator ourInstance = new MyChangesBrowserNodeComparator();
 
-    model.nodeStructureChanged((TreeNode)model.getRoot());
+    public static MyChangesBrowserNodeComparator getInstance() {
+      return ourInstance;
+    }
+
+    public int compare(ChangesBrowserNode node1, ChangesBrowserNode node2) {
+      final int classdiff = node1.getSortWeight() - node2.getSortWeight();
+      if (classdiff != 0) return classdiff;
+      return node1.compareUserObjects(node2.getUserObject());
+    }
   }
 
   private static void collapseDirectories(DefaultTreeModel model, ChangesBrowserNode node) {
@@ -388,6 +406,32 @@ public class TreeModelBuilder {
     }
   }
 
+  private static StaticFilePath getKey(final Object o) {
+    if (o instanceof Change) {
+      return staticFrom(ChangesUtil.getFilePath((Change) o));
+    }
+    else if (o instanceof VirtualFile) {
+      return staticFrom((VirtualFile) o);
+    }
+    else if (o instanceof FilePath) {
+      return staticFrom((FilePath) o);
+    } else if (o instanceof ChangesBrowserLogicallyLockedFile) {
+      return staticFrom(((ChangesBrowserLogicallyLockedFile) o).getUserObject());
+    } else if (o instanceof LocallyDeletedChange) {
+      return staticFrom(((LocallyDeletedChange) o).getPath());
+    }
+
+    return null;
+  }
+
+  private static StaticFilePath staticFrom(final FilePath fp) {
+    return new StaticFilePath(fp.isDirectory(), fp.getIOFile().getAbsolutePath(), fp.getVirtualFile());
+  }
+  
+  private static StaticFilePath staticFrom(final VirtualFile vf) {
+    return new StaticFilePath(vf.isDirectory(), vf.getPath(), vf);
+  }
+
   public static FilePath getPathForObject(Object o) {
     if (o instanceof Change) {
       return ChangesUtil.getFilePath((Change)o);
@@ -406,32 +450,29 @@ public class TreeModelBuilder {
     return null;
   }
 
-  private ChangesBrowserNode getParentNodeFor(ChangesBrowserNode node, @Nullable ChangesGroupingPolicy policy, ChangesBrowserNode rootNode) {
+  private ChangesBrowserNode getParentNodeFor(final StaticFilePath nodePath, @Nullable ChangesGroupingPolicy policy, ChangesBrowserNode rootNode) {
     if (showFlatten) {
       return rootNode;
     }
 
-    final FilePath path = getPathForObject(node.getUserObject());
-
     if (policy != null) {
-      ChangesBrowserNode nodeFromPolicy = policy.getParentNodeFor(node, rootNode);
+      ChangesBrowserNode nodeFromPolicy = policy.getParentNodeFor(nodePath, rootNode);
       if (nodeFromPolicy != null) {
         return nodeFromPolicy;
       }
     }
 
-    FilePath parentPath = path.getParentPath();
+    final StaticFilePath parentPath = nodePath.getParent();
     if (parentPath == null) {
       return rootNode;
     }
 
-    final String parentKey = parentPath.getIOFile().getAbsolutePath();
-    ChangesBrowserNode parentNode = myFoldersCache.get(parentKey);
+    ChangesBrowserNode parentNode = myFoldersCache.get(parentPath.getKey());
     if (parentNode == null) {
-      parentNode = ChangesBrowserNode.create(myProject, parentPath);
-      ChangesBrowserNode grandPa = getParentNodeFor(parentNode, policy, rootNode);
+      parentNode = ChangesBrowserNode.create(myProject, new FilePathImpl(new File(parentPath.getPath()), true));
+      ChangesBrowserNode grandPa = getParentNodeFor(parentPath, policy, rootNode);
       model.insertNodeInto(parentNode, grandPa, grandPa.getChildCount());
-      myFoldersCache.put(parentKey, parentNode);
+      myFoldersCache.put(parentPath.getKey(), parentNode);
     }
 
     return parentNode;
@@ -440,7 +481,7 @@ public class TreeModelBuilder {
   public DefaultTreeModel clearAndGetModel() {
     root.removeAllChildren();
     model = new DefaultTreeModel(root);
-    myFoldersCache.clear();
+    myFoldersCache = new HashMap<String, ChangesBrowserNode>();
     myPolicyInitialized = false;
     return model;
   }
diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/VirtualFileHierarchicalComparator.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/VirtualFileHierarchicalComparator.java
new file mode 100644 (file)
index 0000000..1b9948f
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * 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.vcs.changes.ui;
+
+import com.intellij.openapi.vfs.VirtualFile;
+
+import java.util.Comparator;
+
+public class VirtualFileHierarchicalComparator implements Comparator<VirtualFile> {
+  private static final VirtualFileHierarchicalComparator ourInstance = new VirtualFileHierarchicalComparator();
+
+  public static VirtualFileHierarchicalComparator getInstance() {
+    return ourInstance;
+  }
+
+  public int compare(final VirtualFile vf1, final VirtualFile vf2) {
+    final boolean isDir1 = vf1.isDirectory();
+    final boolean isDir2 = vf2.isDirectory();
+
+    if ((! isDir1) && (! isDir2)) return 0;
+    if (isDir1 && (! isDir2)) return -1;
+    if ((! isDir1) && isDir2) return 1;
+
+    final int diff = vf1.getPath().length() - vf2.getPath().length();
+    return diff == 0 ? 0 : (diff < 0 ? -1 : 1);
+  }
+}
index 55ec70779737415df17ac69bac976c187350eb8b..d8e676c00d905cf18dbcbdffd2651e18d07a5e70 100644 (file)
@@ -662,6 +662,7 @@ public class GitUtil {
     final List<CommittedChangeList> rc = new ArrayList<CommittedChangeList>();
 
     GitSimpleHandler h = new GitSimpleHandler(project, root, GitCommand.LOG);
+    h.setSilent(true);
     h.setNoSSH(true);
     h.addParameters("--pretty=format:%x0C%n" + GitChangeUtils.COMMITTED_CHANGELIST_FORMAT, "--name-status");
     parametersSpecifier.consume(h);