Merge remote-tracking branch 'origin/master'

This commit is contained in:
anna
2013-11-04 13:06:56 +01:00
54 changed files with 1328 additions and 1122 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);
}
}
}
});
}
@@ -0,0 +1,56 @@
package com.intellij.openapi.vcs.roots;
import com.intellij.openapi.vcs.VcsRoot;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.Collection;
/**
* @author Nadya Zabrodina
*/
public class VcsRootDetectInfo {
private final @NotNull Collection<VcsRoot> myRoots;
private final boolean myFull;
private final boolean myBelow;
/**
* @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<VcsRoot> roots, boolean full, boolean below) {
myRoots = new ArrayList<VcsRoot>(roots);
myFull = full;
myBelow = below;
}
/**
* @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 totallyUnderVcs() {
return myFull;
}
public boolean empty() {
return myRoots.isEmpty();
}
@NotNull
public Collection<VcsRoot> getRoots() {
return new ArrayList<VcsRoot>(myRoots);
}
/**
* 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 projectIsBelowVcs() {
return myBelow;
}
}
@@ -0,0 +1,138 @@
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.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.*;
/**
* @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
public VcsRootDetectInfo detect() {
return detect(myProject.getBaseDir());
}
@NotNull
public VcsRootDetectInfo detect(@Nullable VirtualFile startDir) {
if (startDir == null) {
return new VcsRootDetectInfo(Collections.<VcsRoot>emptyList(), false, false);
}
final Set<VcsRoot> roots = scanForRootsInsideDir(startDir);
roots.addAll(scanForRootsInContentRoots());
for (VcsRoot root : roots) {
if (startDir.equals(root.getPath())) {
return new VcsRootDetectInfo(roots, true, false);
}
}
List<VcsRoot> rootsAbove = scanForSingleRootAboveDir(startDir);
if (!rootsAbove.isEmpty()) {
roots.addAll(rootsAbove);
return new VcsRootDetectInfo(roots, true, true);
}
return new VcsRootDetectInfo(roots, false, false);
}
@NotNull
private Set<VcsRoot> scanForRootsInContentRoots() {
Set<VcsRoot> gitRoots = new HashSet<VcsRoot>();
VirtualFile[] roots = myProjectManager.getContentRoots();
for (VirtualFile contentRoot : roots) {
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<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;
}
if (myProject.isDisposed() || !dir.isDirectory()) {
return roots;
}
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));
}
return roots;
}
@NotNull
private Set<VcsRoot> scanForRootsInsideDir(@NotNull VirtualFile dir) {
return scanForRootsInsideDir(dir, 0);
}
@NotNull
private List<VcsRoot> scanForSingleRootAboveDir(@NotNull final VirtualFile dir) {
List<VcsRoot> roots = new ArrayList<VcsRoot>();
if (myProject.isDisposed()) {
return roots;
}
VirtualFile par = dir.getParent();
while (par != null) {
List<AbstractVcs> vcsList = getVcsListFor(par);
for (AbstractVcs vcs : vcsList) {
roots.add(new VcsRoot(vcs, par));
}
if (!roots.isEmpty()) {
return roots;
}
par = par.getParent();
}
return roots;
}
@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;
}
}
@@ -0,0 +1,121 @@
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.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.util.ArrayList;
import java.util.Collection;
import java.util.List;
/**
* @author Nadya Zabrodina
*/
public class VcsRootErrorsFinder {
private final @NotNull Project myProject;
private final @NotNull ProjectLevelVcsManager myVcsManager;
public VcsRootErrorsFinder(@NotNull Project project) {
myProject = project;
myVcsManager = ProjectLevelVcsManager.getInstance(project);
}
@NotNull
public Collection<VcsRootError> find() {
List<VcsDirectoryMapping> mappings = myVcsManager.getDirectoryMappings();
Collection<VcsRoot> vcsRoots = new VcsRootDetector(myProject).detect().getRoots();
Collection<VcsRootError> errors = new ArrayList<VcsRootError>();
errors.addAll(findExtraMappings(mappings, vcsRoots.isEmpty()));
errors.addAll(findUnregisteredRoots(mappings, vcsRoots));
return errors;
}
@NotNull
private Collection<VcsRootError> findUnregisteredRoots(@NotNull List<VcsDirectoryMapping> mappings,
@NotNull Collection<VcsRoot> vcsRoots) {
Collection<VcsRootError> errors = new ArrayList<VcsRootError>();
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;
}
@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 (isEmptyVcsRoots && !StringUtil.isEmptyOrSpaces(mapping.getVcs())) {
errors.add(new VcsRootError(VcsRootError.Type.EXTRA_MAPPING, VcsDirectoryMapping.PROJECT_CONSTANT, mapping.getVcs()));
}
}
else {
String mappedPath = mapping.systemIndependentPath();
if (!isRoot(mapping)) {
errors.add(new VcsRootError(VcsRootError.Type.EXTRA_MAPPING, mappedPath, mapping.getVcs()));
}
}
}
return errors;
}
@NotNull
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> 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());
}
else {
String basePath = myProject.getBasePath();
if (basePath != null) {
paths.add(FileUtil.toSystemIndependentName(basePath));
}
}
}
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().equalsIgnoreCase(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;
@@ -30,7 +26,6 @@ import com.intellij.openapi.vcs.VcsConfiguration;
import com.intellij.openapi.vcs.VcsDirectoryMapping;
import com.intellij.openapi.vcs.VcsRootError;
import com.intellij.util.Function;
import git4idea.GitPlatformFacade;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -41,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 GitRootErrorsFinder} 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);
}
@@ -84,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);
@@ -94,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);
}
}
@@ -116,29 +109,33 @@ public class GitRootProblemNotifier {
@NotNull
private Collection<VcsRootError> scan() {
return new GitRootErrorsFinder(myProject, myPlatformFacade).find();
return new VcsRootErrorsFinder(myProject).find();
}
@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/>");
@@ -146,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/>");
@@ -162,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;
@@ -212,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);
}
}
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.dvcs.test;
package com.intellij.openapi.vcs;
import com.intellij.execution.process.CapturingProcessHandler;
import com.intellij.execution.process.ProcessOutput;
@@ -1,4 +1,4 @@
package com.intellij.dvcs.test;
package com.intellij.openapi.vcs;
import com.intellij.openapi.application.PluginPathManager;
import com.intellij.openapi.command.WriteCommandAction;
@@ -7,6 +7,8 @@ import com.intellij.openapi.util.Ref;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.vfs.CharsetToolkit;
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;
@@ -15,6 +17,8 @@ import java.io.FilenameFilter;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import static junit.framework.Assert.assertNotNull;
import static junit.framework.Assert.fail;
@@ -22,8 +26,7 @@ import static junit.framework.Assert.fail;
/**
* @author Nadya Zabrodina
*/
public class TestRepositoryUtil {
public class VcsTestUtil {
// TODO: option - create via IDEA or via java.io. In latter case no need in Project parameter.
public static VirtualFile createFile(@NotNull Project project,
@@ -193,4 +196,26 @@ public class TestRepositoryUtil {
public static String stringifyActualExpected(@NotNull Object actual, @NotNull Object expected) {
return "\nExpected:\n" + expected + "\nActual:\n" + actual;
}
@NotNull
public static String toAbsolute(@NotNull String relPath, @NotNull Project project) {
new File(toAbsolute(Collections.singletonList(relPath), project).get(0)).mkdir();
return toAbsolute(Collections.singletonList(relPath), project).get(0);
}
@NotNull
public static List<String> toAbsolute(@NotNull Collection<String> relPaths, @NotNull final Project project) {
return ContainerUtil.map2List(relPaths, new Function<String, String>() {
@Override
public String fun(String s) {
try {
return FileUtil.toSystemIndependentName((new File(project.getBasePath() + "/" + s).getCanonicalPath()));
}
catch (IOException e) {
e.printStackTrace();
return "";
}
}
});
}
}
@@ -0,0 +1,211 @@
/*
* 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 com.intellij.openapi.vcs.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.openapi.vcs.Executor.*;
/**
* @author Nadya Zabrodina
*/
public abstract class VcsPlatformTest 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 final static String myVcsName = "Git"; //now scanner test executed only for git todo: create for all
protected IdeaProjectTestFixture myProjectFixture;
@SuppressWarnings("JUnitTestCaseWithNonTrivialConstructors")
protected VcsPlatformTest() {
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;
}
}
@@ -0,0 +1,216 @@
/*
* 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 com.intellij.openapi.vcs.roots;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.vcs.VcsRoot;
import com.intellij.openapi.vcs.VcsTestUtil;
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.openapi.vcs.Executor.cd;
import static com.intellij.openapi.vcs.Executor.mkdir;
/**
* @author Nadya Zabrodina
*/
public class VcsRootDetectorTest extends VcsPlatformTest {
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);
mkdir("src");
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);
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);
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) {
VcsTestUtil.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());
}
}
@@ -0,0 +1,268 @@
/*
* 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 com.intellij.openapi.vcs.roots;
import com.intellij.openapi.vcs.ProjectLevelVcsManager;
import com.intellij.openapi.vcs.VcsDirectoryMapping;
import com.intellij.openapi.vcs.VcsRootError;
import com.intellij.openapi.vcs.VcsTestUtil;
import org.jetbrains.annotations.NotNull;
import java.io.IOException;
import java.util.*;
/**
* @author Nadya Zabrodina
*/
public class VcsRootErrorsFinderTest extends VcsPlatformTest {
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();
VcsTestUtil.assertEqualCollections(actual, expected);
}
void addVcsRoots(@NotNull Collection<String> relativeRoots) {
for (String root : relativeRoots) {
if (root.equals(PROJECT)) {
myVcsManager.setDirectoryMapping("", myVcsName);
}
else {
String absoluteRoot = VcsTestUtil.toAbsolute(root, myProject);
myVcsManager.setDirectoryMapping(absoluteRoot, myVcsName);
}
}
}
@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, VcsTestUtil.toAbsolute(path, myProject), myVcsName);
}
@NotNull
VcsRootError extra(@NotNull String path) {
return new VcsRootError(VcsRootError.Type.EXTRA_MAPPING, PROJECT.equals(path) ? PROJECT : VcsTestUtil.toAbsolute(path, myProject),
myVcsName);
}
}
+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"/>
+3 -9
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 git4idea.roots.GitRootDetectInfo;
import git4idea.roots.GitRootDetector;
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 {
}
@@ -592,7 +586,7 @@ public class GitVcs extends AbstractVcs<CommittedChangeList> {
public void enableIntegration() {
ApplicationManager.getApplication().executeOnPooledThread(new Runnable() {
public void run() {
GitRootDetectInfo detectInfo = new GitRootDetector(myProject, myPlatformFacade).detect();
VcsRootDetectInfo detectInfo = new VcsRootDetector(myProject).detect();
new GitIntegrationEnabler(myProject, myGit, myPlatformFacade).enable(detectInfo);
}
});
@@ -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,10 +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;
@@ -51,13 +56,20 @@ public class GitIntegrationEnabler {
myPlatformFacade = platformFacade;
}
public void enable(@NotNull GitRootDetectInfo detectInfo) {
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));
@@ -65,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);
@@ -94,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,61 +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 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 GitRootErrorsFinder(project, platformFacade).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 GitRootErrorsFinder 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);
}
}
@@ -1,73 +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.vfs.VirtualFile;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.Collection;
/**
* The result of root detection.
*
* @author Kirill Likhodedov
*/
public class GitRootDetectInfo {
private final @NotNull Collection<VirtualFile> 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.
*/
GitRootDetectInfo(@NotNull Collection<VirtualFile> roots, boolean full, boolean below) {
myRoots = new ArrayList<VirtualFile>(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.
*/
boolean totallyUnderGit() {
return myFull;
}
boolean empty() {
return myRoots.isEmpty();
}
@NotNull
public Collection<VirtualFile> getRoots() {
return new ArrayList<VirtualFile>(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.
*/
boolean projectIsBelowGit() {
return myBelow;
}
}
@@ -1,141 +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.vfs.VirtualFile;
import git4idea.GitPlatformFacade;
import git4idea.GitUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
/**
* <p>
* Scans the file system looking for Git roots, which contain the Project or its parts,
* and returns the information enclosed to the {@link GitRootDetectInfo}.
* 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
*/
public class GitRootDetector {
private static final int MAXIMUM_SCAN_DEPTH = 2;
@NotNull private final Project myProject;
@NotNull private final GitPlatformFacade myPlatformFacade;
public GitRootDetector(@NotNull Project project, @NotNull GitPlatformFacade platformFacade) {
myProject = project;
myPlatformFacade = platformFacade;
}
@NotNull
public GitRootDetectInfo detect() {
return detect(myProject.getBaseDir());
}
@NotNull
public GitRootDetectInfo detect(@Nullable VirtualFile startDir) {
if (startDir == null) {
return new GitRootDetectInfo(Collections.<VirtualFile>emptyList(), false, false);
}
final Set<VirtualFile> roots = scanForRootsInsideDir(startDir);
roots.addAll(scanForRootsInContentRoots());
if (roots.contains(startDir)) {
return new GitRootDetectInfo(roots, true, false);
}
VirtualFile rootAbove = scanForSingleRootAboveDir(startDir);
if (rootAbove != null) {
roots.add(rootAbove);
return new GitRootDetectInfo(roots, true, true);
}
return new GitRootDetectInfo(roots, false, false);
}
private Set<VirtualFile> scanForRootsInContentRoots() {
Set<VirtualFile> gitRoots = new HashSet<VirtualFile>();
VirtualFile[] roots = myPlatformFacade.getProjectRootManager(myProject).getContentRoots();
for (VirtualFile contentRoot : roots) {
Set<VirtualFile> rootsInsideRoot = scanForRootsInsideDir(contentRoot);
if (!rootsInsideRoot.contains(contentRoot)) {
VirtualFile rootAbove = scanForSingleRootAboveDir(contentRoot);
if (rootAbove != null) {
rootsInsideRoot.add(rootAbove);
}
}
gitRoots.addAll(rootsInsideRoot);
}
return gitRoots;
}
@NotNull
private Set<VirtualFile> scanForRootsInsideDir(@NotNull final VirtualFile dir, final int depth) {
final Set<VirtualFile> roots = new HashSet<VirtualFile>();
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;
}
if (myProject.isDisposed() || !dir.isDirectory()) {
return roots;
}
if (hasGitDir(dir)) {
roots.add(dir);
}
for (VirtualFile child : dir.getChildren()) {
roots.addAll(scanForRootsInsideDir(child, depth + 1));
}
return roots;
}
@NotNull
private Set<VirtualFile> scanForRootsInsideDir(@NotNull VirtualFile dir) {
return scanForRootsInsideDir(dir, 0);
}
@Nullable
private VirtualFile scanForSingleRootAboveDir(@NotNull final VirtualFile dir) {
if (myProject.isDisposed()) {
return null;
}
VirtualFile par = dir.getParent();
while (par != null) {
if (hasGitDir(par)) {
return par;
}
par = par.getParent();
}
return null;
}
private static boolean hasGitDir(@NotNull VirtualFile dir) {
VirtualFile gitDir = dir.findChild(GitUtil.DOT_GIT);
return gitDir != null && gitDir.exists();
}
}
@@ -1,124 +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.util.io.FileUtil;
import com.intellij.openapi.vcs.AbstractVcs;
import com.intellij.openapi.vcs.ProjectLevelVcsManager;
import com.intellij.openapi.vcs.VcsDirectoryMapping;
import com.intellij.openapi.vcs.VcsRootError;
import com.intellij.openapi.vfs.VirtualFile;
import git4idea.GitPlatformFacade;
import git4idea.GitUtil;
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
*/
public class GitRootErrorsFinder {
private final @NotNull Project myProject;
private final @NotNull GitPlatformFacade myPlatformFacade;
private final @NotNull ProjectLevelVcsManager myVcsManager;
private final AbstractVcs myVcs;
public GitRootErrorsFinder(@NotNull Project project, @NotNull GitPlatformFacade platformFacade) {
myProject = project;
myPlatformFacade = platformFacade;
myVcsManager = myPlatformFacade.getVcsManager(myProject);
myVcs = myPlatformFacade.getVcs(myProject);
}
@NotNull
public Collection<VcsRootError> find() {
List<VcsDirectoryMapping> mappings = myVcsManager.getDirectoryMappings(myVcs);
Collection<VirtualFile> gitRoots = new GitRootDetector(myProject, myPlatformFacade).detect().getRoots();
Collection<VcsRootError> errors = new ArrayList<VcsRootError>();
Collection<String> gitPaths = rootsToPaths(gitRoots);
errors.addAll(findExtraMappings(mappings, gitPaths));
errors.addAll(findUnregisteredRoots(mappings, gitPaths));
return errors;
}
private Collection<VcsRootError> findUnregisteredRoots(List<VcsDirectoryMapping> mappings, Collection<String> gitPaths) {
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));
}
}
return errors;
}
private static Collection<VcsRootError> findExtraMappings(List<VcsDirectoryMapping> mappings, Collection<String> gitPaths) {
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));
}
}
else {
String mappedPath = mapping.systemIndependentPath();
if (!gitPaths.contains(mappedPath) && !hasGitDir(mappedPath)) {
errors.add(new VcsRootError(VcsRootError.Type.EXTRA_MAPPING, mappedPath));
}
}
}
return errors;
}
private static boolean hasGitDir(String path) {
File file = new File(path, GitUtil.DOT_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;
}
private List<String> mappingsToPaths(List<VcsDirectoryMapping> mappings) {
List<String> paths = new ArrayList<String>();
for (VcsDirectoryMapping mapping : mappings) {
if (!mapping.isDefaultMapping()) {
paths.add(mapping.systemIndependentPath());
}
else {
String basePath = myProject.getBasePath();
if (basePath != null) {
paths.add(FileUtil.toSystemIndependentName(basePath));
}
}
}
return paths;
}
}
@@ -2,10 +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;
@@ -18,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) {
GitRootDetectInfo info = new GitRootDetector(myProject, myPlatformFacade).detect(root);
Collection<VirtualFile> roots = info.getRoots();
VcsRootDetectInfo info = new VcsRootDetector(myProject).detect(root);
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;
}
@@ -23,8 +23,8 @@ import java.util.Collection;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import static com.intellij.dvcs.test.Executor.echo;
import static com.intellij.dvcs.test.Executor.touch;
import static com.intellij.openapi.vcs.Executor.echo;
import static com.intellij.openapi.vcs.Executor.touch;
import static git4idea.GitCucumberWorld.virtualCommits;
import static git4idea.test.GitExecutor.git;
import static org.junit.Assert.assertTrue;
@@ -43,7 +43,7 @@ import git4idea.log.GitContentRevisionFactory;
import java.util.*;
import static com.intellij.dvcs.test.Executor.echo;
import static com.intellij.openapi.vcs.Executor.echo;
import static git4idea.GitCucumberWorld.*;
import static git4idea.test.GitExecutor.git;
import static junit.framework.Assert.assertEquals;
@@ -45,8 +45,8 @@ import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicReference;
import static com.intellij.dvcs.test.Executor.cd;
import static com.intellij.dvcs.test.Executor.mkdir;
import static com.intellij.openapi.vcs.Executor.cd;
import static com.intellij.openapi.vcs.Executor.mkdir;
import static org.junit.Assume.assumeTrue;
/**
@@ -15,7 +15,7 @@
*/
package git4idea.test;
import com.intellij.dvcs.test.Executor;
import com.intellij.openapi.vcs.Executor;
import git4idea.repo.GitRepository;
import java.util.Arrays;
@@ -15,8 +15,8 @@
*/
package git4idea.test;
import com.intellij.dvcs.test.Executor;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vcs.Executor;
import org.jetbrains.annotations.NotNull;
import java.io.File;
@@ -35,10 +35,10 @@ import java.io.File;
import java.util.HashMap;
import java.util.Map;
import static com.intellij.dvcs.test.Executor.cd;
import static com.intellij.dvcs.test.Executor.touch;
import static com.intellij.dvcs.test.TestRepositoryUtil.createDir;
import static com.intellij.dvcs.test.TestRepositoryUtil.createFile;
import static com.intellij.openapi.vcs.Executor.cd;
import static com.intellij.openapi.vcs.Executor.touch;
import static com.intellij.openapi.vcs.VcsTestUtil.createDir;
import static com.intellij.openapi.vcs.VcsTestUtil.createFile;
import static git4idea.test.GitExecutor.git;
import static junit.framework.Assert.assertNotNull;
import static junit.framework.Assert.assertNull;
@@ -42,7 +42,7 @@ import org.junit.Test
import java.util.regex.Matcher
import static com.intellij.dvcs.test.Executor.*
import static com.intellij.openapi.vcs.Executor.*
import static git4idea.test.GitExecutor.cd
import static git4idea.test.GitExecutor.git
import static git4idea.test.GitScenarios.*
@@ -24,7 +24,7 @@ import org.junit.Before
import org.junit.Ignore
import org.junit.Test
import static com.intellij.dvcs.test.Executor.cd
import static com.intellij.openapi.vcs.Executor.cd
import static git4idea.test.GitExecutor.git
import static org.junit.Assert.assertFalse
import static org.junit.Assert.assertTrue
@@ -17,9 +17,9 @@ package git4idea.repo;
import com.google.common.base.Function;
import com.google.common.collect.Collections2;
import com.intellij.dvcs.test.TestRepositoryUtil;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vcs.VcsTestUtil;
import git4idea.GitBranch;
import git4idea.GitLocalBranch;
import git4idea.GitRemoteBranch;
@@ -40,23 +40,23 @@ public class GitConfigTest {
@DataProvider(name = "remote")
public Object[][] loadRemotes() throws IOException {
return TestRepositoryUtil.loadConfigData(getTestDataFolder("remote"));
return VcsTestUtil.loadConfigData(getTestDataFolder("remote"));
}
@DataProvider(name = "branch")
public Object[][] loadBranches() throws IOException {
return TestRepositoryUtil.loadConfigData(getTestDataFolder("branch"));
return VcsTestUtil.loadConfigData(getTestDataFolder("branch"));
}
private static File getTestDataFolder(String subfolder) {
File testData = TestRepositoryUtil.getTestDataFolder();
File testData = VcsTestUtil.getTestDataFolder();
return new File(new File(testData, "config"), subfolder);
}
@Test(dataProvider = "remote")
public void testRemotes(String testName, File configFile, File resultFile) throws IOException {
GitConfig config = GitConfig.read(new GitTestPlatformFacade(), configFile);
TestRepositoryUtil.assertEqualCollections(config.parseRemotes(), readRemoteResults(resultFile));
VcsTestUtil.assertEqualCollections(config.parseRemotes(), readRemoteResults(resultFile));
}
@Test(dataProvider = "branch")
@@ -77,7 +77,7 @@ public class GitConfigTest {
}
});
TestRepositoryUtil.assertEqualCollections(
VcsTestUtil.assertEqualCollections(
GitConfig.read(new GitTestPlatformFacade(), configFile).parseTrackInfos(localBranches, remoteBranches),
expectedInfos);
}
@@ -15,9 +15,9 @@
*/
package git4idea.repo
import com.intellij.dvcs.test.TestRepositoryUtil
import com.intellij.openapi.application.PluginPathManager
import com.intellij.openapi.util.io.FileUtil
import com.intellij.openapi.vcs.VcsTestUtil
import com.intellij.util.Processor
import git4idea.GitBranch
import git4idea.branch.GitBranchesCollection
@@ -88,7 +88,7 @@ public class GitRepositoryReaderTest extends GitLightTest {
}
private static void assertBranches(Collection<GitBranch> actualBranches, Collection<GitTestBranch> expectedBranches) {
TestRepositoryUtil.assertEqualCollections(actualBranches, expectedBranches, new TestRepositoryUtil.EqualityChecker<GitBranch, GitTestBranch>() {
VcsTestUtil.assertEqualCollections(actualBranches, expectedBranches, new VcsTestUtil.EqualityChecker<GitBranch, GitTestBranch>() {
@Override
public boolean areEqual(@NotNull GitBranch actual, @NotNull GitTestBranch expected) {
return branchesAreEqual(actual, expected);
@@ -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))
}
}
@@ -18,8 +18,9 @@ package git4idea.roots
import com.intellij.dvcs.test.MockVirtualFile
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
@@ -27,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();
}
@@ -89,7 +92,7 @@ class GitIntegrationEnablerTest extends GitLightTest {
notification("Added Git roots: ${path("community")}, ${path("contrib")}"))
}
private void doTest(GitRootDetectInfo detectInfo, Map map) {
private void doTest(VcsRootDetectInfo detectInfo, Map map) {
// defaults
if (!map.vcs_roots) {
@@ -117,8 +120,8 @@ class GitIntegrationEnablerTest extends GitLightTest {
assertEquals expectedVcsRoots.toSet(), actualRoots.collect() { it.path }.toSet()
}
GitRootDetectInfo given(Collection<String> roots, boolean full = true, boolean below = false) {
new GitRootDetectInfo(roots.collect { (VirtualFile)new MockVirtualFile(toAbsolute(it, myProject)) }, full, below)
VcsRootDetectInfo given(Collection<String> roots, boolean full = true, boolean below = false) {
new VcsRootDetectInfo(roots.collect { (VirtualFile)new MockVirtualFile(toAbsolute(it, myProject)) }, full, below)
}
Map expect(Map map, Notification notification = null, Class dialogClass = null) {
@@ -156,5 +159,4 @@ class GitIntegrationEnablerTest extends GitLightTest {
void assertNotificationShown(String title, String message, NotificationType type) {
assertNotificationShown(new Notification(TEST_NOTIFICATION_GROUP, title, message, type))
}
}
@@ -1,220 +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.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 GitRootDetector.
* @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, GitRootDetectInfo 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 GitRootDetectInfo detect() {
new GitRootDetector(myProject, myPlatformFacade).detect()
}
}
@@ -1,210 +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 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 GitRootErrorsFinder(myProject, myPlatformFacade).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))
}
}
@@ -33,7 +33,7 @@ import org.jetbrains.plugins.github.util.GithubUtil;
import java.util.Random;
import static com.intellij.dvcs.test.Executor.cd;
import static com.intellij.openapi.vcs.Executor.cd;
import static git4idea.test.GitExecutor.git;
/**
@@ -9,7 +9,7 @@ import org.jetbrains.plugins.github.util.GithubAuthData;
import java.io.IOException;
import static com.intellij.dvcs.test.Executor.cd;
import static com.intellij.openapi.vcs.Executor.cd;
import static git4idea.test.GitExecutor.git;
/**
+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);
}
}
@@ -25,8 +25,8 @@ import org.zmlx.hg4idea.execution.HgCommandException;
import java.util.List;
import static com.intellij.dvcs.test.Executor.cd;
import static com.intellij.dvcs.test.Executor.echo;
import static com.intellij.openapi.vcs.Executor.cd;
import static com.intellij.openapi.vcs.Executor.echo;
/**
* @author Nadya Zabrodina
@@ -15,10 +15,10 @@
*/
package hg4idea.test;
import com.intellij.dvcs.test.Executor;
import com.intellij.openapi.application.PluginPathManager;
import com.intellij.openapi.util.SystemInfo;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.vcs.Executor;
import org.jetbrains.annotations.NotNull;
import java.io.File;
@@ -32,8 +32,8 @@ import org.zmlx.hg4idea.util.HgUtil;
import java.io.File;
import java.io.IOException;
import static com.intellij.dvcs.test.Executor.cd;
import static com.intellij.dvcs.test.Executor.touch;
import static com.intellij.openapi.vcs.Executor.cd;
import static com.intellij.openapi.vcs.Executor.touch;
import static hg4idea.test.HgExecutor.hg;
/**
@@ -14,8 +14,8 @@ import java.util.Arrays;
import java.util.Date;
import java.util.List;
import static com.intellij.dvcs.test.Executor.cd;
import static com.intellij.dvcs.test.Executor.echo;
import static com.intellij.openapi.vcs.Executor.cd;
import static com.intellij.openapi.vcs.Executor.echo;
import static hg4idea.test.HgExecutor.hg;
/**
@@ -1,7 +1,7 @@
package hg4idea.test.config;
import com.intellij.dvcs.test.TestRepositoryUtil;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.vcs.VcsTestUtil;
import hg4idea.test.HgPlatformTest;
import org.zmlx.hg4idea.util.HgUtil;
@@ -10,7 +10,7 @@ import java.io.IOException;
import java.util.Arrays;
import java.util.Collection;
import static com.intellij.dvcs.test.Executor.cd;
import static com.intellij.openapi.vcs.Executor.cd;
/**
* @author Nadya Zabrodina
@@ -63,7 +63,7 @@ public class HgConfigTest extends HgPlatformTest {
updateRepoConfig(myProject, myChildRepo);
final Collection<String> paths = HgUtil.getRepositoryPaths(myProject, myChildRepo);
final Collection<String> expectedPaths = Arrays.asList(FileUtil.toSystemDependentName(myRepository.getPath()), path1, path2, path3);
TestRepositoryUtil.assertEqualCollections(paths,expectedPaths);
VcsTestUtil.assertEqualCollections(paths, expectedPaths);
}
public void testLargeExtensionInClonedRepo() throws IOException {
@@ -27,8 +27,8 @@ import org.zmlx.hg4idea.provider.HgRepositoryLocation;
import java.text.ParseException;
import java.util.List;
import static com.intellij.dvcs.test.Executor.cd;
import static com.intellij.dvcs.test.Executor.touch;
import static com.intellij.openapi.vcs.Executor.cd;
import static com.intellij.openapi.vcs.Executor.touch;
import static hg4idea.test.HgExecutor.hg;
/**
@@ -28,7 +28,7 @@ import org.zmlx.hg4idea.util.HgUtil;
import java.io.File;
import java.util.List;
import static com.intellij.dvcs.test.Executor.*;
import static com.intellij.openapi.vcs.Executor.*;
import static hg4idea.test.HgExecutor.hg;
/**
@@ -11,8 +11,8 @@ import org.zmlx.hg4idea.execution.HgCommandException;
import java.util.List;
import static com.intellij.dvcs.test.Executor.cd;
import static com.intellij.dvcs.test.Executor.touch;
import static com.intellij.openapi.vcs.Executor.cd;
import static com.intellij.openapi.vcs.Executor.touch;
import static hg4idea.test.HgExecutor.hg;
/**
@@ -27,7 +27,7 @@ import org.zmlx.hg4idea.HgVcs;
import java.io.IOException;
import static com.intellij.dvcs.test.Executor.*;
import static com.intellij.openapi.vcs.Executor.*;
import static hg4idea.test.HgExecutor.hg;
import static hg4idea.test.HgExecutor.updateProject;
@@ -16,7 +16,7 @@
package hg4idea.test.repo;
import com.intellij.dvcs.repo.Repository;
import com.intellij.dvcs.test.TestRepositoryUtil;
import com.intellij.openapi.vcs.VcsTestUtil;
import hg4idea.test.HgPlatformTest;
import org.jetbrains.annotations.NotNull;
import org.zmlx.hg4idea.repo.HgRepositoryReader;
@@ -25,7 +25,7 @@ import org.zmlx.hg4idea.util.HgUtil;
import java.io.File;
import java.util.Arrays;
import static com.intellij.dvcs.test.Executor.*;
import static com.intellij.openapi.vcs.Executor.*;
import static hg4idea.test.HgExecutor.hg;
/**
@@ -58,17 +58,17 @@ public class HgRealRepositoryReaderTest extends HgPlatformTest {
}
public void testBranches() {
TestRepositoryUtil.assertEqualCollections(HgUtil.getNamesWithoutHashes(myRepositoryReader.readBranches()),
VcsTestUtil.assertEqualCollections(HgUtil.getNamesWithoutHashes(myRepositoryReader.readBranches()),
Arrays.asList("default", "branchA", "branchB"));
}
public void testTags() {
TestRepositoryUtil.assertEqualCollections(HgUtil.getNamesWithoutHashes(myRepositoryReader.readTags()),
VcsTestUtil.assertEqualCollections(HgUtil.getNamesWithoutHashes(myRepositoryReader.readTags()),
Arrays.asList("tag1", "tag2"));
}
public void testLocalTags() {
TestRepositoryUtil.assertEqualCollections(HgUtil.getNamesWithoutHashes(myRepositoryReader.readLocalTags()),
VcsTestUtil.assertEqualCollections(HgUtil.getNamesWithoutHashes(myRepositoryReader.readLocalTags()),
Arrays.asList("localTag"));
}
@@ -78,7 +78,7 @@ public class HgRealRepositoryReaderTest extends HgPlatformTest {
}
public void testBookmarks() {
TestRepositoryUtil.assertEqualCollections(HgUtil.getNamesWithoutHashes(myRepositoryReader.readBookmarks()),
VcsTestUtil.assertEqualCollections(HgUtil.getNamesWithoutHashes(myRepositoryReader.readBookmarks()),
Arrays.asList("A_BookMark", "B_BookMark", "C_BookMark"));
}
@@ -15,9 +15,9 @@
*/
package hg4idea.test.repo;
import com.intellij.dvcs.test.TestRepositoryUtil;
import com.intellij.openapi.application.PluginPathManager;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.vcs.VcsTestUtil;
import hg4idea.test.HgPlatformTest;
import org.jetbrains.annotations.NotNull;
import org.zmlx.hg4idea.repo.HgRepositoryReader;
@@ -81,12 +81,12 @@ public class HgRepositoryReaderTest extends HgPlatformTest {
public void testBranches() {
Collection<String> branches = HgUtil.getNamesWithoutHashes(myRepositoryReader.readBranches());
TestRepositoryUtil.assertEqualCollections(branches, myBranches);
VcsTestUtil.assertEqualCollections(branches, myBranches);
}
public void testBookmarks() {
Collection<String> bookmarks = HgUtil.getNamesWithoutHashes(myRepositoryReader.readBookmarks());
TestRepositoryUtil.assertEqualCollections(bookmarks, myBookmarks);
Collection<String> bookmarks = myRepositoryReader.readBookmarks();
VcsTestUtil.assertEqualCollections(bookmarks, myBookmarks);
}
public void testTags() {