import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.SystemInfo;
import com.intellij.openapi.util.text.StringUtil;
-import com.intellij.util.ArrayUtil;
-import com.intellij.util.ObjectUtils;
-import com.intellij.util.Processor;
-import com.intellij.util.SystemProperties;
+import com.intellij.util.*;
import com.intellij.util.containers.ContainerUtil;
+import com.intellij.util.containers.Convertor;
import com.intellij.util.io.URLUtil;
import com.intellij.util.text.CaseInsensitiveStringHashingStrategy;
import gnu.trove.TObjectHashingStrategy;
return false;
}
+ return isCanonicalAncestor(strict, ancestorPath, filePath);
+ }
+
+ private static boolean isCanonicalAncestor(boolean strict, String ancestorPath, String filePath) {
boolean startsWith = SystemInfo.isFileSystemCaseSensitive ? StringUtil.startsWith(filePath, ancestorPath)
: StringUtil.startsWithIgnoreCase(filePath, ancestorPath);
if (!startsWith) {
!strict && filePath.length() == ancestorPath.length();
}
+ public static<T> Collection<T> removeAncestors(final Collection<T> files, final Convertor<T, String> convertor,
+ final PairProcessor<String, T> removeProcessor) {
+ if (files.isEmpty()) return files;
+ final TreeMap<String, T> paths = new TreeMap<String, T>();
+ for (T file : files) {
+ final String path = convertor.convert(file);
+ assert path != null;
+ final String canonicalPath = toCanonicalPath(path);
+ paths.put(canonicalPath, file);
+ }
+ final List<Map.Entry<String, T>> ordered = new ArrayList<Map.Entry<String, T>>(paths.entrySet());
+ final List<T> result = new ArrayList<T>(ordered.size());
+ result.add(ordered.get(0).getValue());
+ for (int i = 1; i < ordered.size(); i++) {
+ final Map.Entry<String, T> entry = ordered.get(i);
+ final String child = entry.getKey();
+ boolean parentNotFound = true;
+ for (int j = i - 1; j >= 0; j--) {
+ // possible parents
+ final String parent = ordered.get(j).getKey();
+ if (parent == null) continue;
+ if (isCanonicalAncestor(false, parent, child) && removeProcessor.process(child, entry.getValue())) {
+ parentNotFound = false;
+ break;
+ }
+ }
+ if (parentNotFound) {
+ result.add(entry.getValue());
+ }
+ }
+ return result;
+ }
+
/**
* Get parent for the file. The method correctly
* processes "." and ".." in file names. The name
* @author Gregory.Shrago
*/
public interface PairProcessor<S, T> {
+ PairProcessor TRUE = new PairProcessor() {
+ @Override
+ public boolean process(Object o, Object o1) {
+ return true;
+ }
+ };
boolean process(S s, T t);
}
* @see com.intellij.util.CommonProcessors
*/
public interface Processor<T> {
+ Processor TRUE = new Processor() {
+ @Override
+ public boolean process(Object o) {
+ return true;
+ }
+ };
/**
* @param t consequently takes value of each element of the set this processor is passed to for processing.
* @return {@code true} to continue processing or {@code false} to stop.
package com.intellij.openapi.util.io;
import com.intellij.openapi.util.SystemInfo;
+import com.intellij.util.PairProcessor;
+import com.intellij.util.containers.Convertor;
+import org.junit.Assert;
import org.junit.Test;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
+import java.util.Arrays;
+import java.util.Collection;
+
+import static org.junit.Assert.*;
/**
* @author Roman Shevchenko
assertEquals(!SystemInfo.isFileSystemCaseSensitive, FileUtil.isAncestor("/a/b/c", "/a/B/c/d", true));
}
+
+ @Test
+ public void testRemoveAncestors() throws Exception {
+ final String[] arr = {"/a/b/c", "/a", "/a/b", "/d/e", "/b/c", "/a/d", "/b/c/ttt", "/a/ewqeuq"};
+ final String[] expectedResult = {"/a","/b/c","/d/e"};
+ final Collection<String> result = FileUtil.removeAncestors(Arrays.asList(arr), Convertor.SELF, PairProcessor.TRUE);
+ Assert.assertArrayEquals(expectedResult, result.toArray(new String[result.size()]));
+ }
}
}
}
+ public static VirtualFile findValidParentAccurately(final FilePath filePath) {
+ if (filePath.getVirtualFile() != null) return filePath.getVirtualFile();
+ final LocalFileSystem lfs = LocalFileSystem.getInstance();
+ VirtualFile result = lfs.findFileByIoFile(filePath.getIOFile());
+ if (result != null) return result;
+ if (! ApplicationManager.getApplication().isReadAccessAllowed()) {
+ result = lfs.refreshAndFindFileByIoFile(filePath.getIOFile());
+ if (result != null) return result;
+ }
+ return getValidParentUnderReadAction(filePath);
+ }
+
+ private static VirtualFile getValidParentUnderReadAction(final FilePath filePath) {
+ return ApplicationManager.getApplication().runReadAction(new Computable<VirtualFile>() {
+ public VirtualFile compute() {
+ return findValidParent(filePath);
+ }
+ });
+ }
+
+ /**
+ * @deprecated use {@link #findValidParentAccurately(com.intellij.openapi.vcs.FilePath)}
+ */
@Nullable
+ @Deprecated
public static VirtualFile findValidParent(FilePath file) {
ApplicationManager.getApplication().assertReadAccessAllowed();
VirtualFile parent = file.getVirtualFile();
}
if (parent == null) {
File ioFile = file.getIOFile();
+ final LocalFileSystem lfs = LocalFileSystem.getInstance();
do {
- parent = LocalFileSystem.getInstance().findFileByIoFile(ioFile);
+ parent = lfs.findFileByIoFile(ioFile);
if (parent != null) break;
ioFile = ioFile.getParentFile();
if (ioFile == null) return null;
Collection<VirtualFile> getAffectedVcsRoots();
@Nullable
Iterator<FilePath> getDirtyDirectoriesIterator(VirtualFile root);
- void recheckDirtyDirKeys();
+ void recheckDirtyKeys();
}
}
}
}
- modifier.recheckDirtyDirKeys();
+ modifier.recheckDirtyKeys();
if (scope.isEmpty()) {
iterator.remove();
}
import com.intellij.openapi.vcs.impl.DefaultVcsRootPolicy;
import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.util.containers.MultiMap;
import org.jetbrains.annotations.NotNull;
import java.util.*;
return;
}
final Collection<FilePathUnderVcs> dirs = dirt.getDirsForVcs();
+ final Collection<FilePathUnderVcs> files = dirt.getFilesForVcs();
+
+ final MultiMap<AbstractVcs, FilePath> filesMap = new MultiMap<AbstractVcs, FilePath>();
+ final MultiMap<AbstractVcs, FilePath> dirsMap = new MultiMap<AbstractVcs, FilePath>();
+
for (FilePathUnderVcs dir : dirs) {
- getScope(dir.getVcs()).addDirtyDirRecursively(dir.getPath());
+ dirsMap.putValue(dir.getVcs(), dir.getPath());
}
- final Collection<FilePathUnderVcs> files = dirt.getFilesForVcs();
for (FilePathUnderVcs file : files) {
- getScope(file.getVcs()).addDirtyFile(file.getPath());
+ filesMap.putValue(file.getVcs(), file.getPath());
+ }
+ final Set<AbstractVcs> keys = new HashSet<AbstractVcs>(filesMap.keySet());
+ keys.addAll(dirsMap.keySet());
+ for (AbstractVcs key : keys) {
+ Collection<FilePath> dirPaths = dirsMap.get(key);
+ dirPaths = dirPaths == null ? Collections.<FilePath>emptyList() : dirPaths;
+ Collection<FilePath> filePaths = filesMap.get(key);
+ filePaths = filePaths == null ? Collections.<FilePath>emptyList() : filePaths;
+
+ getScope(key).addDirtyData(dirPaths, filePaths);
}
}
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.Computable;
+import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.vcs.*;
import com.intellij.openapi.vfs.VfsUtil;
import com.intellij.openapi.vfs.VfsUtilCore;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.Consumer;
+import com.intellij.util.PairProcessor;
import com.intellij.util.Processor;
import com.intellij.util.SmartList;
+import com.intellij.util.containers.CompositeIterator;
+import com.intellij.util.containers.Convertor;
+import com.intellij.util.containers.MultiMap;
import gnu.trove.THashSet;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.Nullable;
* @author yole
*/
public class VcsDirtyScopeImpl extends VcsModifiableDirtyScope {
- private final Set<FilePath> myDirtyFiles = new THashSet<FilePath>();
+ private final Map<VirtualFile, THashSet<FilePath>> myDirtyFiles = new HashMap<VirtualFile, THashSet<FilePath>>();
private final Map<VirtualFile, THashSet<FilePath>> myDirtyDirectoriesRecursively = new HashMap<VirtualFile, THashSet<FilePath>>();
private final Set<VirtualFile> myAffectedContentRoots = new THashSet<VirtualFile>();
private final Project myProject;
@Override
public Iterator<FilePath> getDirtyFilesIterator() {
- return myDirtyFiles.iterator();
+ if (myDirtyFiles.isEmpty()) {
+ return Collections.<FilePath>emptyList().iterator();
+ }
+ final ArrayList<Iterator<FilePath>> iteratorList = new ArrayList<Iterator<FilePath>>(myDirtyFiles.size());
+ for (THashSet<FilePath> paths : myDirtyFiles.values()) {
+ iteratorList.add(paths.iterator());
+ }
+ return new CompositeIterator<FilePath>(iteratorList);
}
@Nullable
}
@Override
- public void recheckDirtyDirKeys() {
- for (Iterator<THashSet<FilePath>> iterator = myDirtyDirectoriesRecursively.values().iterator(); iterator.hasNext();) {
+ public void recheckDirtyKeys() {
+ recheckMap(myDirtyDirectoriesRecursively);
+ recheckMap(myDirtyFiles);
+ }
+
+ private void recheckMap(Map<VirtualFile, THashSet<FilePath>> map) {
+ for (Iterator<THashSet<FilePath>> iterator = map.values().iterator(); iterator.hasNext();) {
final THashSet<FilePath> next = iterator.next();
if (next.isEmpty()) {
iterator.remove();
}
public Set<FilePath> getDirtyFiles() {
- final THashSet<FilePath> result = new THashSet<FilePath>(myDirtyFiles);
- for(FilePath filePath: myDirtyFiles) {
- VirtualFile vFile = filePath.getVirtualFile();
- if (vFile != null && vFile.isValid() && vFile.isDirectory()) {
- for(VirtualFile child: vFile.getChildren()) {
- result.add(new FilePathImpl(child));
+ final THashSet<FilePath> result = new THashSet<FilePath>();
+ for (THashSet<FilePath> paths : myDirtyFiles.values()) {
+ result.addAll(paths);
+ }
+ for (THashSet<FilePath> paths : myDirtyFiles.values()) {
+ for (FilePath filePath : paths) {
+ VirtualFile vFile = filePath.getVirtualFile();
+ if (vFile != null && vFile.isValid() && vFile.isDirectory()) {
+ for(VirtualFile child: vFile.getChildren()) {
+ result.add(new FilePathImpl(child));
+ }
}
}
}
}
public Set<FilePath> getDirtyFilesNoExpand() {
- return new THashSet<FilePath>(myDirtyFiles);
+ final THashSet<FilePath> paths = new THashSet<FilePath>();
+ for (THashSet<FilePath> filePaths : myDirtyFiles.values()) {
+ paths.addAll(filePaths);
+ }
+ return paths;
}
public Set<FilePath> getRecursivelyDirtyDirectories() {
return false;
}
+ private static class FileOrDir {
+ private final FilePath myPath;
+ private final boolean myRecursive;
+
+ private FileOrDir(FilePath path, boolean recursive) {
+ myPath = path;
+ myRecursive = recursive;
+ }
+ }
+
+ public void addDirtyData(final Collection<FilePath> dirs, final Collection<FilePath> files) {
+ ApplicationManager.getApplication().runReadAction(new Runnable() {
+ public void run() {
+ final HashSet<FilePath> newFiles = new HashSet<FilePath>(files);
+ newFiles.removeAll(dirs); // if the same dir is added recursively and not recursively, prefer recursive mark
+
+ final MultiMap<VirtualFile, FileOrDir> perRoot = new MultiMap<VirtualFile, FileOrDir>() {
+ @Override
+ protected Collection<FileOrDir> createCollection() {
+ return new THashSet<FileOrDir>();
+ }
+ };
+ for (Map.Entry<VirtualFile, THashSet<FilePath>> entry : myDirtyDirectoriesRecursively.entrySet()) {
+ newFiles.removeAll(entry.getValue()); // if the same dir is added recursively and not recursively, prefer recursive mark
+ for (FilePath path : entry.getValue()) {
+ perRoot.putValue(entry.getKey(), new FileOrDir(path, true));
+ }
+ }
+
+ for (Map.Entry<VirtualFile, THashSet<FilePath>> entry : myDirtyFiles.entrySet()) {
+ for (FilePath path : entry.getValue()) {
+ perRoot.putValue(entry.getKey(), new FileOrDir(path, false));
+ }
+ }
+
+ for (FilePath dir : dirs) {
+ addFilePathToMap(perRoot, dir, true);
+ }
+ for (FilePath file : newFiles) {
+ addFilePathToMap(perRoot, file, false);
+ }
+
+ for (Map.Entry<VirtualFile, Collection<FileOrDir>> entry : perRoot.entrySet()) {
+ final Collection<FileOrDir> set = entry.getValue();
+ final Collection<FileOrDir> newCollection = FileUtil.removeAncestors(set, new Convertor<FileOrDir, String>() {
+ @Override
+ public String convert(FileOrDir o) {
+ return o.myPath.getPath();
+ }
+ }, PairProcessor.TRUE);
+ set.retainAll(newCollection);
+ }
+
+ myAffectedContentRoots.addAll(perRoot.keySet());
+ for (Map.Entry<VirtualFile, Collection<FileOrDir>> entry : perRoot.entrySet()) {
+ final VirtualFile root = entry.getKey();
+ final THashSet<FilePath> curFiles = new THashSet<FilePath>();
+ final THashSet<FilePath> curDirs = new THashSet<FilePath>();
+ final Collection<FileOrDir> value = entry.getValue();
+ for (FileOrDir fileOrDir : value) {
+ if (fileOrDir.myRecursive) {
+ curDirs.add(fileOrDir.myPath);
+ } else {
+ curFiles.add(fileOrDir.myPath);
+ }
+ }
+ // no clear is necessary since no root can disappear
+ // also, we replace contents, so here's no merging
+ if (! curDirs.isEmpty()) {
+ myDirtyDirectoriesRecursively.put(root, curDirs);
+ }
+ if (! curFiles.isEmpty()) {
+ myDirtyFiles.put(root, curFiles);
+ }
+ }
+ }
+ });
+ }
+
+ private void addFilePathToMap(MultiMap<VirtualFile, FileOrDir> perRoot, FilePath dir, final boolean recursively) {
+ final VirtualFile vcsRoot = myVcsManager.getVcsRootFor(dir);
+ if (vcsRoot != null) {
+ perRoot.putValue(vcsRoot, new FileOrDir(dir, recursively));
+ }
+ }
+
/**
* Add dirty directory recursively. If there are already dirty entries
* that are descendants or ancestors for the added directory, the contained
if (vcsRoot == null) return;
myAffectedContentRoots.add(vcsRoot);
- for (Iterator<FilePath> it = myDirtyFiles.iterator(); it.hasNext();) {
- FilePath oldBoy = it.next();
- if (oldBoy.isUnder(newcomer, false)) {
- it.remove();
+ for (Map.Entry<VirtualFile, THashSet<FilePath>> entry : myDirtyFiles.entrySet()) {
+ final VirtualFile groupRoot = entry.getKey();
+ if (VfsUtilCore.isAncestor(vcsRoot, groupRoot, false)) {
+ final THashSet<FilePath> files = entry.getValue();
+ if (files != null) {
+ for (Iterator<FilePath> it = files.iterator(); it.hasNext();) {
+ FilePath oldBoy = it.next();
+ if (oldBoy.isUnder(newcomer, false)) {
+ it.remove();
+ }
+ }
+ }
}
}
}
}
- if (newcomer.isDirectory()) {
- final List<FilePath> files = new ArrayList<FilePath>(myDirtyFiles);
- for (FilePath oldBoy : files) {
- if (!oldBoy.isDirectory() && Comparing.equal(oldBoy.getVirtualFileParent(), newcomer.getVirtualFile())) {
- myDirtyFiles.remove(oldBoy);
+ final THashSet<FilePath> dirtyFiles = myDirtyFiles.get(vcsRoot);
+ if (dirtyFiles == null) {
+ final THashSet<FilePath> set = new THashSet<FilePath>();
+ set.add(newcomer);
+ myDirtyFiles.put(vcsRoot, set);
+ } else {
+ if (newcomer.isDirectory()) {
+ for (Iterator<FilePath> iterator = dirtyFiles.iterator(); iterator.hasNext(); ) {
+ final FilePath oldBoy = iterator.next();
+ if (!oldBoy.isDirectory() && Comparing.equal(oldBoy.getVirtualFileParent(), newcomer.getVirtualFile())) {
+ iterator.remove();
+ }
}
+ } else if (dirtyFiles.size() > 0) {
+ VirtualFile parent = newcomer.getVirtualFileParent();
+ if (parent != null && dirtyFiles.contains(new FilePathImpl(parent))) {
+ return;
+ }
+ dirtyFiles.add(newcomer);
}
}
- else if (myDirtyFiles.size() > 0) {
- VirtualFile parent = newcomer.getVirtualFileParent();
- if (parent != null && myDirtyFiles.contains(new FilePathImpl(parent))) {
- return;
- }
- }
-
- myDirtyFiles.add(newcomer);
}
});
}
}
}
- for (FilePath file : myDirtyFiles) {
- iterator.process(file);
- final VirtualFile vFile = file.getVirtualFile();
- if (vFile != null && vFile.isValid() && vFile.isDirectory()) {
- for (VirtualFile child : vFile.getChildren()) {
- iterator.process(new FilePathImpl(child));
+ for (VirtualFile root : myAffectedContentRoots) {
+ final THashSet<FilePath> files = myDirtyFiles.get(root);
+ if (files != null) {
+ for (FilePath file : files) {
+ iterator.process(file);
+ final VirtualFile vFile = file.getVirtualFile();
+ if (vFile != null && vFile.isValid() && vFile.isDirectory()) {
+ for (VirtualFile child : vFile.getChildren()) {
+ iterator.process(new FilePathImpl(child));
+ }
+ }
}
}
}
}
}
- for (FilePath file : myDirtyFiles) {
- if (file.getVirtualFile() != null) {
- processor.process(file.getVirtualFile());
- }
- final VirtualFile vFile = file.getVirtualFile();
- if (vFile != null && vFile.isValid() && vFile.isDirectory()) {
- for (VirtualFile child : vFile.getChildren()) {
- processor.process(child);
+ for (VirtualFile root : myAffectedContentRoots) {
+ final THashSet<FilePath> files = myDirtyFiles.get(root);
+ if (files != null) {
+ for (FilePath file : files) {
+ if (file.getVirtualFile() != null) {
+ processor.process(file.getVirtualFile());
+ }
+ final VirtualFile vFile = file.getVirtualFile();
+ if (vFile != null && vFile.isValid() && vFile.isDirectory()) {
+ for (VirtualFile child : vFile.getChildren()) {
+ processor.process(child);
+ }
+ }
}
}
}
final VirtualFile vcsRoot = rootObject.getPath();
if (vcsRoot != null) {
for (VirtualFile contentRoot : myAffectedContentRoots) {
+ // since we don't know exact dirty mechanics, maybe we have 3 nested mappings like:
+ // /root -> vcs1, /root/child -> vcs2, /root/child/inner -> vcs1, and we have file /root/child/inner/file,
+ // mapping is detected as vcs1 with root /root/child/inner, but we could possibly have in scope
+ // "affected root" -> /root with scope = /root recursively
if (VfsUtilCore.isAncestor(contentRoot, vcsRoot, false)) {
THashSet<FilePath> dirsByRoot = myDirtyDirectoriesRecursively.get(contentRoot);
if (dirsByRoot != null) {
else {
parent = FilePathImpl.create(path.getIOFile().getParentFile());
}
- if (myDirtyFiles.contains(parent) || myDirtyFiles.contains(path)) return Boolean.TRUE;
+ return isInDirtyFiles(path) || isInDirtyFiles(parent);
}
return Boolean.FALSE;
}).booleanValue();
}
+ private boolean isInDirtyFiles(final FilePath path) {
+ final VcsRoot rootObject = myVcsManager.getVcsRootObjectFor(path);
+ if (rootObject != null && myVcs.equals(rootObject.getVcs())) {
+ final THashSet<FilePath> files = myDirtyFiles.get(rootObject.getPath());
+ if (files != null && files.contains(path)) return true;
+ }
+ return false;
+ }
+
@Override
public boolean belongsTo(final FilePath path) {
return belongsTo(path, null);
@NonNls StringBuilder result = new StringBuilder("VcsDirtyScope[");
if (myDirtyFiles.size() > 0) {
result.append(" files=");
- for(FilePath file: myDirtyFiles) {
- result.append(file).append(" ");
+ for (THashSet<FilePath> paths : myDirtyFiles.values()) {
+ for (FilePath file : paths) {
+ result.append(file).append(" ");
+ }
}
}
if (myDirtyDirectoriesRecursively.size() > 0) {
if (filePath.isNonLocal()) {
return null;
}
- final VirtualFile validParent = ApplicationManager.getApplication().runReadAction(new Computable<VirtualFile>() {
- public VirtualFile compute() {
- return ChangesUtil.findValidParent(filePath);
- }
- });
+ final VirtualFile validParent = ChangesUtil.findValidParentAccurately(filePath);
if (validParent == null) {
return null;
}
@Nullable
public AbstractVcs getVcsFor(final FilePath file) {
+ final VirtualFile vFile = ChangesUtil.findValidParentAccurately(file);
return ApplicationManager.getApplication().runReadAction(new Computable<AbstractVcs>() {
@Nullable
public AbstractVcs compute() {
if (!ApplicationManager.getApplication().isUnitTestMode() && !myProject.isInitialized()) return null;
if (myProject.isDisposed()) throw new ProcessCanceledException();
- VirtualFile vFile = ChangesUtil.findValidParent(file);
if (vFile != null) {
return getVcsFor(vFile);
}
@Nullable
public VirtualFile getVcsRootFor(final FilePath file) {
- return ApplicationManager.getApplication().runReadAction(new Computable<VirtualFile>() {
- @Nullable
- public VirtualFile compute() {
- if (myProject.isDisposed()) return null;
- VirtualFile vFile = ChangesUtil.findValidParent(file);
- if (vFile != null) {
- return getVcsRootFor(vFile);
- }
- return null;
- }
- });
+ if (myProject.isDisposed()) return null;
+ VirtualFile vFile = ChangesUtil.findValidParentAccurately(file);
+ if (vFile != null) {
+ return getVcsRootFor(vFile);
+ }
+ return null;
}
@Override
public VcsRoot getVcsRootObjectFor(final FilePath file) {
- return ApplicationManager.getApplication().runReadAction(new Computable<VcsRoot>() {
- @Nullable
- public VcsRoot compute() {
- if (myProject.isDisposed()) {
- return null;
- }
- VirtualFile vFile = ChangesUtil.findValidParent(file);
- if (vFile != null) {
- return getVcsRootObjectFor(vFile);
- }
- return null;
- }
- });
+ if (myProject.isDisposed()) {
+ return null;
+ }
+ VirtualFile vFile = ChangesUtil.findValidParentAccurately(file);
+ if (vFile != null) {
+ return getVcsRootObjectFor(vFile);
+ }
+ return null;
}
public void unregisterVcs(AbstractVcs vcs) {
@Nullable
public VcsDirectoryMapping getDirectoryMappingFor(final FilePath path) {
- return ApplicationManager.getApplication().runReadAction(new Computable<VcsDirectoryMapping>() {
- @Nullable
- public VcsDirectoryMapping compute() {
- VirtualFile vFile = ChangesUtil.findValidParent(path);
- if (vFile != null) {
- return myMappings.getMappingFor(vFile);
- }
- return null;
- }
- });
+ VirtualFile vFile = ChangesUtil.findValidParentAccurately(path);
+ if (vFile != null) {
+ return myMappings.getMappingFor(vFile);
+ }
+ return null;
}
public boolean hasExplicitMapping(final FilePath f) {
- VirtualFile vFile = ChangesUtil.findValidParent(f);
+ VirtualFile vFile = ChangesUtil.findValidParentAccurately(f);
if (vFile == null) return false;
return hasExplicitMapping(vFile);
}
import com.intellij.cvsSupport2.application.CvsEntriesManager;
import com.intellij.cvsSupport2.connections.CvsEnvironment;
import com.intellij.cvsSupport2.history.CvsRevisionNumber;
-import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.cvsIntegration.CvsResult;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.progress.ProcessCanceledException;
import com.intellij.openapi.project.Project;
-import com.intellij.openapi.util.*;
+import com.intellij.openapi.util.Comparing;
+import com.intellij.openapi.util.Pair;
+import com.intellij.openapi.util.Ref;
+import com.intellij.openapi.util.Trinity;
import com.intellij.openapi.vcs.*;
import com.intellij.openapi.vcs.actions.VcsContextFactory;
import com.intellij.openapi.vcs.changes.ChangesUtil;
}
}
else {
- final VirtualFile validParent = ApplicationManager.getApplication().runReadAction(new Computable<VirtualFile>() {
- @Nullable
- public VirtualFile compute() {
- return ChangesUtil.findValidParent(filePath);
- }
- });
+ final VirtualFile validParent = ChangesUtil.findValidParentAccurately(filePath);
if (validParent == null) return false;
localTag = getDirectoryTag(validParent);
}
import com.intellij.openapi.fileEditor.FileDocumentManager;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.vcs.*;
import com.intellij.openapi.vcs.changes.*;
+import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.util.PairProcessor;
+import com.intellij.util.containers.Convertor;
import git4idea.GitContentRevision;
import git4idea.GitRevisionNumber;
import git4idea.GitUtil;
import git4idea.config.GitVersionSpecialty;
import org.jetbrains.annotations.NotNull;
-import java.util.Collection;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
+import java.util.*;
/**
* Git repository change provider
return;
}
- final Collection<VirtualFile> affected = dirtyScope.getAffectedContentRootsWithCheck();
- final Collection<VirtualFile> affectedContentRoots = dirtyScope.getAffectedContentRoots();
- if (affectedContentRoots.size() != affected.size()) {
- final Set<VirtualFile> set = new HashSet<VirtualFile>(affected);
- set.removeAll(affectedContentRoots);
- for (VirtualFile file : set) {
- debug("adding git root for check: " + file.getPath());
- ((VcsModifiableDirtyScope) dirtyScope).addDirtyDirRecursively(new FilePathImpl(file));
+ final LocalFileSystem lfs = LocalFileSystem.getInstance();
+ final Set<VirtualFile> rootsUnderGit = new HashSet<VirtualFile>(Arrays.asList(myVcsManager.getRootsUnderVcs(vcs)));
+ final Set<VirtualFile> inputColl = new HashSet<VirtualFile>(rootsUnderGit);
+ final Set<VirtualFile> existingInScope = new HashSet<VirtualFile>();
+ for (FilePath dir : dirtyScope.getRecursivelyDirtyDirectories()) {
+ VirtualFile vf = dir.getVirtualFile();
+ if (vf == null) {
+ vf = lfs.findFileByIoFile(dir.getIOFile());
+ }
+ if (vf == null) {
+ vf = lfs.refreshAndFindFileByIoFile(dir.getIOFile());
+ }
+ if (vf != null) {
+ existingInScope.add(vf);
+ } else {
+ PROFILE_LOG.error("Can not find virtual file for recursively dirty dir: " + dir.getIOFile().getPath());
}
}
+ inputColl.addAll(existingInScope);
+ FileUtil.removeAncestors(inputColl, new Convertor<VirtualFile, String>() {
+ @Override
+ public String convert(VirtualFile o) {
+ return o.getPath();
+ }
+ }, new PairProcessor<String, VirtualFile>() {
+ @Override
+ public boolean process(String s, VirtualFile file) {
+ if (! existingInScope.contains(file)) {
+ debug("adding git root for check: " + file.getPath());
+ ((VcsModifiableDirtyScope) dirtyScope).addDirtyDirRecursively(new FilePathImpl(file));
+ }
+ return true;
+ }
+ });
+
+ final Collection<VirtualFile> affected = dirtyScope.getAffectedContentRoots();
Collection<VirtualFile> roots = GitUtil.gitRootsForPaths(affected);
try {
--- /dev/null
+/*
+ * Copyright 2000-2012 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 git4idea.tests;
+
+import com.intellij.openapi.vcs.ProjectLevelVcsManager;
+import com.intellij.openapi.vcs.VcsDirectoryMapping;
+import com.intellij.openapi.vcs.changes.Change;
+import com.intellij.openapi.vcs.changes.ChangeListManager;
+import com.intellij.openapi.vcs.changes.LocalChangeList;
+import com.intellij.openapi.vcs.changes.VcsDirtyScopeManager;
+import com.intellij.openapi.vfs.LocalFileSystem;
+import com.intellij.openapi.vfs.VirtualFile;
+import git4idea.GitVcs;
+import git4idea.test.GitTestUtil;
+import junit.framework.Assert;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import java.io.File;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Map;
+
+/**
+ * Created with IntelliJ IDEA.
+ * User: Irina.Chernushina
+ * Date: 9/5/12
+ * Time: 10:32 AM
+ */
+public class GitChangeProviderNestedRepositoriesTest extends GitChangeProviderTest {
+ private File myChildRepoDir;
+ private GitTestRepository myChildRepo;
+ private ProjectLevelVcsManager myVcsManager;
+ private ChangeListManager myChangeListManager;
+ private Map<String,VirtualFile> myChildFiles;
+ private VcsDirtyScopeManager myDirtyScopeManager;
+
+ @BeforeMethod
+ public void setUp() throws Exception {
+ final File repoRoot = myRepo.getRootDir();
+ myChildRepoDir = new File(repoRoot, "child");
+ myChildRepoDir.mkdir();
+ myChildRepo = GitTestRepository.init(myChildRepoDir);
+ myChildRepo.setName(MAIN_USER_NAME, MAIN_USER_EMAIL);
+ LocalFileSystem.getInstance().refreshAndFindFileByIoFile(myChildRepo.getRootDir());
+ myChildRepo.refresh();
+
+ myChildFiles = GitTestUtil.createFileStructure(myProject, myChildRepo, "in1.txt", "in2.txt", "dirr/inin1.txt", "dirr/inin2.txt");
+ myChildRepo.addCommit();
+ myChildRepo.refresh();
+
+ myVcsManager = ProjectLevelVcsManager.getInstance(myProject);
+ myVcsManager.setDirectoryMappings(Arrays.asList(new VcsDirectoryMapping(myRepo.getRootDir().getPath(), GitVcs.getKey().getName()),
+ new VcsDirectoryMapping(myChildRepo.getRootDir().getPath(), GitVcs.getKey().getName())));
+ myChangeListManager = ChangeListManager.getInstance(myProject);
+ myDirtyScopeManager = VcsDirtyScopeManager.getInstance(myProject);
+ }
+
+ @Test
+ public void testReproduceChangeListsRotten() throws Exception {
+ editFileInCommand(myProject, myFiles.get("a.txt"), "123");
+ VirtualFile in1 = myChildFiles.get("in1.txt");
+ editFileInCommand(myProject, in1, "321");
+ VirtualFile in2 = myChildFiles.get("in2.txt");
+ editFileInCommand(myProject, in2, "321*");
+
+ myDirtyScopeManager.markEverythingDirty();
+ myChangeListManager.ensureUpToDate(false);
+
+ final LocalChangeList localChangeList = myChangeListManager.addChangeList("new", "new");
+ Change change1 = myChangeListManager.getChange(in1);
+ Change change2 = myChangeListManager.getChange(in2);
+ myChangeListManager.moveChangesTo(localChangeList, change1, change2);
+
+ myDirtyScopeManager.filesDirty(Collections.singletonList(in1), Collections.singletonList(myRepo.getVFRootDir()));
+ myChangeListManager.ensureUpToDate(false);
+ LocalChangeList list = myChangeListManager.getChangeList(in1);
+ Assert.assertNotNull(list);
+ Assert.assertEquals("new", list.getName());
+ LocalChangeList list2 = myChangeListManager.getChangeList(in2);
+ Assert.assertNotNull(list2);
+ Assert.assertEquals("new", list2.getName());
+ myDirtyScopeManager.markEverythingDirty();
+ myChangeListManager.ensureUpToDate(false);
+ list = myChangeListManager.getChangeList(in1);
+ Assert.assertNotNull(list);
+ Assert.assertEquals("new", list.getName());
+ list2 = myChangeListManager.getChangeList(in2);
+ Assert.assertNotNull(list2);
+ Assert.assertEquals("new", list2.getName());
+ }
+}
import com.intellij.openapi.ui.MessageType;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.util.Comparing;
-import com.intellij.openapi.util.Computable;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.vcs.*;
import com.intellij.openapi.vcs.changes.*;
myCommittedRevision = revision;
myOldFilePath = myChange.getBeforeRevision().getFile();
myNewFilePath = myChange.getAfterRevision().getFile();
- myBaseForPatch = ApplicationManager.getApplication().runReadAction(new Computable<VirtualFile>() {
- @Override
- public VirtualFile compute() {
- return ChangesUtil.findValidParent(myNewFilePath);
- }
- });
+ myBaseForPatch = ChangesUtil.findValidParentAccurately(myNewFilePath);
myOldPresentation = TreeConflictRefreshablePanel.filePath(myOldFilePath);
myNewPresentation = TreeConflictRefreshablePanel.filePath(myNewFilePath);
package org.jetbrains.idea.svn.treeConflict;
import com.intellij.history.LocalHistory;
-import com.intellij.openapi.application.ApplicationManager;
-import com.intellij.openapi.util.Computable;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.vcs.FilePath;
import com.intellij.openapi.vcs.VcsException;
import com.intellij.openapi.vcs.changes.ChangesUtil;
import com.intellij.openapi.vcs.changes.VcsDirtyScopeManager;
import com.intellij.openapi.vcs.history.VcsRevisionNumber;
-import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VirtualFile;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.idea.svn.SvnRevisionNumber;
}
private void pathDirty(final FilePath path) {
- final VirtualFile validParent = ApplicationManager.getApplication().runReadAction(new Computable<VirtualFile>() {
- @Override
- public VirtualFile compute() {
- return ChangesUtil.findValidParent(path);
- }
- });
+ final VirtualFile validParent = ChangesUtil.findValidParentAccurately(path);
if (validParent == null) return;
validParent.refresh(false, true);
if (path.isDirectory()) {