Root Checker and Root Problem Notifier

*Extension Point for rootChecker created;
    *api changed and moved form git directory to common platform;
    *platform facade remove from root subdirectory exclude integration enabler;
    * root scanner started in ProjectLevelVcsManager;
     *vcs name stored  in VcsRootError;
    *java tests created instead of groovy test;
    *UsefulTestCase class used  as a base class for test instead of light test;
    *platform facade removed (except IntegrationEnablerTest);
     *IntegrationsEnabler test switched off;
    *check that chosen vcs in mapping root is supported for this kind of root;
    *filter git roots in integration enabler
    *do not start scanner in unit test mode or if there are no root checkers in
This commit is contained in:
Nadya Zabrodina
2013-11-04 15:56:59 +04:00
parent f519dd3ae7
commit e4180e6ee6
26 changed files with 1061 additions and 874 deletions
@@ -2,6 +2,8 @@
<extensionPoints>
<extensionPoint name="checkoutProvider"
interface="com.intellij.openapi.vcs.CheckoutProvider"/>
<extensionPoint name="vcsRootChecker"
interface="com.intellij.openapi.vcs.VcsRootChecker"/>
<extensionPoint name="checkoutListener"
interface="com.intellij.openapi.vcs.checkout.CheckoutListener"/>
<extensionPoint name="vcsAwareCheckoutListener"
@@ -429,11 +429,6 @@ public abstract class AbstractVcs<ComList extends CommittedChangeList> extends S
return null;
}
@Nullable
public VcsRootChecker getRootChecker() {
return null;
}
@Nullable
public VcsRootSettings createEmptyVcsRootSettings() {
return null;
@@ -15,9 +15,10 @@
*/
package com.intellij.openapi.vcs;
import com.intellij.openapi.extensions.ExtensionPointName;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import java.util.Collection;
import org.jetbrains.annotations.Nullable;
/**
* Checks VCS roots, revealing invalid roots (registered in the settings, but not related to real VCS roots on disk)
@@ -25,18 +26,31 @@ import java.util.Collection;
*
* @author Kirill Likhodedov
*/
public interface VcsRootChecker {
public abstract class VcsRootChecker {
@NonNls public static final ExtensionPointName<VcsRootChecker> EXTENSION_POINT_NAME =
new ExtensionPointName<VcsRootChecker>("com.intellij.vcsRootChecker");
/**
* @return Paths to VCS roots which are not registered in the Settings | Version Control.
* @param path path to check if it is vcs root directory
* @return true if it is vcs root
*/
@NotNull
Collection<String> getUnregisteredRoots();
public boolean isRoot(@NotNull String path) {
return false;
}
/**
* @return - return vcs for current checker
*/
public abstract VcsKey getSupportedVcs();
/**
* Check if the "dot" directory changed during scan
*
* @param directory root to be checked.
* @return true if the given directory is not a VCS root.
* @param path - path to check
* @return true if it is a DOT_DIR
*/
boolean isInvalidMapping(@NotNull VcsDirectoryMapping mapping);
public boolean isVcsDir(@Nullable String path) {
return false;
}
}
@@ -20,24 +20,24 @@ import org.jetbrains.annotations.NotNull;
/**
* Incorrect VCS root definition: either an unregistered, or an incorrectly registered VCS root.
*
* @see VcsRootChecker
* @see AbstractVcs#getRootChecker
*
* @author Kirill Likhodedov
* @see VcsRootChecker
*/
public class VcsRootError {
private final @NotNull Type myType;
private final @NotNull String myMapping;
private final @NotNull VcsKey myVcsKey;
public enum Type {
EXTRA_MAPPING,
UNREGISTERED_ROOT
}
public VcsRootError(@NotNull Type type, @NotNull String mapping) {
public VcsRootError(@NotNull Type type, @NotNull String mapping, @NotNull String key) {
myType = type;
myMapping = mapping;
myVcsKey = new VcsKey(key);
}
@NotNull
@@ -50,6 +50,11 @@ public class VcsRootError {
return myMapping;
}
@NotNull
public VcsKey getVcsKey() {
return myVcsKey;
}
@Override
public String toString() {
return String.format("VcsRootError{%s - %s}", myType, myMapping);
@@ -17,6 +17,7 @@
package com.intellij.openapi.vcs.configurable;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.extensions.Extensions;
import com.intellij.openapi.options.Configurable;
import com.intellij.openapi.options.ConfigurationException;
import com.intellij.openapi.project.Project;
@@ -25,6 +26,7 @@ import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vcs.*;
import com.intellij.openapi.vcs.impl.DefaultVcsRootPolicy;
import com.intellij.openapi.vcs.impl.VcsDescriptor;
import com.intellij.openapi.vcs.roots.VcsRootErrorsFinder;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.ui.*;
import com.intellij.ui.components.JBLabel;
@@ -125,7 +127,8 @@ public class VcsDirectoryConfigurationPanel extends JPanel implements Configurab
private boolean mappingIsError(VcsDirectoryMapping mapping) {
String vcs = mapping.getVcs();
VcsRootChecker checker = myCheckers.get(vcs);
return checker != null && checker.isInvalidMapping(mapping);
return checker != null &&
(mapping.isDefaultMapping() ? !checker.isRoot(myProject.getBasePath()) : !checker.isRoot(mapping.getDirectory()));
}
}
@@ -263,16 +266,14 @@ public class VcsDirectoryConfigurationPanel extends JPanel implements Configurab
private void updateRootCheckers() {
myCheckers.clear();
for (VcsDescriptor descriptor : myVcsManager.getAllVcss()) {
String name = descriptor.getName();
AbstractVcs vcs = myVcsManager.findVcsByName(name);
VcsRootChecker[] checkers = Extensions.getExtensions(VcsRootChecker.EXTENSION_POINT_NAME);
for (VcsRootChecker checker : checkers) {
VcsKey key = checker.getSupportedVcs();
AbstractVcs vcs = myVcsManager.findVcsByName(key.getName());
if (vcs == null) {
continue;
}
VcsRootChecker checker = vcs.getRootChecker();
if (checker != null) {
myCheckers.put(name, checker);
}
myCheckers.put(key.getName(), checker);
}
}
@@ -416,27 +417,28 @@ public class VcsDirectoryConfigurationPanel extends JPanel implements Configurab
final JComponent errorPanel = Box.createVerticalBox();
final JBScrollPane pane = new JBScrollPane(errorPanel);
for (Map.Entry<String, VcsRootChecker> entry : myCheckers.entrySet()) {
VcsRootChecker checker = entry.getValue();
for (final String root : checker.getUnregisteredRoots()) {
final String vcs = entry.getKey();
String title = "Unregistered " + vcs + " root: " + FileUtil.toSystemDependentName(root);
final VcsRootErrorLabel vcsRootErrorLabel = new VcsRootErrorLabel(title);
vcsRootErrorLabel.setAddRootLinkHandler(new Runnable() {
@Override
public void run() {
addMapping(new VcsDirectoryMapping(root, vcs));
errorPanel.remove(vcsRootErrorLabel);
if (errorPanel.getComponentCount() == 0) {
pane.setVisible(false);
}
pane.setMinimumSize(new Dimension(-1, calcMinHeight(errorPanel, DEFAULT_HEIGHT)));
validate();
Collection<VcsRootError> myErrors = VcsRootErrorsFinder.getInstance(myProject).find();
for (final VcsRootError root : myErrors) {
final VcsKey vcsKey = root.getVcsKey();
final VcsDescriptor vcsDescriptor = myAllVcss.get(vcsKey.getName());
String displayVcsName = vcsDescriptor.getDisplayName();
String title = "Unregistered " + displayVcsName + " root: " + FileUtil.toSystemDependentName(root.getMapping());
final VcsRootErrorLabel vcsRootErrorLabel = new VcsRootErrorLabel(title);
vcsRootErrorLabel.setAddRootLinkHandler(new Runnable() {
@Override
public void run() {
addMapping(new VcsDirectoryMapping(root.getMapping(), vcsKey.getName()));
errorPanel.remove(vcsRootErrorLabel);
if (errorPanel.getComponentCount() == 0) {
pane.setVisible(false);
}
});
errorPanel.add(vcsRootErrorLabel);
}
pane.setMinimumSize(new Dimension(-1, calcMinHeight(errorPanel, DEFAULT_HEIGHT)));
validate();
}
});
errorPanel.add(vcsRootErrorLabel);
}
if (errorPanel.getComponentCount() == 0) {
pane.setVisible(false);
}
@@ -26,6 +26,7 @@ import com.intellij.openapi.editor.EditorFactory;
import com.intellij.openapi.editor.EditorSettings;
import com.intellij.openapi.editor.ex.EditorEx;
import com.intellij.openapi.editor.markup.TextAttributes;
import com.intellij.openapi.extensions.Extensions;
import com.intellij.openapi.progress.ProcessCanceledException;
import com.intellij.openapi.project.DumbAwareRunnable;
import com.intellij.openapi.project.Project;
@@ -44,6 +45,7 @@ import com.intellij.openapi.vcs.checkout.CompositeCheckoutListener;
import com.intellij.openapi.vcs.ex.ProjectLevelVcsManagerEx;
import com.intellij.openapi.vcs.history.VcsHistoryCache;
import com.intellij.openapi.vcs.impl.projectlevelman.*;
import com.intellij.openapi.vcs.roots.VcsRootScanner;
import com.intellij.openapi.vcs.update.ActionInfo;
import com.intellij.openapi.vcs.update.UpdateInfoTree;
import com.intellij.openapi.vcs.update.UpdatedFiles;
@@ -240,6 +242,12 @@ public class ProjectLevelVcsManagerImpl extends ProjectLevelVcsManagerEx impleme
else {
myContentManager = ContentFactory.SERVICE.getInstance().createContentManager(true, myProject);
}
if (!ApplicationManager.getApplication().isUnitTestMode()) {
VcsRootChecker[] checkers = Extensions.getExtensions(VcsRootChecker.EXTENSION_POINT_NAME);
if (checkers.length != 0) {
VcsRootScanner.start(myProject, checkers);
}
}
}
});
}
@@ -1,54 +1,37 @@
/*
* 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 com.intellij.openapi.vcs.roots;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vcs.VcsRoot;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.Collection;
/**
* The result of root detection.
*
* @author Kirill Likhodedov
* @author Nadya Zabrodina
*/
public class VcsRootDetectInfo {
private final @NotNull Collection<VirtualFile> myRoots;
private final @NotNull Collection<VcsRoot> myRoots;
private final boolean myFull;
private final boolean myBelow;
/**
* @param roots Git roots important for the project.
* @param full Pass true to indicate that the project is fully under Git.
* @param below Pass true to indicate that the project dir is below Git dir,
* i.e. .git is above the project dir, and there is no .git directly under the project dir.
* @param roots Vcs roots important for the project.
* @param full Pass true to indicate that the project is fully under Vcs.
* @param below Pass true to indicate that the project dir is below Vcs dir,
* i.e. .git is above the project dir, and there is no DOT dir directly under the project dir.
*/
public VcsRootDetectInfo(@NotNull Collection<VirtualFile> roots, boolean full, boolean below) {
myRoots = new ArrayList<VirtualFile>(roots);
public VcsRootDetectInfo(@NotNull Collection<VcsRoot> roots, boolean full, boolean below) {
myRoots = new ArrayList<VcsRoot>(roots);
myFull = full;
myBelow = below;
}
/**
* @return True if the project is fully under Git.
* It is true if .git is directly inside or above the project dir.
* @return True if the project is fully under Vcs.
* It is true if f.e. .git is directly inside or above the project dir.
*/
public boolean totallyUnderGit() {
public boolean totallyUnderVcs() {
return myFull;
}
@@ -57,17 +40,17 @@ public class VcsRootDetectInfo {
}
@NotNull
public Collection<VirtualFile> getRoots() {
return new ArrayList<VirtualFile>(myRoots);
public Collection<VcsRoot> getRoots() {
return new ArrayList<VcsRoot>(myRoots);
}
/**
* Below implies totally under Git.
* @return true if the uppermost interesting Git root is above the project dir,
* false if all .git directories are immediately under the project dir or deeper.
* Below implies totally under Vcs.
*
* @return true if the uppermost interesting Vcs root is above the project dir,
* false if all vcs internal directories are immediately under the project dir or deeper.
*/
public boolean projectIsBelowGit() {
public boolean projectIsBelowVcs() {
return myBelow;
}
}
@@ -1,53 +1,32 @@
/*
* 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 com.intellij.openapi.vcs.roots;
import com.intellij.openapi.extensions.Extensions;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.ProjectRootManager;
import com.intellij.openapi.vcs.roots.VcsRootDetectInfo;
import com.intellij.openapi.vcs.AbstractVcs;
import com.intellij.openapi.vcs.ProjectLevelVcsManager;
import com.intellij.openapi.vcs.VcsRoot;
import com.intellij.openapi.vcs.VcsRootChecker;
import com.intellij.openapi.vfs.VirtualFile;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.*;
/**
* <p>
* Scans the file system looking for Git roots, which contain the Project or its parts,
* and returns the information enclosed to the {@link com.intellij.openapi.vcs.roots.VcsRootDetectInfo}.
* The main part of the information are Git roots which will be proposed to the user to be added as VCS roots.
* </p>
* <p>
* Linked sources are not scanned. User should add Git roots for them explicitly.
* </p>
*
* @author Kirill Likhodedov
* @author Nadya Zabrodina
*/
public class VcsRootDetector {
private static final int MAXIMUM_SCAN_DEPTH = 2;
@NotNull private final Project myProject;
@NotNull private final ProjectRootManager myProjectManager;
@NotNull private final ProjectLevelVcsManager myVcsManager;
public VcsRootDetector(@NotNull Project project) {
myProject = project;
myProjectManager = ProjectRootManager.getInstance(project);
myVcsManager = ProjectLevelVcsManager.getInstance(project);
}
@NotNull
@@ -58,43 +37,49 @@ public class VcsRootDetector {
@NotNull
public VcsRootDetectInfo detect(@Nullable VirtualFile startDir) {
if (startDir == null) {
return new VcsRootDetectInfo(Collections.<VirtualFile>emptyList(), false, false);
return new VcsRootDetectInfo(Collections.<VcsRoot>emptyList(), false, false);
}
final Set<VirtualFile> roots = scanForRootsInsideDir(startDir);
final Set<VcsRoot> roots = scanForRootsInsideDir(startDir);
roots.addAll(scanForRootsInContentRoots());
if (roots.contains(startDir)) {
return new VcsRootDetectInfo(roots, true, false);
for (VcsRoot root : roots) {
if (startDir.equals(root.getPath())) {
return new VcsRootDetectInfo(roots, true, false);
}
}
VirtualFile rootAbove = scanForSingleRootAboveDir(startDir);
if (rootAbove != null) {
roots.add(rootAbove);
List<VcsRoot> rootsAbove = scanForSingleRootAboveDir(startDir);
if (!rootsAbove.isEmpty()) {
roots.addAll(rootsAbove);
return new VcsRootDetectInfo(roots, true, true);
}
return new VcsRootDetectInfo(roots, false, false);
}
private Set<VirtualFile> scanForRootsInContentRoots() {
Set<VirtualFile> gitRoots = new HashSet<VirtualFile>();
@NotNull
private Set<VcsRoot> scanForRootsInContentRoots() {
Set<VcsRoot> gitRoots = new HashSet<VcsRoot>();
VirtualFile[] roots = myProjectManager.getContentRoots();
for (VirtualFile contentRoot : roots) {
Set<VirtualFile> rootsInsideRoot = scanForRootsInsideDir(contentRoot);
if (!rootsInsideRoot.contains(contentRoot)) {
VirtualFile rootAbove = scanForSingleRootAboveDir(contentRoot);
if (rootAbove != null) {
rootsInsideRoot.add(rootAbove);
Set<VcsRoot> rootsInsideRoot = scanForRootsInsideDir(contentRoot);
boolean shouldScanAbove = true;
for (VcsRoot root : rootsInsideRoot) {
if (contentRoot.equals(root.getPath())) {
shouldScanAbove = false;
}
}
if (shouldScanAbove) {
List<VcsRoot> rootsAbove = scanForSingleRootAboveDir(contentRoot);
rootsInsideRoot.addAll(rootsAbove);
}
gitRoots.addAll(rootsInsideRoot);
}
return gitRoots;
}
@NotNull
private Set<VirtualFile> scanForRootsInsideDir(@NotNull final VirtualFile dir, final int depth) {
final Set<VirtualFile> roots = new HashSet<VirtualFile>();
private Set<VcsRoot> scanForRootsInsideDir(@NotNull final VirtualFile dir, final int depth) {
final Set<VcsRoot> roots = new HashSet<VcsRoot>();
if (depth > MAXIMUM_SCAN_DEPTH) {
// performance optimization via limitation: don't scan deep though the whole VFS, 2 levels under a content root is enough
return roots;
@@ -103,8 +88,9 @@ public class VcsRootDetector {
if (myProject.isDisposed() || !dir.isDirectory()) {
return roots;
}
if (hasGitDir(dir)) {
roots.add(dir);
List<AbstractVcs> vcsList = getVcsListFor(dir);
for (AbstractVcs vcs : vcsList) {
roots.add(new VcsRoot(vcs, dir));
}
for (VirtualFile child : dir.getChildren()) {
roots.addAll(scanForRootsInsideDir(child, depth + 1));
@@ -113,28 +99,40 @@ public class VcsRootDetector {
}
@NotNull
private Set<VirtualFile> scanForRootsInsideDir(@NotNull VirtualFile dir) {
private Set<VcsRoot> scanForRootsInsideDir(@NotNull VirtualFile dir) {
return scanForRootsInsideDir(dir, 0);
}
@Nullable
private VirtualFile scanForSingleRootAboveDir(@NotNull final VirtualFile dir) {
@NotNull
private List<VcsRoot> scanForSingleRootAboveDir(@NotNull final VirtualFile dir) {
List<VcsRoot> roots = new ArrayList<VcsRoot>();
if (myProject.isDisposed()) {
return null;
return roots;
}
VirtualFile par = dir.getParent();
while (par != null) {
if (hasGitDir(par)) {
return par;
List<AbstractVcs> vcsList = getVcsListFor(par);
for (AbstractVcs vcs : vcsList) {
roots.add(new VcsRoot(vcs, par));
}
if (!roots.isEmpty()) {
return roots;
}
par = par.getParent();
}
return null;
return roots;
}
private static boolean hasGitDir(@NotNull VirtualFile dir) {
VirtualFile gitDir = dir.findChild(".git");
return gitDir != null && gitDir.exists();
@NotNull
private List<AbstractVcs> getVcsListFor(@NotNull VirtualFile dir) {
VcsRootChecker[] checkers = Extensions.getExtensions(VcsRootChecker.EXTENSION_POINT_NAME);
List<AbstractVcs> vcsList = new ArrayList<AbstractVcs>();
for (VcsRootChecker checker : checkers) {
if (checker.isRoot(dir.getPath())) {
vcsList.add(myVcsManager.findVcsByName(checker.getSupportedVcs().getName()));
}
}
return vcsList;
}
}
@@ -1,40 +1,24 @@
/*
* 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 com.intellij.openapi.vcs.roots;
import com.intellij.openapi.extensions.Extensions;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Condition;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.vcs.ProjectLevelVcsManager;
import com.intellij.openapi.vcs.VcsDirectoryMapping;
import com.intellij.openapi.vcs.VcsRootError;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vcs.*;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.Function;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
/**
* Detects actual Git roots and compares them to the ones registered in Settings | Version Control.
*
* @author Kirill Likhodedov
* @author Nadya Zabrodina
*/
public class VcsRootErrorsFinder {
private final @NotNull Project myProject;
private final @NotNull ProjectLevelVcsManager myVcsManager;
@@ -46,61 +30,67 @@ public class VcsRootErrorsFinder {
@NotNull
public Collection<VcsRootError> find() {
List<VcsDirectoryMapping> mappings = myVcsManager.getDirectoryMappings();
Collection<VirtualFile> gitRoots = new VcsRootDetector(myProject).detect().getRoots();
Collection<VcsRoot> vcsRoots = new VcsRootDetector(myProject).detect().getRoots();
Collection<VcsRootError> errors = new ArrayList<VcsRootError>();
Collection<String> gitPaths = rootsToPaths(gitRoots);
errors.addAll(findExtraMappings(mappings, gitPaths));
errors.addAll(findUnregisteredRoots(mappings, gitPaths));
errors.addAll(findExtraMappings(mappings, vcsRoots.isEmpty()));
errors.addAll(findUnregisteredRoots(mappings, vcsRoots));
return errors;
}
private Collection<VcsRootError> findUnregisteredRoots(List<VcsDirectoryMapping> mappings, Collection<String> gitPaths) {
@NotNull
private Collection<VcsRootError> findUnregisteredRoots(@NotNull List<VcsDirectoryMapping> mappings,
@NotNull Collection<VcsRoot> vcsRoots) {
Collection<VcsRootError> errors = new ArrayList<VcsRootError>();
List<String> mappedPaths = mappingsToPaths(mappings);
for (String gitPath : gitPaths) {
if (!mappedPaths.contains(gitPath)) {
errors.add(new VcsRootError(VcsRootError.Type.UNREGISTERED_ROOT, gitPath));
List<String> mappedPaths = mappingsToPathsWithSelectedVcs(mappings);
for (VcsRoot root : vcsRoots) {
VirtualFile virtualFileFromRoot = root.getPath();
if (virtualFileFromRoot == null) {
continue;
}
String vcsPath = virtualFileFromRoot.getPath();
if (!mappedPaths.contains(vcsPath) && root.getVcs() != null) {
errors.add(new VcsRootError(VcsRootError.Type.UNREGISTERED_ROOT, vcsPath, root.getVcs().getName()));
}
}
return errors;
}
private static Collection<VcsRootError> findExtraMappings(List<VcsDirectoryMapping> mappings, Collection<String> gitPaths) {
@NotNull
private Collection<VcsRootError> findExtraMappings(@NotNull List<VcsDirectoryMapping> mappings, boolean isEmptyVcsRoots) {
Collection<VcsRootError> errors = new ArrayList<VcsRootError>();
for (VcsDirectoryMapping mapping : mappings) {
if (mapping.isDefaultMapping()) {
if (gitPaths.isEmpty()) {
errors.add(new VcsRootError(VcsRootError.Type.EXTRA_MAPPING, VcsDirectoryMapping.PROJECT_CONSTANT));
if (isEmptyVcsRoots && !StringUtil.isEmptyOrSpaces(mapping.getVcs())) {
errors.add(new VcsRootError(VcsRootError.Type.EXTRA_MAPPING, VcsDirectoryMapping.PROJECT_CONSTANT, mapping.getVcs()));
}
}
else {
String mappedPath = mapping.systemIndependentPath();
if (!gitPaths.contains(mappedPath) && !hasGitDir(mappedPath)) {
errors.add(new VcsRootError(VcsRootError.Type.EXTRA_MAPPING, mappedPath));
if (!isRoot(mapping)) {
errors.add(new VcsRootError(VcsRootError.Type.EXTRA_MAPPING, mappedPath, mapping.getVcs()));
}
}
}
return errors;
}
private static boolean hasGitDir(String path) {
File file = new File(path, ".git");
return file.exists();
}
@NotNull
private static Collection<String> rootsToPaths(@NotNull Collection<VirtualFile> gitRoots) {
Collection<String> gitPaths = new ArrayList<String>(gitRoots.size());
for (VirtualFile root : gitRoots) {
gitPaths.add(root.getPath());
}
return gitPaths;
public static Collection<VirtualFile> vcsRootsToVirtualFiles(@NotNull Collection<VcsRoot> vcsRoots) {
return ContainerUtil.map(vcsRoots, new Function<VcsRoot, VirtualFile>() {
@Override
public VirtualFile fun(VcsRoot root) {
return root.getPath();
}
});
}
private List<String> mappingsToPaths(List<VcsDirectoryMapping> mappings) {
private List<String> mappingsToPathsWithSelectedVcs(@NotNull List<VcsDirectoryMapping> mappings) {
List<String> paths = new ArrayList<String>();
for (VcsDirectoryMapping mapping : mappings) {
if (StringUtil.isEmptyOrSpaces(mapping.getVcs())) {
continue;
}
if (!mapping.isDefaultMapping()) {
paths.add(mapping.systemIndependentPath());
}
@@ -113,4 +103,19 @@ public class VcsRootErrorsFinder {
}
return paths;
}
public static VcsRootErrorsFinder getInstance(Project project) {
return new VcsRootErrorsFinder(project);
}
private boolean isRoot(@NotNull final VcsDirectoryMapping mapping) {
VcsRootChecker[] checkers = Extensions.getExtensions(VcsRootChecker.EXTENSION_POINT_NAME);
final String pathToCheck = mapping.isDefaultMapping() ? myProject.getBasePath() : mapping.getDirectory();
return ContainerUtil.find(checkers, new Condition<VcsRootChecker>() {
@Override
public boolean value(VcsRootChecker checker) {
return checker.getSupportedVcs().getName().equals(mapping.getVcs()) && checker.isRoot(pathToCheck);
}
}) != null;
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2012 JetBrains s.r.o.
* Copyright 2000-2013 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.
@@ -13,15 +13,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package git4idea.roots;
package com.intellij.openapi.vcs.roots;
import com.intellij.idea.ActionsBundle;
import com.intellij.notification.Notification;
import com.intellij.notification.NotificationGroup;
import com.intellij.notification.NotificationListener;
import com.intellij.notification.NotificationType;
import com.intellij.notification.*;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.components.ServiceManager;
import com.intellij.openapi.options.ShowSettingsUtil;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.io.FileUtil;
@@ -29,9 +25,7 @@ import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vcs.VcsConfiguration;
import com.intellij.openapi.vcs.VcsDirectoryMapping;
import com.intellij.openapi.vcs.VcsRootError;
import com.intellij.openapi.vcs.roots.VcsRootErrorsFinder;
import com.intellij.util.Function;
import git4idea.GitPlatformFacade;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -42,33 +36,31 @@ import java.util.Collection;
import static com.intellij.notification.NotificationType.ERROR;
import static com.intellij.notification.NotificationType.INFORMATION;
import static com.intellij.openapi.util.text.StringUtil.pluralize;
import static git4idea.GitVcs.IMPORTANT_ERROR_NOTIFICATION;
import static git4idea.GitVcs.MINOR_NOTIFICATION;
import static git4idea.Notificator.createNotification;
/**
* Searches for Git roots problems via {@link com.intellij.openapi.vcs.roots.VcsRootErrorsFinder} and notifies about them.
* Searches for Vcs roots problems via {@link VcsRootErrorsFinder} and notifies about them.
*
* @author Kirill Likhodedov
* @author Nadya Zabrodina
*/
public class GitRootProblemNotifier {
public class VcsRootProblemNotifier {
private final @NotNull Project myProject;
private final @NotNull GitPlatformFacade myPlatformFacade;
private final @NotNull VcsConfiguration mySettings;
private @Nullable Notification myNotification;
private final @NotNull Object NOTIFICATION_LOCK = new Object();
public static GitRootProblemNotifier getInstance(@NotNull Project project) {
return ServiceManager.getService(project, GitRootProblemNotifier.class);
public static final NotificationGroup IMPORTANT_ERROR_NOTIFICATION = new NotificationGroup(
"Vcs Important Messages", NotificationDisplayType.STICKY_BALLOON, true);
public static final NotificationGroup MINOR_NOTIFICATION = new NotificationGroup(
"Vcs Minor Notifications", NotificationDisplayType.BALLOON, true);
public static VcsRootProblemNotifier getInstance(@NotNull Project project) {
return new VcsRootProblemNotifier(project);
}
// registered as a project service
@SuppressWarnings("UnusedDeclaration")
private GitRootProblemNotifier(@NotNull Project project, @NotNull GitPlatformFacade platformFacade) {
private VcsRootProblemNotifier(@NotNull Project project) {
myProject = project;
myPlatformFacade = platformFacade;
mySettings = VcsConfiguration.getInstance(myProject);
}
@@ -85,8 +77,8 @@ public class GitRootProblemNotifier {
return;
}
Collection<String> unregisteredRoots = getUnregisteredRoots(errors);
Collection<String> invalidRoots = getInvalidRoots(errors);
Collection<VcsRootError> unregisteredRoots = getUnregisteredRoots(errors);
Collection<VcsRootError> invalidRoots = getInvalidRoots(errors);
String title = makeTitle(unregisteredRoots, invalidRoots);
String description = makeDescription(unregisteredRoots, invalidRoots);
@@ -95,9 +87,9 @@ public class GitRootProblemNotifier {
expireNotification();
NotificationGroup notificationGroup = invalidRoots.isEmpty() ? MINOR_NOTIFICATION : IMPORTANT_ERROR_NOTIFICATION;
NotificationType notificationType = invalidRoots.isEmpty() ? INFORMATION : ERROR;
myNotification = createNotification(notificationGroup, title, description, notificationType,
new MyNotificationListener(myProject, mySettings));
myPlatformFacade.getNotificator(myProject).notify(myNotification);
myNotification = notificationGroup.createNotification(title, description, notificationType,
new MyNotificationListener(myProject, mySettings));
myNotification.notify(myProject);
}
}
@@ -121,25 +113,29 @@ public class GitRootProblemNotifier {
}
@SuppressWarnings("StringConcatenationInsideStringBufferAppend")
private static String makeDescription(@NotNull Collection<String> unregisteredRoots, @NotNull Collection<String> invalidRoots) {
Function<String, String> rootToDisplayableString = new Function<String, String>() {
@NotNull
private static String makeDescription(@NotNull Collection<VcsRootError> unregisteredRoots,
@NotNull Collection<VcsRootError> invalidRoots) {
Function<VcsRootError, String> rootToDisplayableString = new Function<VcsRootError, String>() {
@Override
public String fun(String path) {
if (path.equals(VcsDirectoryMapping.PROJECT_CONSTANT)) {
return StringUtil.escapeXml(path);
public String fun(VcsRootError rootError) {
if (rootError.getMapping().equals(VcsDirectoryMapping.PROJECT_CONSTANT)) {
return StringUtil.escapeXml(rootError.getMapping());
}
return FileUtil.toSystemDependentName(path);
return FileUtil.toSystemDependentName(rootError.getMapping());
}
};
StringBuilder description = new StringBuilder();
if (!invalidRoots.isEmpty()) {
if (invalidRoots.size() == 1) {
description.append("The directory " + rootToDisplayableString.fun(invalidRoots.iterator().next()) + " is registered as a Git root, " +
"but no Git repositories were found there.");
VcsRootError rootError = invalidRoots.iterator().next();
description
.append("The directory " + rootToDisplayableString.fun(rootError) + " is registered as a " + rootError.getVcsKey() + " root, " +
"but no " + rootError.getVcsKey() + " repositories were found there.");
}
else {
description.append("The following directories are registered as Git roots, but they are not: <br/>" +
description.append("The following directories are registered as Vcs roots, but they are not: <br/>" +
StringUtil.join(invalidRoots, rootToDisplayableString, ", "));
}
description.append("<br/>");
@@ -147,11 +143,13 @@ public class GitRootProblemNotifier {
if (!unregisteredRoots.isEmpty()) {
if (unregisteredRoots.size() == 1) {
description.append("The directory " + rootToDisplayableString.fun(unregisteredRoots.iterator().next()) + " is under Git, " +
"but is not registered in the Settings.");
VcsRootError unregisteredRoot = unregisteredRoots.iterator().next();
description
.append("The directory " + rootToDisplayableString.fun(unregisteredRoot) + " is under " + unregisteredRoot.getVcsKey() + ", " +
"but is not registered in the Settings.");
}
else {
description.append("The following directories are roots of Git repositories, but they are not registered in the Settings: <br/>" +
description.append("The following directories are roots of Vcs repositories, but they are not registered in the Settings: <br/>" +
StringUtil.join(unregisteredRoots, rootToDisplayableString, ", "));
}
description.append("<br/>");
@@ -163,36 +161,36 @@ public class GitRootProblemNotifier {
}
@NotNull
private static String makeTitle(@NotNull Collection<String> unregisteredRoots, @NotNull Collection<String> invalidRoots) {
private static String makeTitle(@NotNull Collection<VcsRootError> unregisteredRoots, @NotNull Collection<VcsRootError> invalidRoots) {
String title;
if (unregisteredRoots.isEmpty()) {
title = "Invalid Git root " + pluralize("mapping", invalidRoots.size());
title = "Invalid Vcs root " + pluralize("mapping", invalidRoots.size());
}
else if (invalidRoots.isEmpty()) {
title = "Unregistered Git " + pluralize("root", unregisteredRoots.size()) + " detected";
title = "Unregistered Vcs " + pluralize("root", unregisteredRoots.size()) + " detected";
}
else {
title = "Git root configuration problems";
title = "Vcs root configuration problems";
}
return title;
}
@NotNull
private static Collection<String> getUnregisteredRoots(@NotNull Collection<VcsRootError> errors) {
private static Collection<VcsRootError> getUnregisteredRoots(@NotNull Collection<VcsRootError> errors) {
return filterErrorsByType(errors, VcsRootError.Type.UNREGISTERED_ROOT);
}
@NotNull
private static Collection<String> getInvalidRoots(@NotNull Collection<VcsRootError> errors) {
private static Collection<VcsRootError> getInvalidRoots(@NotNull Collection<VcsRootError> errors) {
return filterErrorsByType(errors, VcsRootError.Type.EXTRA_MAPPING);
}
@NotNull
private static Collection<String> filterErrorsByType(@NotNull Collection<VcsRootError> errors, @NotNull VcsRootError.Type type) {
Collection<String> roots = new ArrayList<String>();
private static Collection<VcsRootError> filterErrorsByType(@NotNull Collection<VcsRootError> errors, @NotNull VcsRootError.Type type) {
Collection<VcsRootError> roots = new ArrayList<VcsRootError>();
for (VcsRootError error : errors) {
if (error.getType() == type) {
roots.add(error.getMapping());
roots.add(error);
}
}
return roots;
@@ -213,7 +211,7 @@ public class GitRootProblemNotifier {
if (event.getEventType() == HyperlinkEvent.EventType.ACTIVATED) {
if (event.getDescription().equals("configure") && !myProject.isDisposed()) {
ShowSettingsUtil.getInstance().showSettingsDialog(myProject, ActionsBundle.message("group.VcsGroup.text"));
Collection<VcsRootError> errorsAfterPossibleFix = GitRootProblemNotifier.getInstance(myProject).scan();
Collection<VcsRootError> errorsAfterPossibleFix = getInstance(myProject).scan();
if (errorsAfterPossibleFix.isEmpty() && !notification.isExpired()) {
notification.expire();
}
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2012 JetBrains s.r.o.
* Copyright 2000-2013 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.
@@ -13,10 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package git4idea.roots;
package com.intellij.openapi.vcs.roots;
import com.intellij.ProjectTopics;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.project.DumbAwareRunnable;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.ModuleRootEvent;
@@ -24,23 +23,24 @@ import com.intellij.openapi.roots.ModuleRootListener;
import com.intellij.openapi.startup.StartupManager;
import com.intellij.openapi.vcs.ProjectLevelVcsManager;
import com.intellij.openapi.vcs.VcsListener;
import com.intellij.openapi.vcs.VcsRootChecker;
import com.intellij.openapi.vfs.VirtualFileManager;
import com.intellij.openapi.vfs.newvfs.BulkFileListener;
import com.intellij.openapi.vfs.newvfs.events.VFileEvent;
import com.intellij.util.Alarm;
import com.intellij.util.messages.MessageBus;
import git4idea.GitUtil;
import org.jetbrains.annotations.NotNull;
import java.util.List;
import java.util.concurrent.TimeUnit;
/**
* @author Kirill Likhodedov
* @author Nadya Zabrodina
*/
public class GitRootScanner implements BulkFileListener, ModuleRootListener, VcsListener {
public class VcsRootScanner implements BulkFileListener, ModuleRootListener, VcsListener {
@NotNull private final GitRootProblemNotifier myRootProblemNotifier;
@NotNull private final VcsRootProblemNotifier myRootProblemNotifier;
@NotNull private final VcsRootChecker[] myCheckers;
private volatile boolean myProjectIsInitialized;
private volatile boolean myMappingsAreReady;
@@ -48,12 +48,13 @@ public class GitRootScanner implements BulkFileListener, ModuleRootListener, Vcs
@NotNull private final Alarm myAlarm;
private static final long WAIT_BEFORE_SCAN = TimeUnit.SECONDS.toMillis(1);
public static void start(@NotNull Project project) {
new GitRootScanner(project);
public static void start(@NotNull Project project, @NotNull VcsRootChecker[] checkers) {
new VcsRootScanner(project, checkers);
}
private GitRootScanner(@NotNull Project project) {
myRootProblemNotifier = GitRootProblemNotifier.getInstance(project);
private VcsRootScanner(@NotNull Project project, @NotNull VcsRootChecker[] checkers) {
myRootProblemNotifier = VcsRootProblemNotifier.getInstance(project);
myCheckers = checkers;
StartupManager.getInstance(project).runWhenProjectIsInitialized(new DumbAwareRunnable() {
@Override
@@ -79,8 +80,11 @@ public class GitRootScanner implements BulkFileListener, ModuleRootListener, Vcs
public void after(@NotNull List<? extends VFileEvent> events) {
for (VFileEvent event : events) {
String filePath = event.getPath();
if (filePath != null && filePath.toLowerCase().endsWith(GitUtil.DOT_GIT)) {
scanIfReady();
for (VcsRootChecker checker : myCheckers) {
if (checker.isVcsDir(filePath)) {
scanIfReady();
break;
}
}
}
}
@@ -123,5 +127,4 @@ public class GitRootScanner implements BulkFileListener, ModuleRootListener, Vcs
}
}, WAIT_BEFORE_SCAN);
}
}
+1 -2
View File
@@ -109,6 +109,7 @@
<errorHandler implementation="com.intellij.diagnostic.ITNReporter"/>
<vcs name="Git" vcsClass="git4idea.GitVcs" displayName="Git" administrativeAreaName=".git"/>
<checkoutProvider implementation="git4idea.checkout.GitCheckoutProvider"/>
<vcsRootChecker implementation="git4idea.roots.GitRootChecker"/>
<vcsPopupProvider implementation="git4idea.actions.GitQuickListContentProvider"/>
<vcsCheckinHandlerFactory implementation="git4idea.checkin.GitCheckinHandlerFactory"/>
<checkinHandlerFactory implementation="git4idea.checkin.UnresolvedMergeCheckFactory"/>
@@ -160,8 +161,6 @@
serviceImplementation="git4idea.Notificator"/>
<projectService serviceInterface="git4idea.history.wholeTree.GitLogSettings"
serviceImplementation="git4idea.history.wholeTree.GitLogSettings"/>
<projectService serviceInterface="git4idea.roots.GitRootProblemNotifier"
serviceImplementation="git4idea.roots.GitRootProblemNotifier"/>
<projectService serviceInterface="git4idea.branch.GitBrancher"
serviceImplementation="git4idea.branch.GitBrancherImpl"/>
+2 -8
View File
@@ -46,6 +46,8 @@ import com.intellij.openapi.vcs.history.VcsHistoryProvider;
import com.intellij.openapi.vcs.history.VcsRevisionNumber;
import com.intellij.openapi.vcs.merge.MergeProvider;
import com.intellij.openapi.vcs.rollback.RollbackEnvironment;
import com.intellij.openapi.vcs.roots.VcsRootDetectInfo;
import com.intellij.openapi.vcs.roots.VcsRootDetector;
import com.intellij.openapi.vcs.ui.VcsBalloonProblemNotifier;
import com.intellij.openapi.vcs.update.UpdateEnvironment;
import com.intellij.openapi.vcs.versionBrowser.CommittedChangeList;
@@ -77,9 +79,6 @@ import git4idea.i18n.GitBundle;
import git4idea.merge.GitMergeProvider;
import git4idea.rollback.GitRollbackEnvironment;
import git4idea.roots.GitIntegrationEnabler;
import git4idea.roots.GitRootChecker;
import com.intellij.openapi.vcs.roots.VcsRootDetectInfo;
import com.intellij.openapi.vcs.roots.VcsRootDetector;
import git4idea.status.GitChangeProvider;
import git4idea.ui.branch.GitBranchWidget;
import git4idea.update.GitUpdateEnvironment;
@@ -320,11 +319,6 @@ public class GitVcs extends AbstractVcs<CommittedChangeList> {
return dir.isDirectory() && GitUtil.gitRootOrNull(dir) != null;
}
@Override
public VcsRootChecker getRootChecker() {
return new GitRootChecker(myProject, myPlatformFacade);
}
@Override
protected void start() throws VcsException {
}
@@ -16,13 +16,11 @@
package git4idea.repo;
import com.intellij.dvcs.repo.AbstractRepositoryManager;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vcs.ProjectLevelVcsManager;
import com.intellij.openapi.vfs.VirtualFile;
import git4idea.GitPlatformFacade;
import git4idea.GitUtil;
import git4idea.roots.GitRootScanner;
import org.jetbrains.annotations.NotNull;
/**
@@ -32,25 +30,15 @@ public class GitRepositoryManager extends AbstractRepositoryManager<GitRepositor
@NotNull private final GitPlatformFacade myPlatformFacade;
public GitRepositoryManager(@NotNull Project project,
@NotNull GitPlatformFacade platformFacade,
public GitRepositoryManager(@NotNull Project project, @NotNull GitPlatformFacade platformFacade,
@NotNull ProjectLevelVcsManager vcsManager) {
super(project, vcsManager, platformFacade.getVcs(project), GitUtil.DOT_GIT);
myPlatformFacade = platformFacade;
}
@Override
public void initComponent() {
super.initComponent();
if (!ApplicationManager.getApplication().isUnitTestMode()) {
GitRootScanner.start(myProject);
}
}
@NotNull
@Override
protected GitRepository createRepository(@NotNull VirtualFile root) {
return GitRepositoryImpl.getFullInstance(root, myProject, myPlatformFacade, this);
}
}
@@ -17,11 +17,15 @@ package git4idea.roots;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Condition;
import com.intellij.openapi.vcs.AbstractVcs;
import com.intellij.openapi.vcs.ProjectLevelVcsManager;
import com.intellij.openapi.vcs.VcsDirectoryMapping;
import com.intellij.openapi.vcs.VcsRoot;
import com.intellij.openapi.vcs.roots.VcsRootDetectInfo;
import com.intellij.openapi.vcs.roots.VcsRootErrorsFinder;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.ui.UIUtil;
import git4idea.GitPlatformFacade;
import git4idea.GitVcs;
@@ -54,11 +58,18 @@ public class GitIntegrationEnabler {
public void enable(@NotNull VcsRootDetectInfo detectInfo) {
Notificator notificator = myPlatformFacade.getNotificator(myProject);
Collection<VirtualFile> roots = detectInfo.getRoots();
Collection<VcsRoot> gitRoots = ContainerUtil.filter(detectInfo.getRoots(), new Condition<VcsRoot>() {
@Override
public boolean value(VcsRoot root) {
AbstractVcs gitVcs = root.getVcs();
return gitVcs != null && gitVcs.getKeyInstanceMethod().equals(GitVcs.getKey());
}
});
Collection<VirtualFile> roots = VcsRootErrorsFinder.vcsRootsToVirtualFiles(gitRoots);
VirtualFile projectDir = myProject.getBaseDir();
assert projectDir != null : "Base dir is unexpectedly null for project: " + myProject;
if (detectInfo.empty()) {
if (gitRoots.isEmpty()) {
boolean succeeded = gitInitOrNotifyError(notificator, projectDir);
if (succeeded) {
addVcsRoots(Collections.singleton(projectDir));
@@ -66,7 +77,7 @@ public class GitIntegrationEnabler {
}
else {
assert !roots.isEmpty();
if (roots.size() > 1 || detectInfo.projectIsBelowGit()) {
if (roots.size() > 1 || detectInfo.projectIsBelowVcs()) {
notifyAddedRoots(notificator, roots);
}
addVcsRoots(roots);
@@ -95,9 +106,11 @@ public class GitIntegrationEnabler {
private void refreshGitDir(final VirtualFile projectDir) {
UIUtil.invokeAndWaitIfNeeded(new Runnable() {
@Override public void run() {
@Override
public void run() {
myPlatformFacade.runReadAction(new Runnable() {
@Override public void run() {
@Override
public void run() {
myPlatformFacade.getLocalFileSystem().refreshAndFindFileByPath(projectDir.getPath() + "/.git");
}
});
@@ -15,62 +15,33 @@
*/
package git4idea.roots;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vcs.VcsDirectoryMapping;
import com.intellij.openapi.vcs.VcsKey;
import com.intellij.openapi.vcs.VcsRootChecker;
import com.intellij.openapi.vcs.VcsRootError;
import com.intellij.openapi.vcs.roots.VcsRootErrorsFinder;
import git4idea.GitPlatformFacade;
import git4idea.GitUtil;
import git4idea.GitVcs;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
/**
* @author Kirill Likhodedov
*/
public class GitRootChecker implements VcsRootChecker {
public class GitRootChecker extends VcsRootChecker {
@NotNull private final Collection<VcsRootError> myErrors;
private final boolean myProjectMappingIsInvalid;
public GitRootChecker(@NotNull Project project, @NotNull GitPlatformFacade platformFacade) {
myErrors = new VcsRootErrorsFinder(project).find();
myProjectMappingIsInvalid = isProjectMappingInvalid();
}
private boolean isProjectMappingInvalid() {
for (VcsRootError error : myErrors) {
if (error.getType() == VcsRootError.Type.EXTRA_MAPPING && error.getMapping().equals(VcsDirectoryMapping.PROJECT_CONSTANT)) {
return true;
}
}
return false;
@Override
public boolean isRoot(@NotNull String path) {
return new File(path, GitUtil.DOT_GIT).exists();
}
@Override
@NotNull
@Override
public Collection<String> getUnregisteredRoots() {
Collection<String> roots = new ArrayList<String>();
for (VcsRootError error : myErrors) {
if (error.getType() == VcsRootError.Type.UNREGISTERED_ROOT) {
roots.add(error.getMapping());
}
}
return roots;
public VcsKey getSupportedVcs() {
return GitVcs.getKey();
}
@Override
public boolean isInvalidMapping(@NotNull VcsDirectoryMapping mapping) {
// this information is available in myErrors,
// but the method may be called in VcsDirectoryConfigurationPanel after adding a mapping (to highlight errors right away)
// in which case ProjectLevelVcsManager#getAllVcsRoots() is not aware of new roots yet,
// while VcsRootErrorsFinder relies on the set of roots returned from ProjectLevelVcsManager.
if (mapping.isDefaultMapping()) {
return myProjectMappingIsInvalid;
}
return !new File(mapping.getDirectory(), GitUtil.DOT_GIT).exists();
public boolean isVcsDir(@Nullable String path) {
return path != null && path.toLowerCase().endsWith(GitUtil.DOT_GIT);
}
}
@@ -2,12 +2,12 @@ package git4idea.roots;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vcs.VcsDirectoryMapping;
import com.intellij.openapi.vcs.VcsRoot;
import com.intellij.openapi.vcs.VcsRootFinder;
import com.intellij.openapi.vcs.roots.VcsRootDetectInfo;
import com.intellij.openapi.vcs.roots.VcsRootDetector;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.containers.ContainerUtilRt;
import git4idea.GitPlatformFacade;
import git4idea.GitVcs;
import org.jetbrains.annotations.NotNull;
@@ -20,25 +20,26 @@ import java.util.Collections;
*/
public class GitRootFinder implements VcsRootFinder {
@NotNull private final Project myProject;
@NotNull private final GitPlatformFacade myPlatformFacade;
@NotNull private final Project myProject;
public GitRootFinder(@NotNull Project project, @NotNull GitPlatformFacade platformFacade) {
public GitRootFinder(@NotNull Project project) {
myProject = project;
myPlatformFacade = platformFacade;
}
@NotNull
@Override
public Collection<VcsDirectoryMapping> findRoots(@NotNull VirtualFile root) {
VcsRootDetectInfo info = new VcsRootDetector(myProject).detect(root);
Collection<VirtualFile> roots = info.getRoots();
Collection<VcsRoot> roots = info.getRoots();
if (roots.isEmpty()) {
return Collections.emptyList();
}
Collection<VcsDirectoryMapping> result = ContainerUtilRt.newArrayList();
for (VirtualFile file : roots) {
result.add(new VcsDirectoryMapping(file.getPath(), GitVcs.getKey().getName()));
for (VcsRoot vcsRoot : roots) {
VirtualFile vFile = vcsRoot.getPath();
if (vFile != null) {
result.add(new VcsDirectoryMapping(vFile.getPath(), GitVcs.getKey().getName()));
}
}
return result;
}
@@ -1,107 +0,0 @@
/*
* 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.roots
import com.intellij.dvcs.test.MockProject
import com.intellij.dvcs.test.MockProjectRootManager
import com.intellij.dvcs.test.MockVirtualFile
import com.intellij.openapi.project.Project
import com.intellij.openapi.util.io.FileUtil
import git4idea.test.GitLightTest
import org.junit.After
import org.junit.Before
/**
*
* @author Kirill Likhodedov
*/
class AbstractGitRootTest extends GitLightTest {
static File baseDir
@Before
void setUp() {
super.setUp();
}
@After
void tearDown() {
super.tearDown();
}
/**
* Creates the necessary temporary directories in the filesystem with empty ".git" directories for given roots.
* And creates an instance of the project.
* @param gitRoots path to actual .git roots, relative to the project dir.
*/
Project initProject(Collection<String> gitRoots, Collection<String> projectStructure, Collection<String> contentRoots = []) {
String projectDir = createDirs(gitRoots)
Project project = new MockProject(projectDir)
createProjectStructure(project, projectStructure);
createProjectStructure(project, contentRoots);
contentRoots.each {
((MockProjectRootManager)myPlatformFacade.getProjectRootManager(project)).myContentRoots << MockVirtualFile.fromPath(it, project)
}
project
}
static void createProjectStructure(Project project, Collection<String> paths) {
paths.each { String path ->
File file = new File(project.baseDir.path + "/" + path)
file.mkdir()
}
}
/**
* @return path to the project
*/
private static String createDirs(Collection<String> gitRoots) {
if (gitRoots.empty) {
return baseDir = FileUtil.createTempDirectory("grdt", null);
}
baseDir = createBaseTempDir()
int maxDepth = findMaxDepthAboveProject(gitRoots)
File projectDir = createChild(baseDir, maxDepth)
gitRoots.each { String path ->
File file = new File(projectDir.path + "/" + path)
file.mkdirs()
file.deleteOnExit()
File gitDir = new File(file, ".git")
gitDir.mkdir()
gitDir.deleteOnExit()
}
return projectDir.path
}
private static File createBaseTempDir() {
FileUtil.createTempDirectory("pref", null)
}
private static File createChild(File base, int depth) {
File dir = base
depth.times { dir = FileUtil.createTempDirectory(dir, "grdt", null)}
dir
}
// Assuming that there are no ".." inside the path - only in the beginning
static int findMaxDepthAboveProject(Collection<String> paths) {
def len = { String path -> path.split("/").count("..") }
len(paths.max(len))
}
}
@@ -20,7 +20,7 @@ import com.intellij.notification.Notification
import com.intellij.notification.NotificationType
import com.intellij.openapi.vcs.roots.VcsRootDetectInfo
import com.intellij.openapi.vfs.VirtualFile
import git4idea.test.GitLightTest
import com.intellij.testFramework.UsefulTestCase
import git4idea.test.TestNotificator
import org.junit.After
import org.junit.Before
@@ -28,18 +28,20 @@ import org.junit.Test
import static git4idea.test.GitGTestUtil.toAbsolute
import static junit.framework.Assert.*
import static org.junit.Assume.assumeTrue
/**
*
*
* @author Kirill Likhodedov
*/
class GitIntegrationEnablerTest extends GitLightTest {
class GitIntegrationEnablerTest extends UsefulTestCase {
public static final String TEST_NOTIFICATION_GROUP = "Test"
@Override
@Before
public void setUp() {
assumeTrue(false);
super.setUp();
}
@@ -157,5 +159,4 @@ class GitIntegrationEnablerTest extends GitLightTest {
void assertNotificationShown(String title, String message, NotificationType type) {
assertNotificationShown(new Notification(TEST_NOTIFICATION_GROUP, title, message, type))
}
}
@@ -0,0 +1,209 @@
/*
* Copyright 2000-2013 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.roots;
import com.intellij.ide.highlighter.ModuleFileType;
import com.intellij.openapi.application.Result;
import com.intellij.openapi.application.WriteAction;
import com.intellij.openapi.module.EmptyModuleType;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleManager;
import com.intellij.openapi.module.ModuleType;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.ModuleRootManager;
import com.intellij.openapi.roots.impl.ModuleRootManagerImpl;
import com.intellij.openapi.roots.impl.RootModelImpl;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VfsUtilCore;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.testFramework.PlatformTestCase;
import com.intellij.testFramework.UsefulTestCase;
import com.intellij.testFramework.fixtures.IdeaProjectTestFixture;
import com.intellij.testFramework.fixtures.IdeaTestFixtureFactory;
import org.jetbrains.annotations.NotNull;
import java.io.File;
import java.io.IOException;
import java.util.Collection;
import java.util.HashSet;
import static com.intellij.dvcs.test.Executor.*;
/**
* @author Nadya Zabrodina
*/
public abstract class GitPlatformTest extends UsefulTestCase {
protected Project myProject;
protected VirtualFile myProjectRoot;
protected VirtualFile myRepository;
public static final String myRepositoryFolderName = "repository";
private RootModelImpl myRootModel;
protected static final Collection<File> myFilesToDelete = new HashSet<File>();
protected IdeaProjectTestFixture myProjectFixture;
@SuppressWarnings("JUnitTestCaseWithNonTrivialConstructors")
protected GitPlatformTest() {
PlatformTestCase.initPlatformLangPrefix();
}
@Override
protected void setUp() throws Exception {
super.setUp();
myProjectFixture = IdeaTestFixtureFactory.getFixtureFactory().createFixtureBuilder(getTestName(true)).getFixture();
myProjectFixture.setUp();
myProject = myProjectFixture.getProject();
myProjectRoot = myProject.getBaseDir();
cd(myProjectRoot);
Module module = doCreateRealModuleIn("foo", myProject, EmptyModuleType
.getInstance());
myRootModel = ((ModuleRootManagerImpl)ModuleRootManager.getInstance(module)).getRootModel();
mkdir(myRepositoryFolderName);
myRepository = myProjectRoot.findChild(myRepositoryFolderName);
}
@Override
protected void tearDown() throws Exception {
for (File file : myFilesToDelete) {
delete(file);
}
myProjectFixture.tearDown();
super.tearDown();
}
private static void delete(File file) {
boolean b = FileUtil.delete(file);
if (!b && file.exists()) {
fail("Can't delete " + file.getAbsolutePath());
}
}
/**
* Creates the necessary temporary directories in the filesystem with empty ".git" directories for given roots.
* And creates an instance of the project.
*
* @param gitRoots path to actual .git roots, relative to the project dir.
*/
public void initProject(@NotNull Collection<String> gitRoots,
@NotNull Collection<String> projectStructure,
@NotNull Collection<String> contentRoots)
throws IOException {
createDirs(gitRoots);
createProjectStructure(myProject, projectStructure);
createProjectStructure(myProject, contentRoots);
if (!contentRoots.isEmpty()) {
for (String root : contentRoots) {
VirtualFile f = myProjectRoot.findFileByRelativePath(root);
if (f != null) {
myRootModel.addContentEntry(f);
}
}
}
}
static void createProjectStructure(@NotNull Project project, @NotNull Collection<String> paths) {
for (String path : paths) {
cd(project.getBaseDir().getPath());
File f = new File(project.getBaseDir().getPath(), path);
f.mkdirs();
}
}
@NotNull
public static Module doCreateRealModuleIn(@NotNull String moduleName,
@NotNull final Project project,
@NotNull final ModuleType moduleType) {
final VirtualFile baseDir = project.getBaseDir();
assertNotNull(baseDir);
final File moduleFile = new File(baseDir.getPath().replace('/', File.separatorChar),
moduleName + ModuleFileType.DOT_DEFAULT_EXTENSION);
FileUtil.createIfDoesntExist(moduleFile);
myFilesToDelete.add(moduleFile);
return new WriteAction<Module>() {
@Override
protected void run(Result<Module> result) throws Throwable {
final VirtualFile virtualFile = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(moduleFile);
assert virtualFile != null;
Module module = ModuleManager.getInstance(project).newModule(virtualFile.getPath(), moduleType.getId());
module.getModuleFile();
result.setResult(module);
}
}.execute().getResultObject();
}
/**
* @return path to the project
*/
private void createDirs(@NotNull Collection<String> gitRoots) throws IOException {
File baseDir;
if (gitRoots.isEmpty()) {
return;
}
baseDir = VfsUtilCore.virtualToIoFile(myProject.getBaseDir());
int maxDepth = findMaxDepthAboveProject(gitRoots);
File projectDir = createChild(baseDir, maxDepth - 1);
cd(projectDir.getPath());
for (String path : gitRoots) {
File file = new File(projectDir, path);
file.mkdirs();
File gitDir = new File(file, ".git");
gitDir.mkdirs();
myFilesToDelete.add(gitDir);
gitDir.deleteOnExit();
cd(gitDir.getPath());
touch("HEAD", "ref: refs/heads/master");
File head = new File(gitDir, "HEAD");
myFilesToDelete.add(head);
touch("config", "");
File config = new File(gitDir, "config");
myFilesToDelete.add(config);
}
}
@NotNull
private static File createChild(@NotNull File base, int depth) throws IOException {
File dir = base;
if (depth < 0) {
return dir;
}
for (int i = 0; i < depth; ++i) {
dir = FileUtil.createTempDirectory(dir, "grdt", null);
}
return dir;
}
// Assuming that there are no ".." inside the path - only in the beginning
static int findMaxDepthAboveProject(@NotNull Collection<String> paths) {
int max = 0;
for (String path : paths) {
String[] splits = path.split("/");
int count = 0;
for (String split : splits) {
if (split.equals("..")) {
count++;
}
}
if (count > max) {
max = count;
}
}
return max;
}
}
@@ -1,222 +0,0 @@
/*
* 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.roots
import com.intellij.openapi.project.Project
import com.intellij.openapi.vcs.roots.VcsRootDetectInfo
import com.intellij.openapi.vcs.roots.VcsRootDetector
import com.intellij.openapi.vfs.VirtualFile
import git4idea.test.GitGTestUtil
import org.junit.After
import org.junit.Before
import org.junit.Test
import static org.junit.Assert.assertEquals
import static org.junit.Assert.fail
/**
* @author Kirill Likhodedov
*/
class GitRootDetectorTest extends AbstractGitRootTest {
Project myProject
@Before
void setUp() {
super.setUp();
}
@After
void tearDown() {
super.tearDown();
}
@Test
void "no roots in project"() {
doTest gits: [],
expected: [],
full: false,
below: false
}
@Test
void "project under single Git"() {
doTest "."
}
@Test
void "project with Git under it"() {
doTest project: [".idea", "bin", "src", "community"],
gits: ["community"],
expected: ["community"],
full: false,
below: false
}
@Test
public void "project with all subdirs under Git should still be not fully controlled"() {
doTest gits: [".idea", "src", "community"],
expected: [".idea", "src", "community"],
full: false,
below: false
}
@Test
void "project under Git above it"() {
doTest gits: [".."],
expected: [".."],
full: true,
below: true
}
@Test
void "IDEA project"() {
doTest ".", "./community", "./contrib"
}
@Test
void "one above and one under"() {
doTest gits: ["..", "./community"],
expected: ["..", "./community"],
full: true,
below: true
}
@Test
void "one above and one for project should show only project root"() {
doTest gits: ["..", "."],
expected: ["."],
full: true,
below: false
}
@Test
void "one above and several under project"() {
doTest gits: ["..", "./community", "./contrib"],
expected: ["..", "./community", "./contrib"],
full: true,
below: true
}
@Test
void "multiple above should be detected as one above"() {
doTest gits: ["..", "../.."],
expected: [".."],
full: true,
below: true
}
@Test
void "unrelated root should not be detected"() {
doTest gits: ["../neighbour"],
expected: [],
full: false,
below: false
}
@Test
void "linked source root alone should be detected"() {
doTest content_roots: ["../linked_root"],
gits: ["../linked_root"],
expected: ["../linked_root"],
full: false,
below: false
}
@Test
void "linked source root and project root should be detected"() {
doTest content_roots: ["../linked_root"],
gits: [".", "../linked_root"],
expected: [".", "../linked_root"],
full: true,
below: false
}
@Test
void "linked source below Git"() {
doTest content_roots: ["../linked_root/src"],
gits: [".", "../linked_root"],
expected: [".", "../linked_root"],
full: true,
below: false
}
@Test
// This is a test of performance optimization via limitation: don't scan deep though the whole VFS, i.e. don't detect deep roots
void "don't scan deeper than 2 levels below a content root"() {
doTest content_roots: ["content_root"],
gits: ["community", "content_root/lev1/lev2", "content_root2/lev1/lev2/lev3"],
expected: ["community", "content_root/lev1/lev2"],
full: false,
below: false
}
/**
* Perform test. Map contains actual Git repositories to be created on disk,
* and Git repositories expected to be detected by the VcsRootDetector.
* @param map
*/
private void doTest(Map map) {
myProject = initProject(map.gits, map.project, map.content_roots)
testInfo empty: map.expected.empty,
full : map.full,
roots: map.expected,
below: map.below
}
/**
* Shorthand, when all Git roots are expected to be detected, and project is fully under Git.
* @param roots paths relative to the project dir. "..", ".", "./community" are accepted.
*/
private void doTest(String... roots = []) {
doTest gits: roots.toList(),
expected: roots.toList(),
full: true,
below: false
}
void testInfo(Map expected) {
assertInfo(expected, detect())
}
void assertInfo(Map expected, VcsRootDetectInfo actual) {
assertEquals(expected.empty, actual.empty())
if (expected.full ^ actual.totallyUnderGit()) {
fail("The project is unexpectedly ${actual.totallyUnderGit() ? "" : "not "}under Git${roots(actual.roots)}")
}
if (expected.below ^ actual.projectIsBelowGit()) {
fail("The project is unexpectedly ${actual.projectIsBelowGit() ? "below" : "not below"} Git${roots(actual.roots)}")
}
assertRoots(expected.roots, actual.roots)
}
static String roots(Collection roots) {
"\nRoots:${roots.collect {"\n * $it"}}\n"
}
void assertRoots(Collection<String> expectedRelativePaths, Collection<VirtualFile> actual) {
assertEquals(GitGTestUtil.toAbsolute(expectedRelativePaths, myProject).toSet(), getPaths(actual).toSet())
}
static Collection<String> getPaths(Collection<VirtualFile> files) {
files.collect { it.path }
}
private VcsRootDetectInfo detect() {
new VcsRootDetector(myProject).detect()
}
}
@@ -0,0 +1,222 @@
/*
* Copyright 2000-2013 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.roots;
import com.intellij.dvcs.test.Executor;
import com.intellij.dvcs.test.TestRepositoryUtil;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.vcs.VcsRoot;
import com.intellij.openapi.vcs.roots.VcsRootDetectInfo;
import com.intellij.openapi.vcs.roots.VcsRootDetector;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.Function;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.io.IOException;
import java.util.*;
import static com.intellij.dvcs.test.Executor.cd;
/**
* @author Nadya Zabrodina
*/
public class GitRootDetectorTest extends GitPlatformTest {
public void testNoRootsInProject() throws IOException {
Map<String, Collection<String>> map = new HashMap<String, Collection<String>>();
map.put("git", Collections.<String>emptyList());
map.put("content_roots", Collections.<String>emptyList());
doTest(map, null, Collections.<String>emptyList(), false, false);
}
public void testProjectUnderSingleGit() throws IOException {
Map<String, Collection<String>> map = new HashMap<String, Collection<String>>();
map.put("git", Arrays.asList("."));
map.put("content_roots", Collections.<String>emptyList());
doTest(map, myProjectRoot, Arrays.asList("."), true, false);
}
public void testProjectWithGitUnderIt() throws IOException {
Map<String, Collection<String>> map = new HashMap<String, Collection<String>>();
map.put("git", Arrays.asList("community"));
map.put("content_roots", Collections.<String>emptyList());
cd(myProjectRoot);
Executor.mkdir("src");
Executor.mkdir(".idea");
doTest(map, myProjectRoot, Arrays.asList("community"), false, false);
}
public void testProjectWithAllSubdirsUnderGitShouldStillBeNotFullyControlled() throws IOException {
String[] dirNames = {".idea", "src", "community"};
Map<String, Collection<String>> map = new HashMap<String, Collection<String>>();
map.put("git", Arrays.asList(dirNames));
map.put("content_roots", Collections.<String>emptyList());
doTest(map, myProjectRoot, Arrays.asList(dirNames), false, false);
}
public void testProjectUnderVcsAboveIt() throws IOException {
String subdir = "insideRepo";
cd(myRepository);
Executor.mkdir(subdir);
Map<String, Collection<String>> map = new HashMap<String, Collection<String>>();
map.put("git", Arrays.asList(myRepository.getName()));
map.put("content_roots", Collections.<String>emptyList());
VirtualFile vfile = myRepository.findChild(subdir);
doTest(map, vfile, Arrays.asList(myRepository.getName()),
true, true);
}
public void testIDEAProject() throws IOException {
String[] names = {"community", "contrib", "."};
Map<String, Collection<String>> map = new HashMap<String, Collection<String>>();
map.put("git", Arrays.asList(names));
map.put("content_roots", Collections.<String>emptyList());
doTest(map, myProjectRoot, Arrays.asList(names), true, false);
}
public void testOneAboveAndOneUnder() throws IOException {
String[] names = {myRepository.getName() + "/community", "."};
Map<String, Collection<String>> map = new HashMap<String, Collection<String>>();
map.put("git", Arrays.asList(names));
map.put("content_roots", Collections.<String>emptyList());
doTest(map, myRepository, Arrays.asList(names), true, true);
}
public void testOneAboveAndOneForProjectShouldShowOnlyProjectRoot() throws IOException {
String[] names = {myRepository.getName(), "."};
Map<String, Collection<String>> map = new HashMap<String, Collection<String>>();
map.put("git", Arrays.asList(names));
map.put("content_roots", Collections.<String>emptyList());
doTest(map, myRepository, Arrays.asList(myRepository.getName()), true, false);
}
public void testOneAboveAndSeveralUnderProject() throws IOException {
String[] names = {".", myRepository.getName() + "/community", myRepository.getName() + "/contrib"};
Map<String, Collection<String>> map = new HashMap<String, Collection<String>>();
map.put("git", Arrays.asList(names));
map.put("content_roots", Collections.<String>emptyList());
doTest(map, myRepository, Arrays.asList(names), true, true);
}
public void testMultipleAboveShouldBeDetectedAsOneAbove() throws IOException {
Map<String, Collection<String>> map = new HashMap<String, Collection<String>>();
map.put("git", Arrays.asList(".", myRepository.getName()));
map.put("content_roots", Collections.<String>emptyList());
String subdir = "insideRepo";
cd(myRepository);
Executor.mkdir(subdir);
VirtualFile vfile = myRepository.findChild(subdir);
doTest(map, vfile, Arrays.asList(myRepository.getName()), true, true);
}
public void testUnrelatedRootShouldNotBeDetected() throws IOException {
Map<String, Collection<String>> map = new HashMap<String, Collection<String>>();
map.put("git", Arrays.asList("another"));
map.put("content_roots", Collections.<String>emptyList());
doTest(map, myRepository, Collections.<String>emptyList(), false, false);
}
public void testLinkedSourceRootAloneShouldBeDetected() throws IOException {
Map<String, Collection<String>> map = new HashMap<String, Collection<String>>();
map.put("git", Arrays.asList("linked_root"));
map.put("content_roots", Arrays.asList("linked_root"));
doTest(map, myRepository, Arrays.asList("linked_root"), false, false);
}
public void testLinkedSourceRootAndProjectRootShouldBeDetected() throws IOException {
Map<String, Collection<String>> map = new HashMap<String, Collection<String>>();
map.put("git", Arrays.asList(".", "linked_root"));
map.put("content_roots", Arrays.asList("linked_root"));
doTest(map, myProjectRoot, Arrays.asList(".", "linked_root"), true, false);
}
public void testLinkedSourceBelowGit() throws IOException {
Map<String, Collection<String>> map = new HashMap<String, Collection<String>>();
map.put("git", Arrays.asList(".", "linked_root"));
map.put("content_roots", Arrays.asList("linked_root/src"));
doTest(map, myProjectRoot, Arrays.asList(".", "linked_root"), true, false);
}
// This is a test of performance optimization via limitation: don't scan deep though the whole VFS, i.e. don't detect deep roots
public void testDontScanDeeperThan2LevelsBelowAContentRoot() throws IOException {
Map<String, Collection<String>> map = new HashMap<String, Collection<String>>();
map.put("git", Arrays.asList("community", "content_root/lev1/lev2", "content_root2/lev1/lev2/lev3"));
map.put("content_roots", Arrays.asList("content_root"));
doTest(map, myProjectRoot, Arrays.asList("community", "content_root/lev1/lev2"), false, false);
}
void assertRoots(Collection<String> expectedRelativePaths, Collection<String> actual) {
TestRepositoryUtil.assertEqualCollections(actual, toAbsolute(expectedRelativePaths, myProject));
}
@NotNull
public static Collection<String> toAbsolute(Collection<String> relPaths, final Project project) {
return ContainerUtil.map(relPaths, new Function<String, String>() {
@Override
public String fun(String s) {
try {
return FileUtil.toSystemIndependentName(new File(project.getBaseDir().getPath(), s).getCanonicalPath());
}
catch (IOException e) {
fail();
e.printStackTrace();
return null;
}
}
});
}
@NotNull
static Collection<String> getPaths(@NotNull Collection<VcsRoot> files) {
return ContainerUtil.map(files, new Function<VcsRoot, String>() {
@Override
public String fun(VcsRoot root) {
VirtualFile file = root.getPath();
assert file != null;
return FileUtil.toSystemIndependentName(file.getPath());
}
});
}
@NotNull
private VcsRootDetectInfo detect(@Nullable VirtualFile startDir) {
return new VcsRootDetector(myProject).detect(startDir);
}
public void doTest(@NotNull Map<String, Collection<String>> map,
@Nullable VirtualFile startDir,
@NotNull Collection<String> expectedPaths,
boolean expectedFull,
boolean expectedBelow)
throws IOException {
initProject(map.get("git"), Collections.<String>emptyList(), map.get("content_roots"));
VcsRootDetectInfo info = detect(startDir);
assertRoots(expectedPaths, getPaths(info.getRoots()));
assertEquals(expectedFull, info.totallyUnderVcs());
assertEquals(expectedBelow, info.projectIsBelowVcs());
}
}
@@ -1,211 +0,0 @@
/*
* 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.roots
import com.intellij.openapi.vcs.VcsDirectoryMapping
import com.intellij.openapi.vcs.VcsRootError
import com.intellij.openapi.vcs.roots.VcsRootErrorsFinder
import git4idea.test.GitMockVcsManager
import org.junit.After
import org.junit.Before
import org.junit.Test
import static git4idea.test.GitGTestUtil.toAbsolute
import static junit.framework.Assert.assertEquals
/**
*
* @author Kirill Likhodedov
*/
class GitRootErrorsFinderTest extends AbstractGitRootTest {
static final String PROJECT = VcsDirectoryMapping.PROJECT_CONSTANT
GitMockVcsManager myVcsManager
@Before
void setUp() {
super.setUp()
}
@After
void tearDown() {
super.tearDown();
}
@Test
void "No roots, then no errors"() {
doTest git: [],
roots: [],
errors: []
}
@Test
void "Same 1 root in both, then no errors"() {
doTest git: ["."],
roots: ["."],
errors: []
}
@Test
void "Same 2 roots in both, then no errors"() {
doTest git: ["..", "community"],
roots: ["..", "community"],
errors: []
}
@Test
void "One git, no VCS roots, then error"() {
doTest git: ["."],
roots: [],
errors: [unreg : ["."]]
}
@Test
void "One VCS root, no gits, then error"() {
doTest git: [],
roots: ["."],
errors: [extra: ["."]]
}
@Test
void "One root, but different, then 2 errors"() {
doTest git: ["."],
roots: ["community"],
errors: [unreg: ["."], extra: ["community"]]
}
@Test
void "Two roots, one matching, one different, then 2 errors"() {
doTest git: [".", "community"],
roots: [".", "contrib"],
errors: [unreg: ["community"], extra: ["contrib"]]
}
@Test
void "Two roots in git, one matching in VCS, then error"() {
doTest git: [".", "community"],
roots: ["."],
errors: [unreg: ["community"]]
}
@Test
void "Two roots, both not matching, then 4 errors"() {
doTest git: ["..", "community"],
roots: [".", "contrib"],
errors: [unreg: ["..", "community"], extra: [".", "contrib"]]
}
@Test
void "Project root, no gits, then error about extra root"() {
doTest content_roots: ["."],
git: [],
roots: [PROJECT],
errors: [extra: [PROJECT]]
}
@Test
void "Project root, full under git, then correct"() {
doTest content_roots: ["."],
git: ["."],
roots: [PROJECT],
errors: []
}
@Test
void "Project root, git for a content root below project, then error"() {
doTest content_roots: [".", "content_root"],
git: ["content_root"],
roots: [PROJECT],
errors: [unreg: ["content_root"]]
}
@Test
void "Project root, git below project folder not in a content root, then unregistered root error"() {
// this is to be fixed: auto-detection of Git repositories in subfolders for the <Project> mapping
doTest content_roots: ["."],
git: ["community"],
roots: [PROJECT],
errors: [unreg: ["community"]]
}
@Test
void "Project root, git for full project, content root, linked source, folder below project, then errors"() {
doTest content_roots: [".", "content_root", "../linked_source_root"],
git: [".", "content_root", "../linked_source_root", "folder"],
roots: [PROJECT],
errors: [unreg: ["content_root", "../linked_source_root", "folder"]]
}
@Test
void "Project root, root for folder, git for full project, content root, linked source, folder below project, then errors"() {
doTest content_roots: [".", "content_root", "../linked_source_root"],
git: [".", "content_root", "../linked_source_root", "folder"],
roots: [PROJECT, "folder"],
errors: [unreg: ["content_root", "../linked_source_root"]]
}
@Test
void "Project root, git like in IDEA project, then correct"() {
doTest content_roots: [".", "community", "contrib"],
git: [".", "community", "contrib"],
roots: [PROJECT],
errors: [unreg: ["community", "contrib"]]
}
@Test
void "Real Git root deeper than 3 levels should be detected"() {
doTest content_roots: [".", "community", "contrib"],
git: [".", "community", "contrib", "community/level1/level2/level3"],
roots: [PROJECT, "community/level1/level2/level3"],
errors: [unreg: ["community", "contrib"]]
}
private void doTest(Map map) {
myProject = initProject(map.git, [], map.content_roots)
myVcsManager = (GitMockVcsManager) myPlatformFacade.getVcsManager(myProject)
addVcsRoots(map.roots)
Collection<VcsRootError> expected = new ArrayList<VcsRootError>();
expected.addAll map.errors.unreg.collect { unreg(it) }
expected.addAll map.errors.extra.collect { extra(it) }
Collection<VcsRootError> actual = new VcsRootErrorsFinder(myProject).find()
assertEquals expected.toSet(), actual.toSet()
}
void addVcsRoots(Collection<String> relativeRoots) {
relativeRoots.each {
if (it.equals(PROJECT)) {
myVcsManager.setProjectRootMapping()
}
else {
String root = toAbsolute(it, myProject)
myVcsManager.addRoots(root)
}
}
}
VcsRootError unreg(String path) {
return new VcsRootError(VcsRootError.Type.UNREGISTERED_ROOT, toAbsolute(path, myProject))
}
VcsRootError extra(String path) {
return new VcsRootError(VcsRootError.Type.EXTRA_MAPPING, path.equals(PROJECT) ? PROJECT : toAbsolute(path, myProject))
}
}
@@ -0,0 +1,270 @@
/*
* Copyright 2000-2013 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.roots;
import com.intellij.dvcs.test.TestRepositoryUtil;
import com.intellij.openapi.vcs.ProjectLevelVcsManager;
import com.intellij.openapi.vcs.VcsDirectoryMapping;
import com.intellij.openapi.vcs.VcsRootError;
import com.intellij.openapi.vcs.roots.VcsRootErrorsFinder;
import git4idea.GitVcs;
import org.jetbrains.annotations.NotNull;
import java.io.IOException;
import java.util.*;
import static git4idea.test.GitGTestUtil.toAbsolute;
/**
* @author Nadya Zabrodina
*/
public class GitRootErrorsFinderTest extends GitPlatformTest {
static final String PROJECT = VcsDirectoryMapping.PROJECT_CONSTANT;
@NotNull private ProjectLevelVcsManager myVcsManager;
@Override
public void setUp() throws Exception {
super.setUp();
myVcsManager = ProjectLevelVcsManager.getInstance(myProject);
}
public void testNoRootsThenNoErrors() throws IOException {
Map<String, Collection<String>> map = new HashMap<String, Collection<String>>();
map.put("git", Collections.<String>emptyList());
map.put("roots", Collections.<String>emptyList());
map.put("content_roots", Collections.<String>emptyList());
doTest(map, Collections.<String, Collection<String>>emptyMap());
}
public void testSameOneRootInBothThenNoErrors() throws IOException {
Map<String, Collection<String>> map = new HashMap<String, Collection<String>>();
map.put("git", Arrays.asList("."));
map.put("roots", Arrays.asList("."));
map.put("content_roots", Collections.<String>emptyList());
doTest(map, Collections.<String, Collection<String>>emptyMap());
}
public void testSameTwoRootsInBothThenNoErrors() throws IOException {
Map<String, Collection<String>> map = new HashMap<String, Collection<String>>();
map.put("git", Arrays.asList(".", "community"));
map.put("roots", Arrays.asList(".", "community"));
map.put("content_roots", Collections.<String>emptyList());
doTest(map, Collections.<String, Collection<String>>emptyMap());
}
public void testOneGitNoVCSRootsThenError() throws IOException {
Map<String, Collection<String>> map = new HashMap<String, Collection<String>>();
map.put("git", Arrays.asList("."));
map.put("roots", Collections.<String>emptyList());
map.put("content_roots", Collections.<String>emptyList());
Map<String, Collection<String>> errorsMap = new HashMap<String, Collection<String>>();
errorsMap.put("unreg", Arrays.asList("."));
doTest(map, errorsMap);
}
public void testOneVCSRootNoGitsThenError() throws IOException {
Map<String, Collection<String>> map = new HashMap<String, Collection<String>>();
map.put("roots", Arrays.asList("."));
map.put("git", Collections.<String>emptyList());
map.put("content_roots", Collections.<String>emptyList());
Map<String, Collection<String>> errorsMap = new HashMap<String, Collection<String>>();
errorsMap.put("extra", Arrays.asList("."));
doTest(map, errorsMap);
}
public void testOneRootButDifferentThenTwoErrors() throws IOException {
Map<String, Collection<String>> map = new HashMap<String, Collection<String>>();
map.put("git", Arrays.asList("."));
map.put("roots", Arrays.asList("community"));
map.put("content_roots", Collections.<String>emptyList());
Map<String, Collection<String>> errorsMap = new HashMap<String, Collection<String>>();
errorsMap.put("extra", Arrays.asList("community"));
errorsMap.put("unreg", Arrays.asList("."));
doTest(map, errorsMap);
}
public void testTwoRootsOneMatchingOneDifferentThenTwoErrors() throws IOException {
Map<String, Collection<String>> map = new HashMap<String, Collection<String>>();
map.put("git", Arrays.asList(".", "community"));
map.put("roots", Arrays.asList(".", "contrib"));
map.put("content_roots", Collections.<String>emptyList());
Map<String, Collection<String>> errorsMap = new HashMap<String, Collection<String>>();
errorsMap.put("extra", Arrays.asList("contrib"));
errorsMap.put("unreg", Arrays.asList("community"));
doTest(map, errorsMap);
}
public void testTwoRootsInGitOneMatchingInVCSThenError() throws IOException {
Map<String, Collection<String>> map = new HashMap<String, Collection<String>>();
map.put("git", Arrays.asList(".", "community"));
map.put("roots", Arrays.asList("."));
map.put("content_roots", Collections.<String>emptyList());
Map<String, Collection<String>> errorsMap = new HashMap<String, Collection<String>>();
errorsMap.put("unreg", Arrays.asList("community"));
doTest(map, errorsMap);
}
public void testTwoRootsBothNotMatchingThenFourErrors() throws IOException {
Map<String, Collection<String>> map = new HashMap<String, Collection<String>>();
map.put("git", Arrays.asList(".", "community"));
map.put("roots", Arrays.asList("another", "contrib"));
map.put("content_roots", Collections.<String>emptyList());
Map<String, Collection<String>> errorsMap = new HashMap<String, Collection<String>>();
errorsMap.put("extra", Arrays.asList("contrib", "another"));
errorsMap.put("unreg", Arrays.asList("community", "."));
doTest(map, errorsMap);
}
public void testProjectRootNoGitsThenErrorAboutExtraRoot() throws IOException {
Map<String, Collection<String>> map = new HashMap<String, Collection<String>>();
map.put("git", Collections.<String>emptyList());
map.put("roots", Arrays.asList(PROJECT));
map.put("content_roots", Collections.<String>emptyList());
Map<String, Collection<String>> errorsMap = new HashMap<String, Collection<String>>();
errorsMap.put("extra", Arrays.asList(PROJECT));
doTest(map, errorsMap);
}
public void testProjectRootFullUnderGitThenCorrect() throws IOException {
Map<String, Collection<String>> map = new HashMap<String, Collection<String>>();
map.put("git", Arrays.asList("."));
map.put("roots", Arrays.asList(".", PROJECT));
map.put("content_roots", Collections.<String>emptyList());
doTest(map, Collections.<String, Collection<String>>emptyMap());
}
public void testProjectRootGitForAContentRootBelowProjectThenError() throws IOException {
Map<String, Collection<String>> map = new HashMap<String, Collection<String>>();
map.put("git", Arrays.asList("content_root"));
map.put("roots", Arrays.asList(PROJECT));
map.put("content_roots", Arrays.asList("content_root"));
Map<String, Collection<String>> errorsMap = new HashMap<String, Collection<String>>();
errorsMap.put("unreg", Arrays.asList("content_root"));
doTest(map, errorsMap);
}
public void testProjectRootGitBelowProjectFolderNotInAContentRootThenUnregisteredRootError() throws IOException {
// this is to be fixed: auto-detection of Git repositories in subfolders for the <Project> mapping
Map<String, Collection<String>> map = new HashMap<String, Collection<String>>();
map.put("git", Arrays.asList("community"));
map.put("roots", Arrays.asList(PROJECT));
map.put("content_roots", Arrays.asList("."));
Map<String, Collection<String>> errorsMap = new HashMap<String, Collection<String>>();
errorsMap.put("unreg", Arrays.asList("community"));
doTest(map, errorsMap);
}
public void testProjectRootGitForFullProjectContentRootLinkedSourceFolderBelowProjectThenErrors() throws IOException
{
Map<String, Collection<String>> map = new HashMap<String, Collection<String>>();
map.put("git", Arrays.asList(".", "content_root", "../linked_source_root", "folder"));
map.put("roots", Arrays.asList(PROJECT));
map.put("content_roots", Arrays.asList(".", "content_root", "../linked_source_root"));
Map<String, Collection<String>> errorsMap = new HashMap<String, Collection<String>>();
errorsMap.put("unreg", Arrays.asList("content_root", "../linked_source_root", "folder"));
doTest(map, errorsMap);
}
public void testProjectRootRootForFolderGitForFullProjectContentRootLinkedSourceFolderBelowProjectThenErrors() throws IOException {
Map<String, Collection<String>> map = new HashMap<String, Collection<String>>();
map.put("git", Arrays.asList(".", "content_root", "../linked_source_root", "folder"));
map.put("roots", Arrays.asList(PROJECT, "folder"));
map.put("content_roots", Arrays.asList(".", "content_root", "../linked_source_root"));
Map<String, Collection<String>> errorsMap = new HashMap<String, Collection<String>>();
errorsMap.put("unreg", Arrays.asList("content_root", "../linked_source_root"));
doTest(map, errorsMap);
}
public void testProjectRootGitLikeInIDEAProjectThenError() throws IOException {
Map<String, Collection<String>> map = new HashMap<String, Collection<String>>();
map.put("git", Arrays.asList(".", "community", "contrib"));
map.put("roots", Arrays.asList(PROJECT));
map.put("content_roots", Arrays.asList(".", "community", "contrib"));
Map<String, Collection<String>> errorsMap = new HashMap<String, Collection<String>>();
errorsMap.put("unreg", Arrays.asList("community", "contrib"));
doTest(map, errorsMap);
}
public void testRealGitRootDeeperThanThreeLevelsShouldBeDetected() throws IOException {
Map<String, Collection<String>> map = new HashMap<String, Collection<String>>();
map.put("git", Arrays.asList(".", "community", "contrib", "community/level1/level2/level3"));
map.put("roots", Arrays.asList(PROJECT, "community/level1/level2/level3"));
map.put("content_roots", Arrays.asList(".", "community", "contrib"));
Map<String, Collection<String>> errorsMap = new HashMap<String, Collection<String>>();
errorsMap.put("unreg", Arrays.asList("community", "contrib"));
doTest(map, errorsMap);
}
private void doTest(@NotNull Map<String, Collection<String>> map, @NotNull Map<String, Collection<String>> errors) throws IOException {
initProject(map.get("git"), Collections.<String>emptyList(), map.get("content_roots"));
addVcsRoots(map.get("roots"));
Collection<VcsRootError> expected = new ArrayList<VcsRootError>();
Collection<String> unregPaths = errors.get("unreg");
Collection<String> extraPaths = errors.get("extra");
if (unregPaths != null) {
expected.addAll(unregAll(unregPaths));
}
if (extraPaths != null) {
expected.addAll(extraAll(extraPaths));
}
Collection<VcsRootError> actual = new VcsRootErrorsFinder(myProject).find();
TestRepositoryUtil.assertEqualCollections(actual, expected);
}
void addVcsRoots(@NotNull Collection<String> relativeRoots) {
for (String root : relativeRoots) {
if (root.equals(PROJECT)) {
myVcsManager.setDirectoryMapping("", GitVcs.NAME);
}
else {
String absoluteRoot = toAbsolute(root, myProject);
myVcsManager.setDirectoryMapping(absoluteRoot, GitVcs.NAME);
}
}
}
@NotNull
Collection<VcsRootError> unregAll(@NotNull Collection<String> paths) {
Collection<VcsRootError> unregRoots = new ArrayList<VcsRootError>();
for (String path : paths) {
unregRoots.add(unreg(path));
}
return unregRoots;
}
@NotNull
Collection<VcsRootError> extraAll(@NotNull Collection<String> paths) {
Collection<VcsRootError> extraRoots = new ArrayList<VcsRootError>();
for (String path : paths) {
extraRoots.add(extra(path));
}
return extraRoots;
}
@NotNull
VcsRootError unreg(@NotNull String path) {
return new VcsRootError(VcsRootError.Type.UNREGISTERED_ROOT, toAbsolute(path, myProject), GitVcs.NAME);
}
@NotNull
VcsRootError extra(@NotNull String path) {
return new VcsRootError(VcsRootError.Type.EXTRA_MAPPING, PROJECT.equals(path) ? PROJECT : toAbsolute(path, myProject), GitVcs.NAME);
}
}
+1
View File
@@ -10,6 +10,7 @@
<extensions defaultExtensionNs="com.intellij">
<vcs name="hg4idea" vcsClass="org.zmlx.hg4idea.HgVcs" displayName="Mercurial" administrativeAreaName=".hg"/>
<checkoutProvider implementation="org.zmlx.hg4idea.provider.HgCheckoutProvider"/>
<vcsRootChecker implementation="org.zmlx.hg4idea.roots.HgRootChecker"/>
<errorHandler implementation="com.intellij.diagnostic.ITNReporter"/>
<vcsPopupProvider implementation="org.zmlx.hg4idea.provider.HgQuickListProvider"/>
<logProvider implementation="org.zmlx.hg4idea.log.HgLogProvider"/>
@@ -0,0 +1,45 @@
/*
* Copyright 2000-2013 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 org.zmlx.hg4idea.roots;
import com.intellij.openapi.vcs.VcsKey;
import com.intellij.openapi.vcs.VcsRootChecker;
import org.jetbrains.annotations.NotNull;
import org.zmlx.hg4idea.HgVcs;
import org.zmlx.hg4idea.util.HgUtil;
import java.io.File;
/**
* @author Nadya Zabrodina
*/
public class HgRootChecker extends VcsRootChecker {
@Override
public boolean isRoot(@NotNull String path) {
return new File(path, HgUtil.DOT_HG).exists();
}
@Override
public VcsKey getSupportedVcs() {
return HgVcs.getKey();
}
@Override
public boolean isVcsDir(String path) {
return path != null && path.toLowerCase().endsWith(HgUtil.DOT_HG);
}
}